CVE-2026-54500: Verified Repro With Script Download
CVE-2026-54500: Oj Ruby gem uninitialized stack memory leak via long JSON keys
CVE-2026-54500 is verified against the affected target. This medium reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00208.
What Is CVE-2026-54500?
CVE-2026-54500 is a medium-severity information disclosure vulnerability (CWE-125 out-of-bounds read, CWE-908 uninitialized resource) in the Oj Ruby JSON gem. Parsing a JSON object with a key 254 bytes or longer in :object mode leaks uninitialized process stack memory. Pruva reproduced it (reproduction REPRO-2026-00208).
CVE-2026-54500 Severity & CVSS Score
CVE-2026-54500 is rated medium severity, with a CVSS base score of 5.3 out of 10.
Medium — meaningful risk under specific conditions. Schedule a fix in the normal cycle.
How to Reproduce CVE-2026-54500
pruva-verify REPRO-2026-00208 curl -O https://pruva.dev/api/v1/reproductions/REPRO-2026-00208/artifacts/bundle/repro/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh Proof of Reproduction for CVE-2026-54500
- reached the target end-to-end
- on the real production code path
- high confidence
- the upstream fix blocks the same trigger
JSON object key >= 254 bytes (300 'A' chars) supplied to Oj.load in :object mode
- Oj.load(json, mode: :object)
- object.c:oj_set_obj_ivar
- intern.c:oj_attr_intern
- cache.c:cache_intern (bypasses cache, len>=35)
- intern.c:form_attr long-key path: rb_intern3(uninitialized buf, len+1) reads stack memory
No bypass or distinct alternate trigger found. The fix (intern.c buf->b, commit bbde91a, v3.17.3) fully closes the only reachable copy of the form_attr uninitialized-stack-memory-read sink (Oj.load :object mode -> object.c -> oj_attr_intern -> intern.c form_attr). The duplicate copy in usual.c was already fixed earlie…
How the agent worked
Root Cause and Exploit Chain for CVE-2026-54500
Oj (Optimized JSON), a Ruby gem with a C extension, contains an uninitialized stack
memory read in ext/oj/intern.c's form_attr() function. When Oj.load parses a
JSON object in :object mode whose key is 254 bytes or longer, the long-key code
path allocates a heap buffer b, correctly fills it with the attribute name, then
frees it — but passes the uninitialized 256-byte stack buffer buf (not b) to
rb_intern3(). Ruby therefore interns len + 1 bytes of uninitialized stack memory
(and, for keys ≥ 256 bytes, reads out of bounds past buf). The leaked bytes surface
to the caller via the produced Symbol or via the EncodingError message raised when
the stack garbage is not valid UTF-8, disclosing process stack contents. The fix is a
single-character change: rb_intern3(buf, ...) → rb_intern3(b, ...).
- Package/component:
ohler55/oj— C extension,ext/oj/intern.c,form_attr() - Affected versions: Oj 0.0.1 – 3.17.2 (fixed in 3.17.3)
- Risk level: Medium
- Consequences: Information disclosure of process stack memory. An attacker who
controls the JSON input (a key ≥ 254 bytes) can cause
Oj.loadto read and surface uninitialized stack bytes. The leak is observable through theEncodingErrorexception message (which embeds the invalid bytes) or through the produced Symbol object. The exact bytes and message length vary between process invocations, confirming the source is uninitialized (non-deterministic) memory.
Impact Parity
- Disclosed/claimed maximum impact: Uninitialized stack memory read / out-of-bounds read, leaking process stack contents via Symbol or EncodingError message.
- Reproduced impact from this run: Uninitialized stack memory read confirmed.
Every vulnerable run raised an
EncodingErrorwhose message contained 1262–1423 bytes of non-input (leaked stack) data, with message lengths varying across runs (1276–1432 bytes). The fixed version produced the correct, deterministic attribute name with zero leaked bytes. - Parity:
full— the disclosed information-disclosure symptom (uninitialized stack memory surfacing via the EncodingError message, with per-run variation) was reproduced exactly, and the negative control on the fixed commit confirmed the fix. - Not demonstrated: No code execution was claimed or demonstrated; this is an information-disclosure / memory-read bug, not a code-execution vulnerability.
Root Cause
In ext/oj/intern.c, form_attr(const char *str, size_t len) converts a JSON object
key into a Ruby attribute ID (interned symbol). It declares a 256-byte stack buffer
buf (uninitialized) and branches on key length:
static VALUE form_attr(const char *str, size_t len) {
char buf[256]; // UNINITIALIZED
if (sizeof(buf) - 2 <= len) { // long-key path: len >= 254
char *b = OJ_R_ALLOC_N(char, len + 2); // heap buffer
ID id;
// ... b is filled correctly with '@' + key + '\0' ...
id = rb_intern3(buf, len + 1, oj_utf8_encoding); // BUG: reads `buf`, not `b`
OJ_R_FREE(b);
return id;
}
// short-key path: buf IS properly filled before use (correct)
...
return (VALUE)rb_intern3(buf, len + 1, oj_utf8_encoding);
}
In the long-key path, b is the correctly-populated heap buffer, but rb_intern3 is
called with buf — the uninitialized stack buffer. rb_intern3 reads len + 1 bytes
from buf. When len >= 256, this also reads out of bounds past the 256-byte buf.
The bytes are interned as a symbol; if they are not valid UTF-8, Ruby raises an
EncodingError whose message includes the offending bytes, leaking them to the caller.
This is a duplicate of an earlier fix in ext/oj/usual.c that was missed in intern.c.
Call path: Oj.load(json, mode: :object) → object.c:oj_set_obj_ivar() →
intern.c:oj_attr_intern() → cache.c:cache_intern() → intern.c:form_attr().
Since CACHE_MAX_KEY is 35, keys ≥ 35 bytes bypass the cache and call form_attr
directly every time, so the uninitialized read occurs on every invocation with a
long key.
Fix commit: bbde91a679728f94c4492ebc3683f4fa3309049f ("Fix intern.c and fast.c
(#1015)") — changes rb_intern3(buf, len + 1, oj_utf8_encoding) to
rb_intern3(b, len + 1, oj_utf8_encoding) in the long-key path of form_attr().
Reproduction Steps
- Reference:
bundle/repro/reproduction_steps.sh(self-contained, idempotent). - What the script does:
- Installs Ruby + build tools, clones (or reuses)
ohler55/oj. - Checks out the vulnerable commit
495cc38(v3.17.2, parent of the fix), builds the C extension viaruby extconf.rb && make. - Runs
Oj.load('{"^o":"Oj::Bag","AAA...300...AAA":1}', mode: :object)in 6 separate Ruby processes. The^o:Oj::Bagmarker creates a non-Hash object so thatoj_set_obj_ivar→oj_attr_intern→form_attris invoked. - Checks out the fixed commit
bbde91a, rebuilds, and runs the same probe 6 times as a negative control. - Compares results, writes
runtime_manifest.json, and exits 0 if confirmed.
- Installs Ruby + build tools, clones (or reuses)
- Expected evidence:
- Vulnerable: all runs raise
EncodingError; message lengths vary per run (1276–1432 bytes), with 1262–1423 non-A(leaked stack) bytes. - Fixed: all runs return an
Oj::Bagwith a single 301-byte instance variable@AAA...(0x40 + 300×0x41), deterministic across all runs.
- Vulnerable: all runs raise
Evidence
- Log:
bundle/logs/reproduction_steps.log— full build + probe transcript. - Vulnerable outcomes:
bundle/logs/vuln_outcomes.txt - Fixed outcomes:
bundle/logs/fixed_outcomes.txt - Message-length variation:
bundle/logs/vuln_msg_lengths.txt - Probe script:
bundle/repro/probe.rb - Runtime manifest:
bundle/repro/runtime_manifest.json
Key excerpts (from the second verification run)
Vulnerable (commit 495cc38, v3.17.2) — all 6 runs leak:
[vuln run 1] encoding_error MSG_LEN=1348 NON_A_BYTES=1339
[vuln run 2] encoding_error MSG_LEN=1349 NON_A_BYTES=1341
[vuln run 3] encoding_error MSG_LEN=1350 NON_A_BYTES=1343
[vuln run 4] encoding_error MSG_LEN=1276 NON_A_BYTES=1262
[vuln run 5] encoding_error MSG_LEN=1432 NON_A_BYTES=1423
[vuln run 6] encoding_error MSG_LEN=1368 NON_A_BYTES=1343
The EncodingError message begins invalid symbol in encoding UTF-8 :" followed by
Ruby \xNN escapes of the leaked stack bytes (e.g. \xB8\xFF, \xD8\xFF, \xC0\xFF)
— these are pointers/binary data, not the 0x41 (A) input bytes. The message length
varies across runs (1348–1432), which is impossible for deterministic, initialized
data and confirms the source is uninitialized stack memory.
Fixed (commit bbde91a) — all 6 runs clean:
[fixed run 1] parsed IVAR_LEN=301 CORRECT_ATTR=true FIRST_BYTES=40414141...
[fixed run 2] parsed IVAR_LEN=301 CORRECT_ATTR=true FIRST_BYTES=40414141...
... (identical for all 6 runs)
FIRST_BYTES = 40 (@) + 41 (A) repeated — the correct, deterministic
attribute name. No EncodingError, no leaked bytes.
Environment
- Ruby 3.3.8 (x86_64-linux-gnu), GCC 15.2.0, Ubuntu.
- Oj built from source at vulnerable commit
495cc38and fixed commitbbde91a.
Recommendations / Next Steps
- Upgrade to Oj 3.17.3+ which contains the one-character fix.
- Audit
ext/oj/usual.cand any other copies of theform_attrpattern for the samebuf/bconfusion (this was already a duplicate of ausual.cfix). - Add a regression test that parses a JSON object with a ≥ 254-byte key in
:objectmode and asserts the resulting attribute name matches the input. - Consider compiling with
-ftrivial-auto-var-init=patternto make uninitialized reads more visible in CI, and enabling MSan/ASan in the test suite.
Additional Notes
- Idempotency: The script was run twice consecutively; both runs exited 0 with
CONFIRMED=true. The script cleans all build artifacts between vulnerable/fixed builds (git clean -fdx ext/oj lib/oj) and uses a manualextconf.rb + makeflow (avoidingrake compile, which loads bundler and can interfere with the git checkout state). - Key-length boundary: The bug triggers at
len >= 254(sizeof(buf) - 2 = 254). Atlen >= 256the read also goes out of bounds past the 256-bytebuf. The reproduction uses a 300-byte key to exercise both the uninitialized read and the OOB read. - Cache bypass: Because
CACHE_MAX_KEY = 35, the 300-byte key bypasses the attribute cache entirely, soform_attris called fresh on every invocation — maximizing the observable per-run variation.
Variant Analysis & Alternative Triggers for CVE-2026-54500
No bypass or materially distinct alternate trigger was found. CVE-2026-54500 is an
uninitialized stack-memory read in ext/oj/intern.c's form_attr() long-key path
(rb_intern3(buf, ...) should be rb_intern3(b, ...)). The fix (commit bbde91a,
v3.17.3) is a single-character change that closes the only code path that reaches
this sink: Oj.load(json, mode: :object) → object.c:oj_set_obj_ivar() →
intern.c:oj_attr_intern() → cache.c:cache_intern() → intern.c:form_attr(). An
exhaustive empirical sweep of every Oj parse mode (:object, :compat, :rails,
:strict, :null, :wab, :custom) plus the newer Oj::Parser API (:usual with
object creation, :usual Hash, symbol-cached) — each tested with a 300-byte key on
both the vulnerable commit (495cc38, v3.17.2) and the fixed commit (bbde91a,
v3.17.3) — confirms that only :object mode leaks, and only on the vulnerable
version. The duplicate copy of the same pattern in usual.c was already fixed earlier
(ec368db, #1014, an ancestor of v3.17.2), so :compat/:rails and the newer parser
were never vulnerable to this specific bug on v3.17.2. The bbde91a commit also
bundles an unrelated fast.c depth-overflow fix (doc_each_child) which is a separate
bug, not a variant of CVE-2026-54500.
Fix Coverage / Assumptions
- Invariant the fix relies on: The only way an attacker-controlled JSON key of
length ≥ 254 reaches the uninitialized-stack-buffer read is via
intern.c:form_attr()'s long-key branch. The fix changes that onerb_intern3(buf, len + 1, ...)torb_intern3(b, len + 1, ...)so the correctly-populated heap bufferbis interned instead of the uninitialized stack bufferbuf. - Code path explicitly covered:
Oj.load(..., mode: :object)→object.coj_set_obj_ivar→oj_attr_intern(intern.c:145) →cache_intern(cache.c:324, thresholdCACHE_MAX_KEY = 35) →intern.c:form_attrlong-key path (len ≥ 254). - What the fix does NOT cover (and why that is OK here):
usual.chas its ownstatic form_attr()with the identical long-key pattern. It is not touched bybbde91a, but it was already fixed inec368db(#1014 "Fix stack limits / Fix extreme key length bug"), which is an ancestor of v3.17.2. Verified:git show 495cc38:ext/oj/usual.calready containsrb_intern3(b, ...).:compat/:railsmodes dispatch tooj_compat_parse(compat.c), which usesoj_calc_hash_keyfor Hash keys andjson_createfor object creation — it never callsform_attroroj_attr_intern.:strict/:nullmodes (strict.c) intern keys withrb_intern3(parent->key, parent->klen, ...)directly from the parsed key buffer (no stack buffer).:wab(wab.c) usesoj_sym_intern→form_sym(builds a RubyStringfirst; no stack buffer).:custom(custom.c) usesoj_calc_hash_keyfor Hash keys.- The newer
Oj::ParserAPI accumulates keys in a dynamicp->keybuffer, copies them into aKeystruct (kp->buffor short, heapkp->keyfor long — both properly filled bypush_key), and for object creation reachesusual.c'sform_attrviaget_attr_id→cache_intern(d->attr_cache)(already fixed). Its:objectmode is unimplemented (// TBDplaceholder, parser.c:1263).
Variant / Alternate Trigger
No bypass or alternate trigger was confirmed. The following candidate entry points were tested empirically (4 processes each, on both vulnerable and fixed versions):
| Mode / Entry point | Reaches intern.c form_attr? |
Reaches usual.c form_attr? |
Vuln leak? | Fixed leak? |
|---|---|---|---|---|
Oj.load :object (^o:Oj::Bag) |
Yes (only path) | No | Yes (encoding_error, per-run variation) | No (correct) |
Oj.load :compat (json_class / ^o) |
No (uses oj_calc_hash_key) |
No (compat.c) | No | No |
Oj.load :compat (plain Hash) |
No | No | No | No |
Oj.load :rails |
No (dispatches to oj_compat_parse) |
No | No | No |
Oj.load :strict |
No (rb_intern3 from parsed key) |
No | No | No |
Oj.load :null |
No | No | No | No |
Oj.load :wab |
No (oj_sym_intern/form_sym) |
No | No | No |
Oj.load :custom |
No (oj_calc_hash_key) |
No | No | No |
Oj::Parser.new(:usual) + create_id (object) |
No | Yes (already fixed pre-v3.17.2) | No | No |
Oj::Parser.new(:usual) (Hash) |
No | No | No | No |
Oj::Parser.new(:usual) + create_id + cache_keys |
No | Yes (already fixed) | No | No |
Oj::Parser.new(:object) |
N/A — unimplemented (// TBD) |
N/A | N/A | N/A |
The "alternate entry point that reaches a different unfixed copy of the same sink"
candidate (usual.c form_attr) was ruled out because that copy was already fixed before
the vulnerable version was tagged. There is no third copy of the form_attr long-key
pattern in the codebase (grep for rb_intern3(b, / rb_intern3(buf, / char buf[256]
OJ_R_ALLOC_N(char, lenconfirms onlyintern.candusual.c).
- Package/component:
ohler55/oj— C extension,ext/oj/intern.c,form_attr() - Affected versions (as tested): Oj 3.17.2 (
495cc38) — vulnerable via:objectmode only; Oj 3.17.3 (bbde91a) — fixed. - Risk level: Medium (information disclosure of process stack memory).
- Consequences: An attacker controlling JSON input with a key ≥ 254 bytes can cause
Oj.load(..., mode: :object)to internlen+1bytes of uninitialized stack memory (and, for keys ≥ 256, read out of bounds past the 256-bytebuf). The leaked bytes surface via the produced Symbol/instance-variable name or via theEncodingErrormessage when the garbage is not valid UTF-8.
Impact Parity
- Disclosed/claimed maximum impact: Uninitialized stack memory read / OOB read,
leaking process stack contents via Symbol or
EncodingErrormessage. - Reproduced impact from this variant run: The original
:object-mode leak was re-confirmed on the vulnerable version (EncodingError,MSG_LENvarying 1271–1432 across runs — proving non-deterministic uninitialized memory, 1250–1426 non-Aleaked bytes). No other mode reproduced any leak on either version. - Parity:
nonefor the variant search — no additional impact path was found beyond the already-known:object-mode path, which is fully closed by the fix. - Not demonstrated: No code execution (information-disclosure bug only).
Root Cause
The root cause is a buf/b variable confusion in intern.c:form_attr(). When a key
is ≥ 254 bytes (sizeof(buf) - 2 <= len), the function allocates a heap buffer b,
correctly fills it with '@' + key + '\0', then erroneously passes the uninitialized
256-byte stack buffer buf to rb_intern3() instead of b. This is a duplicate of a
bug that was previously fixed in usual.c (commit ec368db, #1014) but missed in
intern.c. The fix commit bbde91a (#1015, "Fix intern.c and fast.c") corrects
intern.c (buf → b) and, separately, adds a MAX_STACK depth check to
fast.c:doc_each_child() (an unrelated depth-overflow bug, not a variant of this CVE).
Because oj_attr_intern (the sole caller chain to intern.c:form_attr) is invoked only
from object.c:oj_set_obj_ivar, and every other mode uses different key-interning
mechanisms that do not employ the stack-buffer-then-heap-fallback pattern, the fix
completely eliminates the reachable vulnerable sink.
Fix commit: bbde91a679728f94c4492ebc3683f4fa3309049f
Earlier duplicate fix (usual.c): ec368dbe936ef0104b782e4b0f67b17d6c7276f7
Reproduction Steps
- Reference:
bundle/vuln_variant/reproduction_steps.sh(self-contained, idempotent). - What the script does:
- Resolves the durable project cache and reuses the
ohler55/ojclone. - Builds the vulnerable commit
495cc38(v3.17.2) viaruby extconf.rb && make. - Runs
probe_variant.rbfor 11 entry points (all Oj.load modes + newerOj::ParserAPI variants), 4 separate processes each, with a 300-byte key. - Builds the fixed commit
bbde91a(v3.17.3) and repeats the same 44 probe runs. - Emits a variant/bypass matrix and a verdict, writes
runtime_manifest.json, and restores the repo to the fixed commit.
- Resolves the durable project cache and reuses the
- Expected evidence:
- Vulnerable: only
objectmode producesOUTCOME=encoding_errorwith per-runMSG_LENvariation (1271–1432); all other modesOUTCOME=correct. - Fixed: all 11 modes
OUTCOME=correct(ivar_len=301 / key_len=300, deterministic0x40+0x41…/0x41…bytes); zeroencoding_error, zeroleak.
- Vulnerable: only
Evidence
- Log:
bundle/logs/vuln_variant_repro.log— full build + sweep transcript. - Vulnerable outcomes:
bundle/logs/vuln_variant_outcomes.txt - Fixed outcomes:
bundle/logs/fixed_variant_outcomes.txt - Probe script:
bundle/vuln_variant/probe_variant.rb - Runtime manifest:
bundle/vuln_variant/runtime_manifest.json - Fixed/vulnerable version identity:
bundle/logs/vuln_variant/fixed_version.txt
Key excerpts (second verification run)
Vulnerable object mode — leaks, per-run variation (uninitialized memory):
[vuln object run 1] encoding_error MSG_LEN=1271 NON_A=1250
[vuln object run 2] encoding_error MSG_LEN=1343 NON_A=1333
[vuln object run 3] encoding_error MSG_LEN=1272 NON_A=1259
[vuln object run 4] encoding_error MSG_LEN=1351 NON_A=1317
[vuln compat_obj run 1] correct hash_key KEY_LEN=300 FIRST=41414141…
Fixed — all modes clean:
[fixed object run 1] correct ivar IVAR_LEN=301 FIRST=40414141…
[fixed object run 2] correct ivar IVAR_LEN=301 FIRST=40414141…
… (identical for all 11 modes, all 4 runs)
Environment
- Ruby 3.3.8 (x86_64-linux-gnu), GCC 15.2.0, Ubuntu.
- Oj built from source at vulnerable
495cc38and fixedbbde91a.
Recommendations / Next Steps
- No additional fix is required for CVE-2026-54500; the one-character
buf→bchange inintern.cfully closes the only reachable sink. The Coding stage should ensure this change is present and consider the items below as defense-in-depth. - Consolidate the duplicate
form_attr:intern.candusual.ceach carry their own copy ofform_attr. Unifying them into a single shared function would prevent future copy-paste divergence (this exact class of bug already occurred twice). - Add a regression test parsing a JSON object with a ≥ 254-byte key in
:objectmode asserting the resulting attribute name equals the input (deterministic). - Harden the
usual.ccopy parity: note thatintern.c'sform_attrhandles a~-prefix case thatusual.c's does not — a behavioral divergence worth aligning. - Consider
-ftrivial-auto-var-init=patternand MSan/ASan in CI to make uninitialized reads deterministic and caught automatically. - The
Oj::Parser.new(:object)placeholder (// TBD) should be implemented or removed to avoid confusion; currently it silently produces a non-functional parser.
Additional Notes
- Idempotency: The script was run twice consecutively; both runs completed fully
(exit 1 = no bypass found) without crashing, and restored the repo to
bbde91a. - Threat model:
SECURITY.mdis a generic template (supported versions + reporting process) with no explicit exclusions for memory-safety classes; the bug is within scope as an information-disclosure vulnerability reachable from attacker-controlled JSON input. - Scope discipline: The
fast.cdoc_each_childdepth-overflow fix bundled in the same commit (bbde91a) is a separate bug (different root cause, different sink, different impact class) and was deliberately not claimed as a variant of CVE-2026-54500, per the rule that separate bugs are not bypasses of an unrelated fix. - Bounded search justification: Fewer than 3 "real" bypass candidates exist because
static analysis (mode dispatch in
oj.c:1255-1261,rb_intern3/form_attrcall-site enumeration,usual.cfix ancestry) proves there is exactly one reachable copy of the vulnerable sink (intern.c form_attr) and one already-fixed copy (usual.c form_attr). All remaining modes use key-interning paths without the stack-buffer-then-heap-fallback pattern. The 11-mode empirical sweep confirms this exhaustively.
CVE-2026-54500 Reproduction Transcript
The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.
Full session Replay every step — scrub the timeline or play it back.
ls -la /data/pruva/project-cache/dc167dac-a6d2-43f6-837d-84c9d571596f/ 2>/dev/null && echo "---REPO---" && ls -la /data/pruva/project-cache/dc167dac-a6d2-43f6-837d-84c9d571596f/repo/ 2>/dev/null && echo "---MIRRORS---" && ls -la /data/pruva/project-cache/dc167dac-a6d2-43f6-837d-84c9d571596f/repo-mirrors/ 2>/dev/nulltotal 0 drwxr-xr-x 1 vscode vscode 24 Jul 2 17:17 . drwxr-xr-x 1 vscode vscode 1390 Jul 2 17:17 .. drwxr-xr-x 1 vscode vscode 0 Jul 2 17:17 repo-mirrors ---REPO---
which ruby; ruby --version 2>/dev/null; which gem; gem --version 2>/dev/null; which bundle 2>/dev/null; which gcc; gcc --version 2>/dev/null | head -1/usr/bin/gcc gcc (Ubuntu 15.2.0-16ubuntu1) 15.2.0
sudo apt-get update -qq 2>&1 | tail -3; echo "===INSTALL RUBY==="; sudo apt-get install -y ruby ruby-dev build-essential 2>&1 | tail -15===INSTALL RUBY=== Setting up ruby-webrick (1.9.2-1) ... Setting up libjs-jquery (3.7.1+dfsg+~3.5.33-1build1) ... Setting up ruby-did-you-mean (2.0.0-1) ... Setting up ruby-xmlrpc (0.3.3-2build1) ... Setting up ruby3.3 (3.3.8-2ubuntu3) ... Setting up libruby:amd64 (1:3.3build1) ... Setting up ruby (1:3.3build1) ...
Artifacts and Evidence for CVE-2026-54500
Scripts, logs, diffs, and output captured during the reproduction.
How to Fix CVE-2026-54500
FAQ: CVE-2026-54500
How does the Oj stack-leak issue surface?
Which versions of Oj are affected by CVE-2026-54500, and where is it fixed?
How severe is CVE-2026-54500?
How can I reproduce CVE-2026-54500?
References for CVE-2026-54500
Authoritative sources for CVE-2026-54500 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.