Bug Summary

File:src/usr.bin/uudecode/uudecode.c
Warning:line 292, column 5
Address of stack memory associated with local variable 'buf' is still referred to by the global variable 'outfile' upon returning to the caller. This will be a dangling reference

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 uudecode.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/uudecode/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/uudecode/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/uudecode/uudecode.c
1/* $OpenBSD: uudecode.c,v 1.27 2019/06/28 13:35:05 deraadt Exp $ */
2/* $FreeBSD: uudecode.c,v 1.49 2003/05/03 19:44:46 obrien Exp $ */
3
4/*-
5 * Copyright (c) 1983, 1993
6 * The Regents of the University of California. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33/*
34 * Create the specified file, decoding as you go.
35 * Used with uuencode.
36 */
37
38#include <sys/stat.h>
39
40#include <netinet/in.h>
41
42#include <err.h>
43#include <errno(*__errno()).h>
44#include <fcntl.h>
45#include <limits.h>
46#include <pwd.h>
47#include <resolv.h>
48#include <stdio.h>
49#include <stdlib.h>
50#include <string.h>
51#include <unistd.h>
52
53static const char *infile, *outfile;
54static FILE *infp, *outfp;
55static int base64, cflag, iflag, oflag, pflag, rflag, sflag;
56
57static void __dead__attribute__((__noreturn__)) usage(void);
58static int decode(void);
59static int decode2(void);
60static int uu_decode(void);
61static int base64_decode(void);
62
63enum program_mode {
64 MODE_DECODE,
65 MODE_B64DECODE
66} pmode;
67
68int
69main(int argc, char *argv[])
70{
71 int rval, ch;
72 extern char *__progname;
73 static const char *optstr[2] = {
74 "cimo:prs",
75 "cio:prs"
76 };
77
78 pmode = MODE_DECODE;
79 if (strcmp(__progname, "b64decode") == 0) {
1
Assuming the condition is false
2
Taking false branch
80 base64 = 1;
81 pmode = MODE_B64DECODE;
82 }
83
84 while ((ch = getopt(argc, argv, optstr[pmode])) != -1) {
3
Assuming the condition is false
4
Loop condition is false. Execution continues on line 123
85 switch(ch) {
86 case 'c':
87 if (oflag || rflag)
88 usage();
89 cflag = 1; /* multiple uudecode'd files */
90 break;
91 case 'i':
92 iflag = 1; /* ask before override files */
93 break;
94 case 'm':
95 base64 = 1;
96 break;
97 case 'o':
98 if (cflag || pflag || rflag || sflag)
99 usage();
100 oflag = 1; /* output to the specified file */
101 sflag = 1; /* do not strip pathnames for output */
102 outfile = optarg; /* set the output filename */
103 break;
104 case 'p':
105 if (oflag)
106 usage();
107 pflag = 1; /* print output to stdout */
108 break;
109 case 'r':
110 if (cflag || oflag)
111 usage();
112 rflag = 1; /* decode raw data */
113 break;
114 case 's':
115 if (oflag)
116 usage();
117 sflag = 1; /* do not strip pathnames for output */
118 break;
119 default:
120 usage();
121 }
122 }
123 argc -= optind;
124 argv += optind;
125
126 if (sflag) {
5
Assuming 'sflag' is not equal to 0
6
Taking true branch
127 if (pledge("stdio rpath wpath cpath getpw", NULL((void *)0)) == -1)
7
Assuming the condition is false
8
Taking false branch
128 err(1, "pledge");
129 } else if (pflag == 0) {
130 if (pledge("stdio rpath wpath cpath", NULL((void *)0)) == -1)
131 err(1, "pledge");
132 } else {
133 if (pledge("stdio rpath", NULL((void *)0)) == -1)
134 err(1, "pledge");
135 }
136
137 if (*argv) {
9
Assuming the condition is false
10
Taking false branch
138 rval = 0;
139 do {
140 infp = fopen(infile = *argv, "r");
141 if (infp == NULL((void *)0)) {
142 warn("%s", *argv);
143 rval = 1;
144 continue;
145 }
146 rval |= decode();
147 fclose(infp);
148 } while (*++argv);
149 } else {
150 infile = "stdin";
151 infp = stdin(&__sF[0]);
152 rval = decode();
11
Calling 'decode'
153 }
154 return (rval);
155}
156
157static int
158decode(void)
159{
160 int r, v;
161
162 if (rflag) {
12
Assuming 'rflag' is 0
13
Taking false branch
163 /* relaxed alternative to decode2() */
164 outfile = "/dev/stdout";
165 outfp = stdout(&__sF[1]);
166 if (base64)
167 return (base64_decode());
168 else
169 return (uu_decode());
170 }
171 v = decode2();
14
Calling 'decode2'
172 if (v == EOF(-1)) {
173 warnx("%s: missing or bad \"begin\" line", infile);
174 return (1);
175 }
176 for (r = v; cflag; r |= v) {
177 v = decode2();
178 if (v == EOF(-1))
179 break;
180 }
181 return (r);
182}
183
184static int
185decode2(void)
186{
187 int flags, fd, mode;
188 size_t n, m;
189 char *p, *q;
190 void *handle;
191 struct passwd *pw;
192 struct stat st;
193 char buf[PATH_MAX1024];
194
195 base64 = 0;
196 /* search for header line */
197 for (;;) {
15
Loop condition is true. Entering loop body
198 if (fgets(buf, sizeof(buf), infp) == NULL((void *)0))
16
Assuming the condition is false
17
Taking false branch
199 return (EOF(-1));
200 p = buf;
201 if (strncmp(p, "begin-base64 ", 13) == 0) {
18
Assuming the condition is true
19
Taking true branch
202 base64 = 1;
203 p += 13;
204 } else if (strncmp(p, "begin ", 6) == 0)
205 p += 6;
206 else
207 continue;
208 /* p points to mode */
209 q = strchr(p, ' ');
210 if (q == NULL((void *)0))
20
Assuming 'q' is not equal to NULL
21
Taking false branch
211 continue;
212 *q++ = '\0';
213 /* q points to filename */
214 n = strlen(q);
215 while (n > 0 && (q[n-1] == '\n' || q[n-1] == '\r'))
22
Assuming 'n' is > 0
23
Assuming the condition is false
24
Loop condition is false. Execution continues on line 218
216 q[--n] = '\0';
217 /* found valid header? */
218 if (n
24.1
'n' is > 0
> 0)
25
Taking true branch
219 break;
26
Execution continues on line 222
220 }
221
222 handle = setmode(p);
223 if (handle == NULL((void *)0)) {
27
Assuming 'handle' is not equal to NULL
28
Taking false branch
224 warnx("%s: unable to parse file mode", infile);
225 return (1);
226 }
227 mode = getmode(handle, 0) & 0666;
228 free(handle);
229
230 if (sflag
28.1
'sflag' is not equal to 0
) {
29
Taking true branch
231 /* don't strip, so try ~user/file expansion */
232 p = NULL((void *)0);
233 pw = NULL((void *)0);
234 if (*q == '~')
30
Assuming the condition is true
31
Taking true branch
235 p = strchr(q, '/');
236 if (p != NULL((void *)0)) {
32
Assuming 'p' is not equal to NULL
33
Taking true branch
237 *p = '\0';
238 pw = getpwnam(q + 1);
239 *p = '/';
240 }
241 if (pw != NULL((void *)0)) {
34
Assuming 'pw' is not equal to NULL
35
Taking true branch
242 n = strlen(pw->pw_dir);
243 if (buf + n > p) {
36
Assuming the condition is true
37
Taking true branch
244 /* make room */
245 m = strlen(p);
246 if (sizeof(buf) < n + m) {
38
Assuming the condition is false
39
Taking false branch
247 warnx("%s: bad output filename",
248 infile);
249 return (1);
250 }
251 p = memmove(buf + n, p, m);
252 }
253 q = memcpy(p - n, pw->pw_dir, n);
254 }
255 } else {
256 /* strip down to leaf name */
257 p = strrchr(q, '/');
258 if (p != NULL((void *)0))
259 q = p + 1;
260 }
261 if (!oflag)
40
Assuming 'oflag' is 0
41
Taking true branch
262 outfile = q;
263
264 /* POSIX says "/dev/stdout" is a 'magic cookie' not a special file. */
265 if (pflag || strcmp(outfile, "/dev/stdout") == 0)
42
Assuming 'pflag' is 0
43
Assuming the condition is false
44
Taking false branch
266 outfp = stdout(&__sF[1]);
267 else {
268 flags = O_WRONLY0x0001|O_CREAT0x0200|O_EXCL0x0800|O_NOFOLLOW0x0100;
269 if (lstat(outfile, &st) == 0) {
45
Assuming the condition is true
46
Taking true branch
270 if (iflag) {
47
Assuming 'iflag' is 0
48
Taking false branch
271 warnc(EEXIST17, "%s: %s", infile, outfile);
272 return (0);
273 }
274 switch (st.st_mode & S_IFMT0170000) {
49
Control jumps to the 'default' case at line 285
275 case S_IFREG0100000:
276 case S_IFLNK0120000:
277 /* avoid symlink attacks */
278 if (unlink(outfile) == 0 || errno(*__errno()) == ENOENT2)
279 break;
280 warn("%s: unlink %s", infile, outfile);
281 return (1);
282 case S_IFDIR0040000:
283 warnc(EISDIR21, "%s: %s", infile, outfile);
284 return (1);
285 default:
286 if (oflag
49.1
'oflag' is 0
) {
50
Taking false branch
287 /* trust command-line names */
288 flags &= ~(O_EXCL0x0800|O_NOFOLLOW0x0100);
289 break;
290 }
291 warnc(EEXIST17, "%s: %s", infile, outfile);
292 return (1);
51
Address of stack memory associated with local variable 'buf' is still referred to by the global variable 'outfile' upon returning to the caller. This will be a dangling reference
293 }
294 } else if (errno(*__errno()) != ENOENT2) {
295 warn("%s: %s", infile, outfile);
296 return (1);
297 }
298 if ((fd = open(outfile, flags, mode)) == -1 ||
299 (outfp = fdopen(fd, "w")) == NULL((void *)0)) {
300 warn("%s: %s", infile, outfile);
301 return (1);
302 }
303 }
304
305 if (base64)
306 return (base64_decode());
307 else
308 return (uu_decode());
309}
310
311static int
312get_line(char *buf, size_t size)
313{
314 if (fgets(buf, size, infp) != NULL((void *)0))
315 return (2);
316 if (rflag)
317 return (0);
318 warnx("%s: %s: short file", infile, outfile);
319 return (1);
320}
321
322static int
323checkend(const char *ptr, const char *end, const char *msg)
324{
325 size_t n;
326
327 n = strlen(end);
328 if (strncmp(ptr, end, n) != 0 ||
329 strspn(ptr + n, " \t\r\n") != strlen(ptr + n)) {
330 warnx("%s: %s: %s", infile, outfile, msg);
331 return (1);
332 }
333 if (fclose(outfp) != 0) {
334 warn("%s: %s", infile, outfile);
335 return (1);
336 }
337 return (0);
338}
339
340static int
341uu_decode(void)
342{
343 int i, ch;
344 char *p;
345 char buf[PATH_MAX1024];
346
347 /* for each input line */
348 for (;;) {
349 switch (get_line(buf, sizeof(buf))) {
350 case 0:
351 return (0);
352 case 1:
353 return (1);
354 }
355
356#define DEC(c)(((c) - ' ') & 077) (((c) - ' ') & 077) /* single character decode */
357#define IS_DEC(c)( (((c) - ' ') >= 0) && (((c) - ' ') <= 077 + 1
) )
( (((c) - ' ') >= 0) && (((c) - ' ') <= 077 + 1) )
358
359#define OUT_OF_RANGE(c)do { warnx("%s: %s: character value (%d) out of range [%d-%d]"
, infile, outfile, (unsigned char)(c), 1 + ' ', 077 + ' ' + 1
); return (1); } while (0)
do { \
360 warnx("%s: %s: character value (%d) out of range [%d-%d]", \
361 infile, outfile, (unsigned char)(c), 1 + ' ', 077 + ' ' + 1); \
362 return (1); \
363} while (0)
364
365 /*
366 * `i' is used to avoid writing out all the characters
367 * at the end of the file.
368 */
369 p = buf;
370 if ((i = DEC(*p)(((*p) - ' ') & 077)) <= 0)
371 break;
372 for (++p; i > 0; p += 4, i -= 3)
373 if (i >= 3) {
374 if (!IS_DEC(*p)( (((*p) - ' ') >= 0) && (((*p) - ' ') <= 077 +
1) )
)
375 OUT_OF_RANGE(*p)do { warnx("%s: %s: character value (%d) out of range [%d-%d]"
, infile, outfile, (unsigned char)(*p), 1 + ' ', 077 + ' ' + 1
); return (1); } while (0)
;
376 if (!IS_DEC(*(p + 1))( (((*(p + 1)) - ' ') >= 0) && (((*(p + 1)) - ' ')
<= 077 + 1) )
)
377 OUT_OF_RANGE(*(p + 1))do { warnx("%s: %s: character value (%d) out of range [%d-%d]"
, infile, outfile, (unsigned char)(*(p + 1)), 1 + ' ', 077 + ' '
+ 1); return (1); } while (0)
;
378 if (!IS_DEC(*(p + 2))( (((*(p + 2)) - ' ') >= 0) && (((*(p + 2)) - ' ')
<= 077 + 1) )
)
379 OUT_OF_RANGE(*(p + 2))do { warnx("%s: %s: character value (%d) out of range [%d-%d]"
, infile, outfile, (unsigned char)(*(p + 2)), 1 + ' ', 077 + ' '
+ 1); return (1); } while (0)
;
380 if (!IS_DEC(*(p + 3))( (((*(p + 3)) - ' ') >= 0) && (((*(p + 3)) - ' ')
<= 077 + 1) )
)
381 OUT_OF_RANGE(*(p + 3))do { warnx("%s: %s: character value (%d) out of range [%d-%d]"
, infile, outfile, (unsigned char)(*(p + 3)), 1 + ' ', 077 + ' '
+ 1); return (1); } while (0)
;
382 ch = DEC(p[0])(((p[0]) - ' ') & 077) << 2 | DEC(p[1])(((p[1]) - ' ') & 077) >> 4;
383 putc(ch, outfp)(!__isthreaded ? __sputc(ch, outfp) : (putc)(ch, outfp));
384 ch = DEC(p[1])(((p[1]) - ' ') & 077) << 4 | DEC(p[2])(((p[2]) - ' ') & 077) >> 2;
385 putc(ch, outfp)(!__isthreaded ? __sputc(ch, outfp) : (putc)(ch, outfp));
386 ch = DEC(p[2])(((p[2]) - ' ') & 077) << 6 | DEC(p[3])(((p[3]) - ' ') & 077);
387 putc(ch, outfp)(!__isthreaded ? __sputc(ch, outfp) : (putc)(ch, outfp));
388 }
389 else {
390 if (i >= 1) {
391 if (!IS_DEC(*p)( (((*p) - ' ') >= 0) && (((*p) - ' ') <= 077 +
1) )
)
392 OUT_OF_RANGE(*p)do { warnx("%s: %s: character value (%d) out of range [%d-%d]"
, infile, outfile, (unsigned char)(*p), 1 + ' ', 077 + ' ' + 1
); return (1); } while (0)
;
393 if (!IS_DEC(*(p + 1))( (((*(p + 1)) - ' ') >= 0) && (((*(p + 1)) - ' ')
<= 077 + 1) )
)
394 OUT_OF_RANGE(*(p + 1))do { warnx("%s: %s: character value (%d) out of range [%d-%d]"
, infile, outfile, (unsigned char)(*(p + 1)), 1 + ' ', 077 + ' '
+ 1); return (1); } while (0)
;
395 ch = DEC(p[0])(((p[0]) - ' ') & 077) << 2 | DEC(p[1])(((p[1]) - ' ') & 077) >> 4;
396 putc(ch, outfp)(!__isthreaded ? __sputc(ch, outfp) : (putc)(ch, outfp));
397 }
398 if (i >= 2) {
399 if (!IS_DEC(*(p + 1))( (((*(p + 1)) - ' ') >= 0) && (((*(p + 1)) - ' ')
<= 077 + 1) )
)
400 OUT_OF_RANGE(*(p + 1))do { warnx("%s: %s: character value (%d) out of range [%d-%d]"
, infile, outfile, (unsigned char)(*(p + 1)), 1 + ' ', 077 + ' '
+ 1); return (1); } while (0)
;
401 if (!IS_DEC(*(p + 2))( (((*(p + 2)) - ' ') >= 0) && (((*(p + 2)) - ' ')
<= 077 + 1) )
)
402 OUT_OF_RANGE(*(p + 2))do { warnx("%s: %s: character value (%d) out of range [%d-%d]"
, infile, outfile, (unsigned char)(*(p + 2)), 1 + ' ', 077 + ' '
+ 1); return (1); } while (0)
;
403 ch = DEC(p[1])(((p[1]) - ' ') & 077) << 4 | DEC(p[2])(((p[2]) - ' ') & 077) >> 2;
404 putc(ch, outfp)(!__isthreaded ? __sputc(ch, outfp) : (putc)(ch, outfp));
405 }
406 if (i >= 3) {
407 if (!IS_DEC(*(p + 2))( (((*(p + 2)) - ' ') >= 0) && (((*(p + 2)) - ' ')
<= 077 + 1) )
)
408 OUT_OF_RANGE(*(p + 2))do { warnx("%s: %s: character value (%d) out of range [%d-%d]"
, infile, outfile, (unsigned char)(*(p + 2)), 1 + ' ', 077 + ' '
+ 1); return (1); } while (0)
;
409 if (!IS_DEC(*(p + 3))( (((*(p + 3)) - ' ') >= 0) && (((*(p + 3)) - ' ')
<= 077 + 1) )
)
410 OUT_OF_RANGE(*(p + 3))do { warnx("%s: %s: character value (%d) out of range [%d-%d]"
, infile, outfile, (unsigned char)(*(p + 3)), 1 + ' ', 077 + ' '
+ 1); return (1); } while (0)
;
411 ch = DEC(p[2])(((p[2]) - ' ') & 077) << 6 | DEC(p[3])(((p[3]) - ' ') & 077);
412 putc(ch, outfp)(!__isthreaded ? __sputc(ch, outfp) : (putc)(ch, outfp));
413 }
414 }
415 }
416 switch (get_line(buf, sizeof(buf))) {
417 case 0:
418 return (0);
419 case 1:
420 return (1);
421 default:
422 return (checkend(buf, "end", "no \"end\" line"));
423 }
424}
425
426static int
427base64_decode(void)
428{
429 int n;
430 char inbuf[PATH_MAX1024];
431 unsigned char outbuf[PATH_MAX1024 * 4];
432
433 for (;;) {
434 switch (get_line(inbuf, sizeof(inbuf))) {
435 case 0:
436 return (0);
437 case 1:
438 return (1);
439 }
440 n = b64_pton__b64_pton(inbuf, outbuf, sizeof(outbuf));
441 if (n < 0)
442 break;
443 fwrite(outbuf, 1, n, outfp);
444 }
445 return (checkend(inbuf, "====",
446 "error decoding base64 input stream"));
447}
448
449static void __dead__attribute__((__noreturn__))
450usage(void)
451{
452 switch (pmode) {
453 case MODE_DECODE:
454 (void)fprintf(stderr(&__sF[2]),
455 "usage: uudecode [-cimprs] [file ...]\n"
456 " uudecode [-i] -o output_file [file]\n");
457 break;
458 case MODE_B64DECODE:
459 (void)fprintf(stderr(&__sF[2]),
460 "usage: b64decode [-ciprs] [file ...]\n"
461 " b64decode [-i] -o output_file [file]\n");
462 break;
463 }
464 exit(1);
465}