Bug Summary

File:src/gnu/lib/libiberty/src/pex-unix.c
Warning:line 375, column 13
Call to function 'vfork' is insecure as it can lead to denial of service situations in the parent process. Replace calls to vfork with calls to the safer 'posix_spawn' function

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 pex-unix.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 -fhalf-no-semantic-interposition -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/gnu/lib/libiberty/obj -resource-dir /usr/local/lib/clang/13.0.0 -D HAVE_CONFIG_H -I /usr/src/gnu/lib/libiberty/src -I /usr/src/gnu/lib/libiberty/include -I /usr/src/gnu/lib/libiberty/obj -D PIC -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/gnu/lib/libiberty/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/gnu/lib/libiberty/src/pex-unix.c
1/* Utilities to execute a program in a subprocess (possibly linked by pipes
2 with other subprocesses), and wait for it. Generic Unix version
3 (also used for UWIN and VMS).
4 Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005
5 Free Software Foundation, Inc.
6
7This file is part of the libiberty library.
8Libiberty is free software; you can redistribute it and/or
9modify it under the terms of the GNU Library General Public
10License as published by the Free Software Foundation; either
11version 2 of the License, or (at your option) any later version.
12
13Libiberty is distributed in the hope that it will be useful,
14but WITHOUT ANY WARRANTY; without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16Library General Public License for more details.
17
18You should have received a copy of the GNU Library General Public
19License along with libiberty; see the file COPYING.LIB. If not,
20write to the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
21Boston, MA 02110-1301, USA. */
22
23#include "config.h"
24#include "libiberty.h"
25#include "pex-common.h"
26
27#include <stdio.h>
28#include <signal.h>
29#include <errno(*__errno()).h>
30#ifdef NEED_DECLARATION_ERRNO
31extern int errno(*__errno());
32#endif
33#ifdef HAVE_STDLIB_H1
34#include <stdlib.h>
35#endif
36#ifdef HAVE_STRING_H1
37#include <string.h>
38#endif
39#ifdef HAVE_UNISTD_H1
40#include <unistd.h>
41#endif
42
43#include <sys/types.h>
44
45#ifdef HAVE_FCNTL_H1
46#include <fcntl.h>
47#endif
48#ifdef HAVE_SYS_WAIT_H1
49#include <sys/wait.h>
50#endif
51#ifdef HAVE_GETRUSAGE1
52#include <sys/time.h>
53#include <sys/resource.h>
54#endif
55#ifdef HAVE_SYS_STAT_H1
56#include <sys/stat.h>
57#endif
58
59
60#ifdef vfork /* Autoconf may define this to fork for us. */
61# define VFORK_STRING"vfork" "fork"
62#else
63# define VFORK_STRING"vfork" "vfork"
64#endif
65#ifdef HAVE_VFORK_H
66#include <vfork.h>
67#endif
68#ifdef VMS
69#define vfork() (decc$$alloc_vfork_blocks() >= 0 ? \
70 lib$get_current_invo_context(decc$$get_vfork_jmpbuf()) : -1)
71#endif /* VMS */
72
73
74/* File mode to use for private and world-readable files. */
75
76#if defined (S_IRUSR0000400) && defined (S_IWUSR0000200) && defined (S_IRGRP0000040) && defined (S_IWGRP0000020) && defined (S_IROTH0000004) && defined (S_IWOTH0000002)
77#define PUBLIC_MODE(0000400 | 0000200 | 0000040 | 0000020 | 0000004 | 0000002) \
78 (S_IRUSR0000400 | S_IWUSR0000200 | S_IRGRP0000040 | S_IWGRP0000020 | S_IROTH0000004 | S_IWOTH0000002)
79#else
80#define PUBLIC_MODE(0000400 | 0000200 | 0000040 | 0000020 | 0000004 | 0000002) 0666
81#endif
82
83/* Get the exit status of a particular process, and optionally get the
84 time that it took. This is simple if we have wait4, slightly
85 harder if we have waitpid, and is a pain if we only have wait. */
86
87static pid_t pex_wait (struct pex_obj *, pid_t, int *, struct pex_time *);
88
89#ifdef HAVE_WAIT41
90
91static pid_t
92pex_wait (struct pex_obj *obj ATTRIBUTE_UNUSED__attribute__ ((__unused__)), pid_t pid, int *status,
93 struct pex_time *time)
94{
95 pid_t ret;
96 struct rusage r;
97
98#ifdef HAVE_WAITPID1
99 if (time == NULL((void*)0))
100 return waitpid (pid, status, 0);
101#endif
102
103 ret = wait4 (pid, status, 0, &r);
104
105 if (time != NULL((void*)0))
106 {
107 time->user_seconds = r.ru_utime.tv_sec;
108 time->user_microseconds= r.ru_utime.tv_usec;
109 time->system_seconds = r.ru_stime.tv_sec;
110 time->system_microseconds= r.ru_stime.tv_usec;
111 }
112
113 return ret;
114}
115
116#else /* ! defined (HAVE_WAIT4) */
117
118#ifdef HAVE_WAITPID1
119
120#ifndef HAVE_GETRUSAGE1
121
122static pid_t
123pex_wait (struct pex_obj *obj ATTRIBUTE_UNUSED__attribute__ ((__unused__)), pid_t pid, int *status,
124 struct pex_time *time)
125{
126 if (time != NULL((void*)0))
127 memset (time, 0, sizeof (struct pex_time));
128 return waitpid (pid, status, 0);
129}
130
131#else /* defined (HAVE_GETRUSAGE) */
132
133static pid_t
134pex_wait (struct pex_obj *obj ATTRIBUTE_UNUSED__attribute__ ((__unused__)), pid_t pid, int *status,
135 struct pex_time *time)
136{
137 struct rusage r1, r2;
138 pid_t ret;
139
140 if (time == NULL((void*)0))
141 return waitpid (pid, status, 0);
142
143 getrusage (RUSAGE_CHILDREN(-1), &r1);
144
145 ret = waitpid (pid, status, 0);
146 if (ret < 0)
147 return ret;
148
149 getrusage (RUSAGE_CHILDREN(-1), &r2);
150
151 time->user_seconds = r2.ru_utime.tv_sec - r1.ru_utime.tv_sec;
152 time->user_microseconds = r2.ru_utime.tv_usec - r1.ru_utime.tv_usec;
153 if (r2.ru_utime.tv_usec < r1.ru_utime.tv_usec)
154 {
155 --time->user_seconds;
156 time->user_microseconds += 1000000;
157 }
158
159 time->system_seconds = r2.ru_stime.tv_sec - r1.ru_stime.tv_sec;
160 time->system_microseconds = r2.ru_stime.tv_usec - r1.ru_stime.tv_usec;
161 if (r2.ru_stime.tv_usec < r1.ru_stime.tv_usec)
162 {
163 --time->system_seconds;
164 time->system_microseconds += 1000000;
165 }
166
167 return ret;
168}
169
170#endif /* defined (HAVE_GETRUSAGE) */
171
172#else /* ! defined (HAVE_WAITPID) */
173
174struct status_list
175{
176 struct status_list *next;
177 pid_t pid;
178 int status;
179 struct pex_time time;
180};
181
182static pid_t
183pex_wait (struct pex_obj *obj, pid_t pid, int *status, struct pex_time *time)
184{
185 struct status_list **pp;
186
187 for (pp = (struct status_list **) &obj->sysdep;
188 *pp != NULL((void*)0);
189 pp = &(*pp)->next)
190 {
191 if ((*pp)->pid == pid)
192 {
193 struct status_list *p;
194
195 p = *pp;
196 *status = p->status;
197 if (time != NULL((void*)0))
198 *time = p->time;
199 *pp = p->next;
200 free (p);
201 return pid;
202 }
203 }
204
205 while (1)
206 {
207 pid_t cpid;
208 struct status_list *psl;
209 struct pex_time pt;
210#ifdef HAVE_GETRUSAGE1
211 struct rusage r1, r2;
212#endif
213
214 if (time != NULL((void*)0))
215 {
216#ifdef HAVE_GETRUSAGE1
217 getrusage (RUSAGE_CHILDREN(-1), &r1);
218#else
219 memset (&pt, 0, sizeof (struct pex_time));
220#endif
221 }
222
223 cpid = wait (status);
224
225#ifdef HAVE_GETRUSAGE1
226 if (time != NULL((void*)0) && cpid >= 0)
227 {
228 getrusage (RUSAGE_CHILDREN(-1), &r2);
229
230 pt.user_seconds = r2.ru_utime.tv_sec - r1.ru_utime.tv_sec;
231 pt.user_microseconds = r2.ru_utime.tv_usec - r1.ru_utime.tv_usec;
232 if (pt.user_microseconds < 0)
233 {
234 --pt.user_seconds;
235 pt.user_microseconds += 1000000;
236 }
237
238 pt.system_seconds = r2.ru_stime.tv_sec - r1.ru_stime.tv_sec;
239 pt.system_microseconds = r2.ru_stime.tv_usec - r1.ru_stime.tv_usec;
240 if (pt.system_microseconds < 0)
241 {
242 --pt.system_seconds;
243 pt.system_microseconds += 1000000;
244 }
245 }
246#endif
247
248 if (cpid < 0 || cpid == pid)
249 {
250 if (time != NULL((void*)0))
251 *time = pt;
252 return cpid;
253 }
254
255 psl = XNEW (struct status_list)((struct status_list *) xmalloc (sizeof (struct status_list))
)
;
256 psl->pid = cpid;
257 psl->status = *status;
258 if (time != NULL((void*)0))
259 psl->time = pt;
260 psl->next = (struct status_list *) obj->sysdep;
261 obj->sysdep = (void *) psl;
262 }
263}
264
265#endif /* ! defined (HAVE_WAITPID) */
266#endif /* ! defined (HAVE_WAIT4) */
267
268static void pex_child_error (struct pex_obj *, const char *, const char *, int)
269 ATTRIBUTE_NORETURN__attribute__ ((__noreturn__));
270static int pex_unix_open_read (struct pex_obj *, const char *, int);
271static int pex_unix_open_write (struct pex_obj *, const char *, int);
272static long pex_unix_exec_child (struct pex_obj *, int, const char *,
273 char * const *, char * const *,
274 int, int, int, int,
275 const char **, int *);
276static int pex_unix_close (struct pex_obj *, int);
277static int pex_unix_wait (struct pex_obj *, long, int *, struct pex_time *,
278 int, const char **, int *);
279static int pex_unix_pipe (struct pex_obj *, int *, int);
280static FILE *pex_unix_fdopenr (struct pex_obj *, int, int);
281static FILE *pex_unix_fdopenw (struct pex_obj *, int, int);
282static void pex_unix_cleanup (struct pex_obj *);
283
284/* The list of functions we pass to the common routines. */
285
286const struct pex_funcs funcs =
287{
288 pex_unix_open_read,
289 pex_unix_open_write,
290 pex_unix_exec_child,
291 pex_unix_close,
292 pex_unix_wait,
293 pex_unix_pipe,
294 pex_unix_fdopenr,
295 pex_unix_fdopenw,
296 pex_unix_cleanup
297};
298
299/* Return a newly initialized pex_obj structure. */
300
301struct pex_obj *
302pex_init (int flags, const char *pname, const char *tempbase)
303{
304 return pex_init_common (flags, pname, tempbase, &funcs);
305}
306
307/* Open a file for reading. */
308
309static int
310pex_unix_open_read (struct pex_obj *obj ATTRIBUTE_UNUSED__attribute__ ((__unused__)), const char *name,
311 int binary ATTRIBUTE_UNUSED__attribute__ ((__unused__)))
312{
313 return open (name, O_RDONLY0x0000);
314}
315
316/* Open a file for writing. */
317
318static int
319pex_unix_open_write (struct pex_obj *obj ATTRIBUTE_UNUSED__attribute__ ((__unused__)), const char *name,
320 int binary ATTRIBUTE_UNUSED__attribute__ ((__unused__)))
321{
322 /* Note that we can't use O_EXCL here because gcc may have already
323 created the temporary file via make_temp_file. */
324 return open (name, O_WRONLY0x0001 | O_CREAT0x0200 | O_TRUNC0x0400, PUBLIC_MODE(0000400 | 0000200 | 0000040 | 0000020 | 0000004 | 0000002));
325}
326
327/* Close a file. */
328
329static int
330pex_unix_close (struct pex_obj *obj ATTRIBUTE_UNUSED__attribute__ ((__unused__)), int fd)
331{
332 return close (fd);
333}
334
335/* Report an error from a child process. We don't use stdio routines,
336 because we might be here due to a vfork call. */
337
338static void
339pex_child_error (struct pex_obj *obj, const char *executable,
340 const char *errmsg, int err)
341{
342#define writeerr(s)write (2, s, strlen (s)) write (STDERR_FILE_NO2, s, strlen (s))
343 writeerr (obj->pname)write (2, obj->pname, strlen (obj->pname));
344 writeerr (": error trying to exec '")write (2, ": error trying to exec '", strlen (": error trying to exec '"
))
;
345 writeerr (executable)write (2, executable, strlen (executable));
346 writeerr ("': ")write (2, "': ", strlen ("': "));
347 writeerr (errmsg)write (2, errmsg, strlen (errmsg));
348 writeerr (": ")write (2, ": ", strlen (": "));
349 writeerr (xstrerror (err))write (2, xstrerror (err), strlen (xstrerror (err)));
350 writeerr ("\n")write (2, "\n", strlen ("\n"));
351 _exit (-1);
352}
353
354/* Execute a child. */
355
356extern char **environ;
357
358static long
359pex_unix_exec_child (struct pex_obj *obj, int flags, const char *executable,
360 char * const * argv, char * const * env,
361 int in, int out, int errdes,
362 int toclose, const char **errmsg, int *err)
363{
364 pid_t pid;
365
366 /* We declare these to be volatile to avoid warnings from gcc about
367 them being clobbered by vfork. */
368 volatile int sleep_interval;
369 volatile int retries;
370
371 sleep_interval = 1;
372 pid = -1;
373 for (retries = 0; retries < 4; ++retries)
374 {
375 pid = vfork ();
Call to function 'vfork' is insecure as it can lead to denial of service situations in the parent process. Replace calls to vfork with calls to the safer 'posix_spawn' function
376 if (pid >= 0)
377 break;
378 sleep (sleep_interval);
379 sleep_interval *= 2;
380 }
381
382 switch (pid)
383 {
384 case -1:
385 *err = errno(*__errno());
386 *errmsg = VFORK_STRING"vfork";
387 return -1;
388
389 case 0:
390 /* Child process. */
391 if (in != STDIN_FILE_NO0)
392 {
393 if (dup2 (in, STDIN_FILE_NO0) < 0)
394 pex_child_error (obj, executable, "dup2", errno(*__errno()));
395 if (close (in) < 0)
396 pex_child_error (obj, executable, "close", errno(*__errno()));
397 }
398 if (out != STDOUT_FILE_NO1)
399 {
400 if (dup2 (out, STDOUT_FILE_NO1) < 0)
401 pex_child_error (obj, executable, "dup2", errno(*__errno()));
402 if (close (out) < 0)
403 pex_child_error (obj, executable, "close", errno(*__errno()));
404 }
405 if (errdes != STDERR_FILE_NO2)
406 {
407 if (dup2 (errdes, STDERR_FILE_NO2) < 0)
408 pex_child_error (obj, executable, "dup2", errno(*__errno()));
409 if (close (errdes) < 0)
410 pex_child_error (obj, executable, "close", errno(*__errno()));
411 }
412 if (toclose >= 0)
413 {
414 if (close (toclose) < 0)
415 pex_child_error (obj, executable, "close", errno(*__errno()));
416 }
417 if ((flags & PEX_STDERR_TO_STDOUT0x8) != 0)
418 {
419 if (dup2 (STDOUT_FILE_NO1, STDERR_FILE_NO2) < 0)
420 pex_child_error (obj, executable, "dup2", errno(*__errno()));
421 }
422
423 if (env)
424 environ = (char**) env;
425
426 if ((flags & PEX_SEARCH0x2) != 0)
427 {
428 execvp (executable, argv);
429 pex_child_error (obj, executable, "execvp", errno(*__errno()));
430 }
431 else
432 {
433 execv (executable, argv);
434 pex_child_error (obj, executable, "execv", errno(*__errno()));
435 }
436
437 /* NOTREACHED */
438 return -1;
439
440 default:
441 /* Parent process. */
442 if (in != STDIN_FILE_NO0)
443 {
444 if (close (in) < 0)
445 {
446 *err = errno(*__errno());
447 *errmsg = "close";
448 return -1;
449 }
450 }
451 if (out != STDOUT_FILE_NO1)
452 {
453 if (close (out) < 0)
454 {
455 *err = errno(*__errno());
456 *errmsg = "close";
457 return -1;
458 }
459 }
460 if (errdes != STDERR_FILE_NO2)
461 {
462 if (close (errdes) < 0)
463 {
464 *err = errno(*__errno());
465 *errmsg = "close";
466 return -1;
467 }
468 }
469
470 return (long) pid;
471 }
472}
473
474/* Wait for a child process to complete. */
475
476static int
477pex_unix_wait (struct pex_obj *obj, long pid, int *status,
478 struct pex_time *time, int done, const char **errmsg,
479 int *err)
480{
481 /* If we are cleaning up when the caller didn't retrieve process
482 status for some reason, encourage the process to go away. */
483 if (done)
484 kill (pid, SIGTERM15);
485
486 if (pex_wait (obj, pid, status, time) < 0)
487 {
488 *err = errno(*__errno());
489 *errmsg = "wait";
490 return -1;
491 }
492
493 return 0;
494}
495
496/* Create a pipe. */
497
498static int
499pex_unix_pipe (struct pex_obj *obj ATTRIBUTE_UNUSED__attribute__ ((__unused__)), int *p,
500 int binary ATTRIBUTE_UNUSED__attribute__ ((__unused__)))
501{
502 return pipe (p);
503}
504
505/* Get a FILE pointer to read from a file descriptor. */
506
507static FILE *
508pex_unix_fdopenr (struct pex_obj *obj ATTRIBUTE_UNUSED__attribute__ ((__unused__)), int fd,
509 int binary ATTRIBUTE_UNUSED__attribute__ ((__unused__)))
510{
511 return fdopen (fd, "r");
512}
513
514static FILE *
515pex_unix_fdopenw (struct pex_obj *obj ATTRIBUTE_UNUSED__attribute__ ((__unused__)), int fd,
516 int binary ATTRIBUTE_UNUSED__attribute__ ((__unused__)))
517{
518 if (fcntl (fd, F_SETFD2, FD_CLOEXEC1) < 0)
519 return NULL((void*)0);
520 return fdopen (fd, "w");
521}
522
523static void
524pex_unix_cleanup (struct pex_obj *obj ATTRIBUTE_UNUSED__attribute__ ((__unused__)))
525{
526#if !defined (HAVE_WAIT41) && !defined (HAVE_WAITPID1)
527 while (obj->sysdep != NULL((void*)0))
528 {
529 struct status_list *this;
530 struct status_list *next;
531
532 this = (struct status_list *) obj->sysdep;
533 next = this->next;
534 free (this);
535 obj->sysdep = (void *) next;
536 }
537#endif
538}