VYPR
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.

PackageAffected versionsPatched versions
tensorflowPyPI
< 2.12.12.12.1
tensorflow-cpuPyPI
< 2.12.12.12.1
tensorflow-gpuPyPI
< 2.12.12.12.1

Affected products

1

Patches

2
915884fdf5df

Check for correct `values` rank in UpperBound and LowerBound.

https://github.com/tensorflow/tensorflowCesar CrusiusFeb 27, 2023via ghsa
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()
    
6fa05df43b00

Check for correct `values` rank in UpperBound and LowerBound.

https://github.com/tensorflow/tensorflowCesar CrusiusFeb 27, 2023via ghsa
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

News mentions

0

No linked articles in our index yet.