executor: make exit code during fail() depend on fault injection

fail()'s are often used during the validation of kernel reactions to
queries that were issued by pseudo syscalls implementations. As fault
injection may cause the kernel not to succeed in handling these
queries (e.g. socket writes or reads may fail), this could ultimately
lead to unwanted "lost connection to test machine" crashes.

In order to avoid this and, on the other hand, to still have the
ability to signal a disastrous situation, the exit code of this
function now depends on the current context.

All fail() invocations during system call execution with enabled fault
injection lead to termination with zero exit code. In all other cases,
the exit code is kFailStatus.

This is achieved by introduction of a special thread-specific variable
`current_thread` that allows to access information about the thread in
which the current code is executing.

Also, this commit eliminates current_cover as it is no longer needed.
This commit is contained in:
Aleksandr Nogikh 2020-09-21 18:37:45 +03:00 committed by Dmitry Vyukov
parent 2450c42f1b
commit 3e8f6c2755
2 changed files with 27 additions and 7 deletions

View File

@ -215,10 +215,13 @@ struct thread_t {
uint32 reserrno;
bool fault_injected;
cover_t cov;
bool soft_fail_state;
};
static thread_t threads[kMaxThreads];
static thread_t* last_scheduled;
// Threads use this variable to access information about themselves.
static __thread struct thread_t* current_thread;
static cover_t extra_cov;
@ -375,6 +378,7 @@ int main(int argc, char** argv)
start_time_ms = current_time_ms();
os_init(argc, argv, (char*)SYZ_DATA_OFFSET, SYZ_NUM_PAGES * SYZ_PAGE_SIZE);
current_thread = &threads[0];
#if SYZ_EXECUTOR_USES_SHMEM
if (mmap(&input_data[0], kMaxInput, PROT_READ, MAP_PRIVATE | MAP_FIXED, kInFd, 0) != &input_data[0])
@ -1059,7 +1063,7 @@ void thread_create(thread_t* th, int id)
void* worker_thread(void* arg)
{
thread_t* th = (thread_t*)arg;
current_thread = th;
if (flag_coverage)
cover_enable(&th->cov, flag_comparisons, false);
for (;;) {
@ -1084,10 +1088,12 @@ void execute_call(thread_t* th)
debug(")\n");
int fail_fd = -1;
th->soft_fail_state = false;
if (flag_fault && th->call_index == flag_fault_call) {
if (collide)
fail("both collide and fault injection are enabled");
fail_fd = inject_fault(flag_fault_nth);
th->soft_fail_state = true;
}
if (flag_coverage)
@ -1104,6 +1110,9 @@ void execute_call(thread_t* th)
th->res = 0;
th->reserrno = 0;
}
// Reset the flag before the first possible fail().
th->soft_fail_state = false;
if (flag_coverage) {
cover_collect(&th->cov);
if (th->cov.size >= kCoverSize)
@ -1477,6 +1486,20 @@ void fail(const char* msg, ...)
vfprintf(stderr, msg, args);
va_end(args);
fprintf(stderr, " (errno %d)\n", e);
// fail()'s are often used during the validation of kernel reactions to queries
// that were issued by pseudo syscalls implementations. As fault injection may
// cause the kernel not to succeed in handling these queries (e.g. socket writes
// or reads may fail), this could ultimately lead to unwanted "lost connection to
// test machine" crashes.
// In order to avoid this and, on the other hand, to still have the ability to
// signal a disastrous situation, the exit code of this function depends on the
// current context.
// All fail() invocations during system call execution with enabled fault injection
// lead to termination with zero exit code. In all other cases, the exit code is
// kFailStatus.
if (current_thread && current_thread->soft_fail_state)
doexit(0);
doexit(kFailStatus);
}

View File

@ -80,8 +80,6 @@ static void os_init(int argc, char** argv, char* data, size_t data_size)
fail("mmap of right data PROT_NONE page failed");
}
static __thread cover_t* current_cover;
static intptr_t execute_syscall(const call_t* c, intptr_t a[kMaxArgs])
{
if (c->call)
@ -186,7 +184,6 @@ static void cover_enable(cover_t* cov, bool collect_comps, bool extra)
if (!extra) {
if (ioctl(cov->fd, KCOV_ENABLE, kcov_mode))
exitf("cover enable write trace failed, mode=%d", kcov_mode);
current_cover = cov;
return;
}
if (is_kernel_64_bit)
@ -201,9 +198,9 @@ static void cover_reset(cover_t* cov)
if (!flag_coverage)
return;
if (cov == 0) {
if (current_cover == 0)
fail("cover_reset: current_cover == 0");
cov = current_cover;
if (current_thread == 0)
fail("cover_reset: current_thread == 0");
cov = &current_thread->cov;
}
*(uint64*)cov->data = 0;
}