From 853cbadc8fa6c5eaa2c6d2d55a5f10b6a9ab94d4 Mon Sep 17 00:00:00 2001 From: "Alastair F. Donaldson" Date: Tue, 19 Jul 2022 09:31:33 +0000 Subject: [PATCH] Clean regex fuzzer API Introduces a class to clearly identify the interface of the mutation functions used during regex-based fuzzing. Bug: None Change-Id: Ia6d125227cffc1c0b8944764c4e21825fd31d5cd Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/96360 Reviewed-by: Ryan Harrison Kokoro: Kokoro Commit-Queue: Alastair Donaldson --- src/tint/fuzzers/tint_regex_fuzzer/fuzzer.cc | 13 +- .../tint_regex_fuzzer/regex_fuzzer_tests.cc | 102 +++++-- .../fuzzers/tint_regex_fuzzer/wgsl_mutator.cc | 107 ++++--- .../fuzzers/tint_regex_fuzzer/wgsl_mutator.h | 263 +++++++++--------- 4 files changed, 271 insertions(+), 214 deletions(-) diff --git a/src/tint/fuzzers/tint_regex_fuzzer/fuzzer.cc b/src/tint/fuzzers/tint_regex_fuzzer/fuzzer.cc index ac3468489b..9fe2a6f997 100644 --- a/src/tint/fuzzers/tint_regex_fuzzer/fuzzer.cc +++ b/src/tint/fuzzers/tint_regex_fuzzer/fuzzer.cc @@ -64,39 +64,40 @@ extern "C" size_t LLVMFuzzerCustomMutator(uint8_t* data, MutationKind mutation_kind = static_cast( generator.GetUInt32(static_cast(MutationKind::kNumMutationKinds))); + WgslMutator mutator(generator); switch (mutation_kind) { case MutationKind::kSwapIntervals: - if (!SwapRandomIntervals(delimiter, wgsl_code, generator)) { + if (!mutator.SwapRandomIntervals(delimiter, wgsl_code)) { return 0; } break; case MutationKind::kDeleteInterval: - if (!DeleteRandomInterval(delimiter, wgsl_code, generator)) { + if (!mutator.DeleteRandomInterval(delimiter, wgsl_code)) { return 0; } break; case MutationKind::kDuplicateInterval: - if (!DuplicateRandomInterval(delimiter, wgsl_code, generator)) { + if (!mutator.DuplicateRandomInterval(delimiter, wgsl_code)) { return 0; } break; case MutationKind::kReplaceIdentifier: - if (!ReplaceRandomIdentifier(wgsl_code, generator)) { + if (!mutator.ReplaceRandomIdentifier(wgsl_code)) { return 0; } break; case MutationKind::kReplaceLiteral: - if (!ReplaceRandomIntLiteral(wgsl_code, generator)) { + if (!mutator.ReplaceRandomIntLiteral(wgsl_code)) { return 0; } break; case MutationKind::kInsertReturnStatement: - if (!InsertReturnStatement(wgsl_code, generator)) { + if (!mutator.InsertReturnStatement(wgsl_code)) { return 0; } break; diff --git a/src/tint/fuzzers/tint_regex_fuzzer/regex_fuzzer_tests.cc b/src/tint/fuzzers/tint_regex_fuzzer/regex_fuzzer_tests.cc index 891a111057..76856a9281 100644 --- a/src/tint/fuzzers/tint_regex_fuzzer/regex_fuzzer_tests.cc +++ b/src/tint/fuzzers/tint_regex_fuzzer/regex_fuzzer_tests.cc @@ -21,26 +21,44 @@ namespace tint::fuzzers::regex_fuzzer { namespace { +class WgslMutatorTest : public WgslMutator { + public: + explicit WgslMutatorTest(RandomGenerator& generator) : WgslMutator(generator) {} + + using WgslMutator::DeleteInterval; + using WgslMutator::DuplicateInterval; + using WgslMutator::FindClosingBrace; + using WgslMutator::GetFunctionBodyPositions; + using WgslMutator::GetIdentifiers; + using WgslMutator::GetIntLiterals; + using WgslMutator::ReplaceRegion; + using WgslMutator::SwapIntervals; +}; + // Swaps two non-consecutive regions in the edge TEST(SwapRegionsTest, SwapIntervalsEdgeNonConsecutive) { + RandomGenerator generator(0); + WgslMutatorTest mutator(generator); std::string R1 = ";region1;", R2 = ";regionregion2;", R3 = ";regionregionregion3;"; std::string all_regions = R1 + R2 + R3; // this call should swap R1 with R3. - SwapIntervals(0, R1.length(), R1.length() + R2.length(), R3.length(), all_regions); + mutator.SwapIntervals(0, R1.length(), R1.length() + R2.length(), R3.length(), all_regions); ASSERT_EQ(R3 + R2 + R1, all_regions); } // Swaps two non-consecutive regions not in the edge TEST(SwapRegionsTest, SwapIntervalsNonConsecutiveNonEdge) { + RandomGenerator generator(0); + WgslMutatorTest mutator(generator); std::string R1 = ";region1;", R2 = ";regionregion2;", R3 = ";regionregionregion3;", R4 = ";regionregionregionregion4;", R5 = ";regionregionregionregionregion5;"; std::string all_regions = R1 + R2 + R3 + R4 + R5; // this call should swap R2 with R4. - SwapIntervals(R1.length(), R2.length(), R1.length() + R2.length() + R3.length(), R4.length(), - all_regions); + mutator.SwapIntervals(R1.length(), R2.length(), R1.length() + R2.length() + R3.length(), + R4.length(), all_regions); ASSERT_EQ(R1 + R4 + R3 + R2 + R5, all_regions); } @@ -48,12 +66,15 @@ TEST(SwapRegionsTest, SwapIntervalsNonConsecutiveNonEdge) { // Swaps two consecutive regions not in the edge (sorrounded by other // regions) TEST(SwapRegionsTest, SwapIntervalsConsecutiveEdge) { + RandomGenerator generator(0); + WgslMutatorTest mutator(generator); std::string R1 = ";region1;", R2 = ";regionregion2;", R3 = ";regionregionregion3;", R4 = ";regionregionregionregion4;", R5 = ";regionregionregionregionregion5;"; std::string all_regions = R1 + R2 + R3 + R4; // this call should swap R2 with R3. - SwapIntervals(R1.length(), R2.length(), R1.length() + R2.length(), R3.length(), all_regions); + mutator.SwapIntervals(R1.length(), R2.length(), R1.length() + R2.length(), R3.length(), + all_regions); ASSERT_EQ(R1 + R3 + R2 + R4, all_regions); } @@ -61,113 +82,137 @@ TEST(SwapRegionsTest, SwapIntervalsConsecutiveEdge) { // Swaps two consecutive regions not in the edge (not sorrounded by other // regions) TEST(SwapRegionsTest, SwapIntervalsConsecutiveNonEdge) { + RandomGenerator generator(0); + WgslMutatorTest mutator(generator); std::string R1 = ";region1;", R2 = ";regionregion2;", R3 = ";regionregionregion3;", R4 = ";regionregionregionregion4;", R5 = ";regionregionregionregionregion5;"; std::string all_regions = R1 + R2 + R3 + R4 + R5; // this call should swap R4 with R5. - SwapIntervals(R1.length() + R2.length() + R3.length(), R4.length(), - R1.length() + R2.length() + R3.length() + R4.length(), R5.length(), all_regions); + mutator.SwapIntervals(R1.length() + R2.length() + R3.length(), R4.length(), + R1.length() + R2.length() + R3.length() + R4.length(), R5.length(), + all_regions); ASSERT_EQ(R1 + R2 + R3 + R5 + R4, all_regions); } // Deletes the first region. TEST(DeleteRegionTest, DeleteFirstRegion) { + RandomGenerator generator(0); + WgslMutatorTest mutator(generator); std::string R1 = ";region1;", R2 = ";regionregion2;", R3 = ";regionregionregion3;", R4 = ";regionregionregionregion4;", R5 = ";regionregionregionregionregion5;"; std::string all_regions = R1 + R2 + R3 + R4 + R5; // This call should delete R1. - DeleteInterval(0, R1.length(), all_regions); + mutator.DeleteInterval(0, R1.length(), all_regions); ASSERT_EQ(";" + R2 + R3 + R4 + R5, all_regions); } // Deletes the last region. TEST(DeleteRegionTest, DeleteLastRegion) { + RandomGenerator generator(0); + WgslMutatorTest mutator(generator); std::string R1 = ";region1;", R2 = ";regionregion2;", R3 = ";regionregionregion3;", R4 = ";regionregionregionregion4;", R5 = ";regionregionregionregionregion5;"; std::string all_regions = R1 + R2 + R3 + R4 + R5; // This call should delete R5. - DeleteInterval(R1.length() + R2.length() + R3.length() + R4.length(), R5.length(), all_regions); + mutator.DeleteInterval(R1.length() + R2.length() + R3.length() + R4.length(), R5.length(), + all_regions); ASSERT_EQ(R1 + R2 + R3 + R4 + ";", all_regions); } // Deletes the middle region. TEST(DeleteRegionTest, DeleteMiddleRegion) { + RandomGenerator generator(0); + WgslMutatorTest mutator(generator); std::string R1 = ";region1;", R2 = ";regionregion2;", R3 = ";regionregionregion3;", R4 = ";regionregionregionregion4;", R5 = ";regionregionregionregionregion5;"; std::string all_regions = R1 + R2 + R3 + R4 + R5; // This call should delete R3. - DeleteInterval(R1.length() + R2.length(), R3.length(), all_regions); + mutator.DeleteInterval(R1.length() + R2.length(), R3.length(), all_regions); ASSERT_EQ(R1 + R2 + ";" + R4 + R5, all_regions); } TEST(InsertRegionTest, InsertRegionTest1) { + RandomGenerator generator(0); + WgslMutatorTest mutator(generator); std::string R1 = ";region1;", R2 = ";regionregion2;", R3 = ";regionregionregion3;", R4 = ";regionregionregionregion4;", R5 = ";regionregionregionregionregion5;"; std::string all_regions = R1 + R2 + R3 + R4 + R5; // This call should insert R2 after R4. - DuplicateInterval(R1.length(), R2.length(), - R1.length() + R2.length() + R3.length() + R4.length() - 1, all_regions); + mutator.DuplicateInterval(R1.length(), R2.length(), + R1.length() + R2.length() + R3.length() + R4.length() - 1, + all_regions); ASSERT_EQ(R1 + R2 + R3 + R4 + R2.substr(1, R2.size() - 1) + R5, all_regions); } TEST(InsertRegionTest, InsertRegionTest2) { + RandomGenerator generator(0); + WgslMutatorTest mutator(generator); std::string R1 = ";region1;", R2 = ";regionregion2;", R3 = ";regionregionregion3;", R4 = ";regionregionregionregion4;", R5 = ";regionregionregionregionregion5;"; std::string all_regions = R1 + R2 + R3 + R4 + R5; // This call should insert R3 after R1. - DuplicateInterval(R1.length() + R2.length(), R3.length(), R1.length() - 1, all_regions); + mutator.DuplicateInterval(R1.length() + R2.length(), R3.length(), R1.length() - 1, all_regions); ASSERT_EQ(R1 + R3.substr(1, R3.length() - 1) + R2 + R3 + R4 + R5, all_regions); } TEST(InsertRegionTest, InsertRegionTest3) { + RandomGenerator generator(0); + WgslMutatorTest mutator(generator); std::string R1 = ";region1;", R2 = ";regionregion2;", R3 = ";regionregionregion3;", R4 = ";regionregionregionregion4;", R5 = ";regionregionregionregionregion5;"; std::string all_regions = R1 + R2 + R3 + R4 + R5; // This call should insert R2 after R5. - DuplicateInterval(R1.length(), R2.length(), all_regions.length() - 1, all_regions); + mutator.DuplicateInterval(R1.length(), R2.length(), all_regions.length() - 1, all_regions); ASSERT_EQ(R1 + R2 + R3 + R4 + R5 + R2.substr(1, R2.length() - 1), all_regions); } TEST(ReplaceIdentifierTest, ReplaceIdentifierTest1) { + RandomGenerator generator(0); + WgslMutatorTest mutator(generator); std::string R1 = "|region1|", R2 = "; region2;", R3 = "---------region3---------", R4 = "++region4++", R5 = "***region5***"; std::string all_regions = R1 + R2 + R3 + R4 + R5; // Replaces R3 with R1. - ReplaceRegion(0, R1.length(), R1.length() + R2.length(), R3.length(), all_regions); + mutator.ReplaceRegion(0, R1.length(), R1.length() + R2.length(), R3.length(), all_regions); ASSERT_EQ(R1 + R2 + R1 + R4 + R5, all_regions); } TEST(ReplaceIdentifierTest, ReplaceIdentifierTest2) { + RandomGenerator generator(0); + WgslMutatorTest mutator(generator); std::string R1 = "|region1|", R2 = "; region2;", R3 = "---------region3---------", R4 = "++region4++", R5 = "***region5***"; std::string all_regions = R1 + R2 + R3 + R4 + R5; // Replaces R5 with R3. - ReplaceRegion(R1.length() + R2.length(), R3.length(), - R1.length() + R2.length() + R3.length() + R4.length(), R5.length(), all_regions); + mutator.ReplaceRegion(R1.length() + R2.length(), R3.length(), + R1.length() + R2.length() + R3.length() + R4.length(), R5.length(), + all_regions); ASSERT_EQ(R1 + R2 + R3 + R4 + R3, all_regions); } TEST(GetIdentifierTest, GetIdentifierTest1) { + RandomGenerator generator(0); + WgslMutatorTest mutator(generator); std::string wgsl_code = R"(fn clamp_0acf8f() { var res: vec2 = clamp(vec2(), vec2(), vec2()); @@ -187,7 +232,7 @@ TEST(GetIdentifierTest, GetIdentifierTest1) { clamp_0acf8f(); })"; - std::vector> identifiers_pos = GetIdentifiers(wgsl_code); + std::vector> identifiers_pos = mutator.GetIdentifiers(wgsl_code); std::vector> ground_truth = { std::make_pair(3, 12), std::make_pair(28, 3), std::make_pair(37, 4), @@ -204,6 +249,8 @@ TEST(GetIdentifierTest, GetIdentifierTest1) { } TEST(TestGetLiteralsValues, TestGetLiteralsValues1) { + RandomGenerator generator(0); + WgslMutatorTest mutator(generator); std::string wgsl_code = R"(fn clamp_0acf8f() { var res: vec2 = clamp(vec2(), vec2(), vec2()); @@ -227,7 +274,7 @@ TEST(TestGetLiteralsValues, TestGetLiteralsValues1) { foo_1 = 5 + 7; var foo_3 : i32 = -20;)"; - std::vector> literals_pos = GetIntLiterals(wgsl_code); + std::vector> literals_pos = mutator.GetIntLiterals(wgsl_code); std::vector ground_truth = {"3", "10", "5", "7", "-20"}; @@ -241,6 +288,8 @@ TEST(TestGetLiteralsValues, TestGetLiteralsValues1) { } TEST(InsertReturnTest, FindClosingBrace) { + RandomGenerator generator(0); + WgslMutatorTest mutator(generator); std::string wgsl_code = R"(fn clamp_0acf8f() { if(false){ @@ -269,7 +318,7 @@ TEST(InsertReturnTest, FindClosingBrace) { var foo_3 : i32 = -20; )"; size_t opening_bracket_pos = 18; - size_t closing_bracket_pos = FindClosingBrace(opening_bracket_pos, wgsl_code); + size_t closing_bracket_pos = mutator.FindClosingBrace(opening_bracket_pos, wgsl_code); // The -1 is needed since the function body starts after the left bracket. std::string function_body = @@ -286,6 +335,8 @@ TEST(InsertReturnTest, FindClosingBrace) { } TEST(InsertReturnTest, FindClosingBraceFailing) { + RandomGenerator generator(0); + WgslMutatorTest mutator(generator); std::string wgsl_code = R"(fn clamp_0acf8f() { // This comment } causes the test to fail. @@ -314,7 +365,7 @@ TEST(InsertReturnTest, FindClosingBraceFailing) { foo_1 = 5 + 7; var foo_3 : i32 = -20;)"; size_t opening_bracket_pos = 18; - size_t closing_bracket_pos = FindClosingBrace(opening_bracket_pos, wgsl_code); + size_t closing_bracket_pos = mutator.FindClosingBrace(opening_bracket_pos, wgsl_code); // The -1 is needed since the function body starts after the left bracket. std::string function_body = @@ -330,6 +381,8 @@ TEST(InsertReturnTest, FindClosingBraceFailing) { } TEST(TestInsertReturn, TestInsertReturn1) { + RandomGenerator generator(0); + WgslMutatorTest mutator(generator); std::string wgsl_code = R"(fn clamp_0acf8f() { var res: vec2 = clamp(vec2(), vec2(), vec2()); @@ -390,6 +443,8 @@ TEST(TestInsertReturn, TestInsertReturn1) { } TEST(TestInsertReturn, TestFunctionPositions) { + RandomGenerator generator(0); + WgslMutatorTest mutator(generator); std::string wgsl_code = R"(fn clamp_0acf8f() { var res: vec2 = clamp(vec2(), vec2(), vec2()); @@ -418,12 +473,14 @@ TEST(TestInsertReturn, TestFunctionPositions) { foo_1 = 5 + 7; var foo_3 : i32 = -20;)"; - std::vector function_positions = GetFunctionBodyPositions(wgsl_code); + std::vector function_positions = mutator.GetFunctionBodyPositions(wgsl_code); std::vector expected_positions = {180, 586}; ASSERT_EQ(expected_positions, function_positions); } TEST(TestInsertReturn, TestMissingSemicolon) { + RandomGenerator generator(0); + WgslMutatorTest mutator(generator); std::string wgsl_code = R"(fn clamp_0acf8f() { var res: vec2 = clamp(vec2(), vec2(), vec2()) @@ -452,8 +509,7 @@ TEST(TestInsertReturn, TestMissingSemicolon) { foo_1 = 5 + 7; var foo_3 : i32 = -20;)"; - RandomGenerator generator(0); - InsertReturnStatement(wgsl_code, generator); + mutator.InsertReturnStatement(wgsl_code); // No semicolons found in the function's body, so wgsl_code // should remain unchanged. diff --git a/src/tint/fuzzers/tint_regex_fuzzer/wgsl_mutator.cc b/src/tint/fuzzers/tint_regex_fuzzer/wgsl_mutator.cc index 677560bfe5..f525caa701 100644 --- a/src/tint/fuzzers/tint_regex_fuzzer/wgsl_mutator.cc +++ b/src/tint/fuzzers/tint_regex_fuzzer/wgsl_mutator.cc @@ -26,8 +26,10 @@ namespace tint::fuzzers::regex_fuzzer { -std::vector FindDelimiterIndices(const std::string& delimiter, - const std::string& wgsl_code) { +WgslMutator::WgslMutator(RandomGenerator& generator) : generator_(generator) {} + +std::vector WgslMutator::FindDelimiterIndices(const std::string& delimiter, + const std::string& wgsl_code) { std::vector result; for (size_t pos = wgsl_code.find(delimiter, 0); pos != std::string::npos; pos = wgsl_code.find(delimiter, pos + 1)) { @@ -37,7 +39,7 @@ std::vector FindDelimiterIndices(const std::string& delimiter, return result; } -std::vector> GetIdentifiers(const std::string& wgsl_code) { +std::vector> WgslMutator::GetIdentifiers(const std::string& wgsl_code) { std::vector> result; // This regular expression works by looking for a character that @@ -61,7 +63,7 @@ std::vector> GetIdentifiers(const std::string& wgsl_co return result; } -std::vector> GetIntLiterals(const std::string& s) { +std::vector> WgslMutator::GetIntLiterals(const std::string& s) { std::vector> result; // Looks for integer literals in decimal or hexadecimal form. @@ -83,7 +85,7 @@ std::vector> GetIntLiterals(const std::string& s) { return result; } -size_t FindClosingBrace(size_t opening_bracket_pos, const std::string& wgsl_code) { +size_t WgslMutator::FindClosingBrace(size_t opening_bracket_pos, const std::string& wgsl_code) { size_t open_bracket_count = 1; size_t pos = opening_bracket_pos + 1; while (open_bracket_count >= 1 && pos < wgsl_code.size()) { @@ -97,7 +99,7 @@ size_t FindClosingBrace(size_t opening_bracket_pos, const std::string& wgsl_code return (pos == wgsl_code.size() && open_bracket_count >= 1) ? 0 : pos - 1; } -std::vector GetFunctionBodyPositions(const std::string& wgsl_code) { +std::vector WgslMutator::GetFunctionBodyPositions(const std::string& wgsl_code) { // Finds all the functions with a non-void return value. std::regex function_regex("fn.*?->.*?\\{"); std::smatch match; @@ -113,7 +115,7 @@ std::vector GetFunctionBodyPositions(const std::string& wgsl_code) { return result; } -bool InsertReturnStatement(std::string& wgsl_code, RandomGenerator& generator) { +bool WgslMutator::InsertReturnStatement(std::string& wgsl_code) { std::vector function_body_positions = GetFunctionBodyPositions(wgsl_code); // No function was found in wgsl_code. @@ -123,7 +125,7 @@ bool InsertReturnStatement(std::string& wgsl_code, RandomGenerator& generator) { // Pick a random function's opening bracket, find the corresponding closing // bracket, and find a semi-colon within the function body. - size_t left_bracket_pos = generator.GetRandomElement(function_body_positions); + size_t left_bracket_pos = generator_.GetRandomElement(function_body_positions); size_t right_bracket_pos = FindClosingBrace(left_bracket_pos, wgsl_code); @@ -141,14 +143,14 @@ bool InsertReturnStatement(std::string& wgsl_code, RandomGenerator& generator) { return false; } - size_t semicolon_position = generator.GetRandomElement(semicolon_positions); + size_t semicolon_position = generator_.GetRandomElement(semicolon_positions); // Get all identifiers and integer literals to use as potential return values. std::vector> identifiers = GetIdentifiers(wgsl_code); auto return_values = identifiers; std::vector> int_literals = GetIntLiterals(wgsl_code); return_values.insert(return_values.end(), int_literals.begin(), int_literals.end()); - std::pair return_value = generator.GetRandomElement(return_values); + std::pair return_value = generator_.GetRandomElement(return_values); std::string return_statement = "return " + wgsl_code.substr(return_value.first, return_value.second) + ";"; @@ -157,11 +159,11 @@ bool InsertReturnStatement(std::string& wgsl_code, RandomGenerator& generator) { return true; } -void SwapIntervals(size_t idx1, - size_t reg1_len, - size_t idx2, - size_t reg2_len, - std::string& wgsl_code) { +void WgslMutator::SwapIntervals(size_t idx1, + size_t reg1_len, + size_t idx2, + size_t reg2_len, + std::string& wgsl_code) { std::string region_1 = wgsl_code.substr(idx1 + 1, reg1_len - 1); std::string region_2 = wgsl_code.substr(idx2 + 1, reg2_len - 1); @@ -172,36 +174,37 @@ void SwapIntervals(size_t idx1, wgsl_code.replace(idx1 + 1, region_1.size(), region_2); } -void DeleteInterval(size_t idx1, size_t reg_len, std::string& wgsl_code) { +void WgslMutator::DeleteInterval(size_t idx1, size_t reg_len, std::string& wgsl_code) { wgsl_code.erase(idx1 + 1, reg_len - 1); } -void DuplicateInterval(size_t idx1, size_t reg1_len, size_t idx2, std::string& wgsl_code) { +void WgslMutator::DuplicateInterval(size_t idx1, + size_t reg1_len, + size_t idx2, + std::string& wgsl_code) { std::string region = wgsl_code.substr(idx1 + 1, reg1_len - 1); wgsl_code.insert(idx2 + 1, region); } -void ReplaceRegion(size_t idx1, - size_t id1_len, - size_t idx2, - size_t id2_len, - std::string& wgsl_code) { +void WgslMutator::ReplaceRegion(size_t idx1, + size_t id1_len, + size_t idx2, + size_t id2_len, + std::string& wgsl_code) { std::string region_1 = wgsl_code.substr(idx1, id1_len); std::string region_2 = wgsl_code.substr(idx2, id2_len); wgsl_code.replace(idx2, region_2.size(), region_1); } -void ReplaceInterval(size_t start_index, - size_t length, - std::string replacement_text, - std::string& wgsl_code) { +void WgslMutator::ReplaceInterval(size_t start_index, + size_t length, + std::string replacement_text, + std::string& wgsl_code) { std::string region_1 = wgsl_code.substr(start_index, length); wgsl_code.replace(start_index, length, replacement_text); } -bool SwapRandomIntervals(const std::string& delimiter, - std::string& wgsl_code, - RandomGenerator& generator) { +bool WgslMutator::SwapRandomIntervals(const std::string& delimiter, std::string& wgsl_code) { std::vector delimiter_positions = FindDelimiterIndices(delimiter, wgsl_code); // Need to have at least 3 indices. @@ -212,12 +215,12 @@ bool SwapRandomIntervals(const std::string& delimiter, // Choose indices: // interval_1_start < interval_1_end <= interval_2_start < interval_2_end uint32_t interval_1_start = - generator.GetUInt32(static_cast(delimiter_positions.size()) - 2u); - uint32_t interval_1_end = generator.GetUInt32( + generator_.GetUInt32(static_cast(delimiter_positions.size()) - 2u); + uint32_t interval_1_end = generator_.GetUInt32( interval_1_start + 1u, static_cast(delimiter_positions.size()) - 1u); - uint32_t interval_2_start = - generator.GetUInt32(interval_1_end, static_cast(delimiter_positions.size()) - 1u); - uint32_t interval_2_end = generator.GetUInt32( + uint32_t interval_2_start = generator_.GetUInt32( + interval_1_end, static_cast(delimiter_positions.size()) - 1u); + uint32_t interval_2_end = generator_.GetUInt32( interval_2_start + 1u, static_cast(delimiter_positions.size())); SwapIntervals(delimiter_positions[interval_1_start], @@ -229,9 +232,7 @@ bool SwapRandomIntervals(const std::string& delimiter, return true; } -bool DeleteRandomInterval(const std::string& delimiter, - std::string& wgsl_code, - RandomGenerator& generator) { +bool WgslMutator::DeleteRandomInterval(const std::string& delimiter, std::string& wgsl_code) { std::vector delimiter_positions = FindDelimiterIndices(delimiter, wgsl_code); // Need to have at least 2 indices. @@ -240,9 +241,9 @@ bool DeleteRandomInterval(const std::string& delimiter, } uint32_t interval_start = - generator.GetUInt32(static_cast(delimiter_positions.size()) - 1u); - uint32_t interval_end = - generator.GetUInt32(interval_start + 1u, static_cast(delimiter_positions.size())); + generator_.GetUInt32(static_cast(delimiter_positions.size()) - 1u); + uint32_t interval_end = generator_.GetUInt32(interval_start + 1u, + static_cast(delimiter_positions.size())); DeleteInterval(delimiter_positions[interval_start], delimiter_positions[interval_end] - delimiter_positions[interval_start], @@ -251,9 +252,7 @@ bool DeleteRandomInterval(const std::string& delimiter, return true; } -bool DuplicateRandomInterval(const std::string& delimiter, - std::string& wgsl_code, - RandomGenerator& generator) { +bool WgslMutator::DuplicateRandomInterval(const std::string& delimiter, std::string& wgsl_code) { std::vector delimiter_positions = FindDelimiterIndices(delimiter, wgsl_code); // Need to have at least 2 indices @@ -262,11 +261,11 @@ bool DuplicateRandomInterval(const std::string& delimiter, } uint32_t interval_start = - generator.GetUInt32(static_cast(delimiter_positions.size()) - 1u); - uint32_t interval_end = - generator.GetUInt32(interval_start + 1u, static_cast(delimiter_positions.size())); + generator_.GetUInt32(static_cast(delimiter_positions.size()) - 1u); + uint32_t interval_end = generator_.GetUInt32(interval_start + 1u, + static_cast(delimiter_positions.size())); uint32_t duplication_point = - generator.GetUInt32(static_cast(delimiter_positions.size())); + generator_.GetUInt32(static_cast(delimiter_positions.size())); DuplicateInterval(delimiter_positions[interval_start], delimiter_positions[interval_end] - delimiter_positions[interval_start], @@ -275,7 +274,7 @@ bool DuplicateRandomInterval(const std::string& delimiter, return true; } -bool ReplaceRandomIdentifier(std::string& wgsl_code, RandomGenerator& generator) { +bool WgslMutator::ReplaceRandomIdentifier(std::string& wgsl_code) { std::vector> identifiers = GetIdentifiers(wgsl_code); // Need at least 2 identifiers @@ -283,12 +282,12 @@ bool ReplaceRandomIdentifier(std::string& wgsl_code, RandomGenerator& generator) return false; } - uint32_t id1_index = generator.GetUInt32(static_cast(identifiers.size())); - uint32_t id2_index = generator.GetUInt32(static_cast(identifiers.size())); + uint32_t id1_index = generator_.GetUInt32(static_cast(identifiers.size())); + uint32_t id2_index = generator_.GetUInt32(static_cast(identifiers.size())); // The two identifiers must be different while (id1_index == id2_index) { - id2_index = generator.GetUInt32(static_cast(identifiers.size())); + id2_index = generator_.GetUInt32(static_cast(identifiers.size())); } ReplaceRegion(identifiers[id1_index].first, identifiers[id1_index].second, @@ -297,7 +296,7 @@ bool ReplaceRandomIdentifier(std::string& wgsl_code, RandomGenerator& generator) return true; } -bool ReplaceRandomIntLiteral(std::string& wgsl_code, RandomGenerator& generator) { +bool WgslMutator::ReplaceRandomIntLiteral(std::string& wgsl_code) { std::vector> literals = GetIntLiterals(wgsl_code); // Need at least one integer literal @@ -305,13 +304,13 @@ bool ReplaceRandomIntLiteral(std::string& wgsl_code, RandomGenerator& generator) return false; } - uint32_t literal_index = generator.GetUInt32(static_cast(literals.size())); + uint32_t literal_index = generator_.GetUInt32(static_cast(literals.size())); // INT_MAX = 2147483647, INT_MIN = -2147483648 std::vector boundary_values = {"2147483647", "-2147483648", "1", "-1", "0", "4294967295"}; - uint32_t boundary_index = generator.GetUInt32(static_cast(boundary_values.size())); + uint32_t boundary_index = generator_.GetUInt32(static_cast(boundary_values.size())); ReplaceInterval(literals[literal_index].first, literals[literal_index].second, boundary_values[boundary_index], wgsl_code); diff --git a/src/tint/fuzzers/tint_regex_fuzzer/wgsl_mutator.h b/src/tint/fuzzers/tint_regex_fuzzer/wgsl_mutator.h index 23c45bbca5..f04aea1b07 100644 --- a/src/tint/fuzzers/tint_regex_fuzzer/wgsl_mutator.h +++ b/src/tint/fuzzers/tint_regex_fuzzer/wgsl_mutator.h @@ -23,152 +23,153 @@ namespace tint::fuzzers::regex_fuzzer { -/// A function that given a delimiter, returns a vector that contains -/// all the positions of the delimiter in the WGSL code. -/// @param delimiter - the delimiter of the enclosed region. -/// @param wgsl_code - the initial string (WGSL code) that will be mutated. -/// @return a vector with the positions of the delimiter in the WGSL code. -std::vector FindDelimiterIndices(const std::string& delimiter, - const std::string& wgsl_code); +/// Class encapsulating code for regex-based mutation of WGSL shaders. +class WgslMutator { + public: + /// Constructor + /// @param generator - pseudo-random generator to use in mutator + explicit WgslMutator(RandomGenerator& generator); -/// A function that finds all the identifiers in a WGSL-like string. -/// @param wgsl_code - the WGSL-like string where the identifiers will be found. -/// @return a vector with the positions and the length of all the -/// identifiers in wgsl_code. -std::vector> GetIdentifiers(const std::string& wgsl_code); + /// A function that, given WGSL-like string and a delimiter, + /// generates another WGSL-like string by picking two random regions + /// enclosed by the delimiter and swapping them. + /// @param delimiter - the delimiter that will be used to find enclosed regions. + /// @param wgsl_code - the initial string (WGSL code) that will be mutated. + /// @return true if a swap happened or false otherwise. + bool SwapRandomIntervals(const std::string& delimiter, std::string& wgsl_code); -/// A function that returns returns the starting position -/// and the length of all the integer literals in a WGSL-like string. -/// @param wgsl_code - the WGSL-like string where the int literals -/// will be found. -/// @return a vector with the starting positions and the length -/// of all the integer literals. -std::vector> GetIntLiterals(const std::string& wgsl_code); + /// A function that, given a WGSL-like string and a delimiter, + /// generates another WGSL-like string by deleting a random + /// region enclosed by the delimiter. + /// @param delimiter - the delimiter that will be used to find enclosed regions. + /// @param wgsl_code - the initial string (WGSL code) that will be mutated. + /// @return true if a deletion happened or false otherwise. + bool DeleteRandomInterval(const std::string& delimiter, std::string& wgsl_code); -/// Finds a possible closing brace corresponding to the opening -/// brace at position opening_bracket_pos. -/// @param opening_bracket_pos - the position of the opening brace. -/// @param wgsl_code - the WGSL-like string where the closing brace. -/// @return the position of the closing bracket or 0 if there is no closing -/// brace. -size_t FindClosingBrace(size_t opening_bracket_pos, const std::string& wgsl_code); + /// A function that, given a WGSL-like string and a delimiter, + /// generates another WGSL-like string by duplicating a random + /// region enclosed by the delimiter. + /// @param delimiter - the delimiter that will be used to find enclosed regions. + /// @param wgsl_code - the initial string (WGSL code) that will be mutated. + /// @return true if a duplication happened or false otherwise. + bool DuplicateRandomInterval(const std::string& delimiter, std::string& wgsl_code); -/// Returns the starting_position of the bodies of the functions -/// that follow the regular expression: fn.*?->.*?\\{, which searches for the -/// keyword fn followed by the function name, its return type and opening brace. -/// @param wgsl_code - the WGSL-like string where the functions will be -/// searched. -/// @return a vector with the starting position of the function bodies in -/// wgsl_code. -std::vector GetFunctionBodyPositions(const std::string& wgsl_code); + /// Replaces a randomly-chosen identifier in wgsl_code. + /// @param wgsl_code - WGSL-like string where the replacement will occur. + /// @return true if a replacement happened or false otherwise. + bool ReplaceRandomIdentifier(std::string& wgsl_code); -/// Given 4 indices, idx1, idx2, idx3 and idx4 it swaps the regions -/// in the interval (idx1, idx2] with the region in the interval (idx3, idx4] -/// in wgsl_text. -/// @param idx1 - starting index of the first region. -/// @param reg1_len - length of the first region. -/// @param idx2 - starting index of the second region. -/// @param reg2_len - length of the second region. -/// @param wgsl_code - the string where the swap will occur. -void SwapIntervals(size_t idx1, - size_t reg1_len, - size_t idx2, - size_t reg2_len, - std::string& wgsl_code); + /// Replaces the value of a randomly-chosen integer with one of + /// the values in the set {INT_MAX, INT_MIN, 0, -1}. + /// @param wgsl_code - WGSL-like string where the replacement will occur. + /// @return true if a replacement happened or false otherwise. + bool ReplaceRandomIntLiteral(std::string& wgsl_code); -/// Given index idx1 it delets the region of length interval_len -/// starting at index idx1; -/// @param idx1 - starting index of the first region. -/// @param reg_len - terminating index of the second region. -/// @param wgsl_code - the string where the swap will occur. -void DeleteInterval(size_t idx1, size_t reg_len, std::string& wgsl_code); + /// Inserts a return statement in a randomly chosen function of a + /// WGSL-like string. The return value is a randomly-chosen identifier + /// or literal in the string. + /// @param wgsl_code - WGSL-like string that will be mutated. + /// @return true if the mutation was succesful or false otherwise. + bool InsertReturnStatement(std::string& wgsl_code); -/// Given 2 indices, idx1, idx2, it inserts the region of length -/// reg1_len starting at idx1 after idx2. -/// @param idx1 - starting index of region. -/// @param reg1_len - length of the region. -/// @param idx2 - the position where the region will be inserted. -/// @param wgsl_code - the string where the swap will occur. -void DuplicateInterval(size_t idx1, size_t reg1_len, size_t idx2, std::string& wgsl_code); + protected: + /// Given index idx1 it delets the region of length interval_len + /// starting at index idx1; + /// @param idx1 - starting index of the first region. + /// @param reg_len - terminating index of the second region. + /// @param wgsl_code - the string where the swap will occur. + void DeleteInterval(size_t idx1, size_t reg_len, std::string& wgsl_code); -/// Replaces a region of a WGSL-like string of length id2_len starting -/// at position idx2 with a region of length id1_len starting at -/// position idx1. -/// @param idx1 - starting position of the first region. -/// @param id1_len - length of the first region. -/// @param idx2 - starting position of the second region. -/// @param id2_len - length of the second region. -/// @param wgsl_code - the string where the replacement will occur. -void ReplaceRegion(size_t idx1, - size_t id1_len, - size_t idx2, - size_t id2_len, - std::string& wgsl_code); + /// Given 2 indices, idx1, idx2, it inserts the region of length + /// reg1_len starting at idx1 after idx2. + /// @param idx1 - starting index of region. + /// @param reg1_len - length of the region. + /// @param idx2 - the position where the region will be inserted. + /// @param wgsl_code - the string where the swap will occur. + void DuplicateInterval(size_t idx1, size_t reg1_len, size_t idx2, std::string& wgsl_code); -/// Replaces an interval of length `length` starting at start_index -/// with the `replacement_text`. -/// @param start_index - starting position of the interval to be replaced. -/// @param length - length of the interval to be replaced. -/// @param replacement_text - the interval that will be used as a replacement. -/// @param wgsl_code - the WGSL-like string where the replacement will occur. -void ReplaceInterval(size_t start_index, - size_t length, - std::string replacement_text, - std::string& wgsl_code); + /// Finds a possible closing brace corresponding to the opening + /// brace at position opening_bracket_pos. + /// @param opening_bracket_pos - the position of the opening brace. + /// @param wgsl_code - the WGSL-like string where the closing brace. + /// @return the position of the closing bracket or 0 if there is no closing + /// brace. + size_t FindClosingBrace(size_t opening_bracket_pos, const std::string& wgsl_code); -/// A function that, given WGSL-like string and a delimiter, -/// generates another WGSL-like string by picking two random regions -/// enclosed by the delimiter and swapping them. -/// @param delimiter - the delimiter that will be used to find enclosed regions. -/// @param wgsl_code - the initial string (WGSL code) that will be mutated. -/// @param generator - the random number generator. -/// @return true if a swap happened or false otherwise. -bool SwapRandomIntervals(const std::string& delimiter, - std::string& wgsl_code, - RandomGenerator& generator); + /// Returns the starting_position of the bodies of the functions + /// that follow the regular expression: fn.*?->.*?\\{, which searches for the + /// keyword fn followed by the function name, its return type and opening brace. + /// @param wgsl_code - the WGSL-like string where the functions will be + /// searched. + /// @return a vector with the starting position of the function bodies in + /// wgsl_code. + std::vector GetFunctionBodyPositions(const std::string& wgsl_code); -/// A function that, given a WGSL-like string and a delimiter, -/// generates another WGSL-like string by deleting a random -/// region enclosed by the delimiter. -/// @param delimiter - the delimiter that will be used to find enclosed regions. -/// @param wgsl_code - the initial string (WGSL code) that will be mutated. -/// @param generator - the random number generator. -/// @return true if a deletion happened or false otherwise. -bool DeleteRandomInterval(const std::string& delimiter, - std::string& wgsl_code, - RandomGenerator& generator); + /// A function that finds all the identifiers in a WGSL-like string. + /// @param wgsl_code - the WGSL-like string where the identifiers will be found. + /// @return a vector with the positions and the length of all the + /// identifiers in wgsl_code. + std::vector> GetIdentifiers(const std::string& wgsl_code); -/// A function that, given a WGSL-like string and a delimiter, -/// generates another WGSL-like string by duplicating a random -/// region enclosed by the delimiter. -/// @param delimiter - the delimiter that will be used to find enclosed regions. -/// @param wgsl_code - the initial string (WGSL code) that will be mutated. -/// @param generator - the random number generator. -/// @return true if a duplication happened or false otherwise. -bool DuplicateRandomInterval(const std::string& delimiter, - std::string& wgsl_code, - RandomGenerator& generator); + /// A function that returns returns the starting position + /// and the length of all the integer literals in a WGSL-like string. + /// @param wgsl_code - the WGSL-like string where the int literals + /// will be found. + /// @return a vector with the starting positions and the length + /// of all the integer literals. + std::vector> GetIntLiterals(const std::string& wgsl_code); -/// Replaces a randomly-chosen identifier in wgsl_code. -/// @param wgsl_code - WGSL-like string where the replacement will occur. -/// @param generator - the random number generator. -/// @return true if a replacement happened or false otherwise. -bool ReplaceRandomIdentifier(std::string& wgsl_code, RandomGenerator& generator); + /// Replaces a region of a WGSL-like string of length id2_len starting + /// at position idx2 with a region of length id1_len starting at + /// position idx1. + /// @param idx1 - starting position of the first region. + /// @param id1_len - length of the first region. + /// @param idx2 - starting position of the second region. + /// @param id2_len - length of the second region. + /// @param wgsl_code - the string where the replacement will occur. + void ReplaceRegion(size_t idx1, + size_t id1_len, + size_t idx2, + size_t id2_len, + std::string& wgsl_code); -/// Replaces the value of a randomly-chosen integer with one of -/// the values in the set {INT_MAX, INT_MIN, 0, -1}. -/// @param wgsl_code - WGSL-like string where the replacement will occur. -/// @param generator - the random number generator. -/// @return true if a replacement happened or false otherwise. -bool ReplaceRandomIntLiteral(std::string& wgsl_code, RandomGenerator& generator); + /// Given 4 indices, idx1, idx2, idx3 and idx4 it swaps the regions + /// in the interval (idx1, idx2] with the region in the interval (idx3, idx4] + /// in wgsl_text. + /// @param idx1 - starting index of the first region. + /// @param reg1_len - length of the first region. + /// @param idx2 - starting index of the second region. + /// @param reg2_len - length of the second region. + /// @param wgsl_code - the string where the swap will occur. + void SwapIntervals(size_t idx1, + size_t reg1_len, + size_t idx2, + size_t reg2_len, + std::string& wgsl_code); + + private: + /// A function that given a delimiter, returns a vector that contains + /// all the positions of the delimiter in the WGSL code. + /// @param delimiter - the delimiter of the enclosed region. + /// @param wgsl_code - the initial string (WGSL code) that will be mutated. + /// @return a vector with the positions of the delimiter in the WGSL code. + std::vector FindDelimiterIndices(const std::string& delimiter, + const std::string& wgsl_code); + + /// Replaces an interval of length `length` starting at start_index + /// with the `replacement_text`. + /// @param start_index - starting position of the interval to be replaced. + /// @param length - length of the interval to be replaced. + /// @param replacement_text - the interval that will be used as a replacement. + /// @param wgsl_code - the WGSL-like string where the replacement will occur. + void ReplaceInterval(size_t start_index, + size_t length, + std::string replacement_text, + std::string& wgsl_code); + + RandomGenerator& generator_; +}; -/// Inserts a return statement in a randomly chosen function of a -/// WGSL-like string. The return value is a randomly-chosen identifier -/// or literal in the string. -/// @param wgsl_code - WGSL-like string that will be mutated. -/// @param generator - the random number generator. -/// @return true if the mutation was succesful or false otherwise. -bool InsertReturnStatement(std::string& wgsl_code, RandomGenerator& generator); } // namespace tint::fuzzers::regex_fuzzer #endif // SRC_TINT_FUZZERS_TINT_REGEX_FUZZER_WGSL_MUTATOR_H_