Vulnerability: Division by 0 in `QuantizedConv2D` ### Impact
An attacker can trigger a division by 0 in `tf.raw_ops.QuantizedConv2D`:

```python
import tensorflow as tf

input = tf.zeros([1, 1, 1, 1], dtype=tf.quint8)
filter = tf.constant([], shape=[1, 0, 1, 1], dtype=tf.quint8)
min_input = tf.constant(0.0)
max_input = tf.constant(0.0001)
min_filter = tf.constant(0.0)
max_filter = tf.constant(0.0001)
strides = [1, 1, 1, 1]
padding = "SAME"               
                               

tf.raw_ops.QuantizedConv2D(input=input, filter=filter, min_input=min_input, max_input=max_input, min_filter=min_filter, max_filter=max_filter, strides=strides, padding=padding)
```
This is because the [implementation](https://github.com/tensorflow/tensorflow/blob/00e9a4d67d76703fa1aee33dac582acf317e0e81/tensorflow/core/kernels/quantized_conv_ops.cc#L257-L259) does a division by a quantity that is controlled by the caller: 

```cc
const int filter_value_count = filter_width * filter_height * input_depth;
const int64 patches_per_chunk = kMaxChunkSize / (filter_value_count * sizeof(T1));
```
  
### Patches
We have patched the issue in GitHub commit [cfa91be9863a91d5105a3b4941096044ab32036b](https://github.com/tensorflow/tensorflow/commit/cfa91be9863a91d5105a3b4941096044ab32036b).

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.

### For more information
Please consult [our security guide](https://github.com/tensorflow/tensorflow/blob/master/SECURITY.md) for more information regarding the security model and how to contact us with issues and questions.

### Attribution
This vulnerability has been reported by Ying Wang and Yakun Zhang of Baidu X-Team.
Vulnerability ID: GHSA-x4g7-fvjj-prg8
CWE: CWE-369
Severity score: 2.5
Project: https://github.com/tensorflow/tensorflow
Published: 2021-05-21
File: tensorflow/core/kernels/quantized_conv_ops.cc
Commit message: Fix one FPE and remove two CHECK-fails.

PiperOrigin-RevId: 369349640
Change-Id: I1fedbfc2b5bab635c5cb51f103d7c9176f79831a
Commit author: Mihai Maruseac

Vulnerable version (before fix):
 limitations under the License. #include <algorithm> #include <vector>  #define EIGEN_USE_THREADS  #define GEMMLOWP_ALLOW_SLOW_SCALAR_FALLBACK class Im2ColConvFunctor {       return;     }      CHECK_GT(output_width, 0);     CHECK_GT(output_height, 0);     int filter_left_offset;     int filter_top_offset;     if (padding == VALID) { class Im2ColConvFunctor {     // by the width, then the height. This is the standard memory order in the     // image world if it helps to visualize it.     const int filter_value_count = filter_width * filter_height * input_depth;     const int64 patches_per_chunk =         kMaxChunkSize / (filter_value_count * sizeof(T1));     const int64 chunk_value_count =

Fixed version (after fix):
 limitations under the License. #include <algorithm> #include <vector>  #include "tensorflow/core/platform/errors.h"  #define EIGEN_USE_THREADS  #define GEMMLOWP_ALLOW_SLOW_SCALAR_FALLBACK class Im2ColConvFunctor {       return;     }      OP_REQUIRES(         context, output_width > 0,         errors::InvalidArgument("output_width must be strictly positive"));     OP_REQUIRES(         context, output_height > 0,         errors::InvalidArgument("output_height must be strictly positive"));     int filter_left_offset;     int filter_top_offset;     if (padding == VALID) { class Im2ColConvFunctor {     // by the width, then the height. This is the standard memory order in the     // image world if it helps to visualize it.     const int filter_value_count = filter_width * filter_height * input_depth;     OP_REQUIRES(context, filter_value_count > 0,                 errors::InvalidArgument(                     "filter patch must contain at least one element"));     const int64 patches_per_chunk =         kMaxChunkSize / (filter_value_count * sizeof(T1));     const int64 chunk_value_count =

Patch:
patch: @@ -18,6 +18,8 @@ limitations under the License.
 #include <algorithm>
 #include <vector>
 
+#include "tensorflow/core/platform/errors.h"
+
 #define EIGEN_USE_THREADS
 
 #define GEMMLOWP_ALLOW_SLOW_SCALAR_FALLBACK
@@ -227,8 +229,12 @@ class Im2ColConvFunctor {
       return;
     }
 
-    CHECK_GT(output_width, 0);
-    CHECK_GT(output_height, 0);
+    OP_REQUIRES(
+        context, output_width > 0,
+        errors::InvalidArgument("output_width must be strictly positive"));
+    OP_REQUIRES(
+        context, output_height > 0,
+        errors::InvalidArgument("output_height must be strictly positive"));
     int filter_left_offset;
     int filter_top_offset;
     if (padding == VALID) {
@@ -255,6 +261,9 @@ class Im2ColConvFunctor {
     // by the width, then the height. This is the standard memory order in the
     // image world if it helps to visualize it.
     const int filter_value_count = filter_width * filter_height * input_depth;
+    OP_REQUIRES(context, filter_value_count > 0,
+                errors::InvalidArgument(
+                    "filter patch must contain at least one element"));
     const int64 patches_per_chunk =
         kMaxChunkSize / (filter_value_count * sizeof(T1));
     const int64 chunk_value_count =
path: tensorflow/core/kernels/quantized_conv_ops.cc