Skip to content

Commit

Permalink
executor: add runner mode
Browse files Browse the repository at this point in the history
Move all syz-fuzzer logic into syz-executor and remove syz-fuzzer.
Also restore syz-runtest functionality in the manager.

Update google#4917 (sets most signal handlers to SIG_IGN)
  • Loading branch information
dvyukov committed Jun 24, 2024
1 parent f9c1d6c commit d96e753
Show file tree
Hide file tree
Showing 72 changed files with 4,159 additions and 4,400 deletions.
1 change: 1 addition & 0 deletions .clang-format
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
ColumnLimit: 0
AccessModifierOffset: -8
PackConstructorInitializers: Never

20 changes: 4 additions & 16 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,7 @@ ifeq ("$(TARGETOS)", "trusty")
endif

.PHONY: all clean host target \
manager runtest fuzzer executor \
ci hub \
manager executor ci hub \
execprog mutate prog2c trace2syz repro upgrade db \
usbgen symbolize cover kconf syz-build crush \
bin/syz-extract bin/syz-fmt \
Expand All @@ -112,8 +111,8 @@ endif
presubmit_arch_executor presubmit_dashboard presubmit_race presubmit_race_dashboard presubmit_old

all: host target
host: manager runtest repro mutate prog2c db upgrade
target: fuzzer execprog executor
host: manager repro mutate prog2c db upgrade
target: execprog executor

executor: descriptions
ifeq ($(TARGETOS),fuchsia)
Expand Down Expand Up @@ -156,13 +155,6 @@ descriptions:
manager: descriptions
GOOS=$(HOSTOS) GOARCH=$(HOSTARCH) $(HOSTGO) build $(GOHOSTFLAGS) -o ./bin/syz-manager github.com/google/syzkaller/syz-manager

runtest: descriptions
# TODO: fold syz-runtest into syz-manager.
# GOOS=$(HOSTOS) GOARCH=$(HOSTARCH) $(HOSTGO) build $(GOHOSTFLAGS) -o ./bin/syz-runtest github.com/google/syzkaller/tools/syz-runtest

fuzzer: descriptions
GOOS=$(TARGETGOOS) GOARCH=$(TARGETGOARCH) $(GO) build $(GOTARGETFLAGS) -o ./bin/$(TARGETOS)_$(TARGETVMARCH)/syz-fuzzer$(EXE) github.com/google/syzkaller/syz-fuzzer

execprog: descriptions
GOOS=$(TARGETGOOS) GOARCH=$(TARGETGOARCH) $(GO) build $(GOTARGETFLAGS) -o ./bin/$(TARGETOS)_$(TARGETVMARCH)/syz-execprog$(EXE) github.com/google/syzkaller/tools/syz-execprog

Expand Down Expand Up @@ -215,13 +207,9 @@ bisect: descriptions
GOOS=$(HOSTOS) GOARCH=$(HOSTARCH) $(HOSTGO) build $(GOHOSTFLAGS) -o ./bin/syz-bisect github.com/google/syzkaller/tools/syz-bisect

verifier: descriptions
# TODO: switch syz-verifier to use syz-fuzzer.
# TODO: switch syz-verifier to use syz-executor.
# GOOS=$(HOSTOS) GOARCH=$(HOSTARCH) $(HOSTGO) build $(GOHOSTFLAGS) -o ./bin/syz-verifier github.com/google/syzkaller/syz-verifier

runner: descriptions
# TODO: switch syz-verifier to use syz-fuzzer.
# GOOS=$(TARGETGOOS) GOARCH=$(TARGETGOARCH) $(GO) build $(GOTARGETFLAGS) -o ./bin/$(TARGETOS)_$(TARGETVMARCH)/syz-runner$(EXE) github.com/google/syzkaller/syz-runner

# `extract` extracts const files from various kernel sources, and may only
# re-generate parts of files.
extract: bin/syz-extract
Expand Down
9 changes: 4 additions & 5 deletions docs/internals.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,13 @@ red labels indicate corresponding configuration options.

It runs on a host with a stable kernel which does not experience white-noise fuzzer load.

`syz-manager` starts `syz-fuzzer` processes (one inside each VM).
`syz-fuzzer`s comminucate with `syz-manager` over RPC to receive the programs
`syz-manager` starts `syz-executor` processes (one inside each VM).
`syz-executor`s comminucate with `syz-manager` over RPC to receive the programs
that must be executed and to report back the results (error statuses, collected coverage, etc.).

To execute programs, `syz-fuzzer` starts transient `syz-executor` processes.
To execute programs, `syz-executor` starts transient subprocesses.

Each `syz-executor` process executes a single input (a sequence of syscalls).
It accepts the program to execute from the `syz-fuzzer` process and sends results back.
Each transient subprocess executes a single input (a sequence of syscalls).
It is designed to be as simple as possible (to not interfere with fuzzing process),
written in C++, compiled as static binary and uses shared memory for communication.

Expand Down
6 changes: 4 additions & 2 deletions docs/setup_syzbot.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ This doc will be useful to you:
- should you wish to hack on user interface bits like the dashboard / mailing list integration or
- should you wish to continuously run a separate syzbot dashboard for your own kernels

Note: For most development purposes you don't need a full syzbot setup. The meat of syzkaller is really located in syz-manager, syz-fuzzer and syz-executor. You can run syz-manager directly which is usually what you will want to do during fuzzer development. [See this documentation for syz-manager setup instructions](setup.md).
Note: For most development purposes you don't need a full syzbot setup. The meat of syzkaller is really located
in syz-manager and syz-executor. You can run syz-manager directly which is usually what you will want to do during
fuzzer development. [See this documentation for syz-manager setup instructions](setup.md).

This doc assumes that you:
- have a GCP account and billing setup
Expand Down Expand Up @@ -366,4 +368,4 @@ sudo journalctl -fu syz-ci
```
gcloud app browse --project=$PROJECT
```
Once syzkaller finds the first crashes they should show up here. This might take a while.
Once syzkaller finds the first crashes they should show up here. This might take a while.
7 changes: 0 additions & 7 deletions docs/syz_verifier.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,3 @@ ERRNO mismatches found for program:
The order of the results is given by the order in which configuration files
were passed so `Pool: 0 ` reports results for the kernel created using
`kernel0.cfg` and so on.

The [Flags](/pkg/ipc/ipc.go#L82) can be used to determine the state reached by
the system call:
* `0` = syscall not even started
* `1` = syscall started
* `3` = syscall finished executing
* `7` = syscall blocked
6 changes: 3 additions & 3 deletions docs/troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
Here are some things to check if there are problems running syzkaller.

- Use the `-debug` command line option to make syzkaller print all possible debug output,
from both the `syz-manager` top-level program and the `syz-fuzzer` instances. With this option
syzkaller will only run one VM instance.
from both the `syz-manager` top-level program and the `syz-executor` instances.
With this option syzkaller will only run one VM instance.

- Use the `-vv N` command line option to increase the amount of logging output, from both
the `syz-manager` top-level program and the `syz-fuzzer` instances (which go to the
the `syz-manager` top-level program and the `syz-executor` instances (which go to the
output files in the `crashes` subdirectory of the working directory). Higher values of
N give more output.

Expand Down
19 changes: 8 additions & 11 deletions executor/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ static void use_temporary_dir(void)
#endif

#if GOOS_netbsd || GOOS_freebsd || GOOS_darwin || GOOS_openbsd || GOOS_test
#if (SYZ_EXECUTOR || SYZ_REPEAT) && SYZ_EXECUTOR_USES_FORK_SERVER && (SYZ_EXECUTOR || SYZ_USE_TMP_DIR)
#if SYZ_EXECUTOR || SYZ_REPEAT && SYZ_USE_TMP_DIR && SYZ_EXECUTOR_USES_FORK_SERVER
#include <dirent.h>
#include <errno.h>
#include <stdio.h>
Expand Down Expand Up @@ -594,20 +594,17 @@ static void loop(void)

#if SYZ_EXECUTOR || SYZ_REPEAT
static void execute_one(void);
#if SYZ_EXECUTOR_USES_FORK_SERVER
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>

#if GOOS_linux
#define WAIT_FLAGS __WALL
#else
#define WAIT_FLAGS 0
#endif

#if SYZ_EXECUTOR
static void reply_handshake();
#endif
#if SYZ_EXECUTOR_USES_FORK_SERVER
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>

static void loop(void)
{
Expand All @@ -616,7 +613,7 @@ static void loop(void)
#endif
#if SYZ_EXECUTOR
// Tell parent that we are ready to serve.
reply_handshake();
reply_execute(0);
#endif
int iter = 0;
#if SYZ_REPEAT_TIMES
Expand Down Expand Up @@ -675,7 +672,7 @@ static void loop(void)
uint64 start = current_time_ms();
#if SYZ_EXECUTOR
uint64 last_executed = start;
uint32 executed_calls = __atomic_load_n(output_data, __ATOMIC_RELAXED);
uint32 executed_calls = output_data->completed.load(std::memory_order_relaxed);
#endif
for (;;) {
sleep_ms(10);
Expand All @@ -695,7 +692,7 @@ static void loop(void)
uint64 min_timeout_ms = program_timeout_ms * 3 / 5;
uint64 inactive_timeout_ms = syscall_timeout_ms * 20;
uint64 now = current_time_ms();
uint32 now_executed = __atomic_load_n(output_data, __ATOMIC_RELAXED);
uint32 now_executed = output_data->completed.load(std::memory_order_relaxed);
if (executed_calls != now_executed) {
executed_calls = now_executed;
last_executed = now;
Expand Down
192 changes: 192 additions & 0 deletions executor/conn.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
// Copyright 2024 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

#include <arpa/inet.h>
#include <fcntl.h>
#include <netdb.h>
#include <string.h>
#include <sys/select.h>
#include <sys/socket.h>

#include <vector>

// Connection represents a client TCP connection.
// It connects to the given addr:port and allows to send/receive
// flatbuffers-encoded messages.
class Connection
{
public:
Connection(const char* addr, const char* port)
: fd_(Connect(addr, port))
{
}

int FD() const
{
return fd_;
}

template <typename Msg>
void Send(const Msg& msg)
{
typedef typename Msg::TableType Raw;
auto off = Raw::Pack(fbb_, &msg);
fbb_.FinishSizePrefixed(off);
auto data = fbb_.GetBufferSpan();
Send(data.data(), data.size());
fbb_.Reset();
}

template <typename Msg>
void Recv(Msg& msg)
{
typedef typename Msg::TableType Raw;
flatbuffers::uoffset_t size;
Recv(&size, sizeof(size));
recv_buf_.resize(size);
Recv(recv_buf_.data(), size);
auto raw = flatbuffers::GetRoot<Raw>(recv_buf_.data());
raw->UnPackTo(&msg);
}

void Send(const void* data, size_t size)
{
for (size_t sent = 0; sent < size;) {
ssize_t n = write(fd_, static_cast<const char*>(data) + sent, size - sent);
if (n > 0) {
sent += n;
continue;
}
if (errno == EINTR)
continue;
if (errno == EAGAIN) {
sleep_ms(1);
continue;
}
failmsg("failed to send rpc", "fd=%d want=%zu sent=%zu n=%zd", fd_, size, sent, n);
}
}

private:
const int fd_;
std::vector<char> recv_buf_;
flatbuffers::FlatBufferBuilder fbb_;

void Recv(void* data, size_t size)
{
for (size_t recv = 0; recv < size;) {
ssize_t n = read(fd_, static_cast<char*>(data) + recv, size - recv);
if (n > 0) {
recv += n;
continue;
}
if (errno == EINTR)
continue;
if (errno == EAGAIN) {
sleep_ms(1);
continue;
}
failmsg("failed to recv rpc", "fd=%d want=%zu sent=%zu n=%zd", fd_, size, recv, n);
}
}

static int Connect(const char* addr, const char* ports)
{
int port = atoi(ports);
if (port == 0)
failmsg("failed to parse manager port", "port=%s", ports);
if (!strcmp(addr, "stdin"))
return STDIN_FILENO;
sockaddr_in saddr4 = {};
saddr4.sin_family = AF_INET;
saddr4.sin_port = htons(port);
if (inet_pton(AF_INET, addr, &saddr4.sin_addr))
return Connect(&saddr4, &saddr4.sin_addr, port);
sockaddr_in6 saddr6 = {};
saddr6.sin6_family = AF_INET6;
saddr6.sin6_port = htons(port);
if (inet_pton(AF_INET6, addr, &saddr6.sin6_addr))
return Connect(&saddr6, &saddr6.sin6_addr, port);
auto* hostent = gethostbyname(addr);
if (!hostent)
failmsg("failed to resolve manager addr", "addr=%s h_errno=%d", addr, h_errno);
for (char** addr = hostent->h_addr_list; *addr; addr++) {
int fd;
if (hostent->h_addrtype == AF_INET) {
memcpy(&saddr4.sin_addr, *addr, std::min<size_t>(hostent->h_length, sizeof(saddr4.sin_addr)));
fd = Connect(&saddr4, &saddr4.sin_addr, port);
} else if (hostent->h_addrtype == AF_INET6) {
memcpy(&saddr6.sin6_addr, *addr, std::min<size_t>(hostent->h_length, sizeof(saddr6.sin6_addr)));
fd = Connect(&saddr6, &saddr6.sin6_addr, port);
} else {
failmsg("unknown socket family", "family=%d", hostent->h_addrtype);
}
if (fd != -1)
return fd;
}
failmsg("can't connect to manager", "addr=%s:%s", addr, ports);
}

template <typename addr_t>
static int Connect(addr_t* addr, void* ip, int port)
{
auto* saddr = reinterpret_cast<sockaddr*>(addr);
int fd = socket(saddr->sa_family, SOCK_STREAM, IPPROTO_TCP);
if (fd == -1)
fail("failed to create socket");
char str[128] = {};
inet_ntop(saddr->sa_family, ip, str, sizeof(str));
if (connect(fd, saddr, sizeof(*addr))) {
printf("failed to connect to manager at %s:%d: %s\n", str, port, strerror(errno));
close(fd);
return -1;
}
return fd;
}

Connection(const Connection&) = delete;
Connection& operator=(const Connection&) = delete;
};

// Select is a wrapper around select system call.
class Select
{
public:
Select()
{
FD_ZERO(&rdset_);
}

void Arm(int fd)
{
FD_SET(fd, &rdset_);
max_fd_ = std::max(max_fd_, fd);
}

bool Ready(int fd) const
{
return FD_ISSET(fd, &rdset_);
}

void Wait(int ms)
{
timespec timeout = {.tv_sec = ms / 1000, .tv_nsec = (ms % 1000) * 1000 * 1000};
if (pselect(max_fd_ + 1, &rdset_, nullptr, nullptr, &timeout, nullptr) < 0) {
if (errno != EINTR && errno != EAGAIN)
fail("pselect failed");
}
}

static void Prepare(int fd)
{
if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK))
fail("fcntl(O_NONBLOCK) failed");
}

private:
fd_set rdset_;
int max_fd_ = -1;

Select(const Select&) = delete;
Select& operator=(const Select&) = delete;
};
8 changes: 5 additions & 3 deletions executor/cover_filter.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,15 @@
class CoverFilter
{
public:
CoverFilter(const char* file, void* preferred = nullptr)
: shmem_(file, preferred, kMemSize), tab_(static_cast<Table*>(shmem_.Mem()))
CoverFilter()
: shmem_(kMemSize),
tab_(static_cast<Table*>(shmem_.Mem()))
{
}

CoverFilter(int fd, void* preferred = nullptr)
: shmem_(fd, preferred, kMemSize, false), tab_(static_cast<Table*>(shmem_.Mem()))
: shmem_(fd, preferred, kMemSize, false),
tab_(static_cast<Table*>(shmem_.Mem()))
{
}

Expand Down
Loading

0 comments on commit d96e753

Please sign in to comment.