CVE-2022-41908: Improper Input Validation (CWE-20)
value: TensorFlow is an open source platform for machine learning. An input `token` that is not a UTF-8 bytestring will trigger a `CHECK` fail in `tf.raw_ops.PyFunc`. We have patched the issue in GitHub commit 9f03a9d3bafe902c1e6beb105b2f24172f238645. The fix will be included in TensorFlow 2.11. We will also cherrypick this commit on TensorFlow 2.10.1, 2.9.3, and TensorFlow 2.8.4, as these are also affected and still in supported range.
CWE description: The product receives input or data, but it does
        not validate or incorrectly validates that the input has the
        properties that are required to process the data safely and
        correctly.
CVSS v3 base score: 7.5
Published: 2022-11-18T22:15Z
Repository: https://github.com/tensorflow/tensorflow
Language: Python
Files changed: tensorflow/python/lib/core/py_func.cc, tensorflow/python/ops/script_ops_test.py
Commit date: 2022-09-30 18:43:44 -0700
Commit message: Replace CHECK with returning an InternalError on failing to create python tuple

Returns InternalError if a non UTF-8 string is passed in as token, instead of crashing.

PiperOrigin-RevId: 478123498

Diff: diff --git a/tensorflow/python/lib/core/py_func.cc b/tensorflow/python/lib/core/py_func.cc
index 1fdfab1a6c006..925929fa2f050 100644
--- a/tensorflow/python/lib/core/py_func.cc
+++ b/tensorflow/python/lib/core/py_func.cc
@@ -81,12 +81,12 @@ struct PyCall {
 
 bool IsCPUDevice(const Device* d) {
   return d == nullptr || d->tensorflow_accelerator_device_info() == nullptr;
 }
 
-// Givens the 'call', prepares the token and inputs as a python tuple
-// that is appropriate for calling the trampoline.
+// Given the 'call', prepares the token and inputs as a python tuple that is
+// appropriate for calling the trampoline.
 Status MakeArgTuple(const PyCall* call, TFE_Context* ctx, PyObject** tuple) {
   int64_t n = call->ins.size();
   PyObject* lst = PyList_New(n);
   CHECK(lst);
   // TFE_TensorHandle assumes that CPU is identified by nullptr.
@@ -117,11 +117,15 @@ Status MakeArgTuple(const PyCall* call, TFE_Context* ctx, PyObject** tuple) {
       arg = PyArray_Return(reinterpret_cast<PyArrayObject*>(arg));
     }
     PyList_SetItem(lst, i, arg);
   }
   *tuple = Py_BuildValue("(ssN)", call->token.c_str(), device_name, lst);
-  CHECK(*tuple);
+  if (*tuple == nullptr) {
+    return errors::Internal(
+        "Failed to create python tuple. Please make sure `token` is a "
+        "well-formed UTF-8 string.");
+  }
   return OkStatus();
 }
 
 bool IsSingleNone(PyObject* obj) {
   if (!PyArray_Check(obj)) {
diff --git a/tensorflow/python/ops/script_ops_test.py b/tensorflow/python/ops/script_ops_test.py
index 45689b9c897e7..92339c434464e 100644
--- a/tensorflow/python/ops/script_ops_test.py
+++ b/tensorflow/python/ops/script_ops_test.py
@@ -15,11 +15,13 @@
 """Tests for script operations."""
 
 from tensorflow.python.eager import def_function
 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 gen_script_ops
 from tensorflow.python.ops import resource_variable_ops
 from tensorflow.python.ops import script_ops
 from tensorflow.python.ops.script_ops import numpy_function
 from tensorflow.python.platform import test
 
@@ -101,8 +103,17 @@ class PyFunctionTest(test.TestCase):
 
     actual_result = script_ops.eager_py_func(plus, [v1, 2], dtypes.int32)
     expect_result = constant_op.constant(3, dtypes.int32)
     self.assertAllEqual(actual_result, expect_result)
 
+  @test_util.run_in_graph_and_eager_modes
+  def test_fail_on_non_utf8_token(self):
+    value = constant_op.constant(value=[1, 2])
+    token = b"\xb0"
+    data_type = [dtypes.int32]
+    with self.assertRaises((errors.InternalError, UnicodeDecodeError)):
+      self.evaluate(
+          gen_script_ops.py_func(input=[value], token=token, Tout=data_type))
+
 
 if __name__ == "__main__":
   test.main()

Vulnerable code: // Givens the 'call', prepares the token and inputs as a python tuple
// that is appropriate for calling the trampoline.
  CHECK(*tuple);
Fixed code: // Given the 'call', prepares the token and inputs as a python tuple that is
// appropriate for calling the trampoline.
  if (*tuple == nullptr) {
    return errors::Internal(
        "Failed to create python tuple. Please make sure `token` is a "
        "well-formed UTF-8 string.");
  }
from tensorflow.python.framework import errors
from tensorflow.python.ops import gen_script_ops
  @test_util.run_in_graph_and_eager_modes
  def test_fail_on_non_utf8_token(self):
    value = constant_op.constant(value=[1, 2])
    token = b"\xb0"
    data_type = [dtypes.int32]
    with self.assertRaises((errors.InternalError, UnicodeDecodeError)):
      self.evaluate(
          gen_script_ops.py_func(input=[value], token=token, Tout=data_type))