diff options
| author | Andrey Konovalov <andreyknvl@google.com> | 2019-02-05 16:19:34 +0100 |
|---|---|---|
| committer | Andrey Konovalov <andreyknvl@gmail.com> | 2019-03-05 14:30:10 +0100 |
| commit | dfd609eca1871f01757d6b04b19fc273c87c14e5 (patch) | |
| tree | bd16e4561775f52e40f970a35a032f1f390171a9 | |
| parent | c55829aef16e95df564cb23e700e5de8490229a0 (diff) | |
execprog, stress, prog2c: unify flags to enable additional features
This change makes all syz-execprog, syz-prog2c and syz-stress accept
-enable and -disable flags to enable or disable additional features
(tun, net_dev, net_reset, cgroups and binfmt_misc) instead of having
a separate flag for each of them.
The default (without any flags) behavior isn't changed: syz-execprog
and syz-stress enabled all the features (provided the runtime supports
them) and syz-prog2c disables all of them.
| -rw-r--r-- | docs/syzbot.md | 2 | ||||
| -rw-r--r-- | executor/common.h | 1 | ||||
| -rw-r--r-- | executor/common_linux.h | 218 | ||||
| -rw-r--r-- | executor/executor.cc | 18 | ||||
| -rw-r--r-- | pkg/csource/common.go | 5 | ||||
| -rw-r--r-- | pkg/csource/generated.go | 188 | ||||
| -rw-r--r-- | pkg/csource/options.go | 141 | ||||
| -rw-r--r-- | pkg/csource/options_test.go | 189 | ||||
| -rw-r--r-- | pkg/ipc/ipc.go | 9 | ||||
| -rw-r--r-- | pkg/repro/repro.go | 26 | ||||
| -rw-r--r-- | pkg/repro/repro_test.go | 24 | ||||
| -rw-r--r-- | pkg/runtest/run.go | 4 | ||||
| -rw-r--r-- | syz-fuzzer/fuzzer.go | 9 | ||||
| -rw-r--r-- | tools/syz-execprog/execprog.go | 37 | ||||
| -rw-r--r-- | tools/syz-prog2c/prog2c.go | 54 | ||||
| -rw-r--r-- | tools/syz-stress/stress.go | 28 |
16 files changed, 633 insertions, 320 deletions
diff --git a/docs/syzbot.md b/docs/syzbot.md index 94fdb62b0..db5e0110d 100644 --- a/docs/syzbot.md +++ b/docs/syzbot.md @@ -98,7 +98,7 @@ parallel). A syzkaller program can be converted to an almost equivalent C source using `syz-prog2c` utility. `syz-prog2c` has lots of flags in common with [syz-execprog](https://github.com/google/syzkaller/blob/master/docs/executing_syzkaller_programs.md), e.g. `-threaded`/`-collide` which control if the syscalls are executed sequentially or in parallel. An example invocation: ``` -syz-prog2c -prog repro.syz.txt -threaded -collide -repeat -procs=8 -sandbox=namespace -tun -tmpdir -waitrepeat +syz-prog2c -prog repro.syz.txt -enable=all -threaded -collide -repeat -procs=8 -sandbox=namespace -segv -tmpdir -waitrepeat ``` However, note that if `syzbot` did not provide a C reproducer, it wasn't able to trigger the bug using the C program (though, it can be just because the bug is triggered by a subtle race condition). diff --git a/executor/common.h b/executor/common.h index 89dfbd58c..1f8e4b441 100644 --- a/executor/common.h +++ b/executor/common.h @@ -223,6 +223,7 @@ static int inject_fault(int nth) return 0; } #endif + #if SYZ_EXECUTOR static int fault_injected(int fail_fd) { diff --git a/executor/common_linux.h b/executor/common_linux.h index 26137e5b7..0e185553c 100644 --- a/executor/common_linux.h +++ b/executor/common_linux.h @@ -70,7 +70,7 @@ static int event_timedwait(event_t* ev, uint64 timeout) } #endif -#if SYZ_EXECUTOR || SYZ_FAULT_INJECTION || SYZ_ENABLE_CGROUPS || SYZ_SANDBOX_NONE || \ +#if SYZ_EXECUTOR || SYZ_REPEAT || SYZ_TUN_ENABLE || SYZ_FAULT_INJECTION || SYZ_SANDBOX_NONE || \ SYZ_SANDBOX_SETUID || SYZ_SANDBOX_NAMESPACE || SYZ_SANDBOX_ANDROID_UNTRUSTED_APP #include <errno.h> #include <fcntl.h> @@ -1677,6 +1677,8 @@ static void reset_ebtables() static void checkpoint_net_namespace(void) { #if SYZ_EXECUTOR + if (!flag_enable_net_reset) + return; if (flag_sandbox == sandbox_setuid) return; #endif @@ -1689,6 +1691,8 @@ static void checkpoint_net_namespace(void) static void reset_net_namespace(void) { #if SYZ_EXECUTOR + if (!flag_enable_net_reset) + return; if (flag_sandbox == sandbox_setuid) return; #endif @@ -1699,7 +1703,7 @@ static void reset_net_namespace(void) } #endif -#if SYZ_EXECUTOR || SYZ_ENABLE_CGROUPS +#if SYZ_EXECUTOR || (SYZ_ENABLE_CGROUPS && (SYZ_SANDBOX_NONE || SYZ_SANDBOX_SETUID || SYZ_SANDBOX_NAMESPACE || SYZ_SANDBOX_ANDROID_UNTRUSTED_APP)) #include <fcntl.h> #include <sys/mount.h> #include <sys/stat.h> @@ -1707,6 +1711,10 @@ static void reset_net_namespace(void) static void setup_cgroups() { +#if SYZ_EXECUTOR + if (!flag_enable_cgroups) + return; +#endif if (mkdir("/syzcgroup", 0777)) { debug("mkdir(/syzcgroup) failed: %d\n", errno); } @@ -1741,10 +1749,122 @@ static void setup_cgroups() } } -// TODO(dvyukov): this should be under a separate define for separate minimization, -// but for now we bundle this with cgroups. +#if SYZ_EXECUTOR || SYZ_REPEAT +static void setup_cgroups_loop() +{ +#if SYZ_EXECUTOR + if (!flag_enable_cgroups) + return; +#endif + int pid = getpid(); + char file[128]; + char cgroupdir[64]; + snprintf(cgroupdir, sizeof(cgroupdir), "/syzcgroup/unified/syz%llu", procid); + if (mkdir(cgroupdir, 0777)) { + debug("mkdir(%s) failed: %d\n", cgroupdir, errno); + } + // Restrict number of pids per test process to prevent fork bombs. + // We have up to 16 threads + main process + loop. + // 32 pids should be enough for everyone. + snprintf(file, sizeof(file), "%s/pids.max", cgroupdir); + write_file(file, "32"); + // Restrict memory consumption. + // We have some syscalls that inherently consume lots of memory, + // e.g. mounting some filesystem images requires at least 128MB + // image in memory. We restrict RLIMIT_AS to 200MB. Here we gradually + // increase low/high/max limits to make things more interesting. + // Also this takes into account KASAN quarantine size. + // If the limit is lower than KASAN quarantine size, then it can happen + // so that we kill the process, but all of its memory is in quarantine + // and is still accounted against memcg. As the result memcg won't + // allow to allocate any memory in the parent and in the new test process. + // The current limit of 300MB supports up to 9.6GB RAM (quarantine is 1/32). + snprintf(file, sizeof(file), "%s/memory.low", cgroupdir); + write_file(file, "%d", 298 << 20); + snprintf(file, sizeof(file), "%s/memory.high", cgroupdir); + write_file(file, "%d", 299 << 20); + snprintf(file, sizeof(file), "%s/memory.max", cgroupdir); + write_file(file, "%d", 300 << 20); + // Setup some v1 groups to make things more interesting. + snprintf(file, sizeof(file), "%s/cgroup.procs", cgroupdir); + write_file(file, "%d", pid); + snprintf(cgroupdir, sizeof(cgroupdir), "/syzcgroup/cpu/syz%llu", procid); + if (mkdir(cgroupdir, 0777)) { + debug("mkdir(%s) failed: %d\n", cgroupdir, errno); + } + snprintf(file, sizeof(file), "%s/cgroup.procs", cgroupdir); + write_file(file, "%d", pid); + snprintf(cgroupdir, sizeof(cgroupdir), "/syzcgroup/net/syz%llu", procid); + if (mkdir(cgroupdir, 0777)) { + debug("mkdir(%s) failed: %d\n", cgroupdir, errno); + } + snprintf(file, sizeof(file), "%s/cgroup.procs", cgroupdir); + write_file(file, "%d", pid); +} + +static void setup_cgroups_test() +{ +#if SYZ_EXECUTOR + if (!flag_enable_cgroups) + return; +#endif + char cgroupdir[64]; + snprintf(cgroupdir, sizeof(cgroupdir), "/syzcgroup/unified/syz%llu", procid); + if (symlink(cgroupdir, "./cgroup")) { + debug("symlink(%s, ./cgroup) failed: %d\n", cgroupdir, errno); + } + snprintf(cgroupdir, sizeof(cgroupdir), "/syzcgroup/cpu/syz%llu", procid); + if (symlink(cgroupdir, "./cgroup.cpu")) { + debug("symlink(%s, ./cgroup.cpu) failed: %d\n", cgroupdir, errno); + } + snprintf(cgroupdir, sizeof(cgroupdir), "/syzcgroup/net/syz%llu", procid); + if (symlink(cgroupdir, "./cgroup.net")) { + debug("symlink(%s, ./cgroup.net) failed: %d\n", cgroupdir, errno); + } +} +#endif + +#if SYZ_EXECUTOR || SYZ_SANDBOX_NAMESPACE +void initialize_cgroups() +{ +#if SYZ_EXECUTOR + if (!flag_enable_cgroups) + return; +#endif + if (mkdir("./syz-tmp/newroot/syzcgroup", 0700)) + fail("mkdir failed"); + if (mkdir("./syz-tmp/newroot/syzcgroup/unified", 0700)) + fail("mkdir failed"); + if (mkdir("./syz-tmp/newroot/syzcgroup/cpu", 0700)) + fail("mkdir failed"); + if (mkdir("./syz-tmp/newroot/syzcgroup/net", 0700)) + fail("mkdir failed"); + unsigned bind_mount_flags = MS_BIND | MS_REC | MS_PRIVATE; + if (mount("/syzcgroup/unified", "./syz-tmp/newroot/syzcgroup/unified", NULL, bind_mount_flags, NULL)) { + debug("mount(cgroup2, MS_BIND) failed: %d\n", errno); + } + if (mount("/syzcgroup/cpu", "./syz-tmp/newroot/syzcgroup/cpu", NULL, bind_mount_flags, NULL)) { + debug("mount(cgroup/cpu, MS_BIND) failed: %d\n", errno); + } + if (mount("/syzcgroup/net", "./syz-tmp/newroot/syzcgroup/net", NULL, bind_mount_flags, NULL)) { + debug("mount(cgroup/net, MS_BIND) failed: %d\n", errno); + } +} +#endif +#endif + +#if SYZ_EXECUTOR || (SYZ_ENABLE_BINFMT_MISC && (SYZ_SANDBOX_NONE || SYZ_SANDBOX_SETUID || SYZ_SANDBOX_NAMESPACE || SYZ_SANDBOX_ANDROID_UNTRUSTED_APP)) +#include <fcntl.h> +#include <sys/mount.h> +#include <sys/stat.h> +#include <sys/types.h> + static void setup_binfmt_misc() { +#if SYZ_EXECUTOR + if (!flag_enable_binfmt_misc) + return; +#endif if (mount(0, "/proc/sys/fs/binfmt_misc", "binfmt_misc", 0, 0)) { debug("mount(binfmt_misc) failed: %d\n", errno); } @@ -1764,6 +1884,8 @@ static void setup_common() } #if SYZ_EXECUTOR || SYZ_ENABLE_CGROUPS setup_cgroups(); +#endif +#if SYZ_EXECUTOR || SYZ_ENABLE_BINFMT_MISC setup_binfmt_misc(); #endif } @@ -2017,23 +2139,7 @@ static int namespace_sandbox_proc(void* arg) if (mount("/sys", "./syz-tmp/newroot/sys", 0, bind_mount_flags, NULL)) fail("mount(sysfs) failed"); #if SYZ_EXECUTOR || SYZ_ENABLE_CGROUPS - if (mkdir("./syz-tmp/newroot/syzcgroup", 0700)) - fail("mkdir failed"); - if (mkdir("./syz-tmp/newroot/syzcgroup/unified", 0700)) - fail("mkdir failed"); - if (mkdir("./syz-tmp/newroot/syzcgroup/cpu", 0700)) - fail("mkdir failed"); - if (mkdir("./syz-tmp/newroot/syzcgroup/net", 0700)) - fail("mkdir failed"); - if (mount("/syzcgroup/unified", "./syz-tmp/newroot/syzcgroup/unified", NULL, bind_mount_flags, NULL)) { - debug("mount(cgroup2, MS_BIND) failed: %d\n", errno); - } - if (mount("/syzcgroup/cpu", "./syz-tmp/newroot/syzcgroup/cpu", NULL, bind_mount_flags, NULL)) { - debug("mount(cgroup/cpu, MS_BIND) failed: %d\n", errno); - } - if (mount("/syzcgroup/net", "./syz-tmp/newroot/syzcgroup/net", NULL, bind_mount_flags, NULL)) { - debug("mount(cgroup/net, MS_BIND) failed: %d\n", errno); - } + initialize_cgroups(); #endif if (mkdir("./syz-tmp/pivot", 0777)) fail("mkdir failed"); @@ -2354,15 +2460,18 @@ retry: static int inject_fault(int nth) { +#if SYZ_EXECUTOR + if (!flag_enable_fault_injection) + return 0; +#endif int fd; - char buf[16]; - fd = open("/proc/thread-self/fail-nth", O_RDWR); // We treat errors here as temporal/non-critical because we see // occasional ENOENT/EACCES errors returned. It seems that fuzzer // somehow gets its hands to it. if (fd == -1) exitf("failed to open /proc/thread-self/fail-nth"); + char buf[16]; sprintf(buf, "%d", nth + 1); if (write(fd, buf, strlen(buf)) != (ssize_t)strlen(buf)) exitf("failed to write /proc/thread-self/fail-nth"); @@ -2373,6 +2482,8 @@ static int inject_fault(int nth) #if SYZ_EXECUTOR static int fault_injected(int fail_fd) { + if (!flag_enable_fault_injection) + return 0; char buf[16]; int n = read(fail_fd, buf, sizeof(buf) - 1); if (n <= 0) @@ -2455,50 +2566,7 @@ static void kill_and_wait(int pid, int* status) static void setup_loop() { #if SYZ_EXECUTOR || SYZ_ENABLE_CGROUPS - int pid = getpid(); - char cgroupdir[64]; - char file[128]; - snprintf(cgroupdir, sizeof(cgroupdir), "/syzcgroup/unified/syz%llu", procid); - if (mkdir(cgroupdir, 0777)) { - debug("mkdir(%s) failed: %d\n", cgroupdir, errno); - } - // Restrict number of pids per test process to prevent fork bombs. - // We have up to 16 threads + main process + loop. - // 32 pids should be enough for everyone. - snprintf(file, sizeof(file), "%s/pids.max", cgroupdir); - write_file(file, "32"); - // Restrict memory consumption. - // We have some syscalls that inherently consume lots of memory, - // e.g. mounting some filesystem images requires at least 128MB - // image in memory. We restrict RLIMIT_AS to 200MB. Here we gradually - // increase low/high/max limits to make things more interesting. - // Also this takes into account KASAN quarantine size. - // If the limit is lower than KASAN quarantine size, then it can happen - // so that we kill the process, but all of its memory is in quarantine - // and is still accounted against memcg. As the result memcg won't - // allow to allocate any memory in the parent and in the new test process. - // The current limit of 300MB supports up to 9.6GB RAM (quarantine is 1/32). - snprintf(file, sizeof(file), "%s/memory.low", cgroupdir); - write_file(file, "%d", 298 << 20); - snprintf(file, sizeof(file), "%s/memory.high", cgroupdir); - write_file(file, "%d", 299 << 20); - snprintf(file, sizeof(file), "%s/memory.max", cgroupdir); - write_file(file, "%d", 300 << 20); - // Setup some v1 groups to make things more interesting. - snprintf(file, sizeof(file), "%s/cgroup.procs", cgroupdir); - write_file(file, "%d", pid); - snprintf(cgroupdir, sizeof(cgroupdir), "/syzcgroup/cpu/syz%llu", procid); - if (mkdir(cgroupdir, 0777)) { - debug("mkdir(%s) failed: %d\n", cgroupdir, errno); - } - snprintf(file, sizeof(file), "%s/cgroup.procs", cgroupdir); - write_file(file, "%d", pid); - snprintf(cgroupdir, sizeof(cgroupdir), "/syzcgroup/net/syz%llu", procid); - if (mkdir(cgroupdir, 0777)) { - debug("mkdir(%s) failed: %d\n", cgroupdir, errno); - } - snprintf(file, sizeof(file), "%s/cgroup.procs", cgroupdir); - write_file(file, "%d", pid); + setup_cgroups_loop(); #endif #if SYZ_EXECUTOR || SYZ_RESET_NET_NAMESPACE checkpoint_net_namespace(); @@ -2534,22 +2602,10 @@ static void setup_test() prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); setpgrp(); #if SYZ_EXECUTOR || SYZ_ENABLE_CGROUPS - char cgroupdir[64]; - snprintf(cgroupdir, sizeof(cgroupdir), "/syzcgroup/unified/syz%llu", procid); - if (symlink(cgroupdir, "./cgroup")) { - debug("symlink(%s, ./cgroup) failed: %d\n", cgroupdir, errno); - } - snprintf(cgroupdir, sizeof(cgroupdir), "/syzcgroup/cpu/syz%llu", procid); - if (symlink(cgroupdir, "./cgroup.cpu")) { - debug("symlink(%s, ./cgroup.cpu) failed: %d\n", cgroupdir, errno); - } - snprintf(cgroupdir, sizeof(cgroupdir), "/syzcgroup/net/syz%llu", procid); - if (symlink(cgroupdir, "./cgroup.net")) { - debug("symlink(%s, ./cgroup.net) failed: %d\n", cgroupdir, errno); - } + setup_cgroups_test(); +#endif // It's the leaf test process we want to be always killed first. write_file("/proc/self/oom_score_adj", "1000"); -#endif #if SYZ_EXECUTOR || SYZ_TUN_ENABLE // Read all remaining packets from tun to better // isolate consequently executing programs. diff --git a/executor/executor.cc b/executor/executor.cc index 22a728905..f1f6ba294 100644 --- a/executor/executor.cc +++ b/executor/executor.cc @@ -112,10 +112,13 @@ uint64 start_time_ms = 0; static bool flag_debug; static bool flag_cover; static sandbox_type flag_sandbox; +static bool flag_extra_cover; +static bool flag_enable_fault_injection; static bool flag_enable_tun; static bool flag_enable_net_dev; -static bool flag_enable_fault_injection; -static bool flag_extra_cover; +static bool flag_enable_net_reset; +static bool flag_enable_cgroups; +static bool flag_enable_binfmt_misc; static bool flag_collect_cover; static bool flag_dedup_cover; @@ -444,10 +447,13 @@ void parse_env_flags(uint64 flags) flag_sandbox = sandbox_namespace; else if (flags & (1 << 4)) flag_sandbox = sandbox_android_untrusted_app; - flag_enable_tun = flags & (1 << 5); - flag_enable_net_dev = flags & (1 << 6); - flag_enable_fault_injection = flags & (1 << 7); - flag_extra_cover = flags & (1 << 8); + flag_extra_cover = flags & (1 << 5); + flag_enable_fault_injection = flags & (1 << 6); + flag_enable_tun = flags & (1 << 7); + flag_enable_net_dev = flags & (1 << 8); + flag_enable_net_reset = flags & (1 << 9); + flag_enable_cgroups = flags & (1 << 10); + flag_enable_binfmt_misc = flags & (1 << 11); } #if SYZ_EXECUTOR_USES_FORK_SERVER diff --git a/pkg/csource/common.go b/pkg/csource/common.go index 9de387952..a39d714e0 100644 --- a/pkg/csource/common.go +++ b/pkg/csource/common.go @@ -82,8 +82,9 @@ func defineList(p, mmapProg *prog.Prog, opts Options) (defines []string) { "SYZ_FAULT_INJECTION": opts.Fault, "SYZ_TUN_ENABLE": opts.EnableTun, "SYZ_ENABLE_CGROUPS": opts.EnableCgroups, - "SYZ_ENABLE_NETDEV": opts.EnableNetdev, - "SYZ_RESET_NET_NAMESPACE": opts.ResetNet, + "SYZ_ENABLE_NETDEV": opts.EnableNetDev, + "SYZ_RESET_NET_NAMESPACE": opts.EnableNetReset, + "SYZ_ENABLE_BINFMT_MISC": opts.EnableBinfmtMisc, "SYZ_USE_TMP_DIR": opts.UseTmpDir, "SYZ_HANDLE_SEGV": opts.HandleSegv, "SYZ_REPRO": opts.Repro, diff --git a/pkg/csource/generated.go b/pkg/csource/generated.go index f58bf8e30..fbbd671bf 100644 --- a/pkg/csource/generated.go +++ b/pkg/csource/generated.go @@ -208,6 +208,7 @@ static int inject_fault(int nth) return 0; } #endif + #if SYZ_EXECUTOR static int fault_injected(int fail_fd) { @@ -1051,7 +1052,7 @@ static int event_timedwait(event_t* ev, uint64 timeout) } #endif -#if SYZ_EXECUTOR || SYZ_FAULT_INJECTION || SYZ_ENABLE_CGROUPS || SYZ_SANDBOX_NONE || \ +#if SYZ_EXECUTOR || SYZ_REPEAT || SYZ_TUN_ENABLE || SYZ_FAULT_INJECTION || SYZ_SANDBOX_NONE || \ SYZ_SANDBOX_SETUID || SYZ_SANDBOX_NAMESPACE || SYZ_SANDBOX_ANDROID_UNTRUSTED_APP #include <errno.h> #include <fcntl.h> @@ -3492,6 +3493,8 @@ static void reset_ebtables() static void checkpoint_net_namespace(void) { #if SYZ_EXECUTOR + if (!flag_enable_net_reset) + return; if (flag_sandbox == sandbox_setuid) return; #endif @@ -3504,6 +3507,8 @@ static void checkpoint_net_namespace(void) static void reset_net_namespace(void) { #if SYZ_EXECUTOR + if (!flag_enable_net_reset) + return; if (flag_sandbox == sandbox_setuid) return; #endif @@ -3514,7 +3519,7 @@ static void reset_net_namespace(void) } #endif -#if SYZ_EXECUTOR || SYZ_ENABLE_CGROUPS +#if SYZ_EXECUTOR || (SYZ_ENABLE_CGROUPS && (SYZ_SANDBOX_NONE || SYZ_SANDBOX_SETUID || SYZ_SANDBOX_NAMESPACE || SYZ_SANDBOX_ANDROID_UNTRUSTED_APP)) #include <fcntl.h> #include <sys/mount.h> #include <sys/stat.h> @@ -3522,6 +3527,10 @@ static void reset_net_namespace(void) static void setup_cgroups() { +#if SYZ_EXECUTOR + if (!flag_enable_cgroups) + return; +#endif if (mkdir("/syzcgroup", 0777)) { debug("mkdir(/syzcgroup) failed: %d\n", errno); } @@ -3555,8 +3564,108 @@ static void setup_cgroups() debug("chmod(/syzcgroup/net) failed: %d\n", errno); } } + +#if SYZ_EXECUTOR || SYZ_REPEAT +static void setup_cgroups_loop() +{ +#if SYZ_EXECUTOR + if (!flag_enable_cgroups) + return; +#endif + int pid = getpid(); + char file[128]; + char cgroupdir[64]; + snprintf(cgroupdir, sizeof(cgroupdir), "/syzcgroup/unified/syz%llu", procid); + if (mkdir(cgroupdir, 0777)) { + debug("mkdir(%s) failed: %d\n", cgroupdir, errno); + } + snprintf(file, sizeof(file), "%s/pids.max", cgroupdir); + write_file(file, "32"); + snprintf(file, sizeof(file), "%s/memory.low", cgroupdir); + write_file(file, "%d", 298 << 20); + snprintf(file, sizeof(file), "%s/memory.high", cgroupdir); + write_file(file, "%d", 299 << 20); + snprintf(file, sizeof(file), "%s/memory.max", cgroupdir); + write_file(file, "%d", 300 << 20); + snprintf(file, sizeof(file), "%s/cgroup.procs", cgroupdir); + write_file(file, "%d", pid); + snprintf(cgroupdir, sizeof(cgroupdir), "/syzcgroup/cpu/syz%llu", procid); + if (mkdir(cgroupdir, 0777)) { + debug("mkdir(%s) failed: %d\n", cgroupdir, errno); + } + snprintf(file, sizeof(file), "%s/cgroup.procs", cgroupdir); + write_file(file, "%d", pid); + snprintf(cgroupdir, sizeof(cgroupdir), "/syzcgroup/net/syz%llu", procid); + if (mkdir(cgroupdir, 0777)) { + debug("mkdir(%s) failed: %d\n", cgroupdir, errno); + } + snprintf(file, sizeof(file), "%s/cgroup.procs", cgroupdir); + write_file(file, "%d", pid); +} + +static void setup_cgroups_test() +{ +#if SYZ_EXECUTOR + if (!flag_enable_cgroups) + return; +#endif + char cgroupdir[64]; + snprintf(cgroupdir, sizeof(cgroupdir), "/syzcgroup/unified/syz%llu", procid); + if (symlink(cgroupdir, "./cgroup")) { + debug("symlink(%s, ./cgroup) failed: %d\n", cgroupdir, errno); + } + snprintf(cgroupdir, sizeof(cgroupdir), "/syzcgroup/cpu/syz%llu", procid); + if (symlink(cgroupdir, "./cgroup.cpu")) { + debug("symlink(%s, ./cgroup.cpu) failed: %d\n", cgroupdir, errno); + } + snprintf(cgroupdir, sizeof(cgroupdir), "/syzcgroup/net/syz%llu", procid); + if (symlink(cgroupdir, "./cgroup.net")) { + debug("symlink(%s, ./cgroup.net) failed: %d\n", cgroupdir, errno); + } +} +#endif + +#if SYZ_EXECUTOR || SYZ_SANDBOX_NAMESPACE +void initialize_cgroups() +{ +#if SYZ_EXECUTOR + if (!flag_enable_cgroups) + return; +#endif + if (mkdir("./syz-tmp/newroot/syzcgroup", 0700)) + fail("mkdir failed"); + if (mkdir("./syz-tmp/newroot/syzcgroup/unified", 0700)) + fail("mkdir failed"); + if (mkdir("./syz-tmp/newroot/syzcgroup/cpu", 0700)) + fail("mkdir failed"); + if (mkdir("./syz-tmp/newroot/syzcgroup/net", 0700)) + fail("mkdir failed"); + unsigned bind_mount_flags = MS_BIND | MS_REC | MS_PRIVATE; + if (mount("/syzcgroup/unified", "./syz-tmp/newroot/syzcgroup/unified", NULL, bind_mount_flags, NULL)) { + debug("mount(cgroup2, MS_BIND) failed: %d\n", errno); + } + if (mount("/syzcgroup/cpu", "./syz-tmp/newroot/syzcgroup/cpu", NULL, bind_mount_flags, NULL)) { + debug("mount(cgroup/cpu, MS_BIND) failed: %d\n", errno); + } + if (mount("/syzcgroup/net", "./syz-tmp/newroot/syzcgroup/net", NULL, bind_mount_flags, NULL)) { + debug("mount(cgroup/net, MS_BIND) failed: %d\n", errno); + } +} +#endif +#endif + +#if SYZ_EXECUTOR || (SYZ_ENABLE_BINFMT_MISC && (SYZ_SANDBOX_NONE || SYZ_SANDBOX_SETUID || SYZ_SANDBOX_NAMESPACE || SYZ_SANDBOX_ANDROID_UNTRUSTED_APP)) +#include <fcntl.h> +#include <sys/mount.h> +#include <sys/stat.h> +#include <sys/types.h> + static void setup_binfmt_misc() { +#if SYZ_EXECUTOR + if (!flag_enable_binfmt_misc) + return; +#endif if (mount(0, "/proc/sys/fs/binfmt_misc", "binfmt_misc", 0, 0)) { debug("mount(binfmt_misc) failed: %d\n", errno); } @@ -3576,6 +3685,8 @@ static void setup_common() } #if SYZ_EXECUTOR || SYZ_ENABLE_CGROUPS setup_cgroups(); +#endif +#if SYZ_EXECUTOR || SYZ_ENABLE_BINFMT_MISC setup_binfmt_misc(); #endif } @@ -3802,23 +3913,7 @@ static int namespace_sandbox_proc(void* arg) if (mount("/sys", "./syz-tmp/newroot/sys", 0, bind_mount_flags, NULL)) fail("mount(sysfs) failed"); #if SYZ_EXECUTOR || SYZ_ENABLE_CGROUPS - if (mkdir("./syz-tmp/newroot/syzcgroup", 0700)) - fail("mkdir failed"); - if (mkdir("./syz-tmp/newroot/syzcgroup/unified", 0700)) - fail("mkdir failed"); - if (mkdir("./syz-tmp/newroot/syzcgroup/cpu", 0700)) - fail("mkdir failed"); - if (mkdir("./syz-tmp/newroot/syzcgroup/net", 0700)) - fail("mkdir failed"); - if (mount("/syzcgroup/unified", "./syz-tmp/newroot/syzcgroup/unified", NULL, bind_mount_flags, NULL)) { - debug("mount(cgroup2, MS_BIND) failed: %d\n", errno); - } - if (mount("/syzcgroup/cpu", "./syz-tmp/newroot/syzcgroup/cpu", NULL, bind_mount_flags, NULL)) { - debug("mount(cgroup/cpu, MS_BIND) failed: %d\n", errno); - } - if (mount("/syzcgroup/net", "./syz-tmp/newroot/syzcgroup/net", NULL, bind_mount_flags, NULL)) { - debug("mount(cgroup/net, MS_BIND) failed: %d\n", errno); - } + initialize_cgroups(); #endif if (mkdir("./syz-tmp/pivot", 0777)) fail("mkdir failed"); @@ -4090,12 +4185,15 @@ retry: static int inject_fault(int nth) { +#if SYZ_EXECUTOR + if (!flag_enable_fault_injection) + return 0; +#endif int fd; - char buf[16]; - fd = open("/proc/thread-self/fail-nth", O_RDWR); if (fd == -1) exitf("failed to open /proc/thread-self/fail-nth"); + char buf[16]; sprintf(buf, "%d", nth + 1); if (write(fd, buf, strlen(buf)) != (ssize_t)strlen(buf)) exitf("failed to write /proc/thread-self/fail-nth"); @@ -4106,6 +4204,8 @@ static int inject_fault(int nth) #if SYZ_EXECUTOR static int fault_injected(int fail_fd) { + if (!flag_enable_fault_injection) + return 0; char buf[16]; int n = read(fail_fd, buf, sizeof(buf) - 1); if (n <= 0) @@ -4181,35 +4281,7 @@ static void kill_and_wait(int pid, int* status) static void setup_loop() { #if SYZ_EXECUTOR || SYZ_ENABLE_CGROUPS - int pid = getpid(); - char cgroupdir[64]; - char file[128]; - snprintf(cgroupdir, sizeof(cgroupdir), "/syzcgroup/unified/syz%llu", procid); - if (mkdir(cgroupdir, 0777)) { - debug("mkdir(%s) failed: %d\n", cgroupdir, errno); - } - snprintf(file, sizeof(file), "%s/pids.max", cgroupdir); - write_file(file, "32"); - snprintf(file, sizeof(file), "%s/memory.low", cgroupdir); - write_file(file, "%d", 298 << 20); - snprintf(file, sizeof(file), "%s/memory.high", cgroupdir); - write_file(file, "%d", 299 << 20); - snprintf(file, sizeof(file), "%s/memory.max", cgroupdir); - write_file(file, "%d", 300 << 20); - snprintf(file, sizeof(file), "%s/cgroup.procs", cgroupdir); - write_file(file, "%d", pid); - snprintf(cgroupdir, sizeof(cgroupdir), "/syzcgroup/cpu/syz%llu", procid); - if (mkdir(cgroupdir, 0777)) { - debug("mkdir(%s) failed: %d\n", cgroupdir, errno); - } - snprintf(file, sizeof(file), "%s/cgroup.procs", cgroupdir); - write_file(file, "%d", pid); - snprintf(cgroupdir, sizeof(cgroupdir), "/syzcgroup/net/syz%llu", procid); - if (mkdir(cgroupdir, 0777)) { - debug("mkdir(%s) failed: %d\n", cgroupdir, errno); - } - snprintf(file, sizeof(file), "%s/cgroup.procs", cgroupdir); - write_file(file, "%d", pid); + setup_cgroups_loop(); #endif #if SYZ_EXECUTOR || SYZ_RESET_NET_NAMESPACE checkpoint_net_namespace(); @@ -4245,21 +4317,9 @@ static void setup_test() prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); setpgrp(); #if SYZ_EXECUTOR || SYZ_ENABLE_CGROUPS - char cgroupdir[64]; - snprintf(cgroupdir, sizeof(cgroupdir), "/syzcgroup/unified/syz%llu", procid); - if (symlink(cgroupdir, "./cgroup")) { - debug("symlink(%s, ./cgroup) failed: %d\n", cgroupdir, errno); - } - snprintf(cgroupdir, sizeof(cgroupdir), "/syzcgroup/cpu/syz%llu", procid); - if (symlink(cgroupdir, "./cgroup.cpu")) { - debug("symlink(%s, ./cgroup.cpu) failed: %d\n", cgroupdir, errno); - } - snprintf(cgroupdir, sizeof(cgroupdir), "/syzcgroup/net/syz%llu", procid); - if (symlink(cgroupdir, "./cgroup.net")) { - debug("symlink(%s, ./cgroup.net) failed: %d\n", cgroupdir, errno); - } - write_file("/proc/self/oom_score_adj", "1000"); + setup_cgroups_test(); #endif + write_file("/proc/self/oom_score_adj", "1000"); #if SYZ_EXECUTOR || SYZ_TUN_ENABLE flush_tun(); #endif diff --git a/pkg/csource/options.go b/pkg/csource/options.go index 4a0cc9fbc..d82ff28bd 100644 --- a/pkg/csource/options.go +++ b/pkg/csource/options.go @@ -8,6 +8,8 @@ import ( "encoding/json" "errors" "fmt" + "sort" + "strings" "github.com/google/syzkaller/pkg/mgrconfig" ) @@ -27,12 +29,14 @@ type Options struct { FaultNth int `json:"fault_nth,omitempty"` // These options allow for a more fine-tuned control over the generated C code. - EnableTun bool `json:"tun,omitempty"` - UseTmpDir bool `json:"tmpdir,omitempty"` - EnableCgroups bool `json:"cgroups,omitempty"` - EnableNetdev bool `json:"netdev,omitempty"` - ResetNet bool `json:"resetnet,omitempty"` - HandleSegv bool `json:"segv,omitempty"` + EnableTun bool `json:"tun,omitempty"` + EnableNetDev bool `json:"netdev,omitempty"` + EnableNetReset bool `json:"resetnet,omitempty"` + EnableCgroups bool `json:"cgroups,omitempty"` + EnableBinfmtMisc bool `json:"binfmt_misc,omitempty"` + + UseTmpDir bool `json:"tmpdir,omitempty"` + HandleSegv bool `json:"segv,omitempty"` // Generate code for use with repro package to prints log messages, // which allows to detect hangs. @@ -58,8 +62,8 @@ func (opts Options) Check(OS string) error { // This does not affect generated code. return errors.New("Procs>1 without Repeat") } - if opts.ResetNet { - return errors.New("ResetNet without Repeat") + if opts.EnableNetReset { + return errors.New("EnableNetReset without Repeat") } if opts.RepeatTimes > 1 { return errors.New("RepeatTimes without Repeat") @@ -69,11 +73,14 @@ func (opts Options) Check(OS string) error { if opts.EnableTun { return errors.New("EnableTun without sandbox") } + if opts.EnableNetDev { + return errors.New("EnableNetDev without sandbox") + } if opts.EnableCgroups { return errors.New("EnableCgroups without sandbox") } - if opts.EnableNetdev { - return errors.New("EnableNetdev without sandbox") + if opts.EnableBinfmtMisc { + return errors.New("EnableBinfmtMisc without sandbox") } } if opts.Sandbox == sandboxNamespace && !opts.UseTmpDir { @@ -82,12 +89,12 @@ func (opts Options) Check(OS string) error { // which will fail if procs>1 and on second run of the program. return errors.New("Sandbox=namespace without UseTmpDir") } + if opts.EnableNetReset && (opts.Sandbox == "" || opts.Sandbox == sandboxSetuid) { + return errors.New("EnableNetReset without sandbox") + } if opts.EnableCgroups && !opts.UseTmpDir { return errors.New("EnableCgroups without UseTmpDir") } - if opts.ResetNet && (opts.Sandbox == "" || opts.Sandbox == sandboxSetuid) { - return errors.New("ResetNet without sandbox") - } return opts.checkLinuxOnly(OS) } @@ -98,14 +105,17 @@ func (opts Options) checkLinuxOnly(OS string) error { if opts.EnableTun && !(OS == "openbsd" || OS == "freebsd") { return fmt.Errorf("EnableTun is not supported on %v", OS) } + if opts.EnableNetDev { + return fmt.Errorf("EnableNetDev is not supported on %v", OS) + } + if opts.EnableNetReset { + return fmt.Errorf("EnableNetReset is not supported on %v", OS) + } if opts.EnableCgroups { return fmt.Errorf("EnableCgroups is not supported on %v", OS) } - if opts.EnableNetdev { - return fmt.Errorf("EnableNetdev is not supported on %v", OS) - } - if opts.ResetNet { - return fmt.Errorf("ResetNet is not supported on %v", OS) + if opts.EnableBinfmtMisc { + return fmt.Errorf("EnableBinfmtMisc is not supported on %v", OS) } if opts.Sandbox == sandboxNamespace || (opts.Sandbox == sandboxSetuid && !(OS == "openbsd" || OS == "freebsd")) || @@ -120,27 +130,29 @@ func (opts Options) checkLinuxOnly(OS string) error { func DefaultOpts(cfg *mgrconfig.Config) Options { opts := Options{ - Threaded: true, - Collide: true, - Repeat: true, - Procs: cfg.Procs, - Sandbox: cfg.Sandbox, - EnableTun: true, - EnableCgroups: true, - EnableNetdev: true, - ResetNet: true, - UseTmpDir: true, - HandleSegv: true, - Repro: true, + Threaded: true, + Collide: true, + Repeat: true, + Procs: cfg.Procs, + Sandbox: cfg.Sandbox, + EnableTun: true, + EnableNetDev: true, + EnableNetReset: true, + EnableCgroups: true, + EnableBinfmtMisc: true, + UseTmpDir: true, + HandleSegv: true, + Repro: true, } if cfg.TargetOS != linux { opts.EnableTun = false + opts.EnableNetDev = false + opts.EnableNetReset = false opts.EnableCgroups = false - opts.EnableNetdev = false - opts.ResetNet = false + opts.EnableBinfmtMisc = false } if cfg.Sandbox == "" || cfg.Sandbox == "setuid" { - opts.ResetNet = false + opts.EnableNetReset = false } if err := opts.Check(cfg.TargetOS); err != nil { panic(fmt.Sprintf("DefaultOpts created bad opts: %v", err)) @@ -198,3 +210,66 @@ func DeserializeOptions(data []byte) (Options, error) { } return opts, err } + +type Feature struct { + Description string + Enabled bool +} + +type Features map[string]Feature + +func defaultFeatures(value bool) Features { + return map[string]Feature{ + "tun": {"setup and use /dev/tun for packet injection", value}, + "net_dev": {"setup more network devices for testing", value}, + "net_reset": {"reset network namespace between programs", value}, + "cgroups": {"setup cgroups for testing", value}, + "binfmt_misc": {"setup binfmt_misc for testing", value}, + } +} + +func ParseFeaturesFlags(enable string, disable string, defaultValue bool) (Features, error) { + if enable == "none" && disable == "none" { + return defaultFeatures(defaultValue), nil + } + if enable != "none" && disable != "none" { + return nil, fmt.Errorf("can't use -enable and -disable flags at the same time") + } + if enable == "all" || disable == "" { + return defaultFeatures(true), nil + } + if disable == "all" || enable == "" { + return defaultFeatures(false), nil + } + var items []string + var features Features + if enable != "none" { + items = strings.Split(enable, ",") + features = defaultFeatures(false) + } else { + items = strings.Split(disable, ",") + features = defaultFeatures(true) + } + for _, item := range items { + if _, ok := features[item]; !ok { + return nil, fmt.Errorf("unknown feature specified: %s", item) + } + feature := features[item] + feature.Enabled = (enable != "none") + features[item] = feature + } + return features, nil +} + +func PrintAvailableFeaturesFlags() { + fmt.Printf("Available features for -enable and -disable:\n") + features := defaultFeatures(false) + var names []string + for name := range features { + names = append(names, name) + } + sort.Strings(names) + for _, name := range names { + fmt.Printf(" %s - %s\n", name, features[name].Description) + } +} diff --git a/pkg/csource/options_test.go b/pkg/csource/options_test.go index d8e8a217e..0240208e0 100644 --- a/pkg/csource/options_test.go +++ b/pkg/csource/options_test.go @@ -31,86 +31,91 @@ func TestParseOptionsCanned(t *testing.T) { "fault":true,"fault_call":1,"fault_nth":2,"tun":true,"tmpdir":true,"cgroups":true, "netdev":true,"resetnet":true, "segv":true,"waitrepeat":true,"debug":true,"repro":true}`: { - Threaded: true, - Collide: true, - Repeat: true, - Procs: 10, - Sandbox: "namespace", - Fault: true, - FaultCall: 1, - FaultNth: 2, - EnableTun: true, - UseTmpDir: true, - EnableCgroups: true, - EnableNetdev: true, - ResetNet: true, - HandleSegv: true, - Repro: true, + Threaded: true, + Collide: true, + Repeat: true, + Procs: 10, + Sandbox: "namespace", + Fault: true, + FaultCall: 1, + FaultNth: 2, + EnableTun: true, + EnableNetDev: true, + EnableNetReset: true, + EnableCgroups: true, + EnableBinfmtMisc: false, + UseTmpDir: true, + HandleSegv: true, + Repro: true, }, `{"threaded":true,"collide":true,"repeat":true,"procs":10,"sandbox":"android_untrusted_app", "fault":true,"fault_call":1,"fault_nth":2,"tun":true,"tmpdir":true,"cgroups":true, "netdev":true,"resetnet":true, "segv":true,"waitrepeat":true,"debug":true,"repro":true}`: { - Threaded: true, - Collide: true, - Repeat: true, - Procs: 10, - Sandbox: "android_untrusted_app", - Fault: true, - FaultCall: 1, - FaultNth: 2, - EnableTun: true, - UseTmpDir: true, - EnableCgroups: true, - EnableNetdev: true, - ResetNet: true, - HandleSegv: true, - Repro: true, + Threaded: true, + Collide: true, + Repeat: true, + Procs: 10, + Sandbox: "android_untrusted_app", + Fault: true, + FaultCall: 1, + FaultNth: 2, + EnableTun: true, + EnableNetDev: true, + EnableNetReset: true, + EnableCgroups: true, + EnableBinfmtMisc: false, + UseTmpDir: true, + HandleSegv: true, + Repro: true, }, "{Threaded:true Collide:true Repeat:true Procs:1 Sandbox:none Fault:false FaultCall:-1 FaultNth:0 EnableTun:true UseTmpDir:true HandleSegv:true WaitRepeat:true Debug:false Repro:false}": { - Threaded: true, - Collide: true, - Repeat: true, - Procs: 1, - Sandbox: "none", - Fault: false, - FaultCall: -1, - FaultNth: 0, - EnableTun: true, - UseTmpDir: true, - EnableCgroups: false, - HandleSegv: true, - Repro: false, + Threaded: true, + Collide: true, + Repeat: true, + Procs: 1, + Sandbox: "none", + Fault: false, + FaultCall: -1, + FaultNth: 0, + EnableTun: true, + EnableCgroups: false, + EnableBinfmtMisc: false, + UseTmpDir: true, + HandleSegv: true, + Repro: false, }, "{Threaded:true Collide:true Repeat:true Procs:1 Sandbox: Fault:false FaultCall:-1 FaultNth:0 EnableTun:true UseTmpDir:true HandleSegv:true WaitRepeat:true Debug:false Repro:false}": { - Threaded: true, - Collide: true, - Repeat: true, - Procs: 1, - Sandbox: "", - Fault: false, - FaultCall: -1, - FaultNth: 0, - EnableTun: true, - UseTmpDir: true, - EnableCgroups: false, - HandleSegv: true, - Repro: false, + Threaded: true, + Collide: true, + Repeat: true, + Procs: 1, + Sandbox: "", + Fault: false, + FaultCall: -1, + FaultNth: 0, + EnableTun: true, + EnableCgroups: false, + EnableBinfmtMisc: false, + UseTmpDir: true, + HandleSegv: true, + Repro: false, }, "{Threaded:false Collide:true Repeat:true Procs:1 Sandbox:namespace Fault:false FaultCall:-1 FaultNth:0 EnableTun:true UseTmpDir:true EnableCgroups:true HandleSegv:true WaitRepeat:true Debug:false Repro:false}": { - Threaded: false, - Collide: true, - Repeat: true, - Procs: 1, - Sandbox: "namespace", - Fault: false, - FaultCall: -1, - FaultNth: 0, - EnableTun: true, - UseTmpDir: true, - EnableCgroups: true, - HandleSegv: true, - Repro: false, + Threaded: false, + Collide: true, + Repeat: true, + Procs: 1, + Sandbox: "namespace", + Fault: false, + FaultCall: -1, + FaultNth: 0, + EnableTun: true, + EnableCgroups: true, + EnableBinfmtMisc: false, + UseTmpDir: true, + HandleSegv: true, + Repro: false, }, } for data, want := range canned { @@ -194,3 +199,49 @@ func enumerateField(OS string, opt Options, field int) []Options { } return checked } + +func TestParseFeaturesFlags(t *testing.T) { + tests := []struct { + Enable string + Disable string + Default bool + Features map[string]bool + }{ + {"none", "none", true, map[string]bool{ + "tun": true, "net_dev": true, "net_reset": true, "cgroups": true, "binfmt_misc": true, + }}, + {"none", "none", false, map[string]bool{ + "tun": false, "net_dev": false, "net_reset": false, "cgroups": false, "binfmt_misc": false, + }}, + {"all", "none", true, map[string]bool{ + "tun": true, "net_dev": true, "net_reset": true, "cgroups": true, "binfmt_misc": true, + }}, + {"", "none", true, map[string]bool{ + "tun": false, "net_dev": false, "net_reset": false, "cgroups": false, "binfmt_misc": false, + }}, + {"none", "all", true, map[string]bool{ + "tun": false, "net_dev": false, "net_reset": false, "cgroups": false, "binfmt_misc": false, + }}, + {"none", "", true, map[string]bool{ + "tun": true, "net_dev": true, "net_reset": true, "cgroups": true, "binfmt_misc": true, + }}, + {"tun,net_dev", "none", true, map[string]bool{ + "tun": true, "net_dev": true, "net_reset": false, "cgroups": false, "binfmt_misc": false, + }}, + {"none", "cgroups,net_dev", true, map[string]bool{ + "tun": true, "net_dev": false, "net_reset": true, "cgroups": false, "binfmt_misc": true, + }}, + } + for i, test := range tests { + features, err := ParseFeaturesFlags(test.Enable, test.Disable, test.Default) + if err != nil { + t.Fatalf("failed to parse features flags: %v", err) + } + for name, feature := range features { + if feature.Enabled != test.Features[name] { + t.Fatalf("test #%v: invalid value for feature flag %s: got %v, want %v", + i, name, feature.Enabled, test.Features[name]) + } + } + } +} diff --git a/pkg/ipc/ipc.go b/pkg/ipc/ipc.go index b2d4c9d63..e1e22445e 100644 --- a/pkg/ipc/ipc.go +++ b/pkg/ipc/ipc.go @@ -31,10 +31,13 @@ const ( FlagSandboxSetuid // impersonate nobody user FlagSandboxNamespace // use namespaces for sandboxing FlagSandboxAndroidUntrustedApp // use Android sandboxing for the untrusted_app domain - FlagEnableTun // initialize and use tun in executor - FlagEnableNetDev // setup a bunch of various network devices for testing - FlagEnableFault // enable fault injection support FlagExtraCover // collect extra coverage + FlagEnableFault // enable fault injection support + FlagEnableTun // setup and use /dev/tun for packet injection + FlagEnableNetDev // setup more network devices for testing + FlagEnableNetReset // reset network namespace between programs + FlagEnableCgroups // setup cgroups for testing + FlagEnableBinfmtMisc // setup binfmt_misc for testing // Executor does not know about these: FlagUseShmem // use shared memory instead of pipes for communication FlagUseForkServer // use extended protocol with handshake diff --git a/pkg/repro/repro.go b/pkg/repro/repro.go index ab91e545a..2c6a0e6ed 100644 --- a/pkg/repro/repro.go +++ b/pkg/repro/repro.go @@ -777,7 +777,7 @@ var progSimplifies = []Simplify{ } opts.Repeat = false opts.EnableCgroups = false - opts.ResetNet = false + opts.EnableNetReset = false opts.Procs = 1 return true }, @@ -804,9 +804,10 @@ var cSimplifies = append(progSimplifies, []Simplify{ } opts.Sandbox = "" opts.EnableTun = false + opts.EnableNetDev = false + opts.EnableNetReset = false opts.EnableCgroups = false - opts.EnableNetdev = false - opts.ResetNet = false + opts.EnableBinfmtMisc = false return true }, func(opts *csource.Options) bool { @@ -817,24 +818,31 @@ var cSimplifies = append(progSimplifies, []Simplify{ return true }, func(opts *csource.Options) bool { - if !opts.EnableCgroups { + if !opts.EnableNetDev { return false } - opts.EnableCgroups = false + opts.EnableNetDev = false return true }, func(opts *csource.Options) bool { - if !opts.EnableNetdev { + if !opts.EnableNetReset { return false } - opts.EnableNetdev = false + opts.EnableNetReset = false + return true + }, + func(opts *csource.Options) bool { + if !opts.EnableCgroups { + return false + } + opts.EnableCgroups = false return true }, func(opts *csource.Options) bool { - if !opts.ResetNet { + if !opts.EnableBinfmtMisc { return false } - opts.ResetNet = false + opts.EnableBinfmtMisc = false return true }, func(opts *csource.Options) bool { diff --git a/pkg/repro/repro_test.go b/pkg/repro/repro_test.go index 349b412fc..c417b7416 100644 --- a/pkg/repro/repro_test.go +++ b/pkg/repro/repro_test.go @@ -73,18 +73,18 @@ func TestBisect(t *testing.T) { func TestSimplifies(t *testing.T) { opts := csource.Options{ - Threaded: true, - Collide: true, - Repeat: true, - Procs: 10, - Sandbox: "namespace", - EnableTun: true, - EnableCgroups: true, - EnableNetdev: true, - ResetNet: true, - UseTmpDir: true, - HandleSegv: true, - Repro: true, + Threaded: true, + Collide: true, + Repeat: true, + Procs: 10, + Sandbox: "namespace", + EnableTun: true, + EnableNetDev: true, + EnableNetReset: true, + EnableCgroups: true, + UseTmpDir: true, + HandleSegv: true, + Repro: true, } var check func(opts csource.Options, i int) check = func(opts csource.Options, i int) { diff --git a/pkg/runtest/run.go b/pkg/runtest/run.go index 0af03158e..13195dc36 100644 --- a/pkg/runtest/run.go +++ b/pkg/runtest/run.go @@ -349,6 +349,8 @@ func (ctx *Context) createSyzTest(p *prog.Prog, sandbox string, threaded, cov bo if ctx.Features[host.FeatureNetworkDevices].Enabled { cfg.Flags |= ipc.FlagEnableNetDev } + cfg.Flags |= ipc.FlagEnableNetReset + cfg.Flags |= ipc.FlagEnableCgroups req := &RunRequest{ P: p, Cfg: cfg, @@ -376,7 +378,7 @@ func (ctx *Context) createCTest(p *prog.Prog, sandbox string, threaded bool, tim opts.EnableTun = true } if ctx.Features[host.FeatureNetworkDevices].Enabled { - opts.EnableNetdev = true + opts.EnableNetDev = true } } src, err := csource.Write(p, opts) diff --git a/syz-fuzzer/fuzzer.go b/syz-fuzzer/fuzzer.go index d41dfe661..52fa30def 100644 --- a/syz-fuzzer/fuzzer.go +++ b/syz-fuzzer/fuzzer.go @@ -190,15 +190,18 @@ func main() { if r.CheckResult.Features[host.FeatureExtraCoverage].Enabled { config.Flags |= ipc.FlagExtraCover } + if r.CheckResult.Features[host.FeatureFaultInjection].Enabled { + config.Flags |= ipc.FlagEnableFault + } if r.CheckResult.Features[host.FeatureNetworkInjection].Enabled { config.Flags |= ipc.FlagEnableTun } if r.CheckResult.Features[host.FeatureNetworkDevices].Enabled { config.Flags |= ipc.FlagEnableNetDev } - if r.CheckResult.Features[host.FeatureFaultInjection].Enabled { - config.Flags |= ipc.FlagEnableFault - } + config.Flags |= ipc.FlagEnableNetReset + config.Flags |= ipc.FlagEnableCgroups + config.Flags |= ipc.FlagEnableBinfmtMisc if *flagRunTest { runTest(target, manager, *flagName, config.Executor) diff --git a/tools/syz-execprog/execprog.go b/tools/syz-execprog/execprog.go index f65ad9d9b..bfc50beaf 100644 --- a/tools/syz-execprog/execprog.go +++ b/tools/syz-execprog/execprog.go @@ -16,6 +16,7 @@ import ( "time" "github.com/google/syzkaller/pkg/cover" + "github.com/google/syzkaller/pkg/csource" "github.com/google/syzkaller/pkg/host" "github.com/google/syzkaller/pkg/ipc" "github.com/google/syzkaller/pkg/ipc/ipcconfig" @@ -32,29 +33,37 @@ var ( flagRepeat = flag.Int("repeat", 1, "repeat execution that many times (0 for infinite loop)") flagProcs = flag.Int("procs", 1, "number of parallel processes to execute programs") flagOutput = flag.Bool("output", false, "write programs and results to stdout") + flagHints = flag.Bool("hints", false, "do a hints-generation run") flagFaultCall = flag.Int("fault_call", -1, "inject fault into this call (0-based)") flagFaultNth = flag.Int("fault_nth", 0, "inject fault on n-th operation (0-based)") - flagHints = flag.Bool("hints", false, "do a hints-generation run") + flagEnable = flag.String("enable", "none", "enable only listed additional features") + flagDisable = flag.String("disable", "none", "enable all additional features except listed") ) func main() { - flag.Parse() - if len(flag.Args()) == 0 { + flag.Usage = func() { fmt.Fprintf(os.Stderr, "usage: execprog [flags] file-with-programs+\n") flag.PrintDefaults() + csource.PrintAvailableFeaturesFlags() + } + flag.Parse() + if len(flag.Args()) == 0 { + flag.Usage() os.Exit(1) } + featuresFlags, err := csource.ParseFeaturesFlags(*flagEnable, *flagDisable, true) + if err != nil { + log.Fatalf("%v", err) + } target, err := prog.GetTarget(*flagOS, *flagArch) if err != nil { log.Fatalf("%v", err) } - entries := loadPrograms(target, flag.Args()) if len(entries) == 0 { return } - features, err := host.Check(target) if err != nil { log.Fatalf("%v", err) @@ -67,7 +76,7 @@ func main() { if _, err = host.Setup(target, features); err != nil { log.Fatalf("%v", err) } - config, execOpts := createConfig(target, entries, features) + config, execOpts := createConfig(target, entries, features, featuresFlags) ctx := &Context{ entries: entries, @@ -264,7 +273,8 @@ func loadPrograms(target *prog.Target, files []string) []*prog.LogEntry { return entries } -func createConfig(target *prog.Target, entries []*prog.LogEntry, features *host.Features) ( +func createConfig(target *prog.Target, entries []*prog.LogEntry, + features *host.Features, featuresFlags csource.Features) ( *ipc.Config, *ipc.ExecOpts) { config, execOpts, err := ipcconfig.Default(target) if err != nil { @@ -299,11 +309,20 @@ func createConfig(target *prog.Target, entries []*prog.LogEntry, features *host. handled[call.Meta.CallName] = true } } - if features[host.FeatureNetworkInjection].Enabled { + if featuresFlags["tun"].Enabled && features[host.FeatureNetworkInjection].Enabled { config.Flags |= ipc.FlagEnableTun } - if features[host.FeatureNetworkDevices].Enabled { + if featuresFlags["net_dev"].Enabled && features[host.FeatureNetworkDevices].Enabled { config.Flags |= ipc.FlagEnableNetDev } + if featuresFlags["net_reset"].Enabled { + config.Flags |= ipc.FlagEnableNetReset + } + if featuresFlags["cgroups"].Enabled { + config.Flags |= ipc.FlagEnableCgroups + } + if featuresFlags["binfmt_misc"].Enabled { + config.Flags |= ipc.FlagEnableBinfmtMisc + } return config, execOpts } diff --git a/tools/syz-prog2c/prog2c.go b/tools/syz-prog2c/prog2c.go index d45c1e5a8..d076ad2ee 100644 --- a/tools/syz-prog2c/prog2c.go +++ b/tools/syz-prog2c/prog2c.go @@ -7,6 +7,7 @@ import ( "flag" "fmt" "io/ioutil" + "log" "os" "runtime" @@ -27,22 +28,28 @@ var ( flagProg = flag.String("prog", "", "file with program to convert (required)") flagFaultCall = flag.Int("fault_call", -1, "inject fault into this call (0-based)") flagFaultNth = flag.Int("fault_nth", 0, "inject fault on n-th operation (0-based)") - flagEnableTun = flag.Bool("tun", false, "set up TUN/TAP interface") - flagUseTmpDir = flag.Bool("tmpdir", false, "create a temporary dir and execute inside it") - flagCgroups = flag.Bool("cgroups", false, "enable cgroups support") - flagNetdev = flag.Bool("netdev", false, "setup various net devices") - flagResetNet = flag.Bool("resetnet", false, "reset net namespace after each test") flagHandleSegv = flag.Bool("segv", false, "catch and ignore SIGSEGV") + flagUseTmpDir = flag.Bool("tmpdir", false, "create a temporary dir and execute inside it") flagTrace = flag.Bool("trace", false, "trace syscall results") flagStrict = flag.Bool("strict", false, "parse input program in strict mode") + flagEnable = flag.String("enable", "none", "enable only listed additional features") + flagDisable = flag.String("disable", "none", "enable all additional features except listed") ) func main() { + flag.Usage = func() { + flag.PrintDefaults() + csource.PrintAvailableFeaturesFlags() + } flag.Parse() if *flagProg == "" { - flag.PrintDefaults() + flag.Usage() os.Exit(1) } + features, err := csource.ParseFeaturesFlags(*flagEnable, *flagDisable, false) + if err != nil { + log.Fatalf("%v", err) + } target, err := prog.GetTarget(*flagOS, *flagArch) if err != nil { fmt.Fprintf(os.Stderr, "%v", err) @@ -63,23 +70,24 @@ func main() { os.Exit(1) } opts := csource.Options{ - Threaded: *flagThreaded, - Collide: *flagCollide, - Repeat: *flagRepeat != 1, - RepeatTimes: *flagRepeat, - Procs: *flagProcs, - Sandbox: *flagSandbox, - Fault: *flagFaultCall >= 0, - FaultCall: *flagFaultCall, - FaultNth: *flagFaultNth, - EnableTun: *flagEnableTun, - UseTmpDir: *flagUseTmpDir, - EnableCgroups: *flagCgroups, - EnableNetdev: *flagNetdev, - ResetNet: *flagResetNet, - HandleSegv: *flagHandleSegv, - Repro: false, - Trace: *flagTrace, + Threaded: *flagThreaded, + Collide: *flagCollide, + Repeat: *flagRepeat != 1, + RepeatTimes: *flagRepeat, + Procs: *flagProcs, + Sandbox: *flagSandbox, + Fault: *flagFaultCall >= 0, + FaultCall: *flagFaultCall, + FaultNth: *flagFaultNth, + EnableTun: features["tun"].Enabled, + EnableNetDev: features["net_dev"].Enabled, + EnableNetReset: features["net_reset"].Enabled, + EnableCgroups: features["cgroups"].Enabled, + EnableBinfmtMisc: features["binfmt_misc"].Enabled, + UseTmpDir: *flagUseTmpDir, + HandleSegv: *flagHandleSegv, + Repro: false, + Trace: *flagTrace, } src, err := csource.Write(p, opts) if err != nil { diff --git a/tools/syz-stress/stress.go b/tools/syz-stress/stress.go index c56936377..e287b4468 100644 --- a/tools/syz-stress/stress.go +++ b/tools/syz-stress/stress.go @@ -14,6 +14,7 @@ import ( "sync/atomic" "time" + "github.com/google/syzkaller/pkg/csource" "github.com/google/syzkaller/pkg/db" "github.com/google/syzkaller/pkg/host" "github.com/google/syzkaller/pkg/ipc" @@ -32,7 +33,9 @@ var ( flagProcs = flag.Int("procs", 2*runtime.NumCPU(), "number of parallel processes") flagLogProg = flag.Bool("logprog", false, "print programs before execution") flagGenerate = flag.Bool("generate", true, "generate new programs, otherwise only mutate corpus") - flagEnable = flag.String("enable", "", "comma-separated list of enabled syscalls") + flagSyscalls = flag.String("syscalls", "", "comma-separated list of enabled syscalls") + flagEnable = flag.String("enable", "none", "enable only listed additional features") + flagDisable = flag.String("disable", "none", "enable all additional features except listed") statExec uint64 gate *ipc.Gate @@ -41,7 +44,15 @@ var ( const programLength = 30 func main() { + flag.Usage = func() { + flag.PrintDefaults() + csource.PrintAvailableFeaturesFlags() + } flag.Parse() + featuresFlags, err := csource.ParseFeaturesFlags(*flagEnable, *flagDisable, true) + if err != nil { + log.Fatalf("%v", err) + } target, err := prog.GetTarget(*flagOS, *flagArch) if err != nil { log.Fatalf("%v", err) @@ -60,7 +71,7 @@ func main() { log.Fatalf("%v", err) } - calls := buildCallList(target, strings.Split(*flagEnable, ",")) + calls := buildCallList(target, strings.Split(*flagSyscalls, ",")) prios := target.CalculatePriorities(corpus) ct := target.BuildChoiceTable(prios, calls) @@ -68,12 +79,21 @@ func main() { if err != nil { log.Fatalf("%v", err) } - if features[host.FeatureNetworkInjection].Enabled { + if featuresFlags["tun"].Enabled && features[host.FeatureNetworkInjection].Enabled { config.Flags |= ipc.FlagEnableTun } - if features[host.FeatureNetworkDevices].Enabled { + if featuresFlags["net_dev"].Enabled && features[host.FeatureNetworkDevices].Enabled { config.Flags |= ipc.FlagEnableNetDev } + if featuresFlags["net_reset"].Enabled { + config.Flags |= ipc.FlagEnableNetReset + } + if featuresFlags["cgroups"].Enabled { + config.Flags |= ipc.FlagEnableCgroups + } + if featuresFlags["binfmt_misc"].Enabled { + config.Flags |= ipc.FlagEnableBinfmtMisc + } gate = ipc.NewGate(2**flagProcs, nil) for pid := 0; pid < *flagProcs; pid++ { pid := pid |
