WebRenderBridgeParent missing OpDestroy PTexture dedup (variant of Bug 2017894) - UAF with vtable hijack in parent process
Categories
(Core :: Graphics: WebRender, defect, P2)
Tracking
()
People
(Reporter: kerberoasting, Assigned: bradwerth)
References
Details
(Keywords: reporter-external, Whiteboard: [client-bounty-form])
Attachments
(1 file)
|
3.00 KB,
application/x-zip-compressed
|
Details |
Howdy. WebRenderBridgeParent processes OpDestroy arrays without deduplicating PTexture entries — the same bug fixed in ImageBridgeParent by Bug 2017894 (commit 276f54921e, March 10 2026). A compromised content process sends
SetDisplayList or EmptyTransaction with duplicate PTexture actor IDs in the OpDestroy[] array. The first DestroyActor call frees the PTextureParent actor via Send__delete__ → DeallocPTextureParent → delete. The second call dispatches
a virtual function through the freed object's vtable pointer, giving attacker control of the instruction pointer.
Controlled vtable hijack demonstrated:
Thread 100 "Compositor" received signal SIGSEGV, Segmentation fault.
0x00007fffe3d09616 in ParentActor<PTextureParent>::DestroyIfNeeded()
at gfx/layers/IPDLActor.h:46
46 Destroy();
rax 0x4141414141414141 ← attacker-controlled vtable pointer
rbx 0x7fffb5535510 ← freed PTextureParent object
rax holds the vtable pointer read from overwritten freed memory. The crash is at the virtual Destroy() call in ParentActor::DestroyIfNeeded() (IPDLActor.h:46), proving the attacker controls the dispatch target.
Impact:
On Linux (both X11 and Wayland), layers.gpu-process.enabled defaults to false (StaticPrefList.yaml:9804-9814). The Compositor thread runs in the parent/UI process. Virtual call through attacker-controlled vtable = code execution in the parent process from content sandbox.
Three vulnerable code paths, all missing the dedup that ImageBridgeParent has:
- AutoWebRenderBridgeParentAsyncMessageSender destructor — WebRenderBridgeParent.cpp:327
- RecvSetDisplayList() early-return — WebRenderBridgeParent.cpp:1334
- RecvEmptyTransaction() early-return — WebRenderBridgeParent.cpp:1471
Compare to the fixed code in ImageBridgeParent.cpp:183-198 which uses nsTHashSet<PTextureParent*> seenTextureParents with EnsureInserted() to skip duplicates.
How was this issue discovered?
Variant analysis of Bug 2017894. That fix added PTexture dedup to ImageBridgeParent's destroy loop. Searched for the same pattern in other bridge parents and found WebRenderBridgeParent has identical loops with no dedup.
What the PoC does:
-
Child side patch (WebRenderBridgeChild.cpp): AddOpDestroy() duplicates PTexture entries in the mDestroyedActors array before it is sent via SetDisplayList IPC. This simulates a compromised content process including duplicate actor IDs in the serialized message.
-
Parent side patch (WebRenderBridgeParent.cpp): When duplicate PTexture entries are detected, the first DestroyActor() frees the actor normally. Then memset overwrites the freed memory with a fake vtable pointer (0x4141414141414141)
to simulate heap reclaim by attacker spray. The second DestroyActor() crashes at the virtual Destroy() call through the controlled vtable. Without the memset, the UAF is silent because jemalloc retains stale guard values - ASAN would detect it cleanly. -
trigger.html: Creates and destroys ImageBitmap + canvas elements to force PTexture allocation and destruction through WebRender transactions.
Steps to reproduce:
1. Apply PoC patch
cd mozilla-central
patch -p1 < poc_crash.patch
2. Build
./mach build
3. Create profile
mkdir /tmp/poc-profile
cat > /tmp/poc-profile/user.js << 'EOF'
user_pref("gfx.webrender.software", true);
user_pref("gfx.webrender.all", true);
user_pref("layers.gpu-process.enabled", false);
user_pref("browser.tabs.remote.autostart", true);
user_pref("browser.shell.checkDefaultBrowser", false);
EOF
4. Run under gdb
xvfb-run -a -s "-screen 0 1280x1024x24" \
gdb -batch -ex run -ex "bt 10" -ex "info registers" -ex quit
--args ./obj-debug/dist/bin/firefox \
-no-remote -profile /tmp/poc-profile
file:///path/to/trigger.html
Expected: SIGSEGV in ~15 seconds. rax = 0x4141414141414141
Suggested fix: Apply the same dedup pattern from Bug 2017894 to all three loops in WebRenderBridgeParent.
Full disclosure: I have not tested exploit feasibility via winning the heap reclaim race in the tight loop without the memset simulation. However, the UAF and vtable control are confirmed.
Updated•5 months ago
|
| Assignee | ||
Comment 1•5 months ago
|
||
I believe this was fixed by Bug 2011129. Possible duplicate.
| Reporter | ||
Comment 2•5 months ago
|
||
I just pulled the latest and it was fixed. This is definitely a dupe. - Richard
| Assignee | ||
Updated•5 months ago
|
Updated•5 months ago
|
Updated•1 month ago
|
Description
•