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

Check out the documentation for more information.

Arm NN deserializer output-slot/layer-index confusion

Status: reproduced on current Arm NN; fresh prior-art gate open

Summary

Arm NN's FlatBuffers deserializer incorrectly resolves an output connection's outputSlotIndex as though it were a layer ID. A model can reorder its layer table to make the deserializer return tensor metadata from a different source output slot than the slot actually connected to the output layer.

The supplied model completes deserialization, optimization, runtime loading, and inference. No malformed-buffer behavior is required. Arm NN reports attacker-selected quantization metadata to the consumer while executing with the source slot's true metadata. The result is silent, deterministic misinterpretation of inference output.

Tested against Arm NN commit: 2b61cecc9df7a43fca1463795062cf359e6be820.

Root cause

SetupOutputLayers() correctly maps the model-controlled source layer ID to a position in the layer vector:

unsigned int sourceLayerIndex =
    GetLayerIndexInVector(graph, connection->sourceLayerIndex());

It then incorrectly performs the same layer lookup on a source output-slot ID:

unsigned int outputSlotIndex =
    GetLayerIndexInVector(graph, connection->outputSlotIndex());

GetLayerIndexInVector() searches every layer's base.index and returns that layer's position in graph.layers. That result is then used as an index into the source layer's outputSlots vector.

In the proof, layer ID 1 is at vector position 0. A connection to source output slot 1 is therefore read as source output slot 0. A connection to slot 0 similarly resolves to slot 1, because layer ID 0 is at vector position 1.

The graph connection itself is registered from the original slot IDs. Only the public output binding metadata is swapped.

Source:

Verified impact

quantization-swap.armnn declares two one-element QAsymmU8 source outputs:

  • Source slot 0: scale 0.5
  • Source slot 1: scale 2.0

The runtime retains those true scales. The deserializer returns them in the opposite order:

parser output-0 elements=1 scale=2 offset=0
parser output-1 elements=1 scale=0.5 offset=0
runtime output-0 elements=1 scale=0.5 offset=0
runtime output-1 elements=1 scale=2 offset=0
enqueue=success raw_outputs=20,10
consumer_values=40,5

Using the runtime metadata, the correct interpreted values are 10 and 20. Using GetNetworkOutputBindingInfo(), as Arm NN's own deserializer fixture does when allocating and interpreting outputs, the consumer obtains 40 and 5.

The same primitive can also:

  • Swap reported output shapes. metadata-swap.armnn reports [3] and [1] while the runtime reports [1] and [3].
  • Force deterministic load failure when the erroneous layer-vector position exceeds the source output-slot count. poc.armnn reaches Arm NN's null tensor guard.

The quantization proof is the primary impact because inference succeeds and the manipulated values are delivered silently.

Reproduction

Build Arm NN with the deserializer and the reference backend enabled. The local validation build used Apple Clang with:

-fsanitize=address,undefined -fno-omit-frame-pointer

Compile the harness against libarmnnDeserializer and libarmnn:

c++ -std=c++17 \
  -fsanitize=address,undefined -fno-omit-frame-pointer \
  -I /path/to/armnn/include \
  quantization-armnn.cpp \
  -L /path/to/armnn/build \
  -larmnnDeserializer -larmnn \
  -Wl,-rpath,/path/to/armnn/build \
  -o quantization-armnn

ASAN_OPTIONS=detect_leaks=0:halt_on_error=1 \
UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 \
./quantization-armnn quantization-swap.armnn

Expected result: the parser and runtime quantization scales are swapped, the enqueue operation succeeds, and the consumer values are 40,5.

Artifacts:

  • quantization-swap.armnn: primary silent-manipulation proof
  • quantization-swap.armnn.json: human-readable FlatBuffers source
  • quantization-armnn.cpp: optimize/load/enqueue harness
  • control.armnn: control with coincident layer IDs/vector positions
  • metadata-swap.armnn: shape-metadata mismatch variant
  • poc.armnn: guarded invalid-slot variant

Primary model SHA-256: 0e889434ad54742096dde1368b5c1add7ae4efc5c483390e4803d6cd39a143ec.

Suggested fix

Use the connection's raw outputSlotIndex as the source output-slot index, then validate it against sourceBaseLayer->outputSlots()->size():

const unsigned int outputSlotIndex =
    baseLayer->inputSlots()->Get(0)->connection()->outputSlotIndex();

Add a regression test with:

  • Non-sequential layer IDs.
  • Layer IDs deliberately placed at vector positions different from their numeric values.
  • A multi-output source whose outputs have distinguishable tensor metadata.
  • Assertions that GetNetworkOutputBindingInfo() matches the connected source slot and the runtime output metadata.

Novelty

A fresh exact and semantic scan on 2026-07-28 found:

  • 0 matching Hugging Face repositories.
  • 0 matching Arm NN GitHub issues or pull requests.
  • 1 local match: this candidate's own prior-art record.

Public Arm NN proofs found during screening cover other primitives, including dimension-specificity length, tensor-size overflow, empty output slots, Concat origins, and ScatterNd.

Gate record: ../../prior-art/armnn-output-binding-slot-index-confusion/20260729T015629.564Z.md.

Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support