最近一直在研究利用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 时循环假设如何改变: 崩溃发生在循环中,该循环:
初始化 w22 = 0
设置 x26 = x19 + 12 为数组基址
每次迭代加载 array[w22]
递增 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 而非有效的小计数。
证据链
结构体字段损坏: 边界值 0x80010001 不是合理的数组大小。0x8001xxxx 模式表明:
- 带有调试模式的未初始化栈/内存
- use-after-free 导致的损坏
- 错误的结构体布局解释(错误转换)循环无界运行: 边界 = 21 亿,循环迭代 280 多万次后,索引 0x2b97f9 处的访问命中未映射内存。
先前错误表明系统性问题: 重复的"server is invalid"和"not a callback object"错误表明 smcinvoke 子系统在崩溃前已处于损坏状态。
TrustZone 交互: qteeconnector@1 进程与 TrustZone 固件通信。来自用户空间或固件错误的格式错误请求可能损坏内核结构体。
替代假设(较低置信度)
┌──────────────────┬────────────────────────────┬──────────────────────┐
│ 假设 │ 支持证据 │ 反对证据 │
├──────────────────┼────────────────────────────┼──────────────────────┤
│ Use-after-free │ 大的损坏值 │ 无 slab 损坏证据 │
├──────────────────┼────────────────────────────┼──────────────────────┤
│ 竞争条件 │ 并发 smcinvoke 文件描述符 │ 循环计数器不应有竞争 │
├──────────────────┼────────────────────────────┼──────────────────────┤
│ 用户空间恶意输入 │ qteeconnector 处理 TZ 调用 │ 应有输入验证 │
├──────────────────┼────────────────────────────┼──────────────────────┤
│ 固件错误 │ SMC 调用到 TZ │ 无 TZ 符号无法验证 │
└──────────────────┴────────────────────────────┴──────────────────────┘信度与局限性
置信度级别:中-高(70-80%)
为何不是更高:
无 C 源代码 - 分析基于反汇编和推断的结构体布局
drgn 不可用 - 无法以编程方式遍历内核数据结构
模块符号有限 - smcinvoke_dlkm 符号来自 KALLSYMS,无完整调试信息
无法确定损坏来源 - 未知是什么将 0x80010001 写入边界字段
提高置信度所需
完整内核/模块调试符号 - 显示确切的结构体定义
smcinvoke 驱动程序源代码 - 确认循环逻辑和预期边界
更早的转储状态 - 可显示结构体何时损坏
qteeconnector 日志 - 显示触发崩溃的请求
建议措施
立即措施
在 smcinvoke 驱动程序中添加边界验证: // 在 prepare_send_scm_msg() 中,循环前: if (request->count > MAX_CALLBACK_COUNT || request->count == 0x80010001) { pr_err("smcinvoke: 无效的回调计数 %u\n", request->count); return -EINVAL; }
在入口处添加结构体验证:
- 验证所有指针字段
- 检查幻数/签名
- 拒绝明显损坏的结构体启用 smcinvoke 调试日志:
- 跟踪所有 ioctl 调用
- 在入口处记录结构体字段值
- 捕获触发损坏的格式错误请求调查
从 Qualcomm/Xiaomi 获取 smcinvoke 驱动程序源代码
审查 qteeconnector 用户空间代码 查找格式错误的 ioctl 调用
检查同一时间范围内的相关崩溃
启用 KASAN 以更早捕获越界访问
长期措施
添加 smcinvoke 模糊测试 以测试格式错误输入
在内核中实现 smcinvoke 请求验证
考虑在内核边界进行 TrustZone 调用清理
报告生成日期: 2026-03-18
会话 ID: 7f844117-0b6c-4c08-9a87-b891569bbc1b
分析师: bsp-stability-tool by @iliuqi