
Исследовательские заметки и разработка PoC для CVE-2026-43499 (GhostLock) — UAF в ядре на vivo Y200i Android 14, охватывающие примитивы повторного захвата стека через PI-цепочку futex и ограничения компоновки фреймов.
Устройство: vivo Y200i (PD2354C), Snapdragon 4 Gen 2 (SM4450), Android 14 / OriginOS 4 Ядро: 5.10.218-gki-g3a51ea9d5834 (39-битный VA, страницы 4K, KASLR включён, STACKLEAK=y, без SVE)
Цель: временный root через CVE-2026-43499 (GhostLock).
Что уже работает:
Заблокировано: примитив stack-reclaim не может добраться до rb_node структуры rt_mutex_waiter.
Ключевые числа (идентичны в двух независимых пакетах прошивки):
__arm64_sys_futex 0xe0
do_futex 0xc0 (НЕ заинлайнен - существует bl do_futex)
futex_wait_requeue_pi 0x1b0 (waiter по sp+0x20)
=> глубина цепочки futex T0-0x330
__arm64_sys_pselect6 0xa0 core_sys_select 0x1c0 (fd_set по sp+0x50) => глубина цепочки pselect T0-0x210, управляемое окно всего 120 байт => разрыв 0x120, архитектурно не перекрывается
раскладка waiter (компактная 5.10, совпадает с PFEM10): +0x18 pi_tree_entry.__rb_parent_color <- то, что обходит rt_mutex_adjust_prio_chain +0x20 pi_tree_entry.rb_right +0x28 pi_tree_entry.rb_left +0x30 task +0x38 lock +0x40 prio +0x48 deadline
Свидетельство из rt_mutex_adjust_prio_chain: ldr x8, [task, #0x888] ; task->pi_waiters.rb_leftmost sub x8, x8, #0x18 ; rb_entry(node, rt_mutex_waiter, pi_tree_entry)
Поскольку copy_from_user пишет в сторону старших адресов, для покрытия pi_tree_entry требуется, чтобы база копирования была <= T0-0x318.
Исчерпанные пути: pselect6/core_sys_select T0-0x210 -> не хватает 0x120 poll/do_sys_poll окно заканчивается на T0-0x344, максимум 0xf0B (N_STACK_PPS=30) -> не хватает 0x14 rt_sigreturn (fpsimd) T0-0x300 -> достигает только waiter+0x30 rt_sigreturn (SVE) T0-0x310 -> покрыл бы waiter+0x10, но у этого CPU нет SVE; ядро отвергает записи SVE sigframe с -EINVAL compat rt_sigreturn / vfp T0-0x2c0 -> ещё мельче
Полное сканирование ядра по всем местам copy_from_user: всего 655, достижимо 118, максимальная глубина 0x300 (bpf_prog_get_info_by_fd, также ограничен CAP_BPF). Нужно 0x318.
Вывод: геометрия стека этого ядра ограничивает stack-based copy_from_user значением 0x300; для покрытия pi_tree_entry нужно 0x318. Это не вопрос настройки - это различие в раскладке фреймов на этапе компиляции. do_futex здесь не заинлайнен, в отличие от PFEM10 (5.10.236, Clang 12.0.5), скорее всего из-за другой модели стоимости инлайнинга.
Ищу: