Summary
get_attr_opt_scalar<&TensorProto> in tract's ONNX helper code retrieves a TensorProto from a node
attribute with .map(|attr| attr.t.as_ref().unwrap()). The ONNX AttributeProto.t field is optional
— it can be absent in a well-formed protobuf even when attribute_type == TENSOR. When t is unset,
attr.t.as_ref() is None and .unwrap() panics. Because Rust panics propagate regardless of
optimization level, this is a deterministic release panic (DoS) on loading any ONNX model with a
Tensor-type attribute whose t field is missing.
Affected component
- Repository:
sonos/tract (tract-onnx crate)
- File:
tract-onnx/src/pb_helpers.rs:114 — .map(|attr| attr.t.as_ref().unwrap()) in
get_attr_opt_scalar<&TensorProto>.
- Reachable from
onnx().model_for_read() / model_for_path() — any ONNX model load. The confirmed
path goes through the Constant op handler (ops/mod.rs:53) during graph construction; any op that
reads a Tensor-type attribute via get_attr_opt::<&TensorProto>() is similarly affected.
Reproduction
PoC: findings/tract/poc-085-tract-onnx-attr-tensor-unwrap.onnx (86 bytes) — an ONNX model with a
Tensor-type attribute whose t field is unset.
Load via tract_onnx::onnx().model_for_read() (or model_for_path()). Observed:
thread panicked at tract-onnx/src/pb_helpers.rs:114:41:
called `Option::unwrap()` on a `None` value
(via get_attr_opt_scalar → .unwrap() on the absent t field).
Root cause
// tract-onnx/src/pb_helpers.rs:111-116
impl<'a> AttrScalarType<'a> for &'a TensorProto {
fn get_attr_opt_scalar(node: &'a NodeProto, name: &str) -> TractResult<Option<Self>> {
Ok(node
.get_attr_opt_with_type(name, AttributeType::Tensor)?
.map(|attr| attr.t.as_ref().unwrap())) // panics when t is None
}
}
get_attr_opt_with_type returns Some(attr) when it finds a matching-type attribute, but
attr.t (Option<Box<TensorProto>>) can still be None. The .unwrap() turns that absent field
into a process panic.
Suggested fix
Return an error instead of unwrapping:
impl<'a> AttrScalarType<'a> for &'a TensorProto {
fn get_attr_opt_scalar(node: &'a NodeProto, name: &str) -> TractResult<Option<Self>> {
Ok(node
.get_attr_opt_with_type(name, AttributeType::Tensor)?
.map(|attr| attr.t.as_ref()
.ok_or_else(|| anyhow::anyhow!(
"attribute '{}' has type TENSOR but t field is absent", name)))
.transpose()?)
}
}
Revalidation
Source-level revalidation on 2026-06-14 against tract HEAD 5ac3908: onnx/src/pb_helpers.rs:114
still maps with attr.t.as_ref().unwrap() and is unguarded — the release panic is intact. The tract
Sonos email window (opened 2026-05-28) has elapsed, so this is re-routing to a public issue.
Status: advisory drafted; re-routing to public issue + VulDB; revalidated 2026-06-14 source-level vs
tract HEAD 5ac3908.
Proof-of-concept files (base64)
Decode with base64 -d > file.
poc-085-tract-onnx-attr-tensor-unwrap.onnx (86 bytes):
CA06TAovEgFZIghDb25zdGFudCogCgV2YWx1ZSIUCAMQASIMAACAPwAAAEAAAEBAQgCgAQQSCGNvbnN0YW50Yg8KAVkSCgoICAESBAoCCANCBAoAEBE=
Summary
get_attr_opt_scalar<&TensorProto>in tract's ONNX helper code retrieves aTensorProtofrom a nodeattribute with
.map(|attr| attr.t.as_ref().unwrap()). The ONNXAttributeProto.tfield is optional— it can be absent in a well-formed protobuf even when
attribute_type == TENSOR. Whentis unset,attr.t.as_ref()isNoneand.unwrap()panics. Because Rust panics propagate regardless ofoptimization level, this is a deterministic release panic (DoS) on loading any ONNX model with a
Tensor-type attribute whose
tfield is missing.Affected component
sonos/tract(tract-onnxcrate)tract-onnx/src/pb_helpers.rs:114—.map(|attr| attr.t.as_ref().unwrap())inget_attr_opt_scalar<&TensorProto>.onnx().model_for_read()/model_for_path()— any ONNX model load. The confirmedpath goes through the
Constantop handler (ops/mod.rs:53) during graph construction; any op thatreads a Tensor-type attribute via
get_attr_opt::<&TensorProto>()is similarly affected.Reproduction
PoC:
findings/tract/poc-085-tract-onnx-attr-tensor-unwrap.onnx(86 bytes) — an ONNX model with aTensor-type attribute whose
tfield is unset.Load via
tract_onnx::onnx().model_for_read()(ormodel_for_path()). Observed:(via
get_attr_opt_scalar→.unwrap()on the absenttfield).Root cause
get_attr_opt_with_typereturnsSome(attr)when it finds a matching-type attribute, butattr.t(Option<Box<TensorProto>>) can still beNone. The.unwrap()turns that absent fieldinto a process panic.
Suggested fix
Return an error instead of unwrapping:
Revalidation
Source-level revalidation on 2026-06-14 against tract HEAD
5ac3908:onnx/src/pb_helpers.rs:114still maps with
attr.t.as_ref().unwrap()and is unguarded — the release panic is intact. The tractSonos email window (opened 2026-05-28) has elapsed, so this is re-routing to a public issue.
Status: advisory drafted; re-routing to public issue + VulDB; revalidated 2026-06-14 source-level vs
tract HEAD
5ac3908.Proof-of-concept files (base64)
Decode with
base64 -d > file.poc-085-tract-onnx-attr-tensor-unwrap.onnx (86 bytes):