From 5f457f704022b08c5592481bf79d03adcf6450db Mon Sep 17 00:00:00 2001
From: Yong Tang <yong.tang.github@outlook.com>
Date: Wed, 27 Jan 2021 20:25:08 +0000
Subject: [PATCH 1/2] Fix crash when invalid keepdims value is being passed to
 tf.math.reduce_prod

This PR tries to address the issue raised in 46700 where
tf.math.reduce_prod will crash if keepdims is being passed
with a non-boolean value (e.g. numpy value)

The issue was that keepdims is passed through pywrap
which can not interprete numpy values, thus crashes.

A way to detect the type mismatch before being passed
to pywrap is to use `bool(keepdims)` to give python a chance
to convert to bool (and throw out error when appropriate).

This PR also fixes all reduce_ ops.

This PR fixes 46700.

Signed-off-by: Yong Tang <yong.tang.github@outlook.com>
---
 tensorflow/python/ops/math_ops.py | 15 ++++++++-------
 1 file changed, 8 insertions(+), 7 deletions(-)

diff --git a/tensorflow/python/ops/math_ops.py b/tensorflow/python/ops/math_ops.py
index cc0c4415fef..decad558e85 100644
--- a/tensorflow/python/ops/math_ops.py
+++ b/tensorflow/python/ops/math_ops.py
@@ -2016,7 +2016,7 @@ def reduce_sum_with_dims(input_tensor,
                          keepdims=False,
                          name=None,
                          dims=None):
-  keepdims = False if keepdims is None else keepdims
+  keepdims = False if keepdims is None else bool(keepdims)
   return _may_reduce_to_scalar(
       keepdims, axis,
       gen_math_ops._sum(input_tensor, dims, keepdims, name=name))
@@ -2059,6 +2059,7 @@ def reduce_euclidean_norm(input_tensor, axis=None, keepdims=False, name=None):
   Returns:
     The reduced tensor, of the same dtype as the input_tensor.
   """
+  keepdims = bool(keepdims)
   return _may_reduce_to_scalar(
       keepdims, axis,
       gen_math_ops.euclidean_norm(
@@ -2331,7 +2332,7 @@ def reduce_mean(input_tensor, axis=None, keepdims=False, name=None):
 
   @end_compatibility
   """
-  keepdims = False if keepdims is None else keepdims
+  keepdims = False if keepdims is None else bool(keepdims)
   return _may_reduce_to_scalar(
       keepdims, axis,
       gen_math_ops.mean(
@@ -2491,7 +2492,7 @@ def reduce_prod(input_tensor, axis=None, keepdims=False, name=None):
   Equivalent to np.prod
   @end_compatibility
   """
-  keepdims = False if keepdims is None else keepdims
+  keepdims = False if keepdims is None else bool(keepdims)
   return _may_reduce_to_scalar(
       keepdims, axis,
       gen_math_ops.prod(
@@ -2678,7 +2679,7 @@ def reduce_min(input_tensor, axis=None, keepdims=False, name=None):
   Equivalent to np.min
   @end_compatibility
   """
-  keepdims = False if keepdims is None else keepdims
+  keepdims = False if keepdims is None else bool(keepdims)
   return _may_reduce_to_scalar(
       keepdims, axis,
       gen_math_ops._min(
@@ -2805,7 +2806,7 @@ def reduce_max_with_dims(input_tensor,
                          keepdims=False,
                          name=None,
                          dims=None):
-  keepdims = False if keepdims is None else keepdims
+  keepdims = False if keepdims is None else bool(keepdims)
   return _may_reduce_to_scalar(
       keepdims, axis,
       gen_math_ops._max(input_tensor, dims, keepdims, name=name))
@@ -2909,7 +2910,7 @@ def reduce_all(input_tensor, axis=None, keepdims=False, name=None):
   Equivalent to np.all
   @end_compatibility
   """
-  keepdims = False if keepdims is None else keepdims
+  keepdims = False if keepdims is None else bool(keepdims)
   return _may_reduce_to_scalar(
       keepdims, axis,
       gen_math_ops._all(
@@ -3015,7 +3016,7 @@ def reduce_any(input_tensor, axis=None, keepdims=False, name=None):
   Equivalent to np.any
   @end_compatibility
   """
-  keepdims = False if keepdims is None else keepdims
+  keepdims = False if keepdims is None else bool(keepdims)
   return _may_reduce_to_scalar(
       keepdims, axis,
       gen_math_ops._any(

From b8a0b61cdbc48113dc813e395a58baaa7f2790cd Mon Sep 17 00:00:00 2001
From: Yong Tang <yong.tang.github@outlook.com>
Date: Wed, 27 Jan 2021 20:29:07 +0000
Subject: [PATCH 2/2] Add test case for GitHub issue 46700.

Signed-off-by: Yong Tang <yong.tang.github@outlook.com>
---
 .../python/kernel_tests/reduction_ops_test.py | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/tensorflow/python/kernel_tests/reduction_ops_test.py b/tensorflow/python/kernel_tests/reduction_ops_test.py
index 6a0f40108a8..3a58608443f 100644
--- a/tensorflow/python/kernel_tests/reduction_ops_test.py
+++ b/tensorflow/python/kernel_tests/reduction_ops_test.py
@@ -116,6 +116,25 @@ class ReductionUnknownShape(test.TestCase):
           self.assertEqual(y.shape, ())
 
 
+class ReductionInvalidKeepdims(test.TestCase):
+
+  def testBasic(self):
+    # Test case for GitHub issue 46700.
+    for dtype, reductions in [(dtypes.float32,
+                               (math_ops.reduce_sum, math_ops.reduce_mean,
+                                math_ops.reduce_prod, math_ops.reduce_max,
+                                math_ops.reduce_min,
+                                math_ops.reduce_euclidean_norm)),
+                              (dtypes.bool, (math_ops.reduce_all,
+                                             math_ops.reduce_any))]:
+      for reduction in reductions:
+        with self.assertRaisesRegex(ValueError, "The truth value"):
+          x = True if dtype == dtypes.bool else 1
+          y = reduction(
+              input_tensor=x, keepdims=np.array([63600, 1], dtype=np.float16))
+          self.evaluate(y)
+
+
 class BaseReductionTest(test.TestCase):
 
   def _tf_reduce(self, x, reduction_axes, keepdims):