Bug Summary

File:src/usr.bin/tput/tput.c
Warning:line 276, column 17
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 tput.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/tput/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/tput/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/tput/tput.c
1/* $OpenBSD: tput.c,v 1.24 2019/01/25 00:19:27 millert Exp $ */
2
3/*
4 * Copyright (c) 1999 Todd C. Miller <millert@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18/*-
19 * Copyright (c) 1980, 1988, 1993
20 * The Regents of the University of California. All rights reserved.
21 *
22 * Redistribution and use in source and binary forms, with or without
23 * modification, are permitted provided that the following conditions
24 * are met:
25 * 1. Redistributions of source code must retain the above copyright
26 * notice, this list of conditions and the following disclaimer.
27 * 2. Redistributions in binary form must reproduce the above copyright
28 * notice, this list of conditions and the following disclaimer in the
29 * documentation and/or other materials provided with the distribution.
30 * 3. Neither the name of the University nor the names of its contributors
31 * may be used to endorse or promote products derived from this software
32 * without specific prior written permission.
33 *
34 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
35 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
36 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
37 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
38 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
39 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
40 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
41 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
42 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
43 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
44 * SUCH DAMAGE.
45 */
46
47#include <ctype.h>
48#include <err.h>
49#include <curses.h>
50#include <term.h>
51#include <stdio.h>
52#include <stdlib.h>
53#include <termios.h>
54#include <unistd.h>
55#include <errno(*__errno()).h>
56#include <limits.h>
57#include <string.h>
58
59#define MAXIMUM(a, b)(((a) > (b)) ? (a) : (b)) (((a) > (b)) ? (a) : (b))
60
61#include <sys/wait.h>
62
63static void init(void);
64static char **process(char *, char *, char **);
65static void reset(void);
66static void set_margins(void);
67static void usage(void);
68
69extern char *__progname;
70
71int
72main(int argc, char *argv[])
73{
74 int ch, exitval, n, Sflag;
75 size_t len;
76 char *p, *term, *str;
77 char **oargv;
78
79 if (pledge("stdio rpath wpath tty", NULL((void *)0)) == -1)
80 err(1, "pledge");
81
82 oargv = argv;
83 term = NULL((void *)0);
84 Sflag = exitval = 0;
85 while ((ch = getopt(argc, argv, "ST:")) != -1)
86 switch(ch) {
87 case 'T':
88 term = optarg;
89 break;
90 case 'S':
91 Sflag = 1;
92 break;
93 case '?':
94 default:
95 usage();
96 }
97 argc -= optind;
98 argv += optind;
99
100 if (Sflag && argc > 0)
101 usage();
102
103 if (!term && !(term = getenv("TERM")))
104 errx(2, "No value for $TERM and no -T specified");
105
106 /*
107 * NOTE: tgetent() will call setupterm() and set ospeed for us
108 * (this is ncurses-specific behavior)
109 */
110 if (tgetent(NULL((void *)0), term) != 1)
111 errx(3, "Unknown terminal type `%s'", term);
112
113 if (strcmp(__progname, "clear") == 0) {
114 if (Sflag)
115 usage();
116 argv = oargv;
117 *argv = __progname;
118 *(argv+1) = NULL((void *)0);
119 }
120 if (Sflag) {
121 char **av;
122
123 /* Build new argv based on stdin */
124 argc = n = 0;
125 av = NULL((void *)0);
126 while ((str = fgetln(stdin(&__sF[0]), &len)) != NULL((void *)0)) {
127 if (str[len-1] != '\n')
128 errx(1, "premature EOF");
129 str[len-1] = '\0';
130 while ((p = strsep(&str, " \t")) != NULL((void *)0)) {
131 /* grow av as needed */
132 if (argc + 1 >= n) {
133 n += 64;
134 av = reallocarray(av, n,
135 sizeof(char *));
136 if (av == NULL((void *)0))
137 errx(1, "out of memory");
138 }
139 if (*p != '\0' &&
140 (av[argc++] = strdup(p)) == NULL((void *)0))
141 errx(1, "out of memory");
142 }
143 }
144 if (argc > 0) {
145 av[argc] = NULL((void *)0);
146 argv = av;
147 }
148 }
149 while ((p = *argv++)) {
150 switch (*p) {
151 case 'i':
152 if (!strcmp(p, "init")) {
153 init();
154 continue;
155 }
156 break;
157 case 'l':
158 if (!strcmp(p, "longname")) {
159 puts(longname());
160 continue;
161 }
162 break;
163 case 'r':
164 if (!strcmp(p, "reset")) {
165 reset();
166 continue;
167 }
168 break;
169 }
170
171 /* First try as terminfo */
172 if ((str = tigetstr(p)) && str != (char *)-1)
173 argv = process(p, str, argv);
174 else if ((n = tigetnum(p)) != -2)
175 (void)printf("%d\n", n);
176 else if ((n = tigetflag(p)) != -1)
177 exitval = !n;
178 /* Then fall back on termcap */
179 else if ((str = tgetstr(p, NULL((void *)0))))
180 argv = process(p, str, argv);
181 else if ((n = tgetnum(p)) != -1)
182 (void)printf("%d\n", n);
183 else if ((exitval = tgetflag(p)) != 0)
184 exitval = !exitval;
185 else {
186 warnx("Unknown terminfo capability `%s'", p);
187 exitval = 4;
188 }
189 }
190 exit(exitval);
191}
192
193static char **
194process(char *cap, char *str, char **argv)
195{
196 char *cp, *s, *nargv[9];
197 int arg_need, popcount, i;
198
199 /* Count how many values we need for this capability. */
200 for (cp = str, arg_need = popcount = 0; *cp != '\0'; cp++) {
201 if (*cp == '%') {
202 switch (*++cp) {
203 case '%':
204 cp++;
205 break;
206 case 'i':
207 if (popcount < 2)
208 popcount = 2;
209 break;
210 case 'p':
211 cp++;
212 if (isdigit((unsigned char)cp[1]) &&
213 popcount < cp[1] - '0')
214 popcount = cp[1] - '0';
215 break;
216 case 'd':
217 case 's':
218 case '0':
219 case '1':
220 case '2':
221 case '3':
222 case '4':
223 case '5':
224 case '6':
225 case '7':
226 case '8':
227 case '9':
228 case '.':
229 case '+':
230 arg_need++;
231 break;
232 default:
233 break;
234 }
235 }
236 }
237 arg_need = MAXIMUM(arg_need, popcount)(((arg_need) > (popcount)) ? (arg_need) : (popcount));
238 if (arg_need > 9)
239 errx(2, "too many arguments (%d) for capability `%s'",
240 arg_need, cap);
241
242 for (i = 0; i < arg_need; i++) {
243 long l;
244
245 if (argv[i] == NULL((void *)0))
246 errx(2, "not enough arguments (%d) for capability `%s'",
247 arg_need, cap);
248
249 /* convert ascii representation of numbers to longs */
250 if (isdigit((unsigned char)argv[i][0])
251 && (l = strtol(argv[i], &cp, 10)) >= 0
252 && l < LONG_MAX9223372036854775807L && *cp == '\0')
253 nargv[i] = (char *)l;
254 else
255 nargv[i] = argv[i];
256 }
257
258 s = tparm(str, nargv[0], nargv[1], nargv[2], nargv[3],
259 nargv[4], nargv[5], nargv[6], nargv[7], nargv[8]);
260 putp(s);
261 fflush(stdout(&__sF[1]));
262
263 return (argv + arg_need);
264}
265
266static void
267init(void)
268{
269 FILE *ifile;
270 size_t len;
271 char *buf;
272 int wstatus;
273 pid_t pid;
274
275 if (init_progcur_term->type. Strings[138] && !issetugid()) {
276 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
277 case -1:
278 err(4, "vfork");
279 break;
280 case 0:
281 /* child */
282 execl(init_progcur_term->type. Strings[138], init_progcur_term->type. Strings[138], (char *)NULL((void *)0));
283 _exit(127);
284 break;
285 default:
286 while (waitpid(pid, &wstatus, 0) == -1) {
287 if (errno(*__errno()) != EINTR4)
288 break;
289 }
290 /* parent */
291 break;
292 }
293 }
294 if (init_1stringcur_term->type. Strings[48])
295 putp(init_1stringcur_term->type. Strings[48]);
296 if (init_2stringcur_term->type. Strings[49])
297 putp(init_2stringcur_term->type. Strings[49]);
298 set_margins();
299 /* always use 8 space tabs */
300 if (init_tabscur_term->type. Numbers[1] != 8 && clear_all_tabscur_term->type. Strings[4] && set_tabcur_term->type. Strings[132]) {
301 int i;
302
303 putp(clear_all_tabscur_term->type. Strings[4]);
304 for (i = 0; i < (columnscur_term->type. Numbers[0] - 1) / 8; i++) {
305 if (parm_right_cursorcur_term->type. Strings[112])
306 putp(tparm(parm_right_cursorcur_term->type. Strings[112], 8));
307 else
308 fputs(" ", stdout(&__sF[1]));
309 putp(set_tabcur_term->type. Strings[132]);
310 }
311 }
312 if (init_filecur_term->type. Strings[51] && !issetugid() && (ifile = fopen(init_filecur_term->type. Strings[51], "r"))) {
313 while ((buf = fgetln(ifile, &len)) != NULL((void *)0)) {
314 if (buf[len-1] != '\n')
315 errx(1, "premature EOF reading %s", init_filecur_term->type. Strings[51]);
316 buf[len-1] = '\0';
317 putp(buf);
318 }
319 fclose(ifile);
320 }
321 if (init_3stringcur_term->type. Strings[50])
322 putp(init_3stringcur_term->type. Strings[50]);
323 fflush(stdout(&__sF[1]));
324}
325
326static void
327reset(void)
328{
329 FILE *rfile;
330 size_t len;
331 char *buf;
332
333 if (reset_1stringcur_term->type. Strings[122])
334 putp(reset_1stringcur_term->type. Strings[122]);
335 if (reset_2stringcur_term->type. Strings[123])
336 putp(reset_2stringcur_term->type. Strings[123]);
337 set_margins();
338 if (reset_filecur_term->type. Strings[125] && !issetugid() && (rfile = fopen(reset_filecur_term->type. Strings[125], "r"))) {
339 while ((buf = fgetln(rfile, &len)) != NULL((void *)0)) {
340 if (buf[len-1] != '\n')
341 errx(1, "premature EOF reading %s", reset_filecur_term->type. Strings[125]);
342 buf[len-1] = '\0';
343 putp(buf);
344 }
345 fclose(rfile);
346 }
347 if (reset_3stringcur_term->type. Strings[124])
348 putp(reset_3stringcur_term->type. Strings[124]);
349 fflush(stdout(&__sF[1]));
350}
351
352static void
353set_margins(void)
354{
355
356 /*
357 * Four possibilities:
358 * 1) we have set_lr_margin and can set things with one call
359 * 2) we have set_{left,right}_margin_parm, use two calls
360 * 3) we have set_{left,right}_margin, set based on position
361 * 4) none of the above, leave things the way they are
362 */
363 if (set_lr_margincur_term->type. Strings[368]) {
364 putp(tparm(set_lr_margincur_term->type. Strings[368], 0, columnscur_term->type. Numbers[0] - 1));
365 } else if (set_left_margin_parmcur_term->type. Strings[342] && set_right_margin_parmcur_term->type. Strings[343]) {
366 putp(tparm(set_left_margin_parmcur_term->type. Strings[342], 0));
367 putp(tparm(set_right_margin_parmcur_term->type. Strings[343], columnscur_term->type. Numbers[0] - 1));
368 } else if (set_left_margincur_term->type. Strings[271] && set_right_margincur_term->type. Strings[272] && clear_marginscur_term->type. Strings[270]) {
369 putp(clear_marginscur_term->type. Strings[270]);
370
371 /* go to column 0 and set the left margin */
372 putp(carriage_returncur_term->type. Strings[2] ? carriage_returncur_term->type. Strings[2] : "\r");
373 putp(set_left_margincur_term->type. Strings[271]);
374
375 /* go to last column and set the right margin */
376 if (parm_right_cursorcur_term->type. Strings[112])
377 putp(tparm(parm_right_cursorcur_term->type. Strings[112], columnscur_term->type. Numbers[0] - 1));
378 else
379 printf("%*s", columnscur_term->type. Numbers[0] - 1, " ");
380 putp(set_right_margincur_term->type. Strings[272]);
381 putp(carriage_returncur_term->type. Strings[2] ? carriage_returncur_term->type. Strings[2] : "\r");
382 }
383 fflush(stdout(&__sF[1]));
384}
385
386static void
387usage(void)
388{
389
390 if (strcmp(__progname, "clear") == 0)
391 (void)fprintf(stderr(&__sF[2]), "usage: %s [-T term]\n", __progname);
392 else
393 (void)fprintf(stderr(&__sF[2]),
394 "usage: %s [-T term] attribute [attribute-args] ...\n"
395 " %s [-T term] -S\n", __progname, __progname);
396 exit(1);
397}