Bug Summary

File:src/usr.sbin/dhcpd/parse.c
Warning:line 357, column 4
Potential leak of memory pointed to by 'bufp'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.4 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name parse.c -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 -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -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/llvm16/lib/clang/16 -internal-isystem /usr/local/llvm16/lib/clang/16/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 -fcf-protection=branch -fno-jump-tables -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/scan/2024-01-11-140451-98009-1 -x c /usr/src/usr.sbin/dhcpd/parse.c
1/* $OpenBSD: parse.c,v 1.28 2022/01/16 21:20:25 naddy Exp $ */
2
3/* Common parser code for dhcpd and dhclient. */
4
5/*
6 * Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of The Internet Software Consortium nor the names
19 * of its contributors may be used to endorse or promote products derived
20 * from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
23 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
24 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26 * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
27 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
29 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
30 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
31 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
33 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 *
36 * This software has been written for the Internet Software Consortium
37 * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
38 * Enterprises. To learn more about the Internet Software Consortium,
39 * see ``http://www.vix.com/isc''. To learn more about Vixie
40 * Enterprises, see ``http://www.vix.com''.
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(*__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 * Skip to the semicolon ending the current statement. If we encounter
70 * braces, the matching closing brace terminates the statement. If we
71 * encounter a right brace but haven't encountered a left brace, return
72 * leaving the brace in the token buffer for the caller. If we see a
73 * semicolon and haven't seen a left brace, return. This lets us skip
74 * over:
75 *
76 * statement;
77 * statement foo bar { }
78 * statement foo bar { statement { } }
79 * statement}
80 *
81 * ...et cetera.
82 */
83void
84skip_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 * EOL only happens when parsing
107 * /etc/resolv.conf, and we treat it like a
108 * semicolon because the resolv.conf file is
109 * line-oriented.
110 */
111 token = next_token(&val, cfile);
112 return;
113 }
114 token = next_token(&val, cfile);
115 } while (token != EOF(-1));
116}
117
118int
119parse_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 * string-parameter :== STRING SEMI
135 */
136char *
137parse_string(FILE *cfile)
138{
139 char *val, *s;
140 int token;
141
142 token = next_token(&val, cfile);
143 if (token != TOK_STRING262) {
144 parse_warn("filename must be a string");
145 skip_to_semi(cfile);
146 return (NULL((void *)0));
147 }
148 s = strdup(val);
149 if (s == NULL((void *)0))
150 fatalx("no memory for string %s.", val);
151
152 if (!parse_semi(cfile)) {
153 free(s);
154 return (NULL((void *)0));
155 }
156 return (s);
157}
158
159/*
160 * hostname :== identifier | hostname DOT identifier
161 */
162char *
163parse_host_name(FILE *cfile)
164{
165 char *val, *s, *t;
166 int token, len = 0;
167 pair c = NULL((void *)0);
168
169 /* Read a dotted hostname... */
170 do {
171 /* Read a token, which should be an identifier. */
172 token = next_token(&val, cfile);
173 if (!is_identifier(token)((token) >= 256 && (token) != 262 && (token
) != 263 && (token) != (-1))
) {
174 parse_warn("expecting an identifier in hostname");
175 skip_to_semi(cfile);
176 return (NULL((void *)0));
177 }
178 /* Store this identifier... */
179 s = strdup(val);
180 if (s == NULL((void *)0))
181 fatalx("can't allocate temp space for hostname.");
182 c = cons((caddr_t) s, c);
183 len += strlen(s) + 1;
184 /*
185 * Look for a dot; if it's there, keep going, otherwise
186 * we're done.
187 */
188 token = peek_token(&val, cfile);
189 if (token == '.')
190 token = next_token(&val, cfile);
191 } while (token == '.');
192
193 /* Assemble the hostname together into a string. */
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 /* Free up temp space. */
205 free(c->car);
206 free(c);
207 c = cdr;
208 if (t != s)
209 *--t = '.';
210 }
211 return (s);
212}
213
214/*
215 * hardware-parameter :== HARDWARE ETHERNET csns SEMI
216 * csns :== NUMBER | csns COLON NUMBER
217 */
218void
219parse_hardware_param(FILE *cfile, struct hardware *hardware)
220{
221 char *val;
222 int token, hlen = 0;
223 unsigned char *e, *t = NULL((void *)0);
224
225 token = next_token(&val, cfile);
226 switch (token) {
1
Control jumps to 'case 261:' at line 227
227 case TOK_ETHERNET261:
228 hardware->htype = HTYPE_ETHER1;
229 break;
2
Execution continues on line 241
230 case TOK_IPSEC_TUNNEL336:
231 hardware->htype = HTYPE_IPSEC_TUNNEL31;
232 break;
233 default:
234 parse_warn("expecting a network hardware type");
235 skip_to_semi(cfile);
236 return;
237 }
238
239
240 /* Try looking up in /etc/ethers first. */
241 if (hardware->htype
2.1
Field 'htype' is equal to HTYPE_ETHER
== HTYPE_ETHER1) {
3
Taking true branch
242 token = peek_token(&val, cfile);
243 hlen = sizeof(struct ether_addr);
244 if ((e = malloc(hlen)) == NULL((void *)0))
4
Assuming the condition is false
5
Taking false branch
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
7
Taking false branch
247 (void)next_token(&val, cfile); /* consume token */
248 t = e;
249 } else
250 free(e);
251 }
252
253 /*
254 * Parse the hardware address information. Technically, it
255 * would make a lot of sense to restrict the length of the data
256 * we'll accept here to the length of a particular hardware
257 * address type. Unfortunately, there are some broken clients
258 * out there that put bogus data in the chaddr buffer, and we
259 * accept that data in the lease file rather than simply failing
260 * on such clients. Yuck.
261 */
262 if (!t
7.1
't' is null
)
8
Taking true branch
263 t = parse_numeric_aggregate(cfile, NULL((void *)0), &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 * lease-time :== NUMBER SEMI
289 */
290void
291parse_lease_time(FILE *cfile, time_t *timep)
292{
293 const char *errstr;
294 char *val;
295 uint32_t value;
296
297 next_token(&val, cfile);
298
299 value = strtonum(val, 0, UINT32_MAX0xffffffffU, &errstr);
300 if (errstr) {
301 parse_warn("lease time is %s: %s", errstr, val);
302 skip_to_semi(cfile);
303 return;
304 }
305
306 *timep = value;
307
308 parse_semi(cfile);
309}
310
311/*
312 * No BNF for numeric aggregates - that's defined by the caller. What
313 * this function does is to parse a sequence of numbers separated by the
314 * token specified in separator. If max is zero, any number of numbers
315 * will be parsed; otherwise, exactly max numbers are expected. Base
316 * and size tell us how to internalize the numbers once they've been
317 * tokenized.
318 */
319unsigned char *
320parse_numeric_aggregate(FILE *cfile, unsigned char *buf, int *max,
321 int separator, int base, int size)
322{
323 char *val, *t;
324 int token, count = 0;
325 unsigned char *bufp = buf, *s = NULL((void *)0);
326 pair c = NULL((void *)0);
327
328 if (!bufp
9.1
'bufp' is null
&& *max) {
10
Taking true branch
329 bufp = malloc(*max * size / 8);
11
Memory is allocated
330 if (!bufp)
12
Assuming 'bufp' is non-null
13
Taking false branch
331 fatalx("can't allocate space for numeric aggregate");
332 } else
333 s = bufp;
334
335 do {
336 if (count
13.1
'count' is 0
) {
14
Taking false branch
337 token = peek_token(&val, cfile);
338 if (token != separator) {
339 if (!*max)
340 break;
341 if (token != '{' && token != '}')
342 token = next_token(&val, cfile);
343 parse_warn("too few numbers.");
344 if (token != ';')
345 skip_to_semi(cfile);
346 return (NULL((void *)0));
347 }
348 token = next_token(&val, cfile);
349 }
350 token = next_token(&val, cfile);
351
352 if (token == EOF(-1)) {
15
Assuming the condition is false
353 parse_warn("unexpected end of file");
354 break;
355 }
356 if (token != TOK_NUMBER263 && token != TOK_NUMBER_OR_NAME264) {
16
Assuming 'token' is not equal to TOK_NUMBER
17
Assuming 'token' is not equal to TOK_NUMBER_OR_NAME
18
Taking true branch
357 parse_warn("expecting numeric value.");
19
Potential leak of memory pointed to by 'bufp'
358 skip_to_semi(cfile);
359 return (NULL((void *)0));
360 }
361 /*
362 * If we can, convert the number now; otherwise, build a
363 * linked list of all the numbers.
364 */
365 if (s) {
366 convert_num(s, val, base, size);
367 s += size / 8;
368 } else {
369 t = strdup(val);
370 if (t == NULL((void *)0))
371 fatalx("no temp space for number.");
372 c = cons(t, c);
373 }
374 } while (++count != *max);
375
376 /* If we had to cons up a list, convert it now. */
377 if (c) {
378 bufp = malloc(count * size / 8);
379 if (!bufp)
380 fatalx("can't allocate space for numeric aggregate.");
381 s = bufp + count - size / 8;
382 *max = count;
383 }
384 while (c) {
385 pair cdr = c->cdr;
386 convert_num(s, (char *)c->car, base, size);
387 s -= size / 8;
388 /* Free up temp space. */
389 free(c->car);
390 free(c);
391 c = cdr;
392 }
393 return (bufp);
394}
395
396void
397convert_num(unsigned char *buf, char *str, int base, int size)
398{
399 int negative = 0, tval, max;
400 u_int32_t val = 0;
401 char *ptr = str;
402
403 if (*ptr == '-') {
404 negative = 1;
405 ptr++;
406 }
407
408 /* If base wasn't specified, figure it out from the data. */
409 if (!base) {
410 if (ptr[0] == '0') {
411 if (ptr[1] == 'x') {
412 base = 16;
413 ptr += 2;
414 } else if (isascii((unsigned char)ptr[1]) &&
415 isdigit((unsigned char)ptr[1])) {
416 base = 8;
417 ptr += 1;
418 } else
419 base = 10;
420 } else
421 base = 10;
422 }
423
424 do {
425 tval = *ptr++;
426 /* XXX assumes ASCII... */
427 if (tval >= 'a')
428 tval = tval - 'a' + 10;
429 else if (tval >= 'A')
430 tval = tval - 'A' + 10;
431 else if (tval >= '0')
432 tval -= '0';
433 else {
434 log_warnx("Bogus number: %s.", str);
435 break;
436 }
437 if (tval >= base) {
438 log_warnx("Bogus number: %s: digit %d not in base %d",
439 str, tval, base);
440 break;
441 }
442 val = val * base + tval;
443 } while (*ptr);
444
445 if (negative)
446 max = (1 << (size - 1));
447 else
448 max = (1 << (size - 1)) + ((1 << (size - 1)) - 1);
449 if (val > max) {
450 switch (base) {
451 case 8:
452 log_warnx("value %s%o exceeds max (%d) for precision.",
453 negative ? "-" : "", val, max);
454 break;
455 case 16:
456 log_warnx("value %s%x exceeds max (%d) for precision.",
457 negative ? "-" : "", val, max);
458 break;
459 default:
460 log_warnx("value %s%u exceeds max (%d) for precision.",
461 negative ? "-" : "", val, max);
462 break;
463 }
464 }
465
466 if (negative) {
467 switch (size) {
468 case 8:
469 *buf = -(unsigned long)val;
470 break;
471 case 16:
472 putShort(buf, -(unsigned long)val);
473 break;
474 case 32:
475 putLong(buf, -(unsigned long)val);
476 break;
477 default:
478 log_warnx("Unexpected integer size: %d", size);
479 break;
480 }
481 } else {
482 switch (size) {
483 case 8:
484 *buf = (u_int8_t)val;
485 break;
486 case 16:
487 putUShort(buf, (u_int16_t)val);
488 break;
489 case 32:
490 putULong(buf, val);
491 break;
492 default:
493 log_warnx("Unexpected integer size: %d", size);
494 break;
495 }
496 }
497}
498
499/*
500 * date :== NUMBER NUMBER SLASH NUMBER SLASH NUMBER
501 * NUMBER COLON NUMBER COLON NUMBER UTC SEMI
502 *
503 * Dates are always in UTC; first number is day of week; next is
504 * year/month/day; next is hours:minutes:seconds on a 24-hour
505 * clock.
506 */
507time_t
508parse_date(FILE *cfile)
509{
510 struct tm tm;
511 char timestr[26]; /* "w yyyy/mm/dd hh:mm:ss UTC" */
512 char *val, *p;
513 size_t n;
514 time_t guess;
515 int token;
516
517 memset(timestr, 0, sizeof(timestr));
518
519 do {
520 token = peek_token(NULL((void *)0), cfile);
521 switch (token) {
522 case TOK_NAME265:
523 case TOK_NUMBER263:
524 case TOK_NUMBER_OR_NAME264:
525 case '/':
526 case ':':
527 token = next_token(&val, cfile);
528 n = strlcat(timestr, val, sizeof(timestr));
529 if (n >= sizeof(timestr)) {
530 /* XXX Will break after year 9999! */
531 parse_warn("time string too long");
532 skip_to_semi(cfile);
533 return (0);
534 }
535 break;
536 case';':
537 break;
538 default:
539 parse_warn("invalid time string");
540 skip_to_semi(cfile);
541 return (0);
542 }
543 } while (token != ';');
544
545 parse_semi(cfile);
546
547 memset(&tm, 0, sizeof(tm)); /* 'cuz strptime ignores tm_isdt. */
548 p = strptime(timestr, DB_TIMEFMT"%w %Y/%m/%d %T UTC", &tm);
549 if (p == NULL((void *)0) || *p != '\0') {
550 p = strptime(timestr, OLD_DB_TIMEFMT"%w %Y/%m/%d %T", &tm);
551 if (p == NULL((void *)0) || *p != '\0') {
552 parse_warn("unparseable time string");
553 return (0);
554 }
555 }
556
557 guess = timegm(&tm);
558 if (guess == -1) {
559 parse_warn("time could not be represented");
560 return (0);
561 }
562
563 return (guess);
564}
565
566int warnings_occurred;
567
568int
569parse_warn(char *fmt, ...)
570{
571 static char fbuf[1024];
572 static char mbuf[1024];
573 static char spaces[81];
574 va_list list;
575 int i;
576
577 snprintf(fbuf, sizeof(fbuf), "%s line %d: %s", tlname, lexline, mbuf);
578 va_start(list, fmt)__builtin_va_start((list), fmt);
579 vsnprintf(mbuf, sizeof(mbuf), fbuf, list);
580 va_end(list)__builtin_va_end((list));
581
582 log_warnx("%s", mbuf);
583 log_warnx("%s", token_line);
584 if (lexchar < sizeof(spaces)) {
585 memset(spaces, 0, sizeof(spaces));
586 for (i = 0; i < lexchar - 1; i++) {
587 if (token_line[i] == '\t')
588 spaces[i] = '\t';
589 else
590 spaces[i] = ' ';
591 }
592 }
593 log_warnx("%s^", spaces);
594
595 warnings_occurred = 1;
596
597 return (0);
598}