OpenAI 工程师曾耗时数周排查 Rockset(为 ChatGPT 搜索及数据插件提供支持的 C++ 基础设施)中的神秘崩溃问题。现象表现为函数返回错误内存地址、栈指针偏移 8 字节,且所有假设均被反证推翻,该 Bug 看似不可能存在。
最终团队发现,这并非单一 Bug,而是两个互不相关的缺陷巧合地同时爆发。突破点在于采用了“流行病学调试”法:构建自动化管道分析过去一年生产环境的所有核心转储文件,从宏观规律而非个案推断中寻找线索。
数据驱动定位:从混沌到清晰
团队利用 ChatGPT 编写脚本,批量下载并解析核心文件头部的寄存器数据,过滤误报后将崩溃分类标记(如“返回空指针”、“栈对齐错误”等)。并行处理全量数据后,相关性迅速显现:症状相似的崩溃实则对应两组特征截然不同的事件。
硬件故障引发的栈对齐错误
第一类由栈对齐错误导致的崩溃集中发生于特定 Azure 区域,有明确起始时间且未出现在长期运行节点上。追踪发现,根源是一台物理主机的 CPU 在未过热或未抛出机器检查异常的情况下,默默产生错误的数学运算结果。将该主机移除后,此类崩溃彻底消失。
libunwind 竞争条件导致空指针返回
排除硬件干扰后,剩余的“返回空指针”问题均发生在 C++ 异常展开过程中。根本原因指向 GNU libunwind 中 _Ux86_64_setcontext 函数存在长达 18 年的竞争条件:
- 机制缺陷:在异常展开时,libunwind 合成 ucontext_t 结构体并调用 _Ux86_64_setcontext 转移控制权。该函数在未完成旧结构体指令指针(%rip)读取前,便更新了栈指针(%rsp)。
- 时间窗口:一旦 %rsp 更新,原结构体脱离内核红区保护。若信号恰在此时(约 100 皮秒的窗口期)到达,内核将在该结构体上构建信号帧,破坏指令指针,导致跳转至 NULL 或垃圾地址。
- 触发条件:Rockset 使用 timer_create 高频发送 SIGUSR2 信号以实现按查询记账,远超传统应用频率,从而将理论上的竞争条件转化为实际生产崩溃。
团队已向 GNU libunwind 提交修复方案及重现示例。修复通过重排指令,确保先读取 %rip 再更新 %rsp,彻底消除了竞争窗口。验证显示,libgcc 等其他展开器不存在此问题。
核心启示:高质量数据集的价值
最重要的步骤并非巧妙解读汇编代码或深究细节,而是构建高质量数据集。缺乏全量数据会导致将不同现象混为一谈;而一旦获得准确完整的数据,问题结构便显而易见。
对于排查难以解释的生产环境崩溃,建议首先检查是否将多个 Bug 混淆。看似矛盾的症状可能分别符合不同的假设。洞察问题结构的最快途径,是对所有故障案例获取完整、带标签的数据,而非仅对单个案例进行深入分析。
注:完整技术博文包含详细的栈内存示意图、漏洞汇编指令及故障可视化图表。
原文链接:https://www.infoq.com/news/2026/07/openai-libunwind-core-dumps/
声明:本文由 InfoQ 翻译,未经许可禁止转载。
今日荐文
Hugging Face 遭攻击取证受阻,只能靠国产 GLM 5.2 救场?白宫 AI 顾问急眼喊话:我们在失去竞争力
“要么 Fork,要么走人”!Linus 怒怼 AI 反对派:Linux 不搞“反 AI"
暂不跟进视频生成!Kimi K3 力压 Fable 5、登顶 Arena 榜单,提价近 4 倍对标 Sonnet 5

