| name | vmcore-analysis |
| description | Linux 内核 VMcore 崩溃转储深度分析技能(双轨:vmcore 逆向 + 源码正向)。当用户提到 vmcore、 kernel panic、系统崩溃、crash dump、内核转储、kdump、内核 oops、系统挂死等关键词时,必须使用本技能。 覆盖场景:空指针解引用、内核栈溢出、内存越界(OOB)、Use-After-Free(UAF)、硬件 MCE 异常、Bit Flip、 内存 UE、死锁、Soft/Hard Lockup、BUG() 触发、OOM panic、驱动异常、文件系统崩溃、网络子系统崩溃、 存储 IO 崩溃、RCU Stall、SMEP/SMAP 触发、KVM/vCPU 异常、ACPI 固件异常、热插拔/页迁移异常。 即使用户只说“帮我分析一下这个 coredump / 服务器崩了 / 内核挂死了”也要触发本技能。 支持源代码级根因分析(有源码时必须走双轨并行并交叉验证)。
|
VMcore 崩溃转储深度分析(双轨:vmcore + 源码)
第一节:故障目录结构
pcie_panic/ # 故障文件夹
├── src/ # 【可选,优先】源码路径,存在时走源码分析主路径
│ ├── drivers/ # 驱动源码
│ └── ...
├── crash # crash 工具
├── vmlinux # 带调试信息的内核符号文件
└── vmcore # vmcore 崩溃转储
cd pcie_panic && crash ./vmlinux vmcore
第二节:分析策略(并行双轨,交叉验证)
vmcore 分析和源码分析应同时进行,而非二选一。 两条轨道相互独立推进,最终交叉比对以确认根因。
┌─────────────────────────────────────────────────────────────────┐
│ 并行双轨分析模型 │
│ │
│ 轨道一:vmcore 分析(逆向) 轨道二:源码分析(正向) │
│ ───────────────────────── ─────────────────────── │
│ 从崩溃快照出发,逆向推理 从函数调用链出发,正向追踪 │
│ │
│ 回答:在哪里崩?崩溃时 回答:为什么崩?代码逻辑 │
│ 数据状态如何? 上哪里有缺陷? │
│ │
│ ↓ ↓ │
│ └────────────── 交叉验证 ────────────┘ │
│ │
│ 见:第三节(统一分析流程:分支决策→双轨并行→交叉验证→输出) │
└─────────────────────────────────────────────────────────────────┘
两条轨道的分工与互补:
| vmcore 轨道 | 源码轨道 |
|---|
| 优势 | 崩溃时的真实数据状态、精确崩溃地址、并发时序痕迹 | 完整因果模型、错误路径可见、并发竞态窗口可分析 |
| 局限 | 只有结果快照,过程不可见;编译优化可能隐藏关键帧 | 需要版本精确匹配;编译器优化可能改变代码结构 |
| 典型盲区 | 错误处理路径遗漏、引用计数不配对(无历史数据) | Bit Flip 伪装的软件故障(源码逻辑完全正确) |
何时两条轨道都必须做:有源码时,两条轨道必须同时进行,最终通过交叉验证收敛到高置信度结论。
何时只能走 vmcore 轨道:无源码时,仅走 vmcore 分析,但应明确标注分析局限性,并在结论中说明哪些环节依赖推断。
第三节:统一分析流程(分支决策 → 双轨并行 → 交叉验证 → 输出)
本节将“执行总览 + vmcore 轨道 + 源码轨道 + 交叉验证”融合为一条可直接照做的统一流程。
执行约束:所有分析脚本的默认超时时间为 3 分钟(180s)。
Step 1:启动(基线信息收集 + 分支推荐)
运行:
bash scripts/01_baseline_info.sh <vmcore> <vmlinux> [src_dir]
记录输出中的四类关键信息(后续所有步骤都围绕它们推进):
- 内核版本字符串(用于 S0 版本验证)
- 崩溃位置(函数名、
RIP=<addr>、<func+offset>)
- 调用栈(
bt -f/bt -l 的链路)
- 异常值线索(NULL/poison/越界偏移/锁告警/MCE/UE 等关键词)
Step 2:故障类型定界(选择分支脚本一键跑)
按 Step 1 输出推荐,执行对应分支脚本:
bash scripts/branch_X_xxx.sh <vmcore> <vmlinux> [src_dir]
说明:每个 branch_*.sh 已内置 vmcore 侧的 crash 命令序列(覆盖 V1–V4),并在检测到 src_dir 存在时给出源码侧追踪指引(覆盖 S0–S5)。
若 Step 1 输出推荐多个分支脚本,必须按输出顺序全部执行,不可只选其一。
脚本对应执行参考如下:
崩溃信息
├─ log 含 "NULL pointer dereference" → 分支A: 空指针解引用
├─ log 含 "KASAN: slab-out-of-bounds" → 分支B: 内存越界OOB
├─ log 含 "KASAN: use-after-free" → 分支C: Use-After-Free
├─ log 含 "stack-protector"/"stack overflow" → 分支D: 内核栈溢出
├─ log 含 "Machine check:" / MCE bank 转储 → 分支E: 硬件MCE
├─ log 含 "EDAC" + "UE" 记录 → 分支F: 内存UE
├─ log 含 "possible circular locking" → 分支G: 死锁
├─ log 含 "soft lockup" → 分支H: Soft Lockup
├─ log 含 "hard LOCKUP" → 分支I: Hard Lockup
├─ log 含 "kernel BUG at" → 分支J: BUG()触发
├─ log 含 "Out of memory" / "oom_kill" → 分支K: OOM Killer
├─ log 含 "sleeping function called from" → 分支L: 原子上下文睡眠
├─ log 含 "rcu_sched detected stalls" → 分支M: RCU Stall
├─ log 含 "EXT4-fs error"/"XFS.*corruption" → 分支N: 文件系统崩溃
├─ log 含 "double free"/"skb" → 分支O: 网络子系统崩溃
├─ log 含 "DMA mapping error"/"I/O timeout" → 分支P: 存储IO崩溃
├─ log 含 "vmx_"/"kvm_" + VMX exit reason → 分支Q: KVM/vCPU异常
├─ log 含 "acpi_"/"AE_BAD_ADDRESS" → 分支R: ACPI固件异常
├─ log 含 "migrate_pages"/"offline_pages" → 分支S: 热插拔/页迁移
├─ bt 含 第三方 .ko 符号 + RIP落在模块地址段 → 分支T: 驱动/模块异常
├─ CR4 SMEP/SMAP置位 + fault地址在用户态 → 分支U: SMEP/SMAP触发
└─ 随机崩溃 + 软件证据链不完整 + 无法复现 → 分支V: 疑似Bit Flip
Step 3:vmcore 逆向(回答“在哪里崩 + 崩溃时数据状态如何”)
在分支脚本输出基础上,完成并固化四步证据链:
- V1 崩溃现场还原:确认 panic/oops 类型、精确 RIP、崩溃寄存器值
- V2 调用栈重建:用
bt -f + bt -l 确认 #0 崩溃帧与完整调用链
- V3 数据状态验证:用
struct/kmem/rd 读取关键结构体/指针/长度/引用计数等
- V4 独立归因:仅基于 vmcore 客观数据,给出“异常值是什么、首次出现在哪一帧、如何触发 #0”
输出(供后续交叉验证使用):
崩溃帧 #0 函数:<func_name>
RIP 精确地址:<0xffffffff...>
调用链:#0 → #1 → ... → #N
异常值:<NULL指针/poison值/越界偏移/...>
首次异常帧:#<k>
vmcore 归因假设:<一句话>
Step 4:源码正向(有源码时必做;回答“为什么崩 + 代码逻辑哪里有缺陷”)
S0:版本验证(防止版本不匹配导致误判)
bash scripts/01_baseline_info.sh <vmcore> <vmlinux>
crash> dis -l <崩溃函数名>
版本不匹配的典型信号:
dis -l <func> 的源码行与 src/ 实际内容语义不对应
- 源码里有逻辑分支但汇编里找不到(可能 backport patch 改动)
- 结构体字段偏移与
struct <type> 输出不一致
版本不匹配时不停止:标注“版本存在差异”,后续推断以 vmcore 事实校正。
S1–S2:以崩溃帧为入口,完成“源码-汇编对齐”(关键桥梁)
- S1 锚定入口:取 Step 3 的崩溃帧函数名与 RIP,定位到
dis -l 对应的精确源码行
- S2 对齐确认:以汇编执行顺序为准理解源码语义,避免把编译器优化误当成逻辑缺陷
常见优化陷阱与处理:
| 优化行为 | 现象 | 应对方式 |
|---|
| 函数内联 | 调用栈帧缺失,源码有调用但汇编已展开 | 按汇编逻辑追,不只看源码调用 |
| 变量寄存器化 | 源码局部变量在 vmcore 找不到内存地址 | 从寄存器/栈槽推断 |
| 指令重排 | 源码行号与执行顺序不一致 | 以汇编为准,源码理解语义 |
| 尾调用优化 | 栈帧比预期少 | 结合 bt -f 栈内容辅助判断 |
S3:调用栈逐帧追踪(不允许省略链路)
原则:从最底层 #0 向上追溯至最顶层 #N,逐帧完成三件事:
① 找到对应源码函数
② 用 vmcore 的寄存器/栈内容验证该帧的输入参数是否异常
③ 判断:该帧是“崩溃帧(#0)”还是“根因帧(首次引入异常)”?
崩溃帧 vs 根因帧必须严格区分:崩溃帧是异常传播的终点,根因帧是异常最初引入点。
S4:数据流溯源(异常值生命周期追踪)
围绕 Step 3 的“异常值”,在源码中追踪:
分配/初始化 → 正常路径 → 异常引入点(根因)→ 传播路径 → 崩溃点
重点审查的根因高发区:错误处理路径的清理遗漏、引用计数不对称、并发路径缺锁、RCU 保护边界错误、条件编译分支差异。
S5:反事实验证(强制;不能止步于“找到可疑代码”)
用源码根因假设正向推演,并与 vmcore 现象逐条对齐:
✓ 推演的崩溃位置 == vmcore 的 RIP?
✓ 推演的异常值 == vmcore 读到的寄存器/内存值?
✓ 推演的调用路径 == vmcore 的 bt 调用栈?
三条全 ✓ 才能判定“根因确认”。
源码轨道输出格式:
文件:<path/to/file.c>
行号:<line>
函数:<function_name>()
缺陷类型:[缺失检查|引用计数错误|锁保护缺失|竞态|逻辑错误]
代码缺陷:<精确描述>
触发条件:<前置状态>
因果链:[缺陷触发] → [异常状态] → [传播] → [崩溃点/bt #0]
Step 5:交叉验证(双轨汇合,冲突仲裁,置信度收敛)
对每条证据做对齐检查:
| 验证维度 | vmcore 结论 | 源码结论 | 是否吻合? |
|---|
| 崩溃位置 | RIP 在 <func>+<offset> | <file>:<line> 对应此偏移 | □ 吻合 □ 不符 |
| 异常值 | 寄存器/内存读到 <value> | 源码在 <条件> 下产生此值 | □ 吻合 □ 不符 |
| 调用路径 | bt 路径 A→B→C→崩溃 | 源码中 A→B→C 的调用存在 | □ 吻合 □ 不符 |
| 根因帧 | #N 帧首次异常 | 对应函数存在缺陷 | □ 吻合 □ 不符 |
| 触发条件 | 并发/输入特征 | 缺陷在此条件下触发 | □ 吻合 □ 不符 |
不一致时的仲裁原则:
崩溃位置/异常值:优先信任 vmcore(客观事实)
调用路径不符:优先检查内联/尾调用优化,用 bt -f 还原
根因帧不符:保留多假设并补证据(必要时 KASAN/LOCKDEP 复现)
置信度收敛:
- 高:两轨完全吻合 + 反事实验证通过
- 中:两轨基本吻合,但有 1 个维度依赖推断;或仅完成 vmcore 轨道
- 低:两轨存在矛盾且无法解释;或证据链缺失超过两环节
- 疑似硬件:两轨软件证据链均不完整 + 随机崩溃 + 无法复现(参考分支 V/E)
常见误判陷阱(用于复核结论质量):
- 崩溃点不等于根因:RIP 只是非法状态传播的终点,根因可能在更高层帧、更早时刻
- Bit Flip 伪装软件 Bug:vmcore 侧能看到“非法指针/异常值”,但源码侧找不到可闭环的缺陷
- 第三方驱动栈帧污染:bt 含
.ko 不代表模块是根因,需确认 RIP 是否落在模块代码段
- 版本不匹配:debuginfo 与 vmcore 偏差会导致符号/行号映射错误,结论必须回到 vmcore 事实校正
软件 vs 硬件的判断原则:
软件 Bug 优先,但以下情况提升硬件怀疑优先级:
① 多次随机崩溃,位置不同,无法复现
② 两条轨道都找不到完整的软件解释
③ BMC SEL / EDAC 有历史告警记录
→ 参考分支V(Bit Flip)或分支E(MCE)分析流程
Step 6:最终输出(按第九节模板落盘)
将 Step 3/4/5 的输出填入第九节报告结构,并显式写清:结论、证据链、排除项、修复建议、验证建议。
第四节:轨道一 —— vmcore 分析(逆向推理)
本节已合并进第三节的统一流程(Step 3)。
第五节:轨道二 —— 源码分析(正向追踪)
本节已合并进第三节的统一流程(Step 4)。
第六节:交叉验证与结论收敛(双轨汇合)
本节已合并进第三节的统一流程(Step 5)。
第七节:故障类型决策树(两条轨道共用)
本节内容已合并进第三节的统一流程(Step 2)。
第八节:注意事项与置信度评级
本节内容已合并进第三节的统一流程(Step 5)。
第九节:最终报告结构
## 崩溃概要
故障模式:<类型>
置信度:<高/中/低/疑似硬件>
分析轨道:[双轨(vmcore + 源码)| 单轨(仅vmcore,无源码)]
内核版本:<版本号>
## vmcore 轨道结论
崩溃位置:RIP=<addr> 函数:<func+offset>
调用链:#0 → #1 → ... → #N
崩溃时数据状态:<寄存器值/内存内容/异常值>
vmcore 侧根因假设:<基于崩溃快照的推断>
## 源码轨道结论(有源码时填写)
根因代码位置:<file>:<line> 函数:<func>()
缺陷类型:[缺失检查 | 引用计数错误 | 锁保护缺失 | 竞态 | 逻辑错误]
代码缺陷描述:<精确描述,含问题代码片段>
触发条件:<需要什么前置状态>
因果链:[缺陷触发] → [异常状态] → [传播] → [bt #0 崩溃点]
## 交叉验证结果(有源码时填写)
崩溃位置吻合:□ 是 □ 否(差异说明:<...>)
异常值吻合: □ 是 □ 否(差异说明:<...>)
调用路径吻合:□ 是 □ 否(差异说明:<...>)
根因帧吻合: □ 是 □ 否(差异说明:<...>)
触发条件吻合:□ 是 □ 否(差异说明:<...>)
综合判断:<两轨结论是否一致,若有矛盾如何解释>
## 完整因果链(双轨收敛后)
[触发条件] → [<file>:<line> 代码缺陷] → [异常状态产生]
→ [传播路径:#N→...→#1] → [崩溃点:#0 RIP=<addr>]
## 排除的替代假设
- <假设X>:排除原因 <...>
## 修复建议
最小修复(立即可做):
<具体代码修改,diff 形式>
根本修复(设计层面):
<更深层的设计改进方向>
## 验证建议
<如何确认根因 + 如何验证修复有效(KASAN/LOCKDEP/压测)>
第十节:参考文件
references/crash_commands.md:crash 工具命令速查手册
references/hardware_analysis.md:硬件故障分析(MCE/Bit Flip/EDAC)
references/src_analysis_patterns.md:源码分析常见缺陷模式速查