QuickJS
CVE-2025-62491
HIGH
Attack ChainAIDerived
Hypothetical attack flow derived from CVE metadata – not a confirmed exploit chain
-
AccessSupply malicious JS to QuickJS engine
-
DeliveryCreate promise rejected with Error having getter on reason
-
ExploitRejection reporter iterates list and invokes getter
-
ExecutionGetter calls catch() freeing current list entry
-
PersistLoop dereferences freed pointer (UAF)
-
ImpactCorrupt heap for code execution
Vulnerability Assessment
| Weakness |
CWE
ASVS 5.0
V15 - Secure Coding and Architecture Memory lifecycle management
MITRE ATT&CK
2 techniques across 2 tactics: Execution, Privilege Escalation The technique list with ATT&CK links and the kill-chain view is part of the Basic plan and up. |
| Exploitation Conditions | Exploitation requires that the QuickJS std library's unhandled-rejection reporting path (js_std_promise_rejection_check / js_std_dump_error1, part of quickjs-libc, as used by the qjs interpreter) is active, and that the attacker can execute JavaScript in the engine. The concrete trigger is an unhandled promise rejected with an Error object carrying a custom property getter (defined via Object.defineProperty) that, when invoked during error dumping, calls catch() on the same promise. The primary limiting factor is that the host application must evaluate untrusted/attacker-controlled JavaScript and must use the std library's rejection-tracking (this does not affect pure libquickjs embeddings that do not wire up js_std_promise_rejection_tracker); reliable UAF-to-code-execution also depends on heap grooming, making exploitation non-trivial. |
| Impact | This is a memory-safety flaw (CWE-416 Use-After-Free) in QuickJS's standard library (quickjs-libc), triggered when the unhandled-rejected-promise reporter iterates ts->rejected_promise_list and a malicious custom getter on an Error object calls catch() on the same promise, freeing the list entry still being iterated. Exploitation fundamentally requires the attacker to supply/execute JavaScript inside the QuickJS engine, so real-world risk is dominated by deployment context: it is high where an application evaluates untrusted/attacker-supplied scripts via the std library, and effectively low where only trusted local scripts run. Signals are mixed: a POC/exploit reference exists (Google issue tracker, tagged 'exploit'), the reporter is Google's CVE coordination team, and the input CVSS 4.0 base is 8.8 (High) - but there is NO CISA KEV listing and NO EPSS score provided, so there is no evidence of active or widespread exploitation. Note a conflict in the input CVSS 4.0 vector: it declares AV:A (adjacent-network), UI:P (passive user interaction) and a scope change (SC:H/SI:H), none of which the description supports - the description describes a local, attacker-controlled-script UAF with no network or remote-user element, so the provided vector appears mis-scored and should be treated with caution. No EPSS and no KEV means prioritization should rest on whether your deployment runs untrusted JS, not on the raw 8.8. |
| Exploit ScenarioDerived | An application embeds QuickJS and evaluates attacker-supplied JavaScript (e.g., a scripting sandbox, serverless JS runtime, or qjs processing an untrusted script). The attacker creates a promise that rejects with an Error whose 'reason' property has a custom getter defined via Object.defineProperty; when the engine's unhandled-rejection reporter dumps the error, the getter runs and calls catch() on that same promise, freeing the list entry mid-iteration and producing a UAF that can corrupt memory and potentially lead to arbitrary code execution in the host process. A public exploit/POC is referenced in the Google issue tracker. |
Get an e-mail when this CVE is exploited, patched or re-scored. Free account.
Alert meAnalysisAIv1 – Oct 19, 2026
A use-after-free in QuickJS's quickjs-libc standard library lets JavaScript code corrupt heap memory from inside the interpreter's own unhandled-promise reporter: when a rejected promise's reason is an Error carrying a JavaScript-defined property getter, dumping that error re-enters the script engine and a catch() call frees the promise entry that the reporter is still iterating. Per the authoritative assessment this is a local, high-complexity memory-safety issue (CVSS 3.1 AV:L/AC:H, high confidentiality and integrity impact) that requires the host to evaluate attacker-controlled JavaScript using the qjs interpreter's rejection-tracking helpers; bare libquickjs embeddings that do not register the rejection tracker are unaffected. Publicly available exploit code exists, though turning the UAF into reliable code execution additionally depends on heap grooming and makes exploitation non-trivial.
RemediationAI
The primary fix is to move to an upstream QuickJS snapshot that contains the corrected rejection reporting - check https://bellard.org/quickjs/Changelog for the entry addressing the rejected_promise_list reentrancy and rebuild quickjs-libc from that source; no exact patched version number is published in the available data, so verify the fix by re-running the reproducer at https://issuetracker.google.com/434195203 against your build. If immediate upgrade is impossible, the most effective compensating control is to not register the std rejection tracker at all: skip js_std_add_helpers/js_std_init_handlers in your embedding and handle 'unhandledRejection'-style reporting in application code that snapshots the promise list into a separate array before formatting any reason (trade-off: you lose qjs's built-in rejection diagnostics and the default global helpers installed by those calls). Where the qjs interpreter must stay in use, never feed it untrusted or attacker-controlled JavaScript, or sandbox it in a separate low-privilege process with restricted memory and no host-object bridges (trade-off: reduced functionality and added sandboxing overhead, and a crash inside the sandbox remains a denial-of-service vector). A code-level hardening alternative is to defer freeing JSRejectedPromiseEntry objects that are being iterated (or to re-check list membership before advancing el), which keeps diagnostics intact but requires maintaining a local patch against quickjs-libc. Keep heap-exploitation mitigations enabled (ASLR, hardened allocators, stack canaries) while any interim mitigation is in place, since the underlying primitive is heap corruption.
Affects your stack
Find out whether the stacks and servers you run are on an affected version.
Checking this CVE against your own stacks and servers is part of the Basic plan and up.
Technical ContextAI · show details
QuickJS is Fabrice Bellard's small embeddable JavaScript engine; the vulnerable code lives in quickjs-libc (the std library used by the bundled qjs interpreter), not in the engine core. js_std_promise_rejection_check walks the per-runtime linked list ts->rejected_promise_list with a conventional el = el->next loop, and for each entry calls js_std_dump_error1(ctx, rp->reason) to report the rejection. Because the rejection reason is an ordinary JS Error object, any property getter installed on it with Object.defineProperty (for example on message, stack, or a custom field touched during formatting) is re-entered while the dump is in progress. If that getter calls catch() on the very promise being reported, js_std_promise_rejection_tracker fires, removes and frees the corresponding JSRejectedPromiseEntry, and the outer loop then dereferences and advances through freed memory - classic CWE-416 use-after-free caused by reentrancy into code that invalidates an iteration-in-progress data structure. Consequences range from interpreter crash and information disclosure to heap corruption with the potential for code execution, depending on allocator behaviour and object reuse. The CPE cpe:2.3:a:quickjs_project:quickjs:*:*:*:*:*:*:*:* reflects that no version constraint is published, so every QuickJS snapshot shipping this reporting path should be treated as in scope until the upstream changelog entry is confirmed applied.
Know when Quickjs gets its next vulnerability
- Watch products and get an email when something new is published
- Check this CVE against your own stack and servers
- Exploit and PoC references, threat intelligence from 22 sources
No credit card · 7-day full trial
Severity by source
CVSS:4.0/AV:A/AC:H/AT:P/PR:N/UI:P/VC:H/VI:H/VA:L/SC:H/SI:H/SA:L
Attacker must supply JavaScript to the local engine (AV:L), the getter-driven UAF race is precise and hard to weaponize reliably (AC:H), no authentication to the engine is needed (PR:N), and successful memory corruption yields full process compromise (C/I/A:H).
Primary rating from Vendor (google).
CVSS VectorVendor: google
DescriptionCVE.org
A Use-After-Free (UAF) vulnerability exists in the QuickJS engine's standard library when iterating over the global list of unhandled rejected promises (ts->rejected_promise_list).
- The function js_std_promise_rejection_check attempts to iterate over the rejected_promise_list to report unhandled rejections using a standard list loop.
- The reason for a promise rejection is processed inside the loop, including calling js_std_dump_error1(ctx, rp->reason).
- If the promise rejection reason is an Error object that defines a custom property getter (e.g., via Object.defineProperty), this getter is executed during the error dumping process.
- The malicious custom getter can execute JavaScript code that calls catch() on the same rejected promise being processed.
- Calling catch() internally triggers js_std_promise_rejection_tracker, which then removes and frees the current promise entry (JSRejectedPromiseEntry) from the rejected_promise_list.
- Since the list iteration continues using the now-freed memory pointer (el), the subsequent loop access results in a Use-After-Free condition.
Executive ViewAI
QuickJS's quickjs-libc library contains a memory-safety flaw that can let attacker-supplied JavaScript corrupt heap memory in hosts that use the qjs interpreter's unhandled-promise rejection-tracking path. This matters to leadership wherever QuickJS is embedded in products, sandboxes, or services that process untrusted scripts, because successful exploitation could undermine confidentiality and integrity, though the attack is local, high-complexity, and publicly available exploit code exists.
No vendor-released patch identified at time of analysis; disable or avoid qjs unhandled-promise rejection tracking when evaluating untrusted JavaScript, run qjs in a hardened sandbox with least privilege, restrict script input to trusted sources, and monitor for crashes or memory-corruption indicators.
Threat Intelligence Overview
How this decision was reached
The engine reads each signal below and applies a fixed list of rules from top to bottom. The first rule that matches sets the recommended action – like a statement, every line is shown.
What every action level means
SSVC AssessmentENISA
Priority Score
Attack Technique
Use-after-free vulnerabilities occur when a program continues to access memory through a pointer after that memory has been deallocated.
Memory corruption occurs when a program writes data beyond the boundaries of allocated memory regions or accesses memory in unintended ways, violating the integrity of the process's address space.
Information disclosure occurs when an application unintentionally exposes sensitive data that aids attackers in reconnaissance or directly compromises security.
Affected Products (CPE)CVE.org
Use-after-free in QuickJS object-printing routines can corrupt memory when an attacker-influenced Array, Map, or Set is
QuickJS commit 2788d71 was discovered to contain a stack-overflow via the component js_proxy_isArray at quickjs.c. Rated
Heap out-of-bounds write in QuickJS's BigInt string parser can corrupt memory when an embedding application converts an
Integer overflow in QuickJS's libregexp bytecode accounting can become an out-of-bounds write when a crafted recursive r
Type confusion in QuickJS's handling of the string addition (+) operation allows local attackers to trigger out-of-bound
Information disclosure in QuickJS occurs through a floating-point precision error in TypedArray.prototype.indexOf() when
An out-of-bounds heap read in QuickJS's BigInt-to-string conversion (js_bigint_to_string1) lets script-controlled BigInt
Heap out-of-bounds write in QuickJS's bytecode deserializer (JS_ReadFunctionTag) lets a crafted serialized blob corrupt
quickjs-ng through 0.9.0 has a missing length check in JS_ReadString for a string, leading to a heap-based buffer overfl
quickjs-ng through 0.9.0 has an incorrect size calculation in JS_ReadBigInt for a BigInt, leading to a heap-based buffer
Heap-based buffer overflow in QuickJS up to version 0.11.0 within the js_typed_array_constructor function allows unauthe
A vulnerability was found in quickjs-ng QuickJS up to 0.8.0. Rated medium severity (CVSS 5.3), this vulnerability is rem
NVD
CVE.org
EPSS
GitHub PoC
Exploit-DB