OOB read in `Gather_nd` op in TensorFlow Lite
Description
TensorFlow is an open source platform for machine learning. The GatherNd function takes arguments that determine the sizes of inputs and outputs. If the inputs given are greater than or equal to the sizes of the outputs, an out-of-bounds memory read is triggered. This issue has been patched in GitHub commit 595a65a3e224a0362d7e68c2213acfc2b499a196. The fix will be included in TensorFlow 2.10.0. We will also cherrypick this commit on TensorFlow 2.9.1, TensorFlow 2.8.1, and TensorFlow 2.7.2, as these are also affected and still in supported range. There are no known workarounds for this issue.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
tensorflowPyPI | < 2.7.2 | 2.7.2 |
tensorflowPyPI | >= 2.8.0, < 2.8.1 | 2.8.1 |
tensorflowPyPI | >= 2.9.0, < 2.9.1 | 2.9.1 |
tensorflow-cpuPyPI | < 2.7.2 | 2.7.2 |
tensorflow-cpuPyPI | >= 2.8.0, < 2.8.1 | 2.8.1 |
tensorflow-cpuPyPI | >= 2.9.0, < 2.9.1 | 2.9.1 |
tensorflow-gpuPyPI | < 2.7.2 | 2.7.2 |
tensorflow-gpuPyPI | >= 2.8.0, < 2.8.1 | 2.8.1 |
tensorflow-gpuPyPI | >= 2.9.0, < 2.9.1 | 2.9.1 |
Affected products
1- Range: < 2.7.2
Patches
1595a65a3e224Return a TFLite error if gather_nd will result in reading invalid memory
3 files changed · +45 −16
tensorflow/lite/kernels/gather_nd.cc+14 −10 modified@@ -14,6 +14,7 @@ limitations under the License. ==============================================================================*/ #include <stdint.h> +#include "tensorflow/lite/c/c_api_types.h" #include "tensorflow/lite/c/common.h" #include "tensorflow/lite/kernels/internal/optimized/optimized_ops.h" #include "tensorflow/lite/kernels/internal/reference/reference_ops.h" @@ -102,13 +103,16 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) { } template <typename ParamsT, typename IndicesT> -TfLiteStatus GatherNd(const TfLiteTensor* params, const TfLiteTensor* indices, - TfLiteTensor* output) { - reference_ops::GatherNd( +TfLiteStatus GatherNd(TfLiteContext* context, const TfLiteTensor* params, + const TfLiteTensor* indices, TfLiteTensor* output) { + const TfLiteStatus status = reference_ops::GatherNd( GetTensorShape(params), GetTensorData<ParamsT>(params), GetTensorShape(indices), GetTensorData<IndicesT>(indices), GetTensorShape(output), GetTensorData<ParamsT>(output)); - return kTfLiteOk; + if (status != kTfLiteOk) { + TF_LITE_KERNEL_LOG(context, "gather_nd index out of bounds"); + } + return status; } template <typename IndicesT> @@ -136,17 +140,17 @@ TfLiteStatus EvalGatherNd(TfLiteContext* context, const TfLiteTensor* params, switch (params->type) { case kTfLiteFloat32: - return GatherNd<float, IndicesT>(params, indices, output); + return GatherNd<float, IndicesT>(context, params, indices, output); case kTfLiteUInt8: - return GatherNd<uint8_t, IndicesT>(params, indices, output); + return GatherNd<uint8_t, IndicesT>(context, params, indices, output); case kTfLiteInt8: - return GatherNd<int8_t, IndicesT>(params, indices, output); + return GatherNd<int8_t, IndicesT>(context, params, indices, output); case kTfLiteInt16: - return GatherNd<int16_t, IndicesT>(params, indices, output); + return GatherNd<int16_t, IndicesT>(context, params, indices, output); case kTfLiteInt32: - return GatherNd<int32_t, IndicesT>(params, indices, output); + return GatherNd<int32_t, IndicesT>(context, params, indices, output); case kTfLiteInt64: - return GatherNd<int64_t, IndicesT>(params, indices, output); + return GatherNd<int64_t, IndicesT>(context, params, indices, output); case kTfLiteString: return GatherNdString<IndicesT>(params, indices, output); default:
tensorflow/lite/kernels/gather_nd_test.cc+16 −0 modified@@ -73,6 +73,22 @@ TEST(GatherNdOpTest, ElementIndexingIntoMatrix) { EXPECT_THAT(m.GetOutput<float>(), ElementsAreArray({1.1, 2.2})); } +TEST(GatherNdOpTest, ErrorOnOutOfBoundsTooLarge) { + GatherNdOpModel m({TensorType_FLOAT32, {2, 2}}, {TensorType_INT32, {2, 2}}); + m.SetInput<float>({1.1, 1.2, 2.1, 2.2}); + m.SetPositions<int32_t>({0, 0, 2, 0}); + EXPECT_EQ(m.Invoke(), kTfLiteError); + m.SetPositions<int32_t>({0, 0, 1, 2}); + EXPECT_EQ(m.Invoke(), kTfLiteError); +} + +TEST(GatherNdOpTest, ErrorOnOutOfBoundsNegative) { + GatherNdOpModel m({TensorType_FLOAT32, {2, 2}}, {TensorType_INT32, {2, 2}}); + m.SetInput<float>({1.1, 1.2, 2.1, 2.2}); + m.SetPositions<int32_t>({1, -1, 1, 1}); + EXPECT_EQ(m.Invoke(), kTfLiteError); +} + TEST(GatherNdOpTest, SliceIndexingIntoMatrix) { GatherNdOpModel m({TensorType_FLOAT32, {2, 2}}, {TensorType_INT32, {2, 1}}); m.SetInput<float>({1.1, 1.2, 2.1, 2.2});
tensorflow/lite/kernels/internal/reference/reference_ops.h+15 −6 modified@@ -29,6 +29,7 @@ limitations under the License. #include "third_party/eigen3/Eigen/Core" #include "fixedpoint/fixedpoint.h" #include "ruy/profiler/instrumentation.h" // from @ruy +#include "tensorflow/lite/c/c_api_types.h" #include "tensorflow/lite/c/common.h" #include "tensorflow/lite/kernels/internal/common.h" #include "tensorflow/lite/kernels/internal/quantization_util.h" @@ -595,23 +596,31 @@ inline GatherNdHelperResult GatherNdHelper(const RuntimeShape& params_shape, return ret; } +// Implements GatherNd. +// Returns an error if any of the indices_data would cause an out of bounds +// memory read. template <typename ParamsT, typename IndicesT = int32> -inline void GatherNd(const RuntimeShape& params_shape, - const ParamsT* params_data, - const RuntimeShape& indices_shape, - const IndicesT* indices_data, - const RuntimeShape& output_shape, ParamsT* output_data) { +inline TfLiteStatus GatherNd(const RuntimeShape& params_shape, + const ParamsT* params_data, + const RuntimeShape& indices_shape, + const IndicesT* indices_data, + const RuntimeShape& output_shape, + ParamsT* output_data) { ruy::profiler::ScopeLabel label("GatherNd"); const GatherNdHelperResult res = GatherNdHelper(params_shape, indices_shape); for (int i = 0; i < res.n_slices; ++i) { - int from_pos = 0; + int64_t from_pos = 0; for (int j = 0; j < res.indices_nd; ++j) { from_pos += indices_data[i * res.indices_nd + j] * res.dims_to_count[j]; } + if (from_pos < 0 || from_pos + res.slice_size > params_shape.FlatSize()) { + return kTfLiteError; + } std::memcpy(output_data + i * res.slice_size, params_data + from_pos, sizeof(ParamsT) * res.slice_size); } + return kTfLiteOk; } #ifndef TF_LITE_STATIC_MEMORY
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
6- github.com/advisories/GHSA-pxrw-j2fv-hx3hghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2022-35937ghsaADVISORY
- github.com/tensorflow/tensorflow/blob/f463040eb3997e42e60a2ffc6dc72de7ef11dbb4/tensorflow/lite/kernels/gather_nd.ccghsax_refsource_MISCWEB
- github.com/tensorflow/tensorflow/commit/595a65a3e224a0362d7e68c2213acfc2b499a196ghsax_refsource_MISCWEB
- github.com/tensorflow/tensorflow/releases/tag/v2.10.0ghsaWEB
- github.com/tensorflow/tensorflow/security/advisories/GHSA-pxrw-j2fv-hx3hghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.