YAML Metadata Warning:empty or missing yaml metadata in repo card
Check out the documentation for more information.
TFLite NormalizationOptions Metadata Authority β PoC
Summary
This repository demonstrates that the TFLite Task Library (tflite-support) reads and applies
NormalizationOptions mean/std values from a model's embedded FlatBuffer metadata at inference
time for FLOAT32 inputs, without any validation or bounds checking. An adversarially crafted
model with extreme mean/std values produces silently wrong output with no exception or warning.
Affected consumer: tflite_support.task.vision.ImageClassifier (Task Library)
Not affected: Raw tflite_support.Interpreter (does not apply NormalizationOptions)
Root: TensorMetadata.process_units[0].NormalizationOptions.{mean, std} FlatBuffer field
Runtime stage: Pre-inference (applied as (pixel - mean) / std before inference kernel)
Affected Consumer
The TFLite Task Library (tflite-support) ImageClassifier automatically reads
NormalizationOptions from model metadata and applies normalization before inference:
from tflite_support.task import core, vision
opts = vision.ImageClassifierOptions(
base_options=core.BaseOptions(file_name="model.tflite")
)
clf = vision.ImageClassifier.create_from_options(opts)
result = clf.classify(vision.TensorImage.create_from_array(image_array))
The normalization is applied transparently β the caller provides uint8 pixel data and
the library applies (pixel - mean) / std per the embedded metadata before passing to the
inference kernel. There is no API to disable or override this behavior for untrusted models.
Tested Environment
| Item | Value |
|---|---|
| OS | x86_64 Linux (required β wheel not available for aarch64) |
| Python | 3.10 |
| tflite-support | 0.4.4 |
| TFLite delegate | XNNPACK CPU |
| Base model | float32, [1,224,224,3] β GlobalAveragePooling2D β Dense(3) |
Files
| File | Description |
|---|---|
reproduce_tflite_normalization_divergence.py |
Reproduction script β creates/loads models and demonstrates divergence |
inspect_hash_matrix.py |
Hash matrix inspection and metadata diff verification |
clean_model.tflite |
Benign model with NormalizationOptions mean=0.5, std=0.5 |
mutated_model.tflite |
Adversarial model with NormalizationOptions mean=200.0, std=0.001 |
requirements.txt |
Python dependencies |
evidence_runtime_results.json |
Runtime test results (clean vs mutated) |
evidence_reproducibility.json |
Reproducibility test results (5x same-process + 3x subprocess) |
evidence_hash_matrix.json |
SHA256 hashes of all model artifacts |
evidence_distinctness_matrix.json |
Distinctness analysis vs prior TFLite findings |
SHA256SUMS.txt |
SHA256 checksums for all files |
Reproduction Steps
Prerequisites
# x86_64 Linux required
python3 -c "import platform; assert platform.machine() == 'x86_64', 'wrong arch'"
pip install tflite-support==0.4.4 Pillow numpy
Option A: Use pre-built artifacts (recommended)
# Models included in this repository
python3 reproduce_tflite_normalization_divergence.py
Option B: Build from scratch
pip install tensorflow-cpu==2.13.0 tflite-support==0.4.4 Pillow numpy
# The script auto-detects missing pre-built models and creates them
python3 reproduce_tflite_normalization_divergence.py
Docker (self-contained)
docker run --rm --platform linux/amd64 \
-v "$(pwd)":/work \
python:3.10-slim bash -c \
'pip install tflite-support==0.4.4 Pillow numpy -q && python3 /work/reproduce_tflite_normalization_divergence.py'
Expected Output
=== TFLite NormalizationOptions Divergence Reproduction ===
Platform: Linux-...x86_64...
Python: 3.10.x
[Phase 1] Using pre-built model artifacts...
clean SHA256: e45db82dcc520253add7db59ab5c1dd4a3e18cd8c5e0e1b25b91ebd2f659ba99
mutated SHA256: 54a8cd5160cef27d657c311dc6a4c795e23b6abf5e08d0b2bf451cd48673acb3
[Phase 3] Running Task Library ImageClassifier...
Input: 224x224x3 gray (pixel value=128)
Clean normalization: (128 - 0.5) / 0.5 = 255.0
Mutated normalization: (128 - 200.0) / 0.001 = -72000.0
[CLEAN mean=0.5 std=0.5] top3=[(2, 123.179596)]
[MUTATED mean=200.0 std=0.001] top3=[(1, 96959.820312), (0, 17284.539062)]
delta = 96836.64071655273
[Phase 4] Verdict...
DIVERGENCE_CONFIRMED β delta 96836.6407 >> threshold 0.001
Task Library applied NormalizationOptions from model metadata at inference time.
Adversarial mean/std values silently corrupt inference output for FLOAT32 inputs.
Evidence Summary
| Metric | Value |
|---|---|
| Clean top-1 score | 123.179596 |
| Mutated top-1 score | 96959.820312 |
| Delta | 96836.64071655273 |
| Threshold | 0.001 |
| Result | DIVERGENCE_CONFIRMED |
| 5x same-process runs | ALL_PASS (stddev=0.0) |
| 3x subprocess runs | ALL_PASS |
| Determinism | Fully deterministic |
Distinctness from Prior TFLite Findings
This finding is distinct from two prior flite-support findings on all five dimensions:
| Dimension | This finding | Prior finding A | Prior finding B |
|---|---|---|---|
| FlatBuffer table | NormalizationOptions |
SignatureDef |
AssociatedFile |
| Runtime stage | Pre-inference (input preprocessing) | Inference routing | Post-inference (label lookup) |
| Effect | Input tensor amplification | Wrong subgraph executed | Wrong label names |
| Code path | NormalizationPreprocessingOp::Process() |
Subgraph routing | Label resolution |
| Affected component | float32 input tensor values | Subgraph index | Output label strings |
Non-Claims
This finding does not claim:
- Remote code execution (RCE)
- Arbitrary code execution (ACE)
- Memory corruption or buffer overflow
- Universal inference bypass
The impact is silent inference output corruption via adversarial NormalizationOptions metadata.
References
- TFLite metadata schema: metadata_schema.fbs
NormalizationOptionstable- tflite-support version: 0.4.4 (PyPI manylinux2014_x86_64)
- Downloads last month
- 1