VYPR
High severityNVD Advisory· Published Sep 16, 2022· Updated Apr 23, 2025

Out of bounds write in `scatter_nd` op in TensorFlow Lite

CVE-2022-35939

Description

TensorFlow is an open source platform for machine learning. The ScatterNd function takes an input argument that determines the indices of of the output tensor. An input index greater than the output tensor or less than zero will either write content at the wrong index or trigger a crash. We have patched the issue in GitHub commit b4d4b4cb019bd7240a52daa4ba61e3cc814f0384. 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.

PackageAffected versionsPatched versions
tensorflowPyPI
< 2.7.22.7.2
tensorflowPyPI
>= 2.8.0, < 2.8.12.8.1
tensorflowPyPI
>= 2.9.0, < 2.9.12.9.1
tensorflow-cpuPyPI
< 2.7.22.7.2
tensorflow-cpuPyPI
>= 2.8.0, < 2.8.12.8.1
tensorflow-cpuPyPI
>= 2.9.0, < 2.9.12.9.1
tensorflow-gpuPyPI
< 2.7.22.7.2
tensorflow-gpuPyPI
>= 2.8.0, < 2.8.12.8.1
tensorflow-gpuPyPI
>= 2.9.0, < 2.9.12.9.1

Affected products

1

Patches

1
b4d4b4cb019b

Check bounds for reads and writes in scatter_nd

https://github.com/tensorflow/tensorflowAlan KellyJul 26, 2022via ghsa
3 files changed · +60 14
  • tensorflow/lite/kernels/internal/reference/reference_ops.h+13 6 modified
    @@ -656,11 +656,12 @@ inline TfLiteStatus GatherNdString(const RuntimeShape& params_shape,
     #endif
     
     template <typename IndicesT, typename UpdatesT>
    -inline void ScatterNd(const RuntimeShape& indices_shape,
    -                      const IndicesT* indices_data,
    -                      const RuntimeShape& updates_shape,
    -                      const UpdatesT* updates_data,
    -                      const RuntimeShape& output_shape, UpdatesT* output_data) {
    +inline TfLiteStatus ScatterNd(const RuntimeShape& indices_shape,
    +                              const IndicesT* indices_data,
    +                              const RuntimeShape& updates_shape,
    +                              const UpdatesT* updates_data,
    +                              const RuntimeShape& output_shape,
    +                              UpdatesT* output_data) {
       ruy::profiler::ScopeLabel label("ScatterNd");
     
       int n_slices = 1;
    @@ -683,18 +684,24 @@ inline void ScatterNd(const RuntimeShape& indices_shape,
         remain_flat_size = dims_to_count[i];
       }
     
    +  if (n_slices * slice_size > updates_shape.FlatSize()) {
    +    return kTfLiteError;
    +  }
       memset(output_data, 0, sizeof(UpdatesT) * output_flat_size);
       for (int i = 0; i < n_slices; ++i) {
         int to_pos = 0;
         for (int j = 0; j < indices_nd; ++j) {
           IndicesT idx = indices_data[i * indices_nd + j];
    -      TFLITE_DCHECK(0 <= idx && idx < output_shape.Dims(j));
           to_pos += idx * dims_to_count[j];
         }
    +    if (to_pos < 0 || to_pos + slice_size > output_flat_size) {
    +      return kTfLiteError;
    +    }
         for (int j = 0; j < slice_size; j++) {
           output_data[to_pos + j] += updates_data[i * slice_size + j];
         }
       }
    +  return kTfLiteOk;
     }
     
     template <typename T>
    
  • tensorflow/lite/kernels/scatter_nd.cc+18 8 modified
    @@ -129,11 +129,10 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
     template <typename IndicesT, typename UpdatesT>
     TfLiteStatus ScatterNd(const TfLiteTensor* indices, const TfLiteTensor* updates,
                            TfLiteTensor* output) {
    -  reference_ops::ScatterNd(
    +  return reference_ops::ScatterNd(
           GetTensorShape(indices), GetTensorData<IndicesT>(indices),
           GetTensorShape(updates), GetTensorData<UpdatesT>(updates),
           GetTensorShape(output), GetTensorData<UpdatesT>(output));
    -  return kTfLiteOk;
     }
     
     template <typename IndicesT>
    @@ -149,25 +148,36 @@ TfLiteStatus EvalScatterNd(TfLiteContext* context, const TfLiteTensor* indices,
                           ResizeOutputTensor<IndicesT>(context, shape, output));
       }
     
    +  TfLiteStatus status = kTfLiteError;
       switch (updates->type) {
         case kTfLiteFloat32:
    -      return ScatterNd<IndicesT, float>(indices, updates, output);
    +      status = ScatterNd<IndicesT, float>(indices, updates, output);
    +      break;
         case kTfLiteUInt8:
    -      return ScatterNd<IndicesT, uint8_t>(indices, updates, output);
    +      status = ScatterNd<IndicesT, uint8_t>(indices, updates, output);
    +      break;
         case kTfLiteBool:
    -      return ScatterNd<IndicesT, bool>(indices, updates, output);
    +      status = ScatterNd<IndicesT, bool>(indices, updates, output);
    +      break;
         case kTfLiteInt8:
    -      return ScatterNd<IndicesT, int8_t>(indices, updates, output);
    +      status = ScatterNd<IndicesT, int8_t>(indices, updates, output);
    +      break;
         case kTfLiteInt32:
    -      return ScatterNd<IndicesT, int32_t>(indices, updates, output);
    +      status = ScatterNd<IndicesT, int32_t>(indices, updates, output);
    +      break;
         case kTfLiteInt64:
    -      return ScatterNd<IndicesT, int64_t>(indices, updates, output);
    +      status = ScatterNd<IndicesT, int64_t>(indices, updates, output);
    +      break;
         default:
           TF_LITE_KERNEL_LOG(
               context, "Updates of type '%s' are not supported by scatter_nd.",
               TfLiteTypeGetName(updates->type));
           return kTfLiteError;
       }
    +  if (status != kTfLiteOk) {
    +    TF_LITE_KERNEL_LOG(context, "scatter_nd index out of bounds");
    +  }
    +  return status;
     }
     
     TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
    
  • tensorflow/lite/kernels/scatter_nd_test.cc+29 0 modified
    @@ -361,5 +361,34 @@ TEST(ScatterNdOpTest, DynamicShape) {
                                     /*2, 3*/ 1,  2,  3,  4,  5}));
     }
     
    +TEST(ScatterNdOpTest, ReadAndWriteArrayLimits) {
    +  ScatterNdOpModel m({TensorType_INT32, {5, 1}}, {TensorType_INT32, {5}},
    +                     {TensorType_INT32, {1}});
    +  m.SetIndices<int32_t>({4, 3, 1, 0, 2});
    +  m.SetUpdates<int32_t>({1, 2, 3, 7, 9});
    +  m.SetShape<int32_t>({5});
    +  ASSERT_EQ(m.Invoke(), kTfLiteOk);
    +  EXPECT_THAT(m.GetOutputShape(), ElementsAreArray({5}));
    +  EXPECT_THAT(m.GetOutput<int32_t>(), ElementsAreArray({7, 3, 9, 2, 1}));
    +}
    +
    +TEST(ScatterNdOpTest, OOBRead) {
    +  ScatterNdOpModel m({TensorType_INT32, {1, 1}}, {TensorType_INT32, {1}},
    +                     {TensorType_INT32, {1}});
    +  m.SetIndices<int32_t>({4});
    +  m.SetUpdates<int32_t>({1});
    +  m.SetShape<int32_t>({1});
    +  ASSERT_EQ(m.Invoke(), kTfLiteError);
    +}
    +
    +TEST(ScatterNdOpTest, OOBWrites) {
    +  ScatterNdOpModel m({TensorType_INT32, {5, 1}}, {TensorType_INT32, {5}},
    +                     {TensorType_INT32, {1}});
    +  m.SetIndices<int32_t>({4, 3, 1, -0x38, 0x38});
    +  m.SetUpdates<int32_t>({1, 2, 3, 0x44444444, 0x55555555});
    +  m.SetShape<int32_t>({1});
    +  ASSERT_EQ(m.Invoke(), kTfLiteError);
    +}
    +
     }  // namespace
     }  // namespace tflite
    

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

News mentions

0

No linked articles in our index yet.