Skip to content
Permalink
Browse files

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.
  • Loading branch information...
xairy committed Feb 5, 2019
1 parent c55829a commit dfd609eca1871f01757d6b04b19fc273c87c14e5
@@ -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).
@@ -223,6 +223,7 @@ static int inject_fault(int nth)
return 0;
}
#endif

#if SYZ_EXECUTOR
static int fault_injected(int fail_fd)
{
@@ -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,14 +1703,18 @@ 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>
#include <sys/types.h>

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 @@ static void remove_dir(const char* dir)

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.
@@ -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
@@ -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,
Oops, something went wrong.

0 comments on commit dfd609e

Please sign in to comment.
You can’t perform that action at this time.