Summary
MatterBuilder's variable-size parse paths compute the full frame size fs from an attacker-controlled decoded size using unchecked arithmetic, violating the Arithmetic Safety mandatory rule ("No bare arithmetic in production paths that compute sizes/offsets; use checked_* and return Err on overflow").
Surfaced as a follow-up during #33 / the deep-fuzz revival (the same file whose 5BAA lead-byte panic was fixed in #74). Not yet observed as a live crash — filing proactively because it is a latent rule violation and is more likely reachable on wasm32 (a supported target, where usize is 32-bit).
Locations (src/core/matter/builder.rs)
:132 (from_qualified_base64): let fs = … (size * 4) + cs;
:263 (from_qualified_base2): let fs = … (size * 4) + cs;
:265 (from_qualified_base2): let bfs = (fs * 3).div_ceil(4);
size: usize comes from decode_int(soft_str) — i.e. the primitive's soft field, which is untrusted input. Note the adjacent :276 already uses .checked_add(ls), so these bare * 4 / + cs sites are inconsistent oversights, not a deliberate choice.
Impact
For a variable-size code whose soft field encodes a large size:
- Debug builds:
size * 4 panics on overflow → panic-on-untrusted-input (DoS), the same class as the 5BAA bug just fixed.
- Release builds:
size * 4 wraps silently → a small/incorrect fs. The subsequent if stream.len() < fs check then passes against a bogus small fs, and let trim = &stream[..fs] slices to the wrong length → silently-wrong parse (the exact "truncated frame that parses as valid" failure the arithmetic-safety rule warns about). saturating_* would be equally wrong here.
Reachability
usize is 32-bit on wasm32-unknown-unknown (a gated build target — flake.nix cesr-wasm). There, size * 4 overflows once size ≥ 2^30 (≈1.07e9), i.e. a soft field of ≥ 5 base64 chars (64^5 = 2^30).
- On 64-bit it needs
size ≥ 2^62 (soft field ≥ 11 chars).
- Open question (needs confirming): the widest
ss (soft size) among variable-size Matter codes — this determines whether the overflow is reachable today on each target, versus latent. Either way the unchecked arithmetic should be fixed.
Suggested fix
Replace the bare arithmetic with checked_mul / checked_add, returning a typed error on overflow (e.g. ValidationError::IncorrectRawSize or a new ValidationError::SizeOverflow variant) rather than panicking or wrapping. Mirror the existing .checked_add(ls) pattern at :276. Apply to all three sites (qb64 :132, qb2 :263/:265).
Tests
- A regression unit test per site feeding a soft field that would overflow on 32-bit, asserting a typed
Err (not panic, not silent wrap). Consider running it under --target wasm32-* or a #[cfg(target_pointer_width = "32")]-aware assertion.
- Add the discovered input(s) to the
matter_from_qb64 / matter_from_qb2 fuzz corpus once characterized.
Provenance
Found by inspection while fixing the 5BAA lead-byte-slice panic (#74) and reviving the deep-fuzz workflow (#75). The now-working nightly deep-fuzz may independently surface this on a longer run.
Summary
MatterBuilder's variable-size parse paths compute the full frame sizefsfrom an attacker-controlled decodedsizeusing unchecked arithmetic, violating the Arithmetic Safety mandatory rule ("No bare arithmetic in production paths that compute sizes/offsets; usechecked_*and returnErron overflow").Surfaced as a follow-up during #33 / the deep-fuzz revival (the same file whose
5BAAlead-byte panic was fixed in #74). Not yet observed as a live crash — filing proactively because it is a latent rule violation and is more likely reachable onwasm32(a supported target, whereusizeis 32-bit).Locations (
src/core/matter/builder.rs):132(from_qualified_base64):let fs = … (size * 4) + cs;:263(from_qualified_base2):let fs = … (size * 4) + cs;:265(from_qualified_base2):let bfs = (fs * 3).div_ceil(4);size: usizecomes fromdecode_int(soft_str)— i.e. the primitive's soft field, which is untrusted input. Note the adjacent:276already uses.checked_add(ls), so these bare* 4/+ cssites are inconsistent oversights, not a deliberate choice.Impact
For a variable-size code whose soft field encodes a large
size:size * 4panics on overflow → panic-on-untrusted-input (DoS), the same class as the5BAAbug just fixed.size * 4wraps silently → a small/incorrectfs. The subsequentif stream.len() < fscheck then passes against a bogus smallfs, andlet trim = &stream[..fs]slices to the wrong length → silently-wrong parse (the exact "truncated frame that parses as valid" failure the arithmetic-safety rule warns about).saturating_*would be equally wrong here.Reachability
usizeis 32-bit onwasm32-unknown-unknown(a gated build target —flake.nixcesr-wasm). There,size * 4overflows oncesize ≥ 2^30(≈1.07e9), i.e. a soft field of ≥ 5 base64 chars (64^5 = 2^30).size ≥ 2^62(soft field ≥ 11 chars).ss(soft size) among variable-size Matter codes — this determines whether the overflow is reachable today on each target, versus latent. Either way the unchecked arithmetic should be fixed.Suggested fix
Replace the bare arithmetic with
checked_mul/checked_add, returning a typed error on overflow (e.g.ValidationError::IncorrectRawSizeor a newValidationError::SizeOverflowvariant) rather than panicking or wrapping. Mirror the existing.checked_add(ls)pattern at:276. Apply to all three sites (qb64:132, qb2:263/:265).Tests
Err(not panic, not silent wrap). Consider running it under--target wasm32-*or a#[cfg(target_pointer_width = "32")]-aware assertion.matter_from_qb64/matter_from_qb2fuzz corpus once characterized.Provenance
Found by inspection while fixing the
5BAAlead-byte-slice panic (#74) and reviving thedeep-fuzzworkflow (#75). The now-working nightlydeep-fuzzmay independently surface this on a longer run.