GHSA-XX22-P4CH-683R

Vulnerability from github – Published: 2026-07-24 16:13 – Updated: 2026-07-24 16:13
VLAI
Summary
LZ4 Java: Native XXHash implementations can crash the JVM when passed invalid byte array ranges
Details

Summary

Insufficient validation of byte array arguments in JNI-based XXHash implementations in lz4-java 1.11.0 and earlier allows callers to crash the JVM by passing an invalid array reference or invalid range to native XXHash methods.

This affects applications where an attacker can influence the byte array object or the off / len arguments passed to affected XXHash APIs. It does not affect the common case where only the contents of a valid byte array are attacker-controlled.

Java-based XXHash implementations are not affected.

Details

The JNI-backed XXHash implementations pass caller-provided byte array arguments to native code. The affected APIs are:

  • XXHashFactory.nativeInstance().hash32().hash(byte[] buf, int off, int len, int seed)
  • XXHashFactory.nativeInstance().hash64().hash(byte[] buf, int off, int len, long seed)
  • XXHashFactory.nativeInstance().newStreamingHash32(seed).update(byte[] bytes, int off, int len)
  • XXHashFactory.nativeInstance().newStreamingHash64(seed).update(byte[] bytes, int off, int len)

Before the fix, the streaming JNI implementations did not validate bytes, off, or len before calling XXHashJNI.XXH32_update / XXHashJNI.XXH64_update. The non-streaming JNI implementations called SafeUtils.checkRange, but SafeUtils.checkRange(byte[], int, int) skipped all array access when len == 0, so a null byte array with a zero length could still reach JNI.

As a result:

  • hash(null, 0, 0, seed) and update(null, 0, 0) could pass a null array reference to JNI, causing a fatal JVM crash in GetPrimitiveArrayCritical.
  • update(new byte[16], 0, Integer.MAX_VALUE) could cause native XXHash code to read far beyond the end of the Java array, causing a fatal JVM crash and potentially exposing in-process memory to the native routine before the crash.

The oversized-length non-streaming hash(new byte[16], 0, Integer.MAX_VALUE, seed) case was already rejected in Java before this fix. The missing validation affected the streaming oversized-length case and the zero-length null-array case for both streaming and non-streaming JNI XXHash APIs.

The impact of this vulnerability depends on how user code uses the XXHash API. Code that hashes attacker-controlled byte contents in a valid, correctly bounded array is not affected. Code may be affected if an attacker can cause the application to pass a null array, an attacker-controlled offset, or an attacker-controlled length to the native XXHash API. The primary impact is denial of service due to JVM termination. For oversized lengths, native code may also read outside the Java array before the process crashes.

Mitigation

lz4-java 1.11.1 fixes this issue without requiring changes in user code.

If you cannot upgrade, avoid passing attacker-controlled array references, offsets, or lengths to JNI-backed XXHash APIs. In particular, validate that arrays are non-null and that off and len describe a range fully contained in the array before calling native XXHash methods.

Using XXHashFactory.safeInstance() avoids the JNI boundary and is not affected by this native crash behavior.

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 1.11.0"
      },
      "package": {
        "ecosystem": "Maven",
        "name": "at.yawk.lz4:lz4-java"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "1.11.1"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "Maven",
        "name": "org.lz4:lz4-java"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "last_affected": "1.8.1"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-59949"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-125",
      "CWE-476"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-07-24T16:13:41Z",
    "nvd_published_at": null,
    "severity": "MODERATE"
  },
  "details": "### Summary\n\nInsufficient validation of byte array arguments in JNI-based XXHash implementations in lz4-java 1.11.0 and earlier allows callers to crash the JVM by passing an invalid array reference or invalid range to native XXHash methods.\n\nThis affects applications where an attacker can influence the byte array object or the `off` / `len` arguments passed to affected XXHash APIs. It does **not** affect the common case where only the contents of a valid byte array are attacker-controlled.\n\nJava-based XXHash implementations are *not* affected.\n\n### Details\n\nThe JNI-backed XXHash implementations pass caller-provided byte array arguments to native code. The affected APIs are:\n\n- `XXHashFactory.nativeInstance().hash32().hash(byte[] buf, int off, int len, int seed)`\n- `XXHashFactory.nativeInstance().hash64().hash(byte[] buf, int off, int len, long seed)`\n- `XXHashFactory.nativeInstance().newStreamingHash32(seed).update(byte[] bytes, int off, int len)`\n- `XXHashFactory.nativeInstance().newStreamingHash64(seed).update(byte[] bytes, int off, int len)`\n\nBefore the fix, the streaming JNI implementations did not validate `bytes`, `off`, or `len` before calling `XXHashJNI.XXH32_update` / `XXHashJNI.XXH64_update`. The non-streaming JNI implementations called `SafeUtils.checkRange`, but `SafeUtils.checkRange(byte[], int, int)` skipped all array access when `len == 0`, so a null byte array with a zero length could still reach JNI.\n\nAs a result:\n\n- `hash(null, 0, 0, seed)` and `update(null, 0, 0)` could pass a null array reference to JNI, causing a fatal JVM crash in `GetPrimitiveArrayCritical`.\n- `update(new byte[16], 0, Integer.MAX_VALUE)` could cause native XXHash code to read far beyond the end of the Java array, causing a fatal JVM crash and potentially exposing in-process memory to the native routine before the crash.\n\nThe oversized-length non-streaming `hash(new byte[16], 0, Integer.MAX_VALUE, seed)` case was already rejected in Java before this fix. The missing validation affected the streaming oversized-length case and the zero-length null-array case for both streaming and non-streaming JNI XXHash APIs.\n\nThe impact of this vulnerability depends on how user code uses the XXHash API. Code that hashes attacker-controlled byte contents in a valid, correctly bounded array is not affected. Code may be affected if an attacker can cause the application to pass a null array, an attacker-controlled offset, or an attacker-controlled length to the native XXHash API. The primary impact is denial of service due to JVM termination. For oversized lengths, native code may also read outside the Java array before the process crashes.\n\n### Mitigation\n\nlz4-java 1.11.1 fixes this issue without requiring changes in user code.\n\nIf you cannot upgrade, avoid passing attacker-controlled array references, offsets, or lengths to JNI-backed XXHash APIs. In particular, validate that arrays are non-null and that `off` and `len` describe a range fully contained in the array before calling native XXHash methods.\n\nUsing `XXHashFactory.safeInstance()` avoids the JNI boundary and is not affected by this native crash behavior.",
  "id": "GHSA-xx22-p4ch-683r",
  "modified": "2026-07-24T16:13:41Z",
  "published": "2026-07-24T16:13:41Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/yawkat/lz4-java/security/advisories/GHSA-xx22-p4ch-683r"
    },
    {
      "type": "WEB",
      "url": "https://github.com/yawkat/lz4-java/commit/dbd86d04b8dd716e1c2bc626be54189997d910da"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/yawkat/lz4-java"
    },
    {
      "type": "WEB",
      "url": "https://github.com/yawkat/lz4-java/releases/tag/v1.11.1"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:N/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "LZ4 Java: Native XXHash implementations can crash the JVM when passed invalid byte array ranges"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Drag nodes to arrange them. Double-click a related vulnerability to open it, or a source to visit its link. The Physics button on the left tunes the layout and the simulation; the legend at the bottom left hides or shows nodes by kind and by sighting type.

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.
Vulnerability timeline

Sightings and recorded dates, grouped by day · UTC

JSON
How durations are calculated

Following the VTEM framework (Black Hat USA 2015), time to patch starts when the vendor or a coordinator was informed, total time to patch at discovery, and disclosure to patch at public disclosure; vendor response time runs from the report to its acknowledgement. Time to exploit and time unpatched start at the earliest discovery or report, falling back to public disclosure. Unpatched time ends at the first recorded patch, or the calculation time when no patch is recorded. Exploitation to patch shows the interval between those two events; a published proof of concept is a public exploit, not exploitation.

Each result shows its source dates. A negative interval means the end event came first; when date-only inputs leave the order open, the result says so. Date-only inputs are approximate. The earliest evidence wins: KEV report dates and sightings are labelled as such. Records describing another vulnerability are listed but never calculated from, and a PoC alone does not establish exploitation. Missing patch evidence does not prove that no patch exists. Filters below change the event list only.

Events included in the timeline

Loading timeline…


    Loading…

    Loading…

    Loading…

    Related by attack behaviour

    Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.