CVE-2021-29533: Improper Check for Unusual or Exceptional Conditions (CWE-754)
value: TensorFlow is an end-to-end open source platform for machine learning. An attacker can trigger a denial of service via a `CHECK` failure by passing an empty image to `tf.raw_ops.DrawBoundingBoxes`. This is because the implementation(https://github.com/tensorflow/tensorflow/blob/ea34a18dc3f5c8d80a40ccca1404f343b5d55f91/tensorflow/core/kernels/image/draw_bounding_box_op.cc#L148-L165) uses `CHECK_*` assertions instead of `OP_REQUIRES` to validate user controlled inputs. Whereas `OP_REQUIRES` allows returning an error condition back to the user, the `CHECK_*` macros result in a crash if the condition is false, similar to `assert`. In this case, `height` is 0 from the `images` input. This results in `max_box_row_clamp` being negative and the assertion being falsified, followed by aborting program execution. The fix will be included in TensorFlow 2.5.0. We will also cherrypick this commit on TensorFlow 2.4.2, TensorFlow 2.3.3, TensorFlow 2.2.3 and TensorFlow 2.1.4, as these are also affected and still in supported range.
CWE description: The product does not check or incorrectly checks for unusual or exceptional conditions that are not expected to occur frequently during day to day operation of the product.
CVSS v3 base score: 5.5
Published: 2021-05-14T20:15Z
Repository: https://github.com/tensorflow/tensorflow
Language: C++
Files changed: tensorflow/core/kernels/image/draw_bounding_box_op.cc
Commit date: 2021-04-21 16:02:30 -0700
Commit message: Fix overflow CHECK issue with `tf.raw_ops.DrawBoundingBoxes`.

PiperOrigin-RevId: 369753591
Change-Id: I3b45fc98ee0d28a3c20b7e9c995aa647c976ec40

Diff: diff --git a/tensorflow/core/kernels/image/draw_bounding_box_op.cc b/tensorflow/core/kernels/image/draw_bounding_box_op.cc
index 30de99b7d560a..73db76333f086 100644
--- a/tensorflow/core/kernels/image/draw_bounding_box_op.cc
+++ b/tensorflow/core/kernels/image/draw_bounding_box_op.cc
@@ -145,26 +145,50 @@ class DrawBoundingBoxesOp : public OpKernel {
           continue;
         }
 
         // At this point, {min,max}_box_{row,col}_clamp are inside the
         // image.
-        CHECK_GE(min_box_row_clamp, 0);
-        CHECK_GE(max_box_row_clamp, 0);
-        CHECK_LT(min_box_row_clamp, height);
-        CHECK_LT(max_box_row_clamp, height);
-        CHECK_GE(min_box_col_clamp, 0);
-        CHECK_GE(max_box_col_clamp, 0);
-        CHECK_LT(min_box_col_clamp, width);
-        CHECK_LT(max_box_col_clamp, width);
+        OP_REQUIRES(
+            context, min_box_row_clamp >= 0,
+            errors::InvalidArgument("Min box row clamp is less than 0."));
+        OP_REQUIRES(
+            context, max_box_row_clamp >= 0,
+            errors::InvalidArgument("Max box row clamp is less than 0."));
+        OP_REQUIRES(context, min_box_row_clamp <= height,
+                    errors::InvalidArgument(
+                        "Min box row clamp is greater than height."));
+        OP_REQUIRES(context, max_box_row_clamp <= height,
+                    errors::InvalidArgument(
+                        "Max box row clamp is greater than height."));
+
+        OP_REQUIRES(
+            context, min_box_col_clamp >= 0,
+            errors::InvalidArgument("Min box col clamp is less than 0."));
+        OP_REQUIRES(
+            context, max_box_col_clamp >= 0,
+            errors::InvalidArgument("Max box col clamp is less than 0."));
+        OP_REQUIRES(context, min_box_col_clamp <= width,
+                    errors::InvalidArgument(
+                        "Min box col clamp is greater than width."));
+        OP_REQUIRES(context, max_box_col_clamp <= width,
+                    errors::InvalidArgument(
+                        "Max box col clamp is greater than width."));
 
         // At this point, the min_box_row and min_box_col are either
         // in the image or above/left of it, and max_box_row and
         // max_box_col are either in the image or below/right or it.
-        CHECK_LT(min_box_row, height);
-        CHECK_GE(max_box_row, 0);
-        CHECK_LT(min_box_col, width);
-        CHECK_GE(max_box_col, 0);
+
+        OP_REQUIRES(
+            context, min_box_row <= height,
+            errors::InvalidArgument("Min box row is greater than height."));
+        OP_REQUIRES(context, max_box_row >= 0,
+                    errors::InvalidArgument("Max box row is less than 0."));
+        OP_REQUIRES(
+            context, min_box_col <= width,
+            errors::InvalidArgument("Min box col is greater than width."));
+        OP_REQUIRES(context, max_box_col >= 0,
+                    errors::InvalidArgument("Max box col is less than 0."));
 
         // Draw top line.
         if (min_box_row >= 0) {
           for (int64 j = min_box_col_clamp; j <= max_box_col_clamp; ++j)
             for (int64 c = 0; c < depth; c++) {

Vulnerable code:         CHECK_GE(min_box_row_clamp, 0);
        CHECK_GE(max_box_row_clamp, 0);
        CHECK_LT(min_box_row_clamp, height);
        CHECK_LT(max_box_row_clamp, height);
        CHECK_GE(min_box_col_clamp, 0);
        CHECK_GE(max_box_col_clamp, 0);
        CHECK_LT(min_box_col_clamp, width);
        CHECK_LT(max_box_col_clamp, width);
        CHECK_LT(min_box_row, height);
        CHECK_GE(max_box_row, 0);
        CHECK_LT(min_box_col, width);
        CHECK_GE(max_box_col, 0);
Fixed code:         OP_REQUIRES(
            context, min_box_row_clamp >= 0,
            errors::InvalidArgument("Min box row clamp is less than 0."));
        OP_REQUIRES(
            context, max_box_row_clamp >= 0,
            errors::InvalidArgument("Max box row clamp is less than 0."));
        OP_REQUIRES(context, min_box_row_clamp <= height,
                    errors::InvalidArgument(
                        "Min box row clamp is greater than height."));
        OP_REQUIRES(context, max_box_row_clamp <= height,
                    errors::InvalidArgument(
                        "Max box row clamp is greater than height."));

        OP_REQUIRES(
            context, min_box_col_clamp >= 0,
            errors::InvalidArgument("Min box col clamp is less than 0."));
        OP_REQUIRES(
            context, max_box_col_clamp >= 0,
            errors::InvalidArgument("Max box col clamp is less than 0."));
        OP_REQUIRES(context, min_box_col_clamp <= width,
                    errors::InvalidArgument(
                        "Min box col clamp is greater than width."));
        OP_REQUIRES(context, max_box_col_clamp <= width,
                    errors::InvalidArgument(
                        "Max box col clamp is greater than width."));

        OP_REQUIRES(
            context, min_box_row <= height,
            errors::InvalidArgument("Min box row is greater than height."));
        OP_REQUIRES(context, max_box_row >= 0,
                    errors::InvalidArgument("Max box row is less than 0."));
        OP_REQUIRES(
            context, min_box_col <= width,
            errors::InvalidArgument("Min box col is greater than width."));
        OP_REQUIRES(context, max_box_col >= 0,
                    errors::InvalidArgument("Max box col is less than 0."));