Page MenuHomePhabricator

[compiler-rt] Expose fuzzer coverage bitmaps to the target
Needs ReviewPublic

Authored by a1xndr on Aug 2 2019, 12:11 PM.



This change enables a fuzz-target to locate the coverage bitmaps that are
checked at the end of each fuzz-run. The main use-case for this is
a fuzz-target which, similarly to AFL, forks to reset state. Unlike AFL,
libfuzzer doesn't place the coverage data in shared memory. To work around
this, the target can map some shared memory and copy the coverage data from the
child to the parent, at the end of each fuzz run. There is an example of such a
fuzz target in test/fuzzer/DeferredForkTest.cpp

Another use-case is state-resetting through memory-snapshotting. In this case,
the target would use the information to roll-back changes to memory outside of
the fuzzer-related pages.

I am fuzzing virtual QEMU devices as part of Google Summer of Code 2019.
Previously, I used virtual machine snapshots and reboots to reset state, but the
fork-based approach often performs better.

The patch exposes a LLVMFuzzerIterateFeatureRegions() function to the guest,
which leaves the actual implementation of the forking/other type of resetting
logic up to the fuzz-target developer, offering some flexibility over an afl-style

Diff Detail

Event Timeline

a1xndr created this revision.Aug 2 2019, 12:11 PM
Herald added a project: Restricted Project. · View Herald TranscriptAug 2 2019, 12:11 PM
Herald added subscribers: Restricted Project, llvm-commits, dberris. · View Herald Transcript
a1xndr added a comment.Aug 9 2019, 6:42 AM

Ping. This has been a useful feature for fuzzing QEMU virtual devices. It has some overhead due to the memcpy, but there are ways to avoid this overhead, by adding a user-modifiable counters pointer, or possibly re-mapping existing counter regions as shared memory.

Not a libfuzzer person, but i wonder if this lifts the veil from internal implementation detail too much?

Not a libfuzzer person, but i wonder if this lifts the veil from internal implementation detail too much?

I think this is true, but at the same time exposing this info to the fuzzer developer provides them with flexibility to build e.g. custom fork-servers. You can get most of the way there by wrapping libfuzzer's __sanitizer_cov_8bit_counters_init etc using ld --wrap, but this doesn't cover internal counters such as TracePC::ValueProfileMap.
To avoid copying for each fuzzing-run, the counters could even be stored in a dynamically-allocated buffers, provided by the fuzz-target. This way, there is no need to copy the counters, as they could be stored directly in SHM.

My opinion is that this exposes too much implementation detail and seems too hacky/niche to be merged into libFuzzer. But I defer the ultimate decision to @kcc (who is unlikely to respond until next week when he is back in the office).

For your use-case would it work to implement your initialization in LLVMFuzzerInitialize and then use fork mode?