一加 16(PYB110)临时 root:CVE-2026-43499 实测不可行(附移植判定方法)

本页记录一次针对一加 16 的可行性实测:结论是 CVE-2026-43499 在这台机器的 6.18.21 内核上已经被修复,临时 root 路线关闭。过程本身比结论更有价值——它说明判断一台设备能否利用该 CVE,不能只看版本窗口,必须反汇编 remove_waiter() 确认它是否仍读取 current。

❗ 实测结论(2026-10-19):用官方 PYB110_17.0.0.152(CN01) A.13 全量包提取的 boot.img 做检查,remove_waiter()(+0x137299c)不包含 mrs xN, sp_el0,即已按修复版使用 waiter->task,不存在该 UAF。同一工具对一加 15(6.12.23)报告「primitive present(未修复)」,作为对照证明判据有效。

1. 设备与镜像

表 1|一加 16 实测身份

项 值
机型 / SoC PYB110 / SM8975
系统 ColorOS 17(PYB110_17.0.0.133(CN01),OTA 实测包为 17.0.0.152(CN01) A.13)
Android 17(CP2A.260605.016)
内核 uname -r 6.18.21-android17-5-gf957accaf563-ab15926176-4k
KMI android17-6.18
内核编译 2026-07-23,+pgo +bolt +lto +mlgo(clang 22.0.1)
安全补丁 2026-09-01
OTA 包 出厂 PYB110_11.A.04_0040_202609210439(133,补丁 2026-09-01);实测镜像取自 17.0.0.152(CN01) A.13 全量包(security_patch=2026-10-01,内核不变)

镜像来源问题及其解决:出厂包(133)通常不公开。实测发现 17.0.0.152 全量包的内核 banner 与设备当前内核逐字符一致(同为 …gf957accaf563-ab15926176-4k)——即这次更新没有升内核,因此无需升级设备即可拿到与设备精确匹配的 boot.img,「出厂包找不到」的阻塞由此解除。

提取流程复用本地工程的 tools/extract_boot_from_ota.py:从 payload.bin(9.86GB)取 boot 分区 → boot.img(96MB)→ 切出 PE/COFF 内核 kernel.raw(43.1MB,header_version=4,未压缩)。

2. 版本窗口判定(以及它为什么不够)

按一加 15 笔记的表 1,6.18.x 分支的修复起点是 6.18.27;本机 6.18.21 < 6.18.27,表面看仍在窗口内。这正是本页要纠正的判据:

版本子级只能给出「是否可能有漏洞」,不能证明「漏洞仍在」——OEM 可以在不提升子级的情况下回填单个安全补丁(本机即如此)。

3. 运行时前提实测(只读 adb shell)

表 2|GhostLock 运行期前提(一加 16 实测)

检查项 实测值 结论
perf_event_paranoid -1 perf 可用
Seccomp / Seccomp_filters 0 / 0 ADB shell 无过滤
/dev/ashmem 存在(10,259) FOPS/CFI 泄露路径可用
getenforce Enforcing 初始状态
id -Z u:r:shell:s0 shell 域
/data/local/tmp shell 可写 可投放
/proc/kallsyms、kptr_restrict、/proc/iomem Permission denied 偏移不能从设备侧提取

运行时前提全部满足,但仍不可利用——漏洞本身已不存在。

4. 实测判定:反汇编 remove_waiter()

用 ghostlock-app 自带的 ghostlock-extract(从 boot.img 恢复内嵌 kallsyms 与 BTF,无需 root)直接检查:

  • 一加 16:error: kernel already fixed the vulnerability: remove_waiter()@0x137299c never reads current (no mrs sp_el0); rtmutex UAF fix is present
  • 一加 15(对照):info: (CVE-2026-43499 primitive present (remove_waiter@0x118d798 still uses current)

判据的物理含义:arm64 上 current 只能通过 mrs xN, sp_el0 读取。漏洞版 remove_waiter() 用 current->pi_blocked_on = NULL 出队;修复版改用 waiter->task,不再读取 current。因此「函数体内没有 mrs sp_el0」即修复存在。

独立复核(不依赖工具):

range 0x137299c-0x1372b94 (504 bytes)
mrs xN, sp_el0 hits: NONE

并用符号邻接确认目标函数无误:remove_waiter 的全名唯一,后继符号为 rt_mutex_wait_proxy_lock / rt_mutex_slowlock_block / rt_mutex_cleanup_proxy_lock,正是 rtmutex 簇。

表 3|两台设备的同工具对照

设备 内核 remove_waiter() 判定
一加 15(PLK110) 6.12.23-android16-5 +0x118d798 仍读 current 未修复,可继续提偏移
一加 16(PYB110) 6.18.21-android17-5 +0x137299c 不读 current 已修复,门禁直接拒绝

5. 对「不下载 OTA」分析的修正

上一版分析把工作分成「判定 / 交付 / profile 生成 / PGO 判定」四层,并认为后两层只缺镜像。本次实测表明:镜像拿到后,第一道门禁(漏洞是否仍在)就可能直接否决,后续层根本不会执行。正确的顺序应是:

flowchart TB A["取到与 uname -r 精确匹配的 boot.img"] --> B["恢复 kallsyms/BTF"] B --> C{"remove_waiter() 读 current?"} C -- "否(不读)" --> D["已修复:停止

无需生成 profile"] C -- "是(读)" --> E["生成 profile:kallsyms + BTF + 物理布局"] E --> F["PGO / waiter 落位判定"] F --> G["设备侧 W1 冒烟"]

图 1|修正后的判定顺序。漏洞存在性检查必须排在偏移提取之前——它决定后面所有工作是否值得做。

6. 其他路线评估(2026-10-19)

除 CVE-2026-43499 外,逐条核对了当前公开的候选路线。设备组件与 OTA 镜像均做了静态检查,未触发任何利用路径。

表 4|公开过的临时 root 路线在本机的状态

路线 类型 本机现状 依据
CVE-2026-43499(GhostLock) 内核 futex PI UAF ❌ 已修复 §4
AtlasService + olc2(nns.ee[6]) 厂商服务链(无 CVE) ❌ 第一跳已修复 §6.1
CVE-2026-31431(Copy Fail / algif_aead)[9] 通用内核 LPE ❌ 不可达(SELinux 禁止 alg_socket) AOSP neverallow all_untrusted_apps *:alg_socket *
CVE-2026-43284/43500(Dirty Frag / ESP、RxRPC) 通用内核 LPE ❌ 不可达(XFRM/RxRPC socket 被拒) 壳层实测 EACCES
CVE-2026-46242(Bad Epoll)[10] 通用内核 LPE ❌ 已修复(镜像反汇编核验) ep_remove 含 file_ref_get pin
CVE-2026-46333(ptrace) 通用内核 LPE ❓ 需核验;App 侧目标受限 2026-05 披露
CVE-2026-21385(KGSL 整数溢出) GPU 驱动 LPE ⚠️ 已武器化,2026-03 修复;构建更晚(推断已修) CISA KEV
Qualcomm 2026-10 Graphics(CVE-2026-25291/57545/57554)[11] GPU 驱动 ❓ 影响 SM8975,无公开 PoC;152 应含修复 2026-10-05 公告
CVE-2026-64560(posix-cpu-timers UAF)[13] 通用内核 LPE ❗ 本机未修复(镜像核验);有 Android PoC,需移植 影响 < 6.18.41
CVE-2026-80758 / 80776(futex private hash)[14] 通用内核 LPE ❓ 版本受影响(fix 6.18.46/47);无公开 PoC 2026-09-04
CVE-2026-64600(RefluXFS) XFS race ❌ 不适用(内核无 XFS) /proc/filesystems 仅 ext4/f2fs
CVE-2026-10702(Firefox JIT) 浏览器 → untrusted_app ⚠️ 仅投送层,非 root 需再配合一个内核 LPE
2026-10 ASB(蓝牙/NFC/telephony 等)[8] 组件内 EoP ⚠️ 133 未含;152 已含 152 元数据 security_patch=2026-10-01
OEMpocalypse(Calif)Part 4[7] OEM 驱动 page UAF ❓ 细节未发布;公开链只覆盖 Android 16 / 6.12 2026-08-31 预告
官方 BL 解锁 + 修补 init_boot 永久 root ✅ 唯一可靠路径(需支持解锁、清数据) 与漏洞无关

6.1 AtlasService → olc2 链:第一跳已修复

从 17.0.0.152 全量包的 system_ext / odm(EROFS)直接取出两个二进制,无需设备:

  • audioDumpInfo(27 KB):仍 import system@LIBC,但 oplus.audio.dumpinfo.type 的取值必须完全等于 anr_log 或 audio_log(长度 ≤64,memcmp 精确匹配,否则 invalid dumpType 返回);路径还要经过 realpath() 与前缀 /data/persist_log/TMP/audio_dumpsys/ 的校验,新建目录改用 mkdir/chmod 系统调用,不再拼进 shell。注入字符没有机会进入 system() 的命令串。
  • olc2 HAL(68 KB):doShell / doShellBlocking、execl("/vendor/bin/sh") 仍在,但唯一门禁仍是 uid==0;第一跳被堵死后,untrusted_app 无法到达它。

结论:该链在 ColorOS 17 上已不可用,与 OnePlus 官方「16.x 线 ≥ 16.0.10.500 即修复」的说明方向一致。

6.2 补丁级与内核攻击面

  • 17.0.0.152 元数据:security_patch=2026-10-01、security_patch_vendor=2026-10-01、forbid_ota_local_update=false;内核仍为 6.18.21-…-ab15926176(与 133 相同),说明 10 月更新没有更换内核。
  • 2026-10 ASB 没有 Kernel 小节,LPE 集中在 Framework / System(蓝牙、NFC、telephony、wpa_supplicant、libfmq 等),属于组件内提权,不等于内核 root。
  • 通用内核 LPE 是否仍可利用,必须对 6.18.21 镜像做与 §4 相同的「修复是否在」检查(反汇编目标函数),不能只看公告日期;RefluXFS 因内核无 XFS 可直接排除。

6.3 6.18.21 内核漏洞面复核(2026-10-19)

可达性实测(shell 域,仅能力探测):AF_ALG、XFRM、RxRPC、NETLINK_NETFILTER、io_uring_setup 均被 SELinux 拒绝(EACCES);unshare(CLONE_NEWUSER|CLONE_NEWNET) 返回 EINVAL(用户命名空间未启用);/dev/kgsl-3d0 与 /dev/dma_heap/* 可打开。AOSP app_neverallows.te 明确 neverallow all_untrusted_apps *:alg_socket *;[12],因此 Copy Fail 对普通 App 不可达,与内核是否修复无关。

Bad Epoll(CVE-2026-46242):NVD 标注 6.4 ≤ v < 6.18.33 均受影响[10];本机 6.18.21 虽低于修复版,但反汇编 ep_remove(+0x394a5c)显示已含修复结构:file_ref_get 原子 pin(ldaddal + 符号位检查 + 失败返回)、随后 bl 0x7943a4(ep_remove_epi)、内联 ep_refcount_dec_and_test。→ 修复已回填。

KGSL(Adreno):msm_kgsl.ko 是独立模块(/vendor/lib/modules),/dev/kgsl-3d0 对 App 可达。唯一已武器化的公开 KGSL LPE 是 CVE-2026-21385(2026-03 修复、CISA KEV,整数溢出),7 月构建应已包含;Qualcomm 2026-10 公告另列影响 SM8975 的 Graphics 类 CVE-2026-25291(UAF)/ 57545(指针解引用)/ 57554[11],目前无公开 PoC,152 包(security_patch_vendor=2026-10-01)应含对应修复。

结论:当前没有可直接运行的公开链。唯一已确认未修复且有公开 Android PoC 的是 CVE-2026-64560(见 §6.4),但 PoC 绑定 Pixel 偏移,需移植到本机 build。

6.4 8–10 月盲区:CVE-2026-64560 已确认未修复

CVE-2026-64560(posix-cpu-timers 与 non-leader exec() 竞态导致的 k_itimer UAF)影响 5.7–6.18.41[13];本机 6.18.21 在范围内。反汇编 posix_cpu_timer_del(+0x579f04)与 6.18.21 源码一致:单次 pid_task → __lock_task_sighand(+0xb854c)→ 失败走 WARN_ON_ONCE,没有修复版新增的 timer_lock_sighand() 重试循环。两处独立佐证:① 对照组一加 15 的 6.12.23(构建于修复前)posix_cpu_timer_del 与本机指令结构逐项一致;② __lock_task_sighand 的 sighand == NULL 路径没有 dmb ishld,而修复版 smp_acquire__after_ctrl_dep() 在 arm64 上必然编译出该屏障(__smp_rmb() = dmb(ishld))。→ 本机未修复。

漏洞真实性复核:上游修复 920f893f…(Thomas Gleixner,2026-07-03);稳定回填 6a7ecc25… 进入 6.18.41——该版本 ChangeLog 仅 7 KB,是单修复安全更新[16];Red Hat RHSA-2026:65708(2026-09-09);NVD 影响 5.7–6.18.41。

NebuSec 的 Android PoC[15] 在 Pixel 10 Pro(Android 17 / 6.6.118)上实测 >50% 成功率,从 adb shell 直接运行即可关 SELinux 拿 root;它不依赖 userns / AFALG / io_uring,只用 POSIX timer、execv、futex、membarrier。限制是按 ro.build.fingerprint 硬编码了 Pixel 的链接偏移(`BLAZER/LINK_),在本机运行会被TARGET_PROFILE_GATE_FAIL` 拒绝——要落地必须为 PYB110 的 6.18.21 重新生成偏移并适配结构布局。

同期另两条 App 可达的通用候选是 futex 私有哈希的 CVE-2026-80758 / 80776(修复在 6.18.46/47,本机同样在受影响区间[14]),但暂无公开 PoC。其余 8–10 月 NVD 条目多为网络/文件系统驱动缺陷,需要 CAP_NET_ADMIN 或挂载权限,普通 App 不可达。

7. CVE-2026-64560 移植工程(cve-root-oneplus16)

工程位于 /Users/yanghuan/Code/eperf-exporter/source/cve-root-oneplus16,目标是把 NebuSec 的 Pixel PoC 移植到 PYB110 / 6.18.21 并做受控验证。目录结构对齐 cve-root-oneplus15:

路径 内容
reference/NebuSec-CyberMeowfia/ 上游 PoC 副本,pin 94ae8ab301b2ec9f36bdd66321b6e11209ae60d1(exploit.c 5206 行)
tools/extract_rs/ kallsyms 恢复 + sym_offsets 批量符号查询(--analysis 可跳过 GhostLock 门禁)
tools/dump_parts.py 从 OTA payload.bin 抽任意分区(system_ext / odm / vendor_dlkm)
tools/btf_dump.py / btf_elf_dump.py BTF 定位与结构体字段导出(本轮未找到 6.18 BTF)
scripts/build-native.sh NDK r29 / darwin 交叉编译静态 PoC
prebuilts/android-aarch64/exploit-64560 构建产物(2.4 MB)

7.1 已确认的事实

  • 漏洞真实:上游修复 920f893f…(Thomas Gleixner,2026-07-03);稳定回填 6a7ecc25… 进入 6.18.41,该版本是单修复安全更新;Red Hat RHSA-2026:65708(2026-09-09);NVD 影响 5.7–6.18.41。
  • 本机未修复:§6.4 的反汇编 + 对照组结论。
  • PoC 真实可编译:NDK r29 静态编译通过(-Wall -Wextra -Werror)。
  • 安全冒烟:原样推送到设备运行,在指纹门禁安全退出(TARGET_PROFILE_GATE_FAIL fingerprint=OnePlus/PYB110/...,exit 2),未触发任何利用路径。

以 KIMAGE_TEXT_BASE = 0xffffffc080000000(待最终核验)为基址:

PoC 常量 6.18.21 符号 / 偏移 状态
LINK_IMAGE_BASE _text = 0x0 ✅(VA_BITS=39,PAGE_OFFSET=0xffffff8000000000)
LINK_SDATA / LINK_END _sdata = 0x26c0000 / _end = 0x29c0000 ✅(_end 在 BSS,文件不包含)
LINK_INIT_TASK init_task = 0x26df240 ✅
LINK_INIT_CRED init_cred = 0x26f2a50 ✅
LINK_SELINUX_STATE selinux_state = 0x2979630 ✅
LINK_SELINUX_BLOB_SIZES selinux_blob_sizes = 0x1a73af8 ✅
LINK_KMALLOC_CACHES kmalloc_caches = 0x1a69418 ✅
LINK_ANON_PIPE_BUF_OPS anon_pipe_buf_ops = 0x1405690 ✅
LINK_MISC_LIST misc_list = 0x280e0f0 ✅
LINK_SYSCTL_BOOTID sysctl_bootid = 0x298ccb0 ✅
LINK_UHID_MISC / LINK_UHID_FOPS uhid_misc = 0x2847f90 / uhid_fops = 0x1587bc0 ✅
LINK_CONFIGFS_READ_ITER / BIN_WRITE_ITER 0x7d0348 / 0x7d08fc ✅
LINK_MEMSTART_ADDR / KIMAGE_VOFFSET memstart_addr = 0x1a60e70 / kimage_voffset = 0x1a62000 ✅
LINK_MODULE_DIRECT_BASE / PLT_BASE module_direct_base = 0x1a60e88 / module_plt_base = 0x1a60e90 ✅
LINK_CYCLE_C / D init_task+0x7c8(thread_node)/ init_signals+0x10(thread_head) ✅ 结构扫描唯一互链 pair;一加 15 上同构
LINK_WAIT_CHLDEXIT init_signals+0x28(wait_chldexit.head,自环) ✅ 静态镜像 0x26e04e8;PID 0 永不 wait()
LINK_ASHMEM_* 6.18 为 Rust ashmem(mangled MiscDevice vtable),无 C file_operations ⚠️ 需重写该阶段
BLAZER_* / FRANKEL_* direct_bootid_parent 需按 SM8975 物理布局重算 ❌ 未完成

7.3 结构体偏移(反汇编实测,2026-10-19)

6.18 Image 与厂商模块均无 BTF,以下偏移由目标函数的机器码与 6.18.21 源码逐项对照得到:

表|PoC 需要的结构体字段(6.18.21 实测)

结构体 字段 偏移 依据
task_struct tasks 0x648 copy_process 的 list_add(init_task+0x648)
pid / tgid 0x718 / 0x71c copy_process 的 str w8,[x20,#0x718/0x71c];exchange_tids
group_leader 0x758 copy_process 的 str x20,[x20,#0x758]
thread_pid 0x780 exchange_tids
real_cred / cred 0x900 / 0x908 commit_creds / __put_cred
comm 0x918 __set_task_comm(add x22,x19,#0x918,长度 16)
signal / sighand 0x960 / 0x968 __task_pid_nr_ns / __lock_task_sighand
cred usage 0x0 __put_cred
ids(uid..fsgid) 0x8–0x24 commit_creds 的 uid/gid 比较
securebits 0x28 源码布局
cap_* 0x30–0x50 commit_creds
security 0x80 security_cred_free(ldr x8,[x0,#0x80])
user / user_ns / ucounts / group_info 0x88 / 0x90 / 0x98 / 0xa0 __put_cred
sizeof(cred) 0xb8 cred_init(mov w1,#0xb8)
struct page flags / compound_head 0x0 / 0x8 __free_pages_ok
sizeof(page) 0x40 KMI 稳定(与 6.6 同)
mm_struct 对象大小(slab stride) 0x580(1408) /proc/slabinfo:mm_struct ... 1408 23 8(8 页 slab = 0x8000)
pipe_buffer page / offset / len / ops / flags / private 0x0 / 0x8 / 0xc / 0x10 / 0x18 / 0x20 anon_pipe_write(str x10,[x9,#0x10] 写 anon_pipe_buf_ops)
sizeof 0x28 与 PoC 的 _Static_assert 一致
miscdevice minor / fops / list / parent 0x0 / 0x10 / 0x18 / 0x28 misc_open(ldur w9,[x8,#-0x18]、ldur x23,[x8,#-0x8])
k_itimer it_clock 0x24 posix_cpu_timer_del
it.cpu.node / head / pid / firing 0xe0 / 0x100 / 0x108 / 0x120 posix_cpu_timer_del
对象大小(slab stride) 0x180(384) /proc/slabinfo:posix_timers_cache ... 384 21 2(2 页 slab = 0x2000)
fake 节点落位 0xe0(不是 6.6 的 0x78) 一加 15 6.12 的 posix_cpu_timer_del 为 add x21,x19,#0x78,本机为 #0xe0
configfs_buffer page 0x10 configfs_read_iter
needs_read_fill / read_in_progress / write_in_progress 0x48 / 0x4c / 0x4d configfs_read_iter / bin_write_iter
bin_buffer / bin_buffer_size / cb_max_size 0x50 / 0x58 / 0x5c configfs_bin_write_iter

物理布局(2026-10-19 源码 + 静态镜像复核):base DTB 的 /soc/memorymap 显示 memory@C7800000 是 256MB Kernel 区;设备的 /proc/zoneinfo 给出 start_pfn: 229376 → 229376 × 4096 = 0x38000000,这是 memblock_start_of_DRAM() / Normal zone 起点。6.18 arch/arm64/mm/init.c 的 memstart_addr = round_down(memblock_start_of_DRAM(), ARM64_MEMSTART_ALIGN),4K 页 + VA39 下 ARM64_MEMSTART_ALIGN = 1GB → round_down(0x38000000, 1GB) = 0。因此:

  • memstart_addr = 0(0 是合法值;/proc/zoneinfo 的 start_pfn 不能直接当 memstart_addr);
  • kernel_phys_load = 0xc7800000、P0_PAGE_OFFSET = 0xffffff8000000000;
  • delta = 0xc7800000 - 0 = 0xc7800000;
  • PYB110_DIRECT_BOOTID_PARENT = 0xffffff80c8d2afb0;
  • PYB110_DIRECT_CYCLE_C = 0xffffff80c9edfa08;
  • PYB110_DIRECT_WAIT_CHLDEXIT = 0xffffff80c9ee04e8。

之前的 0x8f800000 / memstart=0x38000000 是把 zone 起点当成了 memstart_addr,漏掉了 round_down 这一步。canary 的 changed_nodes=1 出现在 0xc7800000 口径,恰好说明该口径正确,而不是「写到别的地址」。

⚠️ 与 PoC 的 6.6 硬编码相比,configfs_buffer 的 0x50/0x58/0x60/0x64 需要整体前移 8 字节(needs_read_fill 从 0x50 → 0x48),task_struct 的 pid/comm 等偏移全部不同。这些必须在移植时替换,不能沿用 Pixel 值。

7.4 分阶段测试计划(用户建议的「逐步探测」)

原则:每阶段只做一件事,预期不符立即重启(重启即清除内核内存改动),全部通过后才执行最后一次 cred 写入。

表|S0–S5 阶段与 gate

阶段 动作 预期结果 不符时
S0 构建 + 指纹门禁 原样 PoC:TARGET_PROFILE_GATE_FAIL;移植后:GATE_PASS profile=pyb110 编译失败/非预期输出 → 停止
S1 只读 profile 自检:打印符号/结构体偏移/物理布局,不发起内核写 与 §7.2/§7.3 完全一致 任一常量缺失/不一致 → 停止
S2 KASLR 泄露(stage0):向 sysctl_bootid 写入已知地址,回读并还原 算出的 slide 与 LINK_IMAGE_BASE 一致;boot_id 恢复 回读不一致 → 立即重启
S3 misc bridge 只读:通过 fake misc 读取一个已知内核指针 读到预期值(如 uhid_misc 地址) 读失败/值不符 → 重启
S4 cred 定位(只读):在 direct map 扫描 shell 进程的 cred 找到 uid=2000 的 cred,且字段自洽 扫描不到/多解 → 重启
S5 一次性 cred 写入:把目标 cred 改成 init_cred uid=0 + SELinux 关闭 任一 gate 失败 → 重启,不重试

实现上已在移植版中加入 NEBUSEC_STAGE(0 = 只打印 profile,2 = KASLR 后退出,3 = bridge 后清理退出,4 = phys 探测后清理退出,5 = 完整),并在 /Users/yanghuan/Code/eperf-exporter/source/cve-root-oneplus16 落地。

执行记录(2026-10-19):

阶段 参数 结果
S0 原样/移植版 TARGET_PROFILE_GATE_PASS profile=pyb110;NEBUSEC_PROFILE_PASS,exit 0
S1 只读 profile 自检 与 §7.2/§7.3 全部一致(host 与设备各一次)
S2(第一次) Blazer 调参(batch 1 / repeats 24) 5 个 attempt 全部 STAGE0_MISS_CLEAN(竞态未命中);无内核写;设备随后重启(uptime 归零),内核未受损
S2(第二次) Frankel 调参(batch 16 / repeats 64) 命中竞态 → panic(日志停在 SPRAY_DONE attempt=1);设备重启,内核完好
S2(第三次,delta 0x8f800000) batch 16 仍停在 SPRAY_DONE → panic;该 delta 本身错误
S2(第四次,完整 direct 加法 0x90d2afb0/0x91edfa08,仍是 0x8f800000 口径) batch 16 仍然 panic;根因是 cleanup_timerqueue 无限循环(§7.7)
S2(第五次,memstart=0 / delta 0xc7800000 + wait_chldexit) batch 16 代码/生成器已修正、编译通过;待复测

结论:memstart_addr=0(1GB round_down)与 delta=0xc7800000 已由源码 + 静态镜像确认;S2 的 panic 不是物理起址,也不是 firing 路径,而是 cleanup_timerqueue 删除 fake 时 cycle_c/cycle_d 互指导致 rb_next 无限循环(§7.7)。暂停真机写入,先完成离线校准与复测准备。

7.5 待解决

  1. S2 cleanup 死循环(已修复,待复测):节点布局(parent 0 / right 8 / left 0x10 / expires 0x18)与 slot 越界(31→20)已确认;it_signal=1 的 firing 加固已编译,但 canary 仍 panic,说明主因不在 firing。实际是 posix_cpu_timers_exit_group() → cleanup_timers() → cleanup_timerqueue() 无条件删除 fake,而 forged_child=cycle_c 是 thread_node(与 thread_head 互指),rb_next 无限往返。改为 init_signals.wait_chldexit.head(自环、PID 0 不用)后有限次终止;read_validated_result 接受 q0==q1,slide_from_pointer 增加 LINK_WAIT_CHLDEXIT。代码/生成器已改,编译通过,尚未真机复测;
  2. Rust ashmem:PoC 的 fake-fops 阶段需要 ashmem 的 ioctl/open/release;6.18 的 ashmem 是 Rust(mangled MiscDevice vtable),与 C file_operations 不兼容,S3 前必须重写或改用 uhid 路径;
  3. S2 调参:batch 16 已能命中竞态;几何/地址修正后再调 EXEC_AFTER_DELETED/PRIME_*;
  4. 剩余结构体偏移:mm_struct 字段(若后续需要)、k_itimer 其余字段;
  5. PoC 常量:LINK_*、DP_*、CFG_*、FAKE_CPU_TIMER_* 已参数化;LINK_ASHMEM_* 仍为 0。

7.6 风险边界

  • 利用只改内核内存,不写 bootloader/分区/verified boot → 不会因写固件变砖;
  • 真实风险是 panic 与数据损坏(页缓存/文件系统),极端情况下需要 recovery/恢复出厂;
  • 测试前必须备份;152 全量包已在设备 Download/,可作为恢复镜像。

7.7 Cleanup 死循环:根因与修复(2026-10-19)

现场与静态证据:

  • kernel.raw 静态布局:init_task+0x7c8(thread_node)与 init_signals+0x10(thread_head)是互指 list pair(next/prev 都指向对方),两者 +0x10 均为 0;init_task+0x770 是另一个自环 list_head,init_signals+0x28 是 wait_chldexit.head 自环(0x26e04e8),init_task.thread_pid 指向 init_struct_pid(0x26f21d0,count=1)。
  • cleanup_timerqueue()(6.18 kernel/time/posix-cpu-timers.c)对队列无条件 while ((node = timerqueue_getnext(head))) timerqueue_del(head, node);;timerqueue_del() → rb_erase_cached() 在 root->rb_leftmost == node 时先 leftmost = rb_next(node),再 rb_erase(),最后 RB_CLEAR_NODE(node)。
  • 每次 timerqueue_del 后还会 ctmr->head = NULL;node 是 struct cpu_timer 的首成员,head 在 +0x20。删除 X 时写的是 init_signals+0x48(shared_pending.list.prev,静态自指 0xffffffc0826e0500),写 0 后 list.next 仍自指、list_empty 仍为真,PID 0 不使用 shared_pending;此前「写 node-0xc0」的说法是误把 k_itimer+0xe0 当成了 cpu_timer 的成员偏移。
  • 当 forged_child = cycle_c(thread_node)时,rb_erase(fake) 把 cycle_c 写到 cycle_d 的 __rb_parent_color(以及 boot_id 的 ctl_table.data),rb_next 在 cycle_c ↔ cycle_d 间往返,leftmost 永远非空 → cleanup 死循环;panic_on_rcu_stall=1(cmdline)约 21 秒后把 RCU stall 转成 panic。S2 日志停在 SPRAY_DONE 正是这个时间窗,不是写入瞬间的野写 fault。
  • collect_timerqueue() 不走:fake 的 expires=UINT64_MAX 且 ctmr->head=0,cpu_timer_dequeue() 直接跳过;因此写原语只在退出路径的 cleanup 中触发。

修复几何:

  • forged_parent 仍为 boot_id 的 ctl_table 父项(direct alias 0xffffff80c8d2afb0,memstart=0 / delta=0xc7800000)。
  • forged_child 改为 init_signals.wait_chldexit.head 的 direct alias 0xffffff80c9ee04e8。它是自环(next=prev=self)且 PID 0 永不 wait(),运行时保持空。
  • rb_erase(fake) 先把 X_D 写到 ctl_table.data;cleanup 删除 X_D 时 rb_next 返回其 image alias X_image,再删一次后 X_image 自环;下一次 RB_EMPTY_NODE 成立,cleanup 终止。最终 ctl_table.data = X_image,回读 q0 = q1 = X_image = LINK_WAIT_CHLDEXIT + slide。
  • 代价:最后一次删除已 RB_CLEAR 的空节点会触发一次 WARN_ON_ONCE;cmdline 未设 panic_on_warn(ro.config.panic_on_warn 为空,sysctl 被 SELinux 拒绝直读),预计不致命;若复测发现 panic_on_warn=1,改用静态镜像里的 0x27b7b70 自环 + 0x268e880 零节点几何。
  • read_validated_result() 放宽为 qword0 == forged_parent || qword0 == qword1;slide_from_pointer() 的 link 表加入 LINK_WAIT_CHLDEXIT。
  • load_physical_layout() 去掉 !memstart_addr 误判,改为 1GB 对齐检查(memstart=0 合法)。
  • Canary 判定:NEBUSEC_CANARY 下 forged_parent=forged_child=bootid_data(0xffffff80ca18ccb0),写后 q0==forged_parent 即打印 NEBUSEC_CANARY_PASS 并退出,不要求 slide;用于单独验证 direct 写地址。

状态:代码、生成器、profile 已同步修改;scripts/build-native.sh 通过;指针级 rbtree 模拟验证 cleanup 4 次迭代内终止、最终 q0=q1。尚未真机复测(延续暂停写入约定)。

7.8 源码复核:firing 路径与 it_signal 修复

读 v6.18.21 源码后确认的 UAF 路径与修复:

  • 写原语(更正):不在 check_*_timers()。collect_timerqueue() 因 fake expires=UINT64_MAX 且 ctmr->head=0 直接返回;写发生在退出路径 cleanup_timerqueue() 无条件 timerqueue_del(real_head, &fake->node)。disarm_timer() 同样因 ctmr->head=0 跳过。
  • firing 崩溃点:posix_timer_queue_signal() 先检查 posixtimer_valid(timer),其定义为 !(it_signal & 1)。fake it_signal=0 → valid → 继续 posixtimer_send_sigqueue(),使用 fake 的 it_pid/sigq(全 0)→ 崩溃。
  • 修复:把 fake k_itimer 的 it_signal(结构体 +0x30)设为 1(奇数 → invalid),queue_signal 直接返回;cpu_timer_fire() 仍会写 it_status/expires,不影响泄露。
  • 实现:build_fake_fragment() / build_misc_fragments() 在 offset - 0xb0 处写 1(offset 是 node 的 0xe0 偏移);已加入 tools/port_pyb110.py 并生成到 src/exploit-pyb110.c。

该修复已编译通过,但 2026-10-19 canary 实测:带 it_signal=1 仍在 SPRAY_DONE 后 panic——说明 firing 路径不是主因。根因是退出路径 cleanup_timerqueue() 的 cycle_c/cycle_d 互指死循环(§7.7);it_signal=1 保留为加固,不承担 S2 修复。

8. 结论与边界

  • 一加 16(PYB110,6.18.21-android17-5-gf957accaf563-ab15926176-4k)不可用 CVE-2026-43499 临时 root:修复已在该内核中(直接证据),与安全补丁级、运行时前提无关。
  • 同期公开的 AtlasService → olc2 链也已修复(audioDumpInfo 白名单 anr_log / audio_log),两条最现实的临时 root 路线在本机都关闭。
  • 升级到 17.0.0.152 会带来 2026-10-01 补丁级,但内核不变,不改变上述结论。
  • 通用内核 LPE 已逐条核验(§6.3–§6.4):Copy Fail / Dirty Frag 因 SELinux 不可达,Bad Epoll 已修复,RefluXFS 无 XFS 排除;CVE-2026-64560 已确认未修复且有公开 Android PoC,但需移植本机偏移;futex 私有哈希(80758/80776)受影响但无公开 PoC。
  • 移植工程 cve-root-oneplus16 已建立(§7):PoC 已编译、门禁冒烟通过、LINK_* 符号与 CYCLE_C/D 已映射、task_struct/cred/page/pipe_buffer/miscdevice/configfs_buffer 的字段偏移已用反汇编实测(§7.3);物理布局已修正为 memstart=0 / delta=0xc7800000,S2 cleanup 死循环已用 wait_chldexit 自环修复(§7.7,待真机复测);Rust ashmem 仍是 S3 的下一步,目前不能宣称「可提权」。
  • 若目标是永久 root,官方 BL 解锁 + 修补 init_boot 是当前唯一可靠路径(与漏洞无关,会清数据)。
  • 临时 root 不持久、不解锁 BL;本页不构成任何「已 root」表述。
  • 仅限授权安全研究与教育用途。

9. 可复现命令

# 1) 从全量 OTA 取 boot.img(若已有 boot.img 可跳过)
python3 source/cve-root-oneplus15/tools/extract_boot_from_ota.py \
  ~/Downloads/ColorOS-PYB110-17.0.0.152-CN01-A.13/oneplus16-PYB110-17.0.0.152-CN01-A.13.zip <outdir>

# 2) 编译提取器(ghostlock-app/tools/extract_rs)
cargo build --release

# 3) 漏洞存在性门禁(已修复会直接报错退出)
./build/extract/release/ghostlock-extract <boot.img> --format json

# 4) 16 工程:构建 NebuSec PoC 并做指纹门禁冒烟(未移植时安全退出)
cd source/cve-root-oneplus16
scripts/build-native.sh
adb push prebuilts/android-aarch64/exploit-64560 /data/local/tmp/nebusec-64560
adb shell 'chmod 755 /data/local/tmp/nebusec-64560; /data/local/tmp/nebusec-64560'   # 预期 TARGET_PROFILE_GATE_FAIL

# 5) 6.18.21 符号查询(工具自包含)
cd source/cve-root-oneplus16/tools/extract_rs && cargo build --release --example sym_offsets
../../build/extract/release/examples/sym_offsets <boot.img> init_task init_cred misc_list sysctl_bootid

参考文献

[1]: https://www.cve.org/CVERecord?id=CVE-2026-43499 [2]: https://github.com/JoinChang/ghostlock-oneplus [3]: https://github.com/YuKongA/ghostlock-app [4]: /Users/yanghuan/Code/eperf-exporter/source/cve-root-oneplus15 [5]: https://opensource.oneplus.com [6]: https://blog.nns.ee/2026/09/24/oneplus-root/ [7]: https://calif.io/research/oempocalypse [8]: https://source.android.com/docs/security/bulletin/2026/2026-10-01 [9]: https://cert.europa.eu/publications/security-advisories/2026-005/ [10]: https://nvd.nist.gov/vuln/detail/CVE-2026-46242 [11]: https://docs.qualcomm.com/product/publicresources/securitybulletin [12]: https://android.googlesource.com/platform/system/sepolicy/+/refs/heads/main/private/app_neverallows.te [13]: https://nvd.nist.gov/vuln/detail/CVE-2026-64560 [14]: https://nvd.nist.gov/vuln/detail/CVE-2026-80758 [15]: https://github.com/NebuSec/CyberMeowfia [16]: https://cdn.kernel.org/pub/linux/kernel/v6.x/ChangeLog-6.18.41 [17]: https://access.redhat.com/security/cve/cve-2026-64560

1. CVE Program. CVE-2026-43499: rtmutex: Use waiter::task instead of current in remove_waiter()[EB/OL]. 2026. https://www.cve.org/CVERecord?id=CVE-2026-43499 ↩
2. JoinChang. ghostlock-oneplus: GhostLock (CVE-2026-43499) kernel exploit for Android devices with locked bootloader[EB/OL]. GitHub, 2026. https://github.com/JoinChang/ghostlock-oneplus (修复版本表、支持设备、PGO 判定表) ↩
3. YuKongA. ghostlock-app: CVE-2026-43499 exploit app[EB/OL]. GitHub, commit 24c9c511258ca534c325c3127be4c67b184912ff. https://github.com/YuKongA/ghostlock-app (tools/extract_rs:kallsyms/BTF 恢复与 remove_waiter 门禁) ↩
4. 杨欢. cve-root-oneplus15: 一加 15 锁 Bootloader 临时 root 工程[CP/OL]. 2026. 本地工程 /Users/yanghuan/Code/eperf-exporter/source/cve-root-oneplus15(tools/extract_boot_from_ota.py、notes/feasibility.md、notes/offset-gap.md) ↩
5. OnePlus. OnePlus Open Source Code[EB/OL]. 2026. https://opensource.oneplus.com (内核源码发布页) ↩
6. Moorats, Rasmus (nns). Getting root on OnePlus 15 from an untrusted app, via an audio debug service and a vendor HAL[EB/OL]. 2026-09-24. https://blog.nns.ee/2026/09/24/oneplus-root/ ↩
7. Maar, Lukas (Calif). OEMpocalypse Now: A Generic Exploitation Strategy from Android untrusted app to root[EB/OL]. 2026-08-31. https://calif.io/research/oempocalypse ↩
8. Google. Android Security Bulletin—October 2026[EB/OL]. 2026-10-05. https://source.android.com/docs/security/bulletin/2026/2026-10-01 ↩
9. CERT-EU. Security Advisory 2026-005: High Vulnerability in the Linux Kernel ("Copy Fail", CVE-2026-31431)[EB/OL]. 2026. https://cert.europa.eu/publications/security-advisories/2026-005/ ↩
10. NIST. CVE-2026-46242: eventpoll: fix ep_remove struct eventpoll / struct file UAF[EB/OL]. NVD, 2026. https://nvd.nist.gov/vuln/detail/CVE-2026-46242 ↩
11. Qualcomm. October 2026 Security Bulletin[EB/OL]. 2026-10-05. https://docs.qualcomm.com/product/publicresources/securitybulletin ↩
12. Android Open Source Project. private/app_neverallows.te[EB/OL]. AOSP, 2026. https://android.googlesource.com/platform/system/sepolicy/+/refs/heads/main/private/app_neverallows.te ↩
13. NIST. CVE-2026-64560: posix-cpu-timers: Prevent UAF caused by non-leader exec() race[EB/OL]. NVD, 2026. https://nvd.nist.gov/vuln/detail/CVE-2026-64560 ↩
14. NIST. CVE-2026-80758: futex: Avoid private hash use-after-free on final put[EB/OL]. NVD, 2026. https://nvd.nist.gov/vuln/detail/CVE-2026-80758 (另见 CVE-2026-80776) ↩
15. NebuSec. CyberMeowfia: PoCs and exploits for CVEs — Android-CVE-2026-64560[EB/OL]. GitHub, 2026. https://github.com/NebuSec/CyberMeowfia/tree/main/security-research/Ndays/Android-CVE-2026-64560 ↩
16. Linux Kernel Stable. ChangeLog-6.18.41[EB/OL]. 2026. https://cdn.kernel.org/pub/linux/kernel/v6.x/ChangeLog-6.18.41 ↩
17. Red Hat. CVE-2026-64560[EB/OL]. 2026. https://access.redhat.com/security/cve/cve-2026-64560 ↩

© 2026 Yang Huan · yanghuan9812@qq.com

results matching ""

    No results matching ""