元数据
| id | float-crash | 收录 | 2026-08-29 |
|---|---|---|---|
| 问题域 | 编码代理与终端 | 主题分组 | 编程与调试 |
| 适用工具 | gcc · 外部工具 | ||
症状
真实案例:MariaDB 5.5.64 在国产 ARM64 服务器(信创早期、CPU 无 FPU)上偶发崩溃,SIGSEGV s=11;gdb 现场 `best_key = 0x0`(空指针)+ `best = DBL_MAX`(1.797e+308 哨兵初值,从未被任何候选覆盖)。
泛化特征:任何「用 `DBL_MAX` 做最优初值 + 指针记录最优项」的搜索/选择代码,一旦所有比较都不成立(NaN 参与、FPU 指令异常、软硬浮点 ABI 错位),哨兵不被覆盖 → 指针保持 NULL → 解引用即崩溃。看起来"偶发",因为只有数据/环境触发到比较恒 false 时才发生,平时一切正常。
原因
1. **环境根因(文章结论)**:信创早期 ARM64 CPU 无 FPU/浮点指令异常 → 系统按软浮点 ABI(`-msoft-float`)编译运行;MariaDB 二进制按硬浮点 ABI 编译。软浮点程序加载硬浮点二进制/库 → 浮点寄存器约定与运算结果错位 → 浮点值被读成 NaN/错误值。
2. **机制根因(通用)**:IEEE 754 下 **NaN 参与任何比较恒为 false**(`NaN < x`、`NaN >= x` 都不成立)。`if (score < best)` 恒 false → `best` 保持 DBL_MAX、`best_key` 保持 NULL。
3. **代码缺陷**:使用前未判空/未校验——`best_key` 本身就是"有无候选命中"的有效性哨兵,但代码直接解引用,没有 `if (!best_key)` 守卫。
解法
1. **根治(环境)**:确认浮点 ABI 匹配。信创软浮点环境用 `-msoft-float` 重新编译目标程序(文章解法);排查:`readelf -A <二进制>` 看 ARM `Tag_ABI_VFP_args` 标记、`ldd` 检查依赖是否混入硬浮点库、`dmesg | grep -i fpu` / `grep -i fpu /proc/cpuinfo` 确认 CPU 是否真无 FPU。
2. **兜底(代码,任何平台都该做)**:
- 浮点比较前过滤非有限值:`if (!isnan(s) && !isinf(s) && s < best)`——NaN/Inf 不参与"最优"选择;
- 哨兵指针使用前判空:`if (best_key == NULL) { 走错误路径/返回"无结果" }`,绝不直接解引用;
- 可加断言辅助排查:日志输出 `best` 值——若出现 DBL_MAX 说明比较从未成功,直接指向浮点层问题。
3. **警惕软浮点库行为**:软浮点/FTZ 实现常把 denormal 冲成 0(见验证证据 3),极小值比较/分支结果与硬浮点不同;同一套代码在不同 ABI 下行为不一致时,优先怀疑浮点 ABI 而非业务逻辑。
验证证据
- 本机为 x86_64 WSL,无法直接复现 ARM 软浮点 ABI(如实标注);用 NaN 参与比较(IEEE 下 `NaN < x` 恒 false)模拟"比较恒不成立"的同一机制。
- 实测 1(bug 版):C 程序用 NaN 分数数组做最优选择 → `best` 保持 DBL_MAX、`best_key` 保持 NULL → 解引用 SIGSEGV;gdb 现场 `best = 1.7976931348623157e+308`(DBL_MAX)、`best_key = 0x0`、`cands[0].score = nan`——与文章 dump 逐项一致。
- 实测 2(修复版):`isnan` 过滤 + `best_key == NULL` 守卫,同输入走错误路径,不崩溃。
- 实测 3(denormal/下溢):默认 FPU `DBL_MIN/2` 保留为 denormal(1.112537e-308,≠0);开启 FTZ+DAZ(软浮点库常见)后 `DBL_MIN/2 == 0`——软浮点行为差异会真实改变比较/分支结果。
- 实测复现日志(2026-08-29)
来源与证据
素材来源:https://www.cnblogs.com/KBin/p/22614217
验证证据:Evidence details kept locally (2026-08-29)