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