From 214351e168def9426c79e1f65a93ddb112cee906 Mon Sep 17 00:00:00 2001 From: Aleksandr Nogikh Date: Wed, 19 Jan 2022 17:38:24 +0000 Subject: executor: fail on SEGV during clone() As was found out in #2921, fork bombs are still possible in Linux-based instances. One of the possible reasons is described below. An invalid stack can be passed to the clone() call, thus causing it to stumble on an invalid memory access right during returning from the clone() call. This is in turn catched by the NONFAILING() macro and the control actually jumps over it and eventually both the child and the parent continue executing the same code. Prevent it by handling SIGSEGV and SIGBUS differently during the clone process. Co-authored-by: Andrei Vagin --- executor/common.h | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'executor/common.h') diff --git a/executor/common.h b/executor/common.h index 74e75d33b..ddb33bec7 100644 --- a/executor/common.h +++ b/executor/common.h @@ -57,6 +57,11 @@ NORETURN void doexit(int status) for (;;) { } } +NORETURN void doexit_thread(int status) +{ + // For BSD systems, _exit seems to do exactly what's needed. + doexit(status); +} #endif #if SYZ_EXECUTOR || SYZ_MULTI_PROC || SYZ_REPEAT && SYZ_CGROUPS || \ @@ -76,6 +81,7 @@ static unsigned long long procid; #include #endif +static __thread int clone_ongoing; static __thread int skip_segv; static __thread jmp_buf segv_env; @@ -95,6 +101,17 @@ static void segv_handler(int sig, siginfo_t* info, void* ctx) // We additionally opportunistically check that the faulty address // is not within executable data region, because such accesses can corrupt // output region and then fuzzer will fail on corrupted data. + + if (__atomic_load_n(&clone_ongoing, __ATOMIC_RELAXED) != 0) { + // During clone, we always exit on a SEGV. If we do not, then + // it might prevent us from running child-specific code. E.g. + // if an invalid stack is passed to the clone() call, then it + // will trigger a seg fault, which in turn causes the child to + // jump over the NONFAILING macro and continue execution in + // parallel with the parent. + doexit_thread(sig); + } + uintptr_t addr = (uintptr_t)info->si_addr; const uintptr_t prog_start = 1 << 20; const uintptr_t prog_end = 100 << 20; -- cgit mrf-deployment