谁杀死了网络交换机?

本周在Hubris中发现一个巧妙的 bug,最初代码正确但因其他变化而变成 bug。

什么是 Hubris?
Hubris 是为深度嵌入式系统设计的操作系统,用于处理 Oxide Rack 中“大”处理器的启动任务,相关背景可阅读参考手册介绍它的演示文稿

犯罪现场
同事 Arjen Roodselaar 测试网络交换机固件的电源序列和时钟配置更改时,交换机无法开机,部分固件能响应但电源序列器似乎死机,可能是电源序列混乱导致硬件损坏,这是个谜。

从有限内存中挤出更多
在使用 Hubris 的相对便宜的微控制器中,RAM 和闪存有限,Hubris 因由多个单独编译的任务组成,资源需求比其他系统高,同事 Matt Keeter 让系统更智能地利用内存,回收了 30%的 RAM。

确凿证据
Arjen 用 Hubris 的调试器 Humility 检查,发现负责电源序列的sequencer任务状态异常,多次重启,每次启动时都会出错,涉及 Hubris IPC 中重要方面。

在 Hubris IPC 中跨任务扩展 Rust 借用
Hubris 任务通过 IPC 消息通信,任务可借内存给其他任务,这利用了 Rust 的资源所有权模型,但如果实现错误会是安全漏洞,尝试借未拥有的内存是被禁止的,内核会记录触发事件信息。

特性攻击时
内存违规的地址0x801bffd在处理器闪存的一个特殊边界下,且属于同一任务,这是因为内存访问权限检查代码假设借的内存能 fit 进一个区域,而 Matt 的任务打包更改使这假设过时。

两个无辜特性如何合谋杀死网络交换机
构建系统中的任务打包是机会性的,会在任务的闪存和 RAM 区域中间引入区域边界,一个任务的大小变化可能会移动另一个无关任务的 MPU 区域边界,导致随机崩溃,Matt 关闭了构建系统中的任务打包功能。

电话来自屋内!
内核因错误的内存保护算法而杀死任务,需要改变算法,新算法更复杂,已被提取到更可单元测试的 crate 中,开启任务打包功能也不会有问题,从发现网络交换机故障到修复内核 bug 大约三小时。

Hubris 失败的情况
从网络交换机无法用新固件开机,到两位相距 3000 英里的工程师分别分析故障快照并修复内核 bug,过程中体现了故障隔离(部分系统崩溃不影响其他部分)、向安全方向失败(内核内存访问检查 bug 无安全影响)、更安全的共享内存(IPC 机制使共享内存更安全)、内核调试器代码签名(方便定位崩溃代码)、固件崩溃转储很棒(能获取崩溃转储)、设计和实现的简单性(代码量少便于查找问题)以及团队紧密的非层次化集成(团队成员能快速解决问题)等。

总之,这个故事展示了 Hubris 系统在面对问题时的一些特点和优势,也强调了团队合作和简单设计的重要性。

阅读 11
0 条评论