Pixel BigWave BIGO timeout race UAF → 2KB kernel write from mediacodec
TL;DR
- From the SELinux-confined mediacodec context,
/dev/bigwave(Pixel AV1 hardware accelerator) is reachable. A backlog of jobs makesBIGO_IOCX_PROCESShit its 16s wait_for_completion_timeout() and return while the worker thread concurrently dequeues the same inlinejobstructure. - Closing the FD immediately frees
struct bigo_inst(which embedsstruct bigo_job). The worker reconstructsinst = container_of(job, ...)and later uses freed fields such asjob->regsinsidebigo_run_job(), yielding a Use-After-Free on the inline job/inst. bigo_pull_regs(core, job->regs)performsmemcpy_fromio(regs, core->base, core->regs_size). By reclaiming the freed slab and overwritingjob->regs, an attacker gets a ~2144-byte arbitrary kernel write to a chosen address, with partial control of the bytes by pre-programming register values before the timeout.- Tracked as CVE-2025-36934; fixed in the 2026-01-05 Pixel/2025-12-01 ASB builds.
Attack surface mapping (SELinux → /dev reachability)
- Use tools like DriverCartographer to enumerate device nodes accessible from a given SELinux domain. Despite mediacodec’s constrained policy (software decoders should stay in an isolated context),
/dev/bigwaveremained reachable, exposing a large attack surface to post-media-RCE code.
Vulnerability: BIGO_IOCX_PROCESS timeout vs worker
- Flow: ioctl copies user register buffer into
job->regs, queues the inlinejob, thenwait_for_completion_timeout(..., 16s)is called. On timeout it tries to dequeue/cancel and returns to userspace. - Meanwhile
bigo_worker_threadmay have just dequeued the samejob:
inst = container_of(job, struct bigo_inst, job);
bigo_push_regs(core, job->regs);
...
bigo_pull_regs(core, job->regs); // memcpy_fromio(regs, core->base, core->regs_size)
*(u32 *)(job->regs + BIGO_REG_STAT) = status;
- If userspace closes the FD after the timeout,
inst/jobare freed while the worker keeps using them → UAF. No synchronization ties FD lifetime to the worker thread’s job pointer.
Exploitation outline
- Backlog + timeout: Queue enough jobs so the worker is delayed, then issue
BIGO_IOCX_PROCESSand let it hit the 16s timeout path. - Free while in use: As soon as ioctl returns,
close(fd)to freeinst/jobwhile the worker is still running the dequeued job. - Reclaim + pointer control: Spray reclaimers (e.g., Unix domain socket message allocations) to occupy the freed slab slot and overwrite the inline
job, especiallyjob->regs. - Arbitrary write: When
bigo_pull_regs()runs,memcpy_fromio()writes core->regs_size (~2144 bytes) from MMIO into the attacker-supplied address injob->regs, producing a large write-what-where without a KASLR leak. - Data shaping: Because registers are first programmed from user data (
bigo_push_regs), set them so the hardware does not execute, keeping the copied-back register image close to attacker-controlled bytes.
Minimal PoC skeleton (blocking backlog + reclaim)
int fd = open("/dev/bigwave", O_RDWR);
for (int i = 0; i < 64; i++) submit_job(fd, regs_buf); // fill worker queue
submit_job(fd, regs_buf); // victim job
auto t0 = now();
while (now() - t0 < 17000ms) sched_yield(); // hit 16s timeout
close(fd); // free inst/job
spray_uds_msgs(payload_pointing_to_target, spray_count); // reclaim slab
sleep(1); // let worker memcpy_fromio
regs_bufshould preconfigure BigWave to idle (e.g., set control bits to skip execution) so the copied-back register image stays deterministic.
Related successor primitive on Pixel 10: unbounded /dev/vpu mmap() → physical-memory R/W
Project Zero's Pixel 10 follow-up replaced BigWave with another mediacodec-reachable driver: /dev/vpu for the Chips&Media Wave677DV decoder. The bug class is even shallower: the driver intends to expose only the VPU MMIO CSR window, but its mmap handler trusts the attacker-controlled VMA length.
static int vpu_mmap(struct file *fp, struct vm_area_struct *vm)
{
...
pfn = core->paddr >> PAGE_SHIFT;
return remap_pfn_range(vm, vm->vm_start, pfn,
vm->vm_end - vm->vm_start,
vm->vm_page_prot) ? -EAGAIN : 0;
}
Why this is exploitable
pfnis fixed to the VPU MMIO physical base (core->paddr >> PAGE_SHIFT).- The mapped length is
vm->vm_end - vm->vm_start, i.e. the user-requestedmmap()size. - There is no check that the requested size is bounded by the real MMIO resource length.
Therefore, if /dev/vpu is reachable from a compromised app/service domain, a large mmap() does not stop at the register window: it keeps mapping the contiguous physical pages after the VPU MMIO range into userspace.
Exploitation model
- Gain code execution in a context allowed to open
/dev/vpu(for example mediacodec after a media-parser bug). open("/dev/vpu", O_RDWR).mmap()a region much larger than the real CSR/MMIO window.- Compute the offset from the returned mapping to the kernel physical base.
- Read or overwrite kernel
.text,.data, credentials, function pointers, or build a more convenient arbitrary R/W primitive.
Representative pattern:
int fd = open("/dev/vpu", O_RDWR);
void *map = mmap(NULL, HUGE_LEN, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
uint8_t *kbase = (uint8_t *)map + (KERNEL_PHYS_BASE - VPU_PHYS_BASE);
// Direct kernel physical read/write via kbase[...]
Practical notes
- On Pixels, this primitive is especially strong because the kernel physical placement has been observed to be predictable; see also:
arm64-static-linear-map-kaslr-bypass.md
- Compared with the earlier BigWave UAF, this bug skips heap feng shui almost entirely: once the oversized mapping succeeds, the attacker gets direct userspace access to kernel physical memory.
- Review pattern: any driver that exposes MMIO via
remap_pfn_range()must clamprequested_len <= resource_size, align offsets carefully, and reject arbitrary expansion beyond the device BAR/resource.
Takeaways for driver reviewers
- Inline per-FD job structs enqueued to async workers must hold references that survive timeout/cancel paths; closing an FD must synchronize with worker consumption.
- Any MMIO copy helpers (
memcpy_fromio/memcpy_toio) that use buffer pointers from jobs should be validated or duplicated before enqueuing to avoid UAF→write primitives.
References
- Pixel 0-click (Part 2): Escaping the mediacodec sandbox via the BigWave driver
- Project Zero issue 426567975 – BigWave BIGO timeout UAF
- CVE-2025-36934 entry (BigWave driver)
- Project Zero – Pixel 10 Zero-Click-to-Root: Dolby CVE-2025-54957 and /dev/vpu Kernel mmap Privilege Escalation
- Project Zero issue 463438263 – /dev/vpu unbounded mmap