Skip to content

Commit 0fe4701

Browse files
committed
Expand unit tests for fuzzer::Merger
This change adds additional unit tests for fuzzer::Merger::Parse and fuzzer::Merger::Merge in anticipation of additional changes to the merge control file format to support cross-process fuzzing. It modifies the parameter handling of Merge slightly in order to make NewFeatures and NewCov consistent with NewFiles; namely, Merge *replaces* the contents of these output parameters rather than accumulating them (thereby fixing a buggy return value). This is change 1 of (at least) 18 for cross-process fuzzing support. Reviewed By: morehouse Differential Revision: https://reviews.llvm.org/D94506
1 parent aa4e466 commit 0fe4701

File tree

3 files changed

+212
-111
lines changed

3 files changed

+212
-111
lines changed

compiler-rt/lib/fuzzer/FuzzerFork.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -314,8 +314,11 @@ void FuzzWithFork(Random &Rand, const FuzzingOptions &Options,
314314
Env.Files.push_back(File.File);
315315
} else {
316316
auto CFPath = DirPlusFile(Env.TempDir, "merge.txt");
317-
CrashResistantMerge(Env.Args, {}, SeedFiles, &Env.Files, {}, &Env.Features,
318-
{}, &Env.Cov, CFPath, false);
317+
Set<uint32_t> NewFeatures, NewCov;
318+
CrashResistantMerge(Env.Args, {}, SeedFiles, &Env.Files, Env.Features,
319+
&NewFeatures, Env.Cov, &NewCov, CFPath, false);
320+
Env.Features.insert(NewFeatures.begin(), NewFeatures.end());
321+
Env.Cov.insert(NewFeatures.begin(), NewFeatures.end());
319322
RemoveFile(CFPath);
320323
}
321324
Printf("INFO: -fork=%d: %zd seed inputs, starting to fuzz in %s\n", NumJobs,

compiler-rt/lib/fuzzer/FuzzerMerge.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,8 @@ size_t Merger::Merge(const Set<uint32_t> &InitialFeatures,
137137
const Set<uint32_t> &InitialCov, Set<uint32_t> *NewCov,
138138
Vector<std::string> *NewFiles) {
139139
NewFiles->clear();
140+
NewFeatures->clear();
141+
NewCov->clear();
140142
assert(NumFilesInFirstCorpus <= Files.size());
141143
Set<uint32_t> AllFeatures = InitialFeatures;
142144

compiler-rt/lib/fuzzer/tests/FuzzerUnittest.cpp

Lines changed: 205 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -614,73 +614,80 @@ TEST(Corpus, Distribution) {
614614
}
615615
}
616616

617-
TEST(Merge, Bad) {
618-
const char *kInvalidInputs[] = {
619-
"",
620-
"x",
621-
"3\nx",
622-
"2\n3",
623-
"2\n2",
624-
"2\n2\nA\n",
625-
"2\n2\nA\nB\nC\n",
626-
"0\n0\n",
627-
"1\n1\nA\nFT 0",
628-
"1\n1\nA\nSTARTED 1",
629-
};
630-
Merger M;
631-
for (auto S : kInvalidInputs) {
632-
// fprintf(stderr, "TESTING:\n%s\n", S);
633-
EXPECT_FALSE(M.Parse(S, false));
634-
}
617+
template <typename T> void EQ(const Vector<T> &A, const Vector<T> &B) {
618+
EXPECT_EQ(A, B);
635619
}
636620

637-
void EQ(const Vector<uint32_t> &A, const Vector<uint32_t> &B) {
638-
EXPECT_EQ(A, B);
621+
template <typename T> void EQ(const Set<T> &A, const Vector<T> &B) {
622+
EXPECT_EQ(A, Set<T>(B.begin(), B.end()));
639623
}
640624

641-
void EQ(const Vector<std::string> &A, const Vector<std::string> &B) {
642-
Set<std::string> a(A.begin(), A.end());
625+
void EQ(const Vector<MergeFileInfo> &A, const Vector<std::string> &B) {
626+
Set<std::string> a;
627+
for (const auto &File : A)
628+
a.insert(File.Name);
643629
Set<std::string> b(B.begin(), B.end());
644630
EXPECT_EQ(a, b);
645631
}
646632

647-
static void Merge(const std::string &Input,
648-
const Vector<std::string> Result,
649-
size_t NumNewFeatures) {
650-
Merger M;
651-
Vector<std::string> NewFiles;
652-
Set<uint32_t> NewFeatures, NewCov;
653-
EXPECT_TRUE(M.Parse(Input, true));
654-
EXPECT_EQ(NumNewFeatures, M.Merge({}, &NewFeatures, {}, &NewCov, &NewFiles));
655-
EQ(NewFiles, Result);
656-
}
633+
#define TRACED_EQ(A, ...) \
634+
{ \
635+
SCOPED_TRACE(#A); \
636+
EQ(A, __VA_ARGS__); \
637+
}
657638

658-
TEST(Merge, Good) {
639+
TEST(Merger, Parse) {
659640
Merger M;
660641

642+
const char *kInvalidInputs[] = {
643+
// Bad file numbers
644+
"",
645+
"x",
646+
"0\n0",
647+
"3\nx",
648+
"2\n3",
649+
"2\n2",
650+
// Bad file names
651+
"2\n2\nA\n",
652+
"2\n2\nA\nB\nC\n",
653+
// Unknown markers
654+
"2\n1\nA\nSTARTED 0\nBAD 0 0x0",
655+
// Bad file IDs
656+
"1\n1\nA\nSTARTED 1",
657+
"2\n1\nA\nSTARTED 0\nFT 1 0x0",
658+
};
659+
for (auto S : kInvalidInputs) {
660+
SCOPED_TRACE(S);
661+
EXPECT_FALSE(M.Parse(S, false));
662+
}
663+
664+
// Parse initial control file
661665
EXPECT_TRUE(M.Parse("1\n0\nAA\n", false));
662-
EXPECT_EQ(M.Files.size(), 1U);
666+
ASSERT_EQ(M.Files.size(), 1U);
663667
EXPECT_EQ(M.NumFilesInFirstCorpus, 0U);
664668
EXPECT_EQ(M.Files[0].Name, "AA");
665669
EXPECT_TRUE(M.LastFailure.empty());
666670
EXPECT_EQ(M.FirstNotProcessedFile, 0U);
667671

672+
// Parse control file that failed on first attempt
668673
EXPECT_TRUE(M.Parse("2\n1\nAA\nBB\nSTARTED 0 42\n", false));
669-
EXPECT_EQ(M.Files.size(), 2U);
674+
ASSERT_EQ(M.Files.size(), 2U);
670675
EXPECT_EQ(M.NumFilesInFirstCorpus, 1U);
671676
EXPECT_EQ(M.Files[0].Name, "AA");
672677
EXPECT_EQ(M.Files[1].Name, "BB");
673678
EXPECT_EQ(M.LastFailure, "AA");
674679
EXPECT_EQ(M.FirstNotProcessedFile, 1U);
675680

681+
// Parse control file that failed on later attempt
676682
EXPECT_TRUE(M.Parse("3\n1\nAA\nBB\nC\n"
677-
"STARTED 0 1000\n"
678-
"FT 0 1 2 3\n"
679-
"STARTED 1 1001\n"
680-
"FT 1 4 5 6 \n"
681-
"STARTED 2 1002\n"
682-
"", true));
683-
EXPECT_EQ(M.Files.size(), 3U);
683+
"STARTED 0 1000\n"
684+
"FT 0 1 2 3\n"
685+
"STARTED 1 1001\n"
686+
"FT 1 4 5 6 \n"
687+
"STARTED 2 1002\n"
688+
"",
689+
true));
690+
ASSERT_EQ(M.Files.size(), 3U);
684691
EXPECT_EQ(M.NumFilesInFirstCorpus, 1U);
685692
EXPECT_EQ(M.Files[0].Name, "AA");
686693
EXPECT_EQ(M.Files[0].Size, 1000U);
@@ -690,83 +697,172 @@ TEST(Merge, Good) {
690697
EXPECT_EQ(M.Files[2].Size, 1002U);
691698
EXPECT_EQ(M.LastFailure, "C");
692699
EXPECT_EQ(M.FirstNotProcessedFile, 3U);
693-
EQ(M.Files[0].Features, {1, 2, 3});
694-
EQ(M.Files[1].Features, {4, 5, 6});
695-
696-
697-
Vector<std::string> NewFiles;
698-
Set<uint32_t> NewFeatures, NewCov;
700+
TRACED_EQ(M.Files[0].Features, {1, 2, 3});
701+
TRACED_EQ(M.Files[1].Features, {4, 5, 6});
702+
703+
// Parse control file without features or PCs
704+
EXPECT_TRUE(M.Parse("2\n0\nAA\nBB\n"
705+
"STARTED 0 1000\n"
706+
"FT 0\n"
707+
"COV 0\n"
708+
"STARTED 1 1001\n"
709+
"FT 1\n"
710+
"COV 1\n"
711+
"",
712+
true));
713+
ASSERT_EQ(M.Files.size(), 2U);
714+
EXPECT_EQ(M.NumFilesInFirstCorpus, 0U);
715+
EXPECT_TRUE(M.LastFailure.empty());
716+
EXPECT_EQ(M.FirstNotProcessedFile, 2U);
717+
EXPECT_TRUE(M.Files[0].Features.empty());
718+
EXPECT_TRUE(M.Files[0].Cov.empty());
719+
EXPECT_TRUE(M.Files[1].Features.empty());
720+
EXPECT_TRUE(M.Files[1].Cov.empty());
699721

722+
// Parse features and PCs
700723
EXPECT_TRUE(M.Parse("3\n2\nAA\nBB\nC\n"
701-
"STARTED 0 1000\nFT 0 1 2 3\n"
702-
"STARTED 1 1001\nFT 1 4 5 6 \n"
703-
"STARTED 2 1002\nFT 2 6 1 3 \n"
704-
"", true));
705-
EXPECT_EQ(M.Files.size(), 3U);
724+
"STARTED 0 1000\n"
725+
"FT 0 1 2 3\n"
726+
"COV 0 11 12 13\n"
727+
"STARTED 1 1001\n"
728+
"FT 1 4 5 6\n"
729+
"COV 1 7 8 9\n"
730+
"STARTED 2 1002\n"
731+
"FT 2 6 1 3\n"
732+
"COV 2 16 11 13\n"
733+
"",
734+
true));
735+
ASSERT_EQ(M.Files.size(), 3U);
706736
EXPECT_EQ(M.NumFilesInFirstCorpus, 2U);
707737
EXPECT_TRUE(M.LastFailure.empty());
708738
EXPECT_EQ(M.FirstNotProcessedFile, 3U);
709-
EQ(M.Files[0].Features, {1, 2, 3});
710-
EQ(M.Files[1].Features, {4, 5, 6});
711-
EQ(M.Files[2].Features, {1, 3, 6});
712-
EXPECT_EQ(0U, M.Merge({}, &NewFeatures, {}, &NewCov, &NewFiles));
713-
EQ(NewFiles, {});
739+
TRACED_EQ(M.Files[0].Features, {1, 2, 3});
740+
TRACED_EQ(M.Files[0].Cov, {11, 12, 13});
741+
TRACED_EQ(M.Files[1].Features, {4, 5, 6});
742+
TRACED_EQ(M.Files[1].Cov, {7, 8, 9});
743+
TRACED_EQ(M.Files[2].Features, {1, 3, 6});
744+
TRACED_EQ(M.Files[2].Cov, {16});
745+
}
746+
747+
TEST(Merger, Merge) {
748+
Merger M;
749+
Set<uint32_t> Features, NewFeatures;
750+
Set<uint32_t> Cov, NewCov;
751+
Vector<std::string> NewFiles;
714752

753+
// Adds new files and features
754+
EXPECT_TRUE(M.Parse("3\n0\nA\nB\nC\n"
755+
"STARTED 0 1000\n"
756+
"FT 0 1 2 3\n"
757+
"STARTED 1 1001\n"
758+
"FT 1 4 5 6 \n"
759+
"STARTED 2 1002\n"
760+
"FT 2 6 1 3\n"
761+
"",
762+
true));
763+
EXPECT_EQ(M.Merge(Features, &NewFeatures, Cov, &NewCov, &NewFiles), 6U);
764+
TRACED_EQ(M.Files, {"A", "B", "C"});
765+
TRACED_EQ(NewFiles, {"A", "B"});
766+
TRACED_EQ(NewFeatures, {1, 2, 3, 4, 5, 6});
767+
768+
// Doesn't return features or files in the initial corpus.
715769
EXPECT_TRUE(M.Parse("3\n1\nA\nB\nC\n"
716-
"STARTED 0 1000\nFT 0 1 2 3\n"
717-
"STARTED 1 1001\nFT 1 4 5 6 \n"
718-
"STARTED 2 1002\nFT 2 6 1 3\n"
719-
"", true));
720-
EQ(M.Files[0].Features, {1, 2, 3});
721-
EQ(M.Files[1].Features, {4, 5, 6});
722-
EQ(M.Files[2].Features, {1, 3, 6});
723-
EXPECT_EQ(3U, M.Merge({}, &NewFeatures, {}, &NewCov, &NewFiles));
724-
EQ(NewFiles, {"B"});
725-
726-
// Same as the above, but with InitialFeatures.
727-
EXPECT_TRUE(M.Parse("2\n0\nB\nC\n"
728-
"STARTED 0 1001\nFT 0 4 5 6 \n"
729-
"STARTED 1 1002\nFT 1 6 1 3\n"
730-
"", true));
731-
EQ(M.Files[0].Features, {4, 5, 6});
732-
EQ(M.Files[1].Features, {1, 3, 6});
733-
Set<uint32_t> InitialFeatures;
734-
InitialFeatures.insert(1);
735-
InitialFeatures.insert(2);
736-
InitialFeatures.insert(3);
737-
EXPECT_EQ(3U, M.Merge(InitialFeatures, &NewFeatures, {}, &NewCov, &NewFiles));
738-
EQ(NewFiles, {"B"});
739-
}
770+
"STARTED 0 1000\n"
771+
"FT 0 1 2 3\n"
772+
"STARTED 1 1001\n"
773+
"FT 1 4 5 6 \n"
774+
"STARTED 2 1002\n"
775+
"FT 2 6 1 3\n"
776+
"",
777+
true));
778+
EXPECT_EQ(M.Merge(Features, &NewFeatures, Cov, &NewCov, &NewFiles), 3U);
779+
TRACED_EQ(M.Files, {"A", "B", "C"});
780+
TRACED_EQ(NewFiles, {"B"});
781+
TRACED_EQ(NewFeatures, {4, 5, 6});
782+
783+
// No new features, so no new files
784+
EXPECT_TRUE(M.Parse("3\n2\nA\nB\nC\n"
785+
"STARTED 0 1000\n"
786+
"FT 0 1 2 3\n"
787+
"STARTED 1 1001\n"
788+
"FT 1 4 5 6 \n"
789+
"STARTED 2 1002\n"
790+
"FT 2 6 1 3\n"
791+
"",
792+
true));
793+
EXPECT_EQ(M.Merge(Features, &NewFeatures, Cov, &NewCov, &NewFiles), 0U);
794+
TRACED_EQ(M.Files, {"A", "B", "C"});
795+
TRACED_EQ(NewFiles, {});
796+
TRACED_EQ(NewFeatures, {});
797+
798+
// Can pass initial features and coverage.
799+
Features = {1, 2, 3};
800+
Cov = {};
801+
EXPECT_TRUE(M.Parse("2\n0\nA\nB\n"
802+
"STARTED 0 1000\n"
803+
"FT 0 1 2 3\n"
804+
"STARTED 1 1001\n"
805+
"FT 1 4 5 6\n"
806+
"",
807+
true));
808+
EXPECT_EQ(M.Merge(Features, &NewFeatures, Cov, &NewCov, &NewFiles), 3U);
809+
TRACED_EQ(M.Files, {"A", "B"});
810+
TRACED_EQ(NewFiles, {"B"});
811+
TRACED_EQ(NewFeatures, {4, 5, 6});
812+
Features.clear();
813+
Cov.clear();
740814

741-
TEST(Merge, Merge) {
742-
743-
Merge("3\n1\nA\nB\nC\n"
744-
"STARTED 0 1000\nFT 0 1 2 3\n"
745-
"STARTED 1 1001\nFT 1 4 5 6 \n"
746-
"STARTED 2 1002\nFT 2 6 1 3 \n",
747-
{"B"}, 3);
748-
749-
Merge("3\n0\nA\nB\nC\n"
750-
"STARTED 0 2000\nFT 0 1 2 3\n"
751-
"STARTED 1 1001\nFT 1 4 5 6 \n"
752-
"STARTED 2 1002\nFT 2 6 1 3 \n",
753-
{"A", "B", "C"}, 6);
754-
755-
Merge("4\n0\nA\nB\nC\nD\n"
756-
"STARTED 0 2000\nFT 0 1 2 3\n"
757-
"STARTED 1 1101\nFT 1 4 5 6 \n"
758-
"STARTED 2 1102\nFT 2 6 1 3 100 \n"
759-
"STARTED 3 1000\nFT 3 1 \n",
760-
{"A", "B", "C", "D"}, 7);
761-
762-
Merge("4\n1\nA\nB\nC\nD\n"
763-
"STARTED 0 2000\nFT 0 4 5 6 7 8\n"
764-
"STARTED 1 1100\nFT 1 1 2 3 \n"
765-
"STARTED 2 1100\nFT 2 2 3 \n"
766-
"STARTED 3 1000\nFT 3 1 \n",
767-
{"B", "D"}, 3);
815+
// Parse smaller files first
816+
EXPECT_TRUE(M.Parse("3\n0\nA\nB\nC\n"
817+
"STARTED 0 2000\n"
818+
"FT 0 1 2 3\n"
819+
"STARTED 1 1001\n"
820+
"FT 1 4 5 6 \n"
821+
"STARTED 2 1002\n"
822+
"FT 2 6 1 3 \n"
823+
"",
824+
true));
825+
EXPECT_EQ(M.Merge(Features, &NewFeatures, Cov, &NewCov, &NewFiles), 6U);
826+
TRACED_EQ(M.Files, {"B", "C", "A"});
827+
TRACED_EQ(NewFiles, {"B", "C", "A"});
828+
TRACED_EQ(NewFeatures, {1, 2, 3, 4, 5, 6});
829+
830+
EXPECT_TRUE(M.Parse("4\n0\nA\nB\nC\nD\n"
831+
"STARTED 0 2000\n"
832+
"FT 0 1 2 3\n"
833+
"STARTED 1 1101\n"
834+
"FT 1 4 5 6 \n"
835+
"STARTED 2 1102\n"
836+
"FT 2 6 1 3 100 \n"
837+
"STARTED 3 1000\n"
838+
"FT 3 1 \n"
839+
"",
840+
true));
841+
EXPECT_EQ(M.Merge(Features, &NewFeatures, Cov, &NewCov, &NewFiles), 7U);
842+
TRACED_EQ(M.Files, {"A", "B", "C", "D"});
843+
TRACED_EQ(NewFiles, {"D", "B", "C", "A"});
844+
TRACED_EQ(NewFeatures, {1, 2, 3, 4, 5, 6, 100});
845+
846+
// For same sized file, parse more features first
847+
EXPECT_TRUE(M.Parse("4\n1\nA\nB\nC\nD\n"
848+
"STARTED 0 2000\n"
849+
"FT 0 4 5 6 7 8\n"
850+
"STARTED 1 1100\n"
851+
"FT 1 1 2 3 \n"
852+
"STARTED 2 1100\n"
853+
"FT 2 2 3 \n"
854+
"STARTED 3 1000\n"
855+
"FT 3 1 \n"
856+
"",
857+
true));
858+
EXPECT_EQ(M.Merge(Features, &NewFeatures, Cov, &NewCov, &NewFiles), 3U);
859+
TRACED_EQ(M.Files, {"A", "B", "C", "D"});
860+
TRACED_EQ(NewFiles, {"D", "B"});
861+
TRACED_EQ(NewFeatures, {1, 2, 3});
768862
}
769863

864+
#undef TRACED_EQ
865+
770866
TEST(DFT, BlockCoverage) {
771867
BlockCoverage Cov;
772868
// Assuming C0 has 5 instrumented blocks,

0 commit comments

Comments
 (0)