This is an automated email from the ASF dual-hosted git repository. bodewig 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 5836405 COMPRESS-490 throw IOException for certain malformed archives 5836405 is described below commit 58364058bd2903ebd1568f6df22161e142a0dc86 Author: Stefan Bodewig <bode...@apache.org> AuthorDate: Thu Aug 8 18:28:41 2019 +0200 COMPRESS-490 throw IOException for certain malformed archives --- src/changes/changes.xml | 4 +++ .../lz4/BlockLZ4CompressorInputStream.java | 12 ++++++++- .../AbstractLZ77CompressorInputStream.java | 28 ++++++++++++++++++- .../snappy/SnappyCompressorInputStream.java | 31 +++++++++++++++++++--- 4 files changed, 70 insertions(+), 5 deletions(-) diff --git a/src/changes/changes.xml b/src/changes/changes.xml index cd51c61..b41a411 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -92,6 +92,10 @@ The <action> type attribute can be add,update,fix,remove. gathered output in the same order they have been added. Github Pull Requests #78 and #79. </action> + <action type="fix" date="2019-08-08" issue="COMPRESS-490"> + Throw IOException rather than RuntimeExceptions for certain + malformed LZ4 or Snappy inputs. + </action> </release> <release version="1.18" date="2018-08-16" description="Release 1.18"> diff --git a/src/main/java/org/apache/commons/compress/compressors/lz4/BlockLZ4CompressorInputStream.java b/src/main/java/org/apache/commons/compress/compressors/lz4/BlockLZ4CompressorInputStream.java index a52dc60..493ec4e 100644 --- a/src/main/java/org/apache/commons/compress/compressors/lz4/BlockLZ4CompressorInputStream.java +++ b/src/main/java/org/apache/commons/compress/compressors/lz4/BlockLZ4CompressorInputStream.java @@ -100,6 +100,9 @@ public class BlockLZ4CompressorInputStream extends AbstractLZ77CompressorInputSt if (literalSizePart == BACK_REFERENCE_SIZE_MASK) { literalSizePart += readSizeBytes(); } + if (literalSizePart < 0) { + throw new IOException("illegal block with a negative literal size found"); + } startLiteral(literalSizePart); state = State.IN_LITERAL; } @@ -136,7 +139,14 @@ public class BlockLZ4CompressorInputStream extends AbstractLZ77CompressorInputSt backReferenceSize += readSizeBytes(); } // minimal match length 4 is encoded as 0 - startBackReference(backReferenceOffset, backReferenceSize + 4); + if (backReferenceSize < 0) { + throw new IOException("illegal block with a negative match length found"); + } + try { + startBackReference(backReferenceOffset, backReferenceSize + 4); + } catch (IllegalArgumentException ex) { + throw new IOException("illegal block with bad offset found", ex); + } state = State.IN_BACK_REFERENCE; return true; } diff --git a/src/main/java/org/apache/commons/compress/compressors/lz77support/AbstractLZ77CompressorInputStream.java b/src/main/java/org/apache/commons/compress/compressors/lz77support/AbstractLZ77CompressorInputStream.java index 31196ef..ed0cb3c 100644 --- a/src/main/java/org/apache/commons/compress/compressors/lz77support/AbstractLZ77CompressorInputStream.java +++ b/src/main/java/org/apache/commons/compress/compressors/lz77support/AbstractLZ77CompressorInputStream.java @@ -130,9 +130,13 @@ public abstract class AbstractLZ77CompressorInputStream extends CompressorInputS * Size of the window kept for back-references, must be bigger than the biggest offset expected. * * @throws IOException if reading fails + * @throws IllegalArgumentException if windowSize is not bigger than 0 */ public AbstractLZ77CompressorInputStream(final InputStream is, int windowSize) throws IOException { this.in = new CountingInputStream(is); + if (windowSize <= 0) { + throw new IllegalArgumentException("windowSize must be bigger than 0"); + } this.windowSize = windowSize; buf = new byte[3 * windowSize]; writeIndex = readIndex = 0; @@ -201,8 +205,12 @@ public abstract class AbstractLZ77CompressorInputStream extends CompressorInputS * Used by subclasses to signal the next block contains the given * amount of literal data. * @param length the length of the block + * @throws IllegalArgumentException if length is negative */ protected final void startLiteral(long length) { + if (length < 0) { + throw new IllegalArgumentException("length must not be negative"); + } bytesRemaining = length; } @@ -224,6 +232,10 @@ public abstract class AbstractLZ77CompressorInputStream extends CompressorInputS * @throws IOException if the underlying stream throws or signals * an EOF before the amount of data promised for the block have * been read + * @throws NullPointerException if <code>b</code> is null + * @throws IndexOutOfBoundsException if <code>off</code> is + * negative, <code>len</code> is negative, or <code>len</code> is + * greater than <code>b.length - off</code> */ protected final int readLiteral(final byte[] b, final int off, final int len) throws IOException { final int avail = available(); @@ -234,7 +246,7 @@ public abstract class AbstractLZ77CompressorInputStream extends CompressorInputS } private void tryToReadLiteral(int bytesToRead) throws IOException { - // min of "what is still inside the literal", "what does the user want" and "how muc can fit into the buffer" + // min of "what is still inside the literal", "what does the user want" and "how much can fit into the buffer" final int reallyTryToRead = Math.min((int) Math.min(bytesToRead, bytesRemaining), buf.length - writeIndex); final int bytesRead = reallyTryToRead > 0 @@ -271,8 +283,18 @@ public abstract class AbstractLZ77CompressorInputStream extends CompressorInputS * Used by subclasses to signal the next block contains a back-reference with the given coordinates. * @param offset the offset of the back-reference * @param length the length of the back-reference + * @throws IllegalArgumentException if offset not bigger than 0 or + * bigger than the number of bytes available for back-references + * or if length is negative */ protected final void startBackReference(int offset, long length) { + if (offset <= 0 || offset > writeIndex) { + throw new IllegalArgumentException("offset must be bigger than 0 but not bigger than the number" + + " of bytes available for back-references"); + } + if (length < 0) { + throw new IllegalArgumentException("length must not be negative"); + } backReferenceOffset = offset; bytesRemaining = length; } @@ -284,6 +306,10 @@ public abstract class AbstractLZ77CompressorInputStream extends CompressorInputS * @param len maximum amount of data to read * @return number of bytes read, may be 0. Will never return -1 as * EOF-detection is the responsibility of the subclass + * @throws NullPointerException if <code>b</code> is null + * @throws IndexOutOfBoundsException if <code>off</code> is + * negative, <code>len</code> is negative, or <code>len</code> is + * greater than <code>b.length - off</code> */ protected final int readBackReference(final byte[] b, final int off, final int len) { final int avail = available(); diff --git a/src/main/java/org/apache/commons/compress/compressors/snappy/SnappyCompressorInputStream.java b/src/main/java/org/apache/commons/compress/compressors/snappy/SnappyCompressorInputStream.java index 4650cb8..4657ac8 100644 --- a/src/main/java/org/apache/commons/compress/compressors/snappy/SnappyCompressorInputStream.java +++ b/src/main/java/org/apache/commons/compress/compressors/snappy/SnappyCompressorInputStream.java @@ -78,6 +78,7 @@ public class SnappyCompressorInputStream extends AbstractLZ77CompressorInputStre * The block size used in compression * * @throws IOException if reading fails + * @throws IllegalArgumentException if blockSize is not bigger than 0 */ public SnappyCompressorInputStream(final InputStream is, final int blockSize) throws IOException { @@ -135,6 +136,9 @@ public class SnappyCompressorInputStream extends AbstractLZ77CompressorInputStre case 0x00: length = readLiteralLength(b); + if (length < 0) { + throw new IOException("illegal block with a negative literal size found"); + } uncompressedBytesRemaining -= length; startLiteral(length); state = State.IN_LITERAL; @@ -152,6 +156,9 @@ public class SnappyCompressorInputStream extends AbstractLZ77CompressorInputStre */ length = 4 + ((b >> 2) & 0x07); + if (length < 0) { + throw new IOException("illegal block with a negative match length found"); + } uncompressedBytesRemaining -= length; offset = (b & 0xE0) << 3; b = readOneByte(); @@ -160,7 +167,11 @@ public class SnappyCompressorInputStream extends AbstractLZ77CompressorInputStre } offset |= b; - startBackReference(offset, length); + try { + startBackReference(offset, length); + } catch (IllegalArgumentException ex) { + throw new IOException("illegal block with bad offset found", ex); + } state = State.IN_BACK_REFERENCE; break; @@ -175,11 +186,18 @@ public class SnappyCompressorInputStream extends AbstractLZ77CompressorInputStre */ length = (b >> 2) + 1; + if (length < 0) { + throw new IOException("illegal block with a negative match length found"); + } uncompressedBytesRemaining -= length; offset = (int) ByteUtils.fromLittleEndian(supplier, 2); - startBackReference(offset, length); + try { + startBackReference(offset, length); + } catch (IllegalArgumentException ex) { + throw new IOException("illegal block with bad offset found", ex); + } state = State.IN_BACK_REFERENCE; break; @@ -193,11 +211,18 @@ public class SnappyCompressorInputStream extends AbstractLZ77CompressorInputStre */ length = (b >> 2) + 1; + if (length < 0) { + throw new IOException("illegal block with a negative match length found"); + } uncompressedBytesRemaining -= length; offset = (int) ByteUtils.fromLittleEndian(supplier, 4) & 0x7fffffff; - startBackReference(offset, length); + try { + startBackReference(offset, length); + } catch (IllegalArgumentException ex) { + throw new IOException("illegal block with bad offset found", ex); + } state = State.IN_BACK_REFERENCE; break; default: