CVE-2022-36012: Reachable Assertion (CWE-617)
value: TensorFlow is an open source platform for machine learning. When `mlir::tfg::ConvertGenericFunctionToFunctionDef` is given empty function attributes, it crashes. We have patched the issue in GitHub commit ad069af92392efee1418c48ff561fd3070a03d7b. 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-16T23:15Z
Repository: https://github.com/tensorflow/tensorflow
Language: Other
Files changed: tensorflow/core/ir/importexport/functiondef_import.cc, tensorflow/core/ir/importexport/tests/graphdef_to_mlir/invalid_generic_func_with_empty_control_result.pbtxt, tensorflow/core/ir/importexport/tests/graphdef_to_mlir/invalid_generic_func_with_empty_input.pbtxt, tensorflow/core/ir/importexport/tests/graphdef_to_mlir/invalid_generic_func_with_empty_result.pbtxt
Commit date: 2022-05-20 04:09:01 -0700
Commit message: [tfg][functiondef_import] Error on empty edge names

Return an error in the generic function importer if an edge name is empty.

PiperOrigin-RevId: 449953062

Diff: diff --git a/tensorflow/core/ir/importexport/functiondef_import.cc b/tensorflow/core/ir/importexport/functiondef_import.cc
index 781baeaf63251..f891652919767 100644
--- a/tensorflow/core/ir/importexport/functiondef_import.cc
+++ b/tensorflow/core/ir/importexport/functiondef_import.cc
@@ -31,17 +31,19 @@ limitations under the License.
 #include "tensorflow/core/ir/importexport/convert_attributes.h"
 #include "tensorflow/core/ir/importexport/convert_types.h"
 #include "tensorflow/core/ir/ops.h"
 #include "tensorflow/core/platform/errors.h"
 #include "tensorflow/core/platform/status.h"
+#include "tensorflow/core/platform/statusor.h"
 
 using tensorflow::AttrValue;
 using tensorflow::FunctionDef;
 using tensorflow::NodeDef;
 using tensorflow::OpDef;
 using tensorflow::OpDef_AttrDef;
 using tensorflow::Status;
+using tensorflow::StatusOr;
 using tensorflow::errors::InvalidArgument;
 using tensorflow::protobuf::RepeatedPtrField;
 
 #define DEBUG_TYPE "graphdef-to-mlir"
 
@@ -164,13 +166,16 @@ Status ImportNodes(ValueMapManager value_manager,
   for (const NodeDef& node : nodes) {
     DVLOG(1) << "Processing node " << node.name() << "\n";
     if (node.op().empty()) return InvalidArgument("empty op type");
     OperationState state(unknown_loc, absl::StrCat("tfg.", node.op()));
     // Fetch the inputs, creating placeholder if an input hasn't been visited.
-    for (const std::string& input : node.input())
+    for (const std::string& input : node.input()) {
+      if (input.empty())
+        return InvalidArgument("Node '", node.name(), "' has an empty input");
       state.operands.push_back(
           value_manager.GetValueOrCreatePlaceholder(input));
+    }
     // Retrieve the entry in the nodes_map for this node and infer the result
     // count from what was inferred during the first traversal above.
     state.types.push_back(placeholder_ty);
     state.types.push_back(control_ty);
     // Handle attributes.
@@ -459,25 +464,35 @@ Status ImportGenericFunction(
   // previously.
   SmallVector<Value> ret_vals(func.ret_size() + func.control_ret_size(),
                               Value());
   for (const auto& ret_val : func.ret()) {
     auto position = output_name_to_position.find(ret_val.first);
-    if (position == output_name_to_position.end())
+    if (position == output_name_to_position.end()) {
       return InvalidArgument(
           "Can't import function, returned value references unknown output "
           "argument ",
           ret_val.first);
+    }
+    if (ret_val.second.empty()) {
+      return InvalidArgument("Function '", func.signature().name(),
+                             "' has empty result name");
+    }
     ret_vals[position->second] =
         value_manager.GetValueOrCreatePlaceholder(ret_val.second);
   }
   for (const auto& ret_val : func.control_ret()) {
     auto position = control_output_to_position.find(ret_val.first);
-    if (position == control_output_to_position.end())
+    if (position == control_output_to_position.end()) {
       return InvalidArgument(
           "Can't import function, returned value references unknown output "
           "argument ",
           ret_val.first);
+    }
+    if (ret_val.second.empty()) {
+      return InvalidArgument("Function '", func.signature().name(),
+                             "' has empty control result name");
+    }
     Value result = value_manager.GetValueOrCreatePlaceholder(
         (Twine("^") + ret_val.second).str());
     if (!result.getType().isa<ControlType>())
       return InvalidArgument("failed to map returned value ", ret_val.second,
                              ", isn't a control output");
diff --git a/tensorflow/core/ir/importexport/tests/graphdef_to_mlir/invalid_generic_func_with_empty_control_result.pbtxt b/tensorflow/core/ir/importexport/tests/graphdef_to_mlir/invalid_generic_func_with_empty_control_result.pbtxt
new file mode 100644
index 0000000000000..b7d82f87842dc
--- /dev/null
+++ b/tensorflow/core/ir/importexport/tests/graphdef_to_mlir/invalid_generic_func_with_empty_control_result.pbtxt
@@ -0,0 +1,26 @@
+# RUN: not tfg-translate -graphdef-to-mlir %s 2>&1 | FileCheck %s
+
+# CHECK: Function 'foo' has empty control result name
+
+library {
+  function {
+    signature {
+      name: "foo"
+      control_output: "output"
+    }
+    node_def {
+      name: "y"
+      op: "NoOp"
+      attr {
+        key: "T"
+        value {
+          placeholder: "T"
+        }
+      }
+    }
+    control_ret {
+      key: "output"
+      value: ""
+    }
+  }
+}
diff --git a/tensorflow/core/ir/importexport/tests/graphdef_to_mlir/invalid_generic_func_with_empty_input.pbtxt b/tensorflow/core/ir/importexport/tests/graphdef_to_mlir/invalid_generic_func_with_empty_input.pbtxt
new file mode 100644
index 0000000000000..5b1c3cff4f85a
--- /dev/null
+++ b/tensorflow/core/ir/importexport/tests/graphdef_to_mlir/invalid_generic_func_with_empty_input.pbtxt
@@ -0,0 +1,22 @@
+# RUN: not tfg-translate -graphdef-to-mlir %s 2>&1 | FileCheck %s
+
+# CHECK: Node 'y' has an empty input
+
+library {
+  function {
+    signature {
+      name: "foo"
+    }
+    node_def {
+      name: "y"
+      input: ""
+      op: "Identity"
+      attr {
+        key: "T"
+        value {
+          placeholder: "T"
+        }
+      }
+    }
+  }
+}
diff --git a/tensorflow/core/ir/importexport/tests/graphdef_to_mlir/invalid_generic_func_with_empty_result.pbtxt b/tensorflow/core/ir/importexport/tests/graphdef_to_mlir/invalid_generic_func_with_empty_result.pbtxt
new file mode 100644
index 0000000000000..f4fc5263ebf79
--- /dev/null
+++ b/tensorflow/core/ir/importexport/tests/graphdef_to_mlir/invalid_generic_func_with_empty_result.pbtxt
@@ -0,0 +1,29 @@
+# RUN: not tfg-translate -graphdef-to-mlir %s 2>&1 | FileCheck %s
+
+# CHECK: Function 'foo' has empty result name
+
+library {
+  function {
+    signature {
+      name: "foo"
+      output_arg {
+        name: "output"
+        type: DT_INT32
+      }
+    }
+    node_def {
+      name: "y"
+      op: "NoOp"
+      attr {
+        key: "T"
+        value {
+          placeholder: "T"
+        }
+      }
+    }
+    ret {
+      key: "output"
+      value: ""
+    }
+  }
+}

Vulnerable code:     for (const std::string& input : node.input())
    if (position == output_name_to_position.end())
    if (position == control_output_to_position.end())
Fixed code: #include "tensorflow/core/platform/statusor.h"
using tensorflow::StatusOr;
    for (const std::string& input : node.input()) {
      if (input.empty())
        return InvalidArgument("Node '", node.name(), "' has an empty input");
    }
    if (position == output_name_to_position.end()) {
    }
    if (ret_val.second.empty()) {
      return InvalidArgument("Function '", func.signature().name(),
                             "' has empty result name");
    }
    if (position == control_output_to_position.end()) {
    }
    if (ret_val.second.empty()) {
      return InvalidArgument("Function '", func.signature().name(),
                             "' has empty control result name");
    }
# RUN: not tfg-translate -graphdef-to-mlir %s 2>&1 | FileCheck %s

# CHECK: Function 'foo' has empty control result name

library {
  function {
    signature {
      name: "foo"
      control_output: "output"
    }
    node_def {
      name: "y"
      op: "NoOp"
      attr {
        key: "T"
        value {
          placeholder: "T"
        }
      }
    }
    control_ret {
      key: "output"
      value: ""
    }
  }
}
# RUN: not tfg-translate -graphdef-to-mlir %s 2>&1 | FileCheck %s

# CHECK: Node 'y' has an empty input

library {
  function {
    signature {
      name: "foo"
    }
    node_def {
      name: "y"
      input: ""
      op: "Identity"
      attr {
        key: "T"
        value {
          placeholder: "T"
        }
      }
    }
  }
}
# RUN: not tfg-translate -graphdef-to-mlir %s 2>&1 | FileCheck %s

# CHECK: Function 'foo' has empty result name

library {
  function {
    signature {
      name: "foo"
      output_arg {
        name: "output"
        type: DT_INT32
      }
    }
    node_def {
      name: "y"
      op: "NoOp"
      attr {
        key: "T"
        value {
          placeholder: "T"
        }
      }
    }
    ret {
      key: "output"
      value: ""
    }
  }
}