From 2cfeb82e3d3776c168c636693e5ccc43a1ad5ce7 Mon Sep 17 00:00:00 2001 From: thorbjoern Date: Wed, 9 Oct 2019 14:49:28 +0200 Subject: [PATCH 1/2] Shut down if all initial inputs crash If a Fuzz function crashes on every initial input provided, the fuzzer still executes and posts stats to stdout. However, since no inputs are provided to the workers, nothing meaningful happens. The behaviour is confusing because of two points: 1. No proper logging is done. The coordinator continues to execute and posts stats. 2. Crashing inputs in general are allowed. The problem arises only if all initial inputs crash. The problem in more detail: The coordinator maps the initial corpus into memory. If no corpus is provided the coordinator creates a default input (the empty input). Workers then access the corpus over the hub, not over the coordinator. Before this happens they are given the initial corpus files and are testing them. If the Fuzz function crashes on these inputs, they will never be passed to the hub. Therefore they are not available to the workers after this initial stage. The proposed solution is as follows: After the initial triage stage we wait a short amount of time for synchronization purposes. Then we check if the corpus is still empty. If it is we conclude that the target crashed on every input and shut down the fuzzer with the respective error message. --- go-fuzz/worker.go | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/go-fuzz/worker.go b/go-fuzz/worker.go index cbbc5c957..d8fa68fce 100644 --- a/go-fuzz/worker.go +++ b/go-fuzz/worker.go @@ -189,9 +189,21 @@ func (w *Worker) loop() { w.triageInput(input) for { x := atomic.LoadUint32(&w.hub.initialTriage) - if x == 0 || atomic.CompareAndSwapUint32(&w.hub.initialTriage, x, x-1) { + if x == 0 { break } + if atomic.CompareAndSwapUint32(&w.hub.initialTriage, x, x-1) { + // After the last initial input was processed, check if anything was actually added to the corpus. + // If the corpus is empty at this point, we have nothing to feed to our workers. + if x == 1 { + // Give the hub time to store the initial inputs + time.Sleep(100 * time.Millisecond) + if ro := w.hub.ro.Load().(*ROData); len(ro.corpus) == 0 { + log.Fatal("all initial inputs crashed. provide at least one non-crashing input.") + } + } + } + break } continue default: From 240e64a0e525a0442a0d45a7fb27941fc101469e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B6rn=20Schulz?= <41148163+ThorbjoernSchulz@users.noreply.github.com> Date: Mon, 16 Mar 2020 20:51:35 +0100 Subject: [PATCH 2/2] Break at the right spot The break statement accidentally slipped into the wrong block. --- go-fuzz/worker.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go-fuzz/worker.go b/go-fuzz/worker.go index d8fa68fce..a536e781e 100644 --- a/go-fuzz/worker.go +++ b/go-fuzz/worker.go @@ -202,8 +202,8 @@ func (w *Worker) loop() { log.Fatal("all initial inputs crashed. provide at least one non-crashing input.") } } + break } - break } continue default: