
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) |
| OS | HarmonyOS 4.2 (104.2.0.237C00)、出荷時 4.0 (104.0.0.136) |
| カーネル | 4.19.157-perf+ (2025-10-13 build) |
| VA | 39ビット、4Kページ、KASLR 有効 |
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 はゼロでよい(ownerless-lock パスは pi_tree を使わない;prio/deadline はカーネルが
step[7] で上書きする)。pselect は ready fd があるため即座にリターンする → waiter は consumer のトリガーが完了するまで
ユーザー空間でビジーウェイトする(シグナル禁止、syscall ゼロ、カーネルスタック再利用による fake waiter の消去を防ぐ)。
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 触发改动)