├── .gitignore ├── src ├── Makefile ├── params.h ├── util.h ├── argpool.h ├── rman.h ├── util.c ├── syscall.c ├── syscall.h ├── fork.c ├── sched.c ├── rman.c ├── argpool.c ├── params.c ├── sysfuzz.c └── vm.c └── README /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | sysfuzz 3 | sysfuzz.full 4 | sysfuzz.debug 5 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | PROG= sysfuzz 2 | SRCS= argpool.c \ 3 | fork.c \ 4 | params.c \ 5 | rman.c \ 6 | sched.c \ 7 | syscall.c \ 8 | sysfuzz.c \ 9 | util.c \ 10 | vm.c 11 | 12 | CFLAGS+= -DINVARIANTS 13 | 14 | BINDIR=/usr/local/bin 15 | 16 | DEBUG_FLAGS+=-g 17 | 18 | LDADD+= -lnv 19 | 20 | MAN= 21 | WARNS?= 6 22 | 23 | .include 24 | -------------------------------------------------------------------------------- /src/params.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2014 Mark Johnston 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are 6 | * met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in 11 | * the documentation and/or other materials provided with the 12 | * distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | */ 26 | 27 | #ifndef _PARAMS_H_ 28 | #define _PARAMS_H_ 29 | 30 | #include 31 | 32 | void params_init(char **); 33 | void params_dump(void); 34 | 35 | bool param_flag(const char *); 36 | uint64_t param_number(const char *); 37 | const char *param_string(const char *); 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /src/util.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2014 Mark Johnston 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are 6 | * met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in 11 | * the documentation and/or other materials provided with the 12 | * distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | */ 26 | 27 | #ifndef _UTIL_H_ 28 | #define _UTIL_H_ 29 | 30 | #define max(x, y) ((x) > (y) ? (x) : (y)) 31 | #define min(x, y) ((x) > (y) ? (y) : (x)) 32 | 33 | u_int ncpu(void); 34 | u_int pagecnt(void); 35 | void randfile(char *); 36 | void * xmalloc(size_t); 37 | char * xstrdup(const char *); 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /src/argpool.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2014 Mark Johnston 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are 6 | * met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in 11 | * the documentation and/or other materials provided with the 12 | * distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | */ 26 | 27 | #ifndef _ARGPOOL_H_ 28 | #define _ARGPOOL_H_ 29 | 30 | #include 31 | 32 | struct arg_memblk { 33 | void *addr; 34 | size_t len; 35 | }; 36 | 37 | void ap_init(void); 38 | 39 | void ap_dirfd_add(int); 40 | void ap_dirfd_close(int); 41 | int ap_dirfd_random(void); 42 | void ap_fd_add(int); 43 | void ap_fd_close(int); 44 | int ap_fd_random(void); 45 | void ap_memblk_map(void *, size_t); 46 | int ap_memblk_random(struct arg_memblk *); 47 | void ap_memblk_unmap(void *, size_t); 48 | 49 | #endif /* _ARGPOOL_H_ */ 50 | -------------------------------------------------------------------------------- /src/rman.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2015 Mark Johnston 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are 6 | * met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in 11 | * the documentation and/or other materials provided with the 12 | * distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | */ 26 | 27 | #include 28 | #include 29 | 30 | struct resource { 31 | TAILQ_ENTRY(resource) r_next; 32 | u_long r_start; 33 | u_long r_len; 34 | }; 35 | 36 | struct rman { 37 | TAILQ_HEAD(, resource) rm_res; 38 | u_int rm_blksz; 39 | int rm_entries; 40 | }; 41 | 42 | typedef int (*rman_pool_init)(struct rman *); 43 | 44 | int rman_init(struct rman *, u_int blksz, rman_pool_init); 45 | void rman_add(struct rman *, u_long, u_long); 46 | int rman_select(struct rman *, u_long *, u_long *, u_int); 47 | void rman_release(struct rman *, u_long, u_long); 48 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | -=-=-=-=-=-=-= 2 | 3 | sysfuzz is a program that attempts to perform fuzz testing of the FreeBSD 4 | kernel's system call interface. Naive fuzz testing of this interface wouldn't 5 | be very interesting: system calls with random input will fail parameter 6 | validation the vast majority of the time, so it would be mostly limited to 7 | exposing very basic bugs in system call implementations. 8 | 9 | This program attempts to improve on the naive approach by using knowledge of 10 | individual system call arguments to generate valid input. For example, rather 11 | than using random numbers for the first two arguments to ptrace(2), sysfuzz uses 12 | a valid ptrace command for the first argument, and a valid PID for the second 13 | argument. During start-up, sysfuzz allocates various types of resources (e.g. 14 | sockets, child processes, kqueues) for use in system call parameter generation. 15 | 16 | Note that the paragraph above describes the vision for this program. At the 17 | moment, it fuzzes the system calls implemented by the virtual memory subsystem - 18 | mmap(2) and friends. 19 | 20 | -=-=-=-=-=-=-=- 21 | 22 | Some example usages are: 23 | 24 | $ sysfuzz 25 | 26 | Run the fuzzer with default parameters. All supported syscalls will be fuzzed. 27 | 28 | $ sysfuzz -g vm 29 | 30 | Only fuzz system calls in the "vm" system call group. 31 | 32 | $ sysfuzz -l vm 33 | 34 | List the system calls comprising the "vm" system call group. 35 | 36 | $ sysfuzz -d 37 | 38 | Print descriptions and default values of all run-time parameters. 39 | 40 | $ sysfuzz -c mmap,munmap -x num-fuzzers=1 41 | 42 | Fuzz the mmap(2) and munmap(2) system calls using a single fuzzer process. 43 | 44 | -=-=-=-=-=-=-=- 45 | 46 | Brag list. Here are fixes for bugs that I've found using sysfuzz: 47 | 48 | r265002: 49 | http://svnweb.freebsd.org/base?view=revision&revision=265002 50 | 51 | r265843: 52 | http://svnweb.freebsd.org/base?view=revision&revision=265843 53 | 54 | r266780: 55 | http://svnweb.freebsd.org/base?view=revision&revision=266780 56 | 57 | r269134: 58 | http://svnweb.freebsd.org/base?view=revision&revision=269134 59 | 60 | r271716: 61 | https://svnweb.freebsd.org/base?view=revision&revision=271716 62 | 63 | r274482: 64 | https://svnweb.freebsd.org/base?view=revision&revision=274482 65 | 66 | -=-=-=-=-=-=-= 67 | 68 | TODO: 69 | * support errno validation (e.g. based on man page descriptions) 70 | * track per-syscall statistics 71 | 72 | -=-=-=-=-=-=-= 73 | -------------------------------------------------------------------------------- /src/util.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2014 Mark Johnston 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are 6 | * met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in 11 | * the documentation and/or other materials provided with the 12 | * distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | */ 26 | 27 | #include 28 | #include 29 | 30 | #include 31 | #include 32 | #include 33 | 34 | #include "util.h" 35 | 36 | /* 37 | * Generate a random filename. buf should be a buffer of size at least NAME_MAX. 38 | */ 39 | void 40 | randfile(char *buf) 41 | { 42 | size_t len; 43 | 44 | len = (random() % (NAME_MAX - 1)) + 1; 45 | arc4random_buf(buf, len); 46 | buf[len] = '\0'; 47 | /* Hide illegal characters. */ 48 | for (u_int i = 0; i < len; i++) 49 | if (buf[i] == '/' || buf[i] == '\0') 50 | buf[i] = 'm'; 51 | } 52 | 53 | u_int 54 | ncpu() 55 | { 56 | size_t ncpusz; 57 | int ncpu; 58 | 59 | ncpusz = sizeof(ncpu); 60 | if (sysctlbyname("hw.ncpu", &ncpu, &ncpusz, NULL, 0) != 0) 61 | err(1, "could not read hw.ncpu"); 62 | 63 | return (ncpu); 64 | } 65 | 66 | u_int 67 | pagecnt() 68 | { 69 | size_t pgcntsz; 70 | int pgcnt; 71 | 72 | pgcntsz = sizeof(pgcnt); 73 | if (sysctlbyname("vm.stats.vm.v_page_count", &pgcnt, &pgcntsz, 74 | NULL, 0) != 0) 75 | err(1, "could not read vm.stats.vm.v_page_count"); 76 | 77 | return (pgcnt); 78 | } 79 | 80 | void * 81 | xmalloc(size_t sz) 82 | { 83 | void *ret; 84 | 85 | if ((ret = malloc(sz)) == NULL) 86 | err(1, "malloc(%zu)", sz); 87 | return (ret); 88 | } 89 | 90 | char * 91 | xstrdup(const char *str) 92 | { 93 | char *ret; 94 | 95 | if ((ret = strdup(str)) == NULL) 96 | err(1, "strdup"); 97 | return (ret); 98 | } 99 | -------------------------------------------------------------------------------- /src/syscall.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2014 Mark Johnston 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are 6 | * met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in 11 | * the documentation and/or other materials provided with the 12 | * distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | */ 26 | 27 | #include 28 | 29 | #include "syscall.h" 30 | 31 | static struct { 32 | enum scgroup id; 33 | const char *name; 34 | } scgroups[] = { 35 | { SC_GROUP_VM, "vm" }, 36 | { SC_GROUP_SCHED, "sched" }, 37 | { SC_GROUP_FORK, "fork" }, 38 | { SC_GROUP_FILEIO, "fileio" }, 39 | }; 40 | 41 | /* Determine whether to fuzz the given system call. */ 42 | bool 43 | sc_filter(const struct scdesc *desc, const char *sclist, size_t scs, 44 | const char *scgrplist, size_t scgrps) 45 | { 46 | const char *sc, *scgrp; 47 | u_int grpmask; 48 | 49 | for (sc = sclist; scs > 0; sc += strlen(sc) + 1) { 50 | if (strcasecmp(sc, desc->sd_name) == 0) 51 | return (false); 52 | scs--; 53 | } 54 | 55 | grpmask = 0; 56 | for (scgrp = scgrplist; scgrps > 0; scgrp += strlen(scgrp) + 1) { 57 | (void)scgroup_lookup(scgrp, &grpmask); 58 | scgrps--; 59 | } 60 | if ((grpmask & desc->sd_groups) != 0) 61 | return (false); 62 | 63 | return (sclist != NULL || scgrplist != NULL); 64 | } 65 | 66 | /* Look up a system call by name. */ 67 | bool 68 | sc_lookup(const char *name, int *sc) 69 | { 70 | struct scdesc **_desc, *desc; 71 | 72 | SET_FOREACH(_desc, syscalls) { 73 | desc = *_desc; 74 | if (strcasecmp(desc->sd_name, name) == 0) { 75 | if (sc != NULL) 76 | *sc = desc->sd_num; 77 | return (true); 78 | } 79 | } 80 | 81 | return (false); 82 | } 83 | 84 | /* Look up a system call group by name. */ 85 | bool 86 | scgroup_lookup(const char *name, enum scgroup *group) 87 | { 88 | u_int i; 89 | 90 | for (i = 0; i < sizeof(scgroups) / sizeof(scgroups[0]); i++) 91 | if (strcasecmp(scgroups[i].name, name) == 0) { 92 | if (group != NULL) 93 | *group |= scgroups[i].id; 94 | return (true); 95 | } 96 | 97 | return (false); 98 | } 99 | -------------------------------------------------------------------------------- /src/syscall.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2014 Mark Johnston 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are 6 | * met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in 11 | * the documentation and/or other materials provided with the 12 | * distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | */ 26 | 27 | #ifndef _SYSCALL_H_ 28 | #define _SYSCALL_H_ 29 | 30 | #include 31 | #include 32 | #include 33 | 34 | #include 35 | 36 | SET_DECLARE(syscalls, struct scdesc); 37 | 38 | #define SYSCALL_ADD(desc) DATA_SET(syscalls, desc) 39 | 40 | enum scargtype { 41 | ARG_UNSPEC, 42 | ARG_FD, 43 | ARG_DIRFD, 44 | ARG_PATH, 45 | ARG_SOCKET, 46 | ARG_MEMADDR, 47 | ARG_MEMLEN, 48 | ARG_MODE, 49 | ARG_PID, 50 | ARG_PROCDESC, 51 | ARG_IFLAGMASK, 52 | ARG_LFLAGMASK, 53 | ARG_CMD, 54 | ARG_UID, 55 | ARG_GID, 56 | ARG_KQUEUE, 57 | ARG_SCHED_PARAM, 58 | ARG_TIMESPEC, 59 | }; 60 | 61 | /* System call argument descriptor. */ 62 | struct scargdesc { 63 | enum scargtype sa_type; /* argument type */ 64 | const char *sa_name; /* argument name */ 65 | union { 66 | int *sa_iflags; 67 | long *sa_lflags; 68 | int *sa_cmds; 69 | }; 70 | int sa_argcnt; 71 | }; 72 | 73 | enum scgroup { 74 | SC_GROUP_VM = (1 << 0), 75 | SC_GROUP_SCHED = (1 << 1), 76 | SC_GROUP_FORK = (1 << 2), 77 | SC_GROUP_FILEIO = (1 << 3), 78 | }; 79 | 80 | #define SYSCALL_MAXARGS 8 81 | 82 | /* 83 | * A system call descriptor. This contains all the static information needed 84 | * to test a given system call. 85 | */ 86 | struct scdesc { 87 | int sd_num; /* system call number */ 88 | const char *sd_name; /* system call name */ 89 | int sd_nargs; /* number of arguments */ 90 | u_int sd_groups; /* system call groups */ 91 | void (*sd_fixup)(u_long *); /* pre-syscall hook */ 92 | void (*sd_cleanup)(u_long *, u_long); /* post-syscall hook */ 93 | struct scargdesc sd_args[]; /* argument descriptors */ 94 | }; 95 | 96 | bool sc_filter(const struct scdesc *, const char *, size_t, const char *, 97 | size_t); 98 | bool sc_lookup(const char *, int *); 99 | bool scgroup_lookup(const char *, enum scgroup *); 100 | 101 | #endif /* _SYSCALL_H_ */ 102 | -------------------------------------------------------------------------------- /src/fork.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2014 Mark Johnston 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are 6 | * met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in 11 | * the documentation and/or other materials provided with the 12 | * distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | */ 26 | 27 | #include 28 | #include 29 | 30 | #include 31 | #include 32 | 33 | #include "syscall.h" 34 | 35 | /* 36 | * System call definitions for fork(2) and related calls. 37 | */ 38 | 39 | void rfork_fixup(u_long *args); 40 | void fork_cleanup(u_long *args, u_long ret); 41 | 42 | static struct scdesc fork_desc = { 43 | .sd_num = SYS_fork, 44 | .sd_name = "fork", 45 | .sd_nargs = 0, 46 | .sd_groups = SC_GROUP_FORK, 47 | .sd_cleanup = fork_cleanup, 48 | }; 49 | SYSCALL_ADD(fork_desc); 50 | 51 | static int rfork_flags[] = { 52 | RFPROC, 53 | RFNOWAIT, 54 | RFFDG, 55 | RFCFDG, 56 | RFTHREAD, 57 | RFMEM, 58 | RFSIGSHARE, 59 | RFTSIGZMB, 60 | RFLINUXTHPN, 61 | }; 62 | 63 | static struct scdesc rfork_desc = { 64 | .sd_num = SYS_rfork, 65 | .sd_name = "rfork", 66 | .sd_nargs = 1, 67 | .sd_groups = SC_GROUP_FORK, 68 | .sd_fixup = rfork_fixup, 69 | .sd_cleanup = fork_cleanup, 70 | .sd_args = { 71 | { 72 | .sa_type = ARG_IFLAGMASK, 73 | .sa_name = "flags", 74 | .sa_iflags = rfork_flags, 75 | .sa_argcnt = nitems(rfork_flags), 76 | }, 77 | }, 78 | }; 79 | SYSCALL_ADD(rfork_desc); 80 | 81 | void 82 | rfork_fixup(u_long *args) 83 | { 84 | 85 | args[0] |= RFPROC; 86 | args[0] &= ~(RFMEM | RFNOWAIT | RFTSIGZMB | RFLINUXTHPN); 87 | } 88 | 89 | #ifdef notyet 90 | static struct scdesc vfork_desc = { 91 | .sd_num = SYS_vfork, 92 | .sd_name = "vfork", 93 | .sd_nargs = 0, 94 | .sd_groups = SC_GROUP_FORK, 95 | .sd_cleanup = fork_cleanup, 96 | }; 97 | SYSCALL_ADD(vfork_desc); 98 | #endif 99 | 100 | void 101 | fork_cleanup(u_long *args __unused, u_long ret) 102 | { 103 | int status = 0; 104 | 105 | if (ret == 0) 106 | _exit(0); 107 | else if ((pid_t)ret > 0) { 108 | if (wait(&status) == -1) 109 | err(1, "wait"); 110 | if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) 111 | errx(1, "unexpected exit status %d\n", status); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/sched.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2014 Mark Johnston 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are 6 | * met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in 11 | * the documentation and/or other materials provided with the 12 | * distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | */ 26 | 27 | #include 28 | 29 | #include "syscall.h" 30 | 31 | /* 32 | * System call definitions for sched_*. 33 | */ 34 | 35 | static struct scdesc sched_setparam_desc __unused = { 36 | .sd_num = SYS_sched_setparam, 37 | .sd_name = "sched_setparam", 38 | .sd_nargs = 2, 39 | .sd_groups = SC_GROUP_SCHED, 40 | .sd_args = { 41 | { 42 | .sa_type = ARG_PID, 43 | .sa_name = "pid", 44 | }, 45 | { 46 | .sa_type = ARG_SCHED_PARAM, 47 | .sa_name = "param", 48 | }, 49 | }, 50 | }; 51 | #ifdef notyet 52 | SYSCALL_ADD(sched_setparam_desc); 53 | #endif 54 | 55 | static struct scdesc sched_getparam_desc __unused = { 56 | .sd_num = SYS_sched_getparam, 57 | .sd_name = "sched_getparam", 58 | .sd_nargs = 2, 59 | .sd_groups = SC_GROUP_SCHED, 60 | .sd_args = { 61 | { 62 | .sa_type = ARG_PID, 63 | .sa_name = "pid", 64 | }, 65 | { 66 | .sa_type = ARG_SCHED_PARAM, 67 | .sa_name = "param", 68 | }, 69 | }, 70 | }; 71 | #ifdef notyet 72 | SYSCALL_ADD(sched_getparam_desc); 73 | #endif 74 | 75 | static int sched_policies[] = 76 | { 77 | SCHED_FIFO, 78 | SCHED_OTHER, 79 | SCHED_RR, 80 | }; 81 | 82 | static struct scdesc sched_setscheduler_desc __unused = { 83 | .sd_num = SYS_sched_setscheduler, 84 | .sd_name = "sched_setscheduler", 85 | .sd_nargs = 3, 86 | .sd_groups = SC_GROUP_SCHED, 87 | .sd_args = { 88 | { 89 | .sa_type = ARG_PID, 90 | .sa_name = "pid", 91 | }, 92 | { 93 | .sa_type = ARG_CMD, 94 | .sa_name = "policy", 95 | .sa_cmds = sched_policies, 96 | .sa_argcnt = nitems(sched_policies), 97 | }, 98 | { 99 | .sa_type = ARG_SCHED_PARAM, 100 | .sa_name = "param", 101 | }, 102 | }, 103 | }; 104 | #ifdef notyet 105 | SYSCALL_ADD(sched_setscheduler_desc); 106 | #endif 107 | 108 | static struct scdesc sched_getscheduler_desc __unused = { 109 | .sd_num = SYS_sched_getscheduler, 110 | .sd_name = "sched_getscheduler", 111 | .sd_nargs = 1, 112 | .sd_groups = SC_GROUP_SCHED, 113 | .sd_args = { 114 | { 115 | .sa_type = ARG_PID, 116 | .sa_name = "pid", 117 | }, 118 | }, 119 | }; 120 | #ifdef notyet 121 | SYSCALL_ADD(sched_getscheduler_desc); 122 | #endif 123 | 124 | static struct scdesc sched_yield_desc __unused = { 125 | .sd_num = SYS_sched_yield, 126 | .sd_name = "sched_yield", 127 | .sd_nargs = 0, 128 | .sd_groups = SC_GROUP_SCHED, 129 | }; 130 | #ifdef notyet 131 | SYSCALL_ADD(sched_yield_desc); 132 | #endif 133 | 134 | static struct scdesc sched_get_priority_max_desc __unused = { 135 | .sd_num = SYS_sched_get_priority_max, 136 | .sd_name = "sched_get_priority_max", 137 | .sd_nargs = 1, 138 | .sd_groups = SC_GROUP_SCHED, 139 | .sd_args = { 140 | { 141 | .sa_type = ARG_CMD, 142 | .sa_name = "policy", 143 | .sa_cmds = sched_policies, 144 | .sa_argcnt = nitems(sched_policies), 145 | }, 146 | }, 147 | }; 148 | #ifdef notyet 149 | SYSCALL_ADD(sched_get_priority_max_desc); 150 | #endif 151 | 152 | static struct scdesc sched_get_priority_min_desc __unused = { 153 | .sd_num = SYS_sched_get_priority_min, 154 | .sd_name = "sched_get_priority_min", 155 | .sd_nargs = 1, 156 | .sd_groups = SC_GROUP_SCHED, 157 | .sd_args = { 158 | { 159 | .sa_type = ARG_CMD, 160 | .sa_name = "policy", 161 | .sa_cmds = sched_policies, 162 | .sa_argcnt = nitems(sched_policies), 163 | }, 164 | }, 165 | }; 166 | #ifdef notyet 167 | SYSCALL_ADD(sched_get_priority_min_desc); 168 | #endif 169 | 170 | static struct scdesc sched_rr_get_interval_desc __unused = { 171 | .sd_num = SYS_sched_rr_get_interval, 172 | .sd_name = "sched_rr_get_interval", 173 | .sd_nargs = 2, 174 | .sd_groups = SC_GROUP_SCHED, 175 | .sd_args = { 176 | { 177 | .sa_type = ARG_PID, 178 | .sa_name = "pid", 179 | }, 180 | { 181 | .sa_type = ARG_TIMESPEC, 182 | .sa_name = "interval", 183 | }, 184 | }, 185 | }; 186 | #ifdef notyet 187 | SYSCALL_ADD(sched_rr_get_interval_desc); 188 | #endif 189 | -------------------------------------------------------------------------------- /src/rman.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2015 Mark Johnston 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are 6 | * met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in 11 | * the documentation and/or other materials provided with the 12 | * distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | */ 26 | 27 | #include 28 | 29 | #include 30 | #include 31 | #include 32 | 33 | #include "rman.h" 34 | #include "util.h" 35 | 36 | #define rman_adjust(start, len) do { \ 37 | len += start - (start & ~(rman->rm_blksz - 1)); \ 38 | start = start & ~(rman->rm_blksz - 1); \ 39 | len = roundup2(len, rman->rm_blksz); \ 40 | } while (0) 41 | 42 | static void rman_validate(struct rman *rman); 43 | 44 | int 45 | rman_init(struct rman *rman, u_int blksz, rman_pool_init initcb) 46 | { 47 | 48 | if (__bitcount(blksz) != 1) 49 | /* blksz must be a power of two. */ 50 | return (1); 51 | TAILQ_INIT(&rman->rm_res); 52 | rman->rm_blksz = blksz; 53 | rman->rm_entries = 0; 54 | if (initcb != NULL) 55 | return (initcb(rman)); 56 | return (0); 57 | } 58 | 59 | /* 60 | * Insert the resource range [start, start+len) into the list, coalescing 61 | * entries if needed. start+len must be at most ULONG_MAX; in particular, a 62 | * range cannot contain ULONG_MAX. 63 | */ 64 | void 65 | rman_add(struct rman *rman, u_long start, u_long len) 66 | { 67 | struct resource *nres, *res, *next; 68 | 69 | assert(ULONG_MAX - start >= len); 70 | 71 | if (len == 0) 72 | return; 73 | 74 | rman_adjust(start, len); 75 | 76 | TAILQ_FOREACH(res, &rman->rm_res, r_next) { 77 | if (start > res->r_start + res->r_len) 78 | continue; 79 | 80 | /* We need to add a new resource record. */ 81 | if (start + len < res->r_start) 82 | break; 83 | 84 | /* The new range and the current range overlap. */ 85 | res->r_start = min(start, res->r_start); 86 | if (start <= res->r_start) 87 | res->r_len += res->r_start - start; 88 | else 89 | len += start - res->r_start; 90 | res->r_len = max(res->r_len, len); 91 | assert(res->r_len > 0); 92 | 93 | /* Coalesce adjacent overlapping ranges. */ 94 | while ((next = TAILQ_NEXT(res, r_next)) != NULL && 95 | next->r_start <= res->r_start + res->r_len) { 96 | next->r_len += next->r_start - res->r_start; 97 | res->r_len = max(next->r_len, res->r_len); 98 | TAILQ_REMOVE(&rman->rm_res, next, r_next); 99 | free(next); 100 | rman->rm_entries--; 101 | } 102 | assert(res->r_len > 0); 103 | goto done; 104 | } 105 | 106 | nres = xmalloc(sizeof(*nres)); 107 | nres->r_start = start; 108 | nres->r_len = len; 109 | rman->rm_entries++; 110 | if (res != NULL) 111 | TAILQ_INSERT_BEFORE(res, nres, r_next); 112 | else 113 | TAILQ_INSERT_TAIL(&rman->rm_res, nres, r_next); 114 | 115 | assert(nres->r_len > 0); 116 | done: 117 | rman_validate(rman); 118 | } 119 | 120 | /* 121 | * Return a random resource range from the pool without removing it. The range 122 | * length must be a multiple of the rman blksz and must be at most 123 | * blksz * maxblks if maxblks is greater than 0. 124 | * 125 | * The return value is non-zero if no resources are available. 126 | */ 127 | int 128 | rman_select(struct rman *rman, u_long *start, u_long *len, u_int maxblks) 129 | { 130 | struct resource *res; 131 | u_int blks; 132 | int interval; 133 | 134 | if (rman->rm_entries == 0) { 135 | *start = *len = 0; 136 | return (1); 137 | } 138 | 139 | interval = (random() % rman->rm_entries); 140 | TAILQ_FOREACH(res, &rman->rm_res, r_next) { 141 | if (interval-- > 0) 142 | continue; 143 | blks = res->r_len / rman->rm_blksz; 144 | 145 | assert(blks > 0); 146 | *start = (random() % blks) * rman->rm_blksz + res->r_start; 147 | blks -= (*start - res->r_start) / rman->rm_blksz; 148 | if (maxblks > 0 && blks > maxblks) 149 | blks = maxblks; 150 | *len = ((random() % blks) + 1) * rman->rm_blksz; 151 | break; 152 | } 153 | assert(interval == -1); 154 | assert(*len % rman->rm_blksz == 0); 155 | assert(ULONG_MAX - *start >= *len); 156 | return (0); 157 | } 158 | 159 | /* 160 | * Remove the specified resource range. The range must be present. 161 | */ 162 | void 163 | rman_release(struct rman *rman, u_long start, u_long len) 164 | { 165 | struct resource *res, *nres; 166 | 167 | assert(ULONG_MAX - start >= len); 168 | 169 | rman_adjust(start, len); 170 | 171 | TAILQ_FOREACH(res, &rman->rm_res, r_next) { 172 | assert(start >= res->r_start); 173 | if (start > res->r_start + res->r_len) 174 | continue; 175 | 176 | assert(res->r_len >= len); 177 | if (start == res->r_start || 178 | start + len == res->r_start + res->r_len) { 179 | /* We just need to trim an existing range. */ 180 | if (start == res->r_start) 181 | res->r_start = start + len; 182 | res->r_len -= len; 183 | if (res->r_len == 0) { 184 | TAILQ_REMOVE(&rman->rm_res, res, r_next); 185 | free(res); 186 | rman->rm_entries--; 187 | } 188 | } else { 189 | /* An existing range is getting split into two. */ 190 | nres = xmalloc(sizeof(*nres)); 191 | nres->r_start = start + len; 192 | nres->r_len = res->r_len - len - (start - res->r_start); 193 | assert(nres->r_len > 0); 194 | rman->rm_entries++; 195 | TAILQ_INSERT_AFTER(&rman->rm_res, res, nres, r_next); 196 | 197 | res->r_len = start - res->r_start; 198 | } 199 | break; 200 | } 201 | assert(res != NULL); 202 | rman_validate(rman); 203 | } 204 | 205 | #ifdef INVARIANTS 206 | /* 207 | * Ensure that the resource pool is well-formed. 208 | */ 209 | static void 210 | rman_validate(struct rman *rman) 211 | { 212 | struct resource *res, *next; 213 | int count; 214 | 215 | assert(rman->rm_entries >= 0); 216 | 217 | count = 0; 218 | TAILQ_FOREACH(res, &rman->rm_res, r_next) { 219 | assert(res->r_len > 0); 220 | if ((next = TAILQ_NEXT(res, r_next)) != NULL) 221 | assert(res->r_start + res->r_len < next->r_start); 222 | count++; 223 | } 224 | assert(count == rman->rm_entries); 225 | } 226 | #else 227 | static void 228 | rman_validate(struct rman *rman __unused) 229 | { 230 | } 231 | #endif 232 | -------------------------------------------------------------------------------- /src/argpool.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2014 Mark Johnston 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are 6 | * met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in 11 | * the documentation and/or other materials provided with the 12 | * distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | #include "argpool.h" 41 | #include "params.h" 42 | #include "rman.h" 43 | #include "util.h" 44 | 45 | static struct rman dirfds; 46 | static struct rman fds; 47 | static struct rman memblks; 48 | 49 | static void hier_init(const char *, int); 50 | static void hier_extend(int, int); 51 | static int memblk_init(struct rman *); 52 | 53 | static int 54 | memblk_init(struct rman *rman __unused) 55 | { 56 | void *addr; 57 | size_t len; 58 | u_int pgcnt; 59 | 60 | pgcnt = param_number("memblk-page-count"); 61 | while (pgcnt > 0) { 62 | /* 63 | * Allow up to memblk-max-size pages in a memory block, clamp to 64 | * pgcnt. 65 | */ 66 | len = (random() % param_number("memblk-max-size")) + 1; 67 | if (len > pgcnt) 68 | len = pgcnt; 69 | pgcnt -= len; 70 | len *= getpagesize(); 71 | 72 | addr = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_ANON, -1, 0); 73 | if (addr == NULL) 74 | err(1, "mmap"); 75 | if (random() % 2 == 0) 76 | memset(addr, 0, len); 77 | 78 | ap_memblk_map(addr, len); 79 | } 80 | return (0); 81 | } 82 | 83 | void 84 | ap_memblk_map(void *addr, size_t len) 85 | { 86 | 87 | rman_add(&memblks, (uintptr_t)addr, len); 88 | } 89 | 90 | /* 91 | * Randomly pick a memory block from the pool. 92 | */ 93 | int 94 | ap_memblk_random(struct arg_memblk *memblk) 95 | { 96 | u_long start, len; 97 | 98 | if (rman_select(&memblks, &start, &len, 0)) 99 | return (1); 100 | memblk->addr = (void *)(uintptr_t)start; 101 | memblk->len = len; 102 | return (0); 103 | } 104 | 105 | void 106 | ap_memblk_unmap(void *addr, size_t len) 107 | { 108 | 109 | rman_release(&memblks, (u_long)(uintptr_t)addr, len); 110 | } 111 | 112 | /* 113 | * Create a random file hierarchy rooted at the specified path. 114 | * 115 | * The size of the resulting file hierarchy is bounded by four parameters: 116 | * hier-depth, hier-max-fsize, hier-max-files-per-dir, and 117 | * hier-max-subdirs-per-dir. Indeed, if we refer to these four values by 118 | * n, s, f, and m respectively, the bound is given by 119 | * 120 | * f * s * (m^{n+1} - 1) / (m - 1). 121 | * 122 | * This does not include the sizes of the directories themselves, which may 123 | * become significant for large f and n. 124 | * 125 | * The default values give a very rough bound of 4GB. In practice, the 126 | * hierarchy will be somewhat smaller. 127 | * 128 | * XXX we need to have some symlinks and hard links. 129 | */ 130 | static void 131 | hier_init(const char *path, int depth) 132 | { 133 | struct stat sb; 134 | int fd; 135 | 136 | if (stat(path, &sb) == 0) { 137 | if (!S_ISDIR(sb.st_mode)) 138 | errx(1, "path '%s' exists and isn't a directory", path); 139 | } else if (mkdir(path, 0777) != 0) 140 | err(1, "couldn't create '%s'", path); 141 | 142 | fd = open(path, O_DIRECTORY | O_RDONLY); 143 | if (fd < 0) 144 | err(1, "opening '%s'", path); 145 | hier_extend(fd, depth); 146 | (void)close(fd); 147 | } 148 | 149 | static void 150 | hier_extend(int dirfd, int depth) 151 | { 152 | char buf[16384], file[NAME_MAX]; 153 | ssize_t nbytes; 154 | u_int fsize; 155 | int fd, numfiles; 156 | 157 | memset(buf, 0, sizeof(buf)); 158 | 159 | numfiles = (random() % param_number("hier-max-files-per-dir")) + 1; 160 | 161 | /* Create files. */ 162 | for (int i = 0; i < numfiles; i++) { 163 | randfile(file); 164 | fd = openat(dirfd, file, O_CREAT | O_RDWR, 0666); 165 | if (fd < 0) 166 | err(1, "opening '%s'", file); 167 | 168 | fsize = random() % param_number("hier-max-fsize"); 169 | while (fsize > 0) { 170 | nbytes = write(fd, buf, 171 | fsize > sizeof(buf) ? sizeof(buf) : fsize); 172 | if (nbytes < 0) 173 | err(1, "writing to '%s'", file); 174 | fsize -= nbytes; 175 | } 176 | ap_fd_add(fd); 177 | } 178 | 179 | if (depth <= 1) 180 | return; 181 | 182 | numfiles = (random() % param_number("hier-max-subdirs-per-dir")) + 1; 183 | 184 | /* Create subdirs. */ 185 | for (int i = 0; i < numfiles; i++) { 186 | randfile(file); 187 | if (mkdirat(dirfd, file, 0777) != 0) 188 | err(1, "creating directory '%s'", file); 189 | fd = openat(dirfd, file, O_DIRECTORY | O_RDONLY); 190 | if (fd < 0) 191 | err(1, "opening directory '%s'", file); 192 | hier_extend(fd, depth - 1); 193 | ap_dirfd_add(fd); 194 | } 195 | } 196 | 197 | static void 198 | descpool_add(struct rman *rman, int fd) 199 | { 200 | 201 | rman_add(rman, fd, 1); 202 | } 203 | 204 | static void 205 | descpool_release(struct rman *rman, int fd) 206 | { 207 | 208 | rman_release(rman, fd, 1); 209 | } 210 | 211 | static int 212 | descpool_select(struct rman *rman) 213 | { 214 | u_long start, len; 215 | 216 | rman_select(rman, &start, &len, 1); 217 | assert(len == 1); 218 | return ((int)start); 219 | } 220 | 221 | void 222 | ap_fd_add(int fd) 223 | { 224 | 225 | descpool_add(&fds, fd); 226 | } 227 | 228 | void 229 | ap_fd_close(int fd) 230 | { 231 | 232 | descpool_release(&fds, fd); 233 | } 234 | 235 | int 236 | ap_fd_random(void) 237 | { 238 | 239 | return (descpool_select(&fds)); 240 | } 241 | 242 | void 243 | ap_dirfd_add(int fd) 244 | { 245 | 246 | descpool_add(&dirfds, fd); 247 | } 248 | 249 | void 250 | ap_dirfd_close(int fd) 251 | { 252 | 253 | descpool_release(&dirfds, fd); 254 | } 255 | 256 | int 257 | ap_dirfd_random(void) 258 | { 259 | 260 | return (descpool_select(&dirfds)); 261 | } 262 | 263 | /* 264 | * Initialize the argument pool. 265 | */ 266 | void 267 | ap_init(void) 268 | { 269 | 270 | (void)rman_init(&memblks, getpagesize(), memblk_init); 271 | 272 | (void)rman_init(&dirfds, 1, NULL); 273 | (void)rman_init(&fds, 1, NULL); 274 | hier_init(param_string("hier-root"), param_number("hier-depth")); 275 | } 276 | -------------------------------------------------------------------------------- /src/params.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2014 Mark Johnston 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are 6 | * met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in 11 | * the documentation and/or other materials provided with the 12 | * distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | */ 26 | 27 | #include 28 | #include 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include "params.h" 38 | #include "syscall.h" 39 | #include "util.h" 40 | 41 | static void init_defaults(void); 42 | 43 | static nvlist_t *g_params; 44 | static nvlist_t *g_descriptions; 45 | 46 | void 47 | params_init(char **args) 48 | { 49 | uintmax_t num; 50 | char *endptr, *name, *val; 51 | int base; 52 | bool flag; 53 | 54 | g_params = nvlist_create(NV_FLAG_IGNORE_CASE); 55 | g_descriptions = nvlist_create(NV_FLAG_IGNORE_CASE); 56 | if (g_params == NULL || g_descriptions == NULL) 57 | err(1, "nvlist_create failed"); 58 | 59 | init_defaults(); 60 | 61 | while (*args != NULL) { 62 | name = val = *args; 63 | (void)strsep(&val, "="); 64 | if (val == NULL) 65 | errx(1, "invalid name-value pair '%s'", *args); 66 | 67 | if (!nvlist_exists(g_params, name)) 68 | errx(1, "non-existent option '%s'", name); 69 | else if (nvlist_exists_bool(g_params, name)) { 70 | if (strcasecmp(val, "true") != 0 && 71 | strcasecmp(val, "false") != 0) 72 | errx(1, 73 | "invalid value '%s' for boolean option '%s'", 74 | val, name); 75 | flag = strcasecmp(val, "true") == 0; 76 | nvlist_free_bool(g_params, name); 77 | nvlist_add_bool(g_params, name, flag); 78 | } else if (nvlist_exists_number(g_params, name)) { 79 | base = 10; 80 | if (strlen(val) >= 2 && strcmp(val, "0x") == 0) 81 | base = 16; 82 | errno = 0; 83 | num = strtoumax(val, &endptr, base); 84 | if (*val == '\0' || *endptr != '\0' || errno != 0) 85 | errx(1, 86 | "invalid value '%s' for numeric option '%s'", 87 | val, name); 88 | else if (num > UINT64_MAX) 89 | errx(1, "value '%s' for '%s' is out of range", 90 | val, name); 91 | nvlist_free_number(g_params, name); 92 | nvlist_add_number(g_params, name, (uint64_t)num); 93 | } else if (nvlist_exists_string(g_params, name)) { 94 | nvlist_free_string(g_params, name); 95 | nvlist_add_string(g_params, name, val); 96 | } else 97 | errx(1, "unknown type for option '%s'", name); 98 | 99 | if (nvlist_error(g_params) != 0) 100 | errx(1, "couldn't set option '%s': %s", name, 101 | strerror(nvlist_error(g_params))); 102 | free(*args); 103 | args++; 104 | } 105 | } 106 | 107 | void 108 | params_dump() 109 | { 110 | const char *name; 111 | void *cookie; 112 | int type; 113 | 114 | cookie = NULL; 115 | while ((name = nvlist_next(g_params, &type, &cookie)) != NULL) { 116 | printf("%s: ", name); 117 | switch (type) { 118 | case NV_TYPE_BOOL: 119 | printf("%s", nvlist_get_bool(g_params, name) ? 120 | "true" : "false"); 121 | break; 122 | case NV_TYPE_NUMBER: 123 | printf("%ju", nvlist_get_number(g_params, name)); 124 | break; 125 | case NV_TYPE_STRING: 126 | printf("%s", nvlist_get_string(g_params, name)); 127 | break; 128 | default: 129 | errx(1, "unexpected type '%d' for option '%s'", type, 130 | name); 131 | } 132 | printf("\n%s\n\n", nvlist_get_string(g_descriptions, name)); 133 | } 134 | } 135 | 136 | bool 137 | param_flag(const char *name) 138 | { 139 | 140 | if (!nvlist_exists_bool(g_params, name)) 141 | errx(1, "invalid option '%s'", name); 142 | return (nvlist_get_string(g_params, name)); 143 | } 144 | 145 | uint64_t 146 | param_number(const char *name) 147 | { 148 | 149 | if (!nvlist_exists_number(g_params, name)) 150 | errx(1, "invalid option '%s'", name); 151 | return (nvlist_get_number(g_params, name)); 152 | } 153 | 154 | const char * 155 | param_string(const char *name) 156 | { 157 | 158 | if (!nvlist_exists_string(g_params, name)) 159 | errx(1, "invalid option '%s'", name); 160 | return (nvlist_get_string(g_params, name)); 161 | } 162 | 163 | static void 164 | init_defaults() 165 | { 166 | char tmppath[PATH_MAX]; 167 | 168 | snprintf(tmppath, sizeof(tmppath), "%s", "/tmp/sysfuzz.XXXXXX"); 169 | if (mkdtemp(tmppath) == NULL) 170 | err(1, "mkdtemp"); 171 | 172 | struct { 173 | const char *name; 174 | const char *descr; 175 | int type; 176 | union { 177 | const char *string; 178 | uint64_t number; 179 | bool flag; 180 | }; 181 | } params[] = { 182 | { 183 | .name = "hier-depth", 184 | .descr = "Maximum file hierarchy depth.", 185 | .type = NV_TYPE_NUMBER, 186 | .number = 4, 187 | }, 188 | { 189 | .name = "hier-max-fsize", 190 | .descr = "Maximum file size for random file creation.", 191 | .type = NV_TYPE_NUMBER, 192 | .number = (1024 * 1024), 193 | }, 194 | { 195 | .name = "hier-max-files-per-dir", 196 | .descr = "Maximum number of random files per directory.", 197 | .type = NV_TYPE_NUMBER, 198 | .number = 10, 199 | }, 200 | { 201 | .name = "hier-max-subdirs-per-dir", 202 | .descr = "Maximum number of subdirectories per directory.", 203 | .type = NV_TYPE_NUMBER, 204 | .number = 7, 205 | }, 206 | { 207 | .name = "hier-root", 208 | .descr = "The root directory for a random file hierarchy.", 209 | .type = NV_TYPE_STRING, 210 | .string = tmppath, 211 | }, 212 | { 213 | .name = "memblk-page-count", 214 | .descr = "The total number of pages to map in memblks.", 215 | .type = NV_TYPE_NUMBER, 216 | .number = pagecnt() / (ncpu() * 4), 217 | }, 218 | { 219 | .name = "memblk-max-size", 220 | .descr = "The maximum number of pages in a memblk.", 221 | .type = NV_TYPE_NUMBER, 222 | .number = 16 * 1024, 223 | }, 224 | { 225 | .name = "num-fuzzers", 226 | .descr = "The number of fuzzer processes to run.", 227 | .type = NV_TYPE_NUMBER, 228 | .number = ncpu(), 229 | }, 230 | }; 231 | 232 | for (u_int i = 0; i < nitems(params); i++) { 233 | switch (params[i].type) { 234 | case NV_TYPE_BOOL: 235 | nvlist_add_bool(g_params, params[i].name, 236 | params[i].flag); 237 | break; 238 | case NV_TYPE_NUMBER: 239 | nvlist_add_number(g_params, params[i].name, 240 | params[i].number); 241 | break; 242 | case NV_TYPE_STRING: 243 | nvlist_add_string(g_params, params[i].name, 244 | params[i].string); 245 | break; 246 | default: 247 | errx(1, "invalid option type %d", params[i].type); 248 | } 249 | 250 | nvlist_add_string(g_descriptions, params[i].name, 251 | params[i].descr); 252 | } 253 | } 254 | -------------------------------------------------------------------------------- /src/sysfuzz.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2014 Mark Johnston 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are 6 | * met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in 11 | * the documentation and/or other materials provided with the 12 | * distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | #include "argpool.h" 42 | #include "params.h" 43 | #include "syscall.h" 44 | #include "util.h" 45 | 46 | struct sctable { 47 | int cnt; 48 | struct scdesc *scds[1]; 49 | }; 50 | 51 | /* Allocate a table of system call descriptors. */ 52 | static struct sctable * 53 | sctable_alloc(char *sclist, char *scgrplist) 54 | { 55 | struct sctable *table; 56 | struct scdesc **desc; 57 | const char *sc, *scgrp; 58 | char *list; 59 | size_t scs, scgrps; 60 | int sccnt; 61 | 62 | sccnt = 0; 63 | SET_FOREACH(desc, syscalls) 64 | sccnt++; 65 | 66 | table = malloc(sizeof(*table) + sccnt * sizeof(struct scdesc *)); 67 | if (table == NULL) 68 | err(1, "malloc"); 69 | 70 | /* Validate the list of syscall and syscall group filters. */ 71 | scs = scgrps = 0; 72 | list = sclist; 73 | while ((sc = strsep(&list, ",")) != NULL) { 74 | if (!sc_lookup(sc, NULL)) 75 | errx(1, "unknown syscall '%s'", sc); 76 | scs++; 77 | } 78 | list = scgrplist; 79 | while ((scgrp = strsep(&list, ",")) != NULL) { 80 | if (!scgroup_lookup(scgrp, NULL)) 81 | errx(1, "unknown syscall group '%s'", scgrp); 82 | scgrps++; 83 | } 84 | 85 | sccnt = 0; 86 | SET_FOREACH(desc, syscalls) { 87 | if (!sc_filter(*desc, sclist, scs, scgrplist, scgrps)) 88 | table->scds[sccnt++] = *desc; 89 | } 90 | table->cnt = sccnt; 91 | 92 | return (table); 93 | } 94 | 95 | /* List the members of the given system call group. */ 96 | static void 97 | scgroup_list(const char *scgrp) 98 | { 99 | struct scdesc **desc; 100 | enum scgroup group; 101 | 102 | group = 0; 103 | if (!scgroup_lookup(scgrp, &group)) 104 | errx(1, "unknown syscall group '%s'", scgrp); 105 | 106 | SET_FOREACH(desc, syscalls) { 107 | if ((group & (*desc)->sd_groups) != 0) 108 | printf("%s\n", (*desc)->sd_name); 109 | } 110 | } 111 | 112 | static void 113 | scargs_alloc(u_long *args, struct scdesc *sd) 114 | { 115 | struct arg_memblk memblk; 116 | int argcnt, ci; 117 | 118 | for (int i = 0; i < sd->sd_nargs; i++) { 119 | switch (sd->sd_args[i].sa_type) { 120 | case ARG_UNSPEC: 121 | args[i] = random(); 122 | break; 123 | case ARG_MEMADDR: 124 | ap_memblk_random(&memblk); 125 | args[i] = (uintptr_t)memblk.addr; 126 | if (i + 1 < sd->sd_nargs && 127 | sd->sd_args[i + 1].sa_type == ARG_MEMLEN) 128 | args[++i] = memblk.len; 129 | break; 130 | case ARG_MEMLEN: 131 | ap_memblk_random(&memblk); 132 | args[i] = memblk.len; 133 | break; 134 | case ARG_CMD: 135 | ci = random() % sd->sd_args[i].sa_argcnt; 136 | args[i] = sd->sd_args[i].sa_cmds[ci]; 137 | break; 138 | case ARG_IFLAGMASK: 139 | argcnt = sd->sd_args[i].sa_argcnt; 140 | for (int fi = random() % (argcnt + 1); fi > 0; fi--) 141 | args[i] |= 142 | sd->sd_args[i].sa_iflags[random() % argcnt]; 143 | break; 144 | case ARG_FD: 145 | args[i] = ap_fd_random(); 146 | break; 147 | default: 148 | args[i] = 0; 149 | break; 150 | } 151 | } 152 | } 153 | 154 | static void 155 | scloop(u_long ncalls, u_long seed, struct sctable *table) 156 | { 157 | u_long args[SYSCALL_MAXARGS], ret, sofar; 158 | struct scdesc *sd; 159 | u_int n; 160 | int status, toreap; 161 | 162 | printf("%s: seeding with %lu\n", getprogname(), seed); 163 | 164 | for (n = param_number("num-fuzzers"); n > 0; n--) { 165 | pid_t pid = fork(); 166 | if (pid == -1) 167 | err(1, "fork"); 168 | else if (pid == 0) 169 | break; 170 | } 171 | 172 | if (n == 0) { 173 | toreap = param_number("num-fuzzers"); 174 | while (toreap > 0) { 175 | if (wait(&status) != -1) 176 | toreap--; 177 | } 178 | return; 179 | } else 180 | srandom(seed + n); 181 | 182 | for (sofar = 0; ncalls == 0 || sofar < ncalls; sofar++) { 183 | sd = table->scds[random() % table->cnt]; 184 | memset(args, 0, sizeof(args)); 185 | scargs_alloc(args, sd); 186 | 187 | if (sd->sd_fixup != NULL) 188 | (sd->sd_fixup)(args); 189 | ret = __syscall(sd->sd_num, args[0], args[1], args[2], args[3], 190 | args[4], args[5], args[6], args[7]); 191 | if (sd->sd_cleanup != NULL) 192 | (sd->sd_cleanup)(args, ret); 193 | } 194 | } 195 | 196 | /* If we're root, drop privileges. */ 197 | static void 198 | drop_privs() 199 | { 200 | struct passwd *pwd; 201 | struct group *grp; 202 | gid_t gid; 203 | 204 | if (geteuid() != 0) 205 | return; 206 | 207 | pwd = getpwnam("nobody"); 208 | if (pwd == NULL) 209 | return; 210 | grp = getgrnam("nobody"); 211 | if (grp == NULL) 212 | return; 213 | 214 | gid = grp->gr_gid; 215 | if (setgroups(1, &gid) != 0) 216 | err(1, "setgroups"); 217 | if (setgid(grp->gr_gid) != 0) 218 | err(1, "setgid"); 219 | if (setuid(pwd->pw_uid) != 0) 220 | err(1, "setuid"); 221 | } 222 | 223 | static u_long 224 | pickseed(void) 225 | { 226 | const char *path = "/dev/urandom"; 227 | u_long seed; 228 | ssize_t bytes; 229 | int fd; 230 | 231 | fd = open(path, O_RDONLY); 232 | if (fd < 0) 233 | err(1, "opening %s", path); 234 | 235 | bytes = read(fd, &seed, sizeof(seed)); 236 | if (bytes < 0) 237 | err(1, "reading from %s", path); 238 | else if (bytes != sizeof(seed)) 239 | errx(1, "short read from %s (got %zd bytes)", path, bytes); 240 | 241 | return (seed); 242 | } 243 | 244 | static void 245 | usage() 246 | { 247 | const char *pn = getprogname(); 248 | 249 | fprintf(stderr, 250 | "Usage:\t%s [-n count] [-p] [-c [,[,...]]]\n" 251 | "\t [-g [,[,...]]]\n" 252 | "\t [-s ] [-x [=]]\n", pn); 253 | fprintf(stderr, "\t%s -d\n", pn); 254 | fprintf(stderr, "\t%s -l \n", pn); 255 | exit(1); 256 | } 257 | 258 | int 259 | main(int argc, char **argv) 260 | { 261 | struct sctable *table; 262 | char **param, **params; 263 | char *end, *scgrp, *sclist, *scgrplist; 264 | u_long ncalls, seed; 265 | bool dropprivs = true, dumpparams = false; 266 | int ch; 267 | 268 | params = calloc(argc + 1, sizeof(*params)); 269 | if (params == NULL) 270 | err(1, "calloc"); 271 | param = params; 272 | 273 | seed = pickseed(); 274 | 275 | scgrp = sclist = scgrplist = NULL; 276 | while ((ch = getopt(argc, argv, "c:dg:l:n:ps:x:")) != -1) 277 | switch (ch) { 278 | case 'c': 279 | sclist = xstrdup(optarg); 280 | break; 281 | case 'd': 282 | dumpparams = true; 283 | break; 284 | case 'g': 285 | scgrplist = xstrdup(optarg); 286 | break; 287 | case 'l': 288 | scgrp = xstrdup(optarg); 289 | break; 290 | case 'n': 291 | errno = 0; 292 | ncalls = strtoul(optarg, &end, 10); 293 | if (optarg[0] == '\0' || *end != '\0' || errno != 0) 294 | errx(1, "invalid parameter '%s' for -n", optarg); 295 | break; 296 | case 'p': 297 | dropprivs = false; 298 | break; 299 | case 's': 300 | errno = 0; 301 | seed = strtoul(optarg, &end, 10); 302 | if (optarg[0] == '\0' || *end != '\0' || errno != 0) 303 | errx(1, "invalid parameter '%s' for -s", optarg); 304 | break; 305 | case 'x': 306 | *param++ = strdup(optarg); 307 | break; 308 | case '?': 309 | usage(); 310 | break; 311 | } 312 | 313 | /* Initialize runtime parameters. */ 314 | params_init(params); 315 | free(params); 316 | 317 | if (dumpparams) { 318 | if (argc != 2) 319 | usage(); 320 | params_dump(); 321 | return (0); 322 | } 323 | 324 | if (scgrp != NULL) { 325 | if (argc != 3) 326 | usage(); 327 | scgroup_list(scgrp); 328 | free(scgrp); 329 | return (0); 330 | } 331 | 332 | /* Initialize system call descriptors for the calls we'll be fuzzing. */ 333 | table = sctable_alloc(sclist, scgrplist); 334 | free(sclist); 335 | free(scgrplist); 336 | 337 | /* Create argument pools for system calls. */ 338 | ap_init(); 339 | 340 | /* 341 | * XXX there seems to be a truss/ptrace(2) bug which causes it to stop 342 | * tracing when the traced process changes its uid. 343 | */ 344 | if (dropprivs) 345 | drop_privs(); 346 | 347 | scloop(ncalls, seed, table); 348 | 349 | free(table); 350 | 351 | return (0); 352 | } 353 | -------------------------------------------------------------------------------- /src/vm.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2014 Mark Johnston 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are 6 | * met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in 11 | * the documentation and/or other materials provided with the 12 | * distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | */ 26 | 27 | #include 28 | #include 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #include "argpool.h" 37 | #include "params.h" 38 | #include "syscall.h" 39 | #include "util.h" 40 | 41 | /* 42 | * System call definitions for mmap(2) and friends. 43 | */ 44 | 45 | void mmap_fixup(u_long *); 46 | void mmap_cleanup(u_long *, u_long); 47 | void mincore_fixup(u_long *); 48 | void mincore_cleanup(u_long *, u_long); 49 | void munmap_cleanup(u_long *, u_long); 50 | 51 | static int mmap_prot_flags[] = { 52 | PROT_NONE, 53 | PROT_READ, 54 | PROT_WRITE, 55 | PROT_EXEC, 56 | }; 57 | 58 | static int mmap_flags[] = { 59 | #ifdef __LP64__ 60 | MAP_32BIT, 61 | #endif 62 | MAP_ALIGNED_SUPER, 63 | MAP_ANON, 64 | MAP_FIXED, 65 | MAP_HASSEMAPHORE, 66 | MAP_NOCORE, 67 | MAP_NOSYNC, 68 | MAP_PREFAULT_READ, 69 | MAP_PRIVATE, 70 | MAP_SHARED, 71 | MAP_STACK, 72 | MAP_EXCL, 73 | }; 74 | 75 | static struct scdesc mmap_desc = { 76 | .sd_num = SYS_mmap, 77 | .sd_name = "mmap", 78 | .sd_nargs = 6, 79 | .sd_groups = SC_GROUP_VM, 80 | .sd_fixup = mmap_fixup, 81 | .sd_cleanup = mmap_cleanup, 82 | .sd_args = { 83 | { 84 | .sa_type = ARG_UNSPEC, 85 | .sa_name = "addr", 86 | }, 87 | { 88 | .sa_type = ARG_UNSPEC, 89 | .sa_name = "len", 90 | }, 91 | { 92 | .sa_type = ARG_IFLAGMASK, 93 | .sa_name = "prot", 94 | .sa_iflags = mmap_prot_flags, 95 | .sa_argcnt = nitems(mmap_prot_flags), 96 | 97 | }, 98 | { 99 | /* XXX how to handle MAP_ALIGNED(n)? */ 100 | .sa_type = ARG_IFLAGMASK, 101 | .sa_name = "flags", 102 | .sa_iflags = mmap_flags, 103 | .sa_argcnt = nitems(mmap_flags), 104 | }, 105 | { 106 | .sa_type = ARG_FD, 107 | .sa_name = "fd", 108 | }, 109 | { 110 | .sa_type = ARG_UNSPEC, 111 | .sa_name = "offset", 112 | }, 113 | }, 114 | }; 115 | SYSCALL_ADD(mmap_desc); 116 | 117 | void 118 | mmap_fixup(u_long *args) 119 | { 120 | uint64_t fsize; 121 | 122 | if (random() % 2 == 0) { 123 | args[0] = (u_long)NULL; 124 | args[1] = (random() % param_number("memblk-max-size")) + 1; 125 | args[3] |= MAP_ANON; 126 | args[4] = (u_long)-1; 127 | args[5] = 0; 128 | } else { 129 | fsize = param_number("hier-max-fsize"); 130 | args[0] = (u_long)NULL; 131 | args[1] = random() % fsize; 132 | args[3] = MAP_PRIVATE; 133 | args[5] = random() % fsize; 134 | } 135 | args[3] &= ~(MAP_ALIGNED_SUPER | MAP_STACK | MAP_HASSEMAPHORE); /* XXX why? */ 136 | } 137 | 138 | void 139 | mmap_cleanup(u_long *args, u_long ret) 140 | { 141 | void *addr; 142 | 143 | addr = (void *)(uintptr_t)ret; 144 | assert(addr != NULL); 145 | if (addr == MAP_FAILED) 146 | /* The map request failed. */ 147 | return; 148 | 149 | ap_memblk_map(addr, args[1]); 150 | } 151 | 152 | static int madvise_cmds[] = { 153 | MADV_NORMAL, 154 | MADV_RANDOM, 155 | MADV_SEQUENTIAL, 156 | MADV_WILLNEED, 157 | MADV_DONTNEED, 158 | MADV_FREE, 159 | MADV_NOSYNC, 160 | MADV_AUTOSYNC, 161 | MADV_NOCORE, 162 | MADV_CORE, 163 | MADV_PROTECT, 164 | }; 165 | 166 | static struct scdesc madvise_desc = { 167 | .sd_num = SYS_madvise, 168 | .sd_name = "madvise", 169 | .sd_nargs = 3, 170 | .sd_groups = SC_GROUP_VM, 171 | .sd_args = { 172 | { 173 | .sa_type = ARG_MEMADDR, 174 | .sa_name = "addr", 175 | }, 176 | { 177 | .sa_type = ARG_MEMLEN, 178 | .sa_name = "len", 179 | }, 180 | { 181 | .sa_type = ARG_CMD, 182 | .sa_name = "behav", 183 | .sa_cmds = madvise_cmds, 184 | .sa_argcnt = nitems(madvise_cmds), 185 | }, 186 | }, 187 | }; 188 | SYSCALL_ADD(madvise_desc); 189 | 190 | static struct scdesc mincore_desc = { 191 | .sd_num = SYS_mincore, 192 | .sd_name = "mincore", 193 | .sd_nargs = 3, 194 | .sd_groups = SC_GROUP_VM, 195 | .sd_fixup = mincore_fixup, 196 | .sd_cleanup = mincore_cleanup, 197 | .sd_args = { 198 | { 199 | .sa_type = ARG_MEMADDR, 200 | .sa_name = "addr", 201 | }, 202 | { 203 | .sa_type = ARG_MEMLEN, 204 | .sa_name = "len", 205 | }, 206 | { 207 | .sa_type = ARG_UNSPEC, 208 | .sa_name = "vec", 209 | }, 210 | }, 211 | }; 212 | SYSCALL_ADD(mincore_desc); 213 | 214 | void 215 | mincore_fixup(u_long *args) 216 | { 217 | void *vec; 218 | 219 | vec = xmalloc(args[1] / getpagesize()); 220 | args[2] = (uintptr_t)vec; 221 | } 222 | 223 | void 224 | mincore_cleanup(u_long *args, u_long ret __unused) 225 | { 226 | 227 | free((void *)args[2]); 228 | } 229 | 230 | static int minherit_cmds[] = { 231 | INHERIT_SHARE, 232 | INHERIT_NONE, 233 | INHERIT_COPY, 234 | INHERIT_ZERO, 235 | }; 236 | 237 | static struct scdesc minherit_desc = { 238 | .sd_num = SYS_minherit, 239 | .sd_name = "minherit", 240 | .sd_nargs = 3, 241 | .sd_groups = SC_GROUP_VM, 242 | .sd_args = { 243 | { 244 | .sa_type = ARG_MEMADDR, 245 | .sa_name = "addr", 246 | }, 247 | { 248 | .sa_type = ARG_MEMLEN, 249 | .sa_name = "len", 250 | }, 251 | { 252 | .sa_type = ARG_CMD, 253 | .sa_name = "inherit", 254 | .sa_cmds = minherit_cmds, 255 | .sa_argcnt = nitems(minherit_cmds), 256 | }, 257 | }, 258 | }; 259 | SYSCALL_ADD(minherit_desc); 260 | 261 | static struct scdesc mlock_desc = { 262 | .sd_num = SYS_mlock, 263 | .sd_name = "mlock", 264 | .sd_nargs = 2, 265 | .sd_groups = SC_GROUP_VM, 266 | .sd_args = { 267 | { 268 | .sa_type = ARG_MEMADDR, 269 | .sa_name = "addr", 270 | }, 271 | { 272 | .sa_type = ARG_MEMLEN, 273 | .sa_name = "len", 274 | }, 275 | }, 276 | }; 277 | SYSCALL_ADD(mlock_desc); 278 | 279 | static struct scdesc mprotect_desc = { 280 | .sd_num = SYS_mprotect, 281 | .sd_name = "mprotect", 282 | .sd_nargs = 3, 283 | .sd_groups = SC_GROUP_VM, 284 | .sd_args = { 285 | { 286 | .sa_type = ARG_MEMADDR, 287 | .sa_name = "addr", 288 | }, 289 | { 290 | .sa_type = ARG_MEMLEN, 291 | .sa_name = "len", 292 | }, 293 | { 294 | .sa_type = ARG_IFLAGMASK, 295 | .sa_name = "prot", 296 | .sa_iflags = mmap_prot_flags, 297 | .sa_argcnt = nitems(mmap_prot_flags), 298 | }, 299 | }, 300 | }; 301 | SYSCALL_ADD(mprotect_desc); 302 | 303 | static int msync_cmds[] = { 304 | MS_ASYNC, 305 | MS_SYNC, 306 | MS_INVALIDATE, 307 | }; 308 | 309 | static struct scdesc msync_desc = { 310 | .sd_num = SYS_msync, 311 | .sd_name = "msync", 312 | .sd_nargs = 3, 313 | .sd_groups = SC_GROUP_VM, 314 | .sd_args = { 315 | { 316 | .sa_type = ARG_MEMADDR, 317 | .sa_name = "addr", 318 | }, 319 | { 320 | .sa_type = ARG_MEMLEN, 321 | .sa_name = "len", 322 | }, 323 | { 324 | .sa_type = ARG_CMD, 325 | .sa_name = "prot", 326 | .sa_cmds = msync_cmds, 327 | .sa_argcnt = nitems(msync_cmds), 328 | }, 329 | }, 330 | }; 331 | SYSCALL_ADD(msync_desc); 332 | 333 | static struct scdesc munlock_desc = { 334 | .sd_num = SYS_munlock, 335 | .sd_name = "munlock", 336 | .sd_nargs = 2, 337 | .sd_groups = SC_GROUP_VM, 338 | .sd_args = { 339 | { 340 | .sa_type = ARG_MEMADDR, 341 | .sa_name = "addr", 342 | }, 343 | { 344 | .sa_type = ARG_MEMLEN, 345 | .sa_name = "len", 346 | }, 347 | }, 348 | }; 349 | SYSCALL_ADD(munlock_desc); 350 | 351 | static struct scdesc munmap_desc = { 352 | .sd_num = SYS_munmap, 353 | .sd_name = "munmap", 354 | .sd_nargs = 2, 355 | .sd_groups = SC_GROUP_VM, 356 | .sd_cleanup = munmap_cleanup, 357 | .sd_args = { 358 | { 359 | .sa_type = ARG_MEMADDR, 360 | .sa_name = "addr", 361 | }, 362 | { 363 | .sa_type = ARG_MEMLEN, 364 | .sa_name = "len", 365 | }, 366 | }, 367 | }; 368 | SYSCALL_ADD(munmap_desc); 369 | 370 | void 371 | munmap_cleanup(u_long *args, u_long ret) 372 | { 373 | 374 | if (ret != 0) 375 | /* The unmap was unsuccessful. */ 376 | return; 377 | 378 | ap_memblk_unmap((void *)args[0], args[1]); 379 | } 380 | 381 | static int mlockall_flags[] = { 382 | MCL_CURRENT, 383 | MCL_FUTURE, 384 | }; 385 | 386 | static struct scdesc mlockall_desc = { 387 | .sd_num = SYS_mlockall, 388 | .sd_name = "mlockall", 389 | .sd_nargs = 1, 390 | .sd_groups = SC_GROUP_VM, 391 | .sd_args = { 392 | { 393 | .sa_type = ARG_IFLAGMASK, 394 | .sa_name = "flags", 395 | .sa_iflags = mlockall_flags, 396 | .sa_argcnt = nitems(mlockall_flags), 397 | }, 398 | }, 399 | }; 400 | SYSCALL_ADD(mlockall_desc); 401 | 402 | static struct scdesc munlockall_desc = { 403 | .sd_num = SYS_munlockall, 404 | .sd_name = "munlockall", 405 | .sd_nargs = 0, 406 | .sd_groups = SC_GROUP_VM, 407 | }; 408 | SYSCALL_ADD(munlockall_desc); 409 | --------------------------------------------------------------------------------