clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name test.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/test/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/test/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/test/test.c
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | #include <sys/types.h> |
15 | #include <sys/stat.h> |
16 | #include <unistd.h> |
17 | #include <ctype.h> |
18 | #include <errno.h> |
19 | #include <limits.h> |
20 | #include <stdio.h> |
21 | #include <stdlib.h> |
22 | #include <string.h> |
23 | #include <err.h> |
24 | |
25 | |
26 | |
27 | |
28 | |
29 | |
30 | |
31 | |
32 | |
33 | |
34 | |
35 | |
36 | |
37 | |
38 | |
39 | |
40 | |
41 | |
42 | enum token { |
43 | EOI, |
44 | FILRD, |
45 | FILWR, |
46 | FILEX, |
47 | FILEXIST, |
48 | FILREG, |
49 | FILDIR, |
50 | FILCDEV, |
51 | FILBDEV, |
52 | FILFIFO, |
53 | FILSOCK, |
54 | FILSYM, |
55 | FILGZ, |
56 | FILTT, |
57 | FILSUID, |
58 | FILSGID, |
59 | FILSTCK, |
60 | FILNT, |
61 | FILOT, |
62 | FILEQ, |
63 | FILUID, |
64 | FILGID, |
65 | STREZ, |
66 | STRNZ, |
67 | STREQ, |
68 | STRNE, |
69 | STRLT, |
70 | STRGT, |
71 | INTEQ, |
72 | INTNE, |
73 | INTGE, |
74 | INTGT, |
75 | INTLE, |
76 | INTLT, |
77 | UNOT, |
78 | BAND, |
79 | BOR, |
80 | LPAREN, |
81 | RPAREN, |
82 | OPERAND |
83 | }; |
84 | |
85 | enum token_types { |
86 | UNOP, |
87 | BINOP, |
88 | BUNOP, |
89 | BBINOP, |
90 | PAREN |
91 | }; |
92 | |
93 | struct t_op { |
94 | const char *op_text; |
95 | short op_num, op_type; |
96 | } const ops [] = { |
97 | {"-r", FILRD, UNOP}, |
98 | {"-w", FILWR, UNOP}, |
99 | {"-x", FILEX, UNOP}, |
100 | {"-e", FILEXIST,UNOP}, |
101 | {"-f", FILREG, UNOP}, |
102 | {"-d", FILDIR, UNOP}, |
103 | {"-c", FILCDEV,UNOP}, |
104 | {"-b", FILBDEV,UNOP}, |
105 | {"-p", FILFIFO,UNOP}, |
106 | {"-u", FILSUID,UNOP}, |
107 | {"-g", FILSGID,UNOP}, |
108 | {"-k", FILSTCK,UNOP}, |
109 | {"-s", FILGZ, UNOP}, |
110 | {"-t", FILTT, UNOP}, |
111 | {"-z", STREZ, UNOP}, |
112 | {"-n", STRNZ, UNOP}, |
113 | {"-h", FILSYM, UNOP}, |
114 | {"-O", FILUID, UNOP}, |
115 | {"-G", FILGID, UNOP}, |
116 | {"-L", FILSYM, UNOP}, |
117 | {"-S", FILSOCK,UNOP}, |
118 | {"=", STREQ, BINOP}, |
119 | {"!=", STRNE, BINOP}, |
120 | {"<", STRLT, BINOP}, |
121 | {">", STRGT, BINOP}, |
122 | {"-eq", INTEQ, BINOP}, |
123 | {"-ne", INTNE, BINOP}, |
124 | {"-ge", INTGE, BINOP}, |
125 | {"-gt", INTGT, BINOP}, |
126 | {"-le", INTLE, BINOP}, |
127 | {"-lt", INTLT, BINOP}, |
128 | {"-nt", FILNT, BINOP}, |
129 | {"-ot", FILOT, BINOP}, |
130 | {"-ef", FILEQ, BINOP}, |
131 | {"!", UNOT, BUNOP}, |
132 | {"-a", BAND, BBINOP}, |
133 | {"-o", BOR, BBINOP}, |
134 | {"(", LPAREN, PAREN}, |
135 | {")", RPAREN, PAREN}, |
136 | {0, 0, 0} |
137 | }; |
138 | |
139 | char **t_wp; |
140 | struct t_op const *t_wp_op; |
141 | |
142 | static enum token t_lex(char *); |
143 | static enum token t_lex_type(char *); |
144 | static int oexpr(enum token n); |
145 | static int aexpr(enum token n); |
146 | static int nexpr(enum token n); |
147 | static int binop(void); |
148 | static int primary(enum token n); |
149 | static const char *getnstr(const char *, int *, size_t *); |
150 | static int intcmp(const char *, const char *); |
151 | static int filstat(char *nm, enum token mode); |
152 | static int getn(const char *s); |
153 | static int newerf(const char *, const char *); |
154 | static int olderf(const char *, const char *); |
155 | static int equalf(const char *, const char *); |
156 | static __dead void syntax(const char *op, char *msg); |
157 | |
158 | int |
159 | main(int argc, char *argv[]) |
160 | { |
161 | extern char *__progname; |
162 | int res; |
163 | |
164 | if (pledge("stdio rpath", NULL) == -1) |
| 1 | Assuming the condition is false | |
|
| |
165 | err(2, "pledge"); |
166 | |
167 | if (strcmp(__progname, "[") == 0) { |
| 3 | | Assuming the condition is false | |
|
| |
168 | if (strcmp(argv[--argc], "]")) |
169 | errx(2, "missing ]"); |
170 | argv[argc] = NULL; |
171 | } |
172 | |
173 | |
174 | switch (argc) { |
| 5 | | Control jumps to 'case 5:' at line 193 | |
|
175 | case 1: |
176 | return 1; |
177 | case 2: |
178 | return (*argv[1] == '\0'); |
179 | case 3: |
180 | if (argv[1][0] == '!' && argv[1][1] == '\0') { |
181 | return !(*argv[2] == '\0'); |
182 | } |
183 | break; |
184 | case 4: |
185 | if (argv[1][0] != '!' || argv[1][1] != '\0') { |
186 | if (t_lex(argv[2]), |
187 | t_wp_op && t_wp_op->op_type == BINOP) { |
188 | t_wp = &argv[1]; |
189 | return (binop() == 0); |
190 | } |
191 | } |
192 | break; |
193 | case 5: |
194 | if (argv[1][0] == '!' && argv[1][1] == '\0') { |
| 6 | | Assuming the condition is true | |
|
| 7 | | Assuming the condition is true | |
|
| |
195 | if (t_lex(argv[3]), |
| |
196 | t_wp_op && t_wp_op->op_type == BINOP) { |
| 9 | | Assuming field 'op_type' is equal to BINOP | |
|
197 | t_wp = &argv[2]; |
198 | return !(binop() == 0); |
| |
199 | } |
200 | } |
201 | break; |
202 | } |
203 | |
204 | t_wp = &argv[1]; |
205 | res = !oexpr(t_lex(*t_wp)); |
206 | |
207 | if (*t_wp != NULL && *++t_wp != NULL) |
208 | syntax(*t_wp, "unknown operand"); |
209 | |
210 | return res; |
211 | } |
212 | |
213 | static __dead void |
214 | syntax(const char *op, char *msg) |
215 | { |
216 | if (op && *op) |
217 | errx(2, "%s: %s", op, msg); |
218 | else |
219 | errx(2, "%s", msg); |
220 | } |
221 | |
222 | static int |
223 | oexpr(enum token n) |
224 | { |
225 | int res; |
226 | |
227 | res = aexpr(n); |
228 | if (t_lex(*++t_wp) == BOR) |
229 | return oexpr(t_lex(*++t_wp)) || res; |
230 | t_wp--; |
231 | return res; |
232 | } |
233 | |
234 | static int |
235 | aexpr(enum token n) |
236 | { |
237 | int res; |
238 | |
239 | res = nexpr(n); |
240 | if (t_lex(*++t_wp) == BAND) |
241 | return aexpr(t_lex(*++t_wp)) && res; |
242 | t_wp--; |
243 | return res; |
244 | } |
245 | |
246 | static int |
247 | nexpr(enum token n) |
248 | { |
249 | if (n == UNOT) |
250 | return !nexpr(t_lex(*++t_wp)); |
251 | return primary(n); |
252 | } |
253 | |
254 | static int |
255 | primary(enum token n) |
256 | { |
257 | int res; |
258 | |
259 | if (n == EOI) |
260 | syntax(NULL, "argument expected"); |
261 | if (n == LPAREN) { |
262 | res = oexpr(t_lex(*++t_wp)); |
263 | if (t_lex(*++t_wp) != RPAREN) |
264 | syntax(NULL, "closing paren expected"); |
265 | return res; |
266 | } |
267 | |
268 | |
269 | |
270 | |
271 | if(t_lex_type(t_wp[1]) == BINOP) { |
272 | t_lex(t_wp[1]); |
273 | if (t_wp_op && t_wp_op->op_type == BINOP) |
274 | return binop(); |
275 | } |
276 | |
277 | if (t_wp_op && t_wp_op->op_type == UNOP) { |
278 | |
279 | if (*++t_wp == NULL) |
280 | syntax(t_wp_op->op_text, "argument expected"); |
281 | switch (n) { |
282 | case STREZ: |
283 | return strlen(*t_wp) == 0; |
284 | case STRNZ: |
285 | return strlen(*t_wp) != 0; |
286 | case FILTT: |
287 | return isatty(getn(*t_wp)); |
288 | default: |
289 | return filstat(*t_wp, n); |
290 | } |
291 | } |
292 | |
293 | return strlen(*t_wp) > 0; |
294 | } |
295 | |
296 | static const char * |
297 | getnstr(const char *s, int *signum, size_t *len) |
298 | { |
299 | const char *p, *start; |
300 | |
301 | |
302 | p = s; |
303 | while (isspace((unsigned char)*p)) |
304 | p++; |
305 | |
306 | |
307 | if (*p == '-') { |
308 | *signum = -1; |
309 | p++; |
310 | } else { |
311 | *signum = 1; |
312 | if (*p == '+') |
313 | p++; |
314 | } |
315 | |
316 | |
317 | while (*p == '0' && isdigit((unsigned char)p[1])) |
318 | p++; |
319 | |
320 | |
321 | if (*p == '0') |
322 | *signum = 1; |
323 | |
324 | start = p; |
325 | while (isdigit((unsigned char)*p)) |
326 | p++; |
327 | *len = p - start; |
328 | |
329 | |
330 | while (isspace((unsigned char)*p)) |
331 | p++; |
332 | |
333 | |
334 | if (*p != '\0' || *start == '\0') |
335 | errx(2, "%s: invalid", s); |
336 | |
337 | return start; |
338 | } |
339 | |
340 | static int |
341 | intcmp(const char *opnd1, const char *opnd2) |
342 | { |
343 | const char *p1, *p2; |
344 | size_t len1, len2; |
345 | int c, sig1, sig2; |
346 | |
347 | p1 = getnstr(opnd1, &sig1, &len1); |
348 | p2 = getnstr(opnd2, &sig2, &len2); |
349 | |
350 | if (sig1 != sig2) |
351 | c = sig1; |
352 | else if (len1 != len2) |
353 | c = (len1 < len2) ? -sig1 : sig1; |
354 | else |
355 | c = strncmp(p1, p2, len1) * sig1; |
356 | |
357 | return c; |
358 | } |
359 | |
360 | static int |
361 | binop(void) |
362 | { |
363 | const char *opnd1, *opnd2; |
364 | struct t_op const *op; |
365 | |
366 | opnd1 = *t_wp; |
367 | (void) t_lex(*++t_wp); |
| |
| |
368 | op = t_wp_op; |
| 20 | | Null pointer value stored to 'op' | |
|
369 | |
370 | if ((opnd2 = *++t_wp) == NULL) |
| 21 | | Assuming the condition is true | |
|
| |
371 | syntax(op->op_text, "argument expected"); |
| 23 | | Access to field 'op_text' results in a dereference of a null pointer (loaded from variable 'op') |
|
372 | |
373 | switch (op->op_num) { |
374 | case STREQ: |
375 | return strcmp(opnd1, opnd2) == 0; |
376 | case STRNE: |
377 | return strcmp(opnd1, opnd2) != 0; |
378 | case STRLT: |
379 | return strcmp(opnd1, opnd2) < 0; |
380 | case STRGT: |
381 | return strcmp(opnd1, opnd2) > 0; |
382 | case INTEQ: |
383 | return intcmp(opnd1, opnd2) == 0; |
384 | case INTNE: |
385 | return intcmp(opnd1, opnd2) != 0; |
386 | case INTGE: |
387 | return intcmp(opnd1, opnd2) >= 0; |
388 | case INTGT: |
389 | return intcmp(opnd1, opnd2) > 0; |
390 | case INTLE: |
391 | return intcmp(opnd1, opnd2) <= 0; |
392 | case INTLT: |
393 | return intcmp(opnd1, opnd2) < 0; |
394 | case FILNT: |
395 | return newerf(opnd1, opnd2); |
396 | case FILOT: |
397 | return olderf(opnd1, opnd2); |
398 | case FILEQ: |
399 | return equalf(opnd1, opnd2); |
400 | } |
401 | |
402 | syntax(op->op_text, "not a binary operator"); |
403 | } |
404 | |
405 | static enum token |
406 | t_lex_type(char *s) |
407 | { |
408 | struct t_op const *op = ops; |
409 | |
410 | if (s == NULL) |
411 | return -1; |
412 | |
413 | while (op->op_text) { |
414 | if (strcmp(s, op->op_text) == 0) |
415 | return op->op_type; |
416 | op++; |
417 | } |
418 | return -1; |
419 | } |
420 | |
421 | static int |
422 | filstat(char *nm, enum token mode) |
423 | { |
424 | struct stat s; |
425 | mode_t i; |
426 | |
427 | if (mode == FILSYM) { |
428 | #ifdef S_IFLNK |
429 | if (lstat(nm, &s) == 0) { |
430 | i = S_IFLNK; |
431 | goto filetype; |
432 | } |
433 | #endif |
434 | return 0; |
435 | } |
436 | |
437 | if (stat(nm, &s) != 0) |
438 | return 0; |
439 | |
440 | switch (mode) { |
441 | case FILRD: |
442 | return access(nm, R_OK) == 0; |
443 | case FILWR: |
444 | return access(nm, W_OK) == 0; |
445 | case FILEX: |
446 | return access(nm, X_OK) == 0; |
447 | case FILEXIST: |
448 | return access(nm, F_OK) == 0; |
449 | case FILREG: |
450 | i = S_IFREG; |
451 | goto filetype; |
452 | case FILDIR: |
453 | i = S_IFDIR; |
454 | goto filetype; |
455 | case FILCDEV: |
456 | i = S_IFCHR; |
457 | goto filetype; |
458 | case FILBDEV: |
459 | i = S_IFBLK; |
460 | goto filetype; |
461 | case FILFIFO: |
462 | #ifdef S_IFIFO |
463 | i = S_IFIFO; |
464 | goto filetype; |
465 | #else |
466 | return 0; |
467 | #endif |
468 | case FILSOCK: |
469 | #ifdef S_IFSOCK |
470 | i = S_IFSOCK; |
471 | goto filetype; |
472 | #else |
473 | return 0; |
474 | #endif |
475 | case FILSUID: |
476 | i = S_ISUID; |
477 | goto filebit; |
478 | case FILSGID: |
479 | i = S_ISGID; |
480 | goto filebit; |
481 | case FILSTCK: |
482 | i = S_ISVTX; |
483 | goto filebit; |
484 | case FILGZ: |
485 | return s.st_size > 0L; |
486 | case FILUID: |
487 | return s.st_uid == geteuid(); |
488 | case FILGID: |
489 | return s.st_gid == getegid(); |
490 | default: |
491 | return 1; |
492 | } |
493 | |
494 | filetype: |
495 | return ((s.st_mode & S_IFMT) == i); |
496 | |
497 | filebit: |
498 | return ((s.st_mode & i) != 0); |
499 | } |
500 | |
501 | static enum token |
502 | t_lex(char *s) |
503 | { |
504 | struct t_op const *op = ops; |
505 | |
506 | if (s == 0) { |
| |
507 | t_wp_op = NULL; |
508 | return EOI; |
509 | } |
510 | while (op->op_text) { |
| 14 | | Loop condition is true. Entering loop body | |
|
| 17 | | Loop condition is false. Execution continues on line 517 | |
|
511 | if (strcmp(s, op->op_text) == 0) { |
| 15 | | Assuming the condition is false | |
|
| |
512 | t_wp_op = op; |
513 | return op->op_num; |
514 | } |
515 | op++; |
516 | } |
517 | t_wp_op = NULL; |
| 18 | | Null pointer value stored to 't_wp_op' | |
|
518 | return OPERAND; |
519 | } |
520 | |
521 | |
522 | static int |
523 | getn(const char *s) |
524 | { |
525 | char buf[32]; |
526 | const char *errstr, *p; |
527 | size_t len; |
528 | int r, sig; |
529 | |
530 | p = getnstr(s, &sig, &len); |
531 | if (sig != 1) |
532 | errstr = "too small"; |
533 | else if (len >= sizeof(buf)) |
534 | errstr = "too large"; |
535 | else { |
536 | strlcpy(buf, p, sizeof(buf)); |
537 | buf[len] = '\0'; |
538 | r = strtonum(buf, 0, INT_MAX, &errstr); |
539 | } |
540 | |
541 | if (errstr != NULL) |
542 | errx(2, "%s: %s", s, errstr); |
543 | |
544 | return r; |
545 | } |
546 | |
547 | static int |
548 | newerf(const char *f1, const char *f2) |
549 | { |
550 | struct stat b1, b2; |
551 | |
552 | return (stat(f1, &b1) == 0 && |
553 | stat(f2, &b2) == 0 && |
554 | b1.st_mtime > b2.st_mtime); |
555 | } |
556 | |
557 | static int |
558 | olderf(const char *f1, const char *f2) |
559 | { |
560 | struct stat b1, b2; |
561 | |
562 | return (stat(f1, &b1) == 0 && |
563 | stat(f2, &b2) == 0 && |
564 | b1.st_mtime < b2.st_mtime); |
565 | } |
566 | |
567 | static int |
568 | equalf(const char *f1, const char *f2) |
569 | { |
570 | struct stat b1, b2; |
571 | |
572 | return (stat(f1, &b1) == 0 && |
573 | stat(f2, &b2) == 0 && |
574 | b1.st_dev == b2.st_dev && |
575 | b1.st_ino == b2.st_ino); |
576 | } |