# CVE-2023-4211: Use-after-Free in ARM Mali GPU Driver

Splitting the Mali tracking-page VMA into multiple VMAs breaks the single-tracking-page assumption, causing a use-after-free write on a freed mm_struct during page-usage drain.

- Payout: Real-world hack
- Severity: high
- Program: ARM Mali GPU Driver
- Credited: Maddie Stone & Jann Horn
- Disclosed: 2023-10-02
- CVE: CVE-2023-4211
- Write-up quality: 8/10
- Original: https://github.com/googleprojectzero/0days-in-the-wild/blob/main/0day-RCAs/2023/CVE-2023-4211.md
- Library page: https://bugbountylibrary.com/r/p0%3ACVE-2023-4211
- Tags: domain:oss impact:privesc kind:incident platform:project-zero sev:high tech:linux technique:race-window technique:source-review vuln:memory-corruption vuln:race-condition

**Target:** /dev/mali0 kernel driver tracking-page VMA handling
**Root cause:** kbase_tracking_page_setup only enforces one tracking VMA via VM_DONTCOPY/VM_IO/VM_DONTEXPAND and a process_mm check, but a multi-page tracking VMA can be split with munmap, allowing two tracking VMAs across processes. kbasep_os_process_page_usage_drain drops mm_update_lock and does bookkeeping on process_mm without holding a reference, so the mm_struct can be freed concurrently.
**Steps:**
1. Open `/dev/mali0`, issue `KBASE_IOCTL_VERSION_CHECK` (11.11) and `KBASE_IOCTL_SET_FLAGS`.
2. mmap 0x2000 tracking page at `BASE_MEM_MAP_TRACKING_HANDLE` (3<<12).
3. `munmap(parent_tracking_page+0x1000, 0x1000)` to split VMA and invoke close handler, clearing process_mm.
4. fork(); child mmaps 0x1000 tracking page, setting process_mm to child's mm.
5. Parent `munmap`s remaining tracking page; drain reads child mm, sets process_mm NULL, drops lock, blocks in synchronize_rcu (patch adds 5s mdelay for PoC).
6. Child unmaps its tracking page and exits, freeing its mm_struct.
7. Parent resumes and calls kbasep_add_mm_counter on freed mm_struct -> UAF write.
**How found:** Analysis of the tracking-page VMA lifecycle: noticing that VM_DONTCOPY/VM_IO/VM_DONTEXPAND and the process_mm check assume a single tracking VMA, but a multi-page VMA can be split via munmap, and that drain does bookkeeping after dropping the lock without a reference on process_mm.
**Exploitation:** PoC uses a kernel patch injecting a 5-second delay in kbasep_os_process_page_usage_drain to widen the race window; without instrumentation the race is between the parent's drain after synchronize_rcu and the child's exit freeing its mm_struct. Result is a use-after-free write into a freed mm_struct.
**Insight:** When a driver enforces a 'single object' invariant via VMA flags plus a pointer check, test whether the VMA can be split (multi-page mmap + partial munmap) to create multiple VMAs that each pass the check; also audit lock-dropped bookkeeping paths for missing references on the object they touch.
