macOS 里藏着一颗 49.7 天的定时炸弹:TCP 网络会静默失效

安全研究团队 Photon 近日披露了一个影响所有 macOS 设备的 TCP 网络栈漏洞:如果一台 Mac 连续开机超过 49 天 17 小时,新的网络连接将逐步失效,最终完全无法建立任何 TCP 连接。

问题根源在 Apple 的 XNU 内核。内核内部用一个 32 位无符号整数 tcp_now 记录自开机以来的毫秒数,充当整个 TCP 协议栈的内部时钟。这个计数器最大能存到 2³² - 1,换算下来就是 49 天 17 小时 2 分 47 秒。跑满之后,它回绕到零——这本该是正常行为,但内核里有一行代码出了问题。

配图

时钟冻结

XNU 内核在更新 tcp_now 时有一个单调性守卫:只有新值大于旧值时才允许更新。溢出发生后,旧值(接近最大值)大于新值(回绕到接近零),守卫条件不满足,tcp_now 就被锁在溢出前的值,再也不会更新。

TCP 时钟冻结的后果是连锁的。一个 TCP 连接关闭后,系统会在 TIME_WAIT 状态下保留它一段时间(macOS 默认 30 秒),防止旧数据包干扰新连接。到期后由 tcp_now 驱动的垃圾回收机制清理这些条目并释放端口。时钟一停,TIME_WAIT 永远不会被回收。

从静默到崩溃

这个漏洞的危险之处在于它不会立刻出错。溢出后的几分钟内,如果系统本身没在大量创建短连接,你可能完全察觉不到异常。随着 TIME_WAIT 持续堆积,系统可用的临时端口(macOS 约 16384 个)逐步耗尽,新的外发连接开始卡在 SYN_SENT 状态,无法完成三次握手。

Photon 在实验中观察到:溢出 9.5 小时后,两台测试机的 TIME_WAIT 分别累积到 4888 和 8217 条,SYN_SENT 连接超过 3000 条,其中一台机器的负载飙升至 49.74——内核在疯狂扫描永远不会缩小的 TIME_WAIT 队列。已经建立的长连接还活着,但新建连接基本不可能了。ICMP ping 不受影响,因为它不依赖 TCP 计时器。

谁会中招

大多数消费级 Mac 不会触发这个问题——系统更新、睡眠唤醒等日常操作通常在 49 天内会触发至少一次重启。高风险场景是长期运行的 macOS 服务器集群、CI/CD 构建节点(Jenkins、自托管 GitHub Actions Runner)、Mac Pro 工作站(长时间渲染或编译)以及托管在机房的 Mac mini 集群。

Apple 社区论坛和开源项目中已经有多例症状完全吻合的故障报告:TCP 完全失效但 ping 正常,只有重启能恢复,发生前机器已运行数周。

修复进展

目前最直接的缓解方式就是重启设备。Photon 表示正在研究不重启的修复方案。值得注意的是,TCP 时间戳回绕问题在 RFC 7323 中已有相关规范,但那份 RFC 讨论的是传输过程中的远端时间戳处理,XNU 内核自己的本地计时器变量溢出属于实现层面的缺陷。

这类 32 位整数溢出问题在计算历史上出现过多次——Windows 95/98 的 49.7 天崩溃、2038 年问题(Y2K38)、GPS 周数翻转。共同特征是:代码逻辑看起来完全合理,只有在极端条件下才会暴露。区别在于,Mac 用户可能几十年前就习惯了"不用关机",而这个习惯恰好踩中了这颗雷。


来源:Photon Blog · Tom's Hardware

相关推荐