From 9b58271da2aa73a77fa9642e55e50e3f23fdf2b4 Mon Sep 17 00:00:00 2001 From: pprabhala-oss Date: Wed, 16 Sep 2026 11:57:15 -0700 Subject: [PATCH] Fix dest buffer bounds check in Snappy.uncompress Throw IllegalArgumentException when the destination is smaller than uncompressedLength() before calling native RawUncompress. Applied to both the byte[] and ByteBuffer overloads. Fixes #728 (CVE-2026-90559) --- src/main/java/org/xerial/snappy/Snappy.java | 16 +++++++++++- .../java/org/xerial/snappy/SnappyTest.java | 26 +++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/xerial/snappy/Snappy.java b/src/main/java/org/xerial/snappy/Snappy.java index 14d17964..f469bc58 100755 --- a/src/main/java/org/xerial/snappy/Snappy.java +++ b/src/main/java/org/xerial/snappy/Snappy.java @@ -536,10 +536,16 @@ public static byte[] uncompress(byte[] input) * @param outputOffset * @return the byte size of the uncompressed data * @throws IOException + * @throws IllegalArgumentException if the output array does not have enough space */ public static int uncompress(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset) throws IOException { + int requiredSize = uncompressedLength(input, inputOffset, inputLength); + if (output.length - outputOffset < requiredSize) { + throw new IllegalArgumentException("not enough space for output: need " + requiredSize + + " bytes, but only " + (output.length - outputOffset) + " remaining"); + } return rawUncompress(input, inputOffset, inputLength, output, outputOffset); } @@ -553,10 +559,12 @@ public static int uncompress(byte[] input, int inputOffset, int inputLength, byt * crash, use {@link #isValidCompressedBuffer(ByteBuffer)} first. * * @param compressed buffer[pos() ... limit()) containing the input data - * @param uncompressed output of the the uncompressed data. It uses buffer[pos()..] + * @param uncompressed output of the the uncompressed data. It uses buffer[pos()..]. + * remaining() must be at least {@link #uncompressedLength(ByteBuffer)}. * @return uncompressed data size * @throws IOException when failed to uncompress the given input * @throws SnappyError when the input is not a direct buffer + * @throws IllegalArgumentException if the destination buffer does not have enough remaining space */ public static int uncompress(ByteBuffer compressed, ByteBuffer uncompressed) throws IOException @@ -573,6 +581,12 @@ public static int uncompress(ByteBuffer compressed, ByteBuffer uncompressed) int cLen = compressed.remaining(); int uPos = uncompressed.position(); + int requiredSize = uncompressedLength(compressed); + if (uncompressed.remaining() < requiredSize) { + throw new IllegalArgumentException("not enough space for output: need " + requiredSize + + " bytes, but only " + uncompressed.remaining() + " remaining"); + } + // pos limit // [ ......UUUUUU.........] int decompressedSize = impl.rawUncompress(compressed, cPos, cLen, uncompressed, diff --git a/src/test/java/org/xerial/snappy/SnappyTest.java b/src/test/java/org/xerial/snappy/SnappyTest.java index 52c3a005..8b186fc3 100755 --- a/src/test/java/org/xerial/snappy/SnappyTest.java +++ b/src/test/java/org/xerial/snappy/SnappyTest.java @@ -527,6 +527,32 @@ public void isTooLargeShortArrayInputLengthForBitShuffleShuffle() throws Excepti BitShuffle.shuffle(new short[Integer.MAX_VALUE / 2 + 1]); } + @Test(expected = IllegalArgumentException.class) + public void uncompressDirectBufferWithInsufficientSpace() + throws Exception + { + byte[] orig = "Hello snappy dest bounds".getBytes(); + ByteBuffer src = ByteBuffer.allocateDirect(orig.length); + src.put(orig); + src.flip(); + + ByteBuffer compressed = ByteBuffer.allocateDirect(Snappy.maxCompressedLength(orig.length)); + Snappy.compress(src, compressed); + + ByteBuffer dest = ByteBuffer.allocateDirect(8); + Snappy.uncompress(compressed, dest); + } + + @Test(expected = IllegalArgumentException.class) + public void uncompressByteArrayWithInsufficientSpace() + throws Exception + { + byte[] orig = "Hello snappy dest bounds".getBytes(); + byte[] compressed = Snappy.compress(orig); + byte[] dest = new byte[8]; + Snappy.uncompress(compressed, 0, compressed.length, dest, 0); + } + private void assumingCIIsFalse() { if (System.getenv("CI") == null) return;