Skip to content

fix(reader): harden 7 encodings against malformed adversarial input - #327

Merged
dfa1 merged 2 commits into
mainfrom
security/per-encoding-adversarial-tests
Aug 5, 2026
Merged

fix(reader): harden 7 encodings against malformed adversarial input#327
dfa1 merged 2 commits into
mainfrom
security/per-encoding-adversarial-tests

Conversation

@dfa1

@dfa1 dfa1 commented Aug 5, 2026

Copy link
Copy Markdown
Owner

Summary

Part of TODO.md's "Per-encoding adversarial tests" security item (CLAUDE.md §Security contract: a malformed file must always throw VortexException, never a raw JDK exception).

  • Centralizes buffer/child-index bounds checks in DecodeContext.
  • Hardens VarBin, Dict, Bitpacked, ALP, Sparse, Chunked, Struct decoders (plus VarBinArray's lazy accessors and SparseArrays.walkPatches) against crafted metadata that decodes but disagrees with the buffer payload — corrupted offsets, out-of-range indices/exponents/patch counts, zero-byte children, non-monotonic offsets, unsorted patch indices, and dtype/encoding mismatches that previously leaked ArrayIndexOutOfBoundsException, IndexOutOfBoundsException, NegativeArraySizeException, ArithmeticException, ClassCastException, NullPointerException, or OutOfMemoryError.
  • Fixes an unrelated but adjacent gap in PrimitiveEncodingDecoder (raw ClassCastException on a dtype/encoding mismatch), found while hardening Sparse.
  • TODO.md: 7 of 11 per-encoding gotchas closed; RLE/RunEnd, Constant, Zoned, Pco remain (follow-up PR).

No wire-format or public-API changes — every fix is a decode-time validation guard or an accessor-level bounds check.

Test plan

  • ./mvnw test -pl reader -am — reader/core/fsst unit tests green
  • ./mvnw verify -pl reader -am -DskipITs — includes checkstyle, green
  • ./mvnw verify (full 16-module reactor incl. failsafe integration tests against Rust-written files) — green
  • New/updated tests assert VortexException for every hardened case, added as @Nested classes in each encoding's existing decoder test file

🤖 Generated with Claude Code

@dfa1
dfa1 force-pushed the security/per-encoding-adversarial-tests branch from 6aef0de to 78e32b7 Compare August 5, 2026 19:50
dfa1 added 2 commits August 5, 2026 21:54
…gainst malformed input

Part of TODO.md's "Per-encoding adversarial tests" security item (CLAUDE.md
§Security contract): a malformed file must always throw VortexException,
never a raw JDK exception.

- DecodeContext.buffer(i)/decodeChild(i): centralize bounds checks on both
  the position and the segment/child index it holds.
- VarBinArray (OffsetMode/DictMode/ViewMode): checkedLength() guards
  NegativeArraySizeException/IndexOutOfBoundsException from corrupted
  offsets; forEachByteLength paths get boundary catch-and-wrap without
  per-element branches.
- DictEncodingDecoder: expand() guards the values-pool index and
  empty-child division by zero; legacy-dict path routed through the
  hardened buffer accessor instead of raw array indexing.
- BitpackedEncodingDecoder: bit_width/offset range-checked upfront;
  packed-buffer-too-short wrapped at the unpack call site.
- AlpEncodingDecoder: exponent table indices and patch indices
  range-checked; empty encoded/patch children guarded.
- SparseEncodingDecoder: patch count bounded by row count; empty patch
  children rejected; patch-value casts checked instead of raw; missing
  patches metadata rejected; varbin merge loop boundary-wrapped.
- SparseArrays.walkPatches: rejects non-ascending patch indices, which
  previously desynced the fill/patch cursor and over-emitted callbacks
  past the array's own length.
- ChunkedEncodingDecoder: chunk offsets validated non-negative,
  non-decreasing, and spanning exactly rowCount; zero chunks with
  nonzero rowCount rejected uniformly across dtypes.
- StructEncodingDecoder: added test-only coverage (guards already existed
  at the DType/PostscriptParser layer).
- PrimitiveEncodingDecoder: reject a non-primitive requested dtype instead
  of an unchecked cast (a child can be decoded under a dtype not its own).

TODO.md: mark these encodings done, seven of eleven per-encoding gotchas
now closed.
@dfa1
dfa1 force-pushed the security/per-encoding-adversarial-tests branch from 78e32b7 to 3e587d9 Compare August 5, 2026 19:54
@dfa1
dfa1 merged commit e05a92b into main Aug 5, 2026
6 checks passed
@dfa1
dfa1 deleted the security/per-encoding-adversarial-tests branch August 5, 2026 20:08
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