CVE-2022-29212: Improper Input Validation (CWE-20)
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, certain TFLite models that were created using TFLite model converter would crash when loaded in the TFLite interpreter. The culprit is that during quantization the scale of values could be greater than 1 but code was always assuming sub-unit scaling. Thus, since code was calling `QuantizeMultiplierSmallerThanOneExp`, the `TFLITE_CHECK_LT` assertion would trigger and abort the process. Versions 2.9.0, 2.8.1, 2.7.2, and 2.6.4 contain a patch for this issue.
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: 5.5
Published: 2022-05-21T00:15Z
Repository: https://github.com/tensorflow/tensorflow
Language: C++
Files changed: tensorflow/lite/kernels/comparisons.cc, tensorflow/lite/kernels/comparisons_test.cc
Commit date: 2022-03-07 15:35:57 -0800
Commit message: Improve to cover scale value greater than one

PiperOrigin-RevId: 433050921

Diff: diff --git a/tensorflow/lite/kernels/comparisons.cc b/tensorflow/lite/kernels/comparisons.cc
index d0a1876c5c654..c3824c1db0170 100644
--- a/tensorflow/lite/kernels/comparisons.cc
+++ b/tensorflow/lite/kernels/comparisons.cc
@@ -79,26 +79,35 @@ TfLiteStatus ComparisonPrepare(TfLiteContext* context, TfLiteNode* node) {
 TfLiteStatus ComparisonPrepareStringAllowed(TfLiteContext* context,
                                             TfLiteNode* node) {
   return ComparisonPrepareCommon(context, node, true);
 }
 
+void QuantizeMultiplier(double double_multiplier, int32_t* quantized_multiplier,
+                        int* left_shift) {
+  if (double_multiplier < 1.0) {
+    QuantizeMultiplierSmallerThanOneExp(double_multiplier, quantized_multiplier,
+                                        left_shift);
+  } else {
+    QuantizeMultiplierGreaterThanOne(double_multiplier, quantized_multiplier,
+                                     left_shift);
+  }
+}
+
 template <typename input_dtype, reference_ops::ComparisonFn<int32> opname>
 void ComparisonQuantized(const TfLiteTensor* input1, const TfLiteTensor* input2,
                          TfLiteTensor* output, bool requires_broadcast) {
   if (input1->type == kTfLiteUInt8 || input1->type == kTfLiteInt8) {
     auto input1_offset = -input1->params.zero_point;
     auto input2_offset = -input2->params.zero_point;
     const int left_shift = 8;
 
     int32 input1_multiplier;
-    int input1_shift;
-    QuantizeMultiplierSmallerThanOneExp(input1->params.scale,
-                                        &input1_multiplier, &input1_shift);
     int32 input2_multiplier;
+    int input1_shift;
     int input2_shift;
-    QuantizeMultiplierSmallerThanOneExp(input2->params.scale,
-                                        &input2_multiplier, &input2_shift);
+    QuantizeMultiplier(input1->params.scale, &input1_multiplier, &input1_shift);
+    QuantizeMultiplier(input2->params.scale, &input2_multiplier, &input2_shift);
 
     ComparisonParams op_params;
     op_params.left_shift = left_shift;
     op_params.input1_offset = input1_offset;
     op_params.input1_multiplier = input1_multiplier;
diff --git a/tensorflow/lite/kernels/comparisons_test.cc b/tensorflow/lite/kernels/comparisons_test.cc
index f8cf6dee74c4b..074d0f1f61513 100644
--- a/tensorflow/lite/kernels/comparisons_test.cc
+++ b/tensorflow/lite/kernels/comparisons_test.cc
@@ -651,10 +651,30 @@ TEST(ComparisonsTest, QuantizedInt8GreaterWithBroadcast) {
                 ElementsAre(true, false, false, false, true, true))
         << "With shape number " << i;
   }
 }
 
+TEST(ComparisonsTest,
+     QuantizedInt8GreaterWithBroadcastMultiplierGreaterThanOne) {
+  const float kMin = -127.f;
+  const float kMax = 127.f;
+  std::vector<std::vector<int>> test_shapes = {
+      {6}, {2, 3}, {2, 1, 3}, {1, 3, 1, 2}};
+  for (int i = 0; i < test_shapes.size(); ++i) {
+    ComparisonOpModel model({TensorType_INT8, test_shapes[i], kMin, kMax},
+                            {TensorType_INT8, {}, kMin, kMax}, TensorType_INT8,
+                            BuiltinOperator_GREATER);
+    model.QuantizeAndPopulate<int8_t>(model.input1(),
+                                      {572, -2, -71, 8, 11, 20});
+    model.QuantizeAndPopulate<int8_t>(model.input2(), {8});
+    model.Invoke();
+    EXPECT_THAT(model.GetOutput(),
+                ElementsAre(true, false, false, false, true, true))
+        << "With shape number " << i;
+  }
+}
+
 TEST(ComparisonsTest, QuantizedUInt8GreaterEqualWithBroadcast) {
   const float kMin = -1.f;
   const float kMax = 128.f;
   std::vector<std::vector<int>> test_shapes = {
       {6}, {2, 3}, {2, 1, 3}, {1, 3, 1, 2}};

Vulnerable code:     int input1_shift;
    QuantizeMultiplierSmallerThanOneExp(input1->params.scale,
                                        &input1_multiplier, &input1_shift);
    QuantizeMultiplierSmallerThanOneExp(input2->params.scale,
                                        &input2_multiplier, &input2_shift);
Fixed code: void QuantizeMultiplier(double double_multiplier, int32_t* quantized_multiplier,
                        int* left_shift) {
  if (double_multiplier < 1.0) {
    QuantizeMultiplierSmallerThanOneExp(double_multiplier, quantized_multiplier,
                                        left_shift);
  } else {
    QuantizeMultiplierGreaterThanOne(double_multiplier, quantized_multiplier,
                                     left_shift);
  }
}

    int input1_shift;
    QuantizeMultiplier(input1->params.scale, &input1_multiplier, &input1_shift);
    QuantizeMultiplier(input2->params.scale, &input2_multiplier, &input2_shift);
TEST(ComparisonsTest,
     QuantizedInt8GreaterWithBroadcastMultiplierGreaterThanOne) {
  const float kMin = -127.f;
  const float kMax = 127.f;
  std::vector<std::vector<int>> test_shapes = {
      {6}, {2, 3}, {2, 1, 3}, {1, 3, 1, 2}};
  for (int i = 0; i < test_shapes.size(); ++i) {
    ComparisonOpModel model({TensorType_INT8, test_shapes[i], kMin, kMax},
                            {TensorType_INT8, {}, kMin, kMax}, TensorType_INT8,
                            BuiltinOperator_GREATER);
    model.QuantizeAndPopulate<int8_t>(model.input1(),
                                      {572, -2, -71, 8, 11, 20});
    model.QuantizeAndPopulate<int8_t>(model.input2(), {8});
    model.Invoke();
    EXPECT_THAT(model.GetOutput(),
                ElementsAre(true, false, false, false, true, true))
        << "With shape number " << i;
  }
}