{"schema":"threatline.record/2","record":{"id":"CVE-2026-54897","aliases":["GHSA-9ppp-w3g4-fh4q"],"summary":"Oj: Use-After-Free in Oj::Doc Iterators via Reentrant Close","details":"### Summary\n\n`Oj::Doc` iterators (`each_value`, `each_child`, `each_leaf`) are vulnerable to a heap use-after-free. When a Ruby block yielded during iteration calls `doc.close` or `d.close`, the document's heap memory is freed while the C iterator is still running. When control returns from the block, the iterator reads from the freed region, producing a use-after-free accessible from pure Ruby.\n\n### Version\n\n- **Software**: oj gem\n- **Affected**: all versions with `ext/oj/fast.c`\n- **Latest tested**: 3.17.1 (confirmed present)\n\n### Details\n\nThe iterators in `ext/oj/fast.c` follow the pattern:\n\n```c\n// fast.c:1505 (doc_each_child)\nstatic VALUE doc_each_child(VALUE self, ...) {\n    ...\n    while (cur != NULL) {\n        rb_yield(...);       // ← Ruby block executes here\n        cur = cur->next;     // ← cur is now freed if block called close()\n    }\n}\n```\n\n`rb_yield` can invoke arbitrary Ruby code, including calling `close()` on the `Doc` or any child node, which calls `ruby_sized_xfree` on the backing buffer. On return, the C code reads `cur->next` from the freed region. All three iterators are affected.\n\nASAN report (each_child variant):\n```\n==253632==ERROR: AddressSanitizer: heap-use-after-free on address 0x5210000bd080\nREAD of size 8 at 0x5210000bd080 thread T0\n    #0 doc_each_child  /ext/oj/fast.c:1505\n0x5210000bd080 is located 896 bytes inside of 4064-byte region [0x5210000bcd00, 0x5210000bdce0)\nfreed by thread T0 here:\n    #0 free\n    #1 ruby_sized_xfree  (libruby-3.3.so.3.3)\n```\n\nAll three iterators trigger the same freed region (`fd` shadow bytes):\n```\n0x5210000bd080:[fd]fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd\n```\n\n### Reproduce\n\n```ruby\nrequire 'oj'\n# each_child\nOj::Doc.open('[1,2]') { |doc| doc.each_child { |d| d.close } }\n# each_value\nOj::Doc.open('[1,2]') { |doc| doc.each_value { |v| doc.close } }\n# each_leaf\nOj::Doc.open('[1,[2]]') { |doc| doc.each_leaf { |d| d.close } }\n```","categories":["Libraries & packages"],"product":"oj","vendor":"ohler55","ecosystem":"rubygems","affected_ranges":[{"source":"GitHub Advisory Database","source_url":"https://github.com/advisories/GHSA-9ppp-w3g4-fh4q","source_modified_at":"2026-10-06T19:50:14Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"ecosystem":"rubygems","name":"oj"},"range":"<= 3.17.2"},{"source":"CVE List","source_url":"https://www.cve.org/CVERecord?id=CVE-2026-54897","source_modified_at":"2026-07-01T12:43:02.746Z","fetched_at":"2026-10-23T18:45:31.882637Z","product":"oj","vendor":"ohler55","versions":[{"version":"< 3.17.2","status":"affected"}],"defaultStatus":null},{"source":"OSV","source_url":"https://osv.dev/vulnerability/GHSA-9ppp-w3g4-fh4q","source_modified_at":"2026-10-06T20:00:05.188085239Z","fetched_at":"2026-10-23T18:45:31.882637Z","package":{"name":"oj","ecosystem":"RubyGems","purl":"pkg:gem/oj"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"3.17.3"}]}],"versions":["0.5","0.5.1","0.5.2","0.6.0","0.7.0","0.8.0","0.9.0","1.0.0","1.0.1","1.0.2","1.0.3","1.0.4","1.0.5","1.0.6","1.1.0","1.1.1","1.2.0","1.2.1","1.2.10","1.2.11","1.2.12","1.2.13","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.3.0","1.3.2","1.3.4","1.3.5","1.3.6","1.3.7","1.4.0","1.4.1","1.4.2","1.4.3","1.4.4","1.4.5","1.4.6","1.4.6a2","1.4.7","2.0.0","2.0.1","2.0.10","2.0.11","2.0.12"]}],"fixed_versions":[{"source":"GitHub Advisory Database","source_url":"https://github.com/advisories/GHSA-9ppp-w3g4-fh4q","source_modified_at":"2026-10-06T19:50:14Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"ecosystem":"rubygems","name":"oj"},"version":"3.17.3"},{"source":"OSV","source_url":"https://osv.dev/vulnerability/GHSA-9ppp-w3g4-fh4q","source_modified_at":"2026-10-06T20:00:05.188085239Z","fetched_at":"2026-10-23T18:45:31.882637Z","package":{"name":"oj","ecosystem":"RubyGems","purl":"pkg:gem/oj"},"version":"3.17.3"}],"cvss":8.7,"severity":"High","kev":{"listed":false},"epss":{"probability":0.00146,"percentile":0.03473,"score_date":"2026-10-23"},"published_at":"2026-06-30T23:22:43.648Z","modified_at":"2026-10-06T20:00:05.188085239Z","provenance":[{"source":"GitHub Advisory Database","url":"https://github.com/advisories/GHSA-9ppp-w3g4-fh4q","fetched_at":"2026-10-23T18:42:43.628194Z","source_modified_at":"2026-10-06T19:50:14Z"},{"source":"CVE List","url":"https://www.cve.org/CVERecord?id=CVE-2026-54897","fetched_at":"2026-10-23T18:45:31.882637Z","source_modified_at":"2026-07-01T12:43:02.746Z"},{"source":"OSV","url":"https://osv.dev/vulnerability/GHSA-9ppp-w3g4-fh4q","fetched_at":"2026-10-23T18:45:31.882637Z","source_modified_at":"2026-10-06T20:00:05.188085239Z"},{"source":"EPSS","url":"https://api.first.org/data/v1/epss?cve=CVE-2026-54897","fetched_at":"2026-10-23T18:42:43.628194Z","source_modified_at":"2026-10-23"}],"sources":["GitHub Advisory Database","CVE List","OSV","EPSS"],"references":[{"source":"CVE List","url":"https://github.com/ohler55/oj/security/advisories/GHSA-9ppp-w3g4-fh4q","tags":["exploit"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-54897","source":"GitHub Advisory Database"},{"url":"https://github.com/rubysec/ruby-advisory-db/blob/master/gems/oj/CVE-2026-54897.yml","source":"GitHub Advisory Database"},{"url":"https://www.cve.org/CVERecord/SearchResults?query=CVE-2026-54897","source":"GitHub Advisory Database"},{"url":"https://github.com/advisories/GHSA-9ppp-w3g4-fh4q","source":"GitHub Advisory Database"}],"locations":[],"motoko_assessment":{"status":"requires_architecture_review","automatic_remediation":false},"cve_state":"PUBLISHED","ssvc":{"exploitation":"poc","automatable":"no","technical_impact":"partial","assessed_at":"2026-07-01T12:42:39.442835Z"},"classification":{"method":"product-first software-layer rules","rules_version":"2.1","matched_on":"ecosystem","confidence":"heuristic","reviewed":false},"change_timestamp":"2026-10-06T20:00:05.188085239Z","content_hash":"bc838fede604e0133abb4e1f9479c2329a9d0ae5f1e25257fe688c26ed25de67"}}
