
Ricerca su CVE-2026-43499 (GhostLock) per HUAWEI MatePad Pro 11 GOT-W29
Note di ricerca sull'elevazione dei privilegi per CVE-2026-43499 (Linux rtmutex/futex-PI UAF, "GhostLock") su HUAWEI MatePad Pro 11 GOT-W29 (Qualcomm kona / Snapdragon 870, HarmonyOS 4.x, kernel 4.19.157-perf+).
| Campo | Valore |
|---|---|
| Modello | HUAWEI MatePad Pro 11 GOT-W29 (tablet) |
| SoC | Qualcomm kona (SM8250, Snapdragon 870) |
| Sistema | HarmonyOS 4.2 (104.2.0.237C00), versione di fabbrica 4.0 (104.0.0.136) |
| Kernel | 4.19.157-perf+ (build del 2025-10-13) |
| VA | 39-bit, pagine 4K, KASLR attivo |
CVE-2026-43499: remove_waiter() in kernel/locking/rtmutex.c, nel percorso di rollback di rt_mutex_start_proxy_lock(), esegue la pulizia con current invece di waiter->task, lasciando un pi_blocked_on pendente (UAF dello stack). Interessa 2.6.39 ~ 7.1 (questo kernel è nel range). Fix upstream: commit 3bfdc63936dd.
Confermato su questo dispositivo: sorgente rtmutex.c:1110-1112, decompilazione di boot.elf e trigger su dispositivo reale, tutto verificato.
Con la shell (uid 2000), perf_event_paranoid=-1, perf_event_open(PERF_SAMPLE_IP, exclude_user=1) campiona i cluster di indirizzi del testo del kernel; allineandoli agli offset noti dei simboli si ottiene lo slide.
samples=27651 kernel_ips=1685 lo=0xffffff948728176c hi=0xffffff9488ebfc7c
KASLR slide=0x147f200000 (40/40 IP 映射进内核文本区验证)
runtime _stext=0xffffff9487280800
Strumento: tools/perf_kaslr.c. Prerequisito di esecuzione: shell (Shizuku rish), nessuna intercettazione seccomp.
Si crea un ciclo PI per far sì che FUTEX_CMP_REQUEUE_PI restituisca -EDEADLK; il rollback innesca il bug di remove_waiter.
Disposizione chiave: il futex target del requeue è detenuto dal waiter in fase di requeue (futex2 = waiter_tid) → in task_blocks_on_rt_mutex si ha 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
Strumento: tools/edeadlk_probe.c (variante 8+2+1 = 11, oppure 27).
Lo step[7] di rt_mutex_adjust_prio_chain esegue rb_erase (percorso con unico figlio sinistro) sul fake waiter: *(tree_left) = tree_pc (value→target) + scrittura incrementale __rb_change_child. Tutti gli offset in target.h sono misurati tramite disassemblaggio di boot.elf.
Vedi target/got_w29_target.h. Punti chiave:
Il disassemblaggio di task_blocks_on_rt_mutex da boot.elf è la prova definitiva: il kernel di questo dispositivo ha un controllo anticipato owner==task a 0x3808-0x3868 (cmp owner,task; b.eq -> -EDEADLK), che ritorna prima della scrittura di task->pi_blocked_on (0x38d4 str x21,[x20,#0xa90]). Il vecchio trigger GOT-W29 faceva detenere al waiter futex2=waiter_tid (self-own) → colpisce esattamente quel controllo anticipato → pi_blocked_on non viene mai impostato → nessun puntatore pendente. Le osservazioni sul dispositivo (nessun crash + boot_id invariato) corrispondono pienamente a "nessun puntatore pendente" — il posizionamento dell'overlay era una diagnosi errata.
Trigger corretto (riferimento smt878u, implementato): ciclo PI — l'owner FUTEX_LOCK_PI(target) detiene il target del requeue; il waiter detiene il chain futex; l'owner si blocca poi sulla chain (ciclo: waiter→target→owner→chain→waiter). Al requeue, la catena rileva rt_mutex_owner(chain)==top_task (rtmutex step[6]) → -EDEADLK → il rollback di remove_waiter usa il current del requeuer e ripulisce la persona sbagliata → il pi_blocked_on del waiter resta pendente. L'owner deve abbassare la priorità (nice=10) in modo che, dopo il boost, la prio sia diversa da owner_waiter->prio; altrimenti rt_mutex_waiter_equal esce prematuramente.
Con shift=12, le parole 6-7 (task/lock) del fake waiter ricadono in res_in[3..4] (zona azzerata dal kernel). Sfruttando la semantica di do_select: res_in[i] = in[i] & POLLIN-ready. Scrivendo SLIDE_INIT_TASK / fake_lock in in[3]/in[4] e facendo dup2 di tutti i fd corrispondenti sull'"estremità di lettura di una pipe con dati" (sempre EPOLLIN-ready) → res_in[3]=init_task, res_in[4]=fake_lock codificati con precisione. Le parole 3-5 (pi_tree) e 8-10 possono essere zero (il percorso ownerless-lock non usa pi_tree; prio/deadline vengono sovrascritti dal kernel a step[7]). pselect ritorna immediatamente grazie ai fd pronti → il waiter fa busy-wait in spazio utente (segnali disabilitati, zero syscall, per evitare che il riuso dello stack del kernel cancelli il fake waiter) finché il consumer non completa il trigger. La tabella HW_FUTEX_PI a 11 parole, la classe a doppio fd e il timeout di attesa del processo padre sono implementati (git diff).
*(boot_id)=DM(loggers[0][1])); stext=leaked-p0_alias_image_offset(NFULNL_LOGGER) presenta un off-by rispetto a DM(_stext); se la fase root percorre interamente la physmap (spazio DM) è autoconsistente, altrimenti si deve usare lo slide runtime di perf_event_open (disponibile sotto rish).tools/cycle_probe (già compilato): verifica a basso costo del trigger EDEADLK ciclico; dopo EDEADLK, se sched_setattr sul waiter fa scattare l'oops del consumer = il puntatore pendente esiste + overlay atterrato.build_tools/deploy_test.sh, osservare slide-kaslr-ok oppure l'oops del consumer.tools/ 验证工具(perf KASLR, EDEADLK 探针, overlay 测试, kaslr.json)
target/ 全部实测偏移
exploit/ 移植的 slide.c(含 EDEADLK 触发改动)