CVE-2023-34453: Integer Overflow or Wraparound (CWE-190)
value: 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.

The 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`.

The 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.

Version 1.1.10.1 contains a patch for this vulnerability.
CWE 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.
CVSS v3 base score: 7.5
Published: 2023-06-15T17:15Z
Repository: https://github.com/xerial/snappy-java
Language: Java
Files changed: src/main/java/org/xerial/snappy/BitShuffle.java, src/main/java/org/xerial/snappy/SnappyErrorCode.java, src/test/java/org/xerial/snappy/SnappyTest.java
Commit date: 2023-06-14 10:36:02 -0700
Commit message: 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
Diff: diff --git a/src/main/java/org/xerial/snappy/BitShuffle.java b/src/main/java/org/xerial/snappy/BitShuffle.java
index fa62347..b2a4a6f 100644
--- a/src/main/java/org/xerial/snappy/BitShuffle.java
+++ b/src/main/java/org/xerial/snappy/BitShuffle.java
@@ -89,10 +89,13 @@ public class BitShuffle
      * @param input
      * @return bit-shuffled byte array
      * @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);
         return output;
     }
@@ -103,10 +106,13 @@ public class BitShuffle
      * @param input
      * @return bit-shuffled byte array
      * @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);
         return output;
     }
@@ -117,10 +123,13 @@ public class BitShuffle
      * @param input
      * @return bit-shuffled byte array
      * @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);
         return output;
     }
@@ -131,10 +140,13 @@ public class BitShuffle
      * @param input
      * @return bit-shuffled byte array
      * @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);
         return output;
     }
@@ -145,10 +157,13 @@ public class BitShuffle
      * @param input
      * @return bit-shuffled byte array
      * @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);
         return output;
     }
diff --git a/src/main/java/org/xerial/snappy/SnappyErrorCode.java b/src/main/java/org/xerial/snappy/SnappyErrorCode.java
index 24d5b18..661ffd8 100755
--- a/src/main/java/org/xerial/snappy/SnappyErrorCode.java
+++ b/src/main/java/org/xerial/snappy/SnappyErrorCode.java
@@ -40,11 +40,12 @@ public enum SnappyErrorCode
     OUT_OF_MEMORY(4),
     FAILED_TO_UNCOMPRESS(5),
     EMPTY_INPUT(6),
     INCOMPATIBLE_VERSION(7),
     INVALID_CHUNK_SIZE(8),
-    UNSUPPORTED_PLATFORM(9);
+    UNSUPPORTED_PLATFORM(9),
+    TOO_LARGE_INPUT(10);
 
     public final int id;
 
     private SnappyErrorCode(int id)
     {
diff --git a/src/test/java/org/xerial/snappy/SnappyTest.java b/src/test/java/org/xerial/snappy/SnappyTest.java
index 18b39e9..8a68b53 100755
--- a/src/test/java/org/xerial/snappy/SnappyTest.java
+++ b/src/test/java/org/xerial/snappy/SnappyTest.java
@@ -327,6 +327,64 @@ public class SnappyTest
         }
         catch (IOException e) {
             _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]);
+    }
 }

Vulnerable code:     UNSUPPORTED_PLATFORM(9);
Fixed code:         if (input.length * 2 < input.length) {
            throw new SnappyError(SnappyErrorCode.TOO_LARGE_INPUT, "input array size is too large: " + input.length);
        }
        if (input.length * 4 < input.length) {
            throw new SnappyError(SnappyErrorCode.TOO_LARGE_INPUT, "input array size is too large: " + input.length);
        }
        if (input.length * 8 < input.length) {
            throw new SnappyError(SnappyErrorCode.TOO_LARGE_INPUT, "input array size is too large: " + input.length);
        }
        if (input.length * 4 < input.length) {
            throw new SnappyError(SnappyErrorCode.TOO_LARGE_INPUT, "input array size is too large: " + input.length);
        }
        if (input.length * 8 < input.length) {
            throw new SnappyError(SnappyErrorCode.TOO_LARGE_INPUT, "input array size is too large: " + input.length);
        }
    UNSUPPORTED_PLATFORM(9),
    TOO_LARGE_INPUT(10);

    /*
    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]);
    }