Skip to content

Reject out-of-range GATHER index values at runtime to prevent OOB read#3593

Open
evilgensec wants to merge 1 commit into
tensorflow:mainfrom
evilgensec:fix/gather-index-oob-read
Open

Reject out-of-range GATHER index values at runtime to prevent OOB read#3593
evilgensec wants to merge 1 commit into
tensorflow:mainfrom
evilgensec:fix/gather-index-oob-read

Conversation

@evilgensec

@evilgensec evilgensec commented Jun 12, 2026

Copy link
Copy Markdown

BUG=GATHER reference kernel reads out of bounds on out-of-range index values (memory-safety fix)

Problem

GatherEval (tensorflow/lite/micro/kernels/gather.cc) indexes the input tensor with the values from the positions tensor. Those values are only checked with TFLITE_DCHECK_GE/TFLITE_DCHECK_LT, which expand to a no-op under -DNDEBUG — i.e. in the release / release_with_logs builds used on device. GatherPrepare validates axis and batch_dims, but never the index values themselves.

When a GATHER op's positions are a runtime input (e.g. an embedding lookup whose index comes from the application's input), a negative or too-large index makes the kernel read outside the input tensor — a heap-buffer-overflow read whose bytes are copied into the output. The full TFLite GATHER kernel guards index values at runtime (TF_LITE_ENSURE(context, indices_has_only_positive_elements) plus the reference bounds handling) and returns kTfLiteError; the Micro kernel does not.

Reproduction

release_with_logs build (so -DNDEBUG strips the DCHECKs) with AddressSanitizer; a benign GATHER over a 3x4 table with one runtime index value of 3 (axis size is 3):

ERROR: AddressSanitizer: heap-buffer-overflow ... READ of size 16
    #1 tflite::(anonymous namespace)::GatherEval(...) gather.cc:204
    #2 tflite::micro::KernelRunner::Invoke() kernel_runner.cc:111
0x...  is located 0 bytes after 48-byte region   <- the embedding table

A negative index (-1) reads before the table.

Fix

Validate the index values in GatherEval and return kTfLiteError for any index outside [0, axis_size) before dispatching to the reference kernel. Adds regression tests GatherOp_IndexGreaterThanAxisSizeFailsClosed and GatherOp_NegativeIndexFailsClosed. With the fix the out-of-range index returns kTfLiteError with no out-of-bounds access; kernel_gather_test passes 20/20.

The reference GATHER kernel indexes the input tensor with the values in the
`positions` tensor and only guards them with TFLITE_DCHECK_GE/LT, which expand
to no-ops under -DNDEBUG (the release build). A negative or too-large index
supplied as a runtime input therefore makes GatherEval read outside the input
tensor (heap-buffer-overflow). GatherPrepare validates axis and batch_dims but
never the index values.

Validate the index values in GatherEval and return kTfLiteError for any index
outside [0, axis_size), mirroring the runtime index check in the full TFLite
GATHER kernel. Adds regression tests for an out-of-range and a negative index.
@evilgensec evilgensec requested a review from a team as a code owner June 12, 2026 02:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant