aboutsummaryrefslogtreecommitdiffstats
path: root/executor/executor_linux.h
diff options
context:
space:
mode:
Diffstat (limited to 'executor/executor_linux.h')
-rw-r--r--executor/executor_linux.h149
1 files changed, 109 insertions, 40 deletions
diff --git a/executor/executor_linux.h b/executor/executor_linux.h
index cf01327a7..1cdb2dc46 100644
--- a/executor/executor_linux.h
+++ b/executor/executor_linux.h
@@ -1,66 +1,135 @@
-// Copyright 2017 syzkaller project authors. All rights reserved.
+// Copyright 2015 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 <pthread.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/prctl.h>
+#include <sys/syscall.h>
+#include <unistd.h>
-typedef pthread_t osthread_t;
+#define KCOV_INIT_TRACE32 _IOR('c', 1, uint32)
+#define KCOV_INIT_TRACE64 _IOR('c', 1, uint64)
+#define KCOV_ENABLE _IO('c', 100)
+#define KCOV_DISABLE _IO('c', 101)
-void thread_start(osthread_t* t, void* (*fn)(void*), void* arg)
+const unsigned long KCOV_TRACE_PC = 0;
+const unsigned long KCOV_TRACE_CMP = 1;
+
+static bool detect_kernel_bitness();
+
+static void os_init(int argc, char** argv, void* data, size_t data_size)
+{
+ prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0);
+ is_kernel_64_bit = detect_kernel_bitness();
+ if (mmap(data, data_size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0) != data)
+ fail("mmap of data segment failed");
+}
+
+static __thread cover_t* current_cover;
+
+static long execute_syscall(const call_t* c, long a[kMaxArgs])
{
- pthread_attr_t attr;
- pthread_attr_init(&attr);
- pthread_attr_setstacksize(&attr, 128 << 10);
- if (pthread_create(t, &attr, fn, arg))
- exitf("pthread_create failed");
- pthread_attr_destroy(&attr);
+ if (c->call)
+ return c->call(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8]);
+ return syscall(c->sys_nr, a[0], a[1], a[2], a[3], a[4], a[5]);
}
-struct event_t {
- int state;
-};
+static void cover_open(cover_t* cov)
+{
+ cov->fd = open("/sys/kernel/debug/kcov", O_RDWR);
+ if (cov->fd == -1)
+ fail("open of /sys/kernel/debug/kcov failed");
+ const int kcov_init_trace = is_kernel_64_bit ? KCOV_INIT_TRACE64 : KCOV_INIT_TRACE32;
+ if (ioctl(cov->fd, kcov_init_trace, kCoverSize))
+ fail("cover init trace write failed");
+ size_t mmap_alloc_size = kCoverSize * (is_kernel_64_bit ? 8 : 4);
+ cov->data = (char*)mmap(NULL, mmap_alloc_size,
+ PROT_READ | PROT_WRITE, MAP_SHARED, cov->fd, 0);
+ if (cov->data == MAP_FAILED)
+ fail("cover mmap failed");
+ cov->data_end = cov->data + mmap_alloc_size;
+}
+
+static void cover_enable(cover_t* cov, bool collect_comps)
+{
+ int kcov_mode = collect_comps ? KCOV_TRACE_CMP : KCOV_TRACE_PC;
+ // This should be fatal,
+ // but in practice ioctl fails with assorted errors (9, 14, 25),
+ // so we use exitf.
+ if (ioctl(cov->fd, KCOV_ENABLE, kcov_mode))
+ exitf("cover enable write trace failed, mode=%d", kcov_mode);
+ current_cover = cov;
+}
-void event_init(event_t* ev)
+static void cover_reset(cover_t* cov)
{
- ev->state = 0;
+ if (cov == 0)
+ cov = current_cover;
+ *(uint64*)cov->data = 0;
}
-void event_reset(event_t* ev)
+static void cover_collect(cover_t* cov)
{
- ev->state = 0;
+ // Note: this assumes little-endian kernel.
+ cov->size = *(uint32*)cov->data;
}
-void event_set(event_t* ev)
+static bool cover_check(uint32 pc)
{
- if (ev->state)
- fail("event already set");
- __atomic_store_n(&ev->state, 1, __ATOMIC_RELEASE);
- syscall(SYS_futex, &ev->state, FUTEX_WAKE);
+ return true;
}
-void event_wait(event_t* ev)
+static bool cover_check(uint64 pc)
{
- while (!__atomic_load_n(&ev->state, __ATOMIC_ACQUIRE))
- syscall(SYS_futex, &ev->state, FUTEX_WAIT, 0, 0);
+#if defined(__i386__) || defined(__x86_64__)
+ // Text/modules range for x86_64.
+ return pc >= 0xffffffff80000000ull && pc < 0xffffffffff000000ull;
+#else
+ return true;
+#endif
}
-bool event_isset(event_t* ev)
+static bool detect_kernel_bitness()
{
- return __atomic_load_n(&ev->state, __ATOMIC_ACQUIRE);
+ if (sizeof(void*) == 8)
+ return true;
+ // It turns out to be surprisingly hard to understand if the kernel underneath is 64-bits.
+ // A common method is to look at uname.machine. But it is produced in some involved ways,
+ // and we will need to know about all strings it returns and in the end it can be overriden
+ // during build and lie (and there are known precedents of this).
+ // So instead we look at size of addresses in /proc/kallsyms.
+ bool wide = true;
+ int fd = open("/proc/kallsyms", O_RDONLY);
+ if (fd != -1) {
+ char buf[16];
+ if (read(fd, buf, sizeof(buf)) == sizeof(buf) &&
+ (buf[8] == ' ' || buf[8] == '\t'))
+ wide = false;
+ close(fd);
+ }
+ debug("detected %d-bit kernel\n", wide ? 64 : 32);
+ return wide;
}
-bool event_timedwait(event_t* ev, uint64 timeout_ms)
+// One does not simply exit.
+// _exit can in fact fail.
+// syzkaller did manage to generate a seccomp filter that prohibits exit_group syscall.
+// Previously, we get into infinite recursion via segv_handler in such case
+// and corrupted output_data, which does matter in our case since it is shared
+// with fuzzer process. Loop infinitely instead. Parent will kill us.
+// But one does not simply loop either. Compilers are sure that _exit never returns,
+// so they remove all code after _exit as dead. Call _exit via volatile indirection.
+// And this does not work as well. _exit has own handling of failing exit_group
+// in the form of HLT instruction, it will divert control flow from our loop.
+// So call the syscall directly.
+NORETURN void doexit(int status)
{
- uint64 start = current_time_ms();
- uint64 now = start;
- for (;;) {
- timespec ts = {};
- ts.tv_sec = 0;
- ts.tv_nsec = (timeout_ms - (now - start)) * 1000 * 1000;
- syscall(SYS_futex, &ev->state, FUTEX_WAIT, 0, &ts);
- if (__atomic_load_n(&ev->state, __ATOMIC_RELAXED))
- return true;
- now = current_time_ms();
- if (now - start > timeout_ms)
- return false;
+ volatile unsigned i;
+ syscall(__NR_exit_group, status);
+ for (i = 0;; i++) {
}
}