Index: lib/fuzzer/FuzzerInterface.h =================================================================== --- lib/fuzzer/FuzzerInterface.h +++ lib/fuzzer/FuzzerInterface.h @@ -70,6 +70,11 @@ FUZZER_INTERFACE_VISIBILITY size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize); +// Call a user-provided callback for each bitmap region containing coverage +// info. This can be useful for fuzzers that reset state by forking, or using +// memory snapshots, since they shouldn't interfere with the coverage data +FUZZER_INTERFACE_VISIBILITY void +LLVMFuzzerIterateFeatureRegions(void (*CB)(void *, size_t)); #undef FUZZER_INTERFACE_VISIBILITY #ifdef __cplusplus Index: lib/fuzzer/FuzzerLoop.cpp =================================================================== --- lib/fuzzer/FuzzerLoop.cpp +++ lib/fuzzer/FuzzerLoop.cpp @@ -863,5 +863,8 @@ assert(fuzzer::F); return fuzzer::F->GetMD().DefaultMutate(Data, Size, MaxSize); } - +ATTRIBUTE_INTERFACE void LLVMFuzzerIterateFeatureRegions(void (*CB)(void *, + size_t)) { + fuzzer::TPC.IterateFeatureRegions(CB); +} } // extern "C" Index: lib/fuzzer/FuzzerTracePC.h =================================================================== --- lib/fuzzer/FuzzerTracePC.h +++ lib/fuzzer/FuzzerTracePC.h @@ -130,6 +130,7 @@ const PCTableEntry *PCTableEntryByIdx(uintptr_t Idx); static uintptr_t GetNextInstructionPc(uintptr_t PC); bool PcIsFuncEntry(const PCTableEntry *TE) { return TE->PCFlags & 1; } + void IterateFeatureRegions(void (*CB)(void *, size_t)); private: bool UseCounters = false; Index: lib/fuzzer/FuzzerTracePC.cpp =================================================================== --- lib/fuzzer/FuzzerTracePC.cpp +++ lib/fuzzer/FuzzerTracePC.cpp @@ -440,6 +440,12 @@ exit(1); } +void TracePC::IterateFeatureRegions(void (*CB)(void *, size_t)) { + CB(&fuzzer::TPC, sizeof(fuzzer::TPC)); + IterateCounterRegions( + [&](Module::Region &R) { CB(R.Start, R.Stop - R.Start); }); + CB(ExtraCountersBegin(), ExtraCountersEnd() - ExtraCountersBegin()); +} } // namespace fuzzer extern "C" { Index: test/fuzzer/DeferredForkTest.cpp =================================================================== --- /dev/null +++ test/fuzzer/DeferredForkTest.cpp @@ -0,0 +1,68 @@ +#include +#include +#include +#include +#include +#include + +extern "C" void LLVMFuzzerIterateFeatureRegions(void (*CB)(void *, size_t)); + +size_t feature_shm_len = 0; +uintptr_t feature_shm; +size_t offset = 0; + +void count_shm_size(void *start, size_t len) { + feature_shm_len += len; +} + +void feature_load(void *start, size_t len) { + std::memcpy(start, (void *)(feature_shm + offset), len); + offset += len; +} + +void feature_store(void *start, size_t len) { + std::memcpy((void *)(feature_shm + offset), start, len); + offset += len; +} + +int isPrime(long n, int i) { + if (n <= 2) + return (n == 2) ? 1 : 0; + if (n % i == 0) + return 0; + if (i * i > n) + return 1; + return isPrime(n, i + 1); +} + +int fuzz(const uint8_t *Data, size_t Size) { + long *nums = (long *)Data; + while (Size > sizeof(long)) { + Size -= sizeof(long); + isPrime(*nums, 2); + nums++; + } + return 0; +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + if (fork() == 0) { + fuzz(Data, Size); + offset = 0; + LLVMFuzzerIterateFeatureRegions(&feature_store); + _exit(0); // Prevent libfuzzer from catching the exit + } else { + wait(NULL); + offset = 0; + LLVMFuzzerIterateFeatureRegions(&feature_load); + } + return 0; +} + +extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv) { + LLVMFuzzerIterateFeatureRegions(&count_shm_size); + + feature_shm = (uintptr_t)mmap(NULL, feature_shm_len, + PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); + return 0; +} Index: test/fuzzer/deferred-fork-test.test =================================================================== --- /dev/null +++ test/fuzzer/deferred-fork-test.test @@ -0,0 +1,3 @@ +RUN: %cpp_compiler %S/DeferredForkTest.cpp -o %t-DeferredForkTest + +RUN: not %run %t-DeferredForkTest 2>&1 | FileCheck %s --check-prefix=DEFERRED_FORK