
Research repository documenting exploitation attempts of CVE-2026-43499 futex UAF on Honor YLP-W00 kernel 6.12.38, including PoC sources, kernel offsets, and analysis of failed privilege-escalation chains.
Research status: Vulnerability confirmed triggerable, PI walk succeeds without crashing, but full privilege escalation chain not achieved. Awaiting upstream updates.
This repository documents the complete research process of exploiting CVE-2026-43499 for temporary privilege escalation on the Honor YLP-W00 tablet (kernel 6.12.38, +pgo+bolt+lto+mlgo).
Vulnerability trigger succeeded (EDEADLK), PI chain walk succeeded (sched_setattr=0, no crash), but PGO compilation caused do_futex to be inlined, and all stack reclaim carriers failed; CyberMeowfiaNS's late_refs write primitive also cannot hit due to slab geometry mismatch.
| Item | Value |
|---|---|
| Model | Honor YLP-W00 (tablet) |
| System | HONORYLP-W00/10DLDLD170SP3C00E144 |
| System version | 10.0.0.170 (MagicOS 10.0) |
| Kernel | 6.12.38-android16-5-gfde7767f6ef6-abogki481467632-4k |
| Compilation | +pgo,+bolt,+lto,+mlgo (clang 19.0.1) |
| Kernel SHA-256 | 48b622a20a700cdde0b8f2f6e83959df00a7efedf52f347377cf542f7b58948e |
| Bootloader | Locked |
| SELinux | Enforcing |
| kstack randomization | Disabled |
| ashmem | Rust rewrite |
| MTE | Hardware supported but KASAN not enabled |
CyberMeowfiaNS audit confirmed VULNERABLE_PATTERN_PRESENT. There are two successful cases on the same kernel SHA-256 (honor-mt6993, honor8e5), but they used the late_refs write primitive, which cannot be reproduced on this device (see below for details).
The Honor 6.12.38 kernel is compiled with +pgo+bolt+lto+mlgo, causing __arm64_sys_futex to directly call futex_wait_requeue_pi, skipping the do_futex intermediate layer. The futex call chain changed from the standard three layers to two layers:
Standard GKI: __arm64_sys_futex -> do_futex -> futex_wait_requeue_pi
This device: __arm64_sys_futex -> futex_wait_requeue_pi (do_futex inlined)
This causes the waiter to be at a shallower stack position (depth 0x130 instead of the standard 0x1b0+), and the coverage geometry of all standard stack reclaim carriers does not match.
| Field | Offset | Size |
|---|---|---|
| tree_entry (rb_node) | +0x00 | 24B |
| pi_tree_entry (rb_node) | +0x18 | 24B |
| lock | +0x38 | 8B |
| prio | +0x44 | 4B |
| deadline | +0x48 | 8B |
| task | +0x50 | 8B |
| ww_ctx | +0x58 | 8B |
| Total size | 0x70 | 112B |
| Symbol | Offset |
|---|---|
| init_task | 0x023ecf00 |
| init_cred | 0x02402cb0 |
| root_task_group | 0x0261a740 |
| selinux_state.enforcing | 0x026663c8 |
| rb_erase | 0x00bce274 |
| rt_mutex_adjust_prio_chain | 0x115060c |
| commit_creds | 0x00b89c10 |
| worker_thread | 0x00adfef8 |
| remove_waiter | 0x0112b20 |
Confirmed UAF trigger and PI walk success in safe mode:
[futex] CMP_REQUEUE_PI ret=-1 errno=35 (EDEADLK) ← UAF trigger succeeded
[futex] consumer sched_setattr ret=0 errno=0 ← PI walk succeeded, no crash
The device remained online, boot_id unchanged, SELinux still Enforcing. rb_erase wrote to a valid but useless address.
Stack reclaim carriers attempted:
| Carrier | Principle | Result |
|---|---|---|
| pselect | fd_set stack copy | shift=26 (PGO inlined), fd_set capacity insufficient |
| MCAST_JOIN_SOURCE_GROUP | 0x108 byte stack copy | WAITER_OFF=0x308 >> 0x108, no overlap |
| adjtimex | timex struct stack copy | buf depth 0x118 < waiter 0x130, no overlap |
| io_submit | struct iocb stack overwrite | Overwrites waiter[0x28..0x67], lock@0x58 not in range |
| PR_SET_MM_MAP | prctl stack write | EPERM |
| Signal delivery (do_signal) | pt_regs stack save | Frame overwrites waiter[0x00..0x28], task@0x50 and lock@0x58 overwritten to invalid values by other frames, crash |
| rt_sigreturn | fpsimd vregs 512B | On 6.12.38 uses ldtr to load into per-cpu area, does not go through kernel stack |
Core obstacle: The waiter is at depth 0x130, tree_entry(+0x00) and pi_tree_entry(+0x18) can be partially covered by carriers, but task(+0x50) and lock(+0x58) are deeper, and no known carrier can reach them. Overwriting tree_entry only makes rb_erase take the empty path (write NULL), and cannot be directed to a target address.
CyberMeowfiaNS uses a completely different approach, bypassing the stack reclaim problem:
All prerequisite chains succeeded:
--info passed: kernel identity verification succeeded--check passed: carrier DSO (libdumpstateaidl.so) preflight confirmed
late_refs race failed:
reclaim_hits=0 under all delay settingsRoot cause of failure: slab geometry mismatch.
The ep_get_upwards_depth_proc function does not exist on 6.12.38 (specific to the 6.12.58 reference kernel). But ep_loop_check_proc does write to eventpoll.gen (+0xa8), so this is not the direct cause.
The eventpoll structure size is 0xdc0 (3520 bytes), allocated from kmalloc-4k (order=3, 32KB slab, 8 objects). The code's assumption of eventpoll_size=0xd0 (208 bytes) is wrong.
eventpoll_epi (epitem) is 128 bytes, order=0, 4KB page, 32 objects per page. late_refs only frees 1-2 epitems per round, insufficient to empty an entire 4KB page for the page allocator to reclaim.
late_refs requires cross-cache page reclaim: freed epitem page → page allocator → SKB order-3 page. But this requires all 32 epitems on the same page to be freed, and the code's epoll graph layout cannot guarantee this.
The 6.12.58 reference kernel may have a different SLUB configuration or slab layout, making cross-cache reclaim easier to trigger.
| File | Description | Size |
|---|---|---|
firmware/boot_10.0.0.170.img | boot.img for system version 10.0.0.170 | 96 MB |
firmware/honor_kernel_6.12.38.img | Kernel ELF extracted from boot.img (with symbol table) | 44 MB |
firmware/libdumpstateaidl_honor.so | Carrier DSO (pulled from device) | 52 KB |
boot.img can be used with llvm-nm to extract the kernel symbol table, and llvm-objdump to disassemble and analyze function layout.