Bug Summary

File:src/lib/libc/gen/setmode.c
Warning:line 309, column 6
Value stored to 'perm' is never read

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 setmode.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/lib/libc/obj -resource-dir /usr/local/lib/clang/13.0.0 -include namespace.h -I /usr/src/lib/libc/include -I /usr/src/lib/libc/hidden -D __LIBC__ -D APIWARN -D YP -I /usr/src/lib/libc/yp -I /usr/src/lib/libc -I /usr/src/lib/libc/gdtoa -I /usr/src/lib/libc/arch/amd64/gdtoa -D INFNAN_CHECK -D MULTIPLE_THREADS -D NO_FENV_H -D USE_LOCALE -I /usr/src/lib/libc -I /usr/src/lib/libc/citrus -D RESOLVSORT -D FLOATING_POINT -D PRINTF_WIDE_CHAR -D SCANF_WIDE_CHAR -D FUTEX -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/lib/libc/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/lib/libc/gen/setmode.c
1/* $OpenBSD: setmode.c,v 1.22 2014/10/11 04:14:35 deraadt Exp $ */
2/* $NetBSD: setmode.c,v 1.15 1997/02/07 22:21:06 christos Exp $ */
3
4/*
5 * Copyright (c) 1989, 1993, 1994
6 * The Regents of the University of California. All rights reserved.
7 *
8 * This code is derived from software contributed to Berkeley by
9 * Dave Borman at Cray Research, Inc.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36#include <sys/types.h>
37#include <sys/stat.h>
38
39#include <ctype.h>
40#include <errno(*__errno()).h>
41#include <signal.h>
42#include <stdlib.h>
43#include <unistd.h>
44
45#ifdef SETMODE_DEBUG
46#include <stdio.h>
47#endif
48
49#define SET_LEN6 6 /* initial # of bitcmd struct to malloc */
50#define SET_LEN_INCR4 4 /* # of bitcmd structs to add as needed */
51
52typedef struct bitcmd {
53 char cmd;
54 char cmd2;
55 mode_t bits;
56} BITCMD;
57
58#define CMD2_CLR0x01 0x01
59#define CMD2_SET0x02 0x02
60#define CMD2_GBITS0x04 0x04
61#define CMD2_OBITS0x08 0x08
62#define CMD2_UBITS0x10 0x10
63
64static BITCMD *addcmd(BITCMD *, int, int, int, u_int);
65static void compress_mode(BITCMD *);
66#ifdef SETMODE_DEBUG
67static void dumpmode(BITCMD *);
68#endif
69
70/*
71 * Given the old mode and an array of bitcmd structures, apply the operations
72 * described in the bitcmd structures to the old mode, and return the new mode.
73 * Note that there is no '=' command; a strict assignment is just a '-' (clear
74 * bits) followed by a '+' (set bits).
75 */
76mode_t
77getmode(const void *bbox, mode_t omode)
78{
79 const BITCMD *set;
80 mode_t clrval, newmode, value;
81
82 set = (const BITCMD *)bbox;
83 newmode = omode;
84 for (value = 0;; set++)
85 switch(set->cmd) {
86 /*
87 * When copying the user, group or other bits around, we "know"
88 * where the bits are in the mode so that we can do shifts to
89 * copy them around. If we don't use shifts, it gets real
90 * grundgy with lots of single bit checks and bit sets.
91 */
92 case 'u':
93 value = (newmode & S_IRWXU0000700) >> 6;
94 goto common;
95
96 case 'g':
97 value = (newmode & S_IRWXG0000070) >> 3;
98 goto common;
99
100 case 'o':
101 value = newmode & S_IRWXO0000007;
102common: if (set->cmd2 & CMD2_CLR0x01) {
103 clrval =
104 (set->cmd2 & CMD2_SET0x02) ? S_IRWXO0000007 : value;
105 if (set->cmd2 & CMD2_UBITS0x10)
106 newmode &= ~((clrval<<6) & set->bits);
107 if (set->cmd2 & CMD2_GBITS0x04)
108 newmode &= ~((clrval<<3) & set->bits);
109 if (set->cmd2 & CMD2_OBITS0x08)
110 newmode &= ~(clrval & set->bits);
111 }
112 if (set->cmd2 & CMD2_SET0x02) {
113 if (set->cmd2 & CMD2_UBITS0x10)
114 newmode |= (value<<6) & set->bits;
115 if (set->cmd2 & CMD2_GBITS0x04)
116 newmode |= (value<<3) & set->bits;
117 if (set->cmd2 & CMD2_OBITS0x08)
118 newmode |= value & set->bits;
119 }
120 break;
121
122 case '+':
123 newmode |= set->bits;
124 break;
125
126 case '-':
127 newmode &= ~set->bits;
128 break;
129
130 case 'X':
131 if (omode & (S_IFDIR0040000|S_IXUSR0000100|S_IXGRP0000010|S_IXOTH0000001))
132 newmode |= set->bits;
133 break;
134
135 case '\0':
136 default:
137#ifdef SETMODE_DEBUG
138 (void)printf("getmode:%04o -> %04o\n", omode, newmode);
139#endif
140 return (newmode);
141 }
142}
143
144#define ADDCMD(a, b, c, d)if (set >= endset) { BITCMD *newset; setlen += 4; newset =
reallocarray(saveset, setlen, sizeof(BITCMD)); if (newset ==
((void *)0)) { free(saveset); return (((void *)0)); } set = newset
+ (set - saveset); saveset = newset; endset = newset + (setlen
- 2); } set = addcmd(set, (a), (b), (c), (d))
\
145 if (set >= endset) { \
146 BITCMD *newset; \
147 setlen += SET_LEN_INCR4; \
148 newset = reallocarray(saveset, setlen, sizeof(BITCMD)); \
149 if (newset == NULL((void *)0)) { \
150 free(saveset); \
151 return (NULL((void *)0)); \
152 } \
153 set = newset + (set - saveset); \
154 saveset = newset; \
155 endset = newset + (setlen - 2); \
156 } \
157 set = addcmd(set, (a), (b), (c), (d))
158
159#define STANDARD_BITS(0004000|0002000|0000700|0000070|0000007) (S_ISUID0004000|S_ISGID0002000|S_IRWXU0000700|S_IRWXG0000070|S_IRWXO0000007)
160
161void *
162setmode(const char *p)
163{
164 char op, *ep;
165 BITCMD *set, *saveset, *endset;
166 sigset_t sigset, sigoset;
167 mode_t mask, perm, permXbits, who;
168 int equalopdone, setlen;
169 u_long perml;
170
171 if (!*p) {
172 errno(*__errno()) = EINVAL22;
173 return (NULL((void *)0));
174 }
175
176 /*
177 * Get a copy of the mask for the permissions that are mask relative.
178 * Flip the bits, we want what's not set. Since it's possible that
179 * the caller is opening files inside a signal handler, protect them
180 * as best we can.
181 */
182 sigfillset(&sigset);
183 (void)sigprocmask(SIG_BLOCK1, &sigset, &sigoset);
184 (void)umask(mask = umask(0));
185 mask = ~mask;
186 (void)sigprocmask(SIG_SETMASK3, &sigoset, NULL((void *)0));
187
188 setlen = SET_LEN6 + 2;
189
190 if ((set = calloc((u_int)sizeof(BITCMD), setlen)) == NULL((void *)0))
191 return (NULL((void *)0));
192 saveset = set;
193 endset = set + (setlen - 2);
194
195 /*
196 * If an absolute number, get it and return; disallow non-octal digits
197 * or illegal bits.
198 */
199 if (isdigit((unsigned char)*p)) {
200 perml = strtoul(p, &ep, 8);
201 /* The test on perml will also catch overflow. */
202 if (*ep != '\0' || (perml & ~(STANDARD_BITS(0004000|0002000|0000700|0000070|0000007)|S_ISTXT0001000))) {
203 free(saveset);
204 errno(*__errno()) = ERANGE34;
205 return (NULL((void *)0));
206 }
207 perm = (mode_t)perml;
208 ADDCMD('=', (STANDARD_BITS|S_ISTXT), perm, mask)if (set >= endset) { BITCMD *newset; setlen += 4; newset =
reallocarray(saveset, setlen, sizeof(BITCMD)); if (newset ==
((void *)0)) { free(saveset); return (((void *)0)); } set = newset
+ (set - saveset); saveset = newset; endset = newset + (setlen
- 2); } set = addcmd(set, ('='), (((0004000|0002000|0000700|
0000070|0000007)|0001000)), (perm), (mask))
;
209 set->cmd = 0;
210 return (saveset);
211 }
212
213 /*
214 * Build list of structures to set/clear/copy bits as described by
215 * each clause of the symbolic mode.
216 */
217 for (;;) {
218 /* First, find out which bits might be modified. */
219 for (who = 0;; ++p) {
220 switch (*p) {
221 case 'a':
222 who |= STANDARD_BITS(0004000|0002000|0000700|0000070|0000007);
223 break;
224 case 'u':
225 who |= S_ISUID0004000|S_IRWXU0000700;
226 break;
227 case 'g':
228 who |= S_ISGID0002000|S_IRWXG0000070;
229 break;
230 case 'o':
231 who |= S_IRWXO0000007;
232 break;
233 default:
234 goto getop;
235 }
236 }
237
238getop: if ((op = *p++) != '+' && op != '-' && op != '=') {
239 free(saveset);
240 errno(*__errno()) = EINVAL22;
241 return (NULL((void *)0));
242 }
243 if (op == '=')
244 equalopdone = 0;
245
246 who &= ~S_ISTXT0001000;
247 for (perm = 0, permXbits = 0;; ++p) {
248 switch (*p) {
249 case 'r':
250 perm |= S_IRUSR0000400|S_IRGRP0000040|S_IROTH0000004;
251 break;
252 case 's':
253 /*
254 * If specific bits where requested and
255 * only "other" bits ignore set-id.
256 */
257 if (who == 0 || (who & ~S_IRWXO0000007))
258 perm |= S_ISUID0004000|S_ISGID0002000;
259 break;
260 case 't':
261 /*
262 * If specific bits where requested and
263 * only "other" bits ignore sticky.
264 */
265 if (who == 0 || (who & ~S_IRWXO0000007)) {
266 who |= S_ISTXT0001000;
267 perm |= S_ISTXT0001000;
268 }
269 break;
270 case 'w':
271 perm |= S_IWUSR0000200|S_IWGRP0000020|S_IWOTH0000002;
272 break;
273 case 'X':
274 permXbits = S_IXUSR0000100|S_IXGRP0000010|S_IXOTH0000001;
275 break;
276 case 'x':
277 perm |= S_IXUSR0000100|S_IXGRP0000010|S_IXOTH0000001;
278 break;
279 case 'u':
280 case 'g':
281 case 'o':
282 /*
283 * When ever we hit 'u', 'g', or 'o', we have
284 * to flush out any partial mode that we have,
285 * and then do the copying of the mode bits.
286 */
287 if (perm) {
288 ADDCMD(op, who, perm, mask)if (set >= endset) { BITCMD *newset; setlen += 4; newset =
reallocarray(saveset, setlen, sizeof(BITCMD)); if (newset ==
((void *)0)) { free(saveset); return (((void *)0)); } set = newset
+ (set - saveset); saveset = newset; endset = newset + (setlen
- 2); } set = addcmd(set, (op), (who), (perm), (mask))
;
289 perm = 0;
290 }
291 if (op == '=')
292 equalopdone = 1;
293 if (op == '+' && permXbits) {
294 ADDCMD('X', who, permXbits, mask)if (set >= endset) { BITCMD *newset; setlen += 4; newset =
reallocarray(saveset, setlen, sizeof(BITCMD)); if (newset ==
((void *)0)) { free(saveset); return (((void *)0)); } set = newset
+ (set - saveset); saveset = newset; endset = newset + (setlen
- 2); } set = addcmd(set, ('X'), (who), (permXbits), (mask))
;
295 permXbits = 0;
296 }
297 ADDCMD(*p, who, op, mask)if (set >= endset) { BITCMD *newset; setlen += 4; newset =
reallocarray(saveset, setlen, sizeof(BITCMD)); if (newset ==
((void *)0)) { free(saveset); return (((void *)0)); } set = newset
+ (set - saveset); saveset = newset; endset = newset + (setlen
- 2); } set = addcmd(set, (*p), (who), (op), (mask))
;
298 break;
299
300 default:
301 /*
302 * Add any permissions that we haven't already
303 * done.
304 */
305 if (perm || (op == '=' && !equalopdone)) {
306 if (op == '=')
307 equalopdone = 1;
308 ADDCMD(op, who, perm, mask)if (set >= endset) { BITCMD *newset; setlen += 4; newset =
reallocarray(saveset, setlen, sizeof(BITCMD)); if (newset ==
((void *)0)) { free(saveset); return (((void *)0)); } set = newset
+ (set - saveset); saveset = newset; endset = newset + (setlen
- 2); } set = addcmd(set, (op), (who), (perm), (mask))
;
309 perm = 0;
Value stored to 'perm' is never read
310 }
311 if (permXbits) {
312 ADDCMD('X', who, permXbits, mask)if (set >= endset) { BITCMD *newset; setlen += 4; newset =
reallocarray(saveset, setlen, sizeof(BITCMD)); if (newset ==
((void *)0)) { free(saveset); return (((void *)0)); } set = newset
+ (set - saveset); saveset = newset; endset = newset + (setlen
- 2); } set = addcmd(set, ('X'), (who), (permXbits), (mask))
;
313 permXbits = 0;
314 }
315 goto apply;
316 }
317 }
318
319apply: if (!*p)
320 break;
321 if (*p != ',')
322 goto getop;
323 ++p;
324 }
325 set->cmd = 0;
326#ifdef SETMODE_DEBUG
327 (void)printf("Before compress_mode()\n");
328 dumpmode(saveset);
329#endif
330 compress_mode(saveset);
331#ifdef SETMODE_DEBUG
332 (void)printf("After compress_mode()\n");
333 dumpmode(saveset);
334#endif
335 return (saveset);
336}
337
338static BITCMD *
339addcmd(BITCMD *set, int op, int who, int oparg, u_int mask)
340{
341 switch (op) {
342 case '=':
343 set->cmd = '-';
344 set->bits = who ? who : STANDARD_BITS(0004000|0002000|0000700|0000070|0000007);
345 set++;
346
347 op = '+';
348 /* FALLTHROUGH */
349 case '+':
350 case '-':
351 case 'X':
352 set->cmd = op;
353 set->bits = (who ? who : mask) & oparg;
354 break;
355
356 case 'u':
357 case 'g':
358 case 'o':
359 set->cmd = op;
360 if (who) {
361 set->cmd2 = ((who & S_IRUSR0000400) ? CMD2_UBITS0x10 : 0) |
362 ((who & S_IRGRP0000040) ? CMD2_GBITS0x04 : 0) |
363 ((who & S_IROTH0000004) ? CMD2_OBITS0x08 : 0);
364 set->bits = (mode_t)~0;
365 } else {
366 set->cmd2 = CMD2_UBITS0x10 | CMD2_GBITS0x04 | CMD2_OBITS0x08;
367 set->bits = mask;
368 }
369
370 if (oparg == '+')
371 set->cmd2 |= CMD2_SET0x02;
372 else if (oparg == '-')
373 set->cmd2 |= CMD2_CLR0x01;
374 else if (oparg == '=')
375 set->cmd2 |= CMD2_SET0x02|CMD2_CLR0x01;
376 break;
377 }
378 return (set + 1);
379}
380
381#ifdef SETMODE_DEBUG
382static void
383dumpmode(BITCMD *set)
384{
385 for (; set->cmd; ++set)
386 (void)printf("cmd: '%c' bits %04o%s%s%s%s%s%s\n",
387 set->cmd, set->bits, set->cmd2 ? " cmd2:" : "",
388 set->cmd2 & CMD2_CLR0x01 ? " CLR" : "",
389 set->cmd2 & CMD2_SET0x02 ? " SET" : "",
390 set->cmd2 & CMD2_UBITS0x10 ? " UBITS" : "",
391 set->cmd2 & CMD2_GBITS0x04 ? " GBITS" : "",
392 set->cmd2 & CMD2_OBITS0x08 ? " OBITS" : "");
393}
394#endif
395
396/*
397 * Given an array of bitcmd structures, compress by compacting consecutive
398 * '+', '-' and 'X' commands into at most 3 commands, one of each. The 'u',
399 * 'g' and 'o' commands continue to be separate. They could probably be
400 * compacted, but it's not worth the effort.
401 */
402static void
403compress_mode(BITCMD *set)
404{
405 BITCMD *nset;
406 int setbits, clrbits, Xbits, op;
407
408 for (nset = set;;) {
409 /* Copy over any 'u', 'g' and 'o' commands. */
410 while ((op = nset->cmd) != '+' && op != '-' && op != 'X') {
411 *set++ = *nset++;
412 if (!op)
413 return;
414 }
415
416 for (setbits = clrbits = Xbits = 0;; nset++) {
417 if ((op = nset->cmd) == '-') {
418 clrbits |= nset->bits;
419 setbits &= ~nset->bits;
420 Xbits &= ~nset->bits;
421 } else if (op == '+') {
422 setbits |= nset->bits;
423 clrbits &= ~nset->bits;
424 Xbits &= ~nset->bits;
425 } else if (op == 'X')
426 Xbits |= nset->bits & ~setbits;
427 else
428 break;
429 }
430 if (clrbits) {
431 set->cmd = '-';
432 set->cmd2 = 0;
433 set->bits = clrbits;
434 set++;
435 }
436 if (setbits) {
437 set->cmd = '+';
438 set->cmd2 = 0;
439 set->bits = setbits;
440 set++;
441 }
442 if (Xbits) {
443 set->cmd = 'X';
444 set->cmd2 = 0;
445 set->bits = Xbits;
446 set++;
447 }
448 }
449}