CVE-2022-29208: Out-of-bounds Write (CWE-787)
value: TensorFlow is an open source platform for machine learning. Prior to versions 2.9.0, 2.8.1, 2.7.2, and 2.6.4, the implementation of `tf.raw_ops.EditDistance` has incomplete validation. Users can pass negative values to cause a segmentation fault based denial of service. In multiple places throughout the code, one may compute an index for a write operation. However, the existing validation only checks against the upper bound of the array. Hence, it is possible to write before the array by massaging the input to generate negative values for `loc`. Versions 2.9.0, 2.8.1, 2.7.2, and 2.6.4 contain a patch for this issue.
CWE description: The product writes data past the end, or before the beginning, of the intended buffer.
CVSS v3 base score: 7.1
Published: 2022-05-20T23:15Z
Repository: https://github.com/tensorflow/tensorflow
Language: Python
Files changed: tensorflow/core/kernels/edit_distance_op.cc, tensorflow/python/kernel_tests/array_ops/edit_distance_op_test.py
Commit date: 2022-04-29 09:51:06 -0700
Commit message: Fix tf.raw_ops.EditDistance vulnerability with negative indices.

Check that indices are non-negative. Fix several identical code sites.
Clean up grammar in error message.

PiperOrigin-RevId: 445442017

Diff: diff --git a/tensorflow/core/kernels/edit_distance_op.cc b/tensorflow/core/kernels/edit_distance_op.cc
index 3ff290e92b610..3ed0f012b83ce 100644
--- a/tensorflow/core/kernels/edit_distance_op.cc
+++ b/tensorflow/core/kernels/edit_distance_op.cc
@@ -201,13 +201,13 @@ class EditDistanceOp : public OpKernel {
 
       if (g_truth == g_hypothesis) {
         auto loc = std::inner_product(g_truth.begin(), g_truth.end(),
                                       output_strides.begin(), int64_t{0});
         OP_REQUIRES(
-            ctx, loc < output_elements,
+            ctx, 0 <= loc && loc < output_elements,
             errors::Internal("Got an inner product ", loc,
-                             " which would require in writing to outside of "
+                             " which would require writing to outside of "
                              "the buffer for the output tensor (max elements ",
                              output_elements, ")"));
         output_t(loc) =
             gtl::LevenshteinDistance<T>(truth_seq, hypothesis_seq, cmp);
         if (normalize_) output_t(loc) /= truth_seq.size();
@@ -216,13 +216,13 @@ class EditDistanceOp : public OpKernel {
         ++truth_iter;
       } else if (g_truth > g_hypothesis) {  // zero-length truth
         auto loc = std::inner_product(g_hypothesis.begin(), g_hypothesis.end(),
                                       output_strides.begin(), int64_t{0});
         OP_REQUIRES(
-            ctx, loc < output_elements,
+            ctx, 0 <= loc && loc < output_elements,
             errors::Internal("Got an inner product ", loc,
-                             " which would require in writing to outside of "
+                             " which would require writing to outside of "
                              "the buffer for the output tensor (max elements ",
                              output_elements, ")"));
         output_t(loc) = hypothesis_seq.size();
         if (normalize_ && output_t(loc) != 0.0f) {
           output_t(loc) = std::numeric_limits<float>::infinity();
@@ -230,13 +230,13 @@ class EditDistanceOp : public OpKernel {
         ++hypothesis_iter;
       } else {  // zero-length hypothesis
         auto loc = std::inner_product(g_truth.begin(), g_truth.end(),
                                       output_strides.begin(), int64_t{0});
         OP_REQUIRES(
-            ctx, loc < output_elements,
+            ctx, 0 <= loc && loc < output_elements,
             errors::Internal("Got an inner product ", loc,
-                             " which would require in writing to outside of "
+                             " which would require writing to outside of "
                              "the buffer for the output tensor (max elements ",
                              output_elements, ")"));
         output_t(loc) = (normalize_) ? 1.0 : truth_seq.size();
         ++truth_iter;
       }
@@ -246,13 +246,13 @@ class EditDistanceOp : public OpKernel {
       std::vector<int64_t> g_hypothesis = hypothesis_j.group();
       auto hypothesis_seq = hypothesis_j.values<T>();
       auto loc = std::inner_product(g_hypothesis.begin(), g_hypothesis.end(),
                                     output_strides.begin(), int64_t{0});
       OP_REQUIRES(
-          ctx, loc < output_elements,
+          ctx, 0 <= loc && loc < output_elements,
           errors::Internal("Got an inner product ", loc,
-                           " which would require in writing to outside of the "
+                           " which would require writing to outside of the "
                            "buffer for the output tensor (max elements ",
                            output_elements, ")"));
       output_t(loc) = hypothesis_seq.size();
       if (normalize_ && output_t(loc) != 0.0f) {
         output_t(loc) = std::numeric_limits<float>::infinity();
@@ -264,13 +264,13 @@ class EditDistanceOp : public OpKernel {
       std::vector<int64_t> g_truth = truth_i.group();
       auto truth_seq = truth_i.values<T>();
       auto loc = std::inner_product(g_truth.begin(), g_truth.end(),
                                     output_strides.begin(), int64_t{0});
       OP_REQUIRES(
-          ctx, loc < output_elements,
+          ctx, 0 <= loc && loc < output_elements,
           errors::Internal("Got an inner product ", loc,
-                           " which would require in writing to outside of the "
+                           " which would require writing to outside of the "
                            "buffer for the output tensor (max elements ",
                            output_elements, ")"));
       output_t(loc) = (normalize_) ? 1.0 : truth_seq.size();
       ++truth_iter;
     }
diff --git a/tensorflow/python/kernel_tests/array_ops/edit_distance_op_test.py b/tensorflow/python/kernel_tests/array_ops/edit_distance_op_test.py
index 9996a4f621e4b..b74024aa60c8b 100644
--- a/tensorflow/python/kernel_tests/array_ops/edit_distance_op_test.py
+++ b/tensorflow/python/kernel_tests/array_ops/edit_distance_op_test.py
@@ -205,8 +205,26 @@ class EditDistanceTest(test.TestCase):
         hypothesis=(hypothesis_indices, hypothesis_values, hypothesis_shape),
         truth=(truth_indices, truth_values, truth_shape),
         normalize=True,
         expected_output=expected_output)
 
+  def testEditDistanceBadIndices(self):
+    hypothesis_indices = np.full((3, 3), -1250999896764, dtype=np.int64)
+    hypothesis_values = np.empty(3, dtype=np.int64)
+    hypothesis_shape = np.empty(3, dtype=np.int64)
+    truth_indices = np.full((3, 3), -1250999896764, dtype=np.int64)
+    truth_values = np.full([3], 2, dtype=np.int64)
+    truth_shape = np.full([3], 2, dtype=np.int64)
+    expected_output = []  # dummy; ignored
+
+    self._testEditDistance(
+        hypothesis=(hypothesis_indices, hypothesis_values, hypothesis_shape),
+        truth=(truth_indices, truth_values, truth_shape),
+        normalize=False,
+        expected_output=expected_output,
+        expected_err_re=(r"inner product -\d+ which would require writing "
+                         "to outside of the buffer for the output tensor")
+    )
+
 
 if __name__ == "__main__":
   test.main()

Vulnerable code:             ctx, loc < output_elements,
                             " which would require in writing to outside of "
            ctx, loc < output_elements,
                             " which would require in writing to outside of "
            ctx, loc < output_elements,
                             " which would require in writing to outside of "
          ctx, loc < output_elements,
                           " which would require in writing to outside of the "
          ctx, loc < output_elements,
                           " which would require in writing to outside of the "
Fixed code:             ctx, 0 <= loc && loc < output_elements,
                             " which would require writing to outside of "
            ctx, 0 <= loc && loc < output_elements,
                             " which would require writing to outside of "
            ctx, 0 <= loc && loc < output_elements,
                             " which would require writing to outside of "
          ctx, 0 <= loc && loc < output_elements,
                           " which would require writing to outside of the "
          ctx, 0 <= loc && loc < output_elements,
                           " which would require writing to outside of the "
  def testEditDistanceBadIndices(self):
    hypothesis_indices = np.full((3, 3), -1250999896764, dtype=np.int64)
    hypothesis_values = np.empty(3, dtype=np.int64)
    hypothesis_shape = np.empty(3, dtype=np.int64)
    truth_indices = np.full((3, 3), -1250999896764, dtype=np.int64)
    truth_values = np.full([3], 2, dtype=np.int64)
    truth_shape = np.full([3], 2, dtype=np.int64)
    expected_output = []  # dummy; ignored

    self._testEditDistance(
        hypothesis=(hypothesis_indices, hypothesis_values, hypothesis_shape),
        truth=(truth_indices, truth_values, truth_shape),
        normalize=False,
        expected_output=expected_output,
        expected_err_re=(r"inner product -\d+ which would require writing "
                         "to outside of the buffer for the output tensor")
    )