Skip to content

usdc: return errors instead of aborting on damaged crate files - #138

Merged
mxpv merged 1 commit into
mxpv:mainfrom
raspao-studio:usdc-hardening
Oct 1, 2026
Merged

mxpv merged 1 commit into
mxpv:mainfrom
raspao-studio:usdc-hardening

Conversation

@humships

@humships humships commented Sep 30, 2026 •

Copy link
Copy Markdown

While adding USD import to a macOS app that builds with panic = "abort", we fuzzed the usdc reader with mutated copies of valid files. Several inputs crash the process instead of returning an error:

  • allocations sized by an unchecked count from the file: read_compressed (compressed_size and estimated_count), the paths table, read_vec. One example: memory allocation of 7341785157331845235 bytes failed
  • todo!() for pre-0.4 crate versions and for chunked LZ4
  • unchecked slicing in decode_ints, and integer overflow in encoded_buffer_size
  • data-dependent debug_assert!s, which panic in debug builds and pass silently in release

Changes, in usdc/reader.rs and usdc/coding.rs only:

  • counts are checked against the file length before allocating
  • LZ4 output is capped at LZ4's maximum expansion (255x) of the compressed input and at 4 GiB, instead of trusting the estimate stored in the file
  • read_vec reads the bytes before allocating the vector
  • preallocation from file counts is capped at 1024 elements
  • lookup-table indexing and the decode_ints slice are checked
  • the todo!() paths return ReadError::unsupported
  • data-dependent debug assertions become corrupt! errors
  • encoded_buffer_size uses saturating arithmetic

tests/usdc_malformed.rs mutates five fixtures deterministically (2,000 rounds each; OPENUSD_USDC_MUTATIONS raises it) and decodes every field. On main it aborts with an allocation failure; with this change it passes. The CI commands from ci.yml pass locally (check, clippy with all features, fmt, doc, and the full test suite with vendor/usd-wg-assets initialized). It is one commit, written as a release-notes entry per CONTRIBUTING.md.

Downstream, about a million mutations of Blender- and Apple-authored usdc and usdz files went through a Stage without a crash.

Happy to split this up or change the approach.

A damaged or hostile .usdc could make the crate reader allocate whatever
a count in the file asked for (a few bytes of input requested exabytes),
index past tables, reach todo!() for old versions, or overflow while
sizing buffers. Each of these aborts an application that builds with
panic = "abort", and a failed allocation aborts any application.

Counts are now checked against the file length, LZ4 output is capped at
its maximum expansion of the compressed input and at 4 GiB, vectors are
read before they are allocated, preallocation from file counts is
capped, table lookups and slicing are checked, unsupported old versions
return an error, and data-dependent debug assertions are errors in every
build. tests/usdc_malformed.rs mutates five fixtures deterministically
and decodes every field; before this change it aborted the test process.
humships pushed a commit to raspao-studio/openusd that referenced this pull request Sep 30, 2026
Brings this v0.7.0 branch to the change offered upstream in
mxpv#138 (formatting, a 4 GiB cap on one LZ4-decompressed
block, and tests/usdc_malformed.rs).
@mxpv

mxpv commented Oct 1, 2026

Copy link
Copy Markdown
Owner

Thanks!

@mxpv
mxpv merged commit 9484705 into mxpv:main Oct 1, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants