CVE-2023-34453 (published 2023-06-15T17:15Z)

snappy-java is a fast compressor/decompressor for Java. Due to unchecked multiplications, an integer overflow may occur in versions prior to 1.1.10.1, causing a fatal error.\n\nThe function `shuffle(int[] input)` in the file `BitShuffle.java` receives an array of integers and applies a bit shuffle on it. It does so by multiplying the length by 4 and passing it to the natively compiled shuffle function. Since the length is not tested, the multiplication by four can cause an integer overflow and become a smaller value than the true size, or even zero or negative. In the case of a negative value, a `java.lang.NegativeArraySizeException` exception will raise, which can crash the program. In a case of a value that is zero or too small, the code that afterwards references the shuffled array will assume a bigger size of the array, which might cause exceptions such as `java.lang.ArrayIndexOutOfBoundsException`.\n\nThe same issue exists also when using the `shuffle` functions that receive a double, float, long and short, each using a different multiplier that may cause the same issue.\n\nVersion 1.1.10.1 contains a patch for this vulnerability.

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-190
cwe_name: Integer Overflow or Wraparound
description: The product performs a calculation that can
         produce an integer overflow or wraparound when the logic
         assumes that the resulting value will always be larger than
         the original value. This occurs when an integer value is
         incremented to a value that is too large to store in the
         associated representation. When this occurs, the value may
         become a very small or negative number.
extended_description: 
            
            
            
            
         
         

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:36:02-07:00
msg: Merge pull request from GHSA-pqr6-cmr2-h8hf

* Fixed integer overflow by checking if multiplication result is smaller than original value

* Fixed integer overflow by checking if multiplication result is smaller than original value

* Fixed integer overflow by checking if multiplication result is smaller than original value

* imporved error messages and added happy and sad cases for unit test in SnappyTest.java

* switched SnappyError into ILLEGAL_ARGUMENT in SnappyErrorCode.java and Snappy.java

* wrote new and updated unit test methods

* updated comments in SnappyTest.java

* Fixed and updated unit tests in SnappyTest.java
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: BitShuffle.java
change_type: ModificationType.MODIFY
diff: @@ -91,6 +91,9 @@ public class BitShuffle
      * @throws IOException
      */
     public static byte[] shuffle(short[] input) throws IOException {
+        if (input.length * 2 < input.length) {
+            throw new SnappyError(SnappyErrorCode.TOO_LARGE_INPUT, "input array size is too large: " + input.length);
+        }
         byte[] output = new byte[input.length * 2];
         int numProcessed = impl.shuffle(input, 0, 2, input.length * 2, output, 0);
         assert(numProcessed == input.length * 2);
@@ -105,6 +108,9 @@ public class BitShuffle
      * @throws IOException
      */
     public static byte[] shuffle(int[] input) throws IOException {
+        if (input.length * 4 < input.length) {
+            throw new SnappyError(SnappyErrorCode.TOO_LARGE_INPUT, "input array size is too large: " + input.length);
+        }
         byte[] output = new byte[input.length * 4];
         int numProcessed = impl.shuffle(input, 0, 4, input.length * 4, output, 0);
         assert(numProcessed == input.length * 4);
@@ -119,6 +125,9 @@ public class BitShuffle
      * @throws IOException
      */
     public static byte[] shuffle(long[] input) throws IOException {
+        if (input.length * 8 < input.length) {
+            throw new SnappyError(SnappyErrorCode.TOO_LARGE_INPUT, "input array size is too large: " + input.length);
+        }
         byte[] output = new byte[input.length * 8];
         int numProcessed = impl.shuffle(input, 0, 8, input.length * 8, output, 0);
         assert(numProcessed == input.length * 8);
@@ -133,6 +142,9 @@ public class BitShuffle
      * @throws IOException
      */
     public static byte[] shuffle(float[] input) throws IOException {
+        if (input.length * 4 < input.length) {
+            throw new SnappyError(SnappyErrorCode.TOO_LARGE_INPUT, "input array size is too large: " + input.length);
+        }
         byte[] output = new byte[input.length * 4];
         int numProcessed = impl.shuffle(input, 0, 4, input.length * 4, output, 0);
         assert(numProcessed == input.length * 4);
@@ -147,6 +159,9 @@ public class BitShuffle
      * @throws IOException
      */
     public static byte[] shuffle(double[] input) throws IOException {
+        if (input.length * 8 < input.length) {
+            throw new SnappyError(SnappyErrorCode.TOO_LARGE_INPUT, "input array size is too large: " + input.length);
+        }
         byte[] output = new byte[input.length * 8];
         int numProcessed = impl.shuffle(input, 0, 8, input.length * 8, output, 0);
         assert(numProcessed == input.length * 8);

programming_language: Java
filename: SnappyErrorCode.java
change_type: ModificationType.MODIFY
diff: @@ -42,7 +42,8 @@ public enum SnappyErrorCode
     EMPTY_INPUT(6),
     INCOMPATIBLE_VERSION(7),
     INVALID_CHUNK_SIZE(8),
-    UNSUPPORTED_PLATFORM(9);
+    UNSUPPORTED_PLATFORM(9),
+    TOO_LARGE_INPUT(10);
 
     public final int id;
 

programming_language: Java
filename: SnappyTest.java
change_type: ModificationType.MODIFY
diff: @@ -329,4 +329,62 @@ public class SnappyTest
             _logger.debug(e);
         }
     }
+
+    /*
+    Tests happy cases for BitShuffle.shuffle method
+    - double: 0, 10
+    - float: 0, 10
+    - int: 0, 10
+    - long: 0, 10
+    - short: 0, 10
+     */
+    @Test
+    public void isValidArrayInputLengthForBitShuffleShuffle()
+            throws Exception
+    {
+        byte[] b = BitShuffle.shuffle(new double[0]);
+        byte[] c = BitShuffle.shuffle(new float[0]);
+        byte[] d = BitShuffle.shuffle(new int[0]);
+        byte[] e = BitShuffle.shuffle(new long[0]);
+        byte[] f = BitShuffle.shuffle(new short[0]);
+        byte[] n = BitShuffle.shuffle(new double[10]);
+        byte[] o = BitShuffle.shuffle(new float[10]);
+        byte[] p = BitShuffle.shuffle(new int[10]);
+        byte[] q = BitShuffle.shuffle(new long[10]);
+        byte[] r = BitShuffle.shuffle(new short[10]);
+    }
+
+    /*
+    Tests sad cases for BitShuffle.shuffle method
+    - Allocate a buffer whose byte size will be a bit larger than Integer.MAX_VALUE
+    - double: 8
+    - float: 4
+    - int: 4
+    - long: 8
+    - short: 2
+     */
+    @Test(expected = SnappyError.class)
+    public void isTooLargeDoubleArrayInputLengthForBitShuffleShuffle() throws Exception {
+        BitShuffle.shuffle(new double[Integer.MAX_VALUE / 8 + 1]);
+    }
+
+    @Test(expected = SnappyError.class)
+    public void isTooLargeFloatArrayInputLengthForBitShuffleShuffle() throws Exception {
+        BitShuffle.shuffle(new float[Integer.MAX_VALUE / 4 + 1]);
+    }
+
+    @Test(expected = SnappyError.class)
+    public void isTooLargeIntArrayInputLengthForBitShuffleShuffle() throws Exception {
+        BitShuffle.shuffle(new float[Integer.MAX_VALUE / 4 + 1]);
+    }
+
+    @Test(expected = SnappyError.class)
+    public void isTooLargeLongArrayInputLengthForBitShuffleShuffle() throws Exception {
+        BitShuffle.shuffle(new long[Integer.MAX_VALUE / 8 + 1]);
+    }
+
+    @Test(expected = SnappyError.class)
+    public void isTooLargeShortArrayInputLengthForBitShuffleShuffle() throws Exception {
+        BitShuffle.shuffle(new short[Integer.MAX_VALUE / 2 + 1]);
+    }
 }

programming_language: Java