Skip to content

fix: P3D and PBO reads of garbage data - #47

Merged
TheWillard merged 2 commits into
mainfrom
fix/p3d-garbage-input
Nov 6, 2026
Merged

TheWillard merged 2 commits into
mainfrom
fix/p3d-garbage-input

Conversation

@TheWillard

@TheWillard TheWillard commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

Obfuscated PBOs carry decoy files: a valid ODOL or PAA header followed by garbage. Reading them panicked or aborted the process on allocations of up to 17 GB. Now they fail with an error.

  • LZO: lzokay-native 0.2.1's bounded decompression. ODOL arrays use decompress_bounded with the expected size, PAA mipmaps (DXT1, DXT5) decompress_all_bounded, limited to the mipmap's 4x4 blocks (8 bytes each for DXT1, 16 for DXT5). lzokay 0.1 panicked on matches reaching before the start of the output and allocated the expected size up front.
  • decompress_lzss, raw ODOL array reads and array capacities grow with the data instead of trusting the count. Default-filled condensed arrays are capped at 2^24 elements (the largest LOD seen has 669,004 vertices). Element count times size is checked for overflow.
  • AnimationRTWeight: more than 4 bone weights is an error, it indexed past its 8 bytes.
  • PBO entries: compressed data with bytes after the LZSS stream is kept as stored, like other undecodable entries, instead of panicking.
  • Tests: corrupted and truncated test models (fail on main with the lzokay panic), a PBO with garbage compressed entries (fails on main at the assert_eq!).

Checked against Arma 3 (6,682 models, 20,817 textures) and 150 mods (~28,000 models, ~320,000 textures):

  • The same models and textures load as before, with the same mipmap bytes. ODOL::from_path is as fast as before.
  • All 5,496 decoy models (3CB Factions, Pr.1204, Tiger, Stargate, Blastcore) fail with an error: no panics, no allocation over 512 MB.
  • Decoy textures: 442 that panicked and 14 that decoded to more data than the texture holds now fail with an error.

Independent of #46.

@codecov

codecov Bot commented Nov 5, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 84.90566% with 8 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...ma-file-formats/src/real_virtuality/binrw_utils.rs 83.78% 6 Missing ⚠️
...rma-file-formats/src/real_virtuality/paa/mipmap.rs 81.81% 2 Missing ⚠️
Files with missing lines Coverage Δ
arma-file-formats/src/core/lzss.rs 90.78% <100.00%> (+0.46%) ⬆️
arma-file-formats/src/real_virtuality/p3d/lod.rs 98.38% <ø> (ø)
arma-file-formats/src/real_virtuality/pbo/entry.rs 73.80% <100.00%> (-1.75%) ⬇️
...rma-file-formats/src/real_virtuality/paa/mipmap.rs 86.93% <81.81%> (+0.02%) ⬆️
...ma-file-formats/src/real_virtuality/binrw_utils.rs 96.10% <83.78%> (+2.09%) ⬆️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Obfuscated PBOs carry decoy files: a valid ODOL header followed by
garbage. Reading them panicked or aborted the process on allocations of
up to 17 GB. Now they fail with an error.

- core::decompress_lzo: LZO1X decompression with bounds checks, replacing
  lzokay_native::decompress for ODOL arrays. Its output only grows as
  input is read and stops at the expected size. lzokay panicked on
  matches reaching before the start of the output and allocated the
  expected size up front. Gives the same bytes and end position as
  lzokay for every LZO block of Arma 3 and ~28,000 mod models.
- decompress_lzss, raw ODOL array reads and array capacities grow with
  the data instead of trusting the count. Default-filled condensed
  arrays are capped at 2^24 elements (the largest LOD seen has 669,004
  vertices). Element count times size is checked for overflow.
- AnimationRTWeight: more than 4 bone weights is an error, it indexed
  past its 8 bytes.
- PBO entries: compressed data with bytes after the LZSS stream is kept
  as stored, like other undecodable entries, instead of panicking.
- Tests: corrupted and truncated test models, a PBO with garbage
  compressed entries.
lzokay-native 0.2 has bounded decompression that returns errors instead
of panicking, so the crate's own LZO1X decoder is no longer needed.

- ODOL arrays: lzokay_native::decompress_bounded with the expected
  size. core::decompress_lzo and LzoError are removed.
- PAA mipmaps (DXT1, DXT5): decompress_all_bounded, limited to the
  mipmap's 4x4 blocks (8 bytes each for DXT1, 16 for DXT5). Garbage
  mipmaps panicked or decoded to more data than the texture holds.
- Requires 0.2.1: 0.2.0 made ODOL::from_path 15-30% slower.

Arma 3 and ~28,000 mod models and ~320,000 mod textures read the same
as before, with the same mipmap bytes. 442 decoy textures that panicked
and 14 that decoded to oversized garbage now fail with an error.
@TheWillard
TheWillard force-pushed the fix/p3d-garbage-input branch from d78ac9c to c0edc09 Compare November 6, 2026 20:24
@TheWillard
TheWillard merged commit 2414a68 into main Nov 6, 2026
1 of 2 checks passed
@TheWillard
TheWillard deleted the fix/p3d-garbage-input branch November 6, 2026 20:55
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.

1 participant