Every CVE Search/ Get API key

CVE-2026-55093

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

6.1 MediumFix available

Tract is a tiny, no-nonsense, self-contained TensorFlow and ONNX inference toolkit. Prior to 0.21.16, 0.22.2, and 0.23.1, tract-nnef uses unchecked usize multiplication in nnef/src/tensors.rs read_tensor for attacker-controlled tensor dimensions, the allocation size, and the reported tensor length. Loading a crafted NNEF archive through model_for_path or model_for_read reaches the default DatLoader and can make the wrapped size check accept a small allocation while data/src/tensor.rs as_slice_unchecked creates a much larger logical slice. Model construction through as_uniform can then read beyond the heap allocation and disclose adjacent data, and later access can terminate the process with a segmentation fault. The affected dense numeric tensor path does not include the independently guarded bool, String, or block-quant paths, and no out-of-bounds write or code execution was demonstrated. This issue is fixed in versions 0.21.16, 0.22.2, and 0.23.1.

Attack profile

How easy it is to exploit

Attack vector
Local
Complexity
Low
Privileges needed
None
User interaction
Required

What an attacker can affect

Confidentiality
Low
Integrity
None
Availability
High

Exploit likelihood

0.18%Unchanged since Sep 15, 2026
90%50%10%1%

What each source reports

CVE List Score used 6.1

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

Tract is a tiny, no-nonsense, self-contained TensorFlow and ONNX inference toolkit. Prior to 0.21.16, 0.22.2, and 0.23.1, tract-nnef uses unchecked usize multiplication in nnef/src/tensors.rs read_tensor for attacker-controlled tensor dimensions, the allocation size, and the reported tensor length. Loading a crafted NNEF archive through model_for_path or model_for_read reaches the default DatLoader and can make the wrapped size check accept a small allocation while data/src/tensor.rs as_slice_unchecked creates a much larger logical slice. Model construction through as_uniform can then read beyond the heap allocation and disclose adjacent data, and later access can terminate the process with a segmentation fault. The affected dense numeric tensor path does not include the independently guarded bool, String, or block-quant paths, and no out-of-bounds write or code execution was demonstrated. This issue is fixed in versions 0.21.16, 0.22.2, and 0.23.1.

Record
CVE-2026-55093
CVSS 3.1
6.1 MediumCVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:L/I:N/A:H
Weakness
CWE-125, CWE-190
Updated
2026-11-02
EUVDEUVD-2026-77792 6.1

Tract is a tiny, no-nonsense, self-contained TensorFlow and ONNX inference toolkit. Prior to 0.21.16, 0.22.2, and 0.23.1, tract-nnef uses unchecked usize multiplication in nnef/src/tensors.rs read_tensor for attacker-controlled tensor dimensions, the allocation size, and the reported tensor length. Loading a crafted NNEF archive through model_for_path or model_for_read reaches the default DatLoader and can make the wrapped size check accept a small allocation while data/src/tensor.rs as_slice_unchecked creates a much larger logical slice. Model construction through as_uniform can then read beyond the heap allocation and disclose adjacent data, and later access can terminate the process with a segmentation fault. The affected dense numeric tensor path does not include the independently guarded bool, String, or block-quant paths, and no out-of-bounds write or code execution was demonstrated. This issue is fixed in versions 0.21.16, 0.22.2, and 0.23.1.

Record
EUVD-2026-77792
CVSS 3.1
6.1 MediumCVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:L/I:N/A:H
Updated
2026-11-02
GitHub AdvisoriesGHSA-x5mv-8wgw-29hg 6.1

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.0–0.22.2, 0.23.0–0.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…
Record
GHSA-x5mv-8wgw-29hg
CVSS 3.1
6.1 MediumCVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:L/I:N/A:H
Weakness
CWE-125, CWE-190
Updated
2026-11-02
NVD 6.1

Tract is a tiny, no-nonsense, self-contained TensorFlow and ONNX inference toolkit. Prior to 0.21.16, 0.22.2, and 0.23.1, tract-nnef uses unchecked usize multiplication in nnef/src/tensors.rs read_tensor for attacker-controlled tensor dimensions, the allocation size, and the reported tensor length. Loading a crafted NNEF archive through model_for_path or model_for_read reaches the default DatLoader and can make the wrapped size check accept a small allocation while data/src/tensor.rs as_slice_unchecked creates a much larger logical slice. Model construction through as_uniform can then read beyond the heap allocation and disclose adjacent data, and later access can terminate the process with a segmentation fault. The affected dense numeric tensor path does not include the independently guarded bool, String, or block-quant paths, and no out-of-bounds write or code execution was demonstrated. This issue is fixed in versions 0.21.16, 0.22.2, and 0.23.1.

Record
CVE-2026-55093
CVSS 3.1
6.1 MediumCVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:L/I:N/A:H
Weakness
CWE-125, CWE-190
Updated
2026-11-02
OSV 6.1

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

Tract is a tiny, no-nonsense, self-contained TensorFlow and ONNX inference toolkit. Prior to 0.21.16, 0.22.2, and 0.23.1, tract-nnef uses unchecked usize multiplication in nnef/src/tensors.rs read_tensor for attacker-controlled tensor dimensions, the allocation size, and the reported tensor length. Loading a crafted NNEF archive through model_for_path or model_for_read reaches the default DatLoader and can make the wrapped size check accept a small allocation while data/src/tensor.rs as_slice_unchecked creates a much larger logical slice. Model construction through as_uniform can then read beyond the heap allocation and disclose adjacent data, and later access can terminate the process with a segmentation fault. The affected dense numeric tensor path does not include the independently guarded bool, String, or block-quant paths, and no out-of-bounds write or code execution was demonstrated. This issue is fixed in versions 0.21.16, 0.22.2, and 0.23.1.

Record
CVE-2026-55093
CVSS 3.1
6.1 MediumCVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:L/I:N/A:H
Weakness
CWE-125, CWE-190
Updated
2026-11-02
OSVGHSA-x5mv-8wgw-29hg 6.1

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.0–0.22.2, 0.23.0–0.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…
Record
GHSA-x5mv-8wgw-29hg
CVSS 3.1
6.1 MediumCVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:L/I:N/A:H
Weakness
CWE-125, CWE-190
Updated
2026-11-02
OSVRUSTSEC-2026-0217 6.1

Integer overflow in tract-nnef NNEF tensor parser leads to out-of-bounds read on model load

tract_nnef::tensors::read_tensor builds a tensor shape from attacker-controlled 32-bit dimensions and computes both the element count product(shape) and the byte allocation product(shape) * size_of(dt) with unchecked usize arithmetic. In release builds (no overflow-checks) both products wrap modulo 2^64.

A crafted NNEF .dat tensor can choose dimensions whose wrapped products collapse to a small value that satisfies the header size-consistency check, while the true element count stays astronomically large. read_tensor then returns a Tensor whose reported len (e.g. 2^61 + 7) far exceeds its backing heap allocation (e.g. 56 bytes). The unchecked accessor as_slice_unchecked (slice::from_raw_parts(ptr, self.len())) subsequently yields a slice spanning ~18 EiB over the small buffer.

The out-of-bounds read fires automatically during model build (no inference required), reachable through the default DatLoader resource loader via the public tract_nnef::nnef().model_for_path / model_for_read API when the const-folding as_uniform fast-path materializes the over-long constant. The always-on primitive is a bounded adjacent-heap over-read (information disclosure); access further past the mapped region SIGSEGVs (denial of service). No out-of-bounds write or code execution was demonstrated.

Affected: every release line prior to the backported fixes — < 0.21.16, 0.22.0–0.22.1, and 0.23.0. The block-quant path had already received an analogous blob-size guard; the dense DatLoader path was missed.

Mitigation

Upgrade to 0.21.16, 0.22.2, or 0.23.1. The fix computes the shape product and byte size with checked_mul and rejects the tensor on overflow (commit 34c7df2).

Record
RUSTSEC-2026-0217
CVSS 3.1
6.1 MediumCVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:L/I:N/A:H
Updated
2026-11-02

Affected products

Affected packages

Affected packages
PackageAffectedFixed
tract-nnef
crates.io
0.23.0 up to 0.23.1
0.23.1
tract-nnef
crates.io
0.22.0 up to 0.22.2
0.22.2
tract-nnef
crates.io
before 0.21.16
0.21.16
tract-nnef
crates.io
0.0.0-0 up to 0.21.16, 0.22.0 up to 0.22.2, 0.23.0 up to 0.23.1
0.21.160.22.20.23.1

History

History
RecordedChangedSources
first indexedCVE List, EUVD, GitHub Advisories, NVD, OSV

Dates are when Every CVE recorded each change. Upstream dates may differ.

In short

CVE-2026-55093 is a medium vulnerability (CVSS 6.1) in Sonos Tract, published June 18, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.18%, the 7th percentile. Fixed in tract-nnef 0.23.1.

References

Show 7 moreShow less
This page doesn't list everything

Left out here: the rest of 2 source descriptions. The JSON record has them.

MoveEnter Open