2003年公开至今,阿波罗11号制导计算机的源代码被数千名开发者逐行审阅过。论文发表了,模拟器跑通了,字节级校验也通过了。结果上周,一家叫JUXT的英国公司用静态分析工具扫了一遍,在陀螺仪控制模块里揪出一个资源锁泄漏——4字节,2条缺失的指令。
被神话的代码,原来也会漏
阿波罗制导计算机(AGC)的源代码是软件工程界的圣杯。它把人类送上月球,总共只用了7万多字节的内存。NASA把它开源后,GitHub上的镜像仓库收了无数star,程序员们像朝圣一样读它,试图从中提炼出"上世纪60年代的代码智慧"。
JUXT团队原本没打算找bug。他们只是在验证一套新的静态分析工具,顺手拿AGC当测试靶子——毕竟"最可靠的代码"如果都能检出东西,说明工具够灵敏。
结果真检出了。
漏洞藏在IMU MODE SWITCHING ROUTINES里,负责管理惯性测量单元的模式切换。代码申请了一个资源锁,但在某条错误处理路径上忘了释放。如果陀螺仪在特定时刻触发异常,锁就永远挂在那里,后续所有姿态重对准操作都会卡住。
阿波罗11号没触发这个条件,阿波罗12到17号也没触发。但JUXT的模拟器显示,只要满足一组精确的时序条件——大约每发射12次可能出现1次——宇航员会在月球轨道上发现飞船"突然不知道自己在哪"。
57年为什么没人发现
这个问题值得拆解。AGC的代码确实被过度神化了,但它的验证流程在1969年堪称变态:手工走查、硬件在环测试、蒙特卡洛模拟,再加上三位宇航员在模拟器里泡了数百小时。
然而漏洞躲过了所有这些。
原因一:资源锁泄漏属于并发缺陷,而AGC是单任务系统。开发者当年没有"锁"的概念,他们叫它"控制位",思维模型里没有"泄漏"这个类别。换句话说,你没法用1969年的工具检测1970年代才定义的问题类型。
原因二:错误路径的触发条件太刁钻。需要陀螺仪在模式切换的中点恰好报告故障,而地面测试用的故障注入是离散的、手动的,很难覆盖这种连续时序边界。
原因三:代码注释写的是"TEMPORARY, TO BE FIXED"。但阿波罗计划结束后,没人再碰过这块代码。2003年的数字化项目只做了忠实转录,没做语义审查。
JUXT的工程师在报告里写了一句很损的话:"我们用了现代工具,但本质上和1960年代的NASA程序员没什么不同——都是盯着屏幕,试图理解另一群人在压力下写的代码。"
对今天写代码的人意味着什么
这个案例最扎心的不是"古人也会犯错",而是审查机制的天花板。
AGC的代码审查密度是历史级的:核心模块经过多人交叉检查,关键路径有形式化验证的雏形,测试覆盖率按今天的标准依然夸张。但四字节还是漏了,因为审查只能发现审查者能想象到的问题。
JUXT用的是一类叫"抽象解释"的静态分析技术,简单说就是让工具自动遍历所有可能的程序状态,而不是依赖人脑枚举测试用例。这种技术在2003年还不成熟,2010年后才开始商业化。
有意思的是,AGC的代码结构其实特别适合静态分析——没有动态内存分配,没有递归,控制流图是扁平的。如果当年有这套工具,四字节漏洞大概率活不过编译阶段。
但这里有个悖论:现代软件复杂度已经远超AGC,静态分析工具反而经常报出大量误报,开发者被迫关闭检查规则。JUXT团队特意提到,他们的工具在AGC上零误报,是因为"目标代码足够小、足够老、足够简单"。
翻译一下:我们没法用对付简单代码的工具,去对付复杂代码。
最后一点冷知识
这个漏洞如果被触发,宇航员其实有手动备份方案。AGC的设计哲学是"任何自动功能都必须能被人工覆盖",所以阿姆斯特朗们可以靠星图和六分仪重新对准平台——前提是他们在月球轨道上还有心情做三角函数。
JUXT已经把修复补丁提交给AGC的开源维护项目。四字节,两条指令,补上了。
57年后,这段代码终于完整了。下一个会在哪段"最可靠的代码"里?
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.