CVE-2022-21730: Out-of-bounds Read (CWE-125)
value: Tensorflow is an Open Source Machine Learning Framework. The implementation of `FractionalAvgPoolGrad` does not consider cases where the input tensors are invalid allowing an attacker to read from outside of bounds of heap. The fix will be included in TensorFlow 2.8.0. We will also cherrypick this commit on TensorFlow 2.7.1, TensorFlow 2.6.3, and TensorFlow 2.5.3, as these are also affected and still in supported range.
CWE description: The product reads data past the end, or before the beginning, of the intended buffer.
CVSS v3 base score: 8.1
Published: 2022-02-03T11:15Z
Repository: https://github.com/tensorflow/tensorflow
Language: Python
Files changed: tensorflow/core/kernels/fractional_avg_pool_op.cc, tensorflow/python/kernel_tests/nn_ops/fractional_avg_pool_op_test.py
Commit date: 2021-12-02 21:02:49 -0800
Commit message: Add negative bound check for row and column pooling_sequence in FractionalAvgPoolGrad op to avoid out of bound heap access

PiperOrigin-RevId: 413837346
Change-Id: I2b86034101df31bee161abcb781755e236c7bccd

Diff: diff --git a/tensorflow/core/kernels/fractional_avg_pool_op.cc b/tensorflow/core/kernels/fractional_avg_pool_op.cc
index afc72287edadc..b3e65aeaee22f 100644
--- a/tensorflow/core/kernels/fractional_avg_pool_op.cc
+++ b/tensorflow/core/kernels/fractional_avg_pool_op.cc
@@ -309,19 +309,30 @@ class FractionalAvgPoolGradOp : public OpKernel {
     const int64_t in_max_row_index = in_rows - 1;
     const int64_t in_max_col_index = in_cols - 1;
     for (int64_t b = 0; b < out_batch; ++b) {
       for (int64_t r = 0; r < out_rows; ++r) {
         const int64_t in_row_start = row_seq_tensor_flat(r);
+
         int64_t in_row_end = overlapping_ ? row_seq_tensor_flat(r + 1)
                                           : row_seq_tensor_flat(r + 1) - 1;
         in_row_end = std::min(in_row_end, in_max_row_index);
+        OP_REQUIRES(context, in_row_start >= 0 && in_row_end >= 0,
+                    errors::InvalidArgument(
+                        "Row sequence tensor values must not be negative, got ",
+                        row_seq_tensor_flat));
+
         for (int64_t c = 0; c < out_cols; ++c) {
           const int64_t in_col_start = col_seq_tensor_flat(c);
           int64_t in_col_end = overlapping_ ? col_seq_tensor_flat(c + 1)
                                             : col_seq_tensor_flat(c + 1) - 1;
           in_col_end = std::min(in_col_end, in_max_col_index);
 
+          OP_REQUIRES(
+              context, in_col_start >= 0 && in_col_end >= 0,
+              errors::InvalidArgument(
+                  "Column sequence tensor values must not be negative, got ",
+                  col_seq_tensor_flat));
           const int64_t num_elements_in_pooling_cell =
               (in_row_end - in_row_start + 1) * (in_col_end - in_col_start + 1);
           const int64_t out_index = (b * out_rows + r) * out_cols + c;
           // Now we can evenly distribute out_backprop(b, h, w, *) to
           // in_backprop(b, hs:he, ws:we, *).
diff --git a/tensorflow/python/kernel_tests/nn_ops/fractional_avg_pool_op_test.py b/tensorflow/python/kernel_tests/nn_ops/fractional_avg_pool_op_test.py
index ae75c42426462..7b153ae1ed708 100644
--- a/tensorflow/python/kernel_tests/nn_ops/fractional_avg_pool_op_test.py
+++ b/tensorflow/python/kernel_tests/nn_ops/fractional_avg_pool_op_test.py
@@ -18,10 +18,11 @@ import math
 
 import numpy as np
 
 from tensorflow.python.framework import constant_op
 from tensorflow.python.framework import dtypes
+from tensorflow.python.framework import errors
 from tensorflow.python.framework import test_util
 from tensorflow.python.ops import array_ops
 from tensorflow.python.ops import gen_nn_ops
 from tensorflow.python.ops import gradient_checker
 from tensorflow.python.ops import nn_ops
@@ -304,10 +305,36 @@ class FractionalAvgTest(test.TestCase):
       actual, row_seq, col_seq = sess.run([p, r, c], {input_holder: input_b})
       expected = self._GetExpectedFractionalAvgPoolResult(
           input_b, row_seq, col_seq, overlapping)
       self.assertSequenceEqual(expected.shape, actual.shape)
 
+  def testNegativeSeqValuesForGradOp(self):
+    with self.assertRaisesRegex(
+        errors.InvalidArgumentError,
+        r"Row sequence tensor values must not be negative.*"):
+      y = nn_ops.gen_nn_ops.fractional_avg_pool_grad(
+          orig_input_tensor_shape=[2, 2, 2, 2],
+          out_backprop=[[[[1, 2], [3, 4], [5, 6]], [[7, 8], [9, 10], [11,
+                                                                      12]]]],
+          row_pooling_sequence=[-10, 1, 2, 3],
+          col_pooling_sequence=[1, 2, 3, 4],
+          overlapping=True)
+
+      self.evaluate(y)
+      with self.assertRaisesRegex(
+          errors.InvalidArgumentError,
+          r"Column sequence tensor values must not be negative.*"):
+        z = nn_ops.gen_nn_ops.fractional_avg_pool_grad(
+            orig_input_tensor_shape=[2, 2, 2, 2],
+            out_backprop=[[[[1, 2], [3, 4], [5, 6]], [[7, 8], [9, 10], [11,
+                                                                        12]]]],
+            row_pooling_sequence=[10, 1, 2, 3],
+            col_pooling_sequence=[1, 2, -3, 4],
+            overlapping=True)
+
+        self.evaluate(z)
+
 
 class FractionalAvgPoolGradTest(test.TestCase):
   """Tests for FractionalAvgPoolGrad.
 
   Two types of tests for FractionalAvgPoolGrad.

Fixed code: 
        OP_REQUIRES(context, in_row_start >= 0 && in_row_end >= 0,
                    errors::InvalidArgument(
                        "Row sequence tensor values must not be negative, got ",
                        row_seq_tensor_flat));

          OP_REQUIRES(
              context, in_col_start >= 0 && in_col_end >= 0,
              errors::InvalidArgument(
                  "Column sequence tensor values must not be negative, got ",
                  col_seq_tensor_flat));
from tensorflow.python.framework import errors
  def testNegativeSeqValuesForGradOp(self):
    with self.assertRaisesRegex(
        errors.InvalidArgumentError,
        r"Row sequence tensor values must not be negative.*"):
      y = nn_ops.gen_nn_ops.fractional_avg_pool_grad(
          orig_input_tensor_shape=[2, 2, 2, 2],
          out_backprop=[[[[1, 2], [3, 4], [5, 6]], [[7, 8], [9, 10], [11,
                                                                      12]]]],
          row_pooling_sequence=[-10, 1, 2, 3],
          col_pooling_sequence=[1, 2, 3, 4],
          overlapping=True)

      self.evaluate(y)
      with self.assertRaisesRegex(
          errors.InvalidArgumentError,
          r"Column sequence tensor values must not be negative.*"):
        z = nn_ops.gen_nn_ops.fractional_avg_pool_grad(
            orig_input_tensor_shape=[2, 2, 2, 2],
            out_backprop=[[[[1, 2], [3, 4], [5, 6]], [[7, 8], [9, 10], [11,
                                                                        12]]]],
            row_pooling_sequence=[10, 1, 2, 3],
            col_pooling_sequence=[1, 2, -3, 4],
            overlapping=True)

        self.evaluate(z)