Merge pull request from GHSA-55g7-9cwv-5qfv

* Validate chunk size to be within a configured maximum

* Add constructors to have max size configurable

* Code cleanup

* Use 512MB for consistency

---------

Co-authored-by: Taro L. Saito <leo@xerial.org>
This commit is contained in:
BD 2023-09-24 04:25:18 +05:30 committed by GitHub
parent 49d700175f
commit 9f8c3cf742
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 60 additions and 3 deletions

View File

@ -1,9 +1,9 @@
package org.xerial.snappy; package org.xerial.snappy;
import java.io.OutputStream;
import org.xerial.snappy.buffer.CachedBufferAllocator; import org.xerial.snappy.buffer.CachedBufferAllocator;
import java.io.OutputStream;
public class SnappyHadoopCompatibleOutputStream extends SnappyOutputStream public class SnappyHadoopCompatibleOutputStream extends SnappyOutputStream
{ {
public SnappyHadoopCompatibleOutputStream(OutputStream out) public SnappyHadoopCompatibleOutputStream(OutputStream out)

View File

@ -36,8 +36,11 @@ import java.io.InputStream;
public class SnappyInputStream public class SnappyInputStream
extends InputStream extends InputStream
{ {
public static final int MAX_CHUNK_SIZE = 512 * 1024 * 1024; // 512 MiB
private boolean finishedReading = false; private boolean finishedReading = false;
protected final InputStream in; protected final InputStream in;
private final int maxChunkSize;
private byte[] compressed; private byte[] compressed;
private byte[] uncompressed; private byte[] uncompressed;
@ -55,6 +58,21 @@ public class SnappyInputStream
public SnappyInputStream(InputStream input) public SnappyInputStream(InputStream input)
throws IOException throws IOException
{ {
this(input, MAX_CHUNK_SIZE);
}
/**
* Create a filter for reading compressed data as a uncompressed stream with provided maximum chunk size
*
* @param input
* @param maxChunkSize
* @throws IOException
*/
public SnappyInputStream(InputStream input, int maxChunkSize)
throws IOException
{
this.maxChunkSize = maxChunkSize;
this.in = input; this.in = input;
readHeader(); readHeader();
} }
@ -422,6 +440,11 @@ public class SnappyInputStream
throw new SnappyError(SnappyErrorCode.INVALID_CHUNK_SIZE, "chunkSize is too big or negative : " + chunkSize); throw new SnappyError(SnappyErrorCode.INVALID_CHUNK_SIZE, "chunkSize is too big or negative : " + chunkSize);
} }
// chunkSize is big
if (chunkSize > maxChunkSize) {
throw new SnappyError(SnappyErrorCode.FAILED_TO_UNCOMPRESS, String.format("Received chunkSize %,d is greater than max configured chunk size %,d", chunkSize, maxChunkSize));
}
// extend the compressed data buffer size // extend the compressed data buffer size
if (compressed == null || chunkSize > compressed.length) { if (compressed == null || chunkSize > compressed.length) {
// chunkSize exceeds limit // chunkSize exceeds limit

View File

@ -59,6 +59,7 @@ import java.io.OutputStream;
public class SnappyOutputStream public class SnappyOutputStream
extends OutputStream extends OutputStream
{ {
public static final int MAX_BLOCK_SIZE = 512 * 1024 * 1024; // 512 MiB
static final int MIN_BLOCK_SIZE = 1 * 1024; static final int MIN_BLOCK_SIZE = 1 * 1024;
static final int DEFAULT_BLOCK_SIZE = 32 * 1024; // Use 32kb for the default block size static final int DEFAULT_BLOCK_SIZE = 32 * 1024; // Use 32kb for the default block size
@ -84,7 +85,7 @@ public class SnappyOutputStream
/** /**
* @param out * @param out
* @param blockSize byte size of the internal buffer size * @param blockSize byte size of the internal buffer size
* @throws IOException * @throws IllegalArgumentException when blockSize is larger than 512 MiB
*/ */
public SnappyOutputStream(OutputStream out, int blockSize) public SnappyOutputStream(OutputStream out, int blockSize)
{ {
@ -95,6 +96,9 @@ public class SnappyOutputStream
{ {
this.out = out; this.out = out;
this.blockSize = Math.max(MIN_BLOCK_SIZE, blockSize); this.blockSize = Math.max(MIN_BLOCK_SIZE, blockSize);
if (this.blockSize > MAX_BLOCK_SIZE){
throw new IllegalArgumentException(String.format("Provided chunk size %,d larger than max %,d", this.blockSize, MAX_BLOCK_SIZE));
}
int inputSize = blockSize; int inputSize = blockSize;
int outputSize = SnappyCodec.HEADER_SIZE + 4 + Snappy.maxCompressedLength(blockSize); int outputSize = SnappyCodec.HEADER_SIZE + 4 + Snappy.maxCompressedLength(blockSize);

View File

@ -34,6 +34,7 @@ import java.lang.ref.WeakReference;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import org.junit.Test; import org.junit.Test;
import org.junit.Assert;
import org.xerial.snappy.buffer.BufferAllocatorFactory; import org.xerial.snappy.buffer.BufferAllocatorFactory;
import org.xerial.snappy.buffer.CachedBufferAllocator; import org.xerial.snappy.buffer.CachedBufferAllocator;
import org.xerial.snappy.buffer.DefaultBufferAllocator; import org.xerial.snappy.buffer.DefaultBufferAllocator;
@ -106,6 +107,17 @@ public class SnappyOutputStreamTest
is.close(); is.close();
} }
@Test(expected = IllegalArgumentException.class)
public void invalidBlockSize()
throws Exception
{
// We rely on catch below, if there is no error this test will pass
// This can be done better with Assertions.assertThrows
Boolean exceptionThrown = false;
ByteArrayOutputStream b = new ByteArrayOutputStream();
SnappyOutputStream os = new SnappyOutputStream(b, 1024 * 1024 * 1024);
}
@Test @Test
public void smallWrites() public void smallWrites()
throws Exception throws Exception

View File

@ -379,6 +379,24 @@ public class SnappyTest
} }
} }
/*
Tests sad cases for SnappyInputStream.read method
- Expects a failed to compress exception due to upper bounds chunk size
- {-126, 'S', 'N', 'A', 'P', 'P', 'Y', 0, 0, 0, 0, 0, 0, 0, 0, 0,(byte) 0x7f, (byte) 0xff, (byte) 0xff, (byte) 0xff}
*/
@Test
public void isInvalidChunkLengthForSnappyInputStream()
throws Exception {
byte[] data = {-126, 'S', 'N', 'A', 'P', 'P', 'Y', 0, 0, 0, 0, 0, 0, 0, 0, 0, (byte) 0x7f, (byte) 0xff, (byte) 0xff, (byte) 0xff};
SnappyInputStream in = new SnappyInputStream(new ByteArrayInputStream(data));
byte[] out = new byte[50];
try {
in.read(out);
} catch (SnappyError error) {
Assert.assertEquals(error.errorCode, SnappyErrorCode.FAILED_TO_UNCOMPRESS);
}
}
/* /*
Tests happy cases for BitShuffle.shuffle method Tests happy cases for BitShuffle.shuffle method
- double: 0, 10 - double: 0, 10