Skip to content

Fix int overflow in tensor byte-size calculation#3606

Open
Forbiddem wants to merge 1 commit into
tensorflow:mainfrom
Forbiddem:fix/memory-helpers-int-overflow
Open

Fix int overflow in tensor byte-size calculation#3606
Forbiddem wants to merge 1 commit into
tensorflow:mainfrom
Forbiddem:fix/memory-helpers-int-overflow

Conversation

@Forbiddem

Copy link
Copy Markdown

BUG=#3552

Re-submission of #3551. That PR was auto-closed by the stale bot on 2026-06-22 before any review took place — the only outstanding gate was the maintainer-approved CI matrix (approval-gate / call-*), which was never triggered for an external contributor. The PR could not be reopened (the closed-PR head ref had become detached and both API reopen and push-to-branch were refused), so I'm re-submitting the identical commit. CLA is signed and the local tests pass.

Summary

BytesRequiredForTensor, TfLiteEvalTensorByteLength, and
AllocateOutputDimensionsFromInput in
tensorflow/lite/micro/memory_helpers.cc all multiply tensor shape dimensions
into a signed 32-bit int element_count running product before assigning the
result to a size_t byte count. Shape dimensions and the element type size
come from the (untrusted) flatbuffer model, so a model with a shape such as
[65536, 65536] over a 4-byte element type produces a product of 2^32, which
wraps to 0 in the original code and yields *bytes = 0. Subsequent buffer
arithmetic in the allocator then operates on a tensor whose advertised size
is 0 while its dimensions imply ~17 GiB of storage, opening a memory
corruption surface when a model is loaded from an untrusted source.

This PR:

  • Performs the running product in size_t, matching the result type, and
    rejects negative shape dimensions with kTfLiteError.
  • Guards every multiplication (shape * shape and elements * type_size)
    against overflow of size_t and returns kTfLiteError when the next
    multiplication would wrap.
  • Propagates the previously discarded status of TfLiteTypeSizeOf in
    AllocateOutputDimensionsFromInput.

Test plan

  • make -f tensorflow/lite/micro/tools/make/Makefile memory_helpers_test
    builds clean.
  • gen/linux_x86_64_default_gcc/bin/memory_helpers_test — all 10 cases
    pass, including the 3 new regression tests:
    • TfLiteEvalTensorByteLengthDoesNotTruncateAcrossInt32Boundary — shape
      [65536, 65536] float32 now reports 17,179,869,184 bytes on 64-bit
      size_t platforms (previously: 0), and kTfLiteError on 32-bit
      size_t platforms.
    • TfLiteEvalTensorByteLengthRejectsSizeTOverflow — a shape that
      overflows even a 64-bit size_t is rejected with kTfLiteError.
    • TfLiteEvalTensorByteLengthRejectsNegativeDimension — a negative
      shape dimension is rejected with kTfLiteError.

Notes for review

  • The overflow checks use std::numeric_limits<size_t>::max() / x, which is
    portable and MSVC-safe — deliberately not __builtin_mul_overflow, which
    would break the call-windows build.
  • @veblush — since this is a memory-safety fix for untrusted-model parsing,
    would you be able to approve the workflow run and take a look when you have a
    moment? Happy to switch the manual checks to any form the tree prefers and to
    address any other feedback.

`BytesRequiredForTensor`, `TfLiteEvalTensorByteLength`, and
`AllocateOutputDimensionsFromInput` in `tensorflow/lite/micro/memory_helpers.cc`
all multiplied tensor shape dimensions into a signed 32-bit `int element_count`
running product before assigning the result to a `size_t` byte count. Shape
dimensions and the element type size are sourced from the (untrusted)
flatbuffer model, so a model with a shape such as [65536, 65536] over a
4-byte element type produces an element-count product of 2^32, which wraps to
0 in the original code and yields `*bytes = 0`. Subsequent buffer
arithmetic in the allocator then operates on a tensor whose advertised size
is 0 while its dimensions imply ~17 GiB of storage, opening a memory
corruption surface.

This change:

  * Performs the running product in `size_t`, matching the result type, and
    rejects negative shape dimensions with `kTfLiteError`.
  * Guards every multiplication (shape * shape and elements * type_size)
    against overflow of `size_t` and returns `kTfLiteError` when the next
    multiplication would wrap.
  * Propagates the previously discarded status of `TfLiteTypeSizeOf` in
    `AllocateOutputDimensionsFromInput`.

Adds three regression tests in `memory_helpers_test.cc`:

  * Shape [65536, 65536] float32 reports the correct 17,179,869,184 byte
    length on platforms with a 64-bit `size_t` and `kTfLiteError` on
    platforms with a 32-bit `size_t` (instead of the pre-fix value of 0).
  * A shape that overflows even a 64-bit `size_t` is rejected with
    `kTfLiteError`.
  * A negative shape dimension is rejected with `kTfLiteError`.

All existing `memory_helpers_test` cases continue to pass.
@Forbiddem Forbiddem requested a review from a team as a code owner June 22, 2026 12:26
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