CompressionStream/DecompressionStream: MOZ_CRASH via reentrancy in ProcessTypedArraysFixed (missed variant of Bug 2044843)
Categories
(Core :: DOM: Streams, defect, P3)
Tracking
()
| Tracking | Status | |
|---|---|---|
| firefox-esr140 | --- | unaffected |
| firefox-esr153 | --- | fixed |
| firefox153 | --- | wontfix |
| firefox154 | --- | wontfix |
| firefox155 | --- | fixed |
People
(Reporter: med.mbarek.isi, Assigned: sfink)
References
Details
(Keywords: crash, csectype-dos, reporter-external)
Crash Data
Attachments
(2 files, 2 external files)
Type: Denial of service (content process crash)
Attack vector: Web content (no user interaction beyond page visit)
Platform: All
ASAN confirmation: Yes (consistent crash, 4/4 on fresh Nightly 155.0a1)
Root Cause
CompressionStreamAlgorithms::TransformCallbackImpl (dom/compression/BaseAlgorithms.cpp:40-44) calls CompressAndEnqueue() inside a ProcessTypedArraysFixed lambda. CompressAndEnqueue calls aController.Enqueue() (BaseAlgorithms.cpp:89), which is MOZ_CAN_RUN_SCRIPT. The lambda is annotated MOZ_CAN_RUN_SCRIPT_BOUNDARY, silencing the static analyzer but not preventing reentrancy.
The same pattern exists in DecompressionStreamAlgorithms::TransformCallbackImpl at BaseAlgorithms.cpp:124-128.
Reentrancy mechanism
When Enqueue() resolves a pending read request, the resolution value {value: Uint8Array, done: false} is an Object. ResolvePromiseInternal (Promise.cpp:1522) performs GetProperty("then") on the resolution value, walking up the prototype chain to Object.prototype. An attacker-defined getter on Object.prototype.then fires synchronously inside the ProcessTypedArraysFixed lambda while the buffer's length is pinned.
This is the same bug class fixed in Bug 2044843 for VideoFrame.CopyTo and ImageDecoder.Initialize. The fix there moved MaybeResolve outside the ProcessTypedArraysFixed lambda. The compression/decompression stream code was not updated.
Crash path
cs1.writable.getWriter().write(data)->TransformCallbackImpl->ProcessTypedArraysFixed(data)-> pins buffer -> lambda ->CompressAndEnqueue->Enqueue()Enqueue->ReadableStreamFulfillReadRequest->ChunkSteps->MaybeResolve({value: Uint8Array, done: false})->ResolvePromiseInternal->GetProperty("then")-> getter fires- Getter calls
cs2.writable.getWriter().write(data)->TransformCallbackImpl->ProcessTypedArraysFixed(data)on the SAME buffer - Inner
ProcessFixedData->EnsureNonInlineArrayBufferOrView-> buffer is inline (<=96 bytes) AND already pinned -> returns false MOZ_CRASH("failed to make ArrayBuffer data be stored separately")at TypedArray.h:700 -- unconditional in ALL builds including release
Impact
Any web page can crash the content process by writing a small (<=96 byte) buffer to a CompressionStream while an Object.prototype.then getter triggers a nested write. No user interaction is required beyond visiting the page. The MOZ_CRASH fires in release builds, not just debug/ASAN builds.
Suggested Fix
Move CompressAndEnqueue/DecompressAndEnqueue calls outside the ProcessTypedArraysFixed lambda, matching the fix applied to VideoFrame.CopyTo in Bug 2044843.
The most targeted fix: compress inside the lambda (Compress() doesn't run script) and move only the Enqueue loop outside:
JS::RootedVector<JSObject*> array(cx);
ProcessTypedArraysFixed(
bufferSource,
[&](const Span<uint8_t>& aData) {
Compress(cx, aData, &array, Flush::No, aRv);
});
if (!aRv.Failed()) {
for (const auto& view : array) {
JS::Rooted<JS::Value> value(cx, JS::ObjectValue(*view));
aController.Enqueue(cx, value, aRv);
if (aRv.Failed()) break;
}
}
ASAN Output (fresh Nightly 155.0a1, July 22 2026)
Key frames (full trace and PoC attached):
#0 MOZ_CrashSequence -- Assertions.h:261
#1 ArrayBufferObject::ensureNonInline -- ArrayBufferObject.cpp:3119
#4 ProcessFixedData -- TypedArray.h:664 <- INNER (nested) call
#10 CompressionStreamAlgorithms::TransformCallbackImpl -- BaseAlgorithms.cpp:40
#34 CallGetter -- NativeObject.cpp:2178 <- Object.prototype.then getter
#41 ResolvePromiseInternal -- Promise.cpp:1522 <- thenable check
#48 Read_ReadRequest::ChunkSteps
#50 ReadableStreamDefaultControllerEnqueue -- ReadableStreamDefaultController.cpp:266
#51 TransformStreamDefaultController::Enqueue
#52 CompressAndEnqueue -- BaseAlgorithms.cpp:89 <- Enqueue INSIDE lambda
#53 lambda -- BaseAlgorithms.cpp:42
#55 ProcessFixedData -- TypedArray.h:673 <- OUTER call
#61 CompressionStreamAlgorithms::TransformCallbackImpl -- BaseAlgorithms.cpp:40
Relationship to Bug 2044843
Bug 2044843 fixed this pattern in VideoFrame.CopyTo and ImageDecoder.Initialize. The dom/compression/BaseAlgorithms.cpp call sites were not updated. The July 20 2026 follow-up fix (changeset 863391 by Steve Fink) modified ensureNonInline to allow re-pinning of out-of-line buffers, but inline buffers (<=96 bytes) still hit MOZ_CRASH.
Tested on: Mozilla Firefox 155.0a1 (ASAN Nightly, July 22 2026, Taskcluster build)
| Reporter | ||
Comment 1•3 months ago
|
||
| Reporter | ||
Comment 2•3 months ago
|
||
Updated•3 months ago
|
Comment 3•3 months ago
|
||
The rating assigned by our bug rating tool (currently under development) is:
Rating: N/A
Explanation: Not a security vulnerability. The reentrancy is real (CompressionStream/DecompressionStream call Enqueue() inside the ProcessTypedArraysFixed lambda, and an Object.prototype.then getter can re-enter), but the reporter's central claim of a release-build MOZ_CRASH is inaccurate. The outer ProcessFixedData calls EnsureNonInlineArrayBufferOrView first, which MOVES a small inline (<=96B) buffer OUT-OF-LINE (ArrayBufferObject.cpp:3138+) before pinning. So when the nested write runs, the buffer is already out-of-line, and ensureNonInline takes the out-of-line branch (lines 3108-3124) which returns true in Release/late-Beta (no crash, memory-safe: the pin keeps out-of-line data stable; LengthPinner's mWasPinned prevents premature unpin). Only in Nightly/DevEdition/debug does it hit the MOZ_DIAGNOSTIC_ASSERT at line 3119 - a deliberate, safe diagnostic crash to flag unexpected reentrancy. The reporter's own ASAN trace shows the crash at ensureNonInline:3119 (the diagnostic assert), not TypedArray.h:669's MOZ_CRASH, confirming this. No memory corruption and no Release DoS. This is at most a code-quality cleanup (move Enqueue out of the lambda per Bug 2044843), not an exploitable/security issue.
Comment 4•3 months ago
|
||
Steve, do you agree that this isn't a sec issue? I'll unhide it if so. Thanks.
Updated•3 months ago
|
Updated•3 months ago
|
Updated•3 months ago
|
Updated•3 months ago
|
| Assignee | ||
Comment 5•3 months ago
|
||
(In reply to Andrew McCreight [:mccr8] from comment #4)
Steve, do you agree that this isn't a sec issue? I'll unhide it if so. Thanks.
Sorry for the delay. Yes, I agree. In fact, this is sort of a false positive of the ProcessFixed mechanism. What's going on is basically
ProcessTypedArraysFixed(source, CompressAndEnqueue);
CompressAndEnqueue(inputData) {
obj = compress(inputData);
// data is no longer used.
enqueue(obj); // can run script
}
So even if the script moves the inputData and re-enters, the old location would never be used by anything.
| Assignee | ||
Comment 6•3 months ago
|
||
Also fix up some spec references.
Updated•3 months ago
|
Comment 8•3 months ago
|
||
| bugherder | ||
Updated•3 months ago
|
Comment 9•3 months ago
|
||
The patch landed in nightly and beta is affected, along with ESR.
:sfink, is this bug important enough to require an uplift?
- If yes, please nominate the patch for beta and ESR approvals.
- See https://wiki.mozilla.org/Release_Management/Requesting_an_Uplift for documentation on how to request an uplift.
- If no, please set
status-firefox154and the ESR status flag(s) towontfix.
For more information, please visit BugBot documentation.
Updated•3 months ago
|
Updated•2 months ago
|
Comment 10•1 month ago
|
||
Did you want to nominate this for ESR153 uplift? Go ahead and do so if yes.
I'll do it because this blocks another uplift I want to do.
Also fix up some spec references.
Original Revision: https://phabricator.services.mozilla.com/D316356
Updated•1 month ago
|
Comment 13•1 month ago
|
||
firefox-esr153 Uplift Approval Request
- User impact if declined/Reason for urgency: potential content process crash on some webpage
- Code covered by automated testing?: yes
- Fix verified in Nightly?: yes
- Needs manual QE testing?: no
- Steps to reproduce for manual QE testing: N/A
- Risk associated with taking this patch: low
- Explanation of risk level: Tested in multiple stable releases
- String changes made/needed?: N/A
- Is Android affected?: yes
Updated•1 month ago
|
Updated•1 month ago
|
Comment 14•1 month ago
|
||
| uplift | ||
Updated•1 month ago
|
Description
•