1512 字
8 分钟
给 GRUB 修了个 bug,结果寄错了地址
2026-10-25

今天干了一件尴尬到想找个地缝钻进去的事:我给 GRUB 修了一个 bug,把补丁一本正经地发给了维护者和整个邮件列表,发完才发现,这个 bug 三个月前就被人修好了,而我寄去的那个地址,早就没人住了。

起因:永远的「无休眠镜像」#

我的 GRUB 菜单是自己定制过的,每个系统后面都会标出它现在是不是在休眠:Manjaro 休眠了显示「待恢复」,Windows 休眠了显示「休眠/快速启动」。不过 Windows 那一项一直不太灵,不管怎样都显示「无休眠镜像」。今天上午正好在和 Claude 折腾 GRUB 的 4K 主题,就顺手让它在虚拟机里复现一下。

它在虚拟机里造了一块假的 NTFS 盘,放进一个文件头写着 HIBR 的 hiberfil.sys,也就是 Windows 休眠时会写下的那个标记。结果 GRUB 明明读出了这 4 个字节,判断却还是「没休眠」,还报了一句 run offset out of range。

4 个字节#

GRUB 自带一个检测 Windows 休眠的命令 file --is-hibernated-hiberfil,原理很简单:读 hiberfil.sys 开头的 4 个字节,看是不是 HIBR。问题出在 GRUB 2.14 的 NTFS 驱动上。2025 年 5 月的一次安全加固给它加了一道检查,本意是防止有人构造恶意的 NTFS 分区,让 GRUB 读到不该读的内存。可这道检查拿错了比较的对象,把「这次要读几个字节」当成了「这段数据有多长」,结果读得越少,越容易被拦下来,一次只读 4 个字节的休眠检测就这样永远判成了「没休眠」。

配置文件、字体、壁纸这些,GRUB 都是成块读的,一次至少 1 KB,正好躲过了这道检查。所以这个 bug 只会坑到这种小读取,而休眠检测这个功能【确实没什么人会用】,它也就这么待了一年多都没人修。

那一行代码

出问题的是 grub-core/fs/ntfs.c 的 read_data()。大文件的 NTFS 属性里存的是一张 run list(数据段表),run_offset 是它在属性里的偏移,通常是 0x40,也就是 64 字节。安全加固加的检查是这样的:

grub_uint8_t *end_ptr = (pa + len); // len 是这次要读的字节数,不是属性长度
...
if ((run_offset + pa) >= end_ptr || ((run_offset + pa) >= (at->end)))
return grub_error (GRUB_ERR_BAD_FS, "run offset out of range");

代进去就是 run_offset >= len:读取量少于 64 字节就一律报错。我们的改法是把 end_ptr 改成 pa + u32at (pa, 4),也就是属性头里记的属性长度;后面会看到,上游的改法更干脆,直接删掉了这个条件,只留下「不超出 MFT 缓冲区」的那半句。

修起来倒不难。Claude 先在我自己的 GRUB 模块里加了一个 hiberfil 命令,一次读 4 KB,绕开这个 bug。装到真机上实测,休眠的 Windows 终于显示对了。然后它去查了上游的代码,给出的结论是:

Claude:「确认上游至今未修复」
Claude:「确认上游至今未修复」

理由也很充分:上游截至 2026 年 3 月 13 日的最新代码里,这行还在,网上和邮件列表里也搜不到相关的报告。说实话,我还挺兴奋的,觉得自己发现了 GRUB 这种重要基础设施的问题,虽然只是一个无人在意的角落里的小问题。于是我问它:要不要给 GRUB 提个 PR?

发补丁#

GRUB 不收 GitHub 那样的 PR,补丁要以邮件的形式发到 grub-devel 邮件列表,再抄送给当初加这道检查的作者和维护者。Claude 帮我准备好了补丁、英文说明和测试结果,订阅邮件列表、配邮箱这些就得我自己来了。前前后后折腾了一个小时,不过相比最近的各种折腾,这已经算顺利的了。

下午一点四十一分,终端里打出了一行 Result: 250,邮件发出去了。

GRUB development has moved#

发完我想去邮件列表的归档里看看邮件到了没有。这个月的归档一共就 9 封邮件,我那封排在最后,而前面几封补丁下面的回复,写的全是同一句话:

GRUB development has moved to freedesktop.org. Please submit patches there.

原来 GRUB 的开发已经搬到了 freedesktop.org 的 GitLab 上。Claude 赶紧去 GitLab 拉了一份新仓库,结果更扎心:同样的 bug,一位叫 haobinnan 的开发者 7 月就写好了修复,8 月 3 日合入,审阅人里正好有当初加这道检查的那位作者。诊断和我们一模一样,修法还更干净,而且已经跟着 9 月 21 日的 GRUB 2.16 发布了,Arch 三天后就跟进,只有 Manjaro 还停在 2.14。我们查的是 GNU savannah 上的旧仓库,它从 3 月起就不再更新,比 GitLab 上的新仓库少了 411 个提交。

十来分钟后:「这件事要先更正我自己」
十来分钟后:「这件事要先更正我自己」

看到「已经修好了」的那一刻,我脑子里轰的一声。邮件不像社交媒体,发错了还能撤回、删帖,一想到一个早就修好的问题,被我一本正经地发给了维护者和所有订阅者,还永远挂在了 GNU 的存档里,真是尴尬死了。Claude 还贴心地帮我拟好了一封撤回邮件,我想了想,还是算了,希望他们没看到吧。

后记#

这件事我和 Claude 都有责任。它查上游时,给的理由里就写着「截至 2026 年 3 月 13 日的最新代码」,可这既没引起它的警觉,也没引起我的。照理说,GRUB 这么重要、这么活跃的项目,怎么可能连续 7 个月没有一个提交?这怎么都不对劲,本该再核查一遍的。

我把前后这两张截图发给了 situ。他听完整个过程,回了我一句:

面对幻觉,GNU 给出的惩罚是……
面对幻觉,GNU 给出的惩罚是……
给 GRUB 修了个 bug,结果寄错了地址
作者
Yuzi
发布于
2026-10-25
许可协议
CC BY-NC-SA 4.0