High severityNVD Advisory· Published Jul 30, 2024· Updated Aug 2, 2024
TensorFlow segfault in array_ops.upper_bound
CVE-2023-33976
Description
TensorFlow is an end-to-end open source platform for machine learning. array_ops.upper_bound causes a segfault when not given a rank 2 tensor. The fix will be included in TensorFlow 2.13 and will also cherrypick this commit on TensorFlow 2.12.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
tensorflowPyPI | < 2.12.1 | 2.12.1 |
tensorflow-cpuPyPI | < 2.12.1 | 2.12.1 |
tensorflow-gpuPyPI | < 2.12.1 | 2.12.1 |
Affected products
1- Range: < 2.13.0
Patches
2915884fdf5dfCheck for correct `values` rank in UpperBound and LowerBound.
2 files changed · +71 −15
tensorflow/core/kernels/searchsorted_op.cc+26 −6 modified@@ -101,10 +101,20 @@ class UpperBoundOp : public OpKernel { const Tensor& sorted_inputs_t = ctx->input(0); const Tensor& values_t = ctx->input(1); - // inputs must be at least a matrix + // Inputs must be a matrix + // This replicates the shape requirements for the op in array_ops.cc OP_REQUIRES( - ctx, sorted_inputs_t.shape().dims() >= 2, - errors::InvalidArgument("sorted input argument must be a matrix")); + ctx, sorted_inputs_t.shape().dims() == 2, + errors::InvalidArgument(absl::StrCat( + "Shape must be rank 2 but is rank ", sorted_inputs_t.shape().dims(), + " for " + "`sorted_inputs` argument"))); + // Values must be a matrix + // This replicates the shape requirements for the op in array_ops.cc + OP_REQUIRES(ctx, values_t.shape().dims() == 2, + errors::InvalidArgument(absl::StrCat( + "Shape must be rank 2 but is rank ", + values_t.shape().dims(), " for `values` argument"))); // must have same batch dim_size for both OP_REQUIRES(ctx, sorted_inputs_t.dim_size(0) == values_t.dim_size(0), Status(error::INVALID_ARGUMENT, @@ -154,10 +164,20 @@ class LowerBoundOp : public OpKernel { const Tensor& sorted_inputs_t = ctx->input(0); const Tensor& values_t = ctx->input(1); - // inputs must be at least a matrix + // Inputs must be a matrix + // This replicates the shape requirements for the op in array_ops.cc OP_REQUIRES( - ctx, sorted_inputs_t.shape().dims() >= 2, - errors::InvalidArgument("sorted input argument must be a matrix")); + ctx, sorted_inputs_t.shape().dims() == 2, + errors::InvalidArgument(absl::StrCat( + "Shape must be rank 2 but is rank ", sorted_inputs_t.shape().dims(), + " for " + "`sorted_inputs` argument"))); + // Values must be a matrix + // This replicates the shape requirements for the op in array_ops.cc + OP_REQUIRES(ctx, values_t.shape().dims() == 2, + errors::InvalidArgument(absl::StrCat( + "Shape must be rank 2 but is rank ", + values_t.shape().dims(), " for `values` argument"))); // must have same batch dim_size for both OP_REQUIRES(ctx, sorted_inputs_t.dim_size(0) == values_t.dim_size(0), Status(error::INVALID_ARGUMENT,
tensorflow/python/ops/array_ops_test.py+45 −9 modified@@ -20,6 +20,7 @@ from tensorflow.python.framework import tensor_spec from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops +from tensorflow.python.ops import gen_array_ops from tensorflow.python.ops import math_ops from tensorflow.python.ops import random_ops from tensorflow.python.platform import test @@ -31,9 +32,8 @@ def testGatherGradHasPartialStaticShape(self): # Create a tensor with an unknown dim 1. x = random_ops.random_normal([4, 10, 10]) x = array_ops.gather( - x, - array_ops.reshape(array_ops.where_v2(x[0, :, 0] > 0.5), [-1]), - axis=1) + x, array_ops.reshape(array_ops.where_v2(x[0, :, 0] > 0.5), [-1]), axis=1 + ) x.shape.assert_is_compatible_with([4, None, 10]) with backprop.GradientTape() as tape: @@ -54,9 +54,8 @@ def testReshapeShapeInference(self): # Create a tensor with an unknown dim 1. x = random_ops.random_normal([4, 10, 10]) x = array_ops.gather( - x, - array_ops.reshape(array_ops.where_v2(x[0, :, 0] > 0.5), [-1]), - axis=1) + x, array_ops.reshape(array_ops.where_v2(x[0, :, 0] > 0.5), [-1]), axis=1 + ) x.shape.assert_is_compatible_with([4, None, 10]) a = array_ops.reshape(x, array_ops.shape(x)) a.shape.assert_is_compatible_with([4, None, 10]) @@ -68,14 +67,15 @@ def testReshapeShapeInference(self): c = array_ops.reshape( x, math_ops.cast( - math_ops.cast(array_ops.shape(x), dtypes.float32), dtypes.int32)) + math_ops.cast(array_ops.shape(x), dtypes.float32), dtypes.int32 + ), + ) c.shape.assert_is_compatible_with([None, None, None]) def testEmptyMeshgrid(self): self.assertEqual(array_ops.meshgrid(), []) def testSlicedPartialShapeInference(self): - @def_function.function(autograph=False) def g(x): return array_ops.zeros([array_ops.shape(x)[0]]) @@ -84,7 +84,6 @@ def g(x): self.assertAllEqual(conc.output_shapes.as_list(), [10]) def testIdentityOnSlicedPartialShapeInference(self): - @def_function.function(autograph=False) def g(x): return array_ops.zeros([array_ops.identity(array_ops.shape(x)[0])]) @@ -106,6 +105,43 @@ def func(): ): func() + @test_util.run_in_graph_and_eager_modes + def testUpperBoundValuesWrongRank(self): + # Used to cause a segfault, b/266336058 + arg0 = array_ops.zeros([2, 3], dtype=dtypes.float32) + arg1 = array_ops.zeros([2, 1, 0], dtype=dtypes.float32) + with self.assertRaisesRegex( + Exception, "Shape must be rank 2 but is rank 3" + ): + gen_array_ops.upper_bound(arg0, arg1) + + def testLowerBoundValuesWrongRank(self): + # Used to cause a segfault, b/266336058 + arg0 = array_ops.zeros([2, 3], dtype=dtypes.float32) + arg1 = array_ops.zeros([2, 1, 0], dtype=dtypes.float32) + with self.assertRaisesRegex( + Exception, "Shape must be rank 2 but is rank 3" + ): + gen_array_ops.lower_bound(arg0, arg1) + + def testUpperBoundInputsWrongRank(self): + # Used to cause a segfault, b/266336058 + arg0 = array_ops.zeros([2, 1, 0], dtype=dtypes.float32) + arg1 = array_ops.zeros([2, 3], dtype=dtypes.float32) + with self.assertRaisesRegex( + Exception, "Shape must be rank 2 but is rank 3" + ): + gen_array_ops.upper_bound(arg0, arg1) + + def testLowerBoundInputsWrongRank(self): + # Used to cause a segfault, b/266336058 + arg0 = array_ops.zeros([2, 1, 0], dtype=dtypes.float32) + arg1 = array_ops.zeros([2, 3], dtype=dtypes.float32) + with self.assertRaisesRegex( + Exception, "Shape must be rank 2 but is rank 3" + ): + gen_array_ops.lower_bound(arg0, arg1) + if __name__ == "__main__": test.main()
6fa05df43b00Check for correct `values` rank in UpperBound and LowerBound.
2 files changed · +71 −15
tensorflow/core/kernels/searchsorted_op.cc+26 −6 modified@@ -101,10 +101,20 @@ class UpperBoundOp : public OpKernel { const Tensor& sorted_inputs_t = ctx->input(0); const Tensor& values_t = ctx->input(1); - // inputs must be at least a matrix + // Inputs must be a matrix + // This replicates the shape requirements for the op in array_ops.cc OP_REQUIRES( - ctx, sorted_inputs_t.shape().dims() >= 2, - errors::InvalidArgument("sorted input argument must be a matrix")); + ctx, sorted_inputs_t.shape().dims() == 2, + errors::InvalidArgument(absl::StrCat( + "Shape must be rank 2 but is rank ", sorted_inputs_t.shape().dims(), + " for " + "`sorted_inputs` argument"))); + // Values must be a matrix + // This replicates the shape requirements for the op in array_ops.cc + OP_REQUIRES(ctx, values_t.shape().dims() == 2, + errors::InvalidArgument(absl::StrCat( + "Shape must be rank 2 but is rank ", + values_t.shape().dims(), " for `values` argument"))); // must have same batch dim_size for both OP_REQUIRES(ctx, sorted_inputs_t.dim_size(0) == values_t.dim_size(0), Status(error::INVALID_ARGUMENT, @@ -154,10 +164,20 @@ class LowerBoundOp : public OpKernel { const Tensor& sorted_inputs_t = ctx->input(0); const Tensor& values_t = ctx->input(1); - // inputs must be at least a matrix + // Inputs must be a matrix + // This replicates the shape requirements for the op in array_ops.cc OP_REQUIRES( - ctx, sorted_inputs_t.shape().dims() >= 2, - errors::InvalidArgument("sorted input argument must be a matrix")); + ctx, sorted_inputs_t.shape().dims() == 2, + errors::InvalidArgument(absl::StrCat( + "Shape must be rank 2 but is rank ", sorted_inputs_t.shape().dims(), + " for " + "`sorted_inputs` argument"))); + // Values must be a matrix + // This replicates the shape requirements for the op in array_ops.cc + OP_REQUIRES(ctx, values_t.shape().dims() == 2, + errors::InvalidArgument(absl::StrCat( + "Shape must be rank 2 but is rank ", + values_t.shape().dims(), " for `values` argument"))); // must have same batch dim_size for both OP_REQUIRES(ctx, sorted_inputs_t.dim_size(0) == values_t.dim_size(0), Status(error::INVALID_ARGUMENT,
tensorflow/python/ops/array_ops_test.py+45 −9 modified@@ -20,6 +20,7 @@ from tensorflow.python.framework import tensor_spec from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops +from tensorflow.python.ops import gen_array_ops from tensorflow.python.ops import math_ops from tensorflow.python.ops import random_ops from tensorflow.python.platform import test @@ -31,9 +32,8 @@ def testGatherGradHasPartialStaticShape(self): # Create a tensor with an unknown dim 1. x = random_ops.random_normal([4, 10, 10]) x = array_ops.gather( - x, - array_ops.reshape(array_ops.where_v2(x[0, :, 0] > 0.5), [-1]), - axis=1) + x, array_ops.reshape(array_ops.where_v2(x[0, :, 0] > 0.5), [-1]), axis=1 + ) x.shape.assert_is_compatible_with([4, None, 10]) with backprop.GradientTape() as tape: @@ -54,9 +54,8 @@ def testReshapeShapeInference(self): # Create a tensor with an unknown dim 1. x = random_ops.random_normal([4, 10, 10]) x = array_ops.gather( - x, - array_ops.reshape(array_ops.where_v2(x[0, :, 0] > 0.5), [-1]), - axis=1) + x, array_ops.reshape(array_ops.where_v2(x[0, :, 0] > 0.5), [-1]), axis=1 + ) x.shape.assert_is_compatible_with([4, None, 10]) a = array_ops.reshape(x, array_ops.shape(x)) a.shape.assert_is_compatible_with([4, None, 10]) @@ -68,14 +67,15 @@ def testReshapeShapeInference(self): c = array_ops.reshape( x, math_ops.cast( - math_ops.cast(array_ops.shape(x), dtypes.float32), dtypes.int32)) + math_ops.cast(array_ops.shape(x), dtypes.float32), dtypes.int32 + ), + ) c.shape.assert_is_compatible_with([None, None, None]) def testEmptyMeshgrid(self): self.assertEqual(array_ops.meshgrid(), []) def testSlicedPartialShapeInference(self): - @def_function.function(autograph=False) def g(x): return array_ops.zeros([array_ops.shape(x)[0]]) @@ -84,7 +84,6 @@ def g(x): self.assertAllEqual(conc.output_shapes.as_list(), [10]) def testIdentityOnSlicedPartialShapeInference(self): - @def_function.function(autograph=False) def g(x): return array_ops.zeros([array_ops.identity(array_ops.shape(x)[0])]) @@ -106,6 +105,43 @@ def func(): ): func() + @test_util.run_in_graph_and_eager_modes + def testUpperBoundValuesWrongRank(self): + # Used to cause a segfault, b/266336058 + arg0 = array_ops.zeros([2, 3], dtype=dtypes.float32) + arg1 = array_ops.zeros([2, 1, 0], dtype=dtypes.float32) + with self.assertRaisesRegex( + Exception, "Shape must be rank 2 but is rank 3" + ): + gen_array_ops.upper_bound(arg0, arg1) + + def testLowerBoundValuesWrongRank(self): + # Used to cause a segfault, b/266336058 + arg0 = array_ops.zeros([2, 3], dtype=dtypes.float32) + arg1 = array_ops.zeros([2, 1, 0], dtype=dtypes.float32) + with self.assertRaisesRegex( + Exception, "Shape must be rank 2 but is rank 3" + ): + gen_array_ops.lower_bound(arg0, arg1) + + def testUpperBoundInputsWrongRank(self): + # Used to cause a segfault, b/266336058 + arg0 = array_ops.zeros([2, 1, 0], dtype=dtypes.float32) + arg1 = array_ops.zeros([2, 3], dtype=dtypes.float32) + with self.assertRaisesRegex( + Exception, "Shape must be rank 2 but is rank 3" + ): + gen_array_ops.upper_bound(arg0, arg1) + + def testLowerBoundInputsWrongRank(self): + # Used to cause a segfault, b/266336058 + arg0 = array_ops.zeros([2, 1, 0], dtype=dtypes.float32) + arg1 = array_ops.zeros([2, 3], dtype=dtypes.float32) + with self.assertRaisesRegex( + Exception, "Shape must be rank 2 but is rank 3" + ): + gen_array_ops.lower_bound(arg0, arg1) + if __name__ == "__main__": test.main()
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- github.com/advisories/GHSA-gjh7-xx4r-x345ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2023-33976ghsaADVISORY
- github.com/tensorflow/tensorflow/commit/6fa05df43b00038b048f4f0e51ef522da6532fecghsax_refsource_MISCWEB
- github.com/tensorflow/tensorflow/commit/915884fdf5df34aaedd00fc6ace33a2cfdefa586ghsax_refsource_MISCWEB
- github.com/tensorflow/tensorflow/security/advisories/GHSA-gjh7-xx4r-x345ghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.