Jul 30, 2026·5 min read·57 visits
A Use-After-Free in msgpack-ruby < 1.8.2 allows cross-buffer memory aliasing, resulting in same-process information disclosure and stream corruption when buffers are cleared and reused.
A Use-After-Free (UAF) vulnerability exists in msgpack-ruby prior to version 1.8.2. The MessagePack::Buffer#clear method returns the associated 4 KiB rmem page to the shared pool but fails to reset the buffer's tracking pointers (rmem_last, rmem_end, and rmem_owner). Subsequent write operations on the cleared buffer can alias the freed page, allowing concurrent buffers to access, disclose, or corrupt cross-buffer data. This issue is resolved in version 1.8.2.
MessagePack for Ruby (msgpack-ruby) is a binary serialization library that leverages a custom memory management subsystem to minimize allocation overhead. This subsystem, known as rmem, manages 4 KiB memory pages through a global pool, bypassing the standard glibc malloc/free cycles for performance optimization. Within this framework, individual MessagePack::Buffer objects track active memory pages utilizing cursors.
The vulnerability, designated as CVE-2026-54522 (and GHSA-4mrv-5p47-p938), is a same-process Use-After-Free (UAF) flaw residing in ext/msgpack/buffer.c. When MessagePack::Buffer#clear is called, the library returns the active memory page to the shared global allocator pool. However, it fails to nullify internal cursor tracking pointers, leaving them as stale references pointing to the now-freed memory page.
Subsequent write operations on the cleared buffer can reuse these stale pointers, mapping new write data directly onto the freed page. If another independent buffer has since been assigned that same page from the global pool, both buffers will alias the same physical memory space. This memory collision enables unauthenticated cross-buffer information disclosure and data corruption within the hosting process.
The root cause of the vulnerability lies in a tracking pointer desynchronization flaw during buffer flush operations. The msgpack_buffer_t structure manages memory chunks and relies on three tracking cursors to optimize subsequent allocations: rmem_last (start of the last allocated slice), rmem_end (end boundary of the page), and rmem_owner (the specific chunk currently owning the page).
When a developer invokes MessagePack::Buffer#clear, the function internally executes a loop that shifts out chunks by calling _msgpack_buffer_shift_chunk(). If the buffer becomes completely empty, the function releases the underlying rmem page back to the global pool using the _msgpack_buffer_chunk_destroy and msgpack_rmem_free calls.
While the underlying page is successfully freed, _msgpack_buffer_shift_chunk() fails to invalidate or nullify the rmem_last, rmem_end, and rmem_owner pointers. When the same buffer executes its next write, _msgpack_buffer_chunk_malloc() evaluates these pointers to determine if cached space remains in the active page. Because the pointers are non-NULL, the allocator erroneously concludes that the buffer still owns a valid, active page, returning a slice of memory pointing into the recycled pool page.
An analysis of the vulnerable source code in ext/msgpack/buffer.c reveals the structural omission. In the vulnerable version, when the head of the buffer queue becomes NULL, the function resets the basic read pointers but leaves the rmem cursors untouched.
// Vulnerable code structure in ext/msgpack/buffer.c
bool _msgpack_buffer_shift_chunk(msgpack_buffer_t* b)
{
// ... chunk shifting logic ...
if(b->head == NULL) {
b->tail_buffer_end = NULL;
b->read_buffer = NULL;
// Missing: Nullification of rmem_end, rmem_last, and rmem_owner
return false;
}
}The patch implemented in version 1.8.2 corrects this omission by explicitly resetting the cursors to NULL, which prevents the allocation optimizer from attempting to write to a stale memory page.
// Patched code structure in ext/msgpack/buffer.c
bool _msgpack_buffer_shift_chunk(msgpack_buffer_t* b)
{
// ... chunk shifting logic ...
if(b->head == NULL) {
b->tail_buffer_end = NULL;
b->read_buffer = NULL;
/* Explicitly clear the stale rmem cursors to prevent UAF */
b->rmem_end = NULL;
b->rmem_last = NULL;
b->rmem_owner = NULL;
return false;
}
}This fix ensures that once the buffer is cleared of all its chunks, any subsequent write operations are forced to request a fresh page allocation from the pool, preventing memory aliasing.
Exploitation of CVE-2026-54522 relies on specific application design patterns, such as the pooling or reuse of MessagePack::Buffer instances across concurrent connections or tasks within a single process. In high-throughput Ruby servers, this pattern is often implemented to minimize memory overhead.
The attack sequence is initiated when an attacker sends a request that populates and subsequently clears a shared buffer instance (Buffer 1). Following the clear operation, the attacker performs a minor write to Buffer 1, triggering the use of the stale tracking pointers. In parallel, a concurrent request (Buffer 2) requests memory from the global pool, and is assigned the recycled page.
Because both buffers point to the same physical memory, reading Buffer 1 will disclose the binary serialization stream written by Buffer 2. This stream can contain sensitive parameters, user credentials, or application session state. Conversely, writing to Buffer 1 will overwrite the active memory of Buffer 2, corrupting its serialized output.
The impact of CVE-2026-54522 is primarily characterized by information disclosure and data corruption. Because the custom slab allocator manages raw bytes rather than native Ruby objects directly, typical exploitation results in same-process data leaks rather than remote code execution.
In multi-tenant environments where a single Ruby worker process handles sequential sessions using a pooled buffer, an attacker can extract session keys, database records, and authentication tokens belonging to concurrent users. This breaks logical tenant boundaries at the database/application layer.
The stream corruption aspect also poses a integrity threat. An attacker can selectively inject corrupted serialized payloads into concurrent streams, causing downstream application exceptions, logic bypasses, or data storage corruption when the invalid payloads are deserialized and committed to databases.
The primary and recommended remediation is to upgrade the msgpack gem to version 1.8.2 or later, which contains the fix that invalidates stale pointers upon buffer clearing.
If immediate patching is not possible, applications must be modified to eliminate buffer reuse patterns. Developers must instantiate a new MessagePack::Buffer object for each distinct serialization lifecycle rather than calling the #clear method on a shared instance.
# Vulnerable Pattern
@shared_buffer.clear
@shared_buffer.write(data)
# Mitigated Pattern (Temporary Workaround)
local_buffer = MessagePack::Buffer.new
local_buffer.write(data)Standard deserialization endpoints, such as MessagePack.unpack(bytes), do not invoke this reuse code path and are not vulnerable to this memory aliasing flaw.
CVSS:4.0/AV:L/AC:L/AT:P/PR:N/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
msgpack-ruby MessagePack | < 1.8.2 | 1.8.2 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-416 (Use After Free) |
| Attack Vector | Local (AV:L) |
| CVSS v4.0 Score | 2.1 (Low) |
| Exploit Status | Proof-of-Concept Available |
| Impact | Information Disclosure / Stream Corruption |
| KEV Status | Not Listed |
Referencing memory after it has been freed can cause a program to crash, use unexpected values, or execute arbitrary code.
Nginx UI versions 2.2.0 through 2.5.10 fail to properly configure Gin framework trusted proxies when deployed behind a reverse proxy. This causes all incoming HTTP requests to be attributed to the loopback IP (127.0.0.1), enabling IP allowlist bypass and global authentication lockouts.
Nginx UI versions 2.5.0 through 2.5.10 contain an uncontrolled resource consumption vulnerability in the node authentication handler. Unauthenticated remote attackers can exhaust host disk storage and I/O resources by submitting large HTTP request bodies to node-signature endpoints prior to cryptographic signature validation.
Vikunja versions 2.3.0 through 2.6.0 contain an insufficient session expiration vulnerability (CWE-613) within the WebSocket authentication handler. Although Vikunja enforces server-side session tracking and revocation for REST API routes, the WebSocket handshake handler validates cryptographic JWT signatures without querying the database session state. Consequently, revoked JWT tokens can establish new real-time WebSocket connections, and existing connections persist after session revocation.
A cross-tenant boundary breach vulnerability in Vikunja allows an authenticated user to trigger global task position recalculations across all tenant instances by creating a saved filter with an empty filter string payload.
An access revocation flaw in Vikunja allows removed collaborators to retain outbound webhooks and link shares created prior to revocation, enabling persistent exfiltration of sensitive task data.
Vikunja v2.6.0 contains a permission inheritance regression in pkg/models/project_access.go where explicit down-restrictions on sub-projects are overridden by higher parent project permissions due to MAX aggregation across project tree nodes.