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.

TFLite READ_VARIABLE Static Output Shape Heap Out-of-Bounds Read PoC

malicious_read_variable_shape_16.tflite is a valid, self-contained TFL3 model that triggers a native heap out-of-bounds read during normal CPU inference. The graph is VAR_HANDLE -> ASSIGN_VARIABLE -> READ_VARIABLE. It assigns one FLOAT32 value (1.25, four bytes) to a resource variable, while the serialized READ_VARIABLE output is a non-empty static FLOAT32[16] tensor (64 bytes).

TFLite preserves the attacker-supplied non-empty output shape, type-checks only, and copies output->bytes from the resource buffer. The resource holds four bytes, so inference copies 64 bytes from a four-byte source allocation and returns the first value followed by 60 bytes read past it. The tail is allocator-dependent process memory and can disclose data held adjacent to the resource allocation.

The control model is byte-for-byte equivalent except for that one output-shape value: it declares FLOAT32[1], returns only 1.25, and performs no over-read.

Files

  • malicious_read_variable_shape_16.tflite โ€” uploadable malicious model.
  • control_read_variable_shape_1.tflite โ€” matching safe control model.
  • build_and_verify.py โ€” regenerates both models and proves normal CPU execution plus the 60-byte copy excess.
  • verify_report.json โ€” fresh CPU verification results; incidental process-memory bytes are deliberately not committed.
  • source_citations.md โ€” upstream root-cause links.
  • duplicate_check.md โ€” public-duplicate and scope gate.

Reproduction

Run with CPU TensorFlow 2.21.0. No GPU, custom operator, malformed FlatBuffer, external file, or model input is required.

python -m pip install -r requirements.txt
python build_and_verify.py

The verifier runs all three normal loading paths:

  • default CPU tf.lite.Interpreter(model_path=...);
  • CPU built-ins only (BUILTIN_WITHOUT_DEFAULT_DELEGATES);
  • normal model_content= loading.

For each path, the control allocates and returns four bytes. The malicious model loads, allocates, and invokes successfully; it returns a 64-byte tensor whose first float is 1.25, leaving a 60-byte returned tail. The script prints that tail locally because it is direct evidence of the out-of-bounds read, but omits it from the committed JSON report to avoid publishing incidental host-memory contents.

Root cause

READ_VARIABLE::Prepare() makes only rank-zero outputs dynamic. It leaves every non-empty output shape serialized in the model static. In Eval(), the kernel checks the element type but not the source and destination byte counts or shapes. A static output therefore avoids the resize-to-resource-shape path, after which the kernel calls:

memcpy(output->data.raw, variable_tensor->data.raw, output->bytes);

ASSIGN_VARIABLE stores the one-element value in a ResourceVariable, whose backing allocation is exactly the assigned tensor's bytes value: four bytes. For the malicious static output, output->bytes is 64. The unchecked copy consequently reads 60 bytes past the resource allocation and exposes those bytes through the model output.

See source_citations.md for exact upstream locations and the 2021 regression that introduced the static-output behavior.

Impact

An attacker who can supply a .tflite model can cause a victim to return process-heap data beyond a resource variable during ordinary CPU inference. In a service or application that exposes model outputs, this can disclose adjacent in-process data. This is a native memory-safety issue with a directly observable data-disclosure primitive, not a parser rejection, allocation-only condition, GPU-only path, or crash-only PoC.

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