You need to agree to share your Hugging Face 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.

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

Check out the documentation for more information.

burn-onnx / onnx-ir uncapped external_data length -> DoS (crash)

Target: tracel-ai/burn-onnx (crates.io onnx-ir + burn-onnx), commit 727753694198fe8daca60969cdfb8a3a4e8ad66a, tag v0.21.0. Binary under test: onnx2burn (crates/burn-onnx/src/bin/onnx2burn.rs), built with default features (mmap feature ON, matching upstream defaults for both onnx-ir and burn-onnx).

Sink

crates/onnx-ir/src/tensor_store.rs

fn load_external_read(file_path: &PathBuf, offset: u64, length: u64) -> Vec<u8> {
    let mut file = File::open(file_path).unwrap_or_else(|e| { panic!(...) });
    file.seek(SeekFrom::Start(offset)).unwrap_or_else(|e| { panic!(...) });
    let mut buffer = vec![0u8; length as usize];      // <-- L185: no upper bound on `length`
    file.read_exact(&mut buffer).unwrap_or_else(|e| { // <-- L186-193: panics on short/EOF read
        panic!("Failed to read {} bytes from '{}': {}", length, file_path.display(), e)
    });
    buffer
}

length originates from the ONNX file's TensorProto.external_data "length" key (crates/onnx-ir/src/external_data.rs:76-82), parsed with a bare value.parse::<u64>() β€” no cap, no comparison against the actual external file size at parse time.

crates/onnx-ir/src/tensor_store.rs::load_external_data (L119-134) tries load_external_mmap first (when the mmap feature is on, which is the default); if the declared offset+length exceeds the real mmap'd file size, load_external_mmap returns Err(UnexpectedEof) and the dispatcher silently falls through to the unguarded load_external_read above.

Materialization is lazy (TensorDataRef::to_tensor_data) but is inevitably triggered during normal onnx2burn codegen: any node that consumes the initializer as a constant (e.g. type inference on a Constant/weight argument via Argument::value() -> ValueStore::get_tensor_data() -> TensorStore::get() -> to_tensor_data()) forces the load. A model that uses the initializer in an actual op (e.g. MatMul) guarantees this path is hit; no dead-code elimination shortcut avoids it.

Two observed crash variants, same root cause, same line (tensor_store.rs L185)

  1. Allocator abort (length = 100 GiB, poc_variant_a/): the vec![0u8; length as usize] allocation itself fails and Rust's global allocator calls handle_alloc_error, which aborts the process (memory allocation of 107374182400 bytes failed, SIGABRT, exit 134). This is not catchable via catch_unwind β€” process death is immediate.
  2. Unwinding panic on short read (length = 512 MiB, poc_variant_a_moderate/): the 512 MiB allocation succeeds, but read_exact from the real (1-byte) file hits EOF and the unwrap_or_else explicitly panics: panicked at crates/onnx-ir/src/tensor_store.rs:187:13: Failed to read 536870912 bytes from '.../w.bin': failed to fill whole buffer (exit 101 β€” Rust's standard uncaught-panic exit code).

Either way, a 1-byte external-data file plus a 160-byte crafted .onnx is enough to kill the onnx2burn process outright β€” no large payload, no disk/transfer amplification required, purely from a length field the parser never validates against reality.

Reproduction

cd /home/wey/fuzz/burn/repo
cargo build -p burn-onnx --bin onnx2burn      # default features (mmap on)

# Variant A (lead): allocator-abort DoS from a 160-byte model + 1-byte file
RUST_BACKTRACE=full ./target/debug/onnx2burn \
    /home/wey/fuzz/burn/poc/poc_variant_a/model.onnx \
    /home/wey/fuzz/burn/poc/poc_variant_a/out
# => "memory allocation of 107374182400 bytes failed", SIGABRT, exit 134
# Backtrace confirms: load_external_read (tensor_store.rs:185) <-
#   load_external_data <- to_tensor_data <- TensorStore::get <-
#   ValueStore::get_tensor_data <- Argument::value <- ConstantProcessor::infer_types

# Variant A-moderate: catchable-panic DoS (smaller declared length)
./target/debug/onnx2burn \
    /home/wey/fuzz/burn/poc/poc_variant_a_moderate/model.onnx \
    /home/wey/fuzz/burn/poc/poc_variant_a_moderate/out
# => panicked at crates/onnx-ir/src/tensor_store.rs:187:13, exit 101

# Negative control: correct length (64 bytes, matches real 64-byte weight
# file) -> loads and codegens successfully, exit 0
./target/debug/onnx2burn \
    /home/wey/fuzz/burn/poc/control_valid/model.onnx \
    /home/wey/fuzz/burn/poc/control_valid/out
# => "Model generated", "Finished converting ONNX to Burn", exit 0

Files

  • craft_onnx.py β€” dependency-free protobuf encoder that builds a minimal valid ONNX graph (X[1,4] --MatMul(weight[4,4])--> Y[1,4]) with the weight initializer's external_data length field fully attacker-controlled. No onnx/protobuf Python package required.
  • poc_variant_a/{model.onnx,w.bin,run.log} β€” 100 GiB declared length, 1-byte real file -> allocator abort (SIGABRT).
  • poc_variant_a_moderate/{model.onnx,w.bin,run.log} β€” 512 MiB declared length, 1-byte real file -> read_exact panic (unwinding, exit 101).
  • control_valid/{model.onnx,w.bin,out/} β€” negative control, correctly sized external data (64 bytes), loads and generates code successfully.

Dedup note

onnx-ir is a from-scratch Rust protobuf/ONNX parser (not a wrapper around upstream onnx/onnxruntime). CVE-2025-51480 is a path-traversal bug in the upstream Python onnx package's model writer β€” a different codebase. The equivalent path-traversal class of bug is already hardened here (see crates/onnx-ir/src/external_data.rs header comment + tests: null-byte, absolute-path, ..-traversal, and canonicalize+starts_with checks are all present and passing). This finding is the separate, still-open "length is unchecked against reality" issue in the same external-data loader.

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