CVE-2022-35938 (C++, published 2022-09-16T20:15Z)
Description: value: 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 or a crash is triggered. This issue has been patched in GitHub commit 4142e47e9e31db481781b955ed3ff807a781b494. 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.
Severity: nan (CVSS v3 base score: 9.1)
Weakness: CWE-125 \u2014 Out-of-bounds Read
The product reads data past the end, or before the beginning, of the intended buffer.
Repository: https://github.com/tensorflow/tflite-micro
Files changed:
tensorflow/lite/micro/kernels/gather_nd.cc
tensorflow/lite/micro/kernels/gather_nd_test.cc
Commit message: GatherNd verifies that an index is valid before reading. (#1286)



Vulnerable code:
  int remain_flat_size = ElementCount(*params->dims);
      return GatherNd<float, IndicesT>(params, indices, output);
      return GatherNd<int8_t, IndicesT>(params, indices, output);
                  ParamType* output_data,
                  const ParamType* expected_output_data) {
  TF_LITE_MICRO_EXPECT_EQ(kTfLiteOk, runner.Invoke());
  // The output tensor's data and shape have been updated by the kernel.
  TfLiteTensor* actual_output_tensor = &tensors[2];
  TfLiteIntArray* actual_output_dims = actual_output_tensor->dims;
  const int output_size = ElementCount(*actual_output_dims);
  for (int i = 0; i < output_size; ++i) {
    TF_LITE_MICRO_EXPECT_EQ(expected_output_data[i], output_data[i]);

Fixed code:
  int params_flat_size = ElementCount(*params->dims);
  int remain_flat_size = params_flat_size;
    if (from_pos < 0 || from_pos + slice_size > params_flat_size) {
      return kTfLiteError;
    }
  TfLiteStatus status = kTfLiteError;
      status = GatherNd<float, IndicesT>(params, indices, output);
      status = GatherNd<int8_t, IndicesT>(params, indices, output);
  if (status != kTfLiteOk) {
    TF_LITE_KERNEL_LOG(context, "gather_nd index out of bounds");
  }
  return status;
                  ParamType* output_data, const ParamType* expected_output_data,
                  const TfLiteStatus expected_status = kTfLiteOk) {
  TF_LITE_MICRO_EXPECT_EQ(expected_status, runner.Invoke());
  if (expected_status == kTfLiteOk) {
    // The output tensor's data and shape have been updated by the kernel.
    TfLiteTensor* actual_output_tensor = &tensors[2];
    TfLiteIntArray* actual_output_dims = actual_output_tensor->dims;
    const int output_size = ElementCount(*actual_output_dims);
    for (int i = 0; i < output_size; ++i) {
      TF_LITE_MICRO_EXPECT_EQ(expected_output_data[i], output_data[i]);
    }
TF_LITE_MICRO_TEST(GatherNd_ReadOOB) {
  // For input_dims[], index_dims[], or output_dims[], element 0 is the
  // number of dimensions in that array, not the actual dimension data.
  int input_dims[] = {2, 2, 2};
  int index_dims[] = {2, 2, 2};
  const int32_t index_data[] = {0, 1, 2, 0};
  const int8_t input_data[] = {1, -1, 1, -2};
  int8_t output_data;
  int output_dims[] = {1, 0, 0};
  tflite::testing::TestGatherNd<int8_t, int32_t>(
      input_dims, input_data, index_dims, index_data, output_dims, &output_data,
      nullptr, kTfLiteError);
}

TF_LITE_MICRO_TEST(GatherNd_ReadOOBNegative) {
  // For input_dims[], index_dims[], or output_dims[], element 0 is the
  // number of dimensions in that array, not the actual dimension data.
  int input_dims[] = {2, 2, 2};
  int index_dims[] = {2, 2, 2};
  const int32_t index_data[] = {0, -1, 1, 0};
  const int8_t input_data[] = {1, -1, 1, -2};
  int8_t output_data;
  int output_dims[] = {1, 0, 0};
  tflite::testing::TestGatherNd<int8_t, int32_t>(
      input_dims, input_data, index_dims, index_data, output_dims, &output_data,
      nullptr, kTfLiteError);
}