clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name parse.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/usr.sbin/dhcpd/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/usr.sbin/dhcpd/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/usr.sbin/dhcpd/parse.c
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | |
25 | |
26 | |
27 | |
28 | |
29 | |
30 | |
31 | |
32 | |
33 | |
34 | |
35 | |
36 | |
37 | |
38 | |
39 | |
40 | |
41 | |
42 | |
43 | #include <sys/types.h> |
44 | #include <sys/socket.h> |
45 | |
46 | #include <net/if.h> |
47 | |
48 | #include <netinet/in.h> |
49 | #include <netinet/if_ether.h> |
50 | |
51 | #include <ctype.h> |
52 | #include <errno.h> |
53 | #include <stdarg.h> |
54 | #include <stdint.h> |
55 | #include <stdio.h> |
56 | #include <stdlib.h> |
57 | #include <string.h> |
58 | #include <syslog.h> |
59 | #include <time.h> |
60 | #include <unistd.h> |
61 | |
62 | #include "dhcp.h" |
63 | #include "tree.h" |
64 | #include "dhcpd.h" |
65 | #include "dhctoken.h" |
66 | #include "log.h" |
67 | |
68 | |
69 | |
70 | |
71 | |
72 | |
73 | |
74 | |
75 | |
76 | |
77 | |
78 | |
79 | |
80 | |
81 | |
82 | |
83 | void |
84 | skip_to_semi(FILE *cfile) |
85 | { |
86 | int token; |
87 | char *val; |
88 | int brace_count = 0; |
89 | |
90 | do { |
91 | token = peek_token(&val, cfile); |
92 | if (token == '}') { |
93 | if (brace_count) { |
94 | token = next_token(&val, cfile); |
95 | if (!--brace_count) |
96 | return; |
97 | } else |
98 | return; |
99 | } else if (token == '{') { |
100 | brace_count++; |
101 | } else if (token == ';' && !brace_count) { |
102 | token = next_token(&val, cfile); |
103 | return; |
104 | } else if (token == '\n') { |
105 | |
106 | |
107 | |
108 | |
109 | |
110 | |
111 | token = next_token(&val, cfile); |
112 | return; |
113 | } |
114 | token = next_token(&val, cfile); |
115 | } while (token != EOF); |
116 | } |
117 | |
118 | int |
119 | parse_semi(FILE *cfile) |
120 | { |
121 | int token; |
122 | char *val; |
123 | |
124 | token = next_token(&val, cfile); |
125 | if (token != ';') { |
126 | parse_warn("semicolon expected."); |
127 | skip_to_semi(cfile); |
128 | return (0); |
129 | } |
130 | return (1); |
131 | } |
132 | |
133 | |
134 | |
135 | |
136 | char * |
137 | parse_string(FILE *cfile) |
138 | { |
139 | char *val, *s; |
140 | int token; |
141 | |
142 | token = next_token(&val, cfile); |
143 | if (token != TOK_STRING) { |
144 | parse_warn("filename must be a string"); |
145 | skip_to_semi(cfile); |
146 | return (NULL); |
147 | } |
148 | s = strdup(val); |
149 | if (s == NULL) |
150 | fatalx("no memory for string %s.", val); |
151 | |
152 | if (!parse_semi(cfile)) { |
153 | free(s); |
154 | return (NULL); |
155 | } |
156 | return (s); |
157 | } |
158 | |
159 | |
160 | |
161 | |
162 | char * |
163 | parse_host_name(FILE *cfile) |
164 | { |
165 | char *val, *s, *t; |
166 | int token, len = 0; |
167 | pair c = NULL; |
168 | |
169 | |
170 | do { |
171 | |
172 | token = next_token(&val, cfile); |
173 | if (!is_identifier(token)) { |
174 | parse_warn("expecting an identifier in hostname"); |
175 | skip_to_semi(cfile); |
176 | return (NULL); |
177 | } |
178 | |
179 | s = strdup(val); |
180 | if (s == NULL) |
181 | fatalx("can't allocate temp space for hostname."); |
182 | c = cons((caddr_t) s, c); |
183 | len += strlen(s) + 1; |
184 | |
185 | |
186 | |
187 | |
188 | token = peek_token(&val, cfile); |
189 | if (token == '.') |
190 | token = next_token(&val, cfile); |
191 | } while (token == '.'); |
192 | |
193 | |
194 | if (!(s = malloc(len))) |
195 | fatalx("can't allocate space for hostname."); |
196 | t = s + len; |
197 | *--t = '\0'; |
198 | while (c) { |
199 | pair cdr = c->cdr; |
200 | int l = strlen((char *)c->car); |
201 | |
202 | t -= l; |
203 | memcpy(t, (char *)c->car, l); |
204 | |
205 | free(c->car); |
206 | free(c); |
207 | c = cdr; |
208 | if (t != s) |
209 | *--t = '.'; |
210 | } |
211 | return (s); |
212 | } |
213 | |
214 | |
215 | |
216 | |
217 | |
218 | void |
219 | parse_hardware_param(FILE *cfile, struct hardware *hardware) |
220 | { |
221 | char *val; |
222 | int token, hlen = 0; |
223 | unsigned char *e, *t = NULL; |
224 | |
225 | token = next_token(&val, cfile); |
226 | switch (token) { |
| 1 | Control jumps to 'case 261:' at line 227 | |
|
227 | case TOK_ETHERNET: |
228 | hardware->htype = HTYPE_ETHER; |
229 | break; |
| 2 | | Execution continues on line 241 | |
|
230 | case TOK_IPSEC_TUNNEL: |
231 | hardware->htype = HTYPE_IPSEC_TUNNEL; |
232 | break; |
233 | default: |
234 | parse_warn("expecting a network hardware type"); |
235 | skip_to_semi(cfile); |
236 | return; |
237 | } |
238 | |
239 | |
240 | |
241 | if (hardware->htype == HTYPE_ETHER) { |
| |
242 | token = peek_token(&val, cfile); |
243 | hlen = sizeof(struct ether_addr); |
244 | if ((e = malloc(hlen)) == NULL) |
| 4 | | Assuming the condition is false | |
|
| |
245 | fatalx("can't allocate space for ethernet address."); |
246 | if (ether_hostton(val, (struct ether_addr *)e) == 0) { |
| 6 | | Assuming the condition is false | |
|
| |
247 | (void)next_token(&val, cfile); |
248 | t = e; |
249 | } else |
250 | free(e); |
251 | } |
252 | |
253 | |
254 | |
255 | |
256 | |
257 | |
258 | |
259 | |
260 | |
261 | |
262 | if (!t) |
| |
263 | t = parse_numeric_aggregate(cfile, NULL, &hlen, ':', 16, 8); |
| 9 | | Calling 'parse_numeric_aggregate' | |
|
264 | |
265 | if (!t) |
266 | return; |
267 | if (hlen > sizeof(hardware->haddr)) { |
268 | free(t); |
269 | parse_warn("hardware address too long"); |
270 | } else { |
271 | hardware->hlen = hlen; |
272 | memcpy((unsigned char *)&hardware->haddr[0], t, |
273 | hardware->hlen); |
274 | if (hlen < sizeof(hardware->haddr)) |
275 | memset(&hardware->haddr[hlen], 0, |
276 | sizeof(hardware->haddr) - hlen); |
277 | free(t); |
278 | } |
279 | |
280 | token = next_token(&val, cfile); |
281 | if (token != ';') { |
282 | parse_warn("expecting semicolon."); |
283 | skip_to_semi(cfile); |
284 | } |
285 | } |
286 | |
287 | |
288 | |
289 | |
290 | void |
291 | parse_lease_time(FILE *cfile, time_t *timep) |
292 | { |
293 | const char *errstr; |
294 | char *val; |
295 | uint32_t value; |
296 | int token; |
297 | |
298 | token = next_token(&val, cfile); |
299 | |
300 | value = strtonum(val, 0, UINT32_MAX, &errstr); |
301 | if (errstr) { |
302 | parse_warn("lease time is %s: %s", errstr, val); |
303 | skip_to_semi(cfile); |
304 | return; |
305 | } |
306 | |
307 | *timep = value; |
308 | |
309 | parse_semi(cfile); |
310 | } |
311 | |
312 | |
313 | |
314 | |
315 | |
316 | |
317 | |
318 | |
319 | |
320 | unsigned char * |
321 | parse_numeric_aggregate(FILE *cfile, unsigned char *buf, int *max, |
322 | int separator, int base, int size) |
323 | { |
324 | char *val, *t; |
325 | int token, count = 0; |
326 | unsigned char *bufp = buf, *s = NULL; |
327 | pair c = NULL; |
328 | |
329 | if (!bufp && *max) { |
| |
330 | bufp = malloc(*max * size / 8); |
| |
331 | if (!bufp) |
| 12 | | Assuming 'bufp' is non-null | |
|
| |
332 | fatalx("can't allocate space for numeric aggregate"); |
333 | } else |
334 | s = bufp; |
335 | |
336 | do { |
337 | if (count) { |
| |
338 | token = peek_token(&val, cfile); |
339 | if (token != separator) { |
340 | if (!*max) |
341 | break; |
342 | if (token != '{' && token != '}') |
343 | token = next_token(&val, cfile); |
344 | parse_warn("too few numbers."); |
345 | if (token != ';') |
346 | skip_to_semi(cfile); |
347 | return (NULL); |
348 | } |
349 | token = next_token(&val, cfile); |
350 | } |
351 | token = next_token(&val, cfile); |
352 | |
353 | if (token == EOF) { |
| 15 | | Assuming the condition is false | |
|
| |
354 | parse_warn("unexpected end of file"); |
355 | break; |
356 | } |
357 | if (token != TOK_NUMBER && token != TOK_NUMBER_OR_NAME) { |
| 17 | | Assuming 'token' is not equal to TOK_NUMBER | |
|
| 18 | | Assuming 'token' is not equal to TOK_NUMBER_OR_NAME | |
|
| |
358 | parse_warn("expecting numeric value."); |
| 20 | | Potential leak of memory pointed to by 'bufp' |
|
359 | skip_to_semi(cfile); |
360 | return (NULL); |
361 | } |
362 | |
363 | |
364 | |
365 | |
366 | if (s) { |
367 | convert_num(s, val, base, size); |
368 | s += size / 8; |
369 | } else { |
370 | t = strdup(val); |
371 | if (t == NULL) |
372 | fatalx("no temp space for number."); |
373 | c = cons(t, c); |
374 | } |
375 | } while (++count != *max); |
376 | |
377 | |
378 | if (c) { |
379 | bufp = malloc(count * size / 8); |
380 | if (!bufp) |
381 | fatalx("can't allocate space for numeric aggregate."); |
382 | s = bufp + count - size / 8; |
383 | *max = count; |
384 | } |
385 | while (c) { |
386 | pair cdr = c->cdr; |
387 | convert_num(s, (char *)c->car, base, size); |
388 | s -= size / 8; |
389 | |
390 | free(c->car); |
391 | free(c); |
392 | c = cdr; |
393 | } |
394 | return (bufp); |
395 | } |
396 | |
397 | void |
398 | convert_num(unsigned char *buf, char *str, int base, int size) |
399 | { |
400 | int negative = 0, tval, max; |
401 | u_int32_t val = 0; |
402 | char *ptr = str; |
403 | |
404 | if (*ptr == '-') { |
405 | negative = 1; |
406 | ptr++; |
407 | } |
408 | |
409 | |
410 | if (!base) { |
411 | if (ptr[0] == '0') { |
412 | if (ptr[1] == 'x') { |
413 | base = 16; |
414 | ptr += 2; |
415 | } else if (isascii((unsigned char)ptr[1]) && |
416 | isdigit((unsigned char)ptr[1])) { |
417 | base = 8; |
418 | ptr += 1; |
419 | } else |
420 | base = 10; |
421 | } else |
422 | base = 10; |
423 | } |
424 | |
425 | do { |
426 | tval = *ptr++; |
427 | |
428 | if (tval >= 'a') |
429 | tval = tval - 'a' + 10; |
430 | else if (tval >= 'A') |
431 | tval = tval - 'A' + 10; |
432 | else if (tval >= '0') |
433 | tval -= '0'; |
434 | else { |
435 | log_warnx("Bogus number: %s.", str); |
436 | break; |
437 | } |
438 | if (tval >= base) { |
439 | log_warnx("Bogus number: %s: digit %d not in base %d", |
440 | str, tval, base); |
441 | break; |
442 | } |
443 | val = val * base + tval; |
444 | } while (*ptr); |
445 | |
446 | if (negative) |
447 | max = (1 << (size - 1)); |
448 | else |
449 | max = (1 << (size - 1)) + ((1 << (size - 1)) - 1); |
450 | if (val > max) { |
451 | switch (base) { |
452 | case 8: |
453 | log_warnx("value %s%o exceeds max (%d) for precision.", |
454 | negative ? "-" : "", val, max); |
455 | break; |
456 | case 16: |
457 | log_warnx("value %s%x exceeds max (%d) for precision.", |
458 | negative ? "-" : "", val, max); |
459 | break; |
460 | default: |
461 | log_warnx("value %s%u exceeds max (%d) for precision.", |
462 | negative ? "-" : "", val, max); |
463 | break; |
464 | } |
465 | } |
466 | |
467 | if (negative) { |
468 | switch (size) { |
469 | case 8: |
470 | *buf = -(unsigned long)val; |
471 | break; |
472 | case 16: |
473 | putShort(buf, -(unsigned long)val); |
474 | break; |
475 | case 32: |
476 | putLong(buf, -(unsigned long)val); |
477 | break; |
478 | default: |
479 | log_warnx("Unexpected integer size: %d", size); |
480 | break; |
481 | } |
482 | } else { |
483 | switch (size) { |
484 | case 8: |
485 | *buf = (u_int8_t)val; |
486 | break; |
487 | case 16: |
488 | putUShort(buf, (u_int16_t)val); |
489 | break; |
490 | case 32: |
491 | putULong(buf, val); |
492 | break; |
493 | default: |
494 | log_warnx("Unexpected integer size: %d", size); |
495 | break; |
496 | } |
497 | } |
498 | } |
499 | |
500 | |
501 | |
502 | |
503 | |
504 | |
505 | |
506 | |
507 | |
508 | time_t |
509 | parse_date(FILE *cfile) |
510 | { |
511 | struct tm tm; |
512 | char timestr[26]; |
513 | char *val, *p; |
514 | size_t n; |
515 | time_t guess; |
516 | int token; |
517 | |
518 | memset(timestr, 0, sizeof(timestr)); |
519 | |
520 | do { |
521 | token = peek_token(NULL, cfile); |
522 | switch (token) { |
523 | case TOK_NAME: |
524 | case TOK_NUMBER: |
525 | case TOK_NUMBER_OR_NAME: |
526 | case '/': |
527 | case ':': |
528 | token = next_token(&val, cfile); |
529 | n = strlcat(timestr, val, sizeof(timestr)); |
530 | if (n >= sizeof(timestr)) { |
531 | |
532 | parse_warn("time string too long"); |
533 | skip_to_semi(cfile); |
534 | return (0); |
535 | } |
536 | break; |
537 | case';': |
538 | break; |
539 | default: |
540 | parse_warn("invalid time string"); |
541 | skip_to_semi(cfile); |
542 | return (0); |
543 | } |
544 | } while (token != ';'); |
545 | |
546 | parse_semi(cfile); |
547 | |
548 | memset(&tm, 0, sizeof(tm)); |
549 | p = strptime(timestr, DB_TIMEFMT, &tm); |
550 | if (p == NULL || *p != '\0') { |
551 | p = strptime(timestr, OLD_DB_TIMEFMT, &tm); |
552 | if (p == NULL || *p != '\0') { |
553 | parse_warn("unparseable time string"); |
554 | return (0); |
555 | } |
556 | } |
557 | |
558 | guess = timegm(&tm); |
559 | if (guess == -1) { |
560 | parse_warn("time could not be represented"); |
561 | return (0); |
562 | } |
563 | |
564 | return (guess); |
565 | } |
566 | |
567 | int warnings_occurred; |
568 | |
569 | int |
570 | parse_warn(char *fmt, ...) |
571 | { |
572 | static char fbuf[1024]; |
573 | static char mbuf[1024]; |
574 | static char spaces[81]; |
575 | va_list list; |
576 | int i; |
577 | |
578 | snprintf(fbuf, sizeof(fbuf), "%s line %d: %s", tlname, lexline, mbuf); |
579 | va_start(list, fmt); |
580 | vsnprintf(mbuf, sizeof(mbuf), fbuf, list); |
581 | va_end(list); |
582 | |
583 | log_warnx("%s", mbuf); |
584 | log_warnx("%s", token_line); |
585 | if (lexchar < sizeof(spaces)) { |
586 | memset(spaces, 0, sizeof(spaces)); |
587 | for (i = 0; i < lexchar - 1; i++) { |
588 | if (token_line[i] == '\t') |
589 | spaces[i] = '\t'; |
590 | else |
591 | spaces[i] = ' '; |
592 | } |
593 | } |
594 | log_warnx("%s^", spaces); |
595 | |
596 | warnings_occurred = 1; |
597 | |
598 | return (0); |
599 | } |