Index: lib/fuzzer/FuzzerDriver.cpp =================================================================== --- lib/fuzzer/FuzzerDriver.cpp +++ lib/fuzzer/FuzzerDriver.cpp @@ -615,6 +615,12 @@ Options.PrintFinalStats = Flags.print_final_stats; Options.PrintCorpusStats = Flags.print_corpus_stats; Options.PrintCoverage = Flags.print_coverage; + if (Flags.non_deterministic_check && Flags.dump_coverage) { + Printf("Can not use nondeterministic coverage with dump coverage flags\n"); + _Exit(1); + } + if (Flags.non_deterministic_check == 1 || Flags.non_deterministic_check == 2) + Options.NonDetCheck = Flags.non_deterministic_check; Options.DumpCoverage = Flags.dump_coverage; if (Flags.exit_on_src_pos) Options.ExitOnSrcPos = Flags.exit_on_src_pos; Index: lib/fuzzer/FuzzerFlags.def =================================================================== --- lib/fuzzer/FuzzerFlags.def +++ lib/fuzzer/FuzzerFlags.def @@ -110,6 +110,12 @@ FUZZER_FLAG_INT(dump_coverage, 0, "Deprecated." " If 1, dump coverage information as a" " .sancov file at exit.") +FUZZER_FLAG_INT(non_deterministic_check, 0,"Experimental." + " Is not compatible with dump_coverage flag." + " Does 3 runs in total if an UniqFeature is found in run 1" + " If 1, compares all 3 runs for nondeterministic edges" + " If 2, compares run 2 and 3 only taking into account" + " that run 1 is initialization run.") FUZZER_FLAG_INT(handle_segv, 1, "If 1, try to intercept SIGSEGV.") FUZZER_FLAG_INT(handle_bus, 1, "If 1, try to intercept SIGBUS.") FUZZER_FLAG_INT(handle_abrt, 1, "If 1, try to intercept SIGABRT.") Index: lib/fuzzer/FuzzerLoop.cpp =================================================================== --- lib/fuzzer/FuzzerLoop.cpp +++ lib/fuzzer/FuzzerLoop.cpp @@ -351,6 +351,8 @@ void Fuzzer::PrintFinalStats() { if (Options.PrintCoverage) TPC.PrintCoverage(); + if (Options.NonDetCheck) + TPC.DumpNonDetCoverage(Options.NonDetCheck); if (Options.DumpCoverage) TPC.DumpCoverage(); if (Options.PrintCorpusStats) @@ -465,6 +467,34 @@ *FoundUniqFeatures = FoundUniqFeaturesOfII; PrintPulseAndReportSlowInput(Data, Size); size_t NumNewFeatures = Corpus.NumFeatureUpdates() - NumUpdatesBefore; + + if (NumNewFeatures && Options.NonDetCheck) { + for (size_t idx = 0, N = TPC.GetNumPCs(); idx < N; idx++) + if (TPC.CountersNonDet()[idx] != -1) + TPC.CountersNonDet()[idx] = TPC.Counters()[idx]; + + for(size_t run=0; run < 2; run++) { + TPC.ResetMaps(); + CB(Data, Size); + + for (size_t idx = 0, N = TPC.GetNumPCs(); idx < N; idx++) { + if (Options.NonDetCheck == 1) { + if (TPC.CountersNonDet()[idx] != TPC.Counters()[idx]) + TPC.CountersNonDet()[idx] = -1; + } else { + if (run == 0) { + if (TPC.CountersNonDetInitialized()[idx] != -1) + TPC.CountersNonDetInitialized()[idx] = TPC.Counters()[idx]; + } else { + if (TPC.CountersNonDetInitialized()[idx] != TPC.Counters()[idx]) + TPC.CountersNonDetInitialized()[idx] = -1; + } + } + } + } + } + + if (NumNewFeatures) { TPC.UpdateObservedPCs(); Corpus.AddToCorpus({Data, Data + Size}, NumNewFeatures, MayDeleteFile, Index: lib/fuzzer/FuzzerOptions.h =================================================================== --- lib/fuzzer/FuzzerOptions.h +++ lib/fuzzer/FuzzerOptions.h @@ -55,6 +55,7 @@ bool PrintCorpusStats = false; bool PrintCoverage = false; bool DumpCoverage = false; + int NonDetCheck = 0; bool DetectLeaks = true; int PurgeAllocatorIntervalSec = 1; int TraceMalloc = 0; Index: lib/fuzzer/FuzzerTracePC.h =================================================================== --- lib/fuzzer/FuzzerTracePC.h +++ lib/fuzzer/FuzzerTracePC.h @@ -103,6 +103,7 @@ void PrintCoverage(); void DumpCoverage(); + void DumpNonDetCoverage(uint8_t check); template void IterateCoveredFunctions(CallBack CB); @@ -134,6 +135,11 @@ void SetFocusFunction(const std::string &FuncName); bool ObservedFocusFunction(); + + uintptr_t *PCs() const; + uint8_t *Counters() const; + int8_t *CountersNonDet() const; + int8_t *CountersNonDetInitialized() const; private: bool UseCounters = false; @@ -161,9 +167,6 @@ size_t NumPCTables; size_t NumPCsInPCTables; - uint8_t *Counters() const; - uintptr_t *PCs() const; - Set ObservedPCs; Set ObservedFuncs; Index: lib/fuzzer/FuzzerTracePC.cpp =================================================================== --- lib/fuzzer/FuzzerTracePC.cpp +++ lib/fuzzer/FuzzerTracePC.cpp @@ -31,6 +31,10 @@ ATTRIBUTE_INTERFACE uintptr_t __sancov_trace_pc_pcs[fuzzer::TracePC::kNumPCs]; +int8_t __sancov_trace_pc_nondeterministic_counters[fuzzer::TracePC::kNumPCs]; + +int8_t __sancov_trace_pc_nondeterministic_counters_initialized[fuzzer::TracePC::kNumPCs]; + // Used by -fsanitize-coverage=stack-depth to track stack depth ATTRIBUTE_INTERFACE __attribute__((tls_model("initial-exec"))) thread_local uintptr_t __sancov_lowest_stack; @@ -49,6 +53,14 @@ return __sancov_trace_pc_pcs; } +int8_t *TracePC::CountersNonDet() const { + return __sancov_trace_pc_nondeterministic_counters; +} + +int8_t *TracePC::CountersNonDetInitialized() const { + return __sancov_trace_pc_nondeterministic_counters_initialized; +} + size_t TracePC::GetTotalPCCoverage() { if (ObservedPCs.size()) return ObservedPCs.size(); @@ -307,6 +319,21 @@ } } +void TracePC::DumpNonDetCoverage(uint8_t check) { + if (EF->__sanitizer_dump_coverage) { + Vector PCsCopy(GetNumPCs()); + for (size_t i = 0; i < GetNumPCs(); i++) { + if (check == 1) { + PCsCopy[i] = (CountersNonDet()[i] == -1)? GetPreviousInstructionPc(PCs()[i]) : 0; + } else { + if (check == 2) + PCsCopy[i] = (CountersNonDetInitialized()[i] == -1)? GetPreviousInstructionPc(PCs()[i]) : 0; + } + } + EF->__sanitizer_dump_coverage(PCsCopy.data(), PCsCopy.size()); + } +} + // Value profile. // We keep track of various values that affect control flow. // These values are inserted into a bit-set-based hash map.