Oj: Use-After-Free in Oj::Doc Iterators via Reentrant Close
Description
Summary
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.
Version
- Software: oj gem
- Affected: all versions with
ext/oj/fast.c - Latest tested: 3.17.1 (confirmed present)
Details
The iterators in ext/oj/fast.c follow the pattern:
// fast.c:1505 (doc_each_child)
static VALUE doc_each_child(VALUE self, ...) {
...
while (cur != NULL) {
rb_yield(...); // ← Ruby block executes here
cur = cur->next; // ← cur is now freed if block called close()
}
}
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.
ASAN report (each_child variant): `` ==253632==ERROR: AddressSanitizer: heap-use-after-free on address 0x5210000bd080 READ of size 8 at 0x5210000bd080 thread T0 #0 doc_each_child /ext/oj/fast.c:1505 0x5210000bd080 is located 896 bytes inside of 4064-byte region [0x5210000bcd00, 0x5210000bdce0) freed by thread T0 here: #0 free #1 ruby_sized_xfree (libruby-3.3.so.3.3) ``
All three iterators trigger the same freed region (fd shadow bytes): `` 0x5210000bd080:[fd]fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd ``
Reproduce
require 'oj'
# each_child
Oj::Doc.open('[1,2]') { |doc| doc.each_child { |d| d.close } }
# each_value
Oj::Doc.open('[1,2]') { |doc| doc.each_value { |v| doc.close } }
# each_leaf
Oj::Doc.open('[1,[2]]') { |doc| doc.each_leaf { |d| d.close } }
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
ojRubyGems | < 3.17.3 | 3.17.3 |
Affected products
26- osv-coords25 versionspkg:apk/chainguard/ruby3.2-fluentd-kubernetes-daemonset-1.16-kinesispkg:apk/chainguard/ruby3.2-fluentd-kubernetes-daemonset-1.17-kinesispkg:apk/chainguard/ruby3.2-fluentd-kubernetes-daemonset-1.18-kinesispkg:apk/chainguard/ruby3.2-fluentd-kubernetes-daemonset-1.19-kinesispkg:apk/chainguard/ruby3.2-kube-logging-operator-fluentd-outputspkg:apk/chainguard/ruby3.3-fluentd-kubernetes-daemonset-1.16-kinesispkg:apk/chainguard/ruby3.3-fluentd-kubernetes-daemonset-1.17-kinesispkg:apk/chainguard/ruby3.3-fluentd-kubernetes-daemonset-1.18-kinesispkg:apk/chainguard/ruby3.3-fluentd-kubernetes-daemonset-1.19-kinesispkg:apk/chainguard/ruby3.4-fluentd-kubernetes-daemonset-1.16-kinesispkg:apk/chainguard/ruby3.4-fluentd-kubernetes-daemonset-1.18-kinesispkg:apk/chainguard/ruby3.4-fluentd-kubernetes-daemonset-1.19-kinesispkg:apk/chainguard/ruby3.4-kube-logging-operator-fluentd-outputspkg:apk/chainguard/ruby4.0-fluentd-kubernetes-daemonset-1.16-kinesispkg:apk/chainguard/ruby4.0-fluentd-kubernetes-daemonset-1.18-kinesispkg:apk/chainguard/ruby4.0-fluentd-kubernetes-daemonset-1.19-kinesispkg:apk/wolfi/ruby3.2-fluentd-kubernetes-daemonset-1.18-kinesispkg:apk/wolfi/ruby3.2-fluentd-kubernetes-daemonset-1.19-kinesispkg:apk/wolfi/ruby3.2-kube-logging-operator-fluentd-outputspkg:apk/wolfi/ruby3.3-fluentd-kubernetes-daemonset-1.18-kinesispkg:apk/wolfi/ruby3.3-fluentd-kubernetes-daemonset-1.19-kinesispkg:apk/wolfi/ruby3.4-fluentd-kubernetes-daemonset-1.18-kinesispkg:apk/wolfi/ruby3.4-fluentd-kubernetes-daemonset-1.19-kinesispkg:apk/wolfi/ruby3.4-kube-logging-operator-fluentd-outputspkg:apk/wolfi/ruby4.0-fluentd-kubernetes-daemonset-1.19-kinesis
< 1.16.11.1.0-r3+ 24 more
- (no CPE)range: < 1.16.11.1.0-r3
- (no CPE)range: < 1.17.1.1.2-r10
- (no CPE)range: < 1.18.0.1.5-r3
- (no CPE)range: < 1.19.2.1.1-r5
- (no CPE)range: < 6.7.0-r4
- (no CPE)range: < 1.16.11.1.0-r3
- (no CPE)range: < 1.17.1.1.2-r9
- (no CPE)range: < 1.18.0.1.5-r4
- (no CPE)range: < 1.19.3.1.0-r0
- (no CPE)range: < 1.16.11.1.0-r2
- (no CPE)range: < 1.18.0.1.5-r3
- (no CPE)range: < 1.19.3.1.0-r0
- (no CPE)range: < 6.7.0-r4
- (no CPE)range: < 1.16.11.1.0-r4
- (no CPE)range: < 1.18.0.1.5-r3
- (no CPE)range: < 1.19.3.1.0-r0
- (no CPE)range: < 1.18.0.1.5-r3
- (no CPE)range: < 1.19.2.1.1-r5
- (no CPE)range: < 6.7.0-r4
- (no CPE)range: < 1.18.0.1.5-r4
- (no CPE)range: < 1.19.3.1.0-r0
- (no CPE)range: < 1.18.0.1.5-r3
- (no CPE)range: < 1.19.3.1.0-r0
- (no CPE)range: < 6.7.0-r4
- (no CPE)range: < 1.19.3.1.0-r0
Patches
Vulnerability mechanics
References
2News mentions
0No linked articles in our index yet.