CVE-2023-34455 (published 2023-06-15T18:15Z)

snappy-java is a fast compressor/decompressor for Java. Due to use of an unchecked chunk length, an unrecoverable fatal error can occur in versions prior to 1.1.10.1.\n\nThe code in the function hasNextChunk in the fileSnappyInputStream.java checks if a given stream has more chunks to read. It does that by attempting to read 4 bytes. If it wasn’t possible to read the 4 bytes, the function returns false. Otherwise, if 4 bytes were available, the code treats them as the length of the next chunk.\n\nIn the case that the `compressed` variable is null, a byte array is allocated with the size given by the input data. Since the code doesn’t test the legality of the `chunkSize` variable, it is possible to pass a negative number (such as 0xFFFFFFFF which is -1), which will cause the code to raise a `java.lang.NegativeArraySizeException` exception. A worse case would happen when passing a huge positive value (such as 0x7FFFFFFF), which would raise the fatal `java.lang.OutOfMemoryError` error.\n\nVersion 1.1.10.1 contains a patch for this issue.

CVSS v3 base score: 7.5, HIGH (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H)

Weakness information:
cwe_id: CWE-770
cwe_name: Allocation of Resources Without Limits or Throttling
description: The product allocates a reusable resource or group of resources on behalf of an actor without imposing any restrictions on the size or number of resources that can be allocated, in violation of the intended security policy for that actor.
extended_description: Code frequently has to work with limited resources, so programmers must be careful to ensure that resources are not consumed too quickly, or too easily.  Without use of quotas, resource limits, or other protection mechanisms, it can be easy for an attacker to consume many resources by rapidly making many requests, or causing larger resources to be used than is needed. When too many resources are allocated, or if a single resource is too large, then it can prevent the code from working correctly, possibly leading to a denial of service.
         

Fix information:
repo_url: https://github.com/xerial/snappy-java
commit_details: repo_url: https://github.com/xerial/snappy-java
author: aidanchiu1112
author_date: 2023-06-14 10:49:52-07:00
msg: Merge pull request from GHSA-qcwq-55hx-v3vh

* asserted chunksize should be in the bounds of 0-java.outofmmeoryexception

* asserted chunksize should be in the bounds of 0-java.outofmmeoryexception

* https://github.com/xerial/snappy-java-ghsa-qcwq-55hx-v3vh/pull/2

* advisory-fix-3

* added and changed method name for happy and sad cases in SnappyTest.java

* removed expected error for happy case in unit testing

* added another unit test case in SnappyTest.java and fixed comments in SnappyInputStream.java

* switched SnappyError to INVALID_CHUNK_SIZE

* Updated unit tests

* Resolved conflicts with another PR merge
repository: repo_url: https://github.com/xerial/snappy-java
repo_name: xerial/snappy-java
description: Snappy compressor/decompressor for Java
repo_language: Java
owner: xerial
file_changes: filename: SnappyInputStream.java
change_type: ModificationType.MODIFY
diff: @@ -417,9 +417,20 @@ public class SnappyInputStream
             }
         }
 
+        // chunkSize is negative
+        if (chunkSize < 0) {
+            throw new SnappyError(SnappyErrorCode.INVALID_CHUNK_SIZE, "chunkSize is too big or negative : " + chunkSize);
+        }
+
         // extend the compressed data buffer size
         if (compressed == null || chunkSize > compressed.length) {
-            compressed = new byte[chunkSize];
+            // chunkSize exceeds limit
+            try {
+                compressed = new byte[chunkSize];
+            }
+            catch (java.lang.OutOfMemoryError e) {
+                throw new SnappyError(SnappyErrorCode.INVALID_CHUNK_SIZE, e.getMessage());
+            }
         }
         readBytes = 0;
         while (readBytes < chunkSize) {

programming_language: Java
filename: SnappyTest.java
change_type: ModificationType.MODIFY
diff: @@ -26,6 +26,7 @@ package org.xerial.snappy;
 
 import static org.junit.Assert.*;
 
+import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.nio.ByteBuffer;
 
@@ -330,6 +331,53 @@ public class SnappyTest
         }
     }
 
+    /*
+
+    Tests happy cases for SnappyInputStream.read method
+    - {0}
+     */
+    @Test
+    public void isValidChunkLengthForSnappyInputStreamIn()
+            throws Exception {
+        byte[] data = {0};
+        SnappyInputStream in = new SnappyInputStream(new ByteArrayInputStream(data));
+        byte[] out = new byte[50];
+        in.read(out);
+    }
+
+    /*
+    Tests sad cases for SnappyInputStream.read method
+    - Expects a java.lang.NegativeArraySizeException catched into a SnappyError
+    - {-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(expected = SnappyError.class)
+    public void isInvalidChunkLengthForSnappyInputStreamInNegative()
+            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];
+        in.read(out);
+    }
+
+    /*
+    Tests sad cases for SnappyInputStream.read method
+    - Expects a java.lang.OutOfMemoryError
+    - {-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(expected = SnappyError.class)
+    public void isInvalidChunkLengthForSnappyInputStreamOutOfMemory()
+            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 (Exception ignored) {
+            // Exception here will be catched
+            // But OutOfMemoryError will not be caught, and will still be thrown
+        }
+    }
+
     /*
     Tests happy cases for BitShuffle.shuffle method
     - double: 0, 10
@@ -337,7 +385,7 @@ public class SnappyTest
     - int: 0, 10
     - long: 0, 10
     - short: 0, 10
-     */
+    */
     @Test
     public void isValidArrayInputLengthForBitShuffleShuffle()
             throws Exception
@@ -386,5 +434,6 @@ public class SnappyTest
     @Test(expected = SnappyError.class)
     public void isTooLargeShortArrayInputLengthForBitShuffleShuffle() throws Exception {
         BitShuffle.shuffle(new short[Integer.MAX_VALUE / 2 + 1]);
+
     }
 }

programming_language: Java