Skip to content

Commit

Permalink
pkg/vminfo: move feature checking to host
Browse files Browse the repository at this point in the history
Feature checking procedure is split into 2 phases:
1. syz-fuzzer invokes "syz-executor setup feature" for each feature one-by-one,
and checks if executor does not fail.
Executor can also return a special "this feature does not need custom setup",
this allows to not call setup of these features in each new VM.
2. pkg/vminfo runs a simple program with ipc.ExecOpts specific for a concrete feature,
e.g. for wifi injection it will try to run a program with wifi feature enabled,
if setup of the feature fails, executor should also exit with an error.
For coverage features we also additionally check that we actually got coverage.
Then pkg/vminfo combines results of these 2 checks into final result.

syz-execprog now also uses vminfo package and mimics the same checking procedure.

Update #1541
  • Loading branch information
dvyukov committed May 15, 2024
1 parent 94b087b commit af08937
Show file tree
Hide file tree
Showing 36 changed files with 685 additions and 2,077 deletions.
13 changes: 3 additions & 10 deletions docs/pseudo_syscalls.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,17 +56,10 @@ are violated (e.g. passing `NULL` to a `non-NULL` argument, or passing
that.

Now, to handle the pseudo-syscall properly we have to update the
`isSupportedSyzkall` in
[syscalls_linux.go](../pkg/host/syscalls_linux.go) and add a particular
`linuxSyscallChecks` in
[linux_syscalls.go](../pkg/vminfo/linux_syscalls.go) and add a particular
case for this syscall, enabling it when necessary. If we want to enable
it unconditionally we can simply make `isSupportedSyzkall` return `true,
""` for it:

func isSupportedSyzkall(sandbox string, c *prog.Syscall) (bool, string) {
switch c.CallName {
...
case "syz_mycall":
return true, ""
it unconditionally we can simply use `alwaysSupported` for it.

Finally, run `make generate`. Now you can use it in a syscall
description file as if it was a regular system call:
Expand Down
4 changes: 4 additions & 0 deletions executor/common_bsd.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ static void setup_usb(void)
if (dir == NULL)
fail("failed to open /dev");

bool have_vhci = false;
struct dirent* ent = NULL;
while ((ent = readdir(dir)) != NULL) {
if (ent->d_type != DT_CHR)
Expand All @@ -34,7 +35,10 @@ static void setup_usb(void)
snprintf(path, sizeof(path), "/dev/%s", ent->d_name);
if (chmod(path, 0666))
failmsg("failed to chmod vhci", "path=%s", path);
have_vhci = true;
}
if (!have_vhci)
fail("don't have any /dev/vhci devices");

closedir(dir);
}
Expand Down
44 changes: 26 additions & 18 deletions executor/executor.cc
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,7 @@ struct kcov_comparison_t {
typedef char kcov_comparison_size[sizeof(kcov_comparison_t) == 4 * sizeof(uint64) ? 1 : -1];

struct feature_t {
const char* name;
rpc::Feature id;
void (*setup)();
};

Expand Down Expand Up @@ -1658,33 +1658,41 @@ 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.
// Note: this can be called multiple times and must be idempotent.
flag_debug = true;
if (n != 1)
fail("setup: more than one feature");
char* endptr = nullptr;
auto feature = static_cast<rpc::Feature>(strtoull(enable[0], &endptr, 10));
if (endptr == enable[0] || (feature > rpc::Feature::ANY) ||
__builtin_popcountll(static_cast<uint64>(feature)) > 1)
failmsg("setup: failed to parse feature", "feature='%s'", enable[0]);
if (feature == rpc::Feature::NONE) {
#if SYZ_HAVE_FEATURES
setup_sysctl();
setup_cgroups();
setup_sysctl();
setup_cgroups();
#endif
#if SYZ_HAVE_SETUP_EXT
// This can be defined in common_ext.h.
setup_ext();
// This can be defined in common_ext.h.
setup_ext();
#endif
for (int i = 0; i < n; i++) {
bool found = false;
#if SYZ_HAVE_FEATURES
for (unsigned f = 0; f < sizeof(features) / sizeof(features[0]); f++) {
if (strcmp(enable[i], features[f].name) == 0) {
features[f].setup();
found = true;
break;
}
return;
}
for (size_t i = 0; i < sizeof(features) / sizeof(features[0]); i++) {
if (features[i].id == feature) {
features[i].setup();
return;
}
#endif
if (!found)
failmsg("setup features: unknown feature", "feature=%s", enable[i]);
}
// Note: pkg/host knows about this error message.
fail("feature setup is not needed");
}

void failmsg(const char* err, const char* msg, ...)
Expand Down Expand Up @@ -1728,7 +1736,7 @@ void exitf(const char* msg, ...)
vfprintf(stderr, msg, args);
va_end(args);
fprintf(stderr, " (errno %d)\n", e);
doexit(0);
doexit(1);
}

void debug(const char* msg, ...)
Expand Down
4 changes: 2 additions & 2 deletions executor/executor_bsd.h
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,8 @@ static bool use_cover_edges(uint64 pc)
#if GOOS_netbsd
#define SYZ_HAVE_FEATURES 1
static feature_t features[] = {
{"usb", setup_usb},
{"fault", setup_fault},
{rpc::Feature::USBEmulation, setup_usb},
{rpc::Feature::Fault, setup_fault},
};

static void setup_sysctl(void)
Expand Down
52 changes: 45 additions & 7 deletions executor/executor_linux.h
Original file line number Diff line number Diff line change
Expand Up @@ -256,13 +256,51 @@ NORETURN void doexit_thread(int status)
}
}

static void 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
// failing. It searches for the nic in init namespace, but then the nic is moved
// to one of testing namespace, so if number of procs is more than the number of devices,
// then some of them won't fine a nic (the code is also racy, more than one proc
// 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");
}

static void 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");
}

static void setup_delay_kcov()
{
is_kernel_64_bit = detect_kernel_bitness();
cover_t cov = {};
cov.fd = kCoverFd;
cover_open(&cov, false);
cover_mmap(&cov);
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;
}

#define SYZ_HAVE_FEATURES 1
static feature_t features[] = {
{"leak", setup_leak},
{"fault", setup_fault},
{"binfmt_misc", setup_binfmt_misc},
{"kcsan", setup_kcsan},
{"usb", setup_usb},
{"802154", setup_802154},
{"swap", setup_swap},
{rpc::Feature::DelayKcovMmap, setup_delay_kcov},
{rpc::Feature::Fault, setup_fault},
{rpc::Feature::Leak, setup_leak},
{rpc::Feature::KCSAN, setup_kcsan},
{rpc::Feature::USBEmulation, setup_usb},
{rpc::Feature::LRWPANEmulation, setup_802154},
{rpc::Feature::BinFmtMisc, setup_binfmt_misc},
{rpc::Feature::Swap, setup_swap},
{rpc::Feature::NicVF, setup_nicvf},
{rpc::Feature::DevlinkPCI, setup_devlink_pci},
};

0 comments on commit af08937

Please sign in to comment.