Bug Summary

File:src/usr.bin/vi/build/../ex/ex_shell.c
Warning:line 117, column 15
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 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))
1
Assuming the condition is false
2
Taking false branch
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)
);
3
Assuming the condition is false
4
'?' condition is false
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))));
5
Assuming the condition is false
6
Calling 'ex_exec_proc'
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))
7
Assuming the condition is false
8
Taking false branch
91 return (1);
92
93 /* Enter ex mode. */
94 if (F_ISSET(sp, SC_VI)(((sp)->flags) & ((0x00000002)))) {
9
Assuming the condition is false
10
Taking false branch
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
10.1
'need_newline' is 0
)
11
Taking false branch
105 (void)ex_puts(sp, "\n");
106 if (msg
11.1
'msg' is equal to NULL
!= NULL((void *)0)) {
12
Taking false branch
107 (void)ex_puts(sp, msg);
108 (void)ex_puts(sp, "\n");
109 }
110 (void)ex_fflush(sp);
111
112 switch (pid = vfork()) {
13
Control jumps to 'case 0:' at line 116
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))
14
Assuming the condition is false
15
'?' condition is false
16
This function call is prohibited after a successful vfork
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}