Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions lucene/CHANGES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,10 @@ Bug Fixes
which made the join set degenerate to the whole graph on hub-and-spoke shaped HNSW graphs.
(Mayya Sharipova)

* GITHUB#16478: Reject LZ4 match offsets that point before the start of the output in
LZ4#decompress. Corrupt input previously threw ArrayIndexOutOfBoundsException instead of
a checked IOException. (Serhiy Bzhezytskyy)

* GITHUB#16350: Disable bulk-scoring in monitor queries. (Alan Woodward)

* GITHUB#16378: Accumulate join Total/Avg scores in double precision in
Expand Down
5 changes: 5 additions & 0 deletions lucene/core/src/java/org/apache/lucene/util/compress/LZ4.java
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,11 @@ public static int decompress(DataInput compressed, int decompressedLen, byte[] d
if (matchDec == 0) {
throw new IOException("offset 0 is invalid");
}
if (matchDec > dOff) {
// The match would reference bytes before dest[0], which this call never wrote.
throw new IOException(
"match offset " + matchDec + " is invalid, only " + dOff + " bytes are available");
}

int matchLen = token & 0x0F;
if (matchLen == 0x0F) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,70 @@ public void testDecompressOffset0() {
() -> LZ4.decompress(new ByteArrayDataInput(input), output.length, output, 0));
assertEquals("offset 0 is invalid", e.getMessage());
}

public void testDecompressOffsetBeyondOutput() {
// A match offset must not point before the start of the output, otherwise the match would
// reference bytes that this call never decompressed.
byte[] input =
new byte[] {
// token: 0 literals, match length 4 (MIN_MATCH)
0x0,
// offset 8, which is greater than the 0 bytes decompressed so far
8,
0,
// last literals
// token
7 << 4,
// literals
0,
0,
0,
0,
0,
0,
0
};

byte[] output = new byte[18];

var e =
assertThrows(
IOException.class,
() -> LZ4.decompress(new ByteArrayDataInput(input), output.length, output, 0));
assertEquals("match offset 8 is invalid, only 0 bytes are available", e.getMessage());
}

public void testDecompressOffsetBeyondOutputWithDictionary() {
// With a preset dictionary the match offset may reach into the dictionary that the caller
// placed in dest[dOff-dictLen:dOff], so the bound is dOff, not the number of bytes that this
// call decompressed.
byte[] input =
new byte[] {
// token: 0 literals, match length 4 (MIN_MATCH)
0x0,
// offset 5, one byte past the 4-byte dictionary
5,
0,
// last literals
// token
7 << 4,
// literals
0,
0,
0,
0,
0,
0,
0
};

byte[] output = new byte[22];
final int dictLen = 4;

var e =
assertThrows(
IOException.class,
() -> LZ4.decompress(new ByteArrayDataInput(input), 18, output, dictLen));
assertEquals("match offset 5 is invalid, only 4 bytes are available", e.getMessage());
}
}
Loading