新年伊始,微軟 Exchange Server 2016 與 2019 中出現日期檢查錯誤,導致伺服器無法正確識別 2022 年這一時間標記。因此也有人稱其為 Y2K22 bug,即千年蟲 2022 版。
據悉,微軟的郵件程式會將日期與時間儲存為 signed 整數(帶符號的整數),最大值為 2147483647,即 2^31 - 1。而微軟使用更新版本的前兩位數字表示其釋出年份,所以只要時間在 2021 年或更早,那就一切 OK。然而,就在微軟於新年前夜釋出 2201010001 版本時,本地伺服器卻由於無法正確解析日期而發生崩潰,導致遞送訊息卡在傳輸佇列中動彈不得。
Log Name: Application
Source: FIPFS
Logged: 1/1/2022 1:03:42 AM
Event ID: 5300
Level: Error
Computer: server1.contoso.com
Description: The FIP-FS "Microsoft" Scan Engine failed to load. PID: 23092, Error Code: 0x80004005. Error Description: Can't convert "2201010001" to long.
Log Name: Application
Source: FIPFS
Logged: 1/1/2022 11:47:16 AM
Event ID: 1106
Level: Error
Computer: server1.contoso.com
Description: 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/