aboutsummaryrefslogtreecommitdiffstats
path: root/executor
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2024-05-31 11:16:40 +0200
committerDmitry Vyukov <dvyukov@google.com>2024-06-03 10:06:46 +0000
commit720b7efa99d7f3b4069af440ea7d004a6f27467d (patch)
treea812d72870d72580cf4b902d00fc2e9cf14fd122 /executor
parentd2862abe19f233f936ad61550df88db5b41049c8 (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.h26
-rw-r--r--executor/common_bsd.h23
-rw-r--r--executor/common_linux.h159
-rw-r--r--executor/executor.cc14
-rw-r--r--executor/executor_linux.h60
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