Skip to content

Commit 707fca2

Browse files
dfa1claude
andcommitted
fix(reader): reject a negative delta element count on its own
Self-review follow-up on #338. `rowCount > deltasLen - offset` was the only thing standing between a negative `deltas_len` and the chunk loop, and the subtraction wraps: `Long.MIN_VALUE - 1` is positive, so the window check passed, the chunk range came out empty, and decode handed back a zero-filled array of the requested length. A malformed file answered instead of rejected — no raw exception, so ADR 0003 held, but the file is still garbage and should say so. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent 2ee7d87 commit 707fca2

2 files changed

Lines changed: 7 additions & 2 deletions

File tree

reader/src/main/java/io/github/dfa1/vortex/reader/decode/DeltaEncodingDecoder.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,11 @@ public Array decode(DecodeContext ctx) {
6565
// it — NegativeArraySizeException or OutOfMemoryError, neither a VortexException
6666
// (ADR 0003). Checked before any child decode, so a bogus length never drives an
6767
// allocation.
68-
if (offset < 0 || rowCount > deltasLen - offset) {
68+
// `deltasLen < 0` is checked on its own rather than left to the subtraction: a
69+
// sufficiently negative one makes `deltasLen - offset` wrap positive, which passes a
70+
// window check it should fail, and the chunk loop then simply does nothing and hands
71+
// back a zero-filled array — a malformed file answered instead of rejected.
72+
if (offset < 0 || deltasLen < 0 || rowCount > deltasLen - offset) {
6973
throw new VortexException(EncodingId.FASTLANES_DELTA,
7074
"row window [" + offset + ", " + (offset + rowCount) + ") outside the "
7175
+ deltasLen + " delta element(s)");

reader/src/test/java/io/github/dfa1/vortex/reader/decode/DeltaEncodingDecoderTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,8 @@ void decode_constantChildren_broadcastsAcrossChunk() {
8989
"1024, 0, 2000", // more rows than the chunks reconstruct
9090
"1024, 900, 200", // window starts inside the chunk but runs off its end
9191
"1024, -1, 4", // negative start position
92-
"-1024, 0, 4" // negative element count
92+
"-1024, 0, 4", // negative element count
93+
"-9223372036854775808, 1, 4" // negative enough that `deltasLen - offset` wraps positive
9394
})
9495
void decode_windowOutsideDeltas_throws(long deltasLen, int offset, long rowCount) {
9596
// Given — metadata whose row window is not covered by `deltasLen` elements

0 commit comments

Comments
 (0)