Skip to main content
Vulnerability Database/CVE-2026-87823

CVE-2026-87823: zstd-jni Buffer Overflow Vulnerability

CVE-2026-87823 is a buffer overflow in zstd-jni before version 1.5.7-14 that allows out-of-bounds memory reads through negative offset values. This article covers the technical details, affected versions, security impact, and mitigation strategies.

Published:

CVE-2026-87823 Overview

CVE-2026-87823 is an out-of-bounds read vulnerability in zstd-jni versions before 1.5.7-14. The library provides Java Native Interface (JNI) bindings for the Zstandard compression algorithm. Three direct-ByteBuffer frame-size native methods perform 32-bit signed bounds checks that fail to reject negative offsets. Attackers supplying values near Integer.MIN_VALUE can force the native code to read unmapped memory. The result is JVM termination or extraction of frame-size data from unintended memory regions. The flaw is classified under CWE-190: Integer Overflow or Wraparound.

Critical Impact

Remote attackers can supply crafted offset values to network-facing Java services using zstd-jni, crashing the JVM or leaking adjacent process memory without authentication.

Affected Products

  • zstd-jni versions 1.1.1 through 1.5.7-13
  • Java applications embedding zstd-jni for Zstandard compression or decompression
  • Downstream frameworks and services that expose direct-ByteBuffer compression APIs to untrusted input

Discovery Timeline

  • 2026-09-09 - CVE-2026-87823 published to NVD
  • 2026-09-09 - Last updated in NVD database

Technical Details for CVE-2026-87823

Vulnerability Analysis

The vulnerability affects three native methods in jni_zstd.c that operate on direct ByteBuffer instances to compute Zstandard frame sizes. Each method retrieves the buffer capacity, then validates the caller-supplied src_offset and src_size against that capacity. The original guard used a single signed comparison: if (src_offset > src_cap - src_size) return -ZSTD_error_GENERIC;. When src_offset is negative, the comparison passes even though the effective read position lies outside the buffer. The native code then executes ZSTD_findFrameCompressedSize(src_buf_ptr + src_offset, ...), dereferencing a pointer that points before the base of the direct buffer.

The consequences depend on the memory layout of the JVM process. If the computed address falls on an unmapped page, the process receives SIGSEGV and the JVM terminates, producing a denial-of-service condition. If the address falls on a mapped page, the Zstandard parser interprets whatever bytes it finds as a compressed frame header and returns a size value derived from unrelated process memory.

Root Cause

The root cause is missing lower-bound validation on the jint offset and size parameters crossing the JNI boundary. jint is a 32-bit signed integer, and the pre-patch check src_offset > src_cap - src_size was written under the assumption that both operands were non-negative. Negative offsets or oversized lengths near Integer.MIN_VALUE bypass the check entirely, and pointer arithmetic on src_buf_ptr + src_offset produces an address outside the allocated buffer.

Attack Vector

Any code path that forwards untrusted offset or length values into the affected frame-size APIs is exploitable. Because the attack requires only that a caller reach the native method with a controlled jint, network-facing services that decode Zstandard-framed data from clients are directly reachable. No authentication is required, and complexity is low.

c
// Pre-patch (vulnerable) - src/main/native/jni_zstd.c
(JNIEnv *env, jclass obj, jobject src_buf, jint src_offset, jint src_size) {
    size_t size = -ZSTD_error_memory_allocation;
    jsize src_cap = (*env)->GetDirectBufferCapacity(env, src_buf);
    if (src_offset > src_cap - src_size) return -ZSTD_error_GENERIC;
    char *src_buf_ptr = (char*)(*env)->GetDirectBufferAddress(env, src_buf);
    if (src_buf_ptr == NULL) goto E1;
    size = ZSTD_findFrameCompressedSize(src_buf_ptr + src_offset, (size_t) src_size);

// Post-patch (fixed) - adds negative-value guards
    if (src_offset < 0 || src_size < 0 || (jsize) src_offset > src_cap - (jsize) src_size)
        return -ZSTD_error_GENERIC;

Source: GitHub Commit d7a1c99

Detection Methods for CVE-2026-87823

Indicators of Compromise

  • Unexpected JVM crashes with SIGSEGV in hs_err_pid log files referencing frames inside libzstd-jni or ZSTD_findFrameCompressedSize.
  • Anomalous responses from services that decompress client-supplied Zstandard data, including size values inconsistent with the input payload.
  • Presence of zstd-jni JARs with versions between 1.1.1 and 1.5.7-13 on production hosts, identified via software bill of materials (SBOM) or dependency scans.

Detection Strategies

  • Perform dependency scanning across build pipelines and container images to flag vulnerable zstd-jni versions before deployment.
  • Instrument JVM crash reporting to forward hs_err_pid files to a central log store and alert on native-frame crashes involving Zstandard symbols.
  • Add application-layer logging of negative or extreme int values passed to Zstandard decompression entry points.

Monitoring Recommendations

  • Monitor process restart rates for Java services that ingest compressed network payloads, correlating spikes with source IP addresses.
  • Track outbound response sizes from frame-size APIs to detect information-disclosure patterns in application logs.
  • Enable OS-level auditing of segmentation faults on hosts running Java workloads that link zstd-jni.

How to Mitigate CVE-2026-87823

Immediate Actions Required

  • Upgrade zstd-jni to version 1.5.7-14 or later across all Java applications and container images.
  • Inventory build manifests, Maven, Gradle, and SBT descriptors for transitive dependencies pulling in vulnerable zstd-jni versions.
  • Restart JVM processes after the upgrade to ensure the patched native library is loaded.

Patch Information

The fix ships in zstd-jni release v1.5.7-14. Commit d7a1c99 adds explicit src_offset < 0 and src_size < 0 guards, and commit 46dcb2a hardens the direct-ByteBuffer compress and decompress paths. Additional details are in GitHub Security Advisory GHSA-jfr6-9xqw-2g2q and the VulnCheck Advisory for zstd-jni.

Workarounds

  • If immediate patching is not possible, validate all offset and length parameters in Java wrappers before invoking Zstd.findFrameCompressedSize variants, rejecting negative values and values exceeding buffer capacity.
  • Restrict exposure of Zstandard decompression endpoints to authenticated or trusted callers using network segmentation or reverse-proxy access controls.
  • Prefer heap byte[] variants over direct-ByteBuffer variants where the application design permits until the upgrade is complete.
bash
# Maven dependency upgrade
mvn versions:use-dep-version -Dincludes=com.github.luben:zstd-jni -DdepVersion=1.5.7-14 -DforceVersion=true

# Gradle: pin the resolved version in build.gradle
# implementation 'com.github.luben:zstd-jni:1.5.7-14'

# Verify installed version inside a container image
jar tf /path/to/app.jar | grep zstd-jni

Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.