Repository navigation
Fix buffer overflows and unbounded allocations from untrusted input (CVE-2026-90559 and others) - #739
Merged
Merged
Conversation
The JNI layer writes through raw pointers without any bounds check, so the Java API must validate every range it hands to native code. - compress/rawCompress: require maxCompressedLength(inputLength) bytes of output space and an in-range input (#732, #717) - uncompress/rawUncompress: require the declared uncompressed length to fit in the output buffer (#728, CVE-2026-90559, #717) - uncompress*Array: reject uncompressed lengths that are not a multiple of the element size, which under-allocated the result array (#729) - uncompressedLength: reject negative lengths and lengths no valid Snappy stream of the given size can produce (> 64/3 of the compressed size), preventing huge allocations from a few bytes of input (#625, #730) - SnappyFramedInputStream: skip consecutive skippable chunks iteratively instead of recursing once per 4-byte chunk (#731) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 3, 2026
Closed
Closed
Closed
This was referenced Oct 5, 2026
Closed
vharseko
added a commit
to OpenIdentityPlatform/OpenAM
that referenced
this pull request
Oct 29, 2026
…-108106 CVE-2026-116590 GHSA-6gp7-6wmv-gxqw org.xerial.snappy:snappy-java: Out-of-bounds writes, unbounded allocation and pooled-buffer double release on untrusted input (1.1.10.5 -> 1.1.10.12) (#1206) Bumps `org.xerial.snappy:snappy-java` from 1.1.10.5 to 1.1.10.12 in `openam-cassandra/pom.xml` (`dependencyManagement`), clearing Dependabot alerts #386, #387 and #388. Dependabot proposed 1.1.10.9, the first release that fixes those three; this PR goes on to 1.1.10.12, because upstream published seven advisories against the 1.1.10.x line on 2026-10-03 and two of them, GHSA-c73m-r934-8qvg and GHSA-6gp7-6wmv-gxqw, are fixed only in 1.1.10.10. Neither of those two is a reviewed entry in the GitHub Advisory Database yet, so no alert has been raised for them. This follows #648, which took the same dependency to 1.1.10.5 for CVE-2023-43642. ## Advisories **CVE-2026-90559 / GHSA-wmgv-28fv-894x** (fixed in 1.1.10.9) — Out-of-bounds write (CWE-787). `Snappy.uncompress(ByteBuffer, ByteBuffer)` did not check that the destination had room for the decompressed data. The native decompressor writes as many bytes as the compressed input declares, starting at the destination's position, so a crafted input declaring more than `uncompressed.remaining()` writes past the end of the direct buffer's off-heap allocation. 1.1.10.9 throws `IllegalArgumentException` when `remaining()` is smaller than `Snappy.uncompressedLength(compressed)` (xerial/snappy-java#739). **CVE-2026-93451 / GHSA-gwq9-9ffj-mvrv** (fixed in 1.1.10.9) — Off-by-one error; out-of-bounds write (CWE-193, CWE-787). `Snappy.uncompress{Char,Short,Int,Long,Float,Double}Array` sized the result by dividing the declared byte length by the element size, but the native code wrote the full byte length, so a length that is not a multiple of the element size wrote up to 7 bytes past the end of the Java array. 1.1.10.9 rejects such lengths with `SnappyIOException` (#739). **CVE-2026-93452 / GHSA-38f5-hf66-x965** (fixed in 1.1.10.9) — Out-of-bounds read and write (CWE-125, CWE-787). The `byte[]`, typed-array and `ByteBuffer` variants of `Snappy.compress`, `Snappy.rawCompress` and `Snappy.rawUncompress` passed caller-supplied offsets and lengths to native code unchecked and did not check that the output had `Snappy.maxCompressedLength(inputLength)` bytes available, so an out-of-range length or offset, or an undersized output buffer, read or wrote outside the arrays. NVD files the `compress(ByteBuffer, ByteBuffer)` case (xerial/snappy-java#732) as CVE-2026-93452. 1.1.10.9 checks the input ranges and the output space before the native call and throws `IllegalArgumentException` (#739). **CVE-2026-108106 / GHSA-q233-g92q-qxp6** (fixed in 1.1.10.9) — Allocation of resources without limits; memory allocation with excessive size value (CWE-770, CWE-789). `Snappy.uncompress(byte[])`, `Snappy.uncompressString`, the `uncompress*Array` methods, `SnappyInputStream` and `SnappyFramedInputStream` allocate the output at the uncompressed length declared in the first bytes of the input, before decompressing anything, so a few bytes force an allocation of up to 2 GB (`OutOfMemoryError`), or a `NegativeArraySizeException` at 2^31 and above. The `maxChunkSize` limit added for CVE-2023-43642 bounds only the compressed chunk length. 1.1.10.9 makes `Snappy.uncompressedLength()` reject negative lengths and lengths above 64/3 of the compressed size: the densest Snappy element expands 3 bytes to 64, so no valid stream exceeds that (#739). **CVE-2026-116590 / GHSA-xqrx-qh46-34m7** (fixed in 1.1.10.9) — Improper validation of array index; out-of-bounds write (CWE-129, CWE-787). `Snappy.arrayCopy(Object, int, int, Object, int)` ran a native `memcpy` without checking the ranges, and `SnappyOutputStream.rawWrite` and `SnappyInputStream.rawRead`, and through them the typed-array `write` / `read` methods, pass caller-supplied ranges to it, so a negative or oversized length or offset read or wrote outside the arrays and crashed the JVM. 1.1.10.9 checks both ranges before copying; the stream methods validate offsets and lengths and reject typed-array ranges whose byte size overflows `int` (xerial/snappy-java#741). **CVE-2026-108104 / GHSA-c73m-r934-8qvg** (fixed in 1.1.10.10) — Double free; improper control of a resource through its lifetime (CWE-415, CWE-664). When `SnappyFramedInputStream` needed larger buffers, it returned the current ones to the buffer pool before allocating the replacements. If the allocation failed, for example with an `OutOfMemoryError` caused by a large declared chunk length, the stream still referenced the released buffers and `close()` released them a second time. The default `CachingBufferPool` does not detect duplicates, so two later streams could receive the same backing array, and one stream's decompressed data could be read or overwritten through another. 1.1.10.10 clears the buffer fields right after release (xerial/snappy-java#747). **GHSA-6gp7-6wmv-gxqw** (fixed in 1.1.10.10; no CVE assigned) — Uncontrolled recursion; allocation of resources without limits (CWE-674, CWE-770). `SnappyInputStream` handled each stream header of concatenated data with a recursive call, so a long run of 16-byte headers overflowed the thread's stack (`StackOverflowError`), and it allocated the compressed-chunk buffer at the declared chunk size, up to the 512 MiB `MAX_CHUNK_SIZE`, before reading any chunk data. 1.1.10.10 skips headers in a loop and grows the chunk buffer as data arrives (#747). | | Severity | Affected | Fixed in | |---|---|---|---| | GHSA-wmgv-28fv-894x | High — CVSS 3.1 7.5 (`AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H`) | <= 1.1.10.8 | 1.1.10.9 | | GHSA-gwq9-9ffj-mvrv | High — CVSS 3.1 7.5 (`AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H`) | <= 1.1.10.8 | 1.1.10.9 | | GHSA-38f5-hf66-x965 | Medium — CVSS 3.1 6.5 (`AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:N/A:H`) | <= 1.1.10.8 | 1.1.10.9 | | GHSA-q233-g92q-qxp6 | High — CVSS 3.1 7.5 (`AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H`) | <= 1.1.10.8 | 1.1.10.9 | | GHSA-xqrx-qh46-34m7 | Medium — CVSS 3.1 5.9 (`AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H`) | <= 1.1.10.8 | 1.1.10.9 | | GHSA-c73m-r934-8qvg | High — CVSS 3.1 8.7 (`AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:H/A:N`) | <= 1.1.10.9 | 1.1.10.10 | | GHSA-6gp7-6wmv-gxqw | High — CVSS 3.1 7.5 (`AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H`) | <= 1.1.10.9 | 1.1.10.10 | The ids are those of the upstream repository advisories. Only GHSA-gwq9, GHSA-q233 and GHSA-xqrx are reviewed entries in the GitHub Advisory Database, which is what Dependabot alerts on. CVE-2026-90559, CVE-2026-93452 and CVE-2026-108104 have only unreviewed entries there with no package mapping (GHSA-mrhq-xxpp-qf7j, GHSA-pm27-vjq7-f5fv, GHSA-2xrm-qggp-hr4m), and GHSA-6gp7 has none. Every one of the seven needs code that passes snappy-java untrusted compressed data or caller-controlled ranges. ## Impact on OpenAM `snappy-java` ships in the WAR (`WEB-INF/lib/snappy-java-1.1.10.5.jar`). `java-driver-core` 4.19.2 declares it `optional`, so `openam-cassandra-datastore` redeclares it explicitly, and `openam-server-only` depends on that module and on `openam-cassandra-cts`. No OpenAM source imports `org.xerial.snappy`; of the jars in `WEB-INF/lib`, only `java-driver-core` references it, from `com.datastax.oss.driver.internal.core.protocol.SnappyCompressor`. The driver creates `SnappyCompressor` only when `advanced.protocol.compression = snappy` is set. OpenAM's `openam-cassandra-datastore/src/main/resources/application.conf` does not set it, and the driver's `reference.conf` leaves it absent (frames uncompressed), so by default snappy-java is never called and its native library is never loaded. A deployment that turns snappy protocol compression on through its own driver configuration has the driver compress request frames and decompress the response frames of the Cassandra nodes it connects to; no data from OpenAM's HTTP clients reaches the decompressor. The driver's own handling keeps every flaw above out of reach: - Decompression first runs `Snappy.isValidCompressedBuffer` over the whole frame, a native pass that allocates nothing and succeeds only if the data decodes to exactly the declared length. Only then does it size the output from `Snappy.uncompressedLength` and call `Snappy.uncompress(byte[], int, int, byte[], int)` or `Snappy.uncompress(ByteBuffer, ByteBuffer)` into a buffer of exactly that size. A frame that declares more than it holds is rejected before any allocation, the destination is never undersized (CVE-2026-90559), and frames are capped by `advanced.protocol.max-frame-length` (256 MiB) (CVE-2026-108106). - Compression sizes its output buffer with `Snappy.maxCompressedLength` of the frame's readable bytes, so neither the ranges nor the output space can be wrong (CVE-2026-93452). - The driver never calls the typed `uncompress*Array` methods (CVE-2026-93451), `Snappy.arrayCopy` (CVE-2026-116590), or any of the stream classes, `SnappyInputStream`, `SnappyOutputStream` and `SnappyFramedInputStream` (CVE-2026-116590, CVE-2026-108104, GHSA-6gp7). `openam-cassandra-embedded` (`cassandra-all` 5.0.7, the embedded Cassandra server) also takes its `snappy-java` version from this `dependencyManagement` entry, but it is a `test`-scoped dependency of the datastore and CTS modules and is not in the WAR. None of the seven is reachable from OpenAM. The bump is hardening: it takes the jar that ships in the WAR out of every affected range. ## Change `openam-cassandra/pom.xml`, `dependencyManagement`: `org.xerial.snappy:snappy-java` 1.1.10.5 → 1.1.10.12 — Dependabot's commit to 1.1.10.9 and a commit on top to 1.1.10.12. It governs `openam-cassandra-datastore`, and through it the WAR, and `openam-cassandra-embedded`. No code change. Behaviour changes in the new range, none of which touches the driver's code path: - 1.1.10.8: snappy-java no longer catches `OutOfMemoryError` internally (xerial/snappy-java#647). - 1.1.10.9: `compress` throws `IllegalArgumentException` for an output buffer smaller than `maxCompressedLength(n)`, and `uncompress` for an undersized destination; `uncompressedLength` rejects declared lengths above 64/3 of the compressed size; the typed `uncompress*Array` methods reject misaligned lengths; `maxCompressedLength` throws `SnappyError(TOO_LARGE_INPUT)` instead of returning a negative number for inputs of 1,840,700,243 bytes and more. musl is no longer misdetected on glibc systems that also have it installed, the Windows DLLs are built with ASLR and DEP, and the jar declares `Automatic-Module-Name: org.xerial.snappy`. - 1.1.10.10: `SnappyInputStream` grows its chunk buffer as data arrives instead of allocating the declared chunk size up front. - 1.1.10.11: the output check of `compress(ByteBuffer, ByteBuffer)` measures `capacity() - position()` instead of `remaining()`, which restores Hadoop 3.5.0's `SnappyCompressor`, which sets the output limit to zero before compressing (xerial/snappy-java#766). - 1.1.10.12: `SnappyHadoopCompatibleOutputStream` accounts for Hadoop's 8-byte block header in its output-space check (xerial/snappy-java#768). Verified: - `snappy-java-1.1.10.12.jar` matches the SHA-1 published on Maven Central (`c97fdfd953df72ae08be22384a1581ce665c2694`) - the jar still targets Java 8 (class file version 52), and its POM declares no runtime dependencies (`test` and `provided` only) - the published 1.1.10.12 carries the fixes: `Snappy` has the range, output-space, alignment and uncompressed-length checks (`checkArrayRange`, `checkOutputSpace`, `checkElementAlignment`, `checkUncompressedLength`, the last with the 64/3 bound), `Snappy.arrayCopy` checks both ranges before the native copy, `SnappyFramedInputStream` clears its buffer fields right after releasing them, and `SnappyInputStream.hasNextChunk()` no longer calls itself - `SnappyCompressor` in `java-driver-core` 4.19.2 calls only `maxCompressedLength`, the `compress(ByteBuffer, ByteBuffer)` and `compress(byte[], int, int, byte[], int)` overloads, `isValidCompressedBuffer`, `uncompressedLength` and the two `uncompress` overloads above; it validates before it allocates and sizes every output buffer itself - among the jars in the built WAR's `WEB-INF/lib`, only `java-driver-core` references `org.xerial.snappy` - `openam-cassandra-embedded`, `openam-cassandra-datastore` and `openam-cassandra-cts` resolve `snappy-java` 1.1.10.12; `mvn verify` of the four `openam-cassandra` modules passes on JDK 11, where the `jdk-15-cassandra` profile does not skip the tests: 29 tests, 0 failures, with the datastore and CTS tests running against the embedded Cassandra server - the GitHub Advisory Database lists no advisory affecting `snappy-java@1.1.10.12` ## References - GHSA-wmgv-28fv-894x - GHSA-gwq9-9ffj-mvrv - GHSA-38f5-hf66-x965 - GHSA-q233-g92q-qxp6 - GHSA-xqrx-qh46-34m7 - GHSA-c73m-r934-8qvg - GHSA-6gp7-6wmv-gxqw - https://nvd.nist.gov/vuln/detail/CVE-2026-90559 - https://nvd.nist.gov/vuln/detail/CVE-2026-93451 - https://nvd.nist.gov/vuln/detail/CVE-2026-93452 - https://nvd.nist.gov/vuln/detail/CVE-2026-108106 - https://nvd.nist.gov/vuln/detail/CVE-2026-116590 - https://nvd.nist.gov/vuln/detail/CVE-2026-108104 - xerial/snappy-java#739 - xerial/snappy-java#741 - xerial/snappy-java#747 - xerial/snappy-java#766 - xerial/snappy-java#768 - xerial/snappy-java@v1.1.10.5...v1.1.10.12 --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Valera V Harseko <vharseko@3a-systems.ru>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The JNI layer (
SnappyNative.cpp) writes through raw pointers with no bounds checks. This PR adds validation in the Java API before every native call, so bad sizes now raise a Java exception instead of crashing the JVM or corrupting memory. The native libraries don't need to be rebuilt.uncompress(ByteBuffer, ByteBuffer)writes past an undersized destinationuncompressed.remaining() >= uncompressedLength(compressed)compress(ByteBuffer, ByteBuffer)writes past an undersized destinationcompressed.remaining() >= maxCompressedLength(len)rawCompress/rawUncompress/uncompress(byte[]…)accept out-of-range offsets and lengths and undersized outputsuncompress{Char,Short,Int,Long,Float,Double}Arrayunder-allocate when the length isn't a multiple of the element sizeSnappyIOExceptionuncompress(byte[])with a 5-byte input leads to an OOM orNegativeArraySizeExceptionuncompressedLength()rejects negative lengths and lengths above 64/3 × the compressed sizeSnappyFramedInputStreamallocates from the declared length before the CRC checkuncompressedLength()bound: allocation is now proportional to the bytes actually readStackOverflowErrorthrough recursionWhy a ratio bound instead of a fixed cap
The densest Snappy element is a 3-byte copy that expands to 64 bytes, so a valid stream can never declare more than 64/3 times its compressed size. The bound rejects only data that would fail to decompress anyway, and it can't break legitimate data. It lives in
uncompressedLength(), so it also covers third-party code that doesnew byte[Snappy.uncompressedLength(x)].For the framed stream, I left out a hard 64 KiB per-chunk cap (the framing spec limit). The existing
testLargerFrames_*tests show the reader accepts larger frames on purpose for compatibility.Behavior change
Passing an output buffer smaller than
maxCompressedLength(n)tocompressnow throwsIllegalArgumentException. The nativeRawCompressalready required that size, and an undersized buffer was undefined behavior before.Related community PRs
#733, #734, #735, #736 and #737 each fix one of these issues. This PR covers all of them in one consistent change, and thanks to their authors for the reports and proposed fixes.
Test plan
SnappyBoundsCheckTest(20 tests) covering each issue's PoC plus valid edge cases: max-ratio data, offset output, empty input./sbt test: 124 passed, 0 failed🤖 Generated with Claude Code