GitHub
07/27/2026, 12:13 AMJSON.dump(obj, io) and JSON::State#generate(obj, io) can write past the
internal JSON generator buffer when a streamed object contains an
attacker-controlled string near 16 KB. The issue is a heap out-of-bounds write
in the IO-streaming path and is demonstrated as a reliable process crash /
denial of service.
This was triaged on HackerOne as report #3785370. The issue was confirmed there
and I was asked to open it here.
Details
Root cause is in ext/json/fbuffer/fbuffer.h, fbuffer_do_inc_capa().
On the IO path, the buffer is grown to FBUFFER_IO_BUFFER_SIZE (16383), but the
early return checks total capacity instead of remaining capacity:
if (RB_UNLIKELY(fb->io)) {
if (fb->capa < FBUFFER_IO_BUFFER_SIZE) {
fbuffer_realloc(fb, FBUFFER_IO_BUFFER_SIZE);
} else {
fbuffer_flush(fb);
}
if (RB_LIKELY(requested < fb->capa)) {
return;
}
}
If fb->len already contains JSON syntax bytes, and a string flush has
16383 - fb->len <= requested < 16383, this check returns even though there is
not enough space left. fbuffer_append_reserved() then writes past the buffer:
MEMCPY(fb->ptr + fb->len, newstr, char, len);
The minimal fix is to compare against the remaining capacity:
- if (RB_LIKELY(requested < fb->capa)) {
+ if (RB_LIKELY(requested <= fb->capa - fb->len)) {
return;
}
PoC
require "json"
require "stringio"
io = StringIO.new
big = "a" * 16385
big[16382] = '"' # escapable byte near the buffer boundary
JSON.dump([big], io)
Verified results:
Ruby 4.0.5 / bundled json 2.18.0:
malloc(): invalid size (unsorted)
.../json/common.rb:956: [BUG] Aborted
ruby/ruby master c78418b7a0 / json 2.19.8 / ASan:
heap-buffer-overflow WRITE of size 16382
fbuffer_append_reserved ext/json/fbuffer/fbuffer.h:145
search_flush ext/json/generator/generator.c:139
convert_UTF8_to_JSON ext/json/generator/generator.c:231
raw_generate_json_string ext/json/generator/generator.c:922
cState_m_generate ext/json/generator/generator.c:1891
Control: the same data through JSON.dump([big]) without an IO argument returns
normally. The bug is specific to the IO-streaming path.
Impact
A remote attacker can trigger a heap out-of-bounds write if they control a
string field that an application serializes through JSON.dump(obj, io) or
JSON::State#generate(obj, io). The demonstrated impact is reliable denial of
service. I am not claiming code execution or information disclosure.
Severity
• CVSS Score: 3.7 / 10 (Low)
• Vector String: CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L
References
• https://github.com/ruby/json/security/advisories/GHSA-x2f5-4prf-w687
• https://nvd.nist.gov/vuln/detail/CVE-2026-54696
• https://github.com/ruby/json/commit/996bac686d64e4e3aaeae03b14a7f9ee9695ebdb
• https://github.com/ruby/json/releases/tag/v2.19.9
• https://github.com/rubysec/ruby-advisory-db/blob/master/gems/json/CVE-2026-54696.yml
• https://github.com/advisories/GHSA-x2f5-4prf-w687
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
---
Configuration
📅 Schedule: (UTC)
• Branch creation
• At any time (no schedule defined)
• Automerge
• At any time (no schedule defined)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻️ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
---
• If you want to rebase/retry this PR, check this box
---
This PR was generated by <https://mend.io/renovate/|…
pact-foundation/pact-standaloneGitHub
08/10/2026, 11:17 PM