Skip to content

Unchecked size arithmetic in MatterBuilder variable-size parse (size * 4 overflow, wasm32/32-bit) #76

Description

@joeldsouzax

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingrustPull requests that update rust code

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions