Heap OOB in TensorFlow Lite's `Gather*` implementations
Description
TensorFlow is an end-to-end open source platform for machine learning. In affected versions TFLite's `GatherNd` implementation does not support negative indices but there are no checks for this situation. Hence, an attacker can read arbitrary data from the heap by carefully crafting a model with negative values in indices. Similar issue exists in `Gather` implementation. We have patched the issue in GitHub commits bb6a0383ed553c286f87ca88c207f6774d5c4a8f and eb921122119a6b6e470ee98b89e65d721663179d. The fix will be included in TensorFlow 2.6.0. We will also cherrypick this commit on TensorFlow 2.5.1, TensorFlow 2.4.3, and TensorFlow 2.3.4, as these are also affected and still in supported range.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
tensorflowPyPI | < 2.3.4 | 2.3.4 |
tensorflowPyPI | >= 2.4.0, < 2.4.3 | 2.4.3 |
tensorflowPyPI | >= 2.5.0, < 2.5.1 | 2.5.1 |
tensorflow-cpuPyPI | < 2.3.4 | 2.3.4 |
tensorflow-cpuPyPI | >= 2.4.0, < 2.4.3 | 2.4.3 |
tensorflow-cpuPyPI | >= 2.5.0, < 2.5.1 | 2.5.1 |
tensorflow-gpuPyPI | < 2.3.4 | 2.3.4 |
tensorflow-gpuPyPI | >= 2.4.0, < 2.4.3 | 2.4.3 |
tensorflow-gpuPyPI | >= 2.5.0, < 2.5.1 | 2.5.1 |
Affected products
1- Range: >= 2.5.0, < 2.5.1
Patches
2eb921122119aPrevent heap OOB read in TFLite's `gather.cc`.
1 file changed · +53 −16
tensorflow/lite/kernels/gather.cc+53 −16 modified@@ -117,8 +117,20 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) { } template <typename InputT, typename PositionsT> -TfLiteStatus Gather(const TfLiteGatherParams& params, const TfLiteTensor* input, - const TfLiteTensor* positions, TfLiteTensor* output) { +TfLiteStatus Gather(TfLiteContext* context, const TfLiteGatherParams& params, + const TfLiteTensor* input, const TfLiteTensor* positions, + TfLiteTensor* output) { + const PositionsT* indexes = GetTensorData<PositionsT>(positions); + bool indices_has_only_positive_elements = true; + const size_t num_indices = positions->bytes / sizeof(PositionsT); + for (size_t i = 0; i < num_indices; i++) { + if (indexes[i] < 0) { + indices_has_only_positive_elements = false; + break; + } + } + TF_LITE_ENSURE(context, indices_has_only_positive_elements); + tflite::GatherParams op_params; op_params.axis = params.axis; op_params.batch_dims = params.batch_dims; @@ -134,7 +146,18 @@ TfLiteStatus GatherStrings(TfLiteContext* context, const TfLiteTensor* input, const TfLiteTensor* positions, TfLiteTensor* output) { DynamicBuffer buffer; + const PositionT* indexes = GetTensorData<PositionT>(positions); + bool indices_has_only_positive_elements = true; + const size_t num_indices = positions->bytes / sizeof(PositionT); + for (size_t i = 0; i < num_indices; i++) { + if (indexes[i] < 0) { + indices_has_only_positive_elements = false; + break; + } + } + TF_LITE_ENSURE(context, indices_has_only_positive_elements); + const PositionT num_strings = GetStringCount(input); const int num_indexes = NumElements(positions); @@ -163,19 +186,26 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { if (positions->type == kTfLiteInt32) { switch (input->type) { case kTfLiteFloat32: - return Gather<float, int32_t>(*params, input, positions, output); + return Gather<float, int32_t>(context, *params, input, positions, + output); case kTfLiteUInt8: - return Gather<uint8_t, int32_t>(*params, input, positions, output); + return Gather<uint8_t, int32_t>(context, *params, input, positions, + output); case kTfLiteInt8: - return Gather<int8_t, int32_t>(*params, input, positions, output); + return Gather<int8_t, int32_t>(context, *params, input, positions, + output); case kTfLiteInt16: - return Gather<int16_t, int32_t>(*params, input, positions, output); + return Gather<int16_t, int32_t>(context, *params, input, positions, + output); case kTfLiteInt32: - return Gather<int32_t, int32_t>(*params, input, positions, output); + return Gather<int32_t, int32_t>(context, *params, input, positions, + output); case kTfLiteInt64: - return Gather<int64_t, int32_t>(*params, input, positions, output); + return Gather<int64_t, int32_t>(context, *params, input, positions, + output); case kTfLiteBool: - return Gather<bool, int32_t>(*params, input, positions, output); + return Gather<bool, int32_t>(context, *params, input, positions, + output); case kTfLiteString: return GatherStrings<int32_t>(context, input, positions, output); default: @@ -187,19 +217,26 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { if (positions->type == kTfLiteInt64) { switch (input->type) { case kTfLiteFloat32: - return Gather<float, int64_t>(*params, input, positions, output); + return Gather<float, int64_t>(context, *params, input, positions, + output); case kTfLiteUInt8: - return Gather<uint8_t, int64_t>(*params, input, positions, output); + return Gather<uint8_t, int64_t>(context, *params, input, positions, + output); case kTfLiteInt8: - return Gather<int8_t, int64_t>(*params, input, positions, output); + return Gather<int8_t, int64_t>(context, *params, input, positions, + output); case kTfLiteInt16: - return Gather<int16_t, int64_t>(*params, input, positions, output); + return Gather<int16_t, int64_t>(context, *params, input, positions, + output); case kTfLiteInt32: - return Gather<int32_t, int64_t>(*params, input, positions, output); + return Gather<int32_t, int64_t>(context, *params, input, positions, + output); case kTfLiteInt64: - return Gather<int64_t, int64_t>(*params, input, positions, output); + return Gather<int64_t, int64_t>(context, *params, input, positions, + output); case kTfLiteBool: - return Gather<bool, int64_t>(*params, input, positions, output); + return Gather<bool, int64_t>(context, *params, input, positions, + output); case kTfLiteString: return GatherStrings<int64_t>(context, input, positions, output); default:
bb6a0383ed55Prevent heap OOB read in TFLite's `gather_nd.cc`.
1 file changed · +11 −0
tensorflow/lite/kernels/gather_nd.cc+11 −0 modified@@ -123,6 +123,17 @@ TfLiteStatus GatherNdString(const TfLiteTensor* params, template <typename IndicesT> TfLiteStatus EvalGatherNd(TfLiteContext* context, const TfLiteTensor* params, const TfLiteTensor* indices, TfLiteTensor* output) { + bool indices_has_only_positive_elements = true; + const auto* indices_values = GetTensorData<IndicesT>(indices); + const size_t num_indices = indices->bytes / sizeof(IndicesT); + for (size_t i = 0; i < num_indices; i++) { + if (indices_values[i] < 0) { + indices_has_only_positive_elements = false; + break; + } + } + TF_LITE_ENSURE(context, indices_has_only_positive_elements); + switch (params->type) { case kTfLiteFloat32: return GatherNd<float, IndicesT>(params, indices, output);
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
10- github.com/advisories/GHSA-jwf9-w5xm-f437ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2021-37687ghsaADVISORY
- github.com/pypa/advisory-database/tree/main/vulns/tensorflow-cpu/PYSEC-2021-600.yamlghsaWEB
- github.com/pypa/advisory-database/tree/main/vulns/tensorflow-gpu/PYSEC-2021-798.yamlghsaWEB
- github.com/pypa/advisory-database/tree/main/vulns/tensorflow/PYSEC-2021-309.yamlghsaWEB
- github.com/tensorflow/tensorflow/blob/149562d49faa709ea80df1d99fc41d005b81082a/tensorflow/lite/kernels/gather.ccghsaWEB
- github.com/tensorflow/tensorflow/blob/149562d49faa709ea80df1d99fc41d005b81082a/tensorflow/lite/kernels/gather_nd.ccghsaWEB
- github.com/tensorflow/tensorflow/commit/bb6a0383ed553c286f87ca88c207f6774d5c4a8fghsax_refsource_MISCWEB
- github.com/tensorflow/tensorflow/commit/eb921122119a6b6e470ee98b89e65d721663179dghsax_refsource_MISCWEB
- github.com/tensorflow/tensorflow/security/advisories/GHSA-jwf9-w5xm-f437ghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.