> When fuzzing Suricata I'm seeing crashes that I can't reproduce.
One thing to try is to test the input by calling the target program
10k times or so, in a simple shell loop. Some crashes are
non-deterministic because they depend on scheduler decisions or memory
randomization artifacts.
> My hypothesis is that it's because I'm keeping some global state.
It's possible, but typically less likely. That said...
> In suricata we process network packets. Some packets (e.g. tcp) update a
> global state. My fuzz input treats the file generated by AFL as a
> packet. The global state is initialized and freed outside of the
> '__AFL_LOOP' loop.
That sounds like a problem, if you have global state that doesn't get
reset within __AFL_LOOP(), you're bound to not only have trouble
reproducing bugs, but also get less out of the instrumentation (since
the information about that persisted state is lost on AFL). My advice
would be to reset state thoroughly (or not use persistent mode if this
is not doable; deferred forkserver may be a viable alternative).
If you want to test multi-packet parsing, modifying __AFL_LOOP to read
and process several packets may be a better option.
> I guess a couple of things could be useful:
>
> 1. crash report by AFL containing asan symbolized output. That would at
> least give me a starting point.
AFL doesn't capture stderr for the target, although you could make a
relatively simple one-off change to accommodate this. Capturing core
files or invoking an external symbolizer is highly problematic,
because it adds an unpredictable but significant delay and tends to
lead to crashes being misdiagnosed as timeouts.
In the next major version of AFL, I'm planning to add support for
re-running a crash to collect this additional data once a fault is
confirmed, but this wouldn't help in your particular case (since
re-running might not necessarily trigger the same issue).
The best piece of info you may have is whatever ended up in dmesg - at
least the faulting instruction pointer. Better than nothing, but not
great.
> 2. AFL option to attach gdb to the first crash case it finds to allow
> manual inspection of the running program
This would require fairly substantial architectural changes (moving to
ptrace()).
> 3. on my end I guess I could write to disk the input AFL has generated
> and then write some logic to read it in in the same order. That would
> hopefully reproduce the same conditions.
This seems problematic, too. Without knowing where the chain starts,
we'd have to store a ton of historical data. Generally, for better or
worse, AFL has an expectation that the state is reset to a blank
slate, and that if you want to test multi-packet handling or something
like that, that it will be still implemented within a single pass of
__AFL_LOOP.
Cheers,
/mz