diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2024-05-31 11:16:40 +0200 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2024-06-03 10:06:46 +0000 |
| commit | 720b7efa99d7f3b4069af440ea7d004a6f27467d (patch) | |
| tree | a812d72870d72580cf4b902d00fc2e9cf14fd122 /executor | |
| parent | d2862abe19f233f936ad61550df88db5b41049c8 (diff) | |
executor: rework feature setup
Return failure reason from setup functions rather than crash.
This will provide better error messages, but also allow setup
w/o creating subprocesses which will be needed when we combine
fuzzer and executor.
Also close all resources created during setup.
This is also useful for in-process setup, but also should improve
chances of reproducing a bug with C reproducer. Currently leaked
file descriptors may disturb repro execution (e.g. it may act
on a wrong fd).
Diffstat (limited to 'executor')
| -rw-r--r-- | executor/common.h | 26 | ||||
| -rw-r--r-- | executor/common_bsd.h | 23 | ||||
| -rw-r--r-- | executor/common_linux.h | 159 | ||||
| -rw-r--r-- | executor/executor.cc | 14 | ||||
| -rw-r--r-- | executor/executor_linux.h | 60 |
5 files changed, 167 insertions, 115 deletions
diff --git a/executor/common.h b/executor/common.h index 9f130ca94..9ea4ec3d9 100644 --- a/executor/common.h +++ b/executor/common.h @@ -319,8 +319,9 @@ static int inject_fault(int nth) #endif #if SYZ_FAULT -static void setup_fault() +static const char* setup_fault() { + return NULL; } #endif @@ -771,26 +772,35 @@ int main(void) #if SYZ_CGROUPS setup_cgroups(); #endif + const char* reason; + (void)reason; #if SYZ_BINFMT_MISC - setup_binfmt_misc(); + if ((reason = setup_binfmt_misc())) + printf("the reproducer may not work as expected: binfmt_misc setup failed: %s\n", reason); #endif #if SYZ_LEAK - setup_leak(); + if ((reason = setup_leak())) + printf("the reproducer may not work as expected: leak checking setup failed: %s\n", reason); #endif #if SYZ_FAULT - setup_fault(); + if ((reason = setup_fault())) + printf("the reproducer may not work as expected: fault injection setup failed: %s\n", reason); #endif #if SYZ_KCSAN - setup_kcsan(); + if ((reason = setup_kcsan())) + printf("the reproducer may not work as expected: KCSAN setup failed: %s\n", reason); #endif #if SYZ_USB - setup_usb(); + if ((reason = setup_usb())) + printf("the reproducer may not work as expected: USB injection setup failed: %s\n", reason); #endif #if SYZ_802154 - setup_802154(); + if ((reason = setup_802154())) + printf("the reproducer may not work as expected: 802154 injection setup failed: %s\n", reason); #endif #if SYZ_SWAP - setup_swap(); + if ((reason = setup_swap())) + printf("the reproducer may not work as expected: swap setup failed: %s\n", reason); #endif #if SYZ_HANDLE_SEGV install_segv_handler(); diff --git a/executor/common_bsd.h b/executor/common_bsd.h index e0beac33f..683e7c140 100644 --- a/executor/common_bsd.h +++ b/executor/common_bsd.h @@ -18,11 +18,12 @@ #endif #if SYZ_EXECUTOR || SYZ_USB #include <dirent.h> -static void setup_usb(void) + +static const char* setup_usb(void) { DIR* dir = opendir("/dev"); if (dir == NULL) - fail("failed to open /dev"); + return "failed to open /dev"; bool have_vhci = false; struct dirent* ent = NULL; @@ -33,14 +34,14 @@ static void setup_usb(void) continue; char path[1024]; snprintf(path, sizeof(path), "/dev/%s", ent->d_name); - if (chmod(path, 0666)) - failmsg("failed to chmod vhci", "path=%s", path); + if (chmod(path, 0666)) { + closedir(dir); + return "failed to chmod /dev/vhci"; + } have_vhci = true; } - if (!have_vhci) - fail("don't have any /dev/vhci devices"); - closedir(dir); + return have_vhci ? NULL : "don't have any /dev/vhci devices"; } #endif @@ -48,11 +49,14 @@ static void setup_usb(void) #include <fcntl.h> #include <sys/fault.h> #include <sys/stat.h> -static void setup_fault(void) + +static const char* setup_fault(void) { if (chmod("/dev/fault", 0666)) - fail("failed to chmod /dev/fault"); + return "failed to chmod /dev/fault"; + return NULL; } + static int inject_fault(int nth) { struct fault_ioc_enable en; @@ -71,6 +75,7 @@ static int inject_fault(int nth) return fd; } #endif + #if SYZ_EXECUTOR static int fault_injected(int fd) { diff --git a/executor/common_linux.h b/executor/common_linux.h index cb554b364..6b1826c65 100644 --- a/executor/common_linux.h +++ b/executor/common_linux.h @@ -4837,16 +4837,16 @@ static void close_fds() #if SYZ_EXECUTOR || SYZ_FAULT #include <errno.h> -static void setup_fault() +static const char* setup_fault() { int fd = open("/proc/self/make-it-fail", O_WRONLY); if (fd == -1) - fail("CONFIG_FAULT_INJECTION is not enabled"); + return "CONFIG_FAULT_INJECTION is not enabled"; close(fd); fd = open("/proc/thread-self/fail-nth", O_WRONLY); if (fd == -1) - fail("kernel does not have systematic fault injection support"); + return "kernel does not have systematic fault injection support"; close(fd); static struct { @@ -4867,9 +4867,10 @@ static void setup_fault() if (!write_file(files[i].file, files[i].val)) { debug("failed to write %s: %d\n", files[i].file, errno); if (files[i].fatal) - failmsg("failed to write fault injection file", "file=%s", files[i].file); + return "failed to write fault injection file"; } } + return NULL; } #endif @@ -4882,22 +4883,23 @@ static void setup_fault() #define KMEMLEAK_FILE "/sys/kernel/debug/kmemleak" -static void setup_leak() +static const char* setup_leak() { if (!write_file(KMEMLEAK_FILE, "scan=off")) { if (errno == EBUSY) - fail("KMEMLEAK disabled: increase CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE" - " or unset CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF"); - fail("failed to write(kmemleak, \"scan=off\")"); + return "KMEMLEAK disabled: increase CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE" + " or unset CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF"; + return "failed to write(kmemleak, \"scan=off\")"; } // Flush boot leaks. if (!write_file(KMEMLEAK_FILE, "scan")) - fail("failed to write(kmemleak, \"scan\")"); + return "failed to write(kmemleak, \"scan\")"; sleep(5); // account for MSECS_MIN_AGE if (!write_file(KMEMLEAK_FILE, "scan")) - fail("failed to write(kmemleak, \"scan\")"); + return "failed to write(kmemleak, \"scan\")"; if (!write_file(KMEMLEAK_FILE, "clear")) - fail("failed to write(kmemleak, \"clear\")"); + return "failed to write(kmemleak, \"clear\")"; + return NULL; } #define SYZ_HAVE_LEAK_CHECK 1 @@ -4984,56 +4986,34 @@ static void check_leaks(void) #include <sys/stat.h> #include <sys/types.h> -static void setup_binfmt_misc() +static const char* setup_binfmt_misc() { if (mount(0, "/proc/sys/fs/binfmt_misc", "binfmt_misc", 0, 0)) { debug("mount(binfmt_misc) failed: %d\n", errno); - return; + return NULL; } if (!write_file("/proc/sys/fs/binfmt_misc/register", ":syz0:M:0:\x01::./file0:") || !write_file("/proc/sys/fs/binfmt_misc/register", ":syz1:M:1:\x02::./file0:POC")) - fail("write(/proc/sys/fs/binfmt_misc/register) failed"); + return "write(/proc/sys/fs/binfmt_misc/register) failed"; + return NULL; } #endif #if SYZ_EXECUTOR || SYZ_KCSAN -#define KCSAN_DEBUGFS_FILE "/sys/kernel/debug/kcsan" - -static void setup_kcsan() +static const char* setup_kcsan() { - if (!write_file(KCSAN_DEBUGFS_FILE, "on")) - fail("write(/sys/kernel/debug/kcsan, on) failed"); -} - -#if SYZ_EXECUTOR // currently only used by executor -static void setup_kcsan_filterlist(char** frames, int nframes, bool suppress) -{ - int fd = open(KCSAN_DEBUGFS_FILE, O_WRONLY); - if (fd == -1) - fail("failed to open kcsan debugfs file"); - - printf("%s KCSAN reports in functions: ", - suppress ? "suppressing" : "only showing"); - if (!suppress) - dprintf(fd, "whitelist\n"); - for (int i = 0; i < nframes; ++i) { - printf("'%s' ", frames[i]); - dprintf(fd, "!%s\n", frames[i]); - } - printf("\n"); - - close(fd); + if (!write_file("/sys/kernel/debug/kcsan", "on")) + return "write(/sys/kernel/debug/kcsan, on) failed"; + return NULL; } - -#define SYZ_HAVE_KCSAN 1 -#endif #endif #if SYZ_EXECUTOR || SYZ_USB -static void setup_usb() +static const char* setup_usb() { if (chmod("/dev/raw-gadget", 0666)) - fail("failed to chmod /dev/raw-gadget"); + return "failed to chmod /dev/raw-gadget"; + return NULL; } #endif @@ -5090,8 +5070,9 @@ static void setup_sysctl() {"/proc/sys/kernel/cad_pid", mypid}, }; for (size_t i = 0; i < sizeof(files) / sizeof(files[0]); i++) { - if (!write_file(files[i].name, files[i].data)) - printf("write to %s failed: %s\n", files[i].name, strerror(errno)); + if (!write_file(files[i].name, files[i].data)) { + debug("write to %s failed: %s\n", files[i].name, strerror(errno)); + } } } #endif @@ -5106,44 +5087,56 @@ static void setup_sysctl() #define NL802154_ATTR_IFINDEX 3 #define NL802154_ATTR_SHORT_ADDR 10 -static void setup_802154() +static const char* setup_802154() { + const char* error = NULL; + int sock_generic = -1; int sock_route = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); - if (sock_route == -1) - fail("socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE) failed"); - int sock_generic = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); - if (sock_generic < 0) - fail("socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC) failed"); - int nl802154_family_id = netlink_query_family_id(&nlmsg, sock_generic, "nl802154", true); - for (int i = 0; i < 2; i++) { - // wpan0/1 are created by CONFIG_IEEE802154_HWSIM. - // sys/linux/socket_ieee802154.txt knowns about these names and consts. - char devname[] = "wpan0"; - devname[strlen(devname) - 1] += i; - uint64 hwaddr = 0xaaaaaaaaaaaa0002 + (i << 8); - uint16 shortaddr = 0xaaa0 + i; - int ifindex = if_nametoindex(devname); - struct genlmsghdr genlhdr; - memset(&genlhdr, 0, sizeof(genlhdr)); - genlhdr.cmd = NL802154_CMD_SET_SHORT_ADDR; - netlink_init(&nlmsg, nl802154_family_id, 0, &genlhdr, sizeof(genlhdr)); - netlink_attr(&nlmsg, NL802154_ATTR_IFINDEX, &ifindex, sizeof(ifindex)); - netlink_attr(&nlmsg, NL802154_ATTR_SHORT_ADDR, &shortaddr, sizeof(shortaddr)); - int err = netlink_send(&nlmsg, sock_generic); - if (err < 0) - fail("NL802154_CMD_SET_SHORT_ADDR failed"); - netlink_device_change(&nlmsg, sock_route, devname, true, 0, &hwaddr, sizeof(hwaddr), 0); - if (i == 0) { - netlink_add_device_impl(&nlmsg, "lowpan", "lowpan0", false); - netlink_done(&nlmsg); - netlink_attr(&nlmsg, IFLA_LINK, &ifindex, sizeof(ifindex)); - int err = netlink_send(&nlmsg, sock_route); - if (err < 0) - fail("netlink: adding device lowpan0 type lowpan link wpan0"); + if (sock_route == -1) { + error = "socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE) failed"; + goto fail; + } + sock_generic = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); + if (sock_generic == -1) { + error = "socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC) failed"; + goto fail; + } + { + int nl802154_family_id = netlink_query_family_id(&nlmsg, sock_generic, "nl802154", true); + for (int i = 0; i < 2; i++) { + // wpan0/1 are created by CONFIG_IEEE802154_HWSIM. + // sys/linux/socket_ieee802154.txt knowns about these names and consts. + char devname[] = "wpan0"; + devname[strlen(devname) - 1] += i; + uint64 hwaddr = 0xaaaaaaaaaaaa0002 + (i << 8); + uint16 shortaddr = 0xaaa0 + i; + int ifindex = if_nametoindex(devname); + struct genlmsghdr genlhdr; + memset(&genlhdr, 0, sizeof(genlhdr)); + genlhdr.cmd = NL802154_CMD_SET_SHORT_ADDR; + netlink_init(&nlmsg, nl802154_family_id, 0, &genlhdr, sizeof(genlhdr)); + netlink_attr(&nlmsg, NL802154_ATTR_IFINDEX, &ifindex, sizeof(ifindex)); + netlink_attr(&nlmsg, NL802154_ATTR_SHORT_ADDR, &shortaddr, sizeof(shortaddr)); + if (netlink_send(&nlmsg, sock_generic) < 0) { + error = "NL802154_CMD_SET_SHORT_ADDR failed"; + goto fail; + } + netlink_device_change(&nlmsg, sock_route, devname, true, 0, &hwaddr, sizeof(hwaddr), 0); + if (i == 0) { + netlink_add_device_impl(&nlmsg, "lowpan", "lowpan0", false); + netlink_done(&nlmsg); + netlink_attr(&nlmsg, IFLA_LINK, &ifindex, sizeof(ifindex)); + if (netlink_send(&nlmsg, sock_route) < 0) { + error = "netlink: adding device lowpan0 type lowpan link wpan0"; + goto fail; + } + } } } +fail: close(sock_route); close(sock_generic); + return error; } #endif @@ -5694,7 +5687,7 @@ static long syz_pkey_set(volatile long pkey, volatile long val) #define SWAP_FILE "./swap-file" #define SWAP_FILE_SIZE (128 * 1000 * 1000) // 128 MB. -static void setup_swap() +static const char* setup_swap() { // The call must be idempotent, so first disable swap and remove the swap file. swapoff(SWAP_FILE); @@ -5702,7 +5695,7 @@ static void setup_swap() // Zero-fill the file. int fd = open(SWAP_FILE, O_CREAT | O_WRONLY | O_CLOEXEC, 0600); if (fd == -1) - failmsg("swap file open failed", "file: %s", SWAP_FILE); + return "swap file open failed"; // We cannot do ftruncate -- swapon complains about this. Do fallocate instead. fallocate(fd, FALLOC_FL_ZERO_RANGE, 0, SWAP_FILE_SIZE); close(fd); @@ -5710,11 +5703,11 @@ static void setup_swap() char cmdline[64]; sprintf(cmdline, "mkswap %s", SWAP_FILE); if (runcmdline(cmdline)) - fail("mkswap failed"); + return "mkswap failed"; if (swapon(SWAP_FILE, SWAP_FLAG_PREFER) == 1) - failmsg("swapon failed", "file: %s", SWAP_FILE); + return "swapon failed"; + return NULL; } - #endif #if SYZ_EXECUTOR || __NR_syz_pidfd_open diff --git a/executor/executor.cc b/executor/executor.cc index 71a376870..d5420e90c 100644 --- a/executor/executor.cc +++ b/executor/executor.cc @@ -370,7 +370,7 @@ typedef char kcov_comparison_size[sizeof(kcov_comparison_t) == 4 * sizeof(uint64 struct feature_t { rpc::Feature id; - void (*setup)(); + const char* (*setup)(); }; static thread_t* schedule_call(int call_index, int call_num, uint64 copyout_index, uint64 num_args, uint64* args, uint8* pos, call_props_t call_props); @@ -410,6 +410,10 @@ static void setup_features(char** enable, int n); #error "unknown OS" #endif +#if !SYZ_HAVE_FEATURES +static feature_t features[] = {}; +#endif + #include "cov_filter.h" #include "test.h" @@ -1655,10 +1659,6 @@ bool kcov_comparison_t::operator<(const struct kcov_comparison_t& other) const } #endif // if SYZ_EXECUTOR_USES_SHMEM -#if !SYZ_HAVE_FEATURES -static feature_t features[] = {}; -#endif - void setup_features(char** enable, int n) { // This does any one-time setup for the requested features on the machine. @@ -1684,7 +1684,9 @@ void setup_features(char** enable, int n) } for (size_t i = 0; i < sizeof(features) / sizeof(features[0]); i++) { if (features[i].id == feature) { - features[i].setup(); + const char* reason = features[i].setup(); + if (reason) + fail(reason); return; } } diff --git a/executor/executor_linux.h b/executor/executor_linux.h index b8c98d3f7..1bae0e460 100644 --- a/executor/executor_linux.h +++ b/executor/executor_linux.h @@ -256,7 +256,27 @@ NORETURN void doexit_thread(int status) } } -static void setup_nicvf() +#define SYZ_HAVE_KCSAN 1 +static void setup_kcsan_filterlist(char** frames, int nframes, bool suppress) +{ + int fd = open("/sys/kernel/debug/kcsan", O_WRONLY); + if (fd == -1) + fail("failed to open kcsan debugfs file"); + + printf("%s KCSAN reports in functions: ", + suppress ? "suppressing" : "only showing"); + if (!suppress) + dprintf(fd, "whitelist\n"); + for (int i = 0; i < nframes; ++i) { + printf("'%s' ", frames[i]); + dprintf(fd, "!%s\n", frames[i]); + } + printf("\n"); + + close(fd); +} + +static const char* setup_nicvf() { // This feature has custom checking precedure rather than just rely on running // a simple program with this feature enabled b/c find_vf_interface cannot be made @@ -266,29 +286,51 @@ static void setup_nicvf() // can find the same device and then moving it will fail for all but one). // So we have to make find_vf_interface non-failing in case of failures, // which means we cannot use it for feature checking. - if (open("/sys/bus/pci/devices/0000:00:11.0/", O_RDONLY | O_NONBLOCK) == -1) - fail("PCI device 0000:00:11.0 is not available"); + int fd = open("/sys/bus/pci/devices/0000:00:11.0/", O_RDONLY | O_NONBLOCK); + if (fd == -1) + return "PCI device 0000:00:11.0 is not available"; + close(fd); + return NULL; } -static void setup_devlink_pci() +static const char* setup_devlink_pci() { // See comment in setup_nicvf. - if (open("/sys/bus/pci/devices/0000:00:10.0/", O_RDONLY | O_NONBLOCK) == -1) - fail("PCI device 0000:00:10.0 is not available"); + int fd = open("/sys/bus/pci/devices/0000:00:10.0/", O_RDONLY | O_NONBLOCK); + if (fd == -1) + return "PCI device 0000:00:10.0 is not available"; + close(fd); + return NULL; } -static void setup_delay_kcov() +static const char* setup_delay_kcov() { - is_kernel_64_bit = detect_kernel_bitness(); + int fd = open("/sys/kernel/debug/kcov", O_RDWR); + if (fd == -1) + return "open of /sys/kernel/debug/kcov failed"; + close(fd); cover_t cov = {}; cov.fd = kCoverFd; cover_open(&cov, false); cover_mmap(&cov); + char* first = cov.data; cov.data = nullptr; cover_mmap(&cov); // If delayed kcov mmap is not supported by the kernel, // accesses to the second mapping will crash. - const_cast<volatile char*>(cov.data)[0] = 1; + // Use clock_gettime to check if it's mapped w/o crashing the process. + const char* error = NULL; + timespec ts; + if (clock_gettime(CLOCK_MONOTONIC, &ts)) { + if (errno != EFAULT) + fail("clock_gettime failed"); + error = "kernel commit b3d7fe86fbd0 is not present"; + } else { + munmap(cov.data - SYZ_PAGE_SIZE, cov.mmap_alloc_size + 2 * SYZ_PAGE_SIZE); + } + munmap(first - SYZ_PAGE_SIZE, cov.mmap_alloc_size + 2 * SYZ_PAGE_SIZE); + close(cov.fd); + return error; } #define SYZ_HAVE_FEATURES 1 |
