3月初,安全研究员Felix Geisendörfer向Go团队提交了两个编译器漏洞,影响版本直至1.26.1。这两个bug的特别之处在于:攻击者无需import unsafe、无需CGO、无需汇编,只用"安全"的Go代码就能打破内存安全保证。

问题藏在prove优化通道里——这是Go后端第二大SSA优化模块,专门负责推断变量边界、"证明"各种事实,然后干掉多余的边界检查。Geisendörfer之前给标准库报过几个拒绝服务漏洞,也断断续续给编译器提过补丁,对这个代码库足够熟悉。去年11月他盯上了这块,结果在正式发布版本里挖到了真东西。

第一个漏洞的触发代码短得离谱:

一个int8从0开始,每次加10,上限120。小学算术:120加10等于130?不对,8位有符号整数会回卷成-126。编译器偏偏没算准这一步。它"证明"了索引安全,顺手删掉边界检查,实际上数组访问已经越界到负数 territory。配合周围代码的精心布置,能劫持控制流、甚至注入指令执行。

Geisendörfer在报告里写:「我必须承认一开始不敢相信,重跑了好几遍程序」。第二个漏洞同样利用prove对算术和符号的处理失误,最终都能实现控制流劫持。完整的端到端利用代码他没有公开,等修复版本铺开得差不多了再说。

这件事的讽刺之处在于:Go语言设计把unsafe包关进笼子,编译器却自己开了后门。Geisendörfer的总结很直白——内存安全是整个工具链的属性,不只是语言本身的事。目前Go 1.26.2已修复,但那些坚信"纯Go就安全"的开发者,或许该重新掂量一下信任链的长度。