CVE-2022-29207: Undefined Behavior for Input to API (CWE-475)
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, multiple TensorFlow operations misbehave in eager mode when the resource handle provided to them is invalid. In graph mode, it would have been impossible to perform these API calls, but migration to TF 2.x eager mode opened up this vulnerability. If the resource handle is empty, then a reference is bound to a null pointer inside TensorFlow codebase (various codepaths). This is undefined behavior. Versions 2.9.0, 2.8.1, 2.7.2, and 2.6.4 contain a patch for this issue.
CWE description: The behavior of this function is undefined unless its control parameter is set to a specific value.
CVSS v3 base score: 5.5
Published: 2022-05-20T22:16Z
Repository: https://github.com/tensorflow/tensorflow
Language: Python
Files changed: tensorflow/python/kernel_tests/summary_ops/summary_ops_test.py, tensorflow/python/ops/summary_ops_v2.py
Commit date: 2022-04-13 16:24:59 -0700
Commit message: Fix segfault from passing invalid input to tf.summary.flush()

This removes an obsolete legacy codepath from tf.summary.flush(). Because that codepath was still used by tpu_estimator.py, for now, we preserve a copy of the legacy codepath and update tpu_estimator.py to use it.

PiperOrigin-RevId: 441606843

Diff: diff --git a/tensorflow/python/kernel_tests/summary_ops/summary_ops_test.py b/tensorflow/python/kernel_tests/summary_ops/summary_ops_test.py
index a85c8634180f3..e18b69b57d1f3 100644
--- a/tensorflow/python/kernel_tests/summary_ops/summary_ops_test.py
+++ b/tensorflow/python/kernel_tests/summary_ops/summary_ops_test.py
@@ -983,14 +983,16 @@ class SummaryWriterTest(test_util.TensorFlowTestCase):
         # Test "writer" parameter
         summary_ops.write('tag', 1, step=0)
         self.assertEqual(3, get_total())
         summary_ops.flush(writer=writer)
         self.assertEqual(4, get_total())
-        summary_ops.write('tag', 1, step=0)
-        self.assertEqual(4, get_total())
-        summary_ops.flush(writer=writer._resource)  # pylint:disable=protected-access
-        self.assertEqual(5, get_total())
+
+  # Regression test for b/228097117.
+  def testFlushFunction_disallowsInvalidWriterInput(self):
+    with context.eager_mode():
+      with self.assertRaisesRegex(ValueError, 'Invalid argument to flush'):
+        summary_ops.flush(writer=())
 
   @test_util.assert_no_new_tensors
   def testNoMemoryLeak_graphMode(self):
     logdir = self.get_temp_dir()
     with context.graph_mode(), ops.Graph().as_default():
diff --git a/tensorflow/python/ops/summary_ops_v2.py b/tensorflow/python/ops/summary_ops_v2.py
index cc7f3aba23c65..6ec42f486b104 100644
--- a/tensorflow/python/ops/summary_ops_v2.py
+++ b/tensorflow/python/ops/summary_ops_v2.py
@@ -1109,16 +1109,39 @@ def flush(writer=None, name=None):
     name: Ignored legacy argument for a name for the operation.
 
   Returns:
     The created `tf.Operation`.
   """
+  del name  # unused
   if writer is None:
     writer = _summary_state.writer
     if writer is None:
       return control_flow_ops.no_op()
   if isinstance(writer, SummaryWriter):
     return writer.flush()
+  raise ValueError("Invalid argument to flush(): %r" % (writer,))
+
+
+def legacy_raw_flush(writer=None, name=None):
+  """Legacy version of flush() that accepts a raw resource tensor for `writer`.
+
+  Do not use this function in any new code. Not supported and not part of the
+  public TF APIs.
+
+  Args:
+    writer: The `tf.summary.SummaryWriter` to flush. If None, the current
+      default writer will be used instead; if there is no current writer, this
+      returns `tf.no_op`. For this legacy version only, also accepts a raw
+      resource tensor pointing to the underlying C++ writer resource.
+    name: Ignored legacy argument for a name for the operation.
+
+  Returns:
+    The created `tf.Operation`.
+  """
+  if writer is None or isinstance(writer, SummaryWriter):
+    # Forward to the TF2 implementation of flush() when possible.
+    return flush(writer, name)
   else:
     # Legacy fallback in case we were passed a raw resource tensor.
     with ops.device("cpu:0"):
       return gen_summary_ops.flush_summary_writer(writer, name=name)
 

Vulnerable code:         summary_ops.write('tag', 1, step=0)
        self.assertEqual(4, get_total())
        summary_ops.flush(writer=writer._resource)  # pylint:disable=protected-access
        self.assertEqual(5, get_total())
Fixed code: 
  # Regression test for b/228097117.
  def testFlushFunction_disallowsInvalidWriterInput(self):
    with context.eager_mode():
      with self.assertRaisesRegex(ValueError, 'Invalid argument to flush'):
        summary_ops.flush(writer=())
  del name  # unused
  raise ValueError("Invalid argument to flush(): %r" % (writer,))


def legacy_raw_flush(writer=None, name=None):
  """Legacy version of flush() that accepts a raw resource tensor for `writer`.

  Do not use this function in any new code. Not supported and not part of the
  public TF APIs.

  Args:
    writer: The `tf.summary.SummaryWriter` to flush. If None, the current
      default writer will be used instead; if there is no current writer, this
      returns `tf.no_op`. For this legacy version only, also accepts a raw
      resource tensor pointing to the underlying C++ writer resource.
    name: Ignored legacy argument for a name for the operation.

  Returns:
    The created `tf.Operation`.
  """
  if writer is None or isinstance(writer, SummaryWriter):
    # Forward to the TF2 implementation of flush() when possible.
    return flush(writer, name)