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