Bug Summary

File:src/bin/expr/expr.c
Warning:line 517, column 2
Potential leak of memory pointed to by 'vp'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name expr.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/bin/expr/obj -resource-dir /usr/local/lib/clang/13.0.0 -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/bin/expr/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/bin/expr/expr.c
1/* $OpenBSD: expr.c,v 1.27 2018/03/31 14:50:56 tobias Exp $ */
2/* $NetBSD: expr.c,v 1.3.6.1 1996/06/04 20:41:47 cgd Exp $ */
3
4/*
5 * Written by J.T. Conklin <jtc@netbsd.org>.
6 * Public domain.
7 */
8
9#include <stdio.h>
10#include <stdint.h>
11#include <stdlib.h>
12#include <string.h>
13#include <limits.h>
14#include <ctype.h>
15#include <unistd.h>
16#include <regex.h>
17#include <err.h>
18
19struct val *make_int(int64_t);
20struct val *make_str(char *);
21void free_value(struct val *);
22int is_integer(struct val *, int64_t *, const char **);
23int to_integer(struct val *, const char **);
24void to_string(struct val *);
25int is_zero_or_null(struct val *);
26void nexttoken(int);
27__dead__attribute__((__noreturn__)) void error(void);
28struct val *eval6(void);
29struct val *eval5(void);
30struct val *eval4(void);
31struct val *eval3(void);
32struct val *eval2(void);
33struct val *eval1(void);
34struct val *eval0(void);
35
36enum token {
37 OR, AND, EQ, LT, GT, ADD, SUB, MUL, DIV, MOD, MATCH, RP, LP,
38 NE, LE, GE, OPERAND, EOI
39};
40
41struct val {
42 enum {
43 integer,
44 string
45 } type;
46
47 union {
48 char *s;
49 int64_t i;
50 } u;
51};
52
53enum token token;
54struct val *tokval;
55char **av;
56
57struct val *
58make_int(int64_t i)
59{
60 struct val *vp;
61
62 vp = malloc(sizeof(*vp));
19
Memory is allocated
63 if (vp == NULL((void *)0)) {
20
Assuming 'vp' is not equal to NULL
21
Taking false branch
64 err(3, NULL((void *)0));
65 }
66 vp->type = integer;
67 vp->u.i = i;
68 return vp;
69}
70
71
72struct val *
73make_str(char *s)
74{
75 struct val *vp;
76
77 vp = malloc(sizeof(*vp));
78 if (vp == NULL((void *)0) || ((vp->u.s = strdup(s)) == NULL((void *)0))) {
79 err(3, NULL((void *)0));
80 }
81 vp->type = string;
82 return vp;
83}
84
85
86void
87free_value(struct val *vp)
88{
89 if (vp->type == string)
90 free(vp->u.s);
91 free(vp);
92}
93
94
95/* determine if vp is an integer; if so, return it's value in *r */
96int
97is_integer(struct val *vp, int64_t *r, const char **errstr)
98{
99 const char *errstrp;
100
101 if (errstr == NULL((void *)0))
102 errstr = &errstrp;
103 *errstr = NULL((void *)0);
104
105 if (vp->type == integer) {
106 *r = vp->u.i;
107 return 1;
108 }
109
110 /*
111 * POSIX.2 defines an "integer" as an optional unary minus
112 * followed by digits. Other representations are unspecified,
113 * which means that strtonum(3) is a viable option here.
114 */
115 *r = strtonum(vp->u.s, INT64_MIN(-0x7fffffffffffffffLL - 1), INT64_MAX0x7fffffffffffffffLL, errstr);
116 return *errstr == NULL((void *)0);
117}
118
119
120/* coerce to vp to an integer */
121int
122to_integer(struct val *vp, const char **errstr)
123{
124 int64_t r;
125
126 if (errstr != NULL((void *)0))
127 *errstr = NULL((void *)0);
128
129 if (vp->type == integer)
130 return 1;
131
132 if (is_integer(vp, &r, errstr)) {
133 free(vp->u.s);
134 vp->u.i = r;
135 vp->type = integer;
136 return 1;
137 }
138
139 return 0;
140}
141
142
143/* coerce to vp to an string */
144void
145to_string(struct val *vp)
146{
147 char *tmp;
148
149 if (vp->type == string)
150 return;
151
152 if (asprintf(&tmp, "%lld", vp->u.i) == -1)
153 err(3, NULL((void *)0));
154
155 vp->type = string;
156 vp->u.s = tmp;
157}
158
159int
160is_zero_or_null(struct val *vp)
161{
162 if (vp->type == integer)
163 return vp->u.i == 0;
164 else
165 return *vp->u.s == 0 || (to_integer(vp, NULL((void *)0)) && vp->u.i == 0);
166}
167
168void
169nexttoken(int pat)
170{
171 char *p;
172
173 if ((p = *av) == NULL((void *)0)) {
174 token = EOI;
175 return;
176 }
177 av++;
178
179
180 if (pat == 0 && p[0] != '\0') {
181 if (p[1] == '\0') {
182 const char *x = "|&=<>+-*/%:()";
183 char *i; /* index */
184
185 if ((i = strchr(x, *p)) != NULL((void *)0)) {
186 token = i - x;
187 return;
188 }
189 } else if (p[1] == '=' && p[2] == '\0') {
190 switch (*p) {
191 case '<':
192 token = LE;
193 return;
194 case '>':
195 token = GE;
196 return;
197 case '!':
198 token = NE;
199 return;
200 }
201 }
202 }
203 tokval = make_str(p);
204 token = OPERAND;
205 return;
206}
207
208__dead__attribute__((__noreturn__)) void
209error(void)
210{
211 errx(2, "syntax error");
212}
213
214struct val *
215eval6(void)
216{
217 struct val *v;
218
219 if (token == OPERAND) {
220 nexttoken(0);
221 return tokval;
222 } else if (token == RP) {
223 nexttoken(0);
224 v = eval0();
225 if (token != LP)
226 error();
227 nexttoken(0);
228 return v;
229 } else
230 error();
231}
232
233/* Parse and evaluate match (regex) expressions */
234struct val *
235eval5(void)
236{
237 regex_t rp;
238 regmatch_t rm[2];
239 char errbuf[256];
240 int eval;
241 struct val *l, *r;
242 struct val *v;
243
244 l = eval6();
245 while (token == MATCH) {
246 nexttoken(1);
247 r = eval6();
248
249 /* coerce to both arguments to strings */
250 to_string(l);
251 to_string(r);
252
253 /* compile regular expression */
254 if ((eval = regcomp(&rp, r->u.s, 0)) != 0) {
255 regerror(eval, &rp, errbuf, sizeof(errbuf));
256 errx(2, "%s", errbuf);
257 }
258
259 /* compare string against pattern -- remember that patterns
260 are anchored to the beginning of the line */
261 if (regexec(&rp, l->u.s, 2, rm, 0) == 0 && rm[0].rm_so == 0) {
262 if (rm[1].rm_so >= 0) {
263 *(l->u.s + rm[1].rm_eo) = '\0';
264 v = make_str(l->u.s + rm[1].rm_so);
265
266 } else {
267 v = make_int(rm[0].rm_eo - rm[0].rm_so);
268 }
269 } else {
270 if (rp.re_nsub == 0) {
271 v = make_int(0);
272 } else {
273 v = make_str("");
274 }
275 }
276
277 /* free arguments and pattern buffer */
278 free_value(l);
279 free_value(r);
280 regfree(&rp);
281
282 l = v;
283 }
284
285 return l;
286}
287
288/* Parse and evaluate multiplication and division expressions */
289struct val *
290eval4(void)
291{
292 const char *errstr;
293 struct val *l, *r;
294 enum token op;
295 volatile int64_t res;
296
297 l = eval5();
298 while ((op = token) == MUL || op == DIV || op == MOD) {
299 nexttoken(0);
300 r = eval5();
301
302 if (!to_integer(l, &errstr))
303 errx(2, "number \"%s\" is %s", l->u.s, errstr);
304 if (!to_integer(r, &errstr))
305 errx(2, "number \"%s\" is %s", r->u.s, errstr);
306
307 if (op == MUL) {
308 res = l->u.i * r->u.i;
309 if (r->u.i != 0 && l->u.i != res / r->u.i)
310 errx(3, "overflow");
311 l->u.i = res;
312 } else {
313 if (r->u.i == 0) {
314 errx(2, "division by zero");
315 }
316 if (op == DIV) {
317 if (l->u.i != INT64_MIN(-0x7fffffffffffffffLL - 1) || r->u.i != -1)
318 l->u.i /= r->u.i;
319 else
320 errx(3, "overflow");
321 } else {
322 if (l->u.i != INT64_MIN(-0x7fffffffffffffffLL - 1) || r->u.i != -1)
323 l->u.i %= r->u.i;
324 else
325 l->u.i = 0;
326 }
327 }
328
329 free_value(r);
330 }
331
332 return l;
333}
334
335/* Parse and evaluate addition and subtraction expressions */
336struct val *
337eval3(void)
338{
339 const char *errstr;
340 struct val *l, *r;
341 enum token op;
342 volatile int64_t res;
343
344 l = eval4();
345 while ((op = token) == ADD || op == SUB) {
346 nexttoken(0);
347 r = eval4();
348
349 if (!to_integer(l, &errstr))
350 errx(2, "number \"%s\" is %s", l->u.s, errstr);
351 if (!to_integer(r, &errstr))
352 errx(2, "number \"%s\" is %s", r->u.s, errstr);
353
354 if (op == ADD) {
355 res = l->u.i + r->u.i;
356 if ((l->u.i > 0 && r->u.i > 0 && res <= 0) ||
357 (l->u.i < 0 && r->u.i < 0 && res >= 0))
358 errx(3, "overflow");
359 l->u.i = res;
360 } else {
361 res = l->u.i - r->u.i;
362 if ((l->u.i >= 0 && r->u.i < 0 && res <= 0) ||
363 (l->u.i < 0 && r->u.i > 0 && res >= 0))
364 errx(3, "overflow");
365 l->u.i = res;
366 }
367
368 free_value(r);
369 }
370
371 return l;
372}
373
374/* Parse and evaluate comparison expressions */
375struct val *
376eval2(void)
377{
378 struct val *l, *r;
379 enum token op;
380 int64_t v = 0, li, ri;
381
382 l = eval3();
383 while ((op = token) == EQ || op == NE || op == LT || op == GT ||
7
Assuming the condition is false
8
Assuming 'op' is not equal to NE
9
Assuming 'op' is not equal to LT
10
Assuming 'op' is not equal to GT
13
Loop condition is true. Entering loop body
23
Assuming the condition is false
24
Assuming 'op' is not equal to NE
25
Assuming 'op' is not equal to LT
26
Assuming 'op' is not equal to GT
29
Loop condition is false. Execution continues on line 444
384 op == LE || op == GE) {
11
Assuming 'op' is not equal to LE
12
Assuming 'op' is equal to GE
27
Assuming 'op' is not equal to LE
28
Assuming 'op' is not equal to GE
385 nexttoken(0);
386 r = eval3();
387
388 if (is_integer(l, &li, NULL((void *)0)) && is_integer(r, &ri, NULL((void *)0))) {
14
Taking true branch
389 switch (op) {
15
Control jumps to 'case GE:' at line 393
390 case GT:
391 v = (li > ri);
392 break;
393 case GE:
394 v = (li >= ri);
16
Assuming 'li' is < 'ri'
395 break;
17
Execution continues on line 439
396 case LT:
397 v = (li < ri);
398 break;
399 case LE:
400 v = (li <= ri);
401 break;
402 case EQ:
403 v = (li == ri);
404 break;
405 case NE:
406 v = (li != ri);
407 break;
408 default:
409 break;
410 }
411 } else {
412 to_string(l);
413 to_string(r);
414
415 switch (op) {
416 case GT:
417 v = (strcoll(l->u.s, r->u.s) > 0);
418 break;
419 case GE:
420 v = (strcoll(l->u.s, r->u.s) >= 0);
421 break;
422 case LT:
423 v = (strcoll(l->u.s, r->u.s) < 0);
424 break;
425 case LE:
426 v = (strcoll(l->u.s, r->u.s) <= 0);
427 break;
428 case EQ:
429 v = (strcoll(l->u.s, r->u.s) == 0);
430 break;
431 case NE:
432 v = (strcoll(l->u.s, r->u.s) != 0);
433 break;
434 default:
435 break;
436 }
437 }
438
439 free_value(l);
440 free_value(r);
441 l = make_int(v);
18
Calling 'make_int'
22
Returned allocated memory
442 }
443
444 return l;
445}
446
447/* Parse and evaluate & expressions */
448struct val *
449eval1(void)
450{
451 struct val *l, *r;
452
453 l = eval2();
6
Calling 'eval2'
30
Returned allocated memory
454 while (token == AND) {
31
Assuming 'token' is not equal to AND
32
Loop condition is false. Execution continues on line 467
455 nexttoken(0);
456 r = eval2();
457
458 if (is_zero_or_null(l) || is_zero_or_null(r)) {
459 free_value(l);
460 free_value(r);
461 l = make_int(0);
462 } else {
463 free_value(r);
464 }
465 }
466
467 return l;
468}
469
470/* Parse and evaluate | expressions */
471struct val *
472eval0(void)
473{
474 struct val *l, *r;
475
476 l = eval1();
5
Calling 'eval1'
33
Returned allocated memory
477 while (token == OR) {
34
Assuming 'token' is not equal to OR
35
Loop condition is false. Execution continues on line 489
478 nexttoken(0);
479 r = eval1();
480
481 if (is_zero_or_null(l)) {
482 free_value(l);
483 l = r;
484 } else {
485 free_value(r);
486 }
487 }
488
489 return l;
490}
491
492
493int
494main(int argc, char *argv[])
495{
496 struct val *vp;
497
498 if (pledge("stdio", NULL((void *)0)) == -1)
1
Assuming the condition is false
2
Taking false branch
499 err(2, "pledge");
500
501 if (argc > 1 && !strcmp(argv[1], "--"))
3
Assuming 'argc' is <= 1
502 argv++;
503
504 av = argv + 1;
505
506 nexttoken(0);
507 vp = eval0();
4
Calling 'eval0'
36
Returned allocated memory
508
509 if (token != EOI)
37
Assuming 'token' is equal to EOI
38
Taking false branch
510 error();
511
512 if (vp->type
38.1
Field 'type' is equal to integer
== integer)
39
Taking true branch
513 printf("%lld\n", vp->u.i);
514 else
515 printf("%s\n", vp->u.s);
516
517 return is_zero_or_null(vp);
40
Potential leak of memory pointed to by 'vp'
518}