| # Arm NN unknown layer discriminator parser-table OOB read |
|
|
| ## Summary |
|
|
| Arm NN's native `.armnn` deserializer accepts an out-of-range `AnyLayer` |
| union discriminator and uses it directly to index `m_ParserFunctions`. |
| `m_ParserFunctions` has `Layer_MAX + 1` entries, but |
| `CreateNetworkFromGraph()` performs no range check before indexing it. |
|
|
| The supplied trigger differs from the valid control by one byte: |
| the `AnyLayer.layer_type` discriminator at file offset 763 changes from |
| `0x09` (`InputLayer`) to `0x7b` (123, beyond `Layer_MAX`). The outer |
| FlatBuffer verifier accepts the file. The subsequent parser-table lookup |
| causes an ASan heap-buffer-overflow read. |
|
|
| ## Verified target |
|
|
| - Repository: `ARM-software/armnn` |
| - Commit: `2b61cecc9df7a43fca1463795062cf359e6be820` |
| - Date: 2026-07-02 |
| - Library: `libarmnnDeserializer.35.0` |
| - Entry point: `armnnDeserializer::IDeserializer::CreateNetworkFromBinary` |
|
|
| ## Root cause |
|
|
| The constructor allocates the dispatch table at |
| `Deserializer.cpp:207-210`: |
|
|
| ```cpp |
| m_ParserFunctions(Layer_MAX+1, |
| &IDeserializer::DeserializerImpl::ParseUnsupportedLayer) |
| ``` |
|
|
| `CreateNetworkFromGraph()` then trusts the model discriminator: |
|
|
| ```cpp |
| for (AnyLayer const* layer : *graph->layers()) |
| { |
| if (layer->layer_type() != Layer_InputLayer && |
| layer->layer_type() != Layer_OutputLayer) |
| { |
| auto& parserFunction = m_ParserFunctions[layer->layer_type()]; |
| (this->*parserFunction)(graph, layerIndex); |
| } |
| } |
| ``` |
|
|
| The union discriminator is an eight-bit value. The current generated enum has |
| `Layer_MAX = Layer_ScatterNdLayer`, while the trigger supplies 123. The |
| FlatBuffers verifier verifies structural offsets but does not reject this |
| unknown enum value. |
|
|
| ## Differential proof |
|
|
| - `control.armnn`: 908 bytes, discriminator byte `0x09` |
| - `trigger.armnn`: 908 bytes, discriminator byte `0x7b` |
| - All other bytes are identical. |
|
|
| Three control runs completed with exit code 0. Three trigger runs terminated |
| with exit code 134 and the same AddressSanitizer heap-buffer-overflow in |
| `CreateNetworkFromGraph()`. |
|
|
| SHA-256: |
|
|
| - control: `e1d1d9895e1629eaea453ac60cdc21cd38aef7bea9422aaefb41e23921cfd7ae` |
| - trigger: `ff1c59432c9de9a631f1df9aab69d077d8b7a31e2e74b82aba4114da66f064bd` |
|
|
| ## Reproduction |
|
|
| With an ASan/UBSan Arm NN build in `cyber/huntr-mfv/build-armnn`: |
|
|
| ```bash |
| chmod +x reproduce.sh |
| ./reproduce.sh /absolute/path/to/makemoney |
| ``` |
|
|
| `generate-trigger.mjs` checks the control length and original discriminator, |
| then deterministically applies the one-byte mutation. |
|
|
| ## Impact |
|
|
| Loading an untrusted `.armnn` model reads a member-function pointer beyond the |
| heap allocation that stores the parser dispatch table. The demonstrated |
| result is a reliable load-time crash. Depending on adjacent heap contents, the |
| subsequent indirect member-function call may also consume attacker-influenced |
| data as a code pointer. |
|
|
| The report does not claim code execution; the verified impact is |
| out-of-bounds read and denial of service. |
|
|
| ## Prior-art distinction |
|
|
| Exact searches for `AnyLayer.layer_type`, `m_ParserFunctions`, |
| `CreateNetworkFromGraph`, source line 933, the fixture hash, and the sanitizer |
| signature returned no public disclosure. Public native Arm NN findings cover |
| different semantic fields and sinks. The previously submitted TFLite |
| `opcode_index` issue is in a different model format, parser, dispatch table, |
| and fix site. |
|
|
| ## Suggested fix |
|
|
| Reject any discriminator outside the generated enum range before indexing: |
|
|
| ```cpp |
| const auto type = layer->layer_type(); |
| if (type <= Layer_NONE || type > Layer_MAX) |
| { |
| throw ParseException("Unsupported layer union discriminator"); |
| } |
| ``` |
|
|
| The generated FlatBuffer union verifier should also fail closed for unknown |
| union discriminators. |
|
|