Bug Summary

File:src/usr.bin/vi/build/../ex/ex_shell.c
Warning:line 112, column 16
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 ex_shell.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/vi/build/obj -resource-dir /usr/local/lib/clang/13.0.0 -I /usr/src/usr.bin/vi/build -I /usr/src/usr.bin/vi/build/../include -I . -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.bin/vi/build/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/vi/build/../ex/ex_shell.c
1/* $OpenBSD: ex_shell.c,v 1.15 2015/03/28 12:54:37 bcallah Exp $ */
2
3/*-
4 * Copyright (c) 1992, 1993, 1994
5 * The Regents of the University of California. All rights reserved.
6 * Copyright (c) 1992, 1993, 1994, 1995, 1996
7 * Keith Bostic. All rights reserved.
8 *
9 * See the LICENSE file for redistribution information.
10 */
11
12#include "config.h"
13
14#include <sys/queue.h>
15#include <sys/wait.h>
16
17#include <bitstring.h>
18#include <ctype.h>
19#include <errno(*__errno()).h>
20#include <limits.h>
21#include <signal.h>
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25#include <unistd.h>
26
27#include "../common/common.h"
28
29#define MINIMUM(a, b)(((a) < (b)) ? (a) : (b)) (((a) < (b)) ? (a) : (b))
30
31/*
32 * ex_shell -- :sh[ell]
33 * Invoke the program named in the SHELL environment variable
34 * with the argument -i.
35 *
36 * PUBLIC: int ex_shell(SCR *, EXCMD *);
37 */
38int
39ex_shell(SCR *sp, EXCMD *cmdp)
40{
41 int rval;
42 char buf[PATH_MAX1024];
43
44 /* We'll need a shell. */
45 if (opts_empty(sp, O_SHELL, 0))
46 return (1);
47
48 /*
49 * XXX
50 * Assumes all shells use -i.
51 */
52 (void)snprintf(buf, sizeof(buf), "%s -i", O_STR(sp, O_SHELL)((((&((sp))->opts[((O_SHELL))])->flags) & ((0x01
))) ? ((sp))->gp->opts[((sp))->opts[((O_SHELL))].o_cur
.val].o_cur.str : ((sp))->opts[((O_SHELL))].o_cur.str)
);
53
54 /* Restore the window name. */
55 (void)sp->gp->scr_rename(sp, NULL((void *)0), 0);
56
57 /* If we're still in a vi screen, move out explicitly. */
58 rval = ex_exec_proc(sp, cmdp, buf, NULL((void *)0), !F_ISSET(sp, SC_SCR_EXWROTE)(((sp)->flags) & ((0x00000010))));
59
60 /* Set the window name. */
61 (void)sp->gp->scr_rename(sp, sp->frp->name, 1);
62
63 /*
64 * !!!
65 * Historically, vi didn't require a continue message after the
66 * return of the shell. Match it.
67 */
68 F_SET(sp, SC_EX_WAIT_NO)(((sp)->flags) |= ((0x00080000)));
69
70 return (rval);
71}
72
73/*
74 * ex_exec_proc --
75 * Run a separate process.
76 *
77 * PUBLIC: int ex_exec_proc(SCR *, EXCMD *, char *, const char *, int);
78 */
79int
80ex_exec_proc(SCR *sp, EXCMD *cmdp, char *cmd, const char *msg,
81 int need_newline)
82{
83 GS *gp;
84 const char *name;
85 pid_t pid;
86
87 gp = sp->gp;
88
89 /* We'll need a shell. */
90 if (opts_empty(sp, O_SHELL, 0))
91 return (1);
92
93 /* Enter ex mode. */
94 if (F_ISSET(sp, SC_VI)(((sp)->flags) & ((0x00000002)))) {
95 if (gp->scr_screen(sp, SC_EX0x00000001)) {
96 ex_emsg(sp, cmdp->cmd->name, EXM_NOCANON);
97 return (1);
98 }
99 (void)gp->scr_attr(sp, SA_ALTERNATE, 0);
100 F_SET(sp, SC_SCR_EX | SC_SCR_EXWROTE)(((sp)->flags) |= ((0x00000004 | 0x00000010)));
101 }
102
103 /* Put out additional newline, message. */
104 if (need_newline)
105 (void)ex_puts(sp, "\n");
106 if (msg != NULL((void *)0)) {
107 (void)ex_puts(sp, msg);
108 (void)ex_puts(sp, "\n");
109 }
110 (void)ex_fflush(sp);
111
112 switch (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
113 case -1: /* Error. */
114 msgq(sp, M_SYSERR, "vfork");
115 return (1);
116 case 0: /* Utility. */
117 if ((name = strrchr(O_STR(sp, O_SHELL)((((&((sp))->opts[((O_SHELL))])->flags) & ((0x01
))) ? ((sp))->gp->opts[((sp))->opts[((O_SHELL))].o_cur
.val].o_cur.str : ((sp))->opts[((O_SHELL))].o_cur.str)
, '/')) == NULL((void *)0))
118 name = O_STR(sp, O_SHELL)((((&((sp))->opts[((O_SHELL))])->flags) & ((0x01
))) ? ((sp))->gp->opts[((sp))->opts[((O_SHELL))].o_cur
.val].o_cur.str : ((sp))->opts[((O_SHELL))].o_cur.str)
;
119 else
120 ++name;
121 execl(O_STR(sp, O_SHELL)((((&((sp))->opts[((O_SHELL))])->flags) & ((0x01
))) ? ((sp))->gp->opts[((sp))->opts[((O_SHELL))].o_cur
.val].o_cur.str : ((sp))->opts[((O_SHELL))].o_cur.str)
, name, "-c", cmd, (char *)NULL((void *)0));
122 msgq_str(sp, M_SYSERR, O_STR(sp, O_SHELL)((((&((sp))->opts[((O_SHELL))])->flags) & ((0x01
))) ? ((sp))->gp->opts[((sp))->opts[((O_SHELL))].o_cur
.val].o_cur.str : ((sp))->opts[((O_SHELL))].o_cur.str)
, "execl: %s");
123 _exit(127);
124 /* NOTREACHED */
125 default: /* Parent. */
126 return (proc_wait(sp, pid, cmd, 0, 0));
127 }
128 /* NOTREACHED */
129}
130
131/*
132 * proc_wait --
133 * Wait for one of the processes.
134 *
135 * !!!
136 * The pid_t type varies in size from a short to a long depending on the
137 * system. It has to be cast into something or the standard promotion
138 * rules get you. I'm using a long based on the belief that nobody is
139 * going to make it unsigned and it's unlikely to be a quad.
140 *
141 * PUBLIC: int proc_wait(SCR *, pid_t, const char *, int, int);
142 */
143int
144proc_wait(SCR *sp, pid_t pid, const char *cmd, int silent, int okpipe)
145{
146 size_t len;
147 int nf, pstat;
148 char *p;
149
150 /* Wait for the utility, ignoring interruptions. */
151 for (;;) {
152 errno(*__errno()) = 0;
153 if (waitpid(pid, &pstat, 0) != -1)
154 break;
155 if (errno(*__errno()) != EINTR4) {
156 msgq(sp, M_SYSERR, "waitpid");
157 return (1);
158 }
159 }
160
161 /*
162 * Display the utility's exit status. Ignore SIGPIPE from the
163 * parent-writer, as that only means that the utility chose to
164 * exit before reading all of its input.
165 */
166 if (WIFSIGNALED(pstat)(((pstat) & 0177) != 0177 && ((pstat) & 0177)
!= 0)
&& (!okpipe || WTERMSIG(pstat)(((pstat) & 0177)) != SIGPIPE13)) {
167 for (; isblank(*cmd); ++cmd);
168 p = msg_print(sp, cmd, &nf);
169 len = strlen(p);
170 msgq(sp, M_ERR, "%.*s%s: received signal: %s%s",
171 MINIMUM(len, 20)(((len) < (20)) ? (len) : (20)), p, len > 20 ? " ..." : "",
172 strsignal(WTERMSIG(pstat)(((pstat) & 0177))),
173 WCOREDUMP(pstat)((pstat) & 0200) ? "; core dumped" : "");
174 if (nf)
175 FREE_SPACE(sp, p, 0){ GS *L__gp = (sp) == ((void *)0) ? ((void *)0) : (sp)->gp
; if (L__gp != ((void *)0) && (p) == L__gp->tmp_bp
) (((L__gp)->flags) &= ~((0x0100))); else free(p); }
;
176 return (1);
177 }
178
179 if (WIFEXITED(pstat)(((pstat) & 0177) == 0) && WEXITSTATUS(pstat)(int)(((unsigned)(pstat) >> 8) & 0xff)) {
180 /*
181 * Remain silent for "normal" errors when doing shell file
182 * name expansions, they almost certainly indicate nothing
183 * more than a failure to match.
184 *
185 * Remain silent for vi read filter errors. It's historic
186 * practice.
187 */
188 if (!silent) {
189 for (; isblank(*cmd); ++cmd);
190 p = msg_print(sp, cmd, &nf);
191 len = strlen(p);
192 msgq(sp, M_ERR, "%.*s%s: exited with status %d",
193 MINIMUM(len, 20)(((len) < (20)) ? (len) : (20)), p, len > 20 ? " ..." : "",
194 WEXITSTATUS(pstat)(int)(((unsigned)(pstat) >> 8) & 0xff));
195 if (nf)
196 FREE_SPACE(sp, p, 0){ GS *L__gp = (sp) == ((void *)0) ? ((void *)0) : (sp)->gp
; if (L__gp != ((void *)0) && (p) == L__gp->tmp_bp
) (((L__gp)->flags) &= ~((0x0100))); else free(p); }
;
197 }
198 return (1);
199 }
200 return (0);
201}