🛡️ CVE-2026-55093 — tract-nnef

⚪ Unknown ✅ No Known Exploit CWE-125 OSV
N/A
CVSS Score
0 Low4 Medium7 High9 Critical10

Description

tract-nnef: integer overflow in NNEF .dat tensor parser yields an out-of-bounds read on model load

  • Component: tract-nnef (nnef/src/tensors.rs::read_tensor) + tract-data (data/src/tensor.rs)
  • Affected versions: < 0.21.16, 0.22.00.22.2, 0.23.00.23.1 — the dense DatLoader path was unguarded across all three release lines; patched in 0.21.16 / 0.22.2 / 0.23.1
  • Class: CWE-190 (integer overflow) → CWE-125 (out-of-bounds read)
  • Trigger: loading a crafted NNEF model archive (*.nnef.tgz / *.nnef.tar / dir) via the public tract_nnef::nnef().model_for_path / model_for_read
  • Impact: read_tensor returns a memory-unsafe tensor (reported len 2^61 over a 56-byte heap allocation). Always-on primitive: a bounded heap out-of-bounds read during model build (as_uniform), an adjacent-heap information-disclosure reachable via the public load API. The resulting slice is an unsound from_raw_parts(ptr, 2^61) that SIGSEGVs (DoS) on any access past the mapped region (demonstrated by direct access). No out-of-bounds write and no RCE were achieved — tract's const-folding/as_uniform fast-paths fold simple consuming graphs without the full read.
  • Severity: Medium

Summary

read_tensor builds a tensor shape from attacker-controlled 32-bit dimensions and computes the element count len = product(shape) and the byte allocation product(shape) * size_of(dt) with unchecked usize arithmetic. In --release (no overflow-checks), both products wrap modulo 2^64. An attacker chooses dimensions so that the wrapped products collapse to a small value that satisfies the header consistency check, while the *true* element count remains astronomically large. read_tensor returns Ok with a Tensor whose reported len (e.g. 2^61+7) is far larger than its backing heap allocation (e.g. 56 bytes). The unchecked slice accessor as_slice_unchecked (from_raw_parts(ptr, self.len)) then produces a slice spanning ~18 exabytes over a 56-byte buffer. The out-of-bounds read fires automatically during model build (no inference required), reachable through the default DatLoader resource loader.

Root cause

nnef/src/tensors.rs, read_tensor:

```

let shape: TVec<usize> = header.dims[0..header.rank as usize].iter().map(|d| *d as _).collect();

let len = shape.iter().product::<usize>(); // (1) unchecked, wraps

...

} else if header.bits_per_item != u32::MAX

&& len * (header.bits_per_item as usize / 8) != header.data_size_bytes as usize // (2) wrapped == u32

{

bail!(...);

}

...

let mut tensor = unsafe { Tensor::uninitialized_dt(dt, &shape)? }; // (3) alloc off the same wrapped product

...

reader.read_exact(plain.as_bytes_mut())?; // storage-bounded read, no overflow here

Ok(tensor)

```

data/src/tensor.rs, uninitialized_aligned_dt:

```

let bytes = shape.iter().cloned().product::<usize>() * dt.size_of(); // (3) wraps to the same small value

let storage = ... Blob::new_for_size_and_align(bytes, alignment) ...;

...

tensor.update_strides_and_len(); // len = product(shape), wraps, no clamp

```

The three quantities — the consistency-check LHS (2), the allocation (3), and the reported len — are all the same wrapped product(shape)*size_of, so they stay mutually consistent and the consistency check at (2) cannot catch the overflow. data_size_bytes is a u32, so the attacker simply sets it to the wrapped value.

Corruption sink — data/src/tensor.rs::as_slice_unchecked (and data/src/tensor/plain_view.rs::as_slice_unchecked):

```

if self.storage.byte_len() == 0 { &[] }

else { std::slice::from_raw_parts(self.as_ptr_unchecked(), self.len()) } // len = 2^61 over a 56-byte alloc

```

The only guard is byte_len() == 0. A small non-zero allocation defeats it and yields an unsound oversized slice.

Witness (F64)

```

dims = [33955849, 7005787, 359, 3, 3, 3] (rank 6, each <= u32::MAX)

product(shape)= 2_305_843_009_213_693_959 = 2^61 + 7

bits_per_item = 64 (F64), item_type = 0, item_type_vendor = 0

data_size_bytes = 56 # == (2^61+7)*8 mod 2^64

```

  • len * (bits/8) mod 2^64 = (2^61+7)*8 mod 2^64 = 56 == data_size_bytes → consistency check passes.
  • allocation = (2^61+7)*8 mod 2^64 = 56 bytes (7 × F64).
  • reported len = 2^61+7 elements.

Only the is_copy() numeric arms (F16/F32/F64/int, and likely the complex arms) are exploitable. F64 is the cleanest (bits/8 divides evenly). The bool, String, and block-quant paths are each guarded by an independent mechanism (size_of==1 prevents byte/element divergence; String bails on a missing num_traits::Zero impl; block-quant has its own ensure!(expected_len == data_size_bytes) and uses non-plain Exotic storage).

Reachability (load-time, public API)

```

nnef().model_for_read(tar)

-> proto_model_for_read nnef/src/framework.rs:303

-> Da

How this vulnerability can be exploited

This issue can be reached with local access to the system, attack complexity is low, an attacker needs no privileges on the target. A user must be tricked into taking some action. The scope is unchanged, so the impact stays within the vulnerable component. Rated impact: confidentiality low, integrity none, availability high.

Weakness class

CVE-2026-55093 is classified as CWE-125: Out-of-bounds Read. The code reads past the limits of a buffer, exposing adjacent memory contents or crashing the process.

Affected software

CVE-2026-55093 is recorded against 1 package.

  • tract-nnef

Timeline and source

Published on 18 June 2026 and last revised on 28 July 2026. No public exploit is currently recorded for this entry. Record sourced from OSV.

References

github.com (Web)
github.com (Package)

Details

Severity Unknown
CVSS Score N/A
CVSS Vector CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:L/I:N/A:H
CWE CWE-125
Public Exploit ✅ No
Source OSV
Published 2026-06-18
Updated 2026-08-12
Modified 2026-07-28
Fix URL N/A

Affected Packages

Software From version Fixed in
tract-nnef

Free Vulnerability Check

Is your site affected by CVE-2026-55093?

BotEraser helps you identify potentially vulnerable plugins and themes by checking your installation against CVE-2026-55093 and other known CVE records.

Scan My Site Free →

No credit card required  ·  Results in minutes

ⓘ Data Notice: The information presented above has been compiled from publicly available internet sources. Boteraser aggregates this data solely for informational purposes and does not independently classify, evaluate, or endorse any findings about the vulnerabilities listed. The accuracy and completeness of this information is the sole responsibility of the original publishers. Boteraser and its operators accept no liability for any decisions made based on this data.