
HUAWEI MatePad Pro 11 GOT-W29에 대한 CVE-2026-43499 (GhostLock) 연구
HUAWEI MatePad Pro 11 GOT-W29(Qualcomm kona / Snapdragon 870, HarmonyOS 4.x, kernel
4.19.157-perf+)에서 CVE-2026-43499(Linux rtmutex/futex-PI UAF, "GhostLock")
권한 상승 연구 기록입니다.
| 항목 | 값 |
|---|---|
| 모델 | HUAWEI MatePad Pro 11 GOT-W29 (tablet) |
| SoC | Qualcomm kona (SM8250, Snapdragon 870) |
| 시스템 | HarmonyOS 4.2 (104.2.0.237C00), 초기 4.0 (104.0.0.136) |
| 커널 | 4.19.157-perf+ (2025-10-13 build) |
| VA | 39-bit, 4K pages, KASLR on |
CVE-2026-43499: kernel/locking/rtmutex.c의 remove_waiter()가
rt_mutex_start_proxy_lock() 롤백 경로에서 waiter->task 대신 current로 정리하여,
댕글링 pi_blocked_on(스택 UAF)을 유발합니다. 영향 범위는 2.6.39 ~ 7.1(현재 커널은 범위 내).
업스트림 수정: commit 3bfdc63936dd.
이 기기에서 확인: 소스 rtmutex.c:1110-1112, 디컴파일 boot.elf, 실기기 트리거 모두 검증됨.
shell(uid 2000) 상태에서 perf_event_paranoid=-1, perf_event_open(PERF_SAMPLE_IP, exclude_user=1)로 커널 텍스트 주소 클러스터를 샘플링하고, 알려진 심볼 오프셋에 정렬하여 slide를 얻습니다.
samples=27651 kernel_ips=1685 lo=0xffffff948728176c hi=0xffffff9488ebfc7c
KASLR slide=0x147f200000 (40/40 IP 映射进内核文本区验证)
runtime _stext=0xffffff9487280800
도구: tools/perf_kaslr.c. 실행 전제: shell(Shizuku rish), seccomp 차단 없음.
PI 사이클을 만들어 FUTEX_CMP_REQUEUE_PI가 -EDEADLK를 반환하게 하고, 롤백이 remove_waiter 버그를 트리거합니다.
핵심 배치: requeue 대상 futex를 requeue되는 waiter가 보유(futex2 = waiter_tid) → task_blocks_on_rt_mutex에서 owner == task → -EDEADLK.
[M] CMP_REQUEUE_PI ret=-1 errno=35 (EDEADLK!)
[W] WAIT_REQUEUE_PI ret=-1 errno=110 (ETIMEDOUT) ← waiter 返回
[M] waiter_returned=1 ← 留下悬空 pi_blocked_on
도구: tools/edeadlk_probe.c(variant 8+2+1 = 11, 또는 27).
rt_mutex_adjust_prio_chain step[7]가 fake waiter에 대해 rb_erase(단일 왼쪽 자식 경로)를 수행:
*(tree_left) = tree_pc(value→target) + __rb_change_child 증분 쓰기. target.h의
모든 오프셋은 boot.elf 디스어셈블리 실측 값.
target/got_w29_target.h 참조. 요점:
boot.elf task_blocks_on_rt_mutex 디스어셈블리로 확정: 이 기기 커널은 0x3808-0x3868에
조기 owner==task 검사(cmp owner,task; b.eq -> -EDEADLK)가 있으며, 이는
task->pi_blocked_on 쓰기(0x38d4 str x21,[x20,#0xa90]) 이전에 반환됩니다.
GOT-W29의 기존 트리거는 waiter가 futex2=waiter_tid를 자체 보유(self-own)하게 하여 → 정확히 이 조기
검사에 걸리고 → pi_blocked_on을 절대 설정하지 않음 → 댕글링 포인터 없음. 기기 관찰(크래시 없음 + boot_id 불변)은
'댕글링 없음'과 완전히 일치 —— overlay 배치는 오진이었습니다.
올바른 트리거(smt878u 참고, 구현됨): PI 사이클 — owner가 FUTEX_LOCK_PI(target)로
requeue 대상을 보유; waiter가 chain futex를 보유; owner가 다시 chain에 블로킹(사이클: waiter→target→
owner→chain→waiter). requeue 시 체인 탐지가 rt_mutex_owner(chain)==top_task
(rtmutex step[6]) → -EDEADLK → 롤백 remove_waiter가 requeuer의 current로
잘못된 대상을 정리 → waiter의 pi_blocked_on이 댕글링. owner는 우선순위를 낮춰(nice=10) boost 이후
prio가 owner_waiter->prio와 다르게 해야 함. 그렇지 않으면 rt_mutex_waiter_equal이 조기 종료.
shift=12에서 fake waiter의 words 6-7(task/lock)은 res_in[3..4](커널 제로 영역)에 위치합니다.
do_select 의미론 활용: res_in[i] = in[i] & POLLIN-ready. SLIDE_INIT_TASK /
fake_lock을 in[3]/in[4]에 쓰고, 해당 fd들을 모두 '데이터가 있는 pipe 읽기 끝'(항상
EPOLLIN-ready)에 dup2 → res_in[3]=init_task, res_in[4]=fake_lock 정밀 인코딩. words 3-5
(pi_tree)와 8-10은 0이어도 됨(ownerless-lock 경로는 pi_tree를 사용하지 않음; prio/deadline은 커널이
step[7]에서 덮어씀). pselect는 ready fd 때문에 즉시 반환 → waiter는 사용자 영역 busy-wait(신호 차단,
syscall 0회, 커널 스택 재사용으로 fake waiter가 지워지는 것 방지)하며 consumer가 트리거를 완료할 때까지 대기.
11-word HW_FUTEX_PI 테이블, 이중 fd 클래스, 부모 프로세스 대기 타임아웃 모두 구현됨(git diff).
*(boot_id)=DM(loggers[0][1]))이며,
stext=leaked-p0_alias_image_offset(NFULNL_LOGGER)와 DM(_stext) 사이에 off-by가 있음;
root 단계에서 끝까지 physmap(DM 공간)으로 진행하면 자체적으로 일관되지만, 그렇지 않으면 perf_event_open의 런타임
slide(rish에서 사용 가능)로 교체해야 함.tools/cycle_probe(컴파일됨): 저비용으로 cycle EDEADLK 트리거를 검증; EDEADLK 후 waiter에
sched_setattr을 실행해 consumer oops가 발생하면 = 댕글링 존재 + overlay 적용.build_tools/deploy_test.sh로 배포하고, slide-kaslr-ok 또는 consumer oops를 확인.tools/ 验证工具(perf KASLR, EDEADLK 探针, overlay 测试, kaslr.json)
target/ 全部实测偏移
exploit/ 移植的 slide.c(含 EDEADLK 触发改动)