Bug Summary

File:src/usr.bin/xargs/xargs.c
Warning:line 540, 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.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name xargs.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.bin/xargs/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.bin/xargs/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.bin/xargs/xargs.c
1/* $OpenBSD: xargs.c,v 1.35 2020/07/19 13:19:25 schwarze 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_MAX2147483647, &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_MAX2147483647, &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_MAX2147483647, &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_MAX2147483647, &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 case '?':
173 default:
174 usage();
175 }
176 argc -= optind;
177 argv += optind;
178
179 if (!Iflag && Rflag)
180 usage();
181 if (Iflag && !Rflag)
182 Rflag = 5;
183 if (xflag && !nflag)
184 usage();
185 if (Iflag || Lflag)
186 xflag = 1;
187 if (replstr != NULL((void *)0) && *replstr == '\0')
188 errx(1, "replstr may not be empty");
189
190 /*
191 * Allocate pointers for the utility name, the utility arguments,
192 * the maximum arguments to be read from stdin and the trailing
193 * NULL.
194 */
195 linelen = 1 + argc + nargs + 1;
196 if ((av = bxp = calloc(linelen, sizeof(char *))) == NULL((void *)0))
197 err(1, NULL((void *)0));
198
199 /*
200 * Use the user's name for the utility as argv[0], just like the
201 * shell. Echo is the default. Set up pointers for the user's
202 * arguments.
203 */
204 if (*argv == NULL((void *)0))
205 cnt = strlen(*bxp++ = _PATH_ECHO"/bin/echo");
206 else {
207 do {
208 if (Jflag && strcmp(*argv, replstr) == 0) {
209 char **avj;
210 jfound = 1;
211 argv++;
212 for (avj = argv; *avj; avj++)
213 cnt += strlen(*avj) + 1;
214 break;
215 }
216 cnt += strlen(*bxp++ = *argv) + 1;
217 } while (*++argv != NULL((void *)0));
218 }
219
220 /*
221 * Set up begin/end/traversing pointers into the array. The -n
222 * count doesn't include the trailing NULL pointer, so the malloc
223 * added in an extra slot.
224 */
225 endxp = (xp = bxp) + nargs;
226
227 /*
228 * Allocate buffer space for the arguments read from stdin and the
229 * trailing NULL. Buffer space is defined as the default or specified
230 * space, minus the length of the utility name and arguments. Set up
231 * begin/end/traversing pointers into the array. The -s count does
232 * include the trailing NULL, so the malloc didn't add in an extra
233 * slot.
234 */
235 nline -= cnt;
236 if (nline <= 0)
237 errx(1, "insufficient space for command");
238
239 if ((bbp = malloc((size_t)(nline + 1))) == NULL((void *)0))
240 err(1, NULL((void *)0));
241 ebp = (argp = p = bbp) + nline - 1;
242 for (;;)
243 parse_input(argc, argv);
244}
245
246static void
247parse_input(int argc, char *argv[])
248{
249 int hasblank = 0;
250 static int hadblank = 0;
251 int ch, foundeof = 0;
252 char **avj;
253
254 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
255 if (isblank(ch)) {
3
Taking false branch
256 /* Quotes escape tabs and spaces. */
257 if (insingle || indouble)
258 goto addch;
259 hasblank = 1;
260 if (zflag)
261 goto addch;
262 goto arg2;
263 }
264
265 switch (ch) {
4
Control jumps to 'case -1:' at line 266
266 case EOF(-1):
267 /* No arguments since last exec. */
268 if (p == bbp) {
5
Assuming 'p' is equal to 'bbp'
6
Taking true branch
269 if (runeof)
7
Assuming 'runeof' is not equal to 0
8
Taking true branch
270 prerun(0, av);
9
Calling 'prerun'
271 waitchildren(*argv, 1);
272 exit(rval);
273 }
274 goto arg1;
275 case '\0':
276 if (zflag) {
277 /*
278 * Increment 'count', so that nulls will be treated
279 * as end-of-line, as well as end-of-argument. This
280 * is needed so -0 works properly with -I and -L.
281 */
282 count++;
283 goto arg2;
284 }
285 goto addch;
286 case '\n':
287 if (zflag)
288 goto addch;
289 hasblank = 1;
290 if (hadblank == 0)
291 count++;
292
293 /* Quotes do not escape newlines. */
294arg1: if (insingle || indouble)
295 errx(1, "unterminated quote");
296arg2:
297 foundeof = *eofstr != '\0' &&
298 strcmp(argp, eofstr) == 0;
299
300 /* Do not make empty args unless they are quoted */
301 if ((argp != p || wasquoted) && !foundeof) {
302 *p++ = '\0';
303 *xp++ = argp;
304 if (Iflag) {
305 size_t curlen;
306
307 if (inpline == NULL((void *)0))
308 curlen = 0;
309 else {
310 /*
311 * If this string is not zero
312 * length, append a space for
313 * separation before the next
314 * argument.
315 */
316 if ((curlen = strlen(inpline)))
317 strlcat(inpline, " ", inpsize);
318 }
319 curlen++;
320 /*
321 * Allocate enough to hold what we will
322 * be holding in a second, and to append
323 * a space next time through, if we have
324 * to.
325 */
326 inpsize = curlen + 2 + strlen(argp);
327 inpline = realloc(inpline, inpsize);
328 if (inpline == NULL((void *)0))
329 errx(1, "realloc failed");
330 if (curlen == 1)
331 strlcpy(inpline, argp, inpsize);
332 else
333 strlcat(inpline, argp, inpsize);
334 }
335 }
336
337 /*
338 * If max'd out on args or buffer, or reached EOF,
339 * run the command. If xflag and max'd out on buffer
340 * but not on args, object. Having reached the limit
341 * of input lines, as specified by -L is the same as
342 * maxing out on arguments.
343 */
344 if (xp == endxp || p > ebp || ch == EOF(-1) ||
345 (Lflag <= count && xflag) || foundeof) {
346 if (xflag && xp != endxp && p > ebp)
347 errx(1, "insufficient space for arguments");
348 if (jfound) {
349 for (avj = argv; *avj; avj++)
350 *xp++ = *avj;
351 }
352 prerun(argc, av);
353 if (ch == EOF(-1) || foundeof) {
354 waitchildren(*argv, 1);
355 exit(rval);
356 }
357 p = bbp;
358 xp = bxp;
359 count = 0;
360 }
361 argp = p;
362 wasquoted = 0;
363 break;
364 case '\'':
365 if (indouble || zflag)
366 goto addch;
367 insingle = !insingle;
368 wasquoted = 1;
369 break;
370 case '"':
371 if (insingle || zflag)
372 goto addch;
373 indouble = !indouble;
374 wasquoted = 1;
375 break;
376 case '\\':
377 if (zflag)
378 goto addch;
379 /* Backslash escapes anything, is escaped by quotes. */
380 if (!insingle && !indouble && (ch = getchar()(!__isthreaded ? (--((&__sF[0]))->_r < 0 ? __srget(
(&__sF[0])) : (int)(*((&__sF[0]))->_p++)) : (getc)
((&__sF[0])))
) == EOF(-1))
381 errx(1, "backslash at EOF");
382 /* FALLTHROUGH */
383 default:
384addch: if (p < ebp) {
385 *p++ = ch;
386 break;
387 }
388
389 /* If only one argument, not enough buffer space. */
390 if (bxp == xp)
391 errx(1, "insufficient space for argument");
392 /* Didn't hit argument limit, so if xflag object. */
393 if (xflag)
394 errx(1, "insufficient space for arguments");
395
396 if (jfound) {
397 for (avj = argv; *avj; avj++)
398 *xp++ = *avj;
399 }
400 prerun(argc, av);
401 xp = bxp;
402 cnt = ebp - argp;
403 memmove(bbp, argp, (size_t)cnt);
404 p = (argp = bbp) + cnt;
405 *p++ = ch;
406 break;
407 }
408 hadblank = hasblank;
409}
410
411/*
412 * Do things necessary before run()'ing, such as -I substitution,
413 * and then call run().
414 */
415static void
416prerun(int argc, char *argv[])
417{
418 char **tmp, **tmp2, **avj;
419 int repls;
420
421 repls = Rflag;
422 runeof = 0;
423
424 if (argc
9.1
'argc' is equal to 0
== 0 || repls == 0) {
425 *xp = NULL((void *)0);
426 run(argv);
10
Calling 'run'
427 return;
428 }
429
430 avj = argv;
431
432 /*
433 * Allocate memory to hold the argument list, and
434 * a NULL at the tail.
435 */
436 tmp = calloc(argc + 1, sizeof(char *));
437 if (tmp == NULL((void *)0))
438 err(1, NULL((void *)0));
439 tmp2 = tmp;
440
441 /*
442 * Save the first argument and iterate over it, we
443 * cannot do strnsubst() to it.
444 */
445 if ((*tmp++ = strdup(*avj++)) == NULL((void *)0))
446 err(1, NULL((void *)0));
447
448 /*
449 * For each argument to utility, if we have not used up
450 * the number of replacements we are allowed to do, and
451 * if the argument contains at least one occurrence of
452 * replstr, call strnsubst(), else just save the string.
453 * Iterations over elements of avj and tmp are done
454 * where appropriate.
455 */
456 while (--argc) {
457 *tmp = *avj++;
458 if (repls && strstr(*tmp, replstr) != NULL((void *)0)) {
459 strnsubst(tmp++, replstr, inpline, (size_t)255);
460 if (repls > 0)
461 repls--;
462 } else {
463 if ((*tmp = strdup(*tmp)) == NULL((void *)0))
464 err(1, NULL((void *)0));
465 tmp++;
466 }
467 }
468
469 /*
470 * Run it.
471 */
472 *tmp = NULL((void *)0);
473 run(tmp2);
474
475 /*
476 * Walk from the tail to the head, free along the way.
477 */
478 for (; tmp2 != tmp; tmp--)
479 free(*tmp);
480 /*
481 * Now free the list itself.
482 */
483 free(tmp2);
484
485 /*
486 * Free the input line buffer, if we have one.
487 */
488 free(inpline);
489 inpline = NULL((void *)0);
490}
491
492static void
493run(char **argv)
494{
495 pid_t pid;
496 int fd;
497 char **avec;
498
499 /*
500 * If the user wants to be notified of each command before it is
501 * executed, notify them. If they want the notification to be
502 * followed by a prompt, then prompt them.
503 */
504 if (tflag || pflag) {
11
Assuming 'tflag' is 0
12
Assuming 'pflag' is 0
13
Taking false branch
505 fprintf(stderr(&__sF[2]), "%s", *argv);
506 for (avec = argv + 1; *avec != NULL((void *)0); ++avec)
507 fprintf(stderr(&__sF[2]), " %s", *avec);
508 /*
509 * If the user has asked to be prompted, do so.
510 */
511 if (pflag)
512 /*
513 * If they asked not to exec, return without execution
514 * but if they asked to, go to the execution. If we
515 * could not open their tty, break the switch and drop
516 * back to -t behaviour.
517 */
518 switch (prompt()) {
519 case 0:
520 return;
521 case 1:
522 goto exec;
523 case 2:
524 break;
525 }
526 fprintf(stderr(&__sF[2]), "\n");
527 fflush(stderr(&__sF[2]));
528 }
529exec:
530 switch (pid = vfork()) {
14
Control jumps to 'case 0:' at line 533
531 case -1:
532 err(1, "vfork");
533 case 0:
534 if (oflag) {
15
Assuming 'oflag' is 0
16
Taking false branch
535 if ((fd = open(_PATH_TTY"/dev/tty", O_RDONLY0x0000)) == -1) {
536 warn("can't open /dev/tty");
537 _exit(1);
538 }
539 } else {
540 fd = open(_PATH_DEVNULL"/dev/null", O_RDONLY0x0000);
17
This function call is prohibited after a successful vfork
541 }
542 if (fd > STDIN_FILENO0) {
543 if (dup2(fd, STDIN_FILENO0) != 0) {
544 warn("can't dup2 to stdin");
545 _exit(1);
546 }
547 close(fd);
548 }
549 execvp(argv[0], argv);
550 warn("%s", argv[0]);
551 _exit(errno(*__errno()) == ENOENT2 ? 127 : 126);
552 }
553 curprocs++;
554 waitchildren(*argv, 0);
555}
556
557static void
558waitchildren(const char *name, int waitall)
559{
560 pid_t pid;
561 int status;
562
563 while ((pid = waitpid(-1, &status, !waitall && curprocs < maxprocs ?
564 WNOHANG1 : 0)) > 0) {
565 curprocs--;
566 /*
567 * According to POSIX, we have to exit if the utility exits
568 * with a 255 status, or is interrupted by a signal.
569 * We are allowed to return any exit status between 1 and
570 * 125 in these cases, but we'll use 124 and 125, the same
571 * values used by GNU xargs.
572 */
573 if (WIFEXITED(status)(((status) & 0177) == 0)) {
574 if (WEXITSTATUS(status)(int)(((unsigned)(status) >> 8) & 0xff) == 255) {
575 warnx("%s exited with status 255", name);
576 exit(124);
577 } else if (WEXITSTATUS(status)(int)(((unsigned)(status) >> 8) & 0xff) == 127 ||
578 WEXITSTATUS(status)(int)(((unsigned)(status) >> 8) & 0xff) == 126) {
579 exit(WEXITSTATUS(status)(int)(((unsigned)(status) >> 8) & 0xff));
580 } else if (WEXITSTATUS(status)(int)(((unsigned)(status) >> 8) & 0xff) != 0) {
581 rval = 123;
582 }
583 } else if (WIFSIGNALED(status)(((status) & 0177) != 0177 && ((status) & 0177
) != 0)
) {
584 if (WTERMSIG(status)(((status) & 0177)) != SIGPIPE13) {
585 if (WTERMSIG(status)(((status) & 0177)) < NSIG33)
586 warnx("%s terminated by SIG%s", name,
587 sys_signame[WTERMSIG(status)(((status) & 0177))]);
588 else
589 warnx("%s terminated by signal %d",
590 name, WTERMSIG(status)(((status) & 0177)));
591 }
592 exit(125);
593 }
594 }
595 if (pid == -1 && errno(*__errno()) != ECHILD10)
596 err(1, "waitpid");
597}
598
599/*
600 * Prompt the user about running a command.
601 */
602static int
603prompt(void)
604{
605 size_t rsize;
606 char *response;
607 FILE *ttyfp;
608 int doit = 0;
609
610 if ((ttyfp = fopen(_PATH_TTY"/dev/tty", "r")) == NULL((void *)0))
611 return (2); /* Indicate that the TTY failed to open. */
612 fprintf(stderr(&__sF[2]), "?...");
613 fflush(stderr(&__sF[2]));
614 response = fgetln(ttyfp, &rsize);
615 doit = response != NULL((void *)0) && (*response == 'y' || *response == 'Y');
616 fclose(ttyfp);
617 return (doit);
618}
619
620static void
621usage(void)
622{
623 fprintf(stderr(&__sF[2]),
624"usage: xargs [-0oprt] [-E eofstr] [-I replstr [-R replacements]] [-J replstr]\n"
625" [-L number] [-n number [-x]] [-P maxprocs] [-s size]\n"
626" [utility [argument ...]]\n");
627 exit(1);
628}