{"id":"CVE-2026-54903","title":"Oj: Integer Overflow in Oj.load 2GB String Handling","summary":"Oj: Integer Overflow in Oj.load 2GB String Handling","severity":"high","cwe":["CWE-190"],"vendor":"oj","product":"oj","ecosystem":"rubygems","affected":["oj < 3.17.2"],"patched":["oj 3.17.3"],"published":"2026-06-19","updated":"2026-06-19","source":"GHSA","sourceUrl":"https://github.com/advisories/GHSA-475m-ph3x-64gp","references":[{"url":"https://github.com/ohler55/oj/security/advisories/GHSA-475m-ph3x-64gp"},{"url":"https://github.com/advisories/GHSA-475m-ph3x-64gp"}],"tags":["ghsa","rubygems"],"ingestedAt":"2026-06-22T13:35:24.406Z","epss":0.00428,"epssPercentile":0.3653,"slug":"CVE-2026-54903","body":"## Overview\n\n### Summary\n\n`Oj.load` is vulnerable to heap corruption when parsing a JSON string longer than 2 GB. An integer overflow in `buf_append_string` (`buf.h:61`) converts the string length to a large negative `size_t`, causing `memcpy` to copy an astronomically large amount of data out of bounds. This crashes the process and can corrupt adjacent heap memory.\n\n### Version\n\n- **Software**: oj gem\n- **Affected**: all versions with `ext/oj/buf.h` and `ext/oj/parse.c`\n- **Latest tested**: 3.17.1 (confirmed present)\n\n### Details\n\n`ext/oj/buf.h`, line 61:\n\n```c\ninline static void buf_append_string(Buf buf, const char *s, size_t slen) {\n    // ...\n    memcpy(buf->tail, s, slen);   // slen derived from 32-bit int that wrapped negative\n```\n\nIn `parse.c`, escape sequence handling computes the remaining string length as an `int`:\n\n```c\n// parse.c:402 (read_escaped_str)\nint  slen = (int)(s - str);   // ← wraps to negative when string > 2 GB\nbuf_append_string(buf, str, (size_t)slen);  // ← (size_t)(-2147483648) = 0x80000000...\n```\n\nASAN report:\n```\n==399019==ERROR: AddressSanitizer: negative-size-param: (size=-2147483648)\n    #0 __asan_memcpy\n    #1 buf_append_string  /ext/oj/buf.h:61\n    #2 read_escaped_str   /ext/oj/parse.c:402\n    #3 read_str           /ext/oj/parse.c:542\n    #4 oj_parse2          /ext/oj/parse.c:882\n    #5 oj_pi_parse        /ext/oj/parse.c:1256\n    #6 oj_object_parse    /ext/oj/object.c:701\n    #7 load               /ext/oj/oj.c:1259\n0x7f5a26ff0801 is located 1 bytes inside of 2147483657-byte region [0x7f5a26ff0800, 0x7f5aa6ff0809)\n```\n\n### Reproduce\n\n```ruby\nrequire 'oj'\nn = 1 << 31                         # 2 GB\njson = '\"' + ('A' * n) + 'A\"'  # >2GB JSON string with a trailing escape\nOj.load(json)\n```\n\n## Affected packages\n\n- `oj < 3.17.2`\n\n## Remediation\n\nUpgrade to a patched release:\n\n- `oj 3.17.3`","depth":"twilight","depthScore":41,"depthScoreParts":{"impact":41.3,"likelihood":0.1,"exploitation":0,"ransomware":0},"changes":[]}