Regex fuzzer: replace the value of an integer with
a value in the set {INT_MAX, INT_MIN, -1 or 0}.

A mutation that replaces the value of a randomly-chosen integer with a value
in the set {INT_MAX, INT_MIN, -1, 0}.

Fixes: tint:1093.

Change-Id: I5ec69e1813785760ed6e7b06d0cbd9c481f69ade
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/60920
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Alastair Donaldson <afdx@google.com>
Commit-Queue: Alastair Donaldson <afdx@google.com>
diff --git a/fuzzers/tint_regex_fuzzer/fuzzer.cc b/fuzzers/tint_regex_fuzzer/fuzzer.cc
index cc87cf5..57eecff 100644
--- a/fuzzers/tint_regex_fuzzer/fuzzer.cc
+++ b/fuzzers/tint_regex_fuzzer/fuzzer.cc
@@ -35,6 +35,8 @@
   kSwapIntervals,
   kDeleteInterval,
   kDuplicateInterval,
+  kReplaceIdentifier,
+  kReplaceLiteral,
   kNumMutationKinds
 };
 
@@ -78,6 +80,18 @@
       }
       break;
 
+    case MutationKind::kReplaceIdentifier:
+      if (!ReplaceRandomIdentifier(wgsl_code, generator)) {
+        return 0;
+      }
+      break;
+
+    case MutationKind::kReplaceLiteral:
+      if (!ReplaceRandomIntLiteral(wgsl_code, generator)) {
+        return 0;
+      }
+      break;
+
     default:
       assert(false && "Unreachable");
       return 0;
diff --git a/fuzzers/tint_regex_fuzzer/regex_fuzzer_tests.cc b/fuzzers/tint_regex_fuzzer/regex_fuzzer_tests.cc
index 252958c..94fe68f 100644
--- a/fuzzers/tint_regex_fuzzer/regex_fuzzer_tests.cc
+++ b/fuzzers/tint_regex_fuzzer/regex_fuzzer_tests.cc
@@ -226,6 +226,40 @@
   ASSERT_EQ(ground_truth, identifiers_pos);
 }
 
+TEST(TestGetLiteralsValues, TestGetLiteralsValues1) {
+  std::string wgsl_code =
+      "fn clamp_0acf8f() {"
+      "var res: vec2<f32> = clamp(vec2<f32>(), vec2<f32>(), vec2<f32>());}"
+      "[[stage(vertex)]]"
+      "fn vertex_main() -> [[builtin(position)]] vec4<f32> {"
+      "  clamp_0acf8f();"
+      "var foo_1: i32 = 3;"
+      "  return vec4<f32>();}"
+      "[[stage(fragment)]]"
+      "fn fragment_main() {"
+      "  clamp_0acf8f();}"
+      "[[stage(compute), workgroup_size(1)]]"
+      "fn compute_main() {"
+      "var<private> foo: f32 = 0.0;"
+      "var foo_2: i32 = 10;"
+      "  clamp_0acf8f();}"
+      "foo_1 = 5 + 7;"
+      "var foo_3 : i32 = -20;";
+
+  std::vector<std::pair<size_t, size_t>> literals_pos =
+      GetIntLiterals(wgsl_code);
+
+  std::vector<std::string> ground_truth = {"3", "10", "5", "7", "-20"};
+
+  std::vector<std::string> result;
+
+  for (auto pos : literals_pos) {
+    result.push_back(wgsl_code.substr(pos.first, pos.second));
+  }
+
+  ASSERT_EQ(ground_truth, result);
+}
+
 }  // namespace
 }  // namespace regex_fuzzer
 }  // namespace fuzzers
diff --git a/fuzzers/tint_regex_fuzzer/wgsl_mutator.cc b/fuzzers/tint_regex_fuzzer/wgsl_mutator.cc
index cb1f850..2745001 100644
--- a/fuzzers/tint_regex_fuzzer/wgsl_mutator.cc
+++ b/fuzzers/tint_regex_fuzzer/wgsl_mutator.cc
@@ -67,6 +67,29 @@
   return result;
 }
 
+std::vector<std::pair<size_t, size_t>> GetIntLiterals(const std::string& s) {
+  std::vector<std::pair<size_t, size_t>> result;
+
+  // Looks for integer literals in decimal or hexadecimal form.
+  // Regex obtained here: https://www.w3.org/TR/WGSL/#literals
+  std::regex int_literal_regex("-?0x[0-9a-fA-F]+ | 0 | -?[1-9][0-9]*");
+  std::regex uint_literal_regex("0x[0-9a-fA-F]+u | 0u | [1-9][0-9]*u");
+  std::smatch match;
+
+  std::string::const_iterator search_start(s.cbegin());
+  std::string prefix = "";
+
+  while (regex_search(search_start, s.cend(), match, int_literal_regex) ||
+         regex_search(search_start, s.cend(), match, uint_literal_regex)) {
+    prefix += match.prefix();
+    result.push_back(
+        std::make_pair(prefix.size() + 1, match.str(0).size() - 1));
+    prefix += match.str(0);
+    search_start = match.suffix().first;
+  }
+  return result;
+}
+
 void SwapIntervals(size_t idx1,
                    size_t reg1_len,
                    size_t idx2,
@@ -76,7 +99,7 @@
 
   std::string region_2 = wgsl_code.substr(idx2 + 1, reg2_len - 1);
 
-  // The second transformation is done first as it doesn't affect ind1 and ind2
+  // The second transformation is done first as it doesn't affect idx2.
   wgsl_code.replace(idx2 + 1, region_2.size(), region_1);
 
   wgsl_code.replace(idx1 + 1, region_1.size(), region_2);
@@ -104,6 +127,14 @@
   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) {
+  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,
                          std::mt19937& generator) {
@@ -211,6 +242,29 @@
   return true;
 }
 
+bool ReplaceRandomIntLiteral(std::string& wgsl_code, std::mt19937& generator) {
+  std::vector<std::pair<size_t, size_t>> literals = GetIntLiterals(wgsl_code);
+
+  // Need at least one integer literal
+  if (literals.size() < 1) {
+    return false;
+  }
+
+  size_t id1_index = GetRandomIntFromRange(0, literals.size() - 1U, generator);
+
+  // INT_MAX = 2147483647, INT_MIN = -2147483648
+  std::vector<std::string> boundary_values = {
+      "2147483647", "-2147483648", "1", "-1", "0", "4294967295"};
+
+  size_t boundary_index =
+      GetRandomIntFromRange(0, boundary_values.size() - 1U, generator);
+
+  ReplaceInterval(literals[id1_index].first, literals[id1_index].second,
+                  boundary_values[boundary_index], wgsl_code);
+
+  return true;
+}
+
 }  // namespace regex_fuzzer
 }  // namespace fuzzers
 }  // namespace tint
diff --git a/fuzzers/tint_regex_fuzzer/wgsl_mutator.h b/fuzzers/tint_regex_fuzzer/wgsl_mutator.h
index bd7975b..1af6b02 100644
--- a/fuzzers/tint_regex_fuzzer/wgsl_mutator.h
+++ b/fuzzers/tint_regex_fuzzer/wgsl_mutator.h
@@ -39,6 +39,15 @@
 std::vector<std::pair<size_t, size_t>> GetIdentifiers(
     const 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<std::pair<size_t, size_t>> GetIntLiterals(
+    const 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.
@@ -74,16 +83,28 @@
 /// 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 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 an interval of length interval1_len starting at start_index
+/// with the interval interval2.
+/// @param start_index - starting position of the interval to be replaced.
+/// @param interval1_len - 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);
+
 /// 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.
@@ -117,13 +138,19 @@
                              std::string& wgsl_code,
                              std::mt19937& generator);
 
-/// Replaces a random identifier in 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, std::mt19937& generator);
 
+/// 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, std::mt19937& generator);
+
 }  // namespace regex_fuzzer
 }  // namespace fuzzers
 }  // namespace tint