CVE-2022-35988: Reachable Assertion (CWE-617)
value: TensorFlow is an open source platform for machine learning. When `tf.linalg.matrix_rank` receives an empty input `a`, the GPU kernel gives a `CHECK` fail that can be used to trigger a denial of service attack. We have patched the issue in GitHub commit c55b476aa0e0bd4ee99d0f3ad18d9d706cd1260a. The fix will be included in TensorFlow 2.10.0. We will also cherrypick this commit on TensorFlow 2.9.1, TensorFlow 2.8.1, and TensorFlow 2.7.2, as these are also affected and still in supported range. There are no known workarounds for this issue.
CWE description: The product contains an assert() or similar statement that can be triggered by an attacker, which leads to an application exit or other behavior that is more severe than necessary.
CVSS v3 base score: 7.5
Published: 2022-09-16T22:15Z
Repository: https://github.com/tensorflow/tensorflow
Language: Python
Files changed: tensorflow/core/kernels/linalg/svd_op_gpu.cu.cc, tensorflow/python/kernel_tests/linalg/svd_op_test.py
Commit date: 2022-07-20 13:02:29 -0700
Commit message: Fix empty batch issue in svd.

If there are zero batches, the GPU kernel previously failed with a `work_element_count > 0`
check failure.  In the zero batch case, there is no output, so simply return.

PiperOrigin-RevId: 462210670

Diff: diff --git a/tensorflow/core/kernels/linalg/svd_op_gpu.cu.cc b/tensorflow/core/kernels/linalg/svd_op_gpu.cu.cc
index a3532f765a414..6168baac069d6 100644
--- a/tensorflow/core/kernels/linalg/svd_op_gpu.cu.cc
+++ b/tensorflow/core/kernels/linalg/svd_op_gpu.cu.cc
@@ -393,10 +393,16 @@ class SvdOpGpu : public AsyncOpKernel {
     OP_REQUIRES_OK_ASYNC(context, context->allocate_output(1, shapeU, &outputU),
                          done);
     OP_REQUIRES_OK_ASYNC(context, context->allocate_output(2, shapeV, &outputV),
                          done);
 
+    // If there are zero batches, we are done.
+    if (shapeRaw.num_elements() == 0) {
+      done();
+      return;
+    }
+
     if (n == 0 || m == 0) {
       if (n == m || !compute_uv_ || !full_matrices_) {
         // S, U, and V are all empty. Nothing to do.
         done();
         return;
diff --git a/tensorflow/python/kernel_tests/linalg/svd_op_test.py b/tensorflow/python/kernel_tests/linalg/svd_op_test.py
index 7a4182b810568..8ceca06beff0a 100644
--- a/tensorflow/python/kernel_tests/linalg/svd_op_test.py
+++ b/tensorflow/python/kernel_tests/linalg/svd_op_test.py
@@ -106,10 +106,18 @@ class SvdOpTest(test.TestCase):
           all_ops += [s1, s2]
     val = self.evaluate(all_ops)
     for i in range(0, len(val), 2):
       self.assertAllEqual(val[i], val[i + 1])
 
+  @test_util.run_in_graph_and_eager_modes(use_gpu=True)
+  def testEmptyBatches(self):
+    matrices = constant_op.constant(1.0, shape=[0, 2, 2])
+    s, u, v = self.evaluate(linalg_ops.svd(matrices))
+    self.assertAllEqual(s, np.zeros([0, 2]))
+    self.assertAllEqual(u, np.zeros([0, 2, 2]))
+    self.assertAllEqual(v, np.zeros([0, 2, 2]))
+
 
 def _GetSvdOpTest(dtype_, shape_, use_static_shape_, compute_uv_,
                   full_matrices_):
 
   def CompareSingularValues(self, x, y, tol):

Fixed code:     // If there are zero batches, we are done.
    if (shapeRaw.num_elements() == 0) {
      done();
      return;
    }

  @test_util.run_in_graph_and_eager_modes(use_gpu=True)
  def testEmptyBatches(self):
    matrices = constant_op.constant(1.0, shape=[0, 2, 2])
    s, u, v = self.evaluate(linalg_ops.svd(matrices))
    self.assertAllEqual(s, np.zeros([0, 2]))
    self.assertAllEqual(u, np.zeros([0, 2, 2]))
    self.assertAllEqual(v, np.zeros([0, 2, 2]))