Bug Summary

File:src/usr.bin/make/str.c
Warning:line 190, column 8
Dereference of null pointer

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 str.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.bin/make/obj -resource-dir /usr/local/llvm16/lib/clang/16 -I /usr/src/usr.bin/make/obj -I /usr/src/usr.bin/make -D MAKE_BSIZE=256 -D DEFMAXJOBS=4 -I /usr/src/usr.bin/make/lst.lib -internal-isystem /usr/local/llvm16/lib/clang/16/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.bin/make/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.bin/make/str.c
1/* $OpenBSD: str.c,v 1.33 2023/09/04 11:35:11 espie Exp $ */
2/* $NetBSD: str.c,v 1.13 1996/11/06 17:59:23 christos Exp $ */
3
4/*-
5 * Copyright (c) 1988, 1989, 1990, 1993
6 * The Regents of the University of California. All rights reserved.
7 * Copyright (c) 1989 by Berkeley Softworks
8 * All rights reserved.
9 *
10 * This code is derived from software contributed to Berkeley by
11 * Adam de Boor.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 * 3. Neither the name of the University nor the names of its contributors
22 * may be used to endorse or promote products derived from this software
23 * without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * SUCH DAMAGE.
36 */
37
38#include <ctype.h>
39#include <string.h>
40#include "defines.h"
41#include "str.h"
42#include "memory.h"
43#include "buf.h"
44
45/* helpers for Str_Matchi */
46static bool_Bool range_match(char, const char **, const char *);
47static bool_Bool star_match(const char *, const char *, const char *, const char *);
48
49char *
50Str_concati(const char *s1, const char *e1, const char *s2, const char *e2,
51 int sep)
52{
53 size_t len1, len2;
54 char *result;
55
56 /* get the length of both strings */
57 len1 = e1 - s1;
58 len2 = e2 - s2;
59
60 /* space for separator */
61 if (sep)
62 len1++;
63 result = emalloc(len1 + len2 + 1);
64
65 /* copy first string into place */
66 memcpy(result, s1, len1);
67
68 /* add separator character */
69 if (sep)
70 result[len1-1] = sep;
71
72 /* copy second string plus EOS into place */
73 memcpy(result + len1, s2, len2);
74 result[len1+len2] = '\0';
75 return result;
76}
77
78/*-
79 * brk_string --
80 * Fracture a string into an array of words (as delineated by tabs or
81 * spaces) taking quotation marks into account. Leading tabs/spaces
82 * are ignored.
83 *
84 * returns --
85 * Pointer to the array of pointers to the words. Fills up
86 * store_args with its size.
87 * The returned parameters are allocated in a single buffer,
88 * return as *buffer, to be freed later.
89 */
90char **
91brk_string(const char *str, int *store_argc, char **buffer)
92{
93 int argc;
94 char ch;
95 char inquote;
96 const char *p;
97 char *start, *t;
98 size_t len;
99 int argmax = 50; /* start at 50 */
100 size_t curlen = 0;
101 char **argv = ereallocarray(NULL((void *)0), argmax + 1, sizeof(char *));
102
103 /* skip leading space chars. */
104 for (; *str == ' ' || *str == '\t'; ++str)
1
Assuming the condition is false
2
Assuming the condition is false
3
Loop condition is false. Execution continues on line 108
105 continue;
106
107 /* allocate room for a copy of the string */
108 if ((len = strlen(str) + 1) > curlen)
4
Taking true branch
109 *buffer = emalloc(curlen = len);
110
111 /*
112 * copy the string; at the same time, parse backslashes,
113 * quotes and build the argument list.
114 */
115 argc = 0;
116 inquote = '\0';
117 for (p = str, start = t = *buffer;; ++p) {
5
Value assigned to 't'
6
Loop condition is true. Entering loop body
118 switch (ch = *p) {
119 case '"':
120 case '\'':
121 if (inquote) {
122 if (inquote == ch)
123 inquote = '\0';
124 else
125 break;
126 } else {
127 inquote = ch;
128 /* Don't miss "" or '' */
129 if (start == NULL((void *)0) && p[1] == inquote) {
130 start = t + 1;
131 break;
132 }
133 }
134 continue;
135 case ' ':
136 case '\t':
137 case '\n':
138 if (inquote)
139 break;
140 if (!start)
141 continue;
142 /* FALLTHROUGH */
143 case '\0':
144 /*
145 * end of a token -- make sure there's enough argv
146 * space and save off a pointer.
147 */
148 if (!start)
149 goto done;
150
151 *t++ = '\0';
152 if (argc == argmax) {
153 argmax *= 2; /* ramp up fast */
154 argv = ereallocarray(argv,
155 (argmax + 1), sizeof(char *));
156 }
157 argv[argc++] = start;
158 start = NULL((void *)0);
159 if (ch == '\n' || ch == '\0')
160 goto done;
161 continue;
162 case '\\':
163 switch (ch = *++p) {
164 case '\0':
165 case '\n':
166 /* hmmm; fix it up as best we can */
167 ch = '\\';
168 --p;
169 break;
170 case 'b':
171 ch = '\b';
172 break;
173 case 'f':
174 ch = '\f';
175 break;
176 case 'n':
177 ch = '\n';
178 break;
179 case 'r':
180 ch = '\r';
181 break;
182 case 't':
183 ch = '\t';
184 break;
185 }
186 break;
187 }
188 if (!start)
7
'Default' branch taken. Execution continues on line 188
8
Assuming 'start' is null
9
Taking true branch
189 start = t;
190 *t++ = ch;
10
Dereference of null pointer
191 }
192 done:
193 argv[argc] = NULL((void *)0);
194 *store_argc = argc;
195 return argv;
196}
197
198
199const char *
200iterate_words(const char **end)
201{
202 const char *start, *p;
203 char state = 0;
204 start = *end;
205
206 while (ISSPACE(*start)(isspace((unsigned char)(*start))))
207 start++;
208 if (*start == '\0')
209 return NULL((void *)0);
210
211 for (p = start;; p++)
212 switch(*p) {
213 case '\\':
214 if (p[1] != '\0')
215 p++;
216 break;
217 case '\'':
218 case '"':
219 if (state == *p)
220 state = 0;
221 else if (state == 0)
222 state = *p;
223 break;
224 case ' ':
225 case '\t':
226 if (state != 0)
227 break;
228 /* FALLTHROUGH */
229 case '\0':
230 *end = p;
231 return start;
232 default:
233 break;
234 }
235}
236
237static bool_Bool
238star_match(const char *string, const char *estring,
239 const char *pattern, const char *epattern)
240{
241 /* '*' matches any substring. We handle this by calling ourselves
242 * recursively for each postfix of string, until either we match or
243 * we reach the end of the string. */
244 pattern++;
245 /* Skip over contiguous sequences of `?*', so that
246 * recursive calls only occur on `real' characters. */
247 while (pattern != epattern &&
248 (*pattern == '?' || *pattern == '*')) {
249 if (*pattern == '?') {
250 if (string == estring)
251 return false0;
252 else
253 string++;
254 }
255 pattern++;
256 }
257 if (pattern == epattern)
258 return true1;
259 for (; string != estring; string++)
260 if (Str_Matchi(string, estring, pattern,
261 epattern))
262 return true1;
263 return false0;
264}
265
266static bool_Bool
267range_match(char c, const char **ppat, const char *epattern)
268{
269 if (*ppat == epattern) {
270 if (c == '[')
271 return true1;
272 else
273 return false0;
274 }
275 if (**ppat == '!' || **ppat == '^') {
276 (*ppat)++;
277 return !range_match(c, ppat, epattern);
278 }
279 for (;;) {
280 if (**ppat == '\\') {
281 if (++(*ppat) == epattern)
282 return false0;
283 }
284 if (**ppat == c)
285 break;
286 if ((*ppat)[1] == '-') {
287 if (*ppat + 2 == epattern)
288 return false0;
289 if (**ppat < c && c <= (*ppat)[2])
290 break;
291 if ((*ppat)[2] <= c && c < **ppat)
292 break;
293 *ppat += 3;
294 } else
295 (*ppat)++;
296 /* The test for ']' is done at the end
297 * so that ']' can be used at the
298 * start of the range without '\' */
299 if (*ppat == epattern || **ppat == ']')
300 return false0;
301 }
302 /* Found matching character, skip over rest
303 * of class. */
304 while (**ppat != ']') {
305 if (**ppat == '\\')
306 (*ppat)++;
307 /* A non-terminated character class
308 * is ok. */
309 if (*ppat == epattern)
310 break;
311 (*ppat)++;
312 }
313 return true1;
314}
315
316bool_Bool
317Str_Matchi(const char *string, const char *estring,
318 const char *pattern, const char *epattern)
319{
320 while (pattern != epattern) {
321 /* Check for a "*" as the next pattern character. */
322 if (*pattern == '*')
323 return star_match(string, estring, pattern, epattern);
324 else if (string == estring)
325 return false0;
326 /* Check for a "[" as the next pattern character. It is
327 * followed by a list of characters that are acceptable, or
328 * by a range (two characters separated by "-"). */
329 else if (*pattern == '[') {
330 pattern++;
331 if (!range_match(*string, &pattern, epattern))
332 return false0;
333
334 }
335 /* '?' matches any single character, so shunt test. */
336 else if (*pattern != '?') {
337 /* If the next pattern character is '\', just strip
338 * off the '\' so we do exact matching on the
339 * character that follows. */
340 if (*pattern == '\\') {
341 if (++pattern == epattern)
342 return false0;
343 }
344 /* There's no special character. Just make sure that
345 * the next characters of each string match. */
346 if (*pattern != *string)
347 return false0;
348 }
349 pattern++;
350 string++;
351 }
352 if (string == estring)
353 return true1;
354 else
355 return false0;
356}
357
358
359/*-
360 *-----------------------------------------------------------------------
361 * Str_SYSVMatch --
362 * Check word against pattern for a match (% is wild),
363 *
364 * Results:
365 * Returns the beginning position of a match or null. The number
366 * of characters matched is returned in len.
367 *-----------------------------------------------------------------------
368 */
369const char *
370Str_SYSVMatch(const char *word, const char *pattern, size_t *len)
371{
372 const char *p = pattern;
373 const char *w = word;
374 const char *m;
375
376 if (*p == '\0') {
377 /* Null pattern is the whole string. */
378 *len = strlen(w);
379 return w;
380 }
381
382 if ((m = strchr(p, '%')) != NULL((void *)0)) {
383 /* Check that the prefix matches. */
384 for (; p != m && *w && *w == *p; w++, p++)
385 continue;
386
387 if (p != m)
388 return NULL((void *)0); /* No match. */
389
390 if (*++p == '\0') {
391 /* No more pattern, return the rest of the string. */
392 *len = strlen(w);
393 return w;
394 }
395 }
396
397 m = w;
398
399 /* Find a matching tail. */
400 do {
401 if (strcmp(p, w) == 0) {
402 *len = w - m;
403 return m;
404 }
405 } while (*w++ != '\0');
406
407 return NULL((void *)0);
408}
409
410
411/*-
412 *-----------------------------------------------------------------------
413 * Str_SYSVSubst --
414 * Substitute '%' in the pattern with len characters from src.
415 * If the pattern does not contain a '%' prepend len characters
416 * from src.
417 *
418 * Side Effects:
419 * Adds result to buf
420 *-----------------------------------------------------------------------
421 */
422void
423Str_SYSVSubst(Buffer buf, const char *pat, const char *src, size_t len)
424{
425 const char *m;
426
427 if ((m = strchr(pat, '%')) != NULL((void *)0)) {
428 /* Copy the prefix. */
429 Buf_Addi(buf, pat, m)Buf_AddChars((buf), (m) - (pat), (pat));
430 /* Skip the %. */
431 pat = m + 1;
432 }
433
434 /* Copy the pattern. */
435 Buf_AddChars(buf, len, src);
436
437 /* Append the rest. */
438 Buf_AddString(buf, pat)Buf_AddChars((buf), strlen(pat), (pat));
439}
440
441char *
442Str_dupi(const char *begin, const char *end)
443{
444 char *s;
445
446 s = emalloc(end - begin + 1);
447 memcpy(s, begin, end - begin);
448 s[end-begin] = '\0';
449 return s;
450}
451
452char *
453escape_dupi(const char *begin, const char *end, const char *set)
454{
455 char *s, *t;
456
457 t = s = emalloc(end - begin + 1);
458 while (begin != end) {
459 if (*begin == '\\') {
460 begin++;
461 if (begin == end) {
462 *t++ = '\\';
463 break;
464 }
465 if (strchr(set, *begin) == NULL((void *)0))
466 *t++ = '\\';
467 }
468 *t++ = *begin++;
469 }
470 *t++ = '\0';
471 return s;
472}
473
474char *
475Str_rchri(const char *begin, const char *end, int c)
476{
477 if (begin != end)
478 do {
479 if (*--end == c)
480 return (char *)end;
481 } while (end != begin);
482 return NULL((void *)0);
483}