最近一直在研究利用AI主动式分析内核问题,都没时间写文章了,就贴一份AI分析一份数组越界访问的报告把,我全程没有干预分析。可以从它分析的流程里我们也能够学到一些东西。

PS : 希望对大家有用,别认为我是在糊弄你们,哈哈~~~


执行摘要

崩溃类型: 数据中止异常(DABT)- 第 3 级转换故障

故障进程: qteeconnector@1(PID 932)- TrustZone 连接器守护进程

故障模块: smcinvoke_dlkm(SMC Invoke 驱动程序)

根本原因: 数组越界访问,位于 prepare_send_scm_msg() 函数,原因是结构体字段损坏,包含无效的边界值(0x80010001 而非正常的小数值如 4-8)。

崩溃发生在迭代回调请求对象数组时。循环边界字段 [structure+8] 包含 0x80010001(2,147,549,185),导致循环运行数百万次,最终访问了 0xffffff8005700000 处的未映射内存。


分析对象

  ┌──────────────┬───────────────────────────────────────────────────────────────────────────┐
  │     对象     │                                    值                                     │
  ├──────────────┼───────────────────────────────────────────────────────────────────────────┤
  │ 转储模式     │ 分离式 ramdump(2 个段)                                                  │
  ├──────────────┼───────────────────────────────────────────────────────────────────────────┤
  │ dump_spec    │ DDRCS0_0.BIN@0x0, DDRCS0_1.BIN@0xC0000000                                 │
  ├──────────────┼───────────────────────────────────────────────────────────────────────────┤
  │ vmlinux_path │ guitar_pre_global-symbols-vmlinux-p85-test-key-0226-userroot-13.0/vmlinux │
  ├──────────────┼───────────────────────────────────────────────────────────────────────────┤
  │ crash_args   │ --kaslr=0x133fa00000 -m vabits_actual=39                                  │
  ├──────────────┼───────────────────────────────────────────────────────────────────────────┤
  │ 模块符号     │ 位于 out/target/product/guitar/                                           │
  ├──────────────┼───────────────────────────────────────────────────────────────────────────┤
  │ 会话 ID      │ 7f844117-0b6c-4c08-9a87-b891569bbc1b                                      │
  ├──────────────┼───────────────────────────────────────────────────────────────────────────┤
  │ drgn 可用    │ 否(仅使用 crash 内置命令)                                               │
  └──────────────┴───────────────────────────────────────────────────────────────────────────┘

会话模式与约束

  • 分离式 ramdump 需要使用 dump_spec 参数而非 vmcore_path

  • KASLR 偏移 0x133fa00000 已应用于符号解析

  • VABITS=39 表示 39 位虚拟地址空间(512GB VA)

  • drgn 不可用 - 仅使用 crash 内置命令进行调查

  • 模块符号从 KALLSYMS 加载(smcinvoke_dlkm 基址:0xffffffd34bc5c000)


调查过程与命令级推理

步骤 1:初始回溯

可用证据: 会话刚打开,无先前信息

假设: 需要识别崩溃位置和调用栈

选择的命令: bt

原因: 标准第一步,识别崩溃函数和调用栈

结果:

PID: 932 TASK: ffffff8036a0b600 CPU: 5 COMMAND: "qteeconnector@1"

分析: bt 命令仅返回任务头信息,没有完整的堆栈跟踪,表明需要从内核日志获取崩溃上下文。


步骤 2:内核日志检查

可用证据: 单行 bt 输出,需要更多崩溃上下文

假设: 内核日志应包含完整的 oops 消息和寄存器状态

选择的命令: log | tail -n 300

原因: 日志缓冲区包含完整的崩溃转储,包括寄存器状态、调用跟踪和任何先前的错误消息

结果: 获得丰富的证据:

  • 完整的 oops 消息及寄存器转储

  • 通过 smcinvoke_dlkm 的调用跟踪

  • 崩溃前的重复错误消息

  • 内核 panic 和 CPU 停止跟踪

假设如何改变: 日志揭示这是 0xffffff8005700000 处的页面故障,位于 prepare_send_scm_msg+0x400,崩溃前有"Request object is not a callback object"和"server is invalid, res: -90"错误。


步骤 3:模块识别

可用证据: smcinvoke_dlkm 中崩溃,需要模块基址和符号

假设: smcinvoke_dlkm 是内核模块,需要确认已加载

选择的命令: mod smcinvoke_dlkm

原因: 验证模块加载状态并获取基地址以进行符号解析

结果:

ffffffd34bc74d80 smcinvoke_dlkm ffffffd34bc5c000 204800

分析: 模块确认加载于 0xffffffd34bc5c000,大小 200KB。


步骤 4:故障函数反汇编

可用证据: 崩溃于 prepare_send_scm_msg+0x400,需要理解代码

假设: 故障指令应揭示哪个内存访问失败

选择的命令: dis prepare_send_scm_msg

原因: 识别偏移 0x400 处的确切指令并理解数据流

结果: 识别关键代码路径:

  0xffffffd34bc67b44 <+1016>: mov     w22, wzr              // w22 = 0(初始化计数器)
  0xffffffd34bc67b48 <+1020>: add     x26, x19, #0xc        // x26 = 数组基址
  0xffffffd34bc67b4c <+1024>: ldr     w9, [x26, w22, sxtw #2]  // w9 = array[w22] <-- 崩溃
  0xffffffd34bc67b54 <+1032>: ldr     w8, [x19, #4]
  ...
  0xffffffd34bc67b68 <+1052>: bl      process_tzcb_req
  0xffffffd34bc67b6c <+1056>: ldr     w8, [x19, #8]         // w8 = 边界
  0xffffffd34bc67b70 <+1060>: add     w22, w22, #1          // w22++
  0xffffffd34bc67b74 <+1064>: cmp     w22, w8
  0xffffffd34bc67b78 <+1068>: b.cc    <+1024>               // 当 w22 < w8 时循环

假设如何改变: 崩溃发生在循环中,该循环:

  1. 初始化 w22 = 0

  2. 设置 x26 = x19 + 12 为数组基址

  3. 每次迭代加载 array[w22]

  4. 递增 w22 并与边界 w8 = [x19+8] 比较

寄存器转储显示 x22 = 0x2b97f9(280 万),意味着循环运行了过多迭代次数。


步骤 5:结构体检查

可用证据: x19 指向结构体,x26 = x19 + 12,边界在 [x19+8]

假设: x19 处的结构体可能有损坏的字段

选择的命令: rd 0xffffff8004c1a000 4

原因: 检查完整结构体并识别边界字段值

结果:

ffffff8004c1a000: 8000000000000001 0000000000000001

ffffff8004c1a010: 0000001400000028 0000000080010001

识别的结构体布局:

  • 偏移 0x00: 0x8000000000000001 - 头/标志

  • 偏移 0x08: 0x0000000000000001 - 计数/类型

  • 偏移 0x10: 0x28(40),0x14(20)- 打包的维度

  • 偏移 0x18: 0x80010001 - 这是边界字段 [x19+8]

假设如何改变: 边界值 0x80010001(2,147,549,185)明显无效。回调数组的合理边界应为 4-8,而非 20 多亿。这确认了结构体损坏或未初始化内存。


步骤 6:故障地址验证

可用证据: x26 = 0xffffff8004c1a01c,故障 = 0xffffff8005700000

假设: 验证数学计算确认越界访问

计算:

故障偏移 = 0xffffff8005700000 - 0xffffff8004c1a01c

= 0xae57e4 字节

数组索引 = 0xae57e4 / 4(字长)

= 0x2b97f9(2,856,953)

结果: 与寄存器 x22 = 0x2b97f9 完全匹配。

分析: 循环运行了 280 多万次迭代后命中未映射内存。


步骤 7:先前错误分析

可用证据: 日志显示崩溃前有重复的 smcinvoke 错误

假设: 错误可能指示根本原因或损坏状态

选择的命令: log | grep -A2 "smcinvoke: process_tzcb_req" | head -20

原因: 理解崩溃前发生了什么错误

结果:

  smcinvoke: process_tzcb_req: Request object is not a callback object
  smcinvoke: process_tzcb_req: server is invalid
  smcinvoke: process_tzcb_req: server invalid, res: -90

分析: 错误 -90(ENOTCONN 或自定义错误)和"server is invalid"消息表明 smcinvoke 子系统在崩溃前已处于损坏状态或接收格式错误的请求。


步骤 8:进程上下文

可用证据: qteeconnector@1 中崩溃,需要理解调用者

假设: 多个 smcinvoke 文件描述符可能相关

选择的命令: files 932

原因: 查看进程打开了哪些 smcinvoke 资源

结果:

FD 6, 7, 8: smcinvoke 设备文件

分析: 进程有 3 个打开的 smcinvoke 文件描述符,表明存在多个并发的 SMC invoke 会话。


关键证据

崩溃位置

  ┌──────────┬───────────────────────────────┐
  │   项目   │              值               │
  ├──────────┼───────────────────────────────┤
  │ 函数     │ prepare_send_scm_msg+0x400    │
  ├──────────┼───────────────────────────────┤
  │ 模块     │ smcinvoke_dlkm                │
  ├──────────┼───────────────────────────────┤
  │ 指令     │ ldr w9, [x26, w22, sxtw #2]   │
  ├──────────┼───────────────────────────────┤
  │ 故障地址 │ 0xffffff8005700000            │
  ├──────────┼───────────────────────────────┤
  │ 故障类型 │ 第 3 级转换故障(未映射 PTE) │
  └──────────┴───────────────────────────────┘

崩溃时寄存器状态

 ┌────────┬────────────────────┬──────────────────────────────┐
 │ 寄存器 │         值         │             含义             │
 ├────────┼────────────────────┼──────────────────────────────┤
 │ x19    │ 0xffffff8004c1a010 │ 结构体基址                   │
 ├────────┼────────────────────┼──────────────────────────────┤
 │ x22    │ 0x00000000002b97f9 │ 循环计数器(280 万次迭代!) │
 ├────────┼────────────────────┼──────────────────────────────┤
 │ x26    │ 0xffffff8004c1a01c │ 数组基址(x19 + 12)         │
 └────────┴────────────────────┴──────────────────────────────┘

x19 处损坏的结构体


  ┌──────┬─────────────────────┬───────────────┐
  │ 偏移 │         值          │    预期值     │
  ├──────┼─────────────────────┼───────────────┤
  │ +0   │ 0x8000000000000001  │ 头            │
  ├──────┼─────────────────────┼───────────────┤
  │ +8   │ 0x80010001(21 亿) │ 小计数(4-8) │
  ├──────┼─────────────────────┼───────────────┤
  │ +12+ │ 数组数据            │ -             │
  └──────┴─────────────────────┴───────────────┘

先前错误

  • 38+ 次"Request object is not a callback object"

  • 多次"server is invalid, res: -90"

  • 错误在崩溃前约 80ms 开始出现


根本原因评估

最高置信度假设

smcinvoke 驱动程序的内部请求结构体已损坏或未初始化,特别是偏移 +8 处的循环边界字段包含 0x80010001 而非有效的小计数。

证据链

  1. 结构体字段损坏: 边界值 0x80010001 不是合理的数组大小。0x8001xxxx 模式表明:

- 带有调试模式的未初始化栈/内存
- use-after-free 导致的损坏
- 错误的结构体布局解释(错误转换)
  1. 循环无界运行: 边界 = 21 亿,循环迭代 280 多万次后,索引 0x2b97f9 处的访问命中未映射内存。

  2. 先前错误表明系统性问题: 重复的"server is invalid"和"not a callback object"错误表明 smcinvoke 子系统在崩溃前已处于损坏状态。

  3. TrustZone 交互: qteeconnector@1 进程与 TrustZone 固件通信。来自用户空间或固件错误的格式错误请求可能损坏内核结构体。

替代假设(较低置信度)


  ┌──────────────────┬────────────────────────────┬──────────────────────┐
  │       假设       │          支持证据          │       反对证据       │
  ├──────────────────┼────────────────────────────┼──────────────────────┤
  │ Use-after-free   │ 大的损坏值                 │ 无 slab 损坏证据     │
  ├──────────────────┼────────────────────────────┼──────────────────────┤
  │ 竞争条件         │ 并发 smcinvoke 文件描述符  │ 循环计数器不应有竞争 │
  ├──────────────────┼────────────────────────────┼──────────────────────┤
  │ 用户空间恶意输入 │ qteeconnector 处理 TZ 调用 │ 应有输入验证         │
  ├──────────────────┼────────────────────────────┼──────────────────────┤
  │ 固件错误         │ SMC 调用到 TZ              │ 无 TZ 符号无法验证   │
  └──────────────────┴────────────────────────────┴──────────────────────┘

信度与局限性

置信度级别:中-高(70-80%)

为何不是更高:

  1. 无 C 源代码 - 分析基于反汇编和推断的结构体布局

  2. drgn 不可用 - 无法以编程方式遍历内核数据结构

  3. 模块符号有限 - smcinvoke_dlkm 符号来自 KALLSYMS,无完整调试信息

  4. 无法确定损坏来源 - 未知是什么将 0x80010001 写入边界字段

提高置信度所需

  1. 完整内核/模块调试符号 - 显示确切的结构体定义

  2. smcinvoke 驱动程序源代码 - 确认循环逻辑和预期边界

  3. 更早的转储状态 - 可显示结构体何时损坏

  4. qteeconnector 日志 - 显示触发崩溃的请求


建议措施

立即措施

  1. 在 smcinvoke 驱动程序中添加边界验证: // 在 prepare_send_scm_msg() 中,循环前: if (request->count > MAX_CALLBACK_COUNT || request->count == 0x80010001) { pr_err("smcinvoke: 无效的回调计数 %u\n", request->count); return -EINVAL; }

  2. 在入口处添加结构体验证:

- 验证所有指针字段
- 检查幻数/签名
- 拒绝明显损坏的结构体
  1. 启用 smcinvoke 调试日志:

- 跟踪所有 ioctl 调用
- 在入口处记录结构体字段值
- 捕获触发损坏的格式错误请求

调查

  1. 从 Qualcomm/Xiaomi 获取 smcinvoke 驱动程序源代码

  2. 审查 qteeconnector 用户空间代码 查找格式错误的 ioctl 调用

  3. 检查同一时间范围内的相关崩溃

  4. 启用 KASAN 以更早捕获越界访问

长期措施

  1. 添加 smcinvoke 模糊测试 以测试格式错误输入

  2. 在内核中实现 smcinvoke 请求验证

  3. 考虑在内核边界进行 TrustZone 调用清理


报告生成日期: 2026-03-18

会话 ID: 7f844117-0b6c-4c08-9a87-b891569bbc1b

分析师: bsp-stability-tool by @iliuqi