49天后突然“断网”?macOS 被曝藏着一枚 TCP 定时炸弹

一枚不会响的炸弹,在第 49 天准时引爆
科技世界里最让人头疼的 bug,往往不是那种一眼就能看出来的。不是黑屏,不是死机,不是弹窗报错,而是“看起来一切都正常”,直到业务突然掉到地上。最近,Photon 在一篇技术博客里披露了一个相当戏剧化的发现:macOS 的 TCP 网络栈里,藏着一个会在系统启动后约 49 天 17 小时准时触发的定时炸弹。
它的症状很诡异。老连接不受影响,ping 还能通,机器也没崩,CPU 可能都还挺平静。但凡要新建一个 TCP 连接,开始越来越难,最后彻底失败。对普通用户来说,也许表现为某些 App 突然转圈、同步失灵、登录超时;对长时间在线的服务来说,这几乎等于慢性休克。更麻烦的是,唯一明确有效的恢复手段居然是:重启。是的,2026 年了,我们还在为一个“重启就好”的内核问题皱眉。
这次出问题的,不是应用,而是 TCP 的“时钟”
要理解这件事,可以先把 TCP 想成一条很讲规矩的高速路。连接关闭之后,系统不会立刻把这条路完全清空,而是会保留一个叫 TIME_WAIT 的状态,短暂地等一等,防止旧数据包“诈尸”闯回来,或者确保最后的关闭确认能够补发。这本来是 TCP 协议里非常经典、也非常必要的设计。
在 macOS 上,一个关闭后的 TCP 连接通常会在 TIME_WAIT 里停留 30 秒,然后被内核回收,端口也重新释放出来。问题就出在,这个“30 秒倒计时”依赖内核中的一个 TCP 时间戳变量 tcp_now。Photon 的分析显示,XNU 内核把这个计时器定义成了 32 位无符号整数,并且按毫秒累计系统启动后的时间。数学很无情:2 的 32 次方毫秒,刚好就是 49.7 天左右。
到达这个边界后,计数器会从最大值绕回到 0,本来这也不一定致命,很多系统都会为“回绕”做好处理。但 XNU 这里偏偏多加了一道“只能前进、不能后退”的单调性保护。结果就是,回绕发生的那一刻,新的时间值反而比旧值小,更新逻辑直接拒绝写入,tcp_now 就此冻结。表面看只是一个变量不动了,实际上等于 TCP 子系统里的钟停了。
钟一停,TIME_WAIT 就不会过期。连接关了,但占着的端口永远不走。新连接不断创建,旧连接又不释放,临时端口池很快被塞满。于是你会看到一个颇有黑色幽默意味的场景:机器活着,网络也“活着”,但 TCP 事实上已经半身不遂。
Photon 的实验,把这个 bug 拍成了“犯罪现场录像”
这次披露最有说服力的地方,不只是“我们怀疑这里有问题”,而是他们确实抓到了 bug 发作的全过程。Photon 运营着一批长期不重启的 Mac 机器,用来监控 iMessage 服务健康状况。3 月底,他们注意到多台机器在上次重启后恰好运行到 49.7 天附近时,开始无法建立新的 TCP 连接。
这家公司没满足于经验判断,而是挑了两台即将跨过临界点的机器做实测。在溢出发生前,他们每两秒向几个公开地址发起十几条短连接;按正常情况,这些连接进入 TIME_WAIT 30 秒后就会被回收,因此系统里 TIME_WAIT 数量会维持在一个动态平衡区间。实验前半段也确实如此:TIME_WAIT 维持在 200 左右,和理论值几乎一致。
真正精彩的部分发生在溢出窗口。随着系统接近 49.7 天,TIME_WAIT 数量突然不再回落,而是开始一路单调上升。脚本停止后,按理说 30 秒左右所有临时连接都应清空,可 84 秒过去了,TIME_WAIT 不但没有归零,反而还在增加。3 分钟过去,依然如此。这个现象几乎等于一记实锤:不是网络波动,不是外部服务问题,而是内核根本没在回收它们。
作为记者,我很少在技术博客里看到这么“像庭审证据”的复现过程。它不靠情绪化措辞,也不靠“疑似”“可能”,而是把 bug 从症状、时间点、实验设计到源码链路完整串了起来。这种工程化调查本身就很值得尊敬。
为什么这件事重要?因为现在的 Mac,早就不只是“个人电脑”
如果这是 15 年前的 macOS bug,很多人可能会耸耸肩:笔记本谁会 49 天不关机?但今天情况已经变了。Mac 正在进入越来越多“像服务器一样工作”的场景:CI/CD 构建机、移动应用打包节点、媒体转码机、消息服务探针、远程办公网关,甚至一些轻量生产环境。尤其在 iOS/macOS 生态里,很多任务还非 Mac 不可。
也正因为如此,这个 bug 的杀伤力被放大了。它不是那种“用户点一下就崩”的消费级问题,而是典型的基础设施级隐患。你平时感觉不到,监控指标可能也不一定第一时间报警,等到端口耗尽、连接池失灵、重试风暴起来,业务层已经开始成片出错。对 SRE 团队来说,这类故障最磨人:不致命,但持续恶化;不显眼,却很贵。
更耐人寻味的是,这还是一个相当“复古”的 bug 类型。Windows 95/98 曾经有著名的 49.7 天计时器崩溃问题,Year 2038 是另一种时间整数溢出焦虑,GPS 周数回绕也坑过不少设备。我们总以为这种故事只会出现在教材和历史梗里,没想到在 2026 年,还能在现代操作系统的核心网络栈里看到一枚原汁原味的 32 位回绕炸弹。说它荒唐吧,它又很典型;说它罕见吧,它其实正提醒我们:底层代码再老、再稳定,也可能在最不起眼的边界条件里埋雷。
苹果该修的不只是一个 bug,还有“长期运行”这件事的心态
从 Photon 给出的源码分析看,问题根源并不玄学,就是 tcp_now 的类型和更新逻辑组合出了灾难性后果。理论上,修复路径并不神秘:要么改成 64 位时间基准,要么正确处理回绕后的比较逻辑,不让计时器在溢出后冻结。难点不在“知不知道怎么修”,而在苹果会以多快的速度承认、修复并下发。
这件事也抛出了一个更大的问题:苹果究竟有没有足够认真地把 macOS 当成一套会被长期连续运行的生产系统?在消费电子叙事里,Mac 是精致、流畅、开盖即用的个人设备;可在真实世界里,它已经承担了越来越多工程角色。如果系统核心模块还默认“用户总会重启”,那和现代基础设施的使用现实显然有点脱节。
从行业比较看,Linux 和 Windows 在长期运行、服务器场景里的问题暴露周期更短,社区和企业用户也更擅长围绕 uptime、时钟源、内核回收机制做压力测试。苹果过去更强的是软硬一体和用户体验,而不是把 Darwin/XNU 打磨成“绝不掉链子的后台机器”。这次事件不一定说明 macOS 不可靠,但至少说明它在某些边角料上,还欠缺一层真正面向运维世界的审视。
眼下最现实的建议,恐怕有点朴素:如果你的 Mac 设备承担持续在线、频繁短连接的任务,先把 uptime 纳入监控,特别关注 45 天之后的异常连接失败、TIME_WAIT 异常堆积和端口耗尽迹象;如果没有补丁,定期重启也许是最土但最有效的风控手段。听起来很不优雅,可有时候工程的本质就是先别让炸弹炸在业务高峰期。
从新闻价值上看,这不是一条“谁又发了新芯片”式的热闹消息,却比很多发布会新闻更重要。因为它提醒我们,技术系统真正危险的地方,常常不在聚光灯下,而在那些默认“应该一直正常”的角落里。