Bug Summary

File:src/sbin/swapctl/swapctl.c
Warning:line 394, column 18
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 swapctl.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/sbin/swapctl/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/sbin/swapctl/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/sbin/swapctl/swapctl.c
1/* $OpenBSD: swapctl.c,v 1.26 2020/02/11 18:16:38 jca Exp $ */
2/* $NetBSD: swapctl.c,v 1.9 1998/07/26 20:23:15 mycroft Exp $ */
3
4/*
5 * Copyright (c) 1996, 1997 Matthew R. Green
6 * 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 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30/*
31 * swapctl command:
32 * -A add all devices listed as `sw' in /etc/fstab
33 * -t [blk|noblk] if -A, add either all block device or all non-block
34 * devices
35 * -a <path> add this device
36 * -d <path> remove this swap device
37 * -l list swap devices
38 * -s short listing of swap devices
39 * -k use kilobytes
40 * -p <pri> use this priority
41 * -c <path> change priority
42 *
43 * or, if invoked as "swapon" (compatibility mode):
44 *
45 * -a all devices listed as `sw' in /etc/fstab
46 * -t same as -t above (feature not present in old
47 * swapon(8) command)
48 * <dev> add this device
49 */
50
51#include <sys/stat.h>
52#include <sys/swap.h>
53#include <sys/wait.h>
54
55#include <unistd.h>
56#include <err.h>
57#include <errno(*__errno()).h>
58#include <stdio.h>
59#include <stdlib.h>
60#include <limits.h>
61#include <string.h>
62#include <fstab.h>
63#include <util.h>
64
65#include "swapctl.h"
66
67int command;
68
69/*
70 * Commands for swapctl(8). These are mutually exclusive.
71 */
72#define CMD_A0x01 0x01 /* process /etc/fstab */
73#define CMD_a0x02 0x02 /* add a swap file/device */
74#define CMD_c0x04 0x04 /* change priority of a swap file/device */
75#define CMD_d0x08 0x08 /* delete a swap file/device */
76#define CMD_l0x10 0x10 /* list swap files/devices */
77#define CMD_s0x20 0x20 /* summary of swap files/devices */
78
79#define SET_COMMAND(cmd)do { if (command) usage(); command = (cmd); } while (0) \
80do { \
81 if (command) \
82 usage(); \
83 command = (cmd); \
84} while (0)
85
86/*
87 * Commands that require a "path" argument at the end of the command
88 * line, and the ones which require that none exist.
89 */
90#define REQUIRE_PATH(0x02 | 0x04 | 0x08) (CMD_a0x02 | CMD_c0x04 | CMD_d0x08)
91#define REQUIRE_NOPATH(0x01 | 0x10 | 0x20) (CMD_A0x01 | CMD_l0x10 | CMD_s0x20)
92
93/*
94 * Option flags, and the commands with which they are valid.
95 */
96int kflag; /* display in 1K blocks */
97#define KFLAG_CMDS(0x10 | 0x20) (CMD_l0x10 | CMD_s0x20)
98
99int pflag; /* priority was specified */
100#define PFLAG_CMDS(0x01 | 0x02 | 0x04) (CMD_A0x01 | CMD_a0x02 | CMD_c0x04)
101
102char *tflag; /* swap device type (blk or noblk) */
103#define TFLAG_CMDS(0x01) (CMD_A0x01)
104
105int pri; /* uses 0 as default pri */
106
107static void change_priority(char *);
108static void add_swap(char *);
109static void del_swap(char *);
110static void do_fstab(void);
111static void usage(void);
112static int swapon_command(int, char **);
113
114extern char *__progname; /* from crt0.o */
115
116int
117main(int argc, char *argv[])
118{
119 const char *errstr;
120 int c;
121
122 if (strcmp(__progname, "swapon") == 0)
123 return swapon_command(argc, argv);
124
125 while ((c = getopt(argc, argv, "Aacdlkp:st:")) != -1) {
126 switch (c) {
127 case 'A':
128 SET_COMMAND(CMD_A)do { if (command) usage(); command = (0x01); } while (0);
129 break;
130
131 case 'a':
132 SET_COMMAND(CMD_a)do { if (command) usage(); command = (0x02); } while (0);
133 break;
134
135 case 'c':
136 SET_COMMAND(CMD_c)do { if (command) usage(); command = (0x04); } while (0);
137 break;
138
139 case 'd':
140 SET_COMMAND(CMD_d)do { if (command) usage(); command = (0x08); } while (0);
141 break;
142
143 case 'l':
144 SET_COMMAND(CMD_l)do { if (command) usage(); command = (0x10); } while (0);
145 break;
146
147 case 'k':
148 kflag = 1;
149 break;
150
151 case 'p':
152 pflag = 1;
153 pri = strtonum(optarg, 0, INT_MAX2147483647, &errstr);
154 if (errstr)
155 errx(1, "-p %s: %s", errstr, optarg);
156 break;
157
158 case 's':
159 SET_COMMAND(CMD_s)do { if (command) usage(); command = (0x20); } while (0);
160 break;
161
162 case 't':
163 if (tflag != NULL((void *)0))
164 usage();
165 tflag = optarg;
166 break;
167
168 default:
169 usage();
170 /* NOTREACHED */
171 }
172 }
173
174 argv += optind;
175 argc -= optind;
176
177 /* Did the user specify a command? */
178 if (command == 0) {
179 if (argc == 0)
180 SET_COMMAND(CMD_l)do { if (command) usage(); command = (0x10); } while (0);
181 else
182 usage();
183 }
184
185 switch (argc) {
186 case 0:
187 if (command & REQUIRE_PATH(0x02 | 0x04 | 0x08))
188 usage();
189 break;
190
191 case 1:
192 if (command & REQUIRE_NOPATH(0x01 | 0x10 | 0x20))
193 usage();
194 break;
195
196 default:
197 usage();
198 }
199
200 /* To change priority, you have to specify one. */
201 if ((command == CMD_c0x04) && pflag == 0)
202 usage();
203
204 /* Sanity-check -t */
205 if (tflag != NULL((void *)0)) {
206 if (command != CMD_A0x01)
207 usage();
208 if (strcmp(tflag, "blk") != 0 &&
209 strcmp(tflag, "noblk") != 0)
210 usage();
211 }
212
213 /* Dispatch the command. */
214 switch (command) {
215 case CMD_l0x10:
216 list_swap(pri, kflag, pflag, 1);
217 break;
218
219 case CMD_s0x20:
220 list_swap(pri, kflag, pflag, 0);
221 break;
222
223 case CMD_c0x04:
224 change_priority(argv[0]);
225 break;
226
227 case CMD_a0x02:
228 add_swap(argv[0]);
229 break;
230
231 case CMD_d0x08:
232 del_swap(argv[0]);
233 break;
234
235 case CMD_A0x01:
236 do_fstab();
237 break;
238 }
239
240 return (0);
241}
242
243/*
244 * swapon_command: emulate the old swapon(8) program.
245 */
246int
247swapon_command(int argc, char **argv)
248{
249 int ch, fiztab = 0;
250
251 while ((ch = getopt(argc, argv, "at:")) != -1) {
252 switch (ch) {
253 case 'a':
254 fiztab = 1;
255 break;
256 case 't':
257 if (tflag != NULL((void *)0))
258 usage();
259 tflag = optarg;
260 break;
261 default:
262 goto swapon_usage;
263 }
264 }
265 argc -= optind;
266 argv += optind;
267
268 if (fiztab) {
269 if (argc)
270 goto swapon_usage;
271 /* Sanity-check -t */
272 if (tflag != NULL((void *)0)) {
273 if (strcmp(tflag, "blk") != 0 &&
274 strcmp(tflag, "noblk") != 0)
275 usage();
276 }
277 do_fstab();
278 return (0);
279 } else if (argc == 0 || tflag != NULL((void *)0))
280 goto swapon_usage;
281
282 while (argc) {
283 add_swap(argv[0]);
284 argc--;
285 argv++;
286 }
287 return (0);
288
289 swapon_usage:
290 fprintf(stderr(&__sF[2]), "usage: %s -a | path\n", __progname);
291 return (1);
292}
293
294/*
295 * change_priority: change the priority of a swap device.
296 */
297void
298change_priority(char *path)
299{
300
301 if (swapctl(SWAP_CTL5, path, pri) == -1)
302 warn("%s", path);
303}
304
305/*
306 * add_swap: add the pathname to the list of swap devices.
307 */
308void
309add_swap(char *path)
310{
311
312 if (swapctl(SWAP_ON1, path, pri) == -1)
313 if (errno(*__errno()) != EBUSY16)
314 err(1, "%s", path);
315}
316
317/*
318 * del_swap: remove the pathname from the list of swap devices.
319 */
320void
321del_swap(char *path)
322{
323
324 if (swapctl(SWAP_OFF2, path, pri) == -1)
325 err(1, "%s", path);
326}
327
328void
329do_fstab(void)
330{
331 struct fstab *fp;
332 char *s;
333 long priority;
334 struct stat st;
335 mode_t rejecttype = 0;
336 int gotone = 0;
337
338 /*
339 * Select which mount point types to reject, depending on the
340 * value of the -t parameter.
341 */
342 if (tflag != NULL((void *)0)) {
343 if (strcmp(tflag, "blk") == 0)
344 rejecttype = S_IFREG0100000;
345 else if (strcmp(tflag, "noblk") == 0)
346 rejecttype = S_IFBLK0060000;
347 }
348
349#define PRIORITYEQ"priority=" "priority="
350#define NFSMNTPT"nfsmntpt=" "nfsmntpt="
351#define PATH_MOUNT"/sbin/mount_nfs" "/sbin/mount_nfs"
352 while ((fp = getfsent()) != NULL((void *)0)) {
353 const char *spec;
354
355 if (strcmp(fp->fs_type, "sw") != 0)
356 continue;
357
358 spec = fp->fs_spec;
359
360 if ((s = strstr(fp->fs_mntops, PRIORITYEQ"priority=")) != NULL((void *)0)) {
361 s += sizeof(PRIORITYEQ"priority=") - 1;
362 priority = atol(s);
363 } else
364 priority = pri;
365
366 if ((s = strstr(fp->fs_mntops, NFSMNTPT"nfsmntpt=")) != NULL((void *)0)) {
367 char *t;
368 pid_t pid;
369 int status;
370
371 /*
372 * Skip this song and dance if we're only
373 * doing block devices.
374 */
375 if (rejecttype == S_IFREG0100000)
376 continue;
377
378 t = strpbrk(s, ",");
379 if (t != 0)
380 *t = '\0';
381 spec = strdup(s + strlen(NFSMNTPT"nfsmntpt="));
382 if (spec == NULL((void *)0))
383 err(1, "strdup");
384
385 if (t != 0)
386 *t = ',';
387
388 if (strlen(spec) == 0) {
389 warnx("empty mountpoint");
390 free((char *)spec);
391 continue;
392 }
393
394 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
395 case -1: /* error */
396 err(1, "vfork");
397 case 0:
398 execl(PATH_MOUNT"/sbin/mount_nfs", PATH_MOUNT"/sbin/mount_nfs", fp->fs_spec, spec,
399 (char *)NULL((void *)0));
400 err(1, "execl");
401 }
402 while (waitpid(pid, &status, 0) == -1)
403 if (errno(*__errno()) != EINTR4)
404 err(1, "waitpid");
405 if (status != 0) {
406 warnx("%s: mount failed", fp->fs_spec);
407 free((char *)spec);
408 continue;
409 }
410 } else if (isduid(spec, 0)) {
411 if (rejecttype == S_IFBLK0060000)
412 continue;
413 } else {
414 /*
415 * Determine blk-ness. Don't even consider a
416 * mountpoint outside /dev as a block device.
417 */
418 if (rejecttype == S_IFREG0100000) {
419 if (strncmp("/dev/", spec, 5) != 0)
420 continue;
421 }
422 if (stat(spec, &st) == -1) {
423 warn("%s", spec);
424 continue;
425 }
426 if ((st.st_mode & S_IFMT0170000) == rejecttype)
427 continue;
428
429 /*
430 * Do not allow fancy objects to be swap areas.
431 */
432 if (!S_ISREG(st.st_mode)((st.st_mode & 0170000) == 0100000) &&
433 !S_ISBLK(st.st_mode)((st.st_mode & 0170000) == 0060000))
434 continue;
435 }
436
437 if (swapctl(SWAP_ON1, spec, (int)priority) == -1) {
438 if (errno(*__errno()) != EBUSY16)
439 warn("%s", spec);
440 } else {
441 gotone = 1;
442 printf("%s: adding %s as swap device at priority %d\n",
443 __progname, fp->fs_spec, (int)priority);
444 }
445
446 if (spec != fp->fs_spec)
447 free((char *)spec);
448 }
449 if (gotone == 0)
450 exit(1);
451}
452
453void
454usage(void)
455{
456
457 fprintf(stderr(&__sF[2]), "usage: %s -A [-p priority] [-t blk | noblk]\n",
458 __progname);
459 fprintf(stderr(&__sF[2]), " %s -a [-p priority] path\n", __progname);
460 fprintf(stderr(&__sF[2]), " %s -c -p priority path\n", __progname);
461 fprintf(stderr(&__sF[2]), " %s -d path\n", __progname);
462 fprintf(stderr(&__sF[2]), " %s [[-l] | -s] [-k]\n", __progname);
463 exit(1);
464}