You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2026/07/14 17:05:55 UTC
(commons-compress) branch master updated: Reject invalid literal/length and distance codes in Deflate64 decoder (#785)
This is an automated email from the ASF dual-hosted git repository.
garydgregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-compress.git
The following commit(s) were added to refs/heads/master by this push:
new 7e29fa061 Reject invalid literal/length and distance codes in Deflate64 decoder (#785)
7e29fa061 is described below
commit 7e29fa061cb05243e6cacba731196aebaa037146
Author: KALI 834X <ka...@gmail.com>
AuthorDate: Tue Jul 14 22:35:49 2026 +0530
Reject invalid literal/length and distance codes in Deflate64 decoder (#785)
* reject invalid literal/length and distance codes in Deflate64 decoder
* add test for invalid Deflate64 distance code
---
.../compressors/deflate64/HuffmanDecoder.java | 10 ++++
.../compressors/deflate64/HuffmanDecoderTest.java | 53 ++++++++++++++++++++++
2 files changed, 63 insertions(+)
diff --git a/src/main/java/org/apache/commons/compress/compressors/deflate64/HuffmanDecoder.java b/src/main/java/org/apache/commons/compress/compressors/deflate64/HuffmanDecoder.java
index f28c7cac7..9ba65bd86 100644
--- a/src/main/java/org/apache/commons/compress/compressors/deflate64/HuffmanDecoder.java
+++ b/src/main/java/org/apache/commons/compress/compressors/deflate64/HuffmanDecoder.java
@@ -30,6 +30,7 @@
import java.nio.ByteOrder;
import java.util.Arrays;
+import org.apache.commons.compress.compressors.CompressorException;
import org.apache.commons.compress.utils.BitInputStream;
import org.apache.commons.compress.utils.ExactMath;
import org.apache.commons.lang3.ArrayFill;
@@ -171,14 +172,23 @@ private int decodeNext(final byte[] b, final int off, final int len) throws IOEx
while (result < len) {
final int symbol = nextSymbol(reader, lengthTree);
if (symbol < 256) {
+ if (symbol < 0) {
+ throw new CompressorException("Invalid Deflate64 literal/length code %,d", symbol);
+ }
b[off + result++] = memory.add((byte) symbol);
} else if (symbol > 256) {
+ if (symbol - 257 >= RUN_LENGTH_TABLE.length) {
+ throw new CompressorException("Invalid Deflate64 literal/length code %,d", symbol);
+ }
final int runMask = RUN_LENGTH_TABLE[symbol - 257];
int run = runMask >>> 5;
final int runXtra = runMask & 0x1F;
run = ExactMath.add(run, readBits(runXtra));
final int distSym = nextSymbol(reader, distanceTree);
+ if (distSym < 0 || distSym >= DISTANCE_TABLE.length) {
+ throw new CompressorException("Invalid Deflate64 distance code %,d", distSym);
+ }
final int distMask = DISTANCE_TABLE[distSym];
int dist = distMask >>> 4;
diff --git a/src/test/java/org/apache/commons/compress/compressors/deflate64/HuffmanDecoderTest.java b/src/test/java/org/apache/commons/compress/compressors/deflate64/HuffmanDecoderTest.java
index bb20912d7..cb05bedda 100644
--- a/src/test/java/org/apache/commons/compress/compressors/deflate64/HuffmanDecoderTest.java
+++ b/src/test/java/org/apache/commons/compress/compressors/deflate64/HuffmanDecoderTest.java
@@ -25,6 +25,7 @@
import java.io.ByteArrayInputStream;
import java.util.Arrays;
+import org.apache.commons.compress.compressors.CompressorException;
import org.junit.jupiter.api.Test;
class HuffmanDecoderTest {
@@ -207,4 +208,56 @@ void testDecodeUncompressedBlockWithInvalidLenNLenValue() throws Exception {
assertEquals("Illegal LEN / NLEN values", e.getMessage());
}
}
+
+ @Test
+ void testDecodeDynamicHuffmanBlockRejectsInvalidDistanceCode() throws Exception {
+ // final block + dynamic huffman with HLIT=1, HDIST=0, HCLEN=14. The code length tree
+ // assigns 1-bit codes to symbols 1 and 18, which encode 1-bit codes for literal/length
+ // symbols 256 and 257 and a single 1-bit code for distance symbol 0. The incomplete
+ // distance tree leaves the 1 branch empty, so the block data - length symbol 257
+ // followed by a 1 bit on the distance side - decodes to distance symbol -1.
+ final byte[] data = {
+ // |--- binary filling ---|76543210
+ 0b00000000000000000000000000001101, // final block + dynamic huffman + HLIT 1
+ 0b11111111111111111111111111000000, // HDIST 0 + low 3 bits of HCLEN 14
+ 0b11111111111111111111111110000001, // high bit of HCLEN + zero lengths for 16, 17 + low bit of length 1 for 18
+ 0b00000000000000000000000000000000, // high bits of length for 18 + zero lengths for 0, 8
+ 0b00000000000000000000000000000000, // zero lengths for 7, 9 + low bits of 6
+ 0b00000000000000000000000000000000, // zero lengths for 6, 10, 5
+ 0b00000000000000000000000000000000, // zero lengths for 11, 4, 12
+ 0b00000000000000000000000000000000, // zero lengths for 3, 13, 2
+ 0b11111111111111111111111110010000, // zero length for 14 + length 1 for symbol 1 + code 18
+ 0b11111111111111111111111111111111, // 7 extra bits (127 -> 138 zero lengths) + code 18
+ 0b00000000000000000000000001101011, // 7 extra bits (107 -> 118 zero lengths) + code 1 (literal 256)
+ 0b00000000000000000000000000001100 // code 1 twice (literal 257, distance 0) + length symbol 257 + invalid distance bit 1
+ };
+ try (HuffmanDecoder decoder = new HuffmanDecoder(new ByteArrayInputStream(data))) {
+ final byte[] result = new byte[100];
+ final CompressorException e = assertThrows(CompressorException.class, () -> {
+ final int len = decoder.decode(result);
+ fail("Should have failed but returned " + len + " entries: " + Arrays.toString(Arrays.copyOf(result, len)));
+ });
+ assertEquals("Invalid Deflate64 distance code -1", e.getMessage());
+ }
+ }
+
+ @Test
+ void testDecodeFixedHuffmanBlockRejectsReservedLiteralLengthCode() throws Exception {
+ // final block + fixed huffman, followed by the 8-bit fixed code 0b11000110 for
+ // literal/length symbol 286. That symbol is a decodable leaf of the fixed tree
+ // but is reserved by RFC 1951, so it has no entry in RUN_LENGTH_TABLE.
+ final byte[] data = {
+ // |--- binary filling ---|76543210
+ 0b00000000000000000000000000011011, // final block + fixed huffman + low 5 bits of code 286
+ 0b00000000000000000000000000000011 // high 3 bits of code 286
+ };
+ try (HuffmanDecoder decoder = new HuffmanDecoder(new ByteArrayInputStream(data))) {
+ final byte[] result = new byte[100];
+ final CompressorException e = assertThrows(CompressorException.class, () -> {
+ final int len = decoder.decode(result);
+ fail("Should have failed but returned " + len + " entries: " + Arrays.toString(Arrays.copyOf(result, len)));
+ });
+ assertEquals("Invalid Deflate64 literal/length code 286", e.getMessage());
+ }
+ }
}