Skip to content

fix(data): reject raw tensor whose data length != declared shape (#2390)#2413

Open
czoli1976 wants to merge 1 commit into
sonos:mainfrom
czoli1976:fix/raw-tensor-length-check
Open

fix(data): reject raw tensor whose data length != declared shape (#2390)#2413
czoli1976 wants to merge 1 commit into
sonos:mainfrom
czoli1976:fix/raw-tensor-length-check

Conversation

@czoli1976

Copy link
Copy Markdown
Contributor

Summary

Fixes #2390. Tensor::from_raw_dt_align allocated ∏(shape) × sizeof(dt) bytes and then copy_from_slice-d the caller-supplied content into it with no length check. When the two lengths differ — e.g. a crafted or malformed .onnx whose initializer raw_data byte count does not match its declared dimscopy_from_slice panics, aborting model load in both debug and release builds. Since tract loads externally-supplied ONNX, this is a reachable DoS.

This validates the content length against the freshly-allocated tensor and returns a TractResult error instead of panicking. The check sits at from_raw_dt_align, the single choke point for all from_raw callers (ONNX, NNEF, …), so every raw-load path is covered.

Reproduction (before this PR)

The 94-byte PoC from #2390 (raw_data of 12 bytes against a shape needing 440):

thread '...' panicked at data/src/tensor.rs:502:31:
copy_from_slice: source slice length (12) does not match destination slice length (440)

After this PR the same file loads to a graceful Err (no panic), verified in release.

Change

let mut tensor = unsafe { Tensor::uninitialized_aligned_dt(dt, shape, align) }?;
let expected = tensor.as_bytes().len();
ensure!(
    content.len() == expected,
    "Raw tensor data length ({}) does not match shape {:?} of {:?} ({} bytes)",
    content.len(), shape, dt, expected
);
tensor.as_bytes_mut().copy_from_slice(content);

Tests

  • New regression test from_raw_rejects_length_mismatch in data (short, long, and exact-match cases).
  • cargo test -p tract-data --lib (140) and cargo test -p tract-onnx --lib (10) pass; the end-to-end PoC load now returns Err instead of panicking.

🤖 Generated with Claude Code

`Tensor::from_raw_dt_align` allocated `∏(shape) × sizeof(dt)` bytes and then
`copy_from_slice`-d the caller-supplied content into it with no length check. A
mismatched length (e.g. an ONNX initializer whose `raw_data` byte count does not
match its `dims`) panicked in `copy_from_slice`, aborting model load in both
debug and release builds (DoS on any process loading untrusted models).

Validate the content length against the allocated tensor before copying and
return a `TractResult` error instead of panicking. This is the single choke
point for all `from_raw` callers (ONNX, NNEF, ...).

Fixes sonos#2390.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@czoli1976

Copy link
Copy Markdown
Contributor Author

@kali

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Panic (copy_from_slice length mismatch) loading an ONNX initializer whose raw_data size != declared shape

1 participant