CVE-2026-87825 in zstd-jni
Summary
by MITRE • 09/09/2026
zstd-jni before 1.5.7-14 contains a use-after-free vulnerability where streams and contexts hold a dictionary's shared lock only during the load call, allowing the dictionary to be closed while still referenced. Attackers can close a dictionary after associating it with a stream or context, causing subsequent read or write operations to access freed native memory, resulting in silent data corruption or JVM crashes.
If you want to get best quality of vulnerability data, you may have to visit VulDB.
Analysis
by VulDB Data Team • 09/09/2026
The vulnerability identified in zstd-jni versions prior to 1.5.7-14 represents a critical use-after-free condition rooted in improper resource management of shared locks during dictionary loading operations. In the context of high-performance compression libraries, dictionaries are often used to improve compression ratios for repetitive data patterns by providing reference material that the compressor can match against. The zstd-jni library serves as a Java Native Interface bridge, allowing Java applications to leverage the underlying C implementation of the Zstandard algorithm. Within this architecture, native memory management is handled explicitly rather than through garbage collection, which places a higher burden on developers and library maintainers to ensure that object lifecycles are correctly synchronized between the managed Java heap and the unmanaged native heap.
The technical flaw arises from the specific scope in which the shared lock protecting the dictionary's internal state is acquired and released. During the load call, where a dictionary is associated with a compression or decompression stream, the library acquires a reference to the dictionary object and holds its shared lock only for the duration of that initialization routine. Once the load operation completes, the lock is released while the stream or context continues to maintain an internal pointer to that same dictionary instance. This creates a temporal gap where the Java garbage collector or explicit user code can invoke close methods on the original dictionary object. Because the native side still holds references to this now-closed resource, any subsequent read or write operation performed by the stream will attempt to access memory regions that have already been deallocated and potentially reallocated for other purposes.
This architectural oversight leads to severe operational impacts ranging from silent data corruption to catastrophic application failures. When a thread attempts to compress or decompress data using a stream linked to a freed dictionary, it reads invalid native memory. If the memory has not yet been reused by another process, the operation may proceed with stale or null data, leading to corrupted output files that are difficult to diagnose because no immediate exception is thrown at the Java level. More commonly, if the memory has been reallocated and modified, the application will encounter segmentation faults or access violations within the native layer. These errors typically manifest as JVM crashes, resulting in service outages for any production system relying on this library for data processing pipelines. The lack of immediate feedback makes it particularly dangerous in long-running services where corruption might go unnoticed until downstream systems reject invalid payloads.
From a classification perspective, this vulnerability aligns with CWE-416, Use After Free, which describes the risk associated with accessing memory after it has been freed. It also relates to CWE-362, Concurrent Execution using Shared Resource with Improper Synchronization, as the root cause is the failure to maintain synchronization between the resource's lifecycle and its usage by concurrent streams. In terms of adversary behavior, this flaw could be leveraged within an ATT&CK framework context under techniques involving exploitation for denial of service or potentially code execution if the memory corruption can be precisely controlled to overwrite function pointers or return addresses in native structures, although the primary observed impact is stability degradation and data integrity loss.
Mitigation strategies must focus on both immediate patching and long-term architectural improvements. The most direct remediation is to upgrade zstd-jni to version 1.5.7-14 or later, where the maintainers have corrected the locking scope to ensure that dictionaries remain locked for as long as any stream holds a reference to them. For organizations unable to patch immediately due to dependency constraints, implementing strict lifecycle management in application code is essential. Developers should avoid closing dictionary objects while they are still associated with active compression or decompression streams. Additionally, enabling native memory debugging tools and heap analysis can help identify premature deallocations during testing phases. Future implementations of JNI wrappers for C libraries should adopt reference counting mechanisms at the native level to track how many Java-side handles point to a specific native resource, ensuring that resources are only freed when all references have been relinquished.