[libFuzzer] Experimenting with dictionary minimization.

Summary:
Tracking issue: https://github.com/google/oss-fuzz/issues/331


Reviewers: kcc

Reviewed By: kcc

Differential Revision: https://reviews.llvm.org/D30940

llvm-svn: 298031
This commit is contained in:
Kostya Serebryany 2017-03-17 01:40:09 +00:00
parent a52c8d0daf
commit f7e610eda1
2 changed files with 82 additions and 0 deletions

View File

@ -355,6 +355,74 @@ int MinimizeCrashInputInternalStep(Fuzzer *F, InputCorpus *Corpus) {
return 0;
}
int AnalyzeDictionary(Fuzzer *F, const std::vector<Unit>& Dict,
UnitVector& Corpus) {
Printf("Started dictionary minimization (up to %d tests)\n",
Dict.size() * Corpus.size() * 2);
// Scores and usage count for each dictionary unit.
std::vector<int> Scores(Dict.size());
std::vector<int> Usages(Dict.size());
std::vector<size_t> InitialFeatures;
std::vector<size_t> ModifiedFeatures;
for (auto &C : Corpus) {
// Get coverage for the testcase without modifications.
F->ExecuteCallback(C.data(), C.size());
InitialFeatures.clear();
TPC.CollectFeatures([&](size_t Feature) -> bool {
InitialFeatures.push_back(Feature);
return true;
});
for (size_t i = 0; i < Dict.size(); ++i) {
auto Data = C;
auto StartPos = std::search(Data.begin(), Data.end(),
Dict[i].begin(), Dict[i].end());
// Skip dictionary unit, if the testcase does not contain it.
if (StartPos == Data.end())
continue;
++Usages[i];
while (StartPos != Data.end()) {
// Replace all occurrences of dictionary unit in the testcase.
auto EndPos = StartPos + Dict[i].size();
for (auto It = StartPos; It != EndPos; ++It)
*It ^= 0xFF;
StartPos = std::search(EndPos, Data.end(),
Dict[i].begin(), Dict[i].end());
}
// Get coverage for testcase with masked occurrences of dictionary unit.
F->ExecuteCallback(Data.data(), Data.size());
ModifiedFeatures.clear();
TPC.CollectFeatures([&](size_t Feature) -> bool {
ModifiedFeatures.push_back(Feature);
return true;
});
if (InitialFeatures == ModifiedFeatures)
--Scores[i];
else
Scores[i] += 2;
}
}
Printf("###### Useless dictionary elements. ######\n");
for (size_t i = 0; i < Dict.size(); ++i) {
// Dictionary units with positive score are treated as useful ones.
if (Scores[i] > 0)
continue;
Printf("\"");
PrintASCII(Dict[i].data(), Dict[i].size(), "\"");
Printf(" # Score: %d, Used: %d\n", Scores[i], Usages[i]);
}
Printf("###### End of useless dictionary elements. ######\n");
return 0;
}
int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) {
using namespace fuzzer;
assert(argc && argv && "Argument pointers cannot be nullptr");
@ -546,6 +614,19 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) {
TemporaryMaxLen, /*ExitOnError=*/false);
}
if (Flags.analyze_dict) {
if (Dictionary.empty() || Inputs->empty()) {
Printf("ERROR: can't analyze dict without dict and corpus provided\n");
return 1;
}
if (AnalyzeDictionary(F, Dictionary, InitialCorpus)) {
Printf("Dictionary analysis failed\n");
exit(1);
}
Printf("Dictionary analysis suceeded\n");
exit(0);
}
if (Options.MaxLen == 0) {
size_t MaxLen = 0;
for (auto &U : InitialCorpus)

View File

@ -108,6 +108,7 @@ FUZZER_FLAG_STRING(exit_on_item, "Exit if an item with a given sha1 sum"
FUZZER_FLAG_STRING(run_equivalence_server, "Experimental")
FUZZER_FLAG_STRING(use_equivalence_server, "Experimental")
FUZZER_FLAG_INT(analyze_dict, 0, "Experimental")
FUZZER_DEPRECATED_FLAG(exit_on_first)
FUZZER_DEPRECATED_FLAG(save_minimized_corpus)