---
tags: sysprog
---

# 軟體缺失導致的危害

資料整理: [jserv](https://wiki.csie.ncku.edu.tw/User/jserv)

:::info
這世界最有可能毀滅的方式 —— 大多數專家都同意 —— 是由意外造成。這就是為什麼會有我們，我們是電腦專家，我們創造意外。
The most likely way for the world to be destroyed, most experts agree, is by accident. That’s where we come in; we’re computer professionals. We cause accidents.
-- [Nathaniel Borenstein](https://en.wikipedia.org/wiki/Nathaniel_Borenstein)
:::

1970 年代推出的首款廣體民航客機波音 747 軟體由大約 40 萬行程式碼構成，而 2011 年引進的波音 787 的軟體規模則是波音 747 的 16 倍，約 650 萬行程式碼。換言之，你我的性命緊繫於一系列極為複雜的軟體系統之中，能不花點時間了解嗎？

軟體開發的安全性設計和測試驗證應獲得更高的重視。


## 豐田汽車在美國高速公路的暴衝死亡車禍

![](https://hackmd.io/_uploads/SkBQfpuJ3.png)

豐田一款 2005 年份 Camry 車款在 2007 年，於美國奧克拉荷馬高速公路上發生的一場暴衝死亡車禍，肇因於該車款內的電子節流閥控制系統軟體發生錯誤，內部的錯誤碼就是造成車輛無預警暴衝的原因。
*   [汽車電子缺陷導致事故？豐田在美惹官司](https://archive.eettaiwan.com/www.eettaiwan.com/ART_8800691341_676964_NT_9cc6a57b.HTM)
*   [不良軟體碼可能殺人嗎？答案是肯定的，而且悲劇顯然已經發生](https://archive.eettaiwan.com/www.eettaiwan.com/ART_8800691385_676964_NT_bd47ba6a.HTM)

2011 年，包括 Barr Group 四位專家在內的一個七人小組接手先前 NASA 為期 10 個月的調查任務，深入分析了發生事故的豐田汽車，並做成一份長達 800 頁的調查報告。首先，檢視車用系統的即時作業系統，找出未受保護的 critical variables，且發現電子節流閥故障安全機制中的漏洞與缺陷。

該專家小組採用 [Green Hills](https://www.ghs.com/) 提供的模擬器進行模擬，透過車輛測試，那些我們所發現的缺陷確實與無預警暴衝有關，對照檢視了汽車黑盒子內的軟體碼，發現它會錯誤記錄車輛意外前最後幾秒的駕駛人動作資訊。針對 2005 年份的 Camry L4 車款原始碼以及車內測試，證實其中有部分關鍵變數並未受軟體保護，記憶體崩潰的原始碼也顯現，stack overflow 與軟體錯誤導致記憶體崩潰，而問題的關鍵就在於那些記憶體崩潰，從而擦槍走火。

美國 Carnegie Mellon 大學 Phil Koopman 教授在 2014 年製作一份簡報〈[A Case Study of Toyota Unintended Acceleration and Software Safety](http://users.ece.cmu.edu/~koopman/pubs/koopman14_toyota_ua_slides.pdf)〉，探討豐田汽車 (Toyota Motor) 在美國捲進一樁官司背後的工程議題，涵蓋即時系統排程和軟體可靠性。

## 克萊斯勒車用軟體因缺失導致一死兩傷，全面召回

![](https://hackmd.io/_uploads/ryRNG6Okh.png)

飛雅特克萊斯勒（Fiat Chrysler Automobiles NV) 在 2017 年 5 月宣布，車載電子的軟體錯誤會導致車輛側翻時，禁用側氣囊和安全帶的部署，並已造成一人死亡、兩人受傷。

對此，該公司將在美國召回生產於 2013 年到 2016 年期間的 102 萬輛房車，包括 Ram 1500 和 Ram 2500 以及 2014 年到 2016 年所生產的 Ram 3500 等車款。此外，預計在加拿大召回 21 萬輛，在墨西哥召回 2 萬輛，北美以外的其他地區召回 2 萬輛。

延伸閱讀: [Fiat Chrysler software error leads to a massive truck recall](https://www.engadget.com/2017/05/13/fiat-chrysler-software-error-massive-truck-recall/)

## 醫療加速器致死

![](https://hackmd.io/_uploads/S1uIMTOJ2.png)

Therac-25 是加拿大原子能有限公司 (AECL) 所生產的放射線療法機器，在 Therac-6 和 Therac-20 之後推出（後兩者是 AECL 和法國的 CGL 公司合作開發)。在 1985 年到 1987 年之間，在美國及加拿大至少有 6 起和 Therac-25 相關的醫療事故，因為軟體設計時的瑕疵，使病人受到過量的輻射。

Therac-25 設備可能會發出兩種射線：低功耗的電子束或 X 射線，其中 X 射線是透過猛烈的高能電子束撞擊到一塊位於電子槍和患者之間的金屬目標而產生。相較於過往 Therac-20，Therac-25 嘗試將電動保險連動裝置改用軟體控制的方式，後者的考量是軟體被認為更加可靠。然而工程師所不知道的是，Therac-20 和 Therac-25 兩個型號裡頭的作業系統遇到 [race condition](https://en.wikipedia.org/wiki/Race_condition)。

醫療操作人員偶然設定 Therac-25，導致電子束將會在高能模式下啟動，但強烈的 X 射線偏離目標。最後病患接受到比正常劑量高 100 倍的輻射，直接導致了 5 名患者死亡，其餘患者受到了嚴重傷害。

此一事故突顯軟體控制的潛在危險性，也是軟體工程及醫學資訊學的經典案例。另外，由於工程師對初期的工程過度自信，沒有相信終端使用者提出的問題，最後產生嚴重的結果。本事件引來軟體開發工程化管理方法論的省思。

延伸閱讀:
* [Killed by a machine: The THERAC-25](http://hackaday.com/2015/10/26/killed-by-a-machine-the-therac-25/)
* [Killer Bug. Therac-25: Quick-and-Dirty](https://hownot2code.com/2016/10/22/killer-bug-therac-25-quick-and-dirty/)

## 巴拿馬市國家腫瘤中心嚴重死傷

一家美國公司 [Multidata Systems International](https://en.wikipedia.org/wiki/Multidata_Systems_International) 開發的醫療軟體，用於巴拿馬市國家腫瘤中心，在 2000 年 11 月，該醫療軟體錯誤地計算放射治療過程中的合適劑量，從而造成多人死傷。

Multidata 公司的軟體允許放射治療師利用電腦螢幕的一個叫做 "blocks" 的金屬裝置，來保護健康組織以免受射線的傷害。但該軟體僅允許治療師使用 4 個 block，但巴拿馬的醫生希望用 5 塊來保護。

醫生發現他們可以通過將所有的 block 畫成一個在中間有孔的大塊，以跳脫軟體的限制。然而，醫生們沒有意識到 Multidata 公司的軟體在這種設定中，根據該孔畫法的不同給出不同的答案：若該孔在一個方向繪製，則給出正確的計算出的劑量，但若在另外不同的方向繪製，軟體就會推薦出較必要暴露的射線的兩倍劑量。

於是悲劇發生，至少 8 個病人在這次事故中喪生，同時接受過多劑量放射的 20 個病人產生嚴重的健康問題。被要求手動兩次檢查電腦運算的醫生被以謀殺罪起訴。

延伸閱讀: [FDA provides details on Panama incidents](http://www.auntminnie.com/index.aspx?sec=ser&sub=def&pag=dis&ItemID=51190)

## 軟體誤判影響上萬篇研究論文

近年研究人類大腦功能性反應的方法中，非侵入性的功能性磁振造影 (Functional Magnetic Resonance Imaging, fMRI) 是最廣泛使用的方法。fMRI 是 MRI 的延伸，當進行 MRI 實驗時，測試受試者之心理、功能上反應，稱為 fMRI, 將人類對大腦研究從醫學帶入認知科學的領域，廣泛的設計應用實驗在人類認知過程中，包含感覺、知覺、運動、語言、工作記憶等。

來自瑞典的神經科學家在 2016 年的論文〈[Cluster failure: Why fMRI inferences for spatial extent have inflated false-positive rates](https://liu.diva-portal.org/smash/get/diva2:944913/FULLTEXT01.pdf)〉指出，最常用於 fMRI 分析的軟體套件，像是 SPM, FSL, AFNI 等，可能會導致高達七成的誤判 (false-positive)，致使高達 4 萬份研究受到影響。

fMRI 的優勢是保證受試者可在安全的環境下，觀察大腦在各種活動中各腦區生理功能的變化，從而為人腦各個腦區定位找出新的依據。但分析軟體的缺失，使得前述判斷失去精確性。儘管軟體缺失已在 2015 年 5 月修正，但由於 MRI 相關研究已經高達 25 年，諸多觀點相互影響，如今很難有效檢驗 fMRI 研究結果。

延伸閱讀:
* [A Bug in FMRI Software Could Invalidate 15 Years of Brain Research](http://www.sciencealert.com/a-bug-in-fmri-software-could-invalidate-decades-of-brain-research-scientists-discover)
* [fMRI 腦造影研究全面崩盤？](http://pansci.asia/archives/101635)

## Intel Pentium 浮點數運算錯誤導致全面回收

![](https://hackmd.io/_uploads/rJOE7pdJn.png)

晶片設計領域中，最知名的失敗案例是 Intel 的 Pentium 處理器浮點運算單元出錯 ([FDIV Bug](https://en.wikipedia.org/wiki/Pentium_FDIV_bug))，數以萬計的 Pentium 處理器不得不回收和替換，給 Intel 造成約 4.7 億美元的損失。

1994 年 10 月，美國維吉尼亞州 Lynchburg College 數學系教授 [Thomas Nicely](http://www.trnicely.net/) 發現用電腦處理長除法時一直出錯。他用一個數字去除以 824,633,702,441 時，答案一直不正確。再者，在當時的 Pentium 處理器做 $\frac{4195835.0}{3145727.0}$ 的除法運算，會輸出 1.33374，而非預期的 1.33382，產生了 0.006 偏差。

事後發現 Intel 為了加速運算，將整個乘法表內建於處理器上，但 2048 個乘法數字中，有 5 個輸入錯誤。這些錯誤其實不容易顯現，在運算過程中，它會自動修復錯誤，只有幾個二進位的數字組，才會造成完全錯誤的結果。Intel 工程師指出，大約 90 億個長除法中會有一個錯誤，但隨後有人聲稱實際上遭遇到這個錯誤的頻率要高得多。Intel 後來召回有缺陷的 Pentium 處理器產品。
* 延伸閱讀: [這個神秘的數，讓 Intel 賠 5 億美元](https://mp.weixin.qq.com/s?__biz=Mzk0NzAyMzgzOQ==&mid=2247515960&idx=1&sn=009e73c77e81075b0d2b105b9c2abad6&source=41)

根據 Moore 定律，硬體的複雜度是每 18 個月增加一倍，而工程師的設計生產力卻跟不上這個速率。徒然增加設計團隊的人力、沒在根本的生產力工具上改進，最終只會拉高整合的困難。現實的經驗中，頗多大型計畫超過一半的預算是花在整合（integration）與驗證（verification）之上。
* 延伸閱讀: [Formal Verification](https://hackmd.io/@sysprog/formal-verification)

## Ariane 5 火箭升空過程中爆炸

![](https://hackmd.io/_uploads/rJ3OQT_kn.png)

1996 年 6 月 4 日，在風和日麗的法屬蓋亞那太空中心 (法文: Centre Spatial Guyanais，位於南美洲北部大西洋畔，與巴西和蘇利南交界)，歐洲太空總署發射一艘名為 Ariane 5 的火箭，預計運送 4 顆太陽風觀察衛星到軌道。火箭本身加上載運的人造衛星及科學儀器值 75 億美元 (台幣約 2250 億元)。Ariane 名稱來源於神話人物阿麗雅杜妮 ([Ariadne](https://en.wikipedia.org/wiki/Ariadne)) 的法語拼寫。

在當時，Ariane 系列火箭承包了全球商業衛星的發射約 50% 的業務量，而人們萬萬沒想到，Ariane 5 發射後 37 秒，升空到 4 公里處，火箭偏離預定飛行軌跡，解體並爆炸，2 名法國士兵當場死亡。

失事調查報告指出：水平加速偵測儀傳遞一個 64 位元浮點數值給電腦，電腦用 16 位元有號整數來接受這個浮點數，很不巧傳來的數字大於 32767，發生溢位 (Integer Overflow) 例外，這樣的軟體設計缺失很常見，但對 Ariane 5 來說，下場竟是火箭的推進向量噴嘴忽然朝某個方向轉到底，在高速偏航後，最終火箭解體。

開發 Ariane 5 火箭的軟體工程師在前一代 Ariane 4 中，小心翼翼地確認數值分佈， 確保水平速率的數值絕不會超過 16-bit 表達範圍以外。這些工程師在 Ariane 5 系統沿用這段程式碼，卻沒有檢查前述假設是否符合新的設計。

C 語言測試程式:
```c
#include <stdio.h>
#include <stdint.h>

void show_info(double d) {
    int16_t n = (int16_t) d;
    printf("d = %f, n = %d \n", d, n);
}
int main() {
    double d1 = 32767.0, d2 = 32768.0;
    show_info(d1); show_info(d2);
    return(0);
}
```

執行輸出:
```
d = 32767.000000, n = 32767 
d = 32768.000000, n = -32768
```

延伸閱讀:
* Discovery 的節目: [Ariane 5 rocket, Flight 501 - EPIC FAIL](https://youtu.be/qnHn8W1Em6E)

## 深擊號太空船持續重開機導致任務失敗

![](https://hackmd.io/_uploads/Hyzd4aOy2.png)

2005 年 1 月 12 日，美國 NASA - National Aeronautics and Space Administration 發射一艘名叫「深擊號」(Deep Impact) 的太空船，展開 4.31 億公里的彗星之旅。「深擊號」掛載一枚 370 kg 重的銅球，預計在 7 月 4 日最靠近坦普一號 (Tempel 1) 彗星時射出銅球以撞擊彗星，藉由彗星爆裂方式及粉塵分佈，了解彗星的內部組成。

整個太空船花費 3.3 億美元 (超過台幣 100 億元)。初期一切順利，準確撞擊彗星並成功觀測到噴發物質。此後「深擊號」繼續進行名為 EPOXI (太陽系外行星觀測和深度撞擊擴展研究) 的任務，先後飛越 Boethin 彗星和哈雷 2 號彗星，拍攝照片供研究之用。

可惜好景不長，在 2013 年 8 月 11 日， NASA 表示:
> Communication with the spacecraft was lost some time between August 11 and August 14... 

接著發現「深擊號」不停地重開機，不理會 NASA 傳過去的任何命令。

分析後，發現又是另一個 integer overflow 導致的問題: 8 月 11 日剛好是從 2000 年 1 月 1 日開始起算的第 2^32^ 個 tick。

延伸閱讀:
* [Deep Impact: Contact lost and end of mission](https://en.wikipedia.org/wiki/Deep_Impact_(spacecraft)#Contact_lost_and_end_of_mission)
* 展示影片: [conceptualized view of Deep Impact's encounter with comet Tempel 1](https://youtu.be/bqg4htHBbds)

## 火星氣候探測者號解體失聯

![](https://hackmd.io/_uploads/HJD0VTuyn.png)

美國 NASA 發射的火星氣候探測者號 (Mars Climate Orbiter) 於 1999 年發射進入預定軌道。不幸的是，在運行 286 天後，這個價值 3 億 2760 萬美元的飛行器失聯了。

失聯原因在於，探測器的地面控制團隊使用英制單位來發送導航指令，而探測器的軟體系統使用公制來讀取命令。這一錯誤大大改變導航控制的路徑。最後探測器進入過低的火星軌道 (約 100 公里的誤差），在過大的火星大氣壓力和摩擦下解體。

該探測器從地球發射到抵達火星，近一年時間，最終化為烏有。

## 火星極地登陸者號著陸意外

![](https://hackmd.io/_uploads/S1vbHTOJh.png)

1999 年 12 月 3 日，火星極地登陸者號 (Mars Polar Lander) 著陸失敗墜毀。

火星極地登陸者號著陸前，船殼 (aeroshell) 會減緩 90% 的速度，降落傘會減緩 9% 的速度，接著逆向噴射，伸出著陸腳，靠衝擊把探針插入地面。

伸出著陸腳時，電腦會因為伸出時的振動，誤認已著地而關閉引擎。於是工程師寫了程式避免此狀況，但是沒有實際測試過。極地登陸者號就在火星上空大約 40 公尺處，關閉逆向噴射引擎而墜毀。探針也因為沒有測試而失效。

月球探勘者 (Lunar Prospector) 計畫主持人 Alan Binder 在 [NASA 3: Mission Failures 訪談][MissionFailures] 中表示，肇因於當時 NASA 局長 Daniel Goldin 提出 "faster, better, cheaper" 策略，但實際上缺乏管理與正確的工程方法。

2005 年孫維新教授在[孫維新談天][WeiHsinSun]提到，因為美國找到火星極地登陸者號的墜毀地點，又詳述該事件的前因後果。

[MissionFailures]: https://www.youtube.com/watch?v=YJ6pbCHpXEI
[WeiHsinSun]: http://abel.math.ntnu.edu.tw/~lyz/commend/%AE%5D0524.mp3

## 波音 787 自動關機

![](https://hackmd.io/_uploads/ryF0S-pE9.png)

波音 787 的全銜為「波音787夢幻客機」(Boeing 787 Dreamliner)，是波音公司最新型號的廣體中型客機，於 2011 年投入服務，可載 242 至 395 人，視座位編排而定。燃料消耗方面，波音 787 比起波音 767 更省油，效益高出 20%。而在用料方面，波音 787 是首款主要使用複合材料建造的主流客機。2015 年 [The New York Times 的報導](https://www.nytimes.com/2015/05/01/business/faa-orders-fix-for-possible-power-loss-in-boeing-787.html)讓這款波音 提及 787 不再「夢幻」：波音 787 的電力控制系統在 248 天電力沒中斷的狀況下，會自動關機，為此 FAA (美國聯邦航空管理局) 告知應每 120 天重開機，看來「重開機治百病」放諸四海都通用？這當然是飛安的治標辦法，我們工程人員當然要探究治本議題。

任教於美國 [Carnegie Mellon University](https://www.cmu.edu/) (CMU) 的 Phil Koopman 教授指出，這其實就是整數溢位，再次驗證「失之毫釐，差之千里」的道理。

我們先將 248 天換成秒數:
$248 days \times 24 hours/day \times 60 minute/hour \times 60 seconds/minute = 21,427,200$
這個數字若乘上 100，繼續觀察：
`0x7FFFFFFF` (32 位元有號整數最大值) = 2147483647
$\frac{2147483647}{24 \times 60 \times 60} \div 100 = 248.55 days$
看出來了嗎？每 $\dfrac{1}{100}$ 秒紀錄在型態為 32 位元有號整數的軟體計數器，然後遇到整數溢位，從而使得機載全部 6 組發電機控制單元進入故障保險 (fail-safe) 模式，也就無法符合預期地運作。

延伸閱讀: [Counter Rollover Bites Boeing 787](http://betterembsw.blogspot.com/2015/05/counter-rollover-bites-boeing-787.html)

## 愛國者反導系統軟體失靈

![](https://hackmd.io/_uploads/H1FLHTuy3.png)

1990 年底至次年初，老布希發動美國對伊拉克的第一次戰爭， 名為「沙漠風暴」(Desert Storm)，就如 21 世紀初由小布希發動的第二次美伊戰爭，多數美軍的傷亡都發生在自己人的不慎，也就是「友軍火力」(friendly fires) 的誤傷。沙漠風暴中美軍最大的一次傷亡事件，於 1991 年 2 月 25 日發生在沙烏地阿拉伯的德蘭 (aẓ-Ẓahrān, Dhahran) 軍營。當時正值用餐時間，造成 28 人死亡、98 人受傷，某個角度也來自「友軍火力」。

飛毛腿飛彈是當年伊拉克最神勇的武器，具備機動、快速、準確等特性，以致於一開始以色列和美國都吃盡苦頭，也無法提出有效攔截飛毛腿飛彈的反制武器。後來美國發現扔在倉庫角落的愛國者 (Patriot) 飛彈恰好是飛毛腿的剋星，就趕快徵調它上戰場。光看技術規格，愛國者是舊式地對空飛彈：設計於 1960 年代，服役於 1970 年代的西歐，所有的武器控制系統都是在當時設計和製造，當然也包含電腦程式。愛國者飛彈部署於歐洲的主要任務是攔截前蘇聯的中、高空飛機或巡弋飛彈，當時假設攔截對象的飛行速度約 2 馬赫 (時速約 2500 公里)。

愛國者飛彈被設計成機動性強、不容易被發現的小型發射砲台。而且，它可被車子載著，藏在樹林裡，發射後就逃跑換個地點躲藏，這意味著，愛國者飛彈的電腦開機時間，通常不會很長，經常不到一小時。綜合來說，愛國者的控制電腦硬體規格也相當精簡：計算儲存器僅 24 位元，對應到無號整數，就是處理介於 `0` 與 `16777215` 之間的整數。 

1991 年 2 月 25 日的美軍德蘭軍營中，「愛國者」防空飛彈竟對伊拉克來襲的「飛毛腿」飛彈無所察覺，導致美軍兵營被炸。事故原因則由於「愛國者」防空飛彈系統的一個致命軟體錯誤，導致其雷達探測系統不能有效識別、跟蹤和攔截敵方飛彈。後來的調查中發現，事故肇因於一個簡單的軟體缺失，使基地的愛國者反飛彈系統失效。

1970 年代的工程人員為愛國者飛彈引入輕便精簡的硬體，程式設計師也開發「夠用」的軟體。他們用 24 位元的整數來表示十進位小數，設定小數點下必有四位，這就是一種 [定點數](https://en.wikipedia.org/wiki/Fixed_point_(mathematics))：小數點固定在第四、五位數之間。 譬如 `31.4` 就記做 `314000`，而 `2.71828` 記做 `27183`。 愛國者的控制電腦用這種方式計算，並且把某種時間的計算結果累加到一個變數內。顯然，時間在 10^-5^ 秒就要被四捨五入到 10^-4^ 秒， 因此將會造成誤差。這小小的誤差會累積在一個紀錄時間的變數裡面， 執行得越久、累積的誤差越多。 具體地說，每小時會累積 0.0034 秒的誤差。而這個數據在愛國者飛彈的控制系統中非常重要，因為要用它和觀測所得的目標物速度 (例如飛毛腿飛彈) 來計算導引雷達的方位變化，使得雷達可以鎖定目標物，然後發射愛國者飛彈升空攔截。

過去，愛國者飛彈並不太在乎這些誤差。原因有二：
1. 愛國者飛彈著眼於高機動性的情境，它躲躲藏藏，每次開機不會太久;
2. 它的假想敵飛得不算太快，所以稍微的誤差不會導致導引雷達失效 

但在 1990 年當它臨危受命，情況卻大不相同：
1. 這次被部署在固定營地中，長時間開機，以備隨時攔截隨時可能射過來的飛毛腿飛彈;
2. 飛毛腿的飛行速度是 5 馬赫 (時速約 6150 公里)，所以前述的小小累積誤差，對導引雷達的追蹤能力有較大的影響 (每小時誤差看來「只有」0.0034 秒，而時速 5 馬赫的物件已飛 7 公尺) ;

愛國者反飛彈系統的時鐘暫存器設計為 24 位元，因而時間的精度也只限於 24 位元的精度。在長時間的工作後，這個微小的精度誤差被漸漸放大。在工作 100 小時後，系統時間的延遲是 $\frac{1}{3}$ 秒。

對絕大多數人來說，$\frac{1}{3}$ 秒微不足道，但對一個需要跟蹤並摧毀一枚空中飛彈的雷達系統來說，這樣的誤差足以釀造災難：飛毛腿導彈空速達 4.2 馬赫 (每秒 1.5 公里），這個「微不足道」的 $\frac{1}{3}$ 秒相當於大約 600 公尺的誤差。在本事件，雷達在空中發現導彈，但由於時鐘誤差沒有能夠準確地跟蹤它，因此基地的反導彈並沒有發射。

美軍並非不知這個道理。根據同年 2 月 11 日由以色列軍方協助作成的研究，發現愛國者飛彈的控制系統若連續開機 8 小時，則會造成導引雷達 20% 的偏差。以此類推，若連續開機 20 小時，則偏差超過 50%，此時愛國者將會和飛毛腿擦身而過，連邊也碰不上更別談攔截。此時已經來不及升級硬體，軟體改寫也沒來得及，於是美軍下一道貌似合理的命令：
> 愛國者飛彈的控制系統必須 ==每小時重新開機== 一遍 

這樣就使累積時間的變數歸零，重新開始，也就不至於累積太多的誤差。每次重新開機需要 90 秒，這段時間沒有防禦功能， 因此美軍安排審慎的重開機時間表，使得大家不會在同一段時間重新開機。

但就像我們可預期的悲慘狀況，那批負責德蘭基地愛國者飛彈的大兵，居然讓它連續開機 100 個小時，完全沒有遵守上述命令。或許他們覺得那台電腦跑得好好的，不明白為什麼要重新開機，也或許是他們以為前一班的人重開過。總之，釀成意外。

相當諷刺的是，美軍針對提升精準度所改寫的軟體，恰好在 2 月 26 日送達德蘭基地，也就是本事件發生的隔天。在沙漠風暴戰爭期間，愛國者飛彈的控制軟體一共改寫 6 次，每次韌體更新大約需 2 小時，在這段期間它沒有防禦能力。

這個事件或許不是程式設計師或者硬體的錯誤，而是裝備操作者的錯誤。無論如何，問題的關鍵也就在於「資料型態」的有限性，以及它在計算中所產生「無可避免」的誤差。 

伊拉克戰爭開始前，美軍專業人士就對「愛國者」飛彈的可靠性持懷疑態度。但負責研發「愛國者」的美國大型國防合約商雷神公司 (Raytheon) 卻辯稱，該公司在試射「愛國者」飛彈過程的確發現有難以分辨敵我的問題，但已改進。綜合來說，「愛國者」防空飛彈倉促投入實戰。

## 差點因為軟體釀成第三次世界大戰

:::info
「我不知道第三次世界大戰會用什麼武器，但是第四次世界大戰將會用木棍和石頭開戰」 —— 愛因斯坦
:::

冷戰時期的 1983 年，蘇聯政府的預警系統錯誤地通知美國發射了 5 枚洲際飛彈到蘇聯境內，幸運的是，當時蘇聯的值勤官員 Stanislav Petrov 中校推斷：若美國真要襲擊蘇聯，那發射的導彈絕對不止 5 枚，因此本次襲擊可能是場虛驚，而沒有大動作處置。

事後調查發現，的確是蘇聯預警系統存在軟體缺失，當陽光反射雲頂時，會給出錯誤的預警訊息。

延伸閱讀:
* [世界存亡一指間：幫助避免美蘇核戰爆發的男人](https://cn.nytimes.com/world/20170919/stanislav-petrov-nuclear-war-dead/zh-hant/)
* [35 years ago today, one man saved us from world-ending nuclear war](https://www.vox.com/2018/9/26/17905796/nuclear-war-1983-stanislav-petrov-soviet-union)

## F-22 軟體錯誤導致系統癱瘓

![image](https://hackmd.io/_uploads/rJTaa5O96.png)

2008 年，美國空軍聲稱 12 架 [F-22 Raptor](https://en.wikipedia.org/wiki/Lockheed_Martin_F-22_Raptor) (「猛禽」戰鬥機) 執行從夏威夷飛往日本的任務中，當途經國際日期變更線的時候，飛機上的全球定位系統 (GPS) 悉數失靈，多個電腦系統發生崩潰，多次重啟也均告失敗。飛行員們再也沒有辦法正確辨識戰機的位置、飛行的高度和速度。他們不得不掉頭返航，幸運的是，當時天氣很好，能見度也非常高，這給 F-22 加油的 KC-135 型加油機可引導它們安全降落，順利地返回位於珍珠港-希卡姆聯合空軍基地。

F-22 一到珍珠港-希卡姆聯合基地，不出幾個小時，問題就真相大白：軟體工程師在電腦程式中犯錯，引發一系列的問題。當時美國空軍退役少將史皮爾德稱：
> 對於那些「猛禽」戰鬥機飛行員來說，他們很幸運，因為若在實戰中發生這個問題，他們可能會被擊落。並且這個小小的軟體錯誤，將可能成為扭轉整個戰局的關鍵點，使美國陷入短時不利的戰爭局面。

值得留意的是，F-22 的航電系統 (Avionics Operating System, AOS) 初期約有 170 萬行的規模，由 Ada 語言撰寫，但美國在兩次網路泡沫後，軟體開發人才價碼高，軍方給出的薪資追不上矽谷的水準，以致於找不到充足的 Ada 人才，之後的多功能戰機 F-35 改用 C++ 撰寫 AOS，程式碼規模達到 830 萬行。
* 延伸閱讀: [戰機航電架構小史](https://www.facebook.com/notes/flak%E8%81%8A%E8%BB%8D%E4%BA%8B/%E6%88%B0%E6%A9%9F%E8%88%AA%E9%9B%BB%E6%9E%B6%E6%A7%8B%E5%B0%8F%E5%8F%B2/562182383970500)

## 缺乏妥善同步機制引發美國大面積停電

2003 年 8 月 14 日，酷暑中的美國東北部和加拿大部分地區發生大面積停電事故，給當地交通、通信和居民生活造成嚴重影響。直到同月 16 日上午， 紐約市才全部恢復正常供電。

據電腦專家分析認為，停電的直接原因在於電控系統的競爭條件錯誤，其中一個操作的兩個獨立執行緒在呼叫一段程式碼時，導致輸電系統突然發生故障。由於沒有適當的同步和容錯機制，執行緒陷入崩潰，致使輸電系統出現連鎖反應。

## AT&T 長途電話網癱瘓長達 9 小時

1990 年代，AT&T 公司在美國占據 70% 的長途通訊量，電話呼叫轉發超過 11.5 億次，是美國最大的電信通訊公司。但在 1990 年 1 月 15 日下午，該公司所有客戶都不能正常撥打長途電話，全國各地的長途電話交換機接連發生故障，陷入癱瘓。

114 台交換機每 6 秒就會當機重啟一次。

大量技術團隊分析後，得到令所有人瞠目結舌的結果，這缺失肇因於一個非常簡單的語法錯誤引起：大型交換機軟體中一個 C 關鍵字 `break` 用法錯誤。

最終 AT&T 工程師重裝電話交換機舊版軟體版本，才得以解決這個問題。但在電話網絡癱瘓的 9 個小時裡，AT&T 公司至少損失 6000 萬美元。

## 死亡之 Ping

![](https://hackmd.io/_uploads/BkkFv6u1n.png)

在 1995 年到 1996 年，許多電腦在使用 TCP/IP 通訊時，面臨嚴重的系統崩潰問題，這現象稱為「死亡之 Ping」(ping of death; POD)，肇因於貌似簡單的 `ping` 命令。

一般來說，一次 `ping` 大小為 56 位元組 (若考慮 ICMP 標頭則爲 64 位元組，再考慮 IP 標頭則為 84 位元組)。當時大部分電腦無法處理大於 IPv4 最大封包大小 (65535 位元組) 的 ping 封包，因此，發送這樣大小的 ping 可能令目標電腦崩潰。這攻擊方式很容易實現，並廣泛影響到包含 Unix, Macintosh, MS-Windows 等作業系統，連同網路印表機和路由器等。

在 1998 年後，幾乎所有的現代作業系統都修正這問題。

## 千禧蟲問題 (Y2K)

![](https://hackmd.io/_uploads/ryI2D6d12.png)

1990 年代末葉，Y2K 問題是許多專家廣泛討論的話題，它可能引發飛機碰撞、輪船偏離航向、證券交易所崩盤等問題。根本原因在於部分個人電腦和自動控制晶片一類資訊系統中的電腦程式，其年份只使用兩位十進位數來表示，因此當系統進行（或涉及到）跨世紀的日期處理運算時 (如多個日期之間的計算或比較等)，就會出現錯誤的結果，進而引發各種各樣的系統功能紊亂甚至崩潰。

比方說 1970 年用 `70` 表示，1999 年用 `99` 表示，所以當到了 2000 年 1 月 1 日之際，很多採用這種計時方法的系統都錯誤地把日期識別為 1900 年 1 月 1 日。

由於許多專家涉入，Y2K 並未造成太大的危害，但仍有零星事故。在西班牙，停車場計費表壞了；法國氣象局公布 ==19100== 年 1 月 1 日的天氣預報；在澳洲，公共汽車驗票系統崩潰。

類似 Y2K 的問題可能也會發生在 UNIX (或相容的) 作業系統身上。1971 年 11 月 3 日發布的第一版《[Unix Programmer's Manual](https://man.cat-v.org/unix-1st/2/sys-time)》提及:
> the time since 00:00:00, Jan. 1, 1971, measured in sixtieths of a second

也就是說，Bell Labs 的工程人員最初將 UNIX 時間定義為自 1971 年 1 月 1 日凌晨開始，每過 $\frac{1}{60}$ 秒遞增 `1` 的計時方式。這表示如果使用 32 位元無號整數 (最大秒值為 `4294967295`) 來儲存 Unix 時間，在大約 829 天 (約 2.5 年) 後就會溢位並重置。因應這一限制，UNIX 時間的起點多次重新設定，最終確定為 1970 年 1 月 1 日凌晨 00:00:00 UTC，並改以每秒鐘更新一次的方式計時。由於 UNIX 系統和 C 語言使用 32 位元有號整數表示時間，因此能夠支援大約 136 年的時間範圍，從 1970 年算起，向前和向後各約 68 年，即分別到 2038 年 1 月 19 日和 1901 年 12 月 13 日會達到上限並重置。

值得慶幸的是，要解決這個問題，從本質上來說並不困難。只要將時鐘系統換成更高位數的值，比如 64 位元即可，那樣就會得到一個更大的上限，Linux 核心在 64 位元硬體架構已作出對應的修正，而在 32 位元架構中則為了向下相容而未變更，但 2015 年開始即便 32 位元架構也會採用 64 位元計數值。

也許你會質疑，64 位元只是將這個問題發生的時間向後推，但實際計算可知，最大計數值對應的時間是 2920 億年，屆時人類文化是否存續都很難說。

延伸閱讀:
* [UNIX 系統的 2038 年問題](https://en.wikipedia.org/wiki/Year_2038_problem)
* [The end of an Era](https://www.linaro.org/blog/the-end-of-an-era/)
* [Time is an illusion, Unix time doubly so...](https://www.netmeister.org/blog/epoch.html)

## 英國郵政以詐欺為名，控告 736 名郵局分局長，其中不乏入獄者

![](https://hackmd.io/_uploads/S16o_6dJ2.png)

英國郵政 (Post Office Ltd) 是英國政府出資成立的郵政系統，卻以私人公司的形式經營，在英國總計部署超過一萬一千家分局。1999 年起，日本業者富士通(Fujitsu) 打造的 Horizon 系統包含軟硬體，用以支援英國郵政的營運與通訊。從 1999 年開始，英國各地的郵局業主在結帳時遇到由 Horizon 系統導致的負數或數字錯誤，導致帳目與系統出現收支不一致 (Receipts and Payments Mismatch，RPM) 的軟體缺失，致使分局的帳面出現短缺，不知情的分局長便收到告訴，根據 BBC 的報導，有些分局長為了平衡帳面上的數字，甚至不惜抵押房產來彌補短缺。

英國郵局的運營模式與台灣不同，它以類似加盟的方式運作，由政府公辦委託給民營的郵政公司，再由後者與有意經營郵局的業主簽約。許多郵局是藏身於其他商店中，這在鄉間小鎮更為常見。許多業主可能是老年人、家庭主婦或移民，並不熟悉法律或高科技。當 Horizon 系統出現問題時，業主們聯繫富士通公司的客服尋求解決方案，但得不到有效回應。系統的數字差異使得業主們不得不自掏腰包來彌補，導致許多人破產，郵局則以經營不善為由終止合約。當時，富士通公司不承認系統存在問題，堅稱系統不會出錯。

近 20 年後，經過無數次的官司和調查，才揭露出富士通和郵局早已知道 Horizon 系統存在問題。逾 700 名的分局長這十多年來相繼被告，而這群分局長之後也對英國郵政提出集體訴訟，英國郵局直到 2018 年才坦承這是 Horizon 系統的失誤，開始與這些分局長和解。其中因詐欺或竊盜被定罪的 39 名分局長，則等到英國上訴法院判決，才沈冤得雪，但過去的 20 多年裡，許多家庭因此破碎，甚至有人因破產後自殺，傷害已造成。

早在上訴判決前，就有人舉報，說電腦操作人員可變更郵局主管的資料，隨後安永會計師事務所的審計報告指出，再次發現 Horizon 系統的缺失，後者使得某些系統管理員可對郵局分局長的帳戶，進行不受限制地存取，進而致使非授權或錯誤的交易。

延伸閱讀: 《[Mr Bates vs The Post Office](https://en.wikipedia.org/wiki/Mr_Bates_vs_The_Post_Office)》

## PayPal 突發的 92 萬億客戶存款

![](https://hackmd.io/_uploads/H1rCda_Jh.png)

2013 年 6 月某日，56 歲的美國男子 Chris Reynolds 吃驚地發現，在 PayPal 發給他的對帳單中，他的帳戶餘額竟然高達 `$92,233,720,368,547,800`，接近 92 兆 (萬億) 美元！

這個數字富可敵國，是當時世界首富墨西哥電信大亨 [Carlos Slim](https://en.wikipedia.org/wiki/Carlos_Slim) (他和家族憑藉 740 億美元資產，蟬聯《富比士》全球富豪榜首位) 資產的 100 萬倍。不過，經 PayPal 及時核實後，發現這是個嚴重的系統缺失。當 Chris Reynolds 再次登錄自己的帳戶時，發現自己從億萬富翁的幻境「打回原形」，變成原本的 100 美元。

## 超過 4 億美元因軟體缺失而蒸發

![NYSE](https://hackmd.io/_uploads/SkR6-adq6.png =60%x)

騎士資本集團 ([Knight Capital Group](https://en.wikipedia.org/wiki/Knight_Capital_Group); KCP) 是美國資本市場前幾大的流動性提供商，一度占據紐約證券交易所 (NYSE) 17.3% 和納斯達克 (NASDAQ) 16.9% 的交易額，騎士資本電子交易集團 (ETG) 平均每日交易量超過 33 億筆，每日交易額超過 210 億美元。然而，2012 年 8 月 1 日，該公司電子交易系統出現故障，交易程式出錯，導致該公司對 150 支不同的股票高價購進、低價拋出，直接給公司帶來 4.4 億美元的稅前虧損，當天股票下跌 62%，使其瀕臨破產邊緣，最終被 Getco LLC 收購，隔年這項收購成為新公司 [KCG Holdings](https://en.wikipedia.org/wiki/KCG_Holdings)。美國證監會後來對其處以 1200 萬美元的罰款。

根據美國證監會的報告，騎士資本會在收到大宗交易指令時，基於市場流動性和當時的交易條件，將這些大宗指令分割為多個較小的指令以促成交易，這一做法旨在保障客戶利益。騎士資本依賴其名為 SMARS 的高頻交易系統來實施此策略，該系統負責處理超過美國上市公司股票交易量的 1%。SMARS 的主體功能包括將收到的大宗交易指令（父訂單）拆分為一個或多個更小的指令（子訂單），以便於匹配相應的買家或賣家，從而促成交易。一般而言，父訂單越大，衍生的子訂單就越多。

2005 年，騎士資本去除其 "Power Peg" 功能模組中的股份追蹤計數功能。到 2012 年，為了支持紐約證券交易所的 [Retail Liquidity Program](https://www.marketswiki.com/wiki/NYSE_Retail_Liquidity_Program) (RLP)，騎士資本進行 SMARS 系統的更新。然而，在部署過程中，第 8 台伺服器未能正確接收新的 RLP 程式碼更新，且 "Power Peg" 功能未從該伺服器上移除。

當 2012 年 8 月 1 日市場於美國東部時間上午 9:30 開盤時，騎士資本開始處理客戶訂單。正確更新 SMARS 系統的 7 台伺服器能夠正常地處理這些訂單，但第 8 台伺服器因觸發 "Power Peg" 標誌而錯誤地啟動舊有的程式碼。該程式碼原本設計於子訂單執行階段根據父訂單進行股份計算，並在父訂單執行完成後終止子訂單的發送。由於 2005 年騎士資本已將 "Power Peg" 中的累計追蹤功能移除，當第 8 台伺服器上的 "Power Peg" 被啟動時，缺少根據父訂單進行股份追蹤的功能，導致系統陷入無止境循環的狀態，不斷處理並執行子訂單。換句話說，SMARS 未能識別客戶訂單已提交過，導致它反覆向交易所發送交易單，引起市場價格的劇烈波動，有 75 支股票的交易量由騎士資本推動，超過市場總交易量的 20%，使得這些股票的價格至少上漲 5%，另有 37 支股票的交易量超過市場總交易量的一半，導致股價至少飆升 50%。統計數據顯示，出問題的伺服器在 45 分鐘內接收 212 個客戶訂單，並向交易所提交數百萬個小型交易單，其中超過 400 萬個交易單被成交，影響 154 支股票，總交易量超過 3.97 億股。

禍不單行的是，騎士資本的風險管理措施以事後處理為主，缺乏預防性的控制。儘管騎士資本為公司設定風險敞口 (risk exposure) 的上限，但當交易超過這個上限時，其交易系統不會觸發任何自動停止機制。2012 年 8 月 1 日的事件中，所有重複提交的交易累積在編號第 33 號帳戶中，迅速超出其風險敞口限制，但系統未因超限而自動暫停交易，使得問題逐步擴大。騎士資本使用的風險管理工具 PMON 主要用於事後分析，由於其高度依賴於人工即時監控，當第 33 號帳戶遭遇問題，業務團隊難以迅速識別問題根源，遑論及時意識到其嚴重性，結果造成長達 45 分鐘的失誤，平均一分鐘損失 1000 萬美元。

延伸閱讀: [In the Matter of Knight Capital Americas LLC Respondent](http://www.sec.gov/litigation/admin/2013/34-70694.pdf) (美國證監會關於騎士資本的調查報告)

## 台灣證交所異常熔斷

2017 年 1 月 9 日，台灣「股王」[大立光電](http://www.largan.com.tw/)盤中觸動證券交易所的「盤中瞬間價格穩定措施」高達 22 次，外界懷疑有特定外資操控價格。後來，證交所澄清，肇因於證交所電腦計算公式有誤，業已修正。

原本台灣證券交易所內部系統中，紀錄股票交易價格的變數類型為 32 位元的無號整數，有效範圍是介於 0 至 4,294,967,295 ($2^{32} - 1$)。證交所股票交易變數的設計，事先預留小數點後三位，比方說公司股票為 10 元，系統則會將其標註為 `10.000` 元，又方便系統進行整數運算，還會額外乘上 1000 倍，也就是說內部儲存 `10000`。在交易系統發生異常的當日，大立光股票價格來到歷史高點的 4150 元。經過交易系統第一次的加工計算，此時，資料欄位所記錄的數值為 4,150,000。

為避免股票市場發生產生過度動盪，在盤中瞬間價格穩定措施中規定，當股票試算後的成交價，與前一次成交價比較後，如果系統發現上下波動超過 3.5％，此時便會暫停交易撮合 2 至 3 分鐘。為了進行比較，交易系統會將股票交易價格變數的值乘上 $1 + 3.5\%$ 來計算。但為避免因浮點數值運算而產生誤差，交易系統還會將處理數值乘上 1000 倍，也就說，實際乘法 1035 倍，而非僅是乘以 $103.5\%$。此時，股票交易價格變數所儲存的數值已經到達 4,295,250,000，後者已經超過前述 32 位元無號整數的上界，進而產生溢位。

延伸閱讀: [大立光股價乘百萬倍導致記憶體溢位](http://www.ithome.com.tw/news/111130)

## PSY 大叔點閱量超出 Youtube 播放上限
![](https://hackmd.io/_uploads/rkSQFa_kn.png)

2014 年，PSY 的《[江南Style](https://www.youtube.com/watch?v=9bZkp7q19f0)》影片在 YouTube 的播放次數超過了計數器上限，導致 Google 不得不對 YouTube 進行技術調整。

YouTube 之前的計數器上限為 32 位元有號整數，即最多為 `2147483647` 次觀看量，當《江南 Style》出現後，點擊量遠遠超過該數，Google 及時調整播放上限為 64 位元，即`9223372036854775808` 次播放數。為此 Google+ 上[發表聲明](https://plus.google.com/+YouTube/posts/BUXfdWqu86Q)稱：
> 「我們從未想過一段影片的觀看量會超過 32 位元的整數上界(=2,147,483,647 次觀看量)，直到我們遇到 PSY」

## Flexcoin 比特幣交易平台破產
![image](https://hackmd.io/_uploads/rJwt_rlRbx.png =70%x)

2014 年， 加拿大 Flexcoin 交易平台遭到攻擊，總共損失 896 BTC (以同年 3 月 2 日收盤價 US$559.79 計算，約為 50.2 萬美元，以 2026 年 4 月 BTC 價格估算，這批 BTC 約值 6790 萬美元)。BTC 的通訊協定本身沒問題，交易所後端在處理提款請求時缺乏適當的並行處理，攻擊者利用系統中的 race condition，在餘額檢查與更新之間的時間差([Time-of-check to time-of-use](https://en.wikipedia.org/wiki/Time-of-check_to_time-of-use), TOCTOU)，同時發送大量提款請求，使多筆交易在餘額尚未更新前通過驗證，導致帳戶透支並重複提領資產。由於 Flexcoin 沒有足夠的資源、資產或其他條件來從這次損失中恢復，因此宣告破產。

示意程式碼:
```python
def withdraw(user_id, amount):
    balance = db.get_balance(user_id)

    if balance >= amount:
        # ⚠ race condition window
        db.update_balance(user_id, balance - amount)
        send_bitcoin(user_id, amount)
        return True

    return False
```
肇因於:
* `get_balance` 和 `update_balance` 分開，沒有使用不可拆分 (atomic) 的 transaction
* 沒有鎖 (lock): 多個請求可以同時通過 `if` 敘述
* 沒有 ledger (帳本): 直接變更 balance、缺乏 transaction log

攻擊流程：
* 準備大量並行請求
* 同時呼叫 withdraw API
* 所有請求端同時看到「餘額足夠」
* 系統重複扣款並發送 BTC
* 直到帳戶變負數，BTC 已發出、無法追回

延伸閱讀: [The race condition that led to Flexcoin bankruptcy](https://vladmihalcea.com/race-condition/)

## 資料來源
* 《致命 Bug 軟體缺陷的災難與啟示》(2016 年簡體中文翻譯出版)
* [孫維新談天錄音檔](http://abel.math.ntnu.edu.tw/~lyz/commend/commend.htm)
* Wikipedia: [火星極地登陸者](https://en.wikipedia.org/wiki/Mars_Polar_Lander)