CVE-2023-43642 (Java, published 2023-09-25T20:15Z)
Description: value: snappy-java is a Java port of the snappy, a fast C++ compresser/decompresser developed by Google. The SnappyInputStream was found to be vulnerable to Denial of Service (DoS) attacks when decompressing data with a too large chunk size. Due to missing upper bound check on chunk length, an unrecoverable fatal error can occur. All versions of snappy-java including the latest released version 1.1.10.3 are vulnerable to this issue. A fix has been introduced in commit `9f8c3cf74` which will be included in the 1.1.10.4 release. Users are advised to upgrade. Users unable to upgrade should only accept compressed data from trusted sources.
Severity: nan (CVSS v3 base score: 7.5)
Weakness: CWE-770 \u2014 Allocation of Resources Without Limits or Throttling
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.
Repository: https://github.com/xerial/snappy-java
Files changed:
src/main/java/org/xerial/snappy/SnappyHadoopCompatibleOutputStream.java
src/main/java/org/xerial/snappy/SnappyInputStream.java
src/main/java/org/xerial/snappy/SnappyOutputStream.java
src/test/java/org/xerial/snappy/SnappyOutputStreamTest.java
src/test/java/org/xerial/snappy/SnappyTest.java
Commit message: 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>

Vulnerable code:
import java.io.OutputStream;

     * @throws IOException

Fixed code:
import java.io.OutputStream;

    public static final int MAX_CHUNK_SIZE = 512 * 1024 * 1024; // 512 MiB

    private final int maxChunkSize;
        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;
        // 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));
        }

    public static final int MAX_BLOCK_SIZE = 512 * 1024 * 1024; // 512 MiB
     * @throws IllegalArgumentException when blockSize is larger than 512 MiB
        if (this.blockSize > MAX_BLOCK_SIZE){
            throw new IllegalArgumentException(String.format("Provided chunk size %,d larger than max %,d", this.blockSize, MAX_BLOCK_SIZE));
        }
import org.junit.Assert;
    @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);
    }

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