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

```python
import tensorflow as tf

input = tf.constant([], shape=[0, 0, 0, 0], dtype=tf.float32)
filter = tf.constant([], shape=[0, 0, 0, 0], dtype=tf.float32)

strides = [1, 1, 1, 1]
padding = "SAME"
                               
tf.raw_ops.Conv2D(input=input, filter=filter, strides=strides, padding=padding)
```                            
                               
This is because the [implementation](https://github.com/tensorflow/tensorflow/blob/988087bd83f144af14087fe4fecee2d250d93737/tensorflow/core/kernels/conv_ops.cc#L261-L263) does a division by a quantity that is controlled by the caller:
```cc
  const int64 patch_depth = filter.dim_size(2);
  if (in_depth % patch_depth != 0) { ... }
```
  
### Patches
We have patched the issue in GitHub commit [b12aa1d44352de21d1a6faaf04172d8c2508b42b](https://github.com/tensorflow/tensorflow/commit/b12aa1d44352de21d1a6faaf04172d8c2508b42b).
  
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-4vf2-4xcg-65cx
CWE: CWE-369
Severity score: 2.5
Project: https://github.com/tensorflow/tensorflow
Published: 2021-05-21
File: tensorflow/core/kernels/conv_ops.cc
Commit message: Fix one more FPE.

PiperOrigin-RevId: 369346568
Change-Id: I840fd575962adc879713a4c9cc59e6da3331caa7
Commit author: Mihai Maruseac

Vulnerable version (before fix):
 struct LaunchConv2DOp<CPUDevice, T> {     const int64 out_depth = output->dim_size(3);     const int64 patch_depth = filter.dim_size(2);      if (in_depth % patch_depth != 0) {       ctx->SetStatus(errors::InvalidArgument(           "input depth must be evenly divisible by filter depth: ", in_depth, struct LaunchConv2DOp<CPUDevice, T> {     }      const int64 num_groups = in_depth / patch_depth;     if (out_depth % num_groups != 0 || out_depth < num_groups) {       ctx->SetStatus(errors::InvalidArgument(           "output depth must be evenly divisible by number of groups: ", Status ComputeConv2DDimension(const Conv2DParameters& params,               errors::InvalidArgument("Patch depth too large"));   const int in_depth = static_cast<int>(in_depth_raw);   const int patch_depth = static_cast<int>(patch_depth_raw);   TF_REQUIRES(in_depth % patch_depth == 0,               errors::InvalidArgument(                   "input depth must be evenly divisible by filter depth: ",

Fixed version (after fix):
 struct LaunchConv2DOp<CPUDevice, T> {     const int64 out_depth = output->dim_size(3);     const int64 patch_depth = filter.dim_size(2);      if (patch_depth <= 0) {       ctx->SetStatus(errors::InvalidArgument(           "filter depth must be stricly positive, got ", patch_depth));       return;     }     if (in_depth % patch_depth != 0) {       ctx->SetStatus(errors::InvalidArgument(           "input depth must be evenly divisible by filter depth: ", in_depth, struct LaunchConv2DOp<CPUDevice, T> {     }      const int64 num_groups = in_depth / patch_depth;     if (num_groups <= 0) {       ctx->SetStatus(errors::InvalidArgument(           "number of groups must be stricly positive, got ", num_groups));       return;     }     if (out_depth % num_groups != 0 || out_depth < num_groups) {       ctx->SetStatus(errors::InvalidArgument(           "output depth must be evenly divisible by number of groups: ", Status ComputeConv2DDimension(const Conv2DParameters& params,               errors::InvalidArgument("Patch depth too large"));   const int in_depth = static_cast<int>(in_depth_raw);   const int patch_depth = static_cast<int>(patch_depth_raw);   TF_REQUIRES(patch_depth > 0,               errors::InvalidArgument(                   "filter depth must be stricly positive, got ", patch_depth));   TF_REQUIRES(in_depth % patch_depth == 0,               errors::InvalidArgument(                   "input depth must be evenly divisible by filter depth: ",

Patch:
patch: @@ -260,6 +260,11 @@ struct LaunchConv2DOp<CPUDevice, T> {
     const int64 out_depth = output->dim_size(3);
     const int64 patch_depth = filter.dim_size(2);
 
+    if (patch_depth <= 0) {
+      ctx->SetStatus(errors::InvalidArgument(
+          "filter depth must be stricly positive, got ", patch_depth));
+      return;
+    }
     if (in_depth % patch_depth != 0) {
       ctx->SetStatus(errors::InvalidArgument(
           "input depth must be evenly divisible by filter depth: ", in_depth,
@@ -268,6 +273,11 @@ struct LaunchConv2DOp<CPUDevice, T> {
     }
 
     const int64 num_groups = in_depth / patch_depth;
+    if (num_groups <= 0) {
+      ctx->SetStatus(errors::InvalidArgument(
+          "number of groups must be stricly positive, got ", num_groups));
+      return;
+    }
     if (out_depth % num_groups != 0 || out_depth < num_groups) {
       ctx->SetStatus(errors::InvalidArgument(
           "output depth must be evenly divisible by number of groups: ",
@@ -536,6 +546,9 @@ Status ComputeConv2DDimension(const Conv2DParameters& params,
               errors::InvalidArgument("Patch depth too large"));
   const int in_depth = static_cast<int>(in_depth_raw);
   const int patch_depth = static_cast<int>(patch_depth_raw);
+  TF_REQUIRES(patch_depth > 0,
+              errors::InvalidArgument(
+                  "filter depth must be stricly positive, got ", patch_depth));
   TF_REQUIRES(in_depth % patch_depth == 0,
               errors::InvalidArgument(
                   "input depth must be evenly divisible by filter depth: ",
path: tensorflow/core/kernels/conv_ops.cc