Pixel BigWave BIGO timeout race UAF → 2KB kernel write from mediacodec

Tip

Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Ucz się i ćwicz Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Wsparcie dla HackTricks

TL;DR

  • From the SELinux-confined mediacodec context, /dev/bigwave (Pixel AV1 hardware accelerator) is reachable. A backlog of jobs makes BIGO_IOCX_PROCESS hit its 16s wait_for_completion_timeout() and return while the worker thread concurrently dequeues the same inline job structure.
  • Closing the FD immediately frees struct bigo_inst (which embeds struct bigo_job). The worker reconstructs inst = container_of(job, ...) and later uses freed fields such as job->regs inside bigo_run_job(), yielding a Use-After-Free on the inline job/inst.
  • bigo_pull_regs(core, job->regs) performs memcpy_fromio(regs, core->base, core->regs_size). By reclaiming the freed slab and overwriting job->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/bigwave remained 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 inline job, then wait_for_completion_timeout(..., 16s) is called. On timeout it tries to dequeue/cancel and returns to userspace.
  • Meanwhile bigo_worker_thread may have just dequeued the same job:
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;
  • Jeśli userspace zamknie FD po timeoutie, inst/job są zwalniane, podczas gdy worker nadal ich używa → UAF. Brak synchronizacji wiążącej czas życia FD ze wskaźnikiem job w wątku worker.

Zarys eksploatacji

  1. Backlog + timeout: Dodaj do kolejki wystarczająco dużo job, aby worker został opóźniony, następnie wykonaj BIGO_IOCX_PROCESS i pozwól, aby trafił na ścieżkę timeoutu 16s.
  2. Free while in use: Gdy tylko ioctl zwróci, wykonaj close(fd), aby zwolnić inst/job, podczas gdy worker nadal wykonuje job pobrany z kolejki.
  3. Reclaim + pointer control: Sprayuj reclaimery (np. alokacje Unix domain socket message) aby zająć zwolniony slot slab i nadpisać inlineowy job, zwłaszcza job->regs.
  4. Arbitrary write: Gdy bigo_pull_regs() się wykona, memcpy_fromio() zapisze core->regs_size (~2144 bytes) z MMIO do adresu dostarczonego przez atakującego w job->regs, powodując duży write-what-where bez KASLR leak.
  5. Data shaping: Ponieważ rejestry są najpierw załadowane z danych użytkownika (bigo_push_regs), ustaw je tak, aby hardware nie wykonał kodu, utrzymując skopiowany obraz rejestrów blisko bajtów kontrolowanych przez atakującego.

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_buf powinien wstępnie skonfigurować BigWave w tryb bezczynny (np. ustawić bity sterujące, aby pominąć wykonanie), tak by skopiowany z powrotem obraz rejestrów pozostał deterministyczny.

Wnioski dla recenzentów sterownika

  • Inline per-FD job structs enqueued to async workers muszą trzymać referencje, które przetrwają ścieżki timeout/cancel; zamknięcie FD musi być zsynchronizowane z przetwarzaniem przez worker.
  • Wszelkie MMIO copy helpers (memcpy_fromio/memcpy_toio) używające wskaźników bufora z jobów powinny zostać zweryfikowane lub zduplikowane przed umieszczeniem w kolejce, aby uniknąć UAF→write primitives.

Źródła

Tip

Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Ucz się i ćwicz Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Wsparcie dla HackTricks