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 1e9612ea4 Throw CompressorException for invalid huffman data (#798)
1e9612ea4 is described below

commit 1e9612ea4774e567c3e2d73048646bf0e59fa840
Author: Fredrik Kjellberg <[email protected]>
AuthorDate: Mon Aug 10 21:36:51 2026 +0200

    Throw CompressorException for invalid huffman data (#798)
---
 .../bzip2/BZip2CompressorInputStream.java          |  8 +---
 .../compressors/deflate64/Deflate64Decoder.java    |  2 +-
 .../commons/compress/huffman/HuffmanDecoder.java   | 20 ++++++----
 .../compress/huffman/HuffmanDecoderTest.java       | 46 ++++++++++++++++++++--
 4 files changed, 58 insertions(+), 18 deletions(-)

diff --git 
a/src/main/java/org/apache/commons/compress/compressors/bzip2/BZip2CompressorInputStream.java
 
b/src/main/java/org/apache/commons/compress/compressors/bzip2/BZip2CompressorInputStream.java
index 08be86dd9..cb928d697 100644
--- 
a/src/main/java/org/apache/commons/compress/compressors/bzip2/BZip2CompressorInputStream.java
+++ 
b/src/main/java/org/apache/commons/compress/compressors/bzip2/BZip2CompressorInputStream.java
@@ -275,12 +275,8 @@ static void recvDecodingTables(final BitInputStream bin, 
final Data dataShadow)
                 }
                 codeLengths[i] = curr;
             }
-            try {
-                // Same limits as in the reference C implementation of bzip2
-                dataShadow.huffmanDecoders[t] = new 
HuffmanDecoder(codeLengths, 1, MAX_CODE_LEN);
-            } catch (final IllegalArgumentException e) {
-                throw new CompressorException("Invalid Huffman data: " + 
e.getMessage(), e);
-            }
+            // Same limits as in the reference C implementation of bzip2
+            dataShadow.huffmanDecoders[t] = new HuffmanDecoder(codeLengths, 1, 
MAX_CODE_LEN);
         }
         dataShadow.huffmanDecodersCount = nGroups;
     }
diff --git 
a/src/main/java/org/apache/commons/compress/compressors/deflate64/Deflate64Decoder.java
 
b/src/main/java/org/apache/commons/compress/compressors/deflate64/Deflate64Decoder.java
index d451da937..ee64475b3 100644
--- 
a/src/main/java/org/apache/commons/compress/compressors/deflate64/Deflate64Decoder.java
+++ 
b/src/main/java/org/apache/commons/compress/compressors/deflate64/Deflate64Decoder.java
@@ -111,7 +111,7 @@ private final class HuffmanCodes extends DecoderState {
         private byte[] runBuffer = ArrayUtils.EMPTY_BYTE_ARRAY;
         private int runBufferLength;
 
-        HuffmanCodes(final Deflate64State state, final int[] lengths, final 
int[] distance) {
+        HuffmanCodes(final Deflate64State state, final int[] lengths, final 
int[] distance) throws CompressorException {
             this.state = state;
             symbolDecoder = new HuffmanDecoder(lengths);
             distanceDecoder = new HuffmanDecoder(distance);
diff --git 
a/src/main/java/org/apache/commons/compress/huffman/HuffmanDecoder.java 
b/src/main/java/org/apache/commons/compress/huffman/HuffmanDecoder.java
index 718ab6c2b..ffd022c2a 100644
--- a/src/main/java/org/apache/commons/compress/huffman/HuffmanDecoder.java
+++ b/src/main/java/org/apache/commons/compress/huffman/HuffmanDecoder.java
@@ -145,9 +145,9 @@ private static int readBitsFully(final BitInputStream in, 
final int numBits) thr
      *
      * @param codeLengths code length per symbol; {@code 0} means the symbol 
is not used; not {@code null}.
      * @throws NullPointerException     if {@code codeLengths} is {@code null}.
-     * @throws IllegalArgumentException if any code length is out of range [0, 
30].
+     * @throws CompressorException      if {@code codeLengths} size is out of 
range or if any code length is out of range
      */
-    public HuffmanDecoder(final int[] codeLengths) {
+    public HuffmanDecoder(final int[] codeLengths) throws CompressorException {
         this(codeLengths, 0, MAX_SUPPORTED_CODE_LENGTH);
     }
 
@@ -162,16 +162,20 @@ public HuffmanDecoder(final int[] codeLengths) {
      * @param minCodeLength  minimum allowed code length present in {@code 
codeLengths}.
      * @param maxCodeLength  maximum allowed code length present in {@code 
codeLengths}.
      * @throws NullPointerException     if {@code codeLengths} is {@code null}.
-     * @throws IllegalArgumentException if {@code codeLengths} size is out of 
range, if any code length is out of range or if {@code maxCodeLength} exceeds 
the
-     *                                  implementation limit (30).
+     * @throws IllegalArgumentException if {@code maxCodeLength} exceeds the 
implementation limit (30) or if {@code minCodeLength}
+     *                                  is not in the range 0-{@code 
maxCodeLength}].
+     * @throws CompressorException      if {@code codeLengths} size is out of 
range or if any code length is out of range
      */
-    public HuffmanDecoder(final int[] codeLengths, final int minCodeLength, 
final int maxCodeLength) throws IllegalArgumentException {
+    public HuffmanDecoder(final int[] codeLengths, final int minCodeLength, 
final int maxCodeLength) throws CompressorException {
         Objects.requireNonNull(codeLengths, "codeLengths");
         if (maxCodeLength > MAX_SUPPORTED_CODE_LENGTH) {
             throw new IllegalArgumentException(String.format("maxCodeLength 
(%d) exceeds supported limit (%d)", maxCodeLength, MAX_SUPPORTED_CODE_LENGTH));
         }
-        if (codeLengths.length <= 0) {
-            throw new IllegalArgumentException(String.format("codeLengthSize 
must be > 0; was %d", codeLengths.length));
+        if (minCodeLength < 0 || minCodeLength > maxCodeLength) {
+            throw new IllegalArgumentException(String.format("minCodeLength 
(%d) not within range [0, %d)", minCodeLength, maxCodeLength));
+        }
+        if (codeLengths.length == 0) {
+            throw new CompressorException(String.format("Empty code length 
list"));
         }
         // Validate and find min/max lengths
         int min = maxCodeLength;
@@ -179,7 +183,7 @@ public HuffmanDecoder(final int[] codeLengths, final int 
minCodeLength, final in
         for (int i = 0; i < codeLengths.length; i++) {
             final int len = codeLengths[i];
             if (len < minCodeLength || len > maxCodeLength) {
-                throw new IllegalArgumentException(
+                throw new CompressorException(
                         String.format("Invalid code length at symbol %d: %d 
(expected in [%d, %d])", i, len, minCodeLength, maxCodeLength));
             }
             if (len == 0) {
diff --git 
a/src/test/java/org/apache/commons/compress/huffman/HuffmanDecoderTest.java 
b/src/test/java/org/apache/commons/compress/huffman/HuffmanDecoderTest.java
index dbb697ce4..d9d70cdc3 100644
--- a/src/test/java/org/apache/commons/compress/huffman/HuffmanDecoderTest.java
+++ b/src/test/java/org/apache/commons/compress/huffman/HuffmanDecoderTest.java
@@ -154,11 +154,51 @@ void testInvalidBitstream() throws Exception {
         assertEquals("Invalid Huffman code: 62", e.getMessage());
     }
 
+    @Test
+    void testMaxSupportedCodeLengthExceeded() {
+        final int[] codeLengths = new int[] {1, 1};
+        final IllegalArgumentException e = 
assertThrows(IllegalArgumentException.class, () -> new 
HuffmanDecoder(codeLengths, 0, 31),
+                "Expected IllegalArgumentException for max code length 
exceeding supported limit");
+        assertEquals("maxCodeLength (31) exceeds supported limit (30)", 
e.getMessage());
+    }
+
+    @Test
+    void testMinCodeLengthBelowSupportedLimit() {
+        final int[] codeLengths = new int[] {1, 1};
+        final IllegalArgumentException e = 
assertThrows(IllegalArgumentException.class, () -> new 
HuffmanDecoder(codeLengths, -1, 30),
+                "Expected IllegalArgumentException for min code length below 
supported limit");
+        assertEquals("minCodeLength (-1) not within range [0, 30)", 
e.getMessage());
+    }
+
+    @Test
+    void testMinCodeLengthExceedingMaxCodeLength() {
+        final int[] codeLengths = new int[] {1, 1};
+        final IllegalArgumentException e = 
assertThrows(IllegalArgumentException.class, () -> new 
HuffmanDecoder(codeLengths, 17, 16),
+                "Expected IllegalArgumentException for min code length 
exceeding max code length");
+        assertEquals("minCodeLength (17) not within range [0, 16)", 
e.getMessage());
+    }
+
     @Test
     void testNoCodeLengths() throws Exception {
-        final IllegalArgumentException e = 
assertThrows(IllegalArgumentException.class, () -> new HuffmanDecoder(new 
int[0]),
-                "Expected IllegalArgumentException for empty code length 
list");
-        assertEquals("codeLengthSize must be > 0; was 0", e.getMessage());
+        final CompressorException e = assertThrows(CompressorException.class, 
() -> new HuffmanDecoder(new int[0]),
+                "Expected CompressorException for empty code length list");
+        assertEquals("Empty code length list", e.getMessage());
+    }
+
+    @Test
+    void testCodeLengthExceedingMaxCodeLength() throws Exception {
+        final int[] codeLengths = new int[] {4, 2, 3, 0, 5, 5, 1};
+        final CompressorException e = assertThrows(CompressorException.class, 
() -> new HuffmanDecoder(codeLengths, 0, 4),
+                "Expected CompressorException for code length exceeding max 
code length");
+        assertEquals("Invalid code length at symbol 4: 5 (expected in [0, 
4])", e.getMessage());
+    }
+
+    @Test
+    void testCodeLengthBelowMinCodeLength() throws Exception {
+        final int[] codeLengths = new int[] {4, 2, 3, 0, 5, 5, 1};
+        final CompressorException e = assertThrows(CompressorException.class, 
() -> new HuffmanDecoder(codeLengths, 1, 5),
+                "Expected CompressorException for code length below min code 
length");
+        assertEquals("Invalid code length at symbol 3: 0 (expected in [1, 
5])", e.getMessage());
     }
 
     @Test

Reply via email to