Bug Summary

File:src/usr.bin/xargs/xargs.c
Warning:line 543, column 9
This function call is prohibited after a successful vfork

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 xargs.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/xargs/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.bin/xargs/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/xargs/xargs.c
1/* $OpenBSD: xargs.c,v 1.38 2023/12/23 15:58:58 millert Exp $ */
2/* $FreeBSD: xargs.c,v 1.51 2003/05/03 19:09:11 obrien Exp $ */
3
4/*-
5 * Copyright (c) 1990, 1993
6 * The Regents of the University of California. All rights reserved.
7 *
8 * This code is derived from software contributed to Berkeley by
9 * John B. Roll Jr.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 *
35 * $xMach: xargs.c,v 1.6 2002/02/23 05:27:47 tim Exp $
36 */
37
38#include <sys/wait.h>
39
40#include <ctype.h>
41#include <err.h>
42#include <errno(*__errno()).h>
43#include <fcntl.h>
44#include <paths.h>
45#include <signal.h>
46#include <stdio.h>
47#include <stdlib.h>
48#include <string.h>
49#include <unistd.h>
50#include <limits.h>
51
52#include "pathnames.h"
53
54static void parse_input(int, char *[]);
55static void prerun(int, char *[]);
56static int prompt(void);
57static void run(char **);
58static void usage(void);
59void strnsubst(char **, const char *, const char *, size_t);
60static void waitchildren(const char *, int);
61
62static char **av, **bxp, **ep, **endxp, **xp;
63static char *argp, *bbp, *ebp, *inpline, *p, *replstr;
64static const char *eofstr;
65static int count, insingle, indouble, oflag, pflag, tflag, Rflag, rval, zflag;
66static int cnt, Iflag, jfound, Lflag, wasquoted, xflag, runeof = 1;
67static int curprocs, maxprocs;
68static size_t inpsize;
69
70extern char **environ;
71
72int
73main(int argc, char *argv[])
74{
75 long arg_max;
76 int ch, Jflag, nargs, nflag, nline;
77 size_t linelen;
78 char *endptr;
79 const char *errstr;
80
81 inpline = replstr = NULL((void *)0);
82 ep = environ;
83 eofstr = "";
84 Jflag = nflag = 0;
85
86 /*
87 * POSIX.2 limits the exec line length to ARG_MAX - 2K. Running that
88 * caused some E2BIG errors, so it was changed to ARG_MAX - 4K. Given
89 * that the smallest argument is 2 bytes in length, this means that
90 * the number of arguments is limited to:
91 *
92 * (ARG_MAX - 4K - LENGTH(utility + arguments)) / 2.
93 *
94 * We arbitrarily limit the number of arguments to 5000. This is
95 * allowed by POSIX.2 as long as the resulting minimum exec line is
96 * at least LINE_MAX. Realloc'ing as necessary is possible, but
97 * probably not worthwhile.
98 */
99 nargs = 5000;
100 if ((arg_max = sysconf(_SC_ARG_MAX1)) == -1)
101 errx(1, "sysconf(_SC_ARG_MAX) failed");
102
103 if (pledge("stdio rpath proc exec", NULL((void *)0)) == -1)
104 err(1, "pledge");
105
106 nline = arg_max - 4 * 1024;
107 while (*ep != NULL((void *)0)) {
108 /* 1 byte for each '\0' */
109 nline -= strlen(*ep++) + 1 + sizeof(*ep);
110 }
111 maxprocs = 1;
112 while ((ch = getopt(argc, argv, "0E:I:J:L:n:oP:pR:rs:tx")) != -1)
113 switch (ch) {
114 case 'E':
115 eofstr = optarg;
116 break;
117 case 'I':
118 Jflag = 0;
119 Iflag = 1;
120 Lflag = 1;
121 replstr = optarg;
122 break;
123 case 'J':
124 Iflag = 0;
125 Jflag = 1;
126 replstr = optarg;
127 break;
128 case 'L':
129 Lflag = strtonum(optarg, 0, INT_MAX0x7fffffff, &errstr);
130 if (errstr)
131 errx(1, "-L %s: %s", optarg, errstr);
132 break;
133 case 'n':
134 nflag = 1;
135 nargs = strtonum(optarg, 1, INT_MAX0x7fffffff, &errstr);
136 if (errstr)
137 errx(1, "-n %s: %s", optarg, errstr);
138 break;
139 case 'o':
140 oflag = 1;
141 break;
142 case 'P':
143 maxprocs = strtonum(optarg, 1, INT_MAX0x7fffffff, &errstr);
144 if (errstr)
145 errx(1, "-P %s: %s", optarg, errstr);
146 break;
147 case 'p':
148 pflag = 1;
149 break;
150 case 'r':
151 runeof = 0;
152 break;
153 case 'R':
154 Rflag = strtol(optarg, &endptr, 10);
155 if (*endptr != '\0')
156 errx(1, "replacements must be a number");
157 break;
158 case 's':
159 nline = strtonum(optarg, 0, INT_MAX0x7fffffff, &errstr);
160 if (errstr)
161 errx(1, "-s %s: %s", optarg, errstr);
162 break;
163 case 't':
164 tflag = 1;
165 break;
166 case 'x':
167 xflag = 1;
168 break;
169 case '0':
170 zflag = 1;
171 break;
172 default:
173 usage();
174 }
175 argc -= optind;
176 argv += optind;
177
178 if (!Iflag && Rflag)
179 usage();
180 if (Iflag && !Rflag)
181 Rflag = 5;
182 if (xflag && !nflag)
183 usage();
184 if (Iflag || Lflag)
185 xflag = 1;
186 if (replstr != NULL((void *)0) && *replstr == '\0')
187 errx(1, "replstr may not be empty");
188
189 /*
190 * Allocate pointers for the utility name, the utility arguments,
191 * the maximum arguments to be read from stdin and the trailing
192 * NULL.
193 */
194 linelen = 1 + argc + nargs + 1;
195 if ((av = bxp = calloc(linelen, sizeof(char *))) == NULL((void *)0))
196 err(1, NULL((void *)0));
197
198 /*
199 * Use the user's name for the utility as argv[0], just like the
200 * shell. Echo is the default. Set up pointers for the user's
201 * arguments.
202 */
203 if (*argv == NULL((void *)0))
204 cnt = strlen(*bxp++ = _PATH_ECHO"/bin/echo");
205 else {
206 do {
207 if (Jflag && strcmp(*argv, replstr) == 0) {
208 char **avj;
209 jfound = 1;
210 argv++;
211 for (avj = argv; *avj; avj++)
212 cnt += strlen(*avj) + 1;
213 break;
214 }
215 cnt += strlen(*bxp++ = *argv) + 1;
216 } while (*++argv != NULL((void *)0));
217 }
218
219 /*
220 * Set up begin/end/traversing pointers into the array. The -n
221 * count doesn't include the trailing NULL pointer, so the malloc
222 * added in an extra slot.
223 */
224 endxp = (xp = bxp) + nargs;
225
226 /*
227 * Allocate buffer space for the arguments read from stdin and the
228 * trailing NULL. Buffer space is defined as the default or specified
229 * space, minus the length of the utility name and arguments. Set up
230 * begin/end/traversing pointers into the array. The -s count does
231 * include the trailing NULL, so the malloc didn't add in an extra
232 * slot.
233 */
234 nline -= cnt;
235 if (nline <= 0)
236 errx(1, "insufficient space for command");
237
238 if ((bbp = malloc((size_t)(nline + 1))) == NULL((void *)0))
239 err(1, NULL((void *)0));
240 ebp = (argp = p = bbp) + nline - 1;
241 for (;;)
242 parse_input(argc, argv);
243}
244
245static void
246parse_input(int argc, char *argv[])
247{
248 int hasblank = 0;
249 static int hadblank = 0;
250 int ch, foundeof = 0;
251 char **avj;
252
253 ch = getchar()(!__isthreaded ? (--((&__sF[0]))->_r < 0 ? __srget(
(&__sF[0])) : (int)(*((&__sF[0]))->_p++)) : (getc)
((&__sF[0])))
;
1
Assuming '__isthreaded' is not equal to 0
2
'?' condition is false
254 if (isblank(ch)) {
3
Assuming the character is not a blank character
4
Taking false branch
255 /* Quotes escape tabs and spaces. */
256 if (insingle || indouble)
257 goto addch;
258 hasblank = 1;
259 if (zflag)
260 goto addch;
261 goto arg2;
262 }
263
264 switch (ch) {
5
Control jumps to 'case -1:' at line 265
265 case EOF(-1):
266 /* No arguments since last exec. */
267 if (p == bbp) {
6
Assuming 'p' is equal to 'bbp'
7
Taking true branch
268 if (runeof)
8
Assuming 'runeof' is not equal to 0
9
Taking true branch
269 prerun(0, av);
10
Calling 'prerun'
270 waitchildren(*argv, 1);
271 exit(rval);
272 }
273 goto arg1;
274 case '\0':
275 if (zflag) {
276 /*
277 * Increment 'count', so that nulls will be treated
278 * as end-of-line, as well as end-of-argument. This
279 * is needed so -0 works properly with -I and -L.
280 */
281 count++;
282 goto arg2;
283 }
284 goto addch;
285 case '\n':
286 if (zflag)
287 goto addch;
288 hasblank = 1;
289 if (hadblank == 0)
290 count++;
291
292 /* Quotes do not escape newlines. */
293arg1: if (insingle || indouble)
294 errx(1, "unterminated quote");
295arg2:
296 foundeof = *eofstr != '\0' &&
297 strcmp(argp, eofstr) == 0;
298
299 /*
300 * Do not make empty args unless they are quoted or
301 * we are run as "find -0" and not at EOF.
302 */
303 if (((zflag && ch != EOF(-1)) || argp != p || wasquoted) &&
304 !foundeof) {
305 *p++ = '\0';
306 *xp++ = argp;
307 if (Iflag) {
308 size_t curlen;
309
310 if (inpline == NULL((void *)0))
311 curlen = 0;
312 else {
313 /*
314 * If this string is not zero
315 * length, append a space for
316 * separation before the next
317 * argument.
318 */
319 if ((curlen = strlen(inpline)))
320 strlcat(inpline, " ", inpsize);
321 }
322 curlen++;
323 /*
324 * Allocate enough to hold what we will
325 * be holding in a second, and to append
326 * a space next time through, if we have
327 * to.
328 */
329 inpsize = curlen + 2 + strlen(argp);
330 inpline = realloc(inpline, inpsize);
331 if (inpline == NULL((void *)0))
332 errx(1, "realloc failed");
333 if (curlen == 1)
334 strlcpy(inpline, argp, inpsize);
335 else
336 strlcat(inpline, argp, inpsize);
337 }
338 }
339
340 /*
341 * If max'd out on args or buffer, or reached EOF,
342 * run the command. If xflag and max'd out on buffer
343 * but not on args, object. Having reached the limit
344 * of input lines, as specified by -L is the same as
345 * maxing out on arguments.
346 */
347 if (xp == endxp || p > ebp || ch == EOF(-1) ||
348 (Lflag <= count && xflag) || foundeof) {
349 if (xflag && xp != endxp && p > ebp)
350 errx(1, "insufficient space for arguments");
351 if (jfound) {
352 for (avj = argv; *avj; avj++)
353 *xp++ = *avj;
354 }
355 prerun(argc, av);
356 if (ch == EOF(-1) || foundeof) {
357 waitchildren(*argv, 1);
358 exit(rval);
359 }
360 p = bbp;
361 xp = bxp;
362 count = 0;
363 }
364 argp = p;
365 wasquoted = 0;
366 break;
367 case '\'':
368 if (indouble || zflag)
369 goto addch;
370 insingle = !insingle;
371 wasquoted = 1;
372 break;
373 case '"':
374 if (insingle || zflag)
375 goto addch;
376 indouble = !indouble;
377 wasquoted = 1;
378 break;
379 case '\\':
380 if (zflag)
381 goto addch;
382 /* Backslash escapes anything, is escaped by quotes. */
383 if (!insingle && !indouble && (ch = getchar()(!__isthreaded ? (--((&__sF[0]))->_r < 0 ? __srget(
(&__sF[0])) : (int)(*((&__sF[0]))->_p++)) : (getc)
((&__sF[0])))
) == EOF(-1))
384 errx(1, "backslash at EOF");
385 /* FALLTHROUGH */
386 default:
387addch: if (p < ebp) {
388 *p++ = ch;
389 break;
390 }
391
392 /* If only one argument, not enough buffer space. */
393 if (bxp == xp)
394 errx(1, "insufficient space for argument");
395 /* Didn't hit argument limit, so if xflag object. */
396 if (xflag)
397 errx(1, "insufficient space for arguments");
398
399 if (jfound) {
400 for (avj = argv; *avj; avj++)
401 *xp++ = *avj;
402 }
403 prerun(argc, av);
404 xp = bxp;
405 cnt = ebp - argp;
406 memmove(bbp, argp, (size_t)cnt);
407 p = (argp = bbp) + cnt;
408 *p++ = ch;
409 break;
410 }
411 hadblank = hasblank;
412}
413
414/*
415 * Do things necessary before run()'ing, such as -I substitution,
416 * and then call run().
417 */
418static void
419prerun(int argc, char *argv[])
420{
421 char **tmp, **tmp2, **avj;
422 int repls;
423
424 repls = Rflag;
425 runeof = 0;
426
427 if (argc
10.1
'argc' is equal to 0
== 0 || repls == 0) {
428 *xp = NULL((void *)0);
429 run(argv);
11
Calling 'run'
430 return;
431 }
432
433 avj = argv;
434
435 /*
436 * Allocate memory to hold the argument list, and
437 * a NULL at the tail.
438 */
439 tmp = calloc(argc + 1, sizeof(char *));
440 if (tmp == NULL((void *)0))
441 err(1, NULL((void *)0));
442 tmp2 = tmp;
443
444 /*
445 * Save the first argument and iterate over it, we
446 * cannot do strnsubst() to it.
447 */
448 if ((*tmp++ = strdup(*avj++)) == NULL((void *)0))
449 err(1, NULL((void *)0));
450
451 /*
452 * For each argument to utility, if we have not used up
453 * the number of replacements we are allowed to do, and
454 * if the argument contains at least one occurrence of
455 * replstr, call strnsubst(), else just save the string.
456 * Iterations over elements of avj and tmp are done
457 * where appropriate.
458 */
459 while (--argc) {
460 *tmp = *avj++;
461 if (repls && strstr(*tmp, replstr) != NULL((void *)0)) {
462 strnsubst(tmp++, replstr, inpline, (size_t)255);
463 if (repls > 0)
464 repls--;
465 } else {
466 if ((*tmp = strdup(*tmp)) == NULL((void *)0))
467 err(1, NULL((void *)0));
468 tmp++;
469 }
470 }
471
472 /*
473 * Run it.
474 */
475 *tmp = NULL((void *)0);
476 run(tmp2);
477
478 /*
479 * Walk from the tail to the head, free along the way.
480 */
481 for (; tmp2 != tmp; tmp--)
482 free(*tmp);
483 /*
484 * Now free the list itself.
485 */
486 free(tmp2);
487
488 /*
489 * Free the input line buffer, if we have one.
490 */
491 free(inpline);
492 inpline = NULL((void *)0);
493}
494
495static void
496run(char **argv)
497{
498 pid_t pid;
499 int fd;
500 char **avec;
501
502 /*
503 * If the user wants to be notified of each command before it is
504 * executed, notify them. If they want the notification to be
505 * followed by a prompt, then prompt them.
506 */
507 if (tflag || pflag) {
12
Assuming 'tflag' is 0
13
Assuming 'pflag' is 0
14
Taking false branch
508 fprintf(stderr(&__sF[2]), "%s", *argv);
509 for (avec = argv + 1; *avec != NULL((void *)0); ++avec)
510 fprintf(stderr(&__sF[2]), " %s", *avec);
511 /*
512 * If the user has asked to be prompted, do so.
513 */
514 if (pflag)
515 /*
516 * If they asked not to exec, return without execution
517 * but if they asked to, go to the execution. If we
518 * could not open their tty, break the switch and drop
519 * back to -t behaviour.
520 */
521 switch (prompt()) {
522 case 0:
523 return;
524 case 1:
525 goto exec;
526 case 2:
527 break;
528 }
529 fprintf(stderr(&__sF[2]), "\n");
530 fflush(stderr(&__sF[2]));
531 }
532exec:
533 switch (pid = vfork()) {
15
Control jumps to 'case 0:' at line 536
534 case -1:
535 err(1, "vfork");
536 case 0:
537 if (oflag) {
16
Assuming 'oflag' is 0
17
Taking false branch
538 if ((fd = open(_PATH_TTY"/dev/tty", O_RDONLY0x0000)) == -1) {
539 warn("can't open /dev/tty");
540 _exit(1);
541 }
542 } else {
543 fd = open(_PATH_DEVNULL"/dev/null", O_RDONLY0x0000);
18
This function call is prohibited after a successful vfork
544 }
545 if (fd > STDIN_FILENO0) {
546 if (dup2(fd, STDIN_FILENO0) != 0) {
547 warn("can't dup2 to stdin");
548 _exit(1);
549 }
550 close(fd);
551 }
552 execvp(argv[0], argv);
553 warn("%s", argv[0]);
554 _exit(errno(*__errno()) == ENOENT2 ? 127 : 126);
555 }
556 curprocs++;
557 waitchildren(*argv, 0);
558}
559
560static void
561waitchildren(const char *name, int waitall)
562{
563 pid_t pid;
564 int status;
565
566 while ((pid = waitpid(-1, &status, !waitall && curprocs < maxprocs ?
567 WNOHANG0x01 : 0)) > 0) {
568 curprocs--;
569 /*
570 * According to POSIX, we have to exit if the utility exits
571 * with a 255 status, or is interrupted by a signal.
572 * We are allowed to return any exit status between 1 and
573 * 125 in these cases, but we'll use 124 and 125, the same
574 * values used by GNU xargs.
575 */
576 if (WIFEXITED(status)(((status) & 0177) == 0)) {
577 if (WEXITSTATUS(status)(int)(((unsigned)(status) >> 8) & 0xff) == 255) {
578 warnx("%s exited with status 255", name);
579 exit(124);
580 } else if (WEXITSTATUS(status)(int)(((unsigned)(status) >> 8) & 0xff) == 127 ||
581 WEXITSTATUS(status)(int)(((unsigned)(status) >> 8) & 0xff) == 126) {
582 exit(WEXITSTATUS(status)(int)(((unsigned)(status) >> 8) & 0xff));
583 } else if (WEXITSTATUS(status)(int)(((unsigned)(status) >> 8) & 0xff) != 0) {
584 rval = 123;
585 }
586 } else if (WIFSIGNALED(status)(((status) & 0177) != 0177 && ((status) & 0177
) != 0)
) {
587 if (WTERMSIG(status)(((status) & 0177)) != SIGPIPE13) {
588 if (WTERMSIG(status)(((status) & 0177)) < NSIG33)
589 warnx("%s terminated by SIG%s", name,
590 sys_signame[WTERMSIG(status)(((status) & 0177))]);
591 else
592 warnx("%s terminated by signal %d",
593 name, WTERMSIG(status)(((status) & 0177)));
594 }
595 exit(125);
596 }
597 }
598 if (pid == -1 && errno(*__errno()) != ECHILD10)
599 err(1, "waitpid");
600}
601
602/*
603 * Prompt the user about running a command.
604 */
605static int
606prompt(void)
607{
608 size_t rsize;
609 char *response;
610 FILE *ttyfp;
611 int doit = 0;
612
613 if ((ttyfp = fopen(_PATH_TTY"/dev/tty", "r")) == NULL((void *)0))
614 return (2); /* Indicate that the TTY failed to open. */
615 fprintf(stderr(&__sF[2]), "?...");
616 fflush(stderr(&__sF[2]));
617 response = fgetln(ttyfp, &rsize);
618 doit = response != NULL((void *)0) && (*response == 'y' || *response == 'Y');
619 fclose(ttyfp);
620 return (doit);
621}
622
623static void
624usage(void)
625{
626 fprintf(stderr(&__sF[2]),
627"usage: xargs [-0oprt] [-E eofstr] [-I replstr [-R replacements]] [-J replstr]\n"
628" [-L number] [-n number [-x]] [-P maxprocs] [-s size]\n"
629" [utility [argument ...]]\n");
630 exit(1);
631}