Skip to content

Commit b008fd4

Browse files
author
Ivan Krasin
committedJan 22, 2016
Use std::piecewise_constant_distribution instead of ad-hoc binary search.
Summary: Fix the issue with the most recently discovered unit receiving much less attention. Note: I had to change the seed for one test to make it pass. Alternatively, the number of runs could be increased. I believe that the average time of 'foo' discovery is not increased, just seed=1 was particularly convenient for the previous PRNG scheme used. Reviewers: aizatsky, kcc Subscribers: llvm-commits, kcc Differential Revision: http://reviews.llvm.org/D16419 llvm-svn: 258473
1 parent 1423921 commit b008fd4

File tree

4 files changed

+57
-17
lines changed

4 files changed

+57
-17
lines changed
 

‎llvm/lib/Fuzzer/FuzzerInterface.h

+12
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,18 @@ class FuzzerRandomBase {
6666
// Return a random number in range [0,n).
6767
size_t operator()(size_t n) { return n ? Rand() % n : 0; }
6868
bool RandBool() { return Rand() % 2; }
69+
70+
// The methods below is to satisfy UniformRandomNumberGenerator:
71+
// http://en.cppreference.com/w/cpp/concept/UniformRandomNumberGenerator\
72+
73+
// Returns a random number between 0 and RAND_MAX inclusive.
74+
double operator()() { return operator()(RAND_MAX); }
75+
76+
// Returns the smallest value that operator() may return.
77+
double min() { return 0; }
78+
79+
// Returns the largest value that operator() may return.
80+
double max() { return RAND_MAX; }
6981
};
7082

7183
// Using libc's stand/rand.

‎llvm/lib/Fuzzer/FuzzerInternal.h

+6-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include <chrono>
1818
#include <cstddef>
1919
#include <cstdlib>
20+
#include <random>
2021
#include <string>
2122
#include <string.h>
2223
#include <vector>
@@ -200,7 +201,7 @@ class Fuzzer {
200201
bool PrintNewCovPcs = false;
201202
};
202203
Fuzzer(UserSuppliedFuzzer &USF, FuzzingOptions Options);
203-
void AddToCorpus(const Unit &U) { Corpus.push_back(U); }
204+
void AddToCorpus(const Unit &U) { Corpus.push_back(U); UpdateCorpusDistribution(); }
204205
size_t ChooseUnitIdxToMutate();
205206
const Unit &ChooseUnitToMutate() { return Corpus[ChooseUnitIdxToMutate()]; };
206207
void Loop();
@@ -241,6 +242,9 @@ class Fuzzer {
241242
void WriteUnitToFileWithPrefix(const Unit &U, const char *Prefix);
242243
void PrintStats(const char *Where, const char *End = "\n");
243244
void PrintStatusForNewUnit(const Unit &U);
245+
// Updates the probability distribution for the units in the corpus.
246+
// Must be called whenever the corpus or unit weights are changed.
247+
void UpdateCorpusDistribution();
244248

245249
void SyncCorpus();
246250

@@ -280,6 +284,7 @@ class Fuzzer {
280284
return Res;
281285
}
282286

287+
std::piecewise_constant_distribution<double> CorpusDistribution;
283288
UserSuppliedFuzzer &USF;
284289
FuzzingOptions Options;
285290
system_clock::time_point ProcessStartTime = system_clock::now();

‎llvm/lib/Fuzzer/FuzzerLoop.cpp

+18-15
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ void Fuzzer::RereadOutputCorpus() {
163163
if (UnitHashesAddedToCorpus.insert(Hash(X)).second) {
164164
if (RunOne(X)) {
165165
Corpus.push_back(X);
166+
UpdateCorpusDistribution();
166167
PrintStats("RELOAD");
167168
}
168169
}
@@ -200,6 +201,7 @@ void Fuzzer::ShuffleAndMinimize() {
200201
}
201202
}
202203
Corpus = NewCorpus;
204+
UpdateCorpusDistribution();
203205
for (auto &X : Corpus)
204206
UnitHashesAddedToCorpus.insert(Hash(X));
205207
PrintStats("INITED");
@@ -347,6 +349,7 @@ void Fuzzer::PrintStatusForNewUnit(const Unit &U) {
347349

348350
void Fuzzer::ReportNewCoverage(const Unit &U) {
349351
Corpus.push_back(U);
352+
UpdateCorpusDistribution();
350353
UnitHashesAddedToCorpus.insert(Hash(U));
351354
USF.GetMD().RecordSuccessfulMutationSequence();
352355
PrintStatusForNewUnit(U);
@@ -409,22 +412,11 @@ void Fuzzer::MutateAndTestOne() {
409412

410413
// Returns an index of random unit from the corpus to mutate.
411414
// Hypothesis: units added to the corpus last are more likely to be interesting.
412-
// This function gives more wieght to the more recent units.
415+
// This function gives more weight to the more recent units.
413416
size_t Fuzzer::ChooseUnitIdxToMutate() {
414-
size_t N = Corpus.size();
415-
size_t Total = (N + 1) * N / 2;
416-
size_t R = USF.GetRand()(Total);
417-
size_t IdxBeg = 0, IdxEnd = N;
418-
// Binary search.
419-
while (IdxEnd - IdxBeg >= 2) {
420-
size_t Idx = IdxBeg + (IdxEnd - IdxBeg) / 2;
421-
if (R > (Idx + 1) * Idx / 2)
422-
IdxBeg = Idx;
423-
else
424-
IdxEnd = Idx;
425-
}
426-
assert(IdxBeg < N);
427-
return IdxBeg;
417+
size_t Idx = static_cast<size_t>(CorpusDistribution(USF.GetRand()));
418+
assert(Idx < Corpus.size());
419+
return Idx;
428420
}
429421

430422
// Experimental search heuristic: drilling.
@@ -447,6 +439,7 @@ void Fuzzer::Drill() {
447439
std::vector<Unit> SavedCorpus;
448440
SavedCorpus.swap(Corpus);
449441
Corpus.push_back(U);
442+
UpdateCorpusDistribution();
450443
assert(Corpus.size() == 1);
451444
RunOne(U);
452445
PrintStats("DRILL ");
@@ -510,4 +503,14 @@ void Fuzzer::SyncCorpus() {
510503
ExecuteCommand(Options.SyncCommand + " " + Options.OutputCorpus);
511504
}
512505

506+
void Fuzzer::UpdateCorpusDistribution() {
507+
size_t N = Corpus.size();
508+
std::vector<double> Intervals(N+1);
509+
std::vector<double> Weights(N);
510+
std::iota(Intervals.begin(), Intervals.end(), 0);
511+
std::iota(Weights.begin(), Weights.end(), 1);
512+
CorpusDistribution = std::piecewise_constant_distribution<double>(
513+
Intervals.begin(), Intervals.end(), Weights.begin());
514+
}
515+
513516
} // namespace fuzzer

‎llvm/lib/Fuzzer/test/FuzzerUnittest.cpp

+21-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ using namespace fuzzer;
66

77
// For now, have LLVMFuzzerTestOneInput just to make it link.
88
// Later we may want to make unittests that actually call LLVMFuzzerTestOneInput.
9-
extern "C" void LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
9+
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
1010
abort();
1111
}
1212

@@ -400,3 +400,23 @@ TEST(FuzzerUtil, Base64) {
400400
EXPECT_EQ("YWJjeHk=", Base64({'a', 'b', 'c', 'x', 'y'}));
401401
EXPECT_EQ("YWJjeHl6", Base64({'a', 'b', 'c', 'x', 'y', 'z'}));
402402
}
403+
404+
TEST(Corpus, Distribution) {
405+
FuzzerRandomLibc Rand(0);
406+
SimpleUserSuppliedFuzzer USF(&Rand, LLVMFuzzerTestOneInput);
407+
Fuzzer::FuzzingOptions Options;
408+
Fuzzer Fuzz(USF, Options);
409+
size_t N = 10;
410+
size_t TriesPerUnit = 1<<20;
411+
for (size_t i = 0; i < N; i++) {
412+
Fuzz.AddToCorpus(Unit{ static_cast<uint8_t>(i) });
413+
}
414+
std::vector<size_t> Hist(N);
415+
for (size_t i = 0; i < N * TriesPerUnit; i++) {
416+
Hist[Fuzz.ChooseUnitIdxToMutate()]++;
417+
}
418+
for (size_t i = 0; i < N; i++) {
419+
// A weak sanity check that every unit gets invoked.
420+
EXPECT_GT(Hist[i], TriesPerUnit / N / 3);
421+
}
422+
}

0 commit comments

Comments
 (0)
Please sign in to comment.