CVE-2022-23581: Reachable Assertion (CWE-617)
value: Tensorflow is an Open Source Machine Learning Framework. The Grappler optimizer in TensorFlow can be used to cause a denial of service by altering a `SavedModel` such that `IsSimplifiableReshape` would trigger `CHECK` failures. 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 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: 6.5
Published: 2022-02-04T23:15Z
Repository: https://github.com/tensorflow/tensorflow
Language: C
Files changed: tensorflow/core/grappler/optimizers/constant_folding.cc, tensorflow/core/grappler/optimizers/constant_folding.h
Commit date: 2021-11-11 09:04:14 -0800
Commit message: Make `IsSimplifiableReshape` return `Status` instead of `bool`.

This is to allow remove `CHECK`-fails in subsequent commits.

PiperOrigin-RevId: 409160987
Change-Id: I3f050218a3832271395c4372a0b8ea05f1c03d80

Diff: diff --git a/tensorflow/core/grappler/optimizers/constant_folding.cc b/tensorflow/core/grappler/optimizers/constant_folding.cc
index 3706d4096ecb..9087ddb59941 100644
--- a/tensorflow/core/grappler/optimizers/constant_folding.cc
+++ b/tensorflow/core/grappler/optimizers/constant_folding.cc
@@ -1682,45 +1682,50 @@ Status ConstantFolding::FoldGraph(
     }
   }
   return Status::OK();
 }
 
-bool ConstantFolding::IsSimplifiableReshape(
+Status ConstantFolding::IsSimplifiableReshape(
     const NodeDef& node, const GraphProperties& properties) const {
   if (!IsReshape(node)) {
-    return false;
+    return errors::Internal("Node ", node.name(), " is not a Reshape node");
   }
   CHECK_LE(2, node.input_size());
   const NodeDef* new_shape = node_map_->GetNode(node.input(1));
   if (!IsReallyConstant(*new_shape)) {
-    return false;
+    return errors::Internal("Node ", node.name(), " has shape ",
+                            new_shape->DebugString(),
+                            " which is not a constant");
   }
   TensorVector outputs;
   auto outputs_cleanup = gtl::MakeCleanup([&outputs] {
     for (const auto& output : outputs) {
       delete output.tensor;
     }
   });
 
   Status s = EvaluateNode(*new_shape, TensorVector(), &outputs);
   if (!s.ok()) {
-    return false;
+    return errors::Internal("Could not evaluate node ", node.name());
   }
   CHECK_EQ(1, outputs.size());
 
   const std::vector<OpInfo::TensorProperties>& props =
       properties.GetInputProperties(node.name());
   if (props.empty()) {
-    return false;
+    return errors::Internal("Node ", node.name(), " has no properties");
   }
   const OpInfo::TensorProperties& prop = props[0];
   if (prop.dtype() == DT_INVALID) {
-    return false;
+    return errors::Internal("Node ", node.name(), " has property ",
+                            prop.DebugString(), " with invalid dtype");
   }
   const PartialTensorShape shape(prop.shape());
   if (!shape.IsFullyDefined()) {
-    return false;
+    return errors::Internal("Node ", node.name(), " has property ",
+                            prop.DebugString(), " with shape ",
+                            shape.DebugString(), " which is not fully defined");
   }
 
   PartialTensorShape new_dims;
   if (outputs[0]->dtype() == DT_INT32) {
     std::vector<int32> shp;
@@ -1736,11 +1741,16 @@ bool ConstantFolding::IsSimplifiableReshape(
       shp.push_back(dim);
     }
     TF_CHECK_OK(TensorShapeUtils::MakeShape(shp, &new_dims));
   }
 
-  return shape.IsCompatibleWith(new_dims);
+  if (!shape.IsCompatibleWith(new_dims)) {
+    return errors::Internal("Expected shape ", shape.DebugString(),
+                            "to be compatible with ", new_dims.DebugString());
+  }
+
+  return Status::OK();
 }
 
 #define IS_VALUE_CASE(DTYPE, VALUE)                   \
   case DTYPE:                                         \
     return AllValuesAre<EnumToDataType<DTYPE>::Type>( \
@@ -2923,11 +2933,11 @@ bool ConstantFolding::SimplifyReduction(GraphDef* optimized_graph,
 }
 
 bool ConstantFolding::SimplifyReshape(const GraphProperties& properties,
                                       bool use_shape_info, NodeDef* node) {
   if (!use_shape_info || node->attr().count("T") == 0 ||
-      !IsSimplifiableReshape(*node, properties)) {
+      !IsSimplifiableReshape(*node, properties).ok()) {
     return false;
   }
   DataType output_type = node->attr().at("T").type();
   node->set_op("Identity");
   EraseRegularNodeAttributes(node);
diff --git a/tensorflow/core/grappler/optimizers/constant_folding.h b/tensorflow/core/grappler/optimizers/constant_folding.h
index 1de88fed0f99..9305aa09764f 100644
--- a/tensorflow/core/grappler/optimizers/constant_folding.h
+++ b/tensorflow/core/grappler/optimizers/constant_folding.h
@@ -127,12 +127,12 @@ class ConstantFolding : public GraphOptimizer {
 
   void ReplaceDivisionOfOnesByReciprocal(NodeDef* node, GraphDef* graph);
   Status FoldGraph(const GraphProperties& properties, GraphDef* output,
                    absl::flat_hash_set<string>* nodes_to_not_simplify);
 
-  bool IsSimplifiableReshape(const NodeDef& node,
-                             const GraphProperties& properties) const;
+  Status IsSimplifiableReshape(const NodeDef& node,
+                               const GraphProperties& properties) const;
   Status SimplifyGraph(GraphDef* optimized_graph, GraphProperties* properties,
                        absl::flat_hash_set<string>* nodes_to_not_simplify);
   Status SimplifyNode(NodeDef* node, GraphDef* optimized_graph,
                       GraphProperties* properties);
 

Vulnerable code: bool ConstantFolding::IsSimplifiableReshape(
    return false;
    return false;
    return false;
    return false;
    return false;
    return false;
  return shape.IsCompatibleWith(new_dims);
      !IsSimplifiableReshape(*node, properties)) {
  bool IsSimplifiableReshape(const NodeDef& node,
                             const GraphProperties& properties) const;
Fixed code: Status ConstantFolding::IsSimplifiableReshape(
    return errors::Internal("Node ", node.name(), " is not a Reshape node");
    return errors::Internal("Node ", node.name(), " has shape ",
                            new_shape->DebugString(),
                            " which is not a constant");
    return errors::Internal("Could not evaluate node ", node.name());
    return errors::Internal("Node ", node.name(), " has no properties");
    return errors::Internal("Node ", node.name(), " has property ",
                            prop.DebugString(), " with invalid dtype");
    return errors::Internal("Node ", node.name(), " has property ",
                            prop.DebugString(), " with shape ",
                            shape.DebugString(), " which is not fully defined");
  if (!shape.IsCompatibleWith(new_dims)) {
    return errors::Internal("Expected shape ", shape.DebugString(),
                            "to be compatible with ", new_dims.DebugString());
  }

  return Status::OK();
      !IsSimplifiableReshape(*node, properties).ok()) {
  Status IsSimplifiableReshape(const NodeDef& node,
                               const GraphProperties& properties) const;