本文来自微信公众号:InfoQ(ID:infoqchina),作者:褚杏娟、核子可乐,题图来自:视觉中国


新年伊始,微软 Exchange Server 2016 与 2019 中出现日期检查错误,导致服务器无法正确识别 2022 年这一时间标记。因此也有人称其为 Y2K22 bug,即千年虫 2022 版。


据悉,微软的邮件程序会将日期与时间存储为 signed 整数(带符号的整数),最大值为 2147483647,即 2^31 - 1。而微软使用更新版本的前两位数字表示其发布年份,所以只要时间在 2021 年或更早,那就一切 OK。然而,就在微软于新年前夜发布 2201010001 版本时,本地服务器却由于无法正确解析日期而发生崩溃,导致递送消息卡在传输队列中动弹不得。


Log Name: ApplicationSource: FIPFSLogged: 1/1/2022 1:03:42 AMEvent ID: 5300Level: ErrorComputer: server1.contoso.comDescription: The FIP-FS "Microsoft" Scan Engine failed to load. PID: 23092, Error Code: 0x80004005. Error Description: Can't convert "2201010001" to long.


Log Name: ApplicationSource: FIPFSLogged: 1/1/2022 11:47:16 AMEvent ID: 1106Level: ErrorComputer: server1.contoso.comDescription: The FIP-FS Scan Process failed initialization. Error: 0x80004005. Error


世界各地的管理员疯狂排查故障,错过了与亲朋好友一同迎接新年的宝贵时光。“微软到底在搞什么鬼?马上要过年了,要不是论坛上说大家普遍遇到了问题,我们就要重新跑回去上班了。”一位管理员在 Reddit 线程中写道。


微软在次日发布了修复方案:自动 PowerShell 脚本和脚本也无法运行时适用的手动解决方案。无论如何,管理员都需要在受到影响的每台本地 Exchange 2016 与 2019 服务器上分别执行修复操作。好在自动化脚本可以在多台服务器上并行运行。微软公司强调,自动化脚本“可能需要一段时间才能运行完成”,并呼吁管理员们耐心等待。


日期与时间检查是在 Exchange 检查 FIP-FS 版本的过程中执行的,FIP-FS 是一种扫描引擎、属于 Exchange 反恶意软件保护机制中的组成部分。一旦 FIP-FS 的版本是以数字 22 开头,则检查将无法完成、投递中的邮件也会被突然叫停。微软发布的修复程序会停止 Microsoft 筛选管理与 Microsoft Exchange 传输服务、删除现有反病毒引擎文件,并安装和启动经过修复的新反病毒引擎。


目前,大部分受到波及的组织已经恢复正常,但还不清楚这项 bug 已经存在了多久,不过从受影响的版本判断,很可能源自 Exchange Server 2016 的开发阶段。


一直在重蹈覆辙


从根本上说,千年虫是一种程序处理日期上的 bug,这并不是严重的技术问题,但却是企业们一直在犯的错误。


2019 年 11 月,部分惠普 SSD 固态硬盘在运行 32768 小时后自动停止工作,盘内存储内容全部消失且无法恢复。特定系统中的所有驱动器可能都预装有相同批次的固件、有着同样的 bug 隐患,一旦同时发生故障,即使是 RAID 系统也承受不了这种“集体罢工”式的极端状况。


惠普并没有做出具体解释,而是直接发布了固件修复升级。但从现象来看,问题应该是与代码中的 16 位值有关。这意味着此系统可负载的最大负整数是 32768,最大正整数则是 32767。


数字溢出问题是最为常见的编程错误之一,一旦值达到极限条件而且未经溢出或下溢检查的校正,那任何代码都有可能出现问题。因此,很多开发者喜欢用超级大的整数进行标定;只要数字够大就不怕意外溢出。


不过,这招并非百试百灵。微控制器中只能使用 8 位或者 16 位整数。考虑到这些值往往与外围控制器相关联,所以必须要为其设置适当的范围限制,确保开发者和代码审查者能够准确掌握这些重要数值。


另一方面,这类超限状况常常引发难以发现的 bug。惠普 SSD 事件中,驱动器要运行几年才能达到极限时长,所以这种在罕见条件下才会触发的错误确实不易被察觉。如果这块 SSD 恰好服务于某台自动驾驶汽车,那么在它停止工作的瞬间,车辆很有可能引发严重的交通事故。


除了惠普 SSD 事件,阿丽亚娜 5 号运载火箭首次测试发射失败的原因也是这样的一个“小”失误。1996 年 6 月 4 日,阿丽亚娜 5 号运载火箭首次测试发射,火箭在发射后 37 秒被迫自行引爆,40 秒后解体。这个价值 5 亿美元的运载系统瞬间灰飞烟灭。


阿丽亚娜 5 号某段控制程序直接复用了阿丽亚娜 4 号火箭的代码,其中一个需要接收 64 位数据的变量为了节省存储空间而使用了 16 位字节,这使更快的阿丽亚娜 5 号在控制过程中产生了整数溢出,导致导航系统对火箭控制失效,程序进入异常处理模块,引爆自毁。该失败成为历史上最臭名昭著和损失最严重的软件 bug 之一。


曾让全世界感到恐惧


“千年虫”问题的根源始于 60 年代。当时计算机存储器的成本很高,如果用四位数字表示年份就要多占用存储器空间,使成本增加。因此为了节省存储空间,计算机系统的编程人员采用两位数字表示年份。


虽然提高了计算机的运行效率,但也带来新的隐患。比如当日期从 1999 年滚动至 2000 年时,99 到 00 的变化会引发哪些后果?有些人担心计算机会不知道如何理解这样一个时间空值,导致日期无效、进而引发全球性计算设施故障。


为了使“1999 年 12 月 31 日”安全过度到“2000 年 1 月 1 日”,数据显示,当时全球大概投入了 3000 亿~6000 亿美元来解决千年虫问题。虽然效果不错,但还是出现了一些问题,甚至笑话。


对于千年虫问题,美国的态度和行动特别积极。当时,美国整个国家至少投入的 1000 亿美元中,约 90 亿美元花在联邦政府身上。五角大楼的情报和国防系统成为资金的主要目的地(总额约 35 亿美元)。但是,虽然开展了为期数月的昂贵计算机修复与硬件更新努力,政府在 2000 年的头三天内仍然遭遇到严重的间谍卫星运行故障。直到经历了重启和再次运行,卫星才终于能够正常发回可以识别的信息内容。


三天时间听起来不长,但五角大楼的一位官员仍将此次事件划入“重大”类别。不过略显讽刺的是,引发故障的并不是千年虫,而是用来解决该 bug 的软件补丁。


此外,美国海军天文台也因千年虫影响暂时失控。美国海军天文台只有一项工作:校准时间。该机构成立于 1830 年,主要负责美国各类航海仪器,并在后续的发展中逐渐成为美国的官方计时机构。正是由于如此重要的地位,才让海军天文台在千禧年第一天宣布日期为“19100 年 1 月 1 日”显得格外尴尬,虽然问题在上报不到一小时后就被解决。


1999 年 12 月 29 日,位于华盛顿特区的美国海军天文台内部


除了美国外,日本的核电站也受到了千年虫的影响。在新年钟声敲响的两分钟之后,日本女川核电站突然响起警报,当时计算机发现某负责测量周围海水温度的设备出现了问题。好在故障只持续了 10 分钟左右,之后一切再次回归平静、并未发现任何严重状况。


日本志贺核电站也发生了类似的事件,千年虫故障导致该站部分警报系统下线。更糟糕的是,政府办公室的一台电站监控电脑与配套警报系统也一同宕机。总之,当天日本各地都出现了类似的小问题,不过很快得到了控制与纠正。日本官员们并没有透露这些事件是否与千年虫 bug 相关。


由于 Y2K 错误,香港期货交易所的计算机系统出现故障,控制恒生指数期权合约定价的计算机系统计算错了许多期权交易的交易日和到期日之间的天数;芝加哥联邦储备银行无法完成 700,000 美元的税款转移;芝加哥的一家银行中断了对部分医院的电子医疗保险支付功能,处理和支付医疗保险索赔的保险公司必须通过联邦快递,将包含已处理索赔信息的软盘寄给银行来保证按时付款。


此外,还有一些让人哭笑不得的事情:


  • 千年虫导致新生儿被登记为百岁老人。丹麦的第一名“千禧婴儿”刚刚降生就被医院计算机登记为百岁老人。德国德意志歌剧院的计算机系统在 2000 年 1 月 1 日将日期跳转回 1900 年,导致所有员工及其子女的年龄都发生了巨大变化。1990 年出生的小朋友瞬间迎来 90 岁高龄,并导致不少员工无法正常收取由政府直接在工资中发放的儿童抚育补贴。


  • 完全没用、又不能退货的“千年虫生存包”。借着全球各地对千年虫大灾难的恐惧心理,不少公司提前几个月推出了一系列“千年虫生存包”。这个业务很快催生出价值数百万美元的市场,其中一家名为 Preparedness Resources 的公司甚至通过推销包含脱水食品、净水器、无电池手电筒、毯子和防水火柴的生存工具套装赚到 1600 万美元。头脑清醒的总裁 Scott Sperry 还一早就定下了“售出不退”的强硬政策。


  • “一夜暴富”的惊喜体验。千年虫让德国的某个男子在新世纪的第一天突然体验了把当富豪的感觉。当天,他的银行账户中随机存入约 600 万美元,交易日期为 1899 年 12 月 30 日。当时的官员并不确定这笔异常转账跟千年虫有没有关系,唯一可以肯定的是这个男子不会真的一夜暴富。


虽然从现在的角度来看,当初全世界对于千年虫问题的恐慌似乎没啥必要,但这主要归功于各国提前几年投入数千亿美元进行 bug 修复。


比尔·盖茨在采访中就曾强调,千年虫“之所以最终没有掀起什么波澜,是因为各方真的不遗余力全力修复。如果没有这样的付出,全世界一定会受到巨大影响。”


“千年虫”能躲开吗?


早在 1999 年之前,世界各地的政府和企业就一直在努力寻找 Y2K 的修复方案。但千年虫问题至今还不能有效避免,千年虫还可能再次现身。


和千年虫问题类似,32 位的 Unix 操作系统和 Linux 操作系统时间溢出问题又称为“2038 年问题”,所有使用 POSIX 时间表示时间的程序都将受到影响。这个问题是由用来写 Unix/Linux 的 C 语言引起的。


C 语言中用 time_t 来代表时间和日期,用来记载从 1970 年 1 月 1 日到 2000 年所经历的秒数,并以 32 位存储。第一位是符号位,其余 31 位用来存数字,这 31 位数字可存储的最大数字为 2147483647,最多可以用到 2038 年 01 月 19 日 03 时 14 分 07 秒。


到这个时间后,数字不会自动增加,而会变为 -2147483648,即 1901 年 12 月 13 日 20 时 45 分 52 秒。这会导致很多的程序出现问题,甚至崩溃。


2038 年问题不仅比千年虫更隐蔽,而且比之前千年虫问题更具有破坏力。千年虫问题只会导致应用层的程序出现问题,比如信用卡支付系统或管理系统。而“2038 年问题”的 bug,将会影响系统最底层的时间控制的功能。


2020 年 2 月发布的 Linux kernel 5.6 声称解决了这个问题,因此 32 位系统也可以运行到 2038 年后。Linux 开发人员 Arnd Bergmann 表示,使用 GNU C Library 2.32 和 Musl libc 1.2 在 64 位 time_t 上构建用户时间。


虽然“2038 年问题”这样的系统性问题可能需要长时间探索解决,像微软这种类似千年虫的 bug,完全是可以避免的。


参考资料:

https://www.mentalfloss.com/article/654225/everything-you-need-perfect-winter-staycation?utm_content=infinitescroll1

https://arstechnica.com/information-technology/2022/01/exchange-server-bug-gets-a-fix-after-ruining-admins-new-years-plans/

https://www.howtogeek.com/671087/what-was-the-y2k-bug-and-why-did-it-terrify-the-world/


本文来自微信公众号:InfoQ(ID:infoqchina),作者:褚杏娟、核子可乐