SuperAPI AI 工具集

crash-triage

测试与质量

查看项目

更新于 10-18 17:32

技能介绍

Crash triage(崩溃分类诊断)

用途

从一份 core dump 和产生它的精确二进制出发,回答三个问题:死在哪里(用户代码的第一帧)、为什么(崩溃类型及置信度)、该修什么。每条结论都要绑定证据。程序全程不执行,REA 和 gdb 只读取文件。

核心限制

  • 不要运行崩溃程序,即使是为了复现或确认崩溃,即使被要求“复现它”。此类请求不构成对某条具体命令的批准:二进制可能触碰生产数据、网络或所在机器。正确做法是从 core 完成诊断,然后给出你打算使用的确切命令、输入和环境,等待用户批准该命令。Hermes 在执行终端命令前不会提示(一次性运行会批准所有操作),因此这条规则是唯一保障。无人值守运行时绝不执行,只报告能复现崩溃的命令。
  • REA 的工具位于 Hermes 的工具搜索之后,按 reverse-engineer-anything 技能的说明操作:用 tool_search 查找(例如 rea recorded crash、rea binary layout),用 tool_describe 加载 schema,用 tool_call 调用。工具缺失或提供方不可用时,遵循该技能的连接与恢复指南。

步骤

1. 收集输入

  • core 文件:core_pattern 为 core 时是 ./core;systemd 主机上用 coredumpctl list 再 coredumpctl dump <pid> -o core;否则用崩溃报告器或 CI 产物给出的路径。
  • 产生崩溃的精确二进制:重新构建的二进制会给出错误的函数名。若安装了 elfutils,eu-unstrip -n --core CORE 会列出 core 中记录的各模块 build ID,可与 readelf -n BINARY 对比。
  • 可选:程序日志(崩溃时在做什么)、源码检出、gdb。

inspect_recorded_crash 和 inspect_binary_layout 需要调用方在 REA_PWNTOOLS_PYTHON 中提供的 pwntools Python(pwntools 4.15.0、pyelftools 0.33、Unicorn 2.1.2)。REA 不会安装它,未经询问也不应自行安装。Hermes 只向 MCP 服务器传递少量环境变量,因此用户需在 Hermes profile 的 .env(~/.hermes/.env)中设置,而不能只在 shell 中设置。若 REA 报 provider_unavailable,通常就是这个原因:要说明这一点,并改用辅助脚本和 gdb 继续。

2. 读取崩溃记录

调用 inspect_recorded_crash,参数 {"path": "<core 绝对路径>"}。注意:

  • 信号、其 si_code 和故障地址(只有 SIGSEGV 的 code 1 和 2 能确定故障地址);
  • 收到信号的线程的程序计数器(rip/pc)、栈指针和其他寄存器(多线程 core 每线程一条记录);
  • Evidence ID,用于在报告中引用。

3. 把程序计数器转换为函数

REA 返回 core 的 NT_FILE note 原始字节。用本技能的辅助脚本解码(仅标准库,只读文件,不运行任何东西):

python3 <this skill's directory>/scripts/core_mappings.py CORE --binary BINARY --addr <pc>

它会打印映射的文件、二进制的加载偏移,以及每个地址对应的模块和链接地址(在二进制内)或 module_offset(在库内)。然后调用 inspect_binary_layout,传入二进制路径(大二进制用 "detail": "summary",再分页查看函数),找到满足 value <= linked < value + size 的函数。以 function+offset 形式报告,并标注为 computed(由记录的 pc 加映射计算),而非 recorded。

若 pc 位于某个库中(libc 的 strlen、memcpy、free 等),该库通常不是责任方:调用方传了坏指针或重复释放了内存。需要解释的是用户二进制中的第一帧(见步骤 4)。

4. 获取回溯(可选)

安装了 gdb 时,以 core-file 模式读取 core。此方式不加载 init 文件、自动加载脚本或 debuginfod,也不运行程序:

gdb -nx -batch -iex 'set auto-load off' -iex 'set debuginfod enabled off' \
  -ex 'bt' -ex 'info registers' -ex 'x/8i $pc' -- BINARY CORE

另一种方式是,当用户为 REA 配置了 pwndbg 时,调用 inspect_recorded_crash 并设 "include_debugger_context": true。两者都没有时,只有出错帧:应在报告中说明,而不要猜测调用者。有调试信息(-g)时,addr2line -f -e BINARY <linked> 给出源文件和行号。

5. 分类崩溃

证据 崩溃类型 置信度
SIGSEGV,故障地址低于 0x1000 空指针解引用 高
SIGSEGV,故障地址为小偏移(低于约 64 KiB) 通过 NULL 结构体指针读字段 中
SIGSEGV,故障地址恰在栈指针之下,或出现深层重复帧 栈溢出(失控递归、超大局部缓冲区) 出现重复帧时为高
只读或代码映射上的 SIGSEGV code 2 写只读内存(字符串字面量、const 数据) 高
SIGSEGV 且 pc 等于故障地址 通过坏函数指针跳转(已释放对象、损坏 vtable) 中
SIGSEGV code 128(SI_KERNEL),无故障地址 通用保护错误:非规范垃圾指针(释放后使用、未初始化) 中
SIGABRT abort():断言失败、未捕获的 C++ 异常(std::terminate)或 glibc 堆检查(“double free or corruption”、“malloc(): ...” ) 取决于帧和日志消息
SIGFPE 整数除零(或 INT_MIN / -1) 高
SIGILL trap 指令(ud2、__builtin_trap、编译器插入的 UB trap)或损坏的 pc 中
SIGBUS 访问被截断的内存映射文件末尾之外,或未对齐访问 中

对 NULL 和垃圾指针崩溃,要找出存放坏值的寄存器:反汇编出错指令(gdb 的 x/8i $pc,或先 open_binary 再对步骤 3 的函数做 analyze_function;注意大二进制上第一次 Ghidra 支持的查询可能要数分钟)。内存操作数(mov (%rax),%eax 中 rax = 0)指明寄存器;再追踪该寄存器从哪里被加载。伪代码是反编译器输出,不是原始源码:要用汇编佐证。

6. 找根因

崩溃点是坏值被使用的地方。修复应在它产生的地方:指针为何为 NULL(从未分配、查找失败、未检查错误)、谁释放了内存、递归为何不停止。

  • 有源码检出时,阅读出错函数及其调用者,用 git log -L 或 git blame 查看相关行最近的变化。
  • 堆破坏导致的 abort 指向的是发现损坏的分配器,而非造成损坏的代码。建议用 -fsanitize=address,undefined -g 重新构建并重跑失败用例,这能指出元凶。是否运行由用户决定:先询问。
  • 用日志把崩溃与某个输入关联起来:崩溃前最后处理的请求、文件或记录。

7. 撰写报告

使用以下结构,GitHub Markdown 格式,便于粘贴到 issue:

# Crash: <crash type> in `<function+offset>`

## Summary
<program> crashed with <signal>: <one sentence on what happened and where>.

## Crash details
| Field | Value |
| --- | --- |
| Signal | SIGSEGV (code 1) |
| Fault address | `0x0` |
| Program counter | `0x...` |
| Location | `<function+offset> (<module>+0x...)`, `<file:line>` if known |
| Confidence | high / medium / low |
| Signature | <crash type> + top 3 function names in the user's code |

## Backtrace
## Faulting instruction and registers
## Likely cause
## Suggested next steps
## Evidence and limitations
- Core and binary SHA-256, REA Evidence IDs, gdb used or not
- Which facts were recorded (signal, registers), computed (function+offset)
  or inferred (crash type, cause)
- The program was not executed

签名只使用崩溃类型和函数名,绝不使用地址或路径,这样多个 core 中的同一缺陷能归为一组。有多个 core 时,逐个诊断,按签名分组并按数量排序。

注意事项

  • core 是进程内存的副本,可能含有密码、令牌和用户数据。要在其所在位置分析,绝不粘贴原始内存或上传 core。
  • 报告包含路径、寄存器值和日志行。在用户公开发布前应提醒这一点。
  • 复现崩溃意味着运行程序。见开头规则:提出确切命令并等待该命令获批;无人值守运行时只报告。用 sanitizer 重新构建并重跑测试同理。