Use-after-free in CanvasTranslator::SetDataSurfaceBuffer via dangling reference after map clear, reachable from content process through PCanvas
Categories
(Core :: Graphics: Canvas2D, defect)
Tracking
()
| Tracking | Status | |
|---|---|---|
| firefox-esr115 | --- | unaffected |
| firefox-esr140 | --- | unaffected |
| firefox147 | --- | wontfix |
| firefox148 | --- | wontfix |
| firefox149 | + | fixed |
People
(Reporter: juny24602, Assigned: lsalzman)
References
(Regression)
Details
(6 keywords, Whiteboard: [client-bounty-form][adv-main149+])
Attachments
(3 files)
|
13.16 KB,
patch
|
Details | Diff | Splinter Review | |
|
41.56 KB,
text/plain
|
Details | |
|
48 bytes,
text/x-phabricator-request
|
dmeehan
:
approval-mozilla-beta-
dveditz
:
sec-approval+
|
Details | Review |
Root Cause
CanvasTranslator::SetDataSurfaceBuffer (gfx/layers/ipc/CanvasTranslator.cpp, line 420-430) takes a reference to an entry in std::map mDataSurfaceShmems, then on Map() failure clears the entire map via DataSurfaceBufferWillChange(0, false), and continues to use the now-dangling reference.
auto& dataSurfaceShmem = mDataSurfaceShmems[aId]; // line 420: ref
dataSurfaceShmem.mShmem = aBufferHandle.Map(); // line 421: fails
if (!dataSurfaceShmem.mShmem) {
DataSurfaceBufferWillChange(0, false); // line 424: clear()
dataSurfaceShmem.mShmem = aBufferHandle.Map(); // line 426: UAF write
if (!dataSurfaceShmem.mShmem) { // line 427: UAF read
return false;
}
}
DataSurfaceBufferWillChange(0, false) with aId=0 and aKeepAlive=false enters the else branch at line 339, iterates entries, and calls mDataSurfaceShmems.clear() (line 361).
This destroys the DataSurfaceShmem entry that dataSurfaceShmem still references. Lines 426-427 then operate on freed memory.
The move-assignment on line 426 invokes MappingBase::operator= which performs std::swap on freed mMemory and mSize members, causing both UAF read and UAF write.
Triggerability
A compromised content process can reliably trigger this by sending a MutableSharedMemoryHandle whose underlying memfd has been sealed with F_SEAL_WRITE.
The seal causes the GPU process's mmap(PROT_WRITE) to fail with EPERM, making Map() return a null mapping on both attempts while the reference-invalidation path is taken.
Tested on Firefox commit:
f8345be3cabc1944760d536c4a43192a02000406
Sun Feb 15 23:53:08 2026 +0000
Attack Scenario
A compromised content process:
- Creates a
PCanvasactor viaCanvasManagerChild. - Serializes 3 recorded events into shared memory:
CANVAS_DRAW_TARGET_CREATION(initializes translator draw target)SETCURRENTDRAWTARGET(sets active draw target)PAUSE_TRANSLATION(puts translator intoPausedstate)
- Calls
SendInitTranslatorto start GPU-side translation. - Waits for the translator to reach
Pausedstate (required bySetDataSurfaceBuffer). - Creates a 4096-byte shared memory region (memfd with
MFD_ALLOW_SEALING). - Clones the handle and seals the clone with
fcntl(F_ADD_SEALS, F_SEAL_WRITE).
The seal applies to the underlying inode, so the original handle is also affected. - Calls
SendSetDataSurfaceBuffer(1, sealed_handle). - GPU process enters
SetDataSurfaceBuffer:mDataSurfaceShmems[1]inserts a new entry and takes a C++ reference.Map()fails (EPERMdue toF_SEAL_WRITE).DataSurfaceBufferWillChange(0, false)clears the map and frees the entry.- The dangling reference is used for a second
Map()attempt: UAF write + read.
Reproduction
Build Firefox with ASan:
mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/obj-debug
ac_add_options --enable-address-sanitizer
ac_add_options --disable-jemalloc
ac_add_options --disable-crashreporter
ac_add_options --disable-elf-hack
ac_add_options --disable-install-strip
ac_add_options --enable-debug
Apply the attached patch and rebuild, then run:
./mach mochitest dom/bindings/test/test_canvas_uaf.html
Expected result: ASan reports a heap-use-after-free in the GPU process.
Updated•6 months ago
|
Updated•6 months ago
|
| Assignee | ||
Updated•6 months ago
|
| Assignee | ||
Comment 2•6 months ago
|
||
Updated•6 months ago
|
| Assignee | ||
Comment 3•6 months ago
|
||
Comment on attachment 9546258 [details]
(secure)
Security Approval Request
- How easily could an exploit be constructed based on the patch?: Not sure how exploitable this is, though it does lead to a use-after-free in the parent/GPU process.
- Do comments in the patch, the check-in comment, or tests included in the patch paint a bulls-eye on the security problem?: Unknown
- Which branches (beta, release, and/or ESR) are affected by this flaw, and do the release status flags reflect this affected/unaffected state correctly?: 147
- If not all supported branches, which bug introduced the flaw?: Bug 2002342
- 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
- Is the patch ready to land after security approval is given?: Yes
- Is Android affected?: Yes
| Assignee | ||
Updated•6 months ago
|
Comment 4•6 months ago
|
||
There's not much window between the free and the re-use to hope that another thread has reallocated that memory with something useful to overwrite, but this is a clever attack on the parent.
| Assignee | ||
Comment 5•6 months ago
|
||
Comment on attachment 9546258 [details]
(secure)
Beta/Release Uplift Approval Request
- User impact if declined/Reason for urgency: Potential use-after-free in GPU/parent process.
- Is this code covered by automated tests?: Unknown
- Has the fix been verified in Nightly?: Yes
- Needs manual test from QE?: No
- If yes, steps to reproduce:
- List of other uplifts needed: None
- Risk to taking this patch: Low
- Why is the change risky/not risky? (and alternatives if risky): Avoids holding reference during clear.
- String changes made/needed:
- Is Android affected?: Yes
(In reply to Daniel Veditz [:dveditz] from comment #4)
There's not much window between the free and the re-use to hope that another thread has reallocated that memory with something useful to overwrite, but this is a clever attack on the parent.
(Note: this still needs further verification.) An attacker may be able to widen the free-to-use window by accumulating up to ~400 entries with valid mappings in mDataSurfaceShmems (bounded by gfx.canvas.accelerated.max-data-shmems) before triggering the use-after-free.
Updated•6 months ago
|
Comment 8•6 months ago
|
||
Comment on attachment 9546258 [details]
(secure)
Rejecting beta uplift request.
Fx148 is already in release and go-live is next week. We only take sec-high bugs in the planned dot release.
Comment 9•6 months ago
|
||
Updated•6 months ago
|
Updated•6 months ago
|
Updated•6 months ago
|
Updated•6 months ago
|
Updated•6 months ago
|
Updated•6 months ago
|
Updated•6 months ago
|
Updated•5 months ago
|
Updated•1 month ago
|
Description
•