有著 1 萬個全域性變數的一大坨程式碼

黃小非發表於2016-05-13

2013 年 10 月,豐田公司匆匆了結了“意外突然加速”(以下簡稱”突然加速“)訴訟案。經過數小時的討論,俄克拉荷馬法庭陪審團得出結論:豐田汽車製造商”貿然不顧“(使用者的安全),並裁定豐田公司賠償原告 300 萬美元。這還不包括對豐田公司可能做出的大額懲罰性賠償。

陪審團是根據所掌握的證據給豐田公司下達“嚴重忽視安全責任”判決的。原告方的兩位軟體專家針對豐田公司的軟體設計以及設計過程給出了證詞,其內容令人驚訝。在審閱了 2005 版的豐田凱美瑞汽車的軟體開發過程和原始碼之後,兩位軟體專家得出一致結論:豐田公司的系統不但有缺陷,而且達到了危險的程度。因為故障保護機制裡充斥著錯誤和不一致,這是導致事故的根源。

Bookout 和 Schwarz 起訴豐田案件起因於 2007 年 9 月的“豐田車突然加速導致嚴重車禍”事件。當時 Jean Bookout 和她的朋友 Barbara Schwarz(當時坐在副駕駛)正準備從俄克拉荷馬 69 號洲際公路駛出,結果她駕駛的 2005 版本豐田凱美瑞的油門失去了控制。當時她踩剎車卻沒用,於是不得不拉起了手剎,結果車的右後輪留下了 150 英尺的剎車印,左後輪留下了 25 英尺的剎車印。然而豐田凱美瑞並沒有停下來,而是加速衝下了匝道,穿過了底部的道路,並撞上了路堤。Schwarz 重傷不治身亡;Bookout在醫院裡躺了5個多月,才從嚴重的頭部和背部傷勢中恢復。

Beasley Allen 律師事務所的 Graham Esdale(原告的律師)是最早做出類似最終裁決判斷的人。他的判斷就是基於匝道的路面上那兩條剎車印做出的。

“豐田公司無法解釋清楚這是怎麼回事,”Esdale說,“有剎車印就證明了她當時確實有剎車動作。”

儘管技術討論已經(客觀)決定了證詞的內容,陪審團的態度還是非常謹慎細緻。在陪審團意識到此案已經結案後,陪審員又詢問Patricia Parrish 法官是否可以對庭審內容繼續進行討論。十二位陪審員、Parrish 法官和被告律師又對此案進行了討論。被告表示,從他們討論的內容看來,陪審團明顯是要對豐田公司的所作所為和企圖掩飾的行動開展進一步的懲罰。

儘管已經有了剎車痕跡作為證據,原告方依然聘請了兩位軟體專家:Phillip Koopman 和 Michael Barr 作為專家證人,讓他們以程式設計師的獨特視角,來洞察豐田公司汽車軟體研發過程和原始碼中的各種問題:例如 位翻轉(bit flips),任務終止導致的故障保護機制失效,對爆棧和記憶體溢位的防護機制不足,單一的故障隔離區域,濫用全域性變數(全域性變數達上萬個之多)等等。(調查結果顯示,)豐田在軟體開發過程以及軟體產品上的缺陷,多得難以逐一列舉。

有著 1 萬個全域性變數的一大坨程式碼

  • Michael Barr 是一位廣受尊敬的嵌入式軟體工程師專家。他花了超過 20 個月的時間審查豐田的原始碼,審查地點設在一個酒店套間大小的房間內,房間一共有五個隔間,Barr 就在這五個隔間中的一個裡進行審查工作。審查過程由保安全程監視,參與者工作時不能穿皮帶或者戴手錶,而且連一張紙都無法帶進帶出。Barr 對豐田原始碼給出了法庭證詞,這些證詞都基於他所做的長達 800 多頁的測試報告。
  • Pillip Koopman 是卡耐基梅隆大學計算機工程系的教授,他是嵌入式系統安全領域的專家,著有《Better Embedded System Software》一書。他的工作也包括為私人企業做軟體設計審查,其客戶中就有很多是汽車製造企業。他也對豐田的工程安全過程給出了證詞。

這兩位都使用了程式設計師們慣用的諷刺詞彙——「一大坨程式碼(譯註:原文是 Spaghetti code,即通心粉程式碼,形容程式碼結構像通心粉一樣繞成一坨,互相糾纏,根本就理不清楚,這是很明顯的諷刺用語。),暗指豐田的程式碼無論是在寫法上還是結構上都是一團亂麻。

有著 1 萬個全域性變數的一大坨程式碼

Barr 的證詞:

(豐田的原始碼中)有大量的函式,並且都過於複雜。以標準工業的尺度來衡量,這些方法中的一部分根本無法測試,也就是說他們的程式碼構成過於複雜,因此根本找不到一個可靠的測試工具或者方法來測試程式碼可能產生的所有情況。

其中有一部分程式碼甚至複雜到了無法維護的地步,具體來說就是如果你嘗試對這型別的程式碼進行除bug或者做任何的修改,那麼很可能就會有新的錯誤產生。你的汽車下載了最新版本的韌體——也就是我們所說的嵌入式軟體——並不意味著比以前更安全……並且能夠得到結論故障保護機制不足。他們(豐田)的故障保護機制有瑕疵和漏洞。

總的來說,他們(豐田)的安全架構就像紙牌屋一樣搖搖欲墜。因此故障保護機制失效同時油門控制失靈事件發生的概率是很高的。

Barr 還從資料中瞭解到,2007年10月,甚至一位來自豐田的程式設計師都將引擎控制應用程式程式碼描述為“像一坨”程式碼。他把這一點引入了他的證詞中。

Koopman 則重點關注豐田的計算機工程過程管理。業界的首個工業編碼標準是汽車工業軟體可信度協會(MISRA)於 1995 年設立的,這個標準雖然只是廠家出於自身目的設立,卻成為了業界普遍認可的行業標準。這個標準把一系列規則當成衡量的標尺,並將破壞規則的程度等同於破壞規則的數量:每違反 30 條規則,你可以認為會有 3 個小的 bug 和 1 個大的 bug 出現。而豐田,按照工業標準的尺度,算是犯了很嚴重的錯誤,Koopman 如是說。

2010年,因為與美國高速公路安全管理局(NHTSA)有合同關係,NASA(美國國家航空航天局)的軟體工程師對豐田的部分原始碼進行了審查,結果他們針對 MSIRA-C 的第 35 條規則,在可審查的原始碼中找到了 7134 處違反點。Barr 以 2004 年版 MISRA 標準為依據,在豐田的原始碼中發現了 81514 處違反點。

豐田有自己的軟體過程標準,並且和工業標準多少有一些重疊。即使如此,豐田的程式設計師還是屢屢違反自己制定的標準。他們沒能有效地跟蹤他們偏離標準的程度,並且會為這種偏離行為進行辯解,認為這樣做是符合實際標準的。Koopman 在證詞中提到:如果不是一開始就把軟體安全納入到產品的研發中去,那麼再往後即使想加也加不進去了。

“在從事安全相關的軟體工作時,你必須要學會萬分小心謹慎。糊弄是不行的。豐田確實關心過一些安全的事情,但是他們沒能達到設計安全相關軟體所能接受的水平。”他說。

豐田所違反的最大的一條安全標準是:在系統中允許單點失靈(single point failures)機制存在。(單點失靈是指將整個系統安全與否的控制權賦予單個軟體或硬體,就類似於單引擎飛機一樣)Koopman 在證詞中提到。

“如果是採用單點失靈的機制,在我見過的任何一種安全標準裡面,都是屬於不安全定義範圍的,並且沒有什麼反制措施,沒有多少故障保護機制能解決這個問題。所有的方法只能降低失靈發生的概率,但是不可能完全根除問題。我們有數以百萬計的車在路上跑,(對於這麼龐大的數字)出問題的可能性真的是千奇百怪,而且問題真的是會發生的。”

另一個非常糟糕的背離標準的行為是:在系統中大量使用全域性變數。(變數代表記憶體中的一個位置,這個位置儲存了一個數。全域性變數則意味著系統中任一地點的任一軟體都可以對其進行讀寫。)理想主義的全域性變數個數應該是 0。豐田則(在程式碼中)使用了超過10000 個全域性變數。

“在工程實踐中,總共採用 5 個或 10 個全域性變數,這都是 OK 的。10000 個就不行,那我們就完蛋了。這是不安全的,我可不想一次性檢視 10000 個全域性變數以後才知道哪裡出了問題。”Koopman在作證時這麼說。

Barr 和 Koopman 在軟體設計中發現的其他錯誤還有:缺乏平級程式碼審查(peer code review),還有就是豐田使用了電裝(Denso)公司的副 CPU,但是卻沒能對其原始碼進行檢查——而豐田公司董事會則信誓旦旦地告訴美國國會和 NHTSA,說這次“突然加速”事故不可能是由引擎的軟體引起。

Barr 作證說,很多機動車的行為故障都是由 CPU 中的任務失靈(death of task)造成的,所以可以推斷 Bookout 的“突然加速”事故也很可能是由 CPU 中某個不知名的任務突然失靈造成的,在庭審中這個任務被稱為“X任務”。Barr 還給這個任務取了個形象的名字,叫“廚房多功能水槽”任務。因為這個任務同時控制著汽車的多項功能,包括油門控制、巡航控制、轉向控制、定速和熄火控制等等——其中很多功能的故障防護功能邏輯都執行在主 CPU 上。

Barr 對豐田的“看門狗”監測程式的設計提出了批評,“看門狗”程式是專門用來探測任務失靈的軟體。他在證詞中說,豐田的“看門狗”檢測程式“對主要任務的失靈現象向來就無法探測。這個程式的唯一目的就是探測任務失靈,但是它卻無法做到。(因為)它就沒按照正確的目的來設計。“

“相反,豐田把監測程式設計來監測CPU是否過載,並且”,Barr作證到:“他們(豐田)其實連監測CPU過載也沒做對。CPU過載是指在瞬間有大量任務迸發,並要在一段時間內把這些任務都處理掉。如果這種場景持續的時間過長,那麼也會將汽車置於險境,因為任務太多造成很多工根本就沒有機會在CPU上執行,這和臨時任務失靈的效果是一樣的。”

Barr 還作證說,豐田的軟體還拋棄了作業系統產生的錯誤程式碼,直接對任務產生的錯誤碼置之不理。Barr 在庭審上說:

對於任務失靈的問題,儘管我的關注點是在 X 任務上,因為這個任務控制了油門,負責執行故障保護,它真的很重要,但是當任務 X 和其他的任務以不同的組合執行時,總會導致任務失靈的現象。比如任務 3 和任務 X 一起執行,或者任務 3 和任務 7 以及任務 X 一起執行,或者只有任務9 執行,(都可能產生失靈現象)。這樣就讓汽車的失常表現的不可預測性大大增加。而“突然加速”只是眾多失常表現中最危險的一種而已。

你很可能想否定兩位軟體專家的結論,因為他們不過是原告方聘請的技術專家,自然要為原告的利益說話,但 Koopman 和 Barr 關於軟體錯誤的評估,以及“突然加速”可能原因的解釋卻揭示了更多事實:豐田的系統如何會出問題後不留下任何系統痕跡;為什麼在豐田其他車型中也出現過“突然加速”的問題,為什麼豐田無法通過“地板墊和剎車踏板召回”來解決問題,以及豐田長期以來如何通過隱藏某些“突然剎車”根本原因來逃脫責任。

根據兩位軟體專家的描述,豐田軟體系統的複雜程度令人難以置信,這也解釋了為什麼NHTSA會做出那樣的反應,以及為什麼NASA沒有能找到豐田汽車存在“引擎馬力全開,忽略使用者剎車指令,以及沒有錯誤程式碼”等嚴重瑕疵的相關證據。比如,Barr作證說,NASA的工程師時間有限,並且沒有檢視所有程式碼的許可權。他們要完全依賴豐田公司給他們進行彙報——在某些情況下,豐田成功地誤導了NASA。例如,NASA就錯誤地相信了豐田已經為汽車設計了針對”位反轉”的硬體保護機制 EDAC(錯誤偵測和糾正編碼)。2005版的豐田凱美瑞其實根本就沒有 EDAC,Barr在證詞中說,但是豐田的郵件卻告訴NASA是有的。在庭審的時候他說:

NASA 根本就不知道2005版凱美瑞沒有 EDAC(保護機制)。2005版豐田凱美瑞根本就沒有EDAC。所以當位反轉發生的時候,肯定就不會有任何硬體機制去偵測。那麼當位反轉在特定情況下發生時,系統也就無法反應這一故障,那麼軟體防護措施自然也就無從對系統進行保護。所以可以得出結論,(在現實中)特定情況下,位反轉故障就是有可能發生的。

軟體專家們的證詞解釋了,為什麼 NHTSA 不太可能查出豐田被軟體問題掩蓋的電氣故障。在對豐田汽車的大多數調查過程中,NHTSA 下屬的故障調查辦公室團根本就沒有軟體工程師參與。他們也沒有真正的專家,能夠對現代汽車的關鍵安全問題的複雜度有足夠的瞭解。NHTSA 下屬的這些故障調查辦公室的工程師就像遠古人一樣工作,工具原始,效率低下。然後人們就見識到了這個政府機構的固執程度翻了兩倍,翻了三倍,翻了四倍,一直像一個老婦人喋喋不休,把“突然加速”的問題歸咎於汽車的腳墊。

不過即使是 NHTSA 配備了專家,由於豐田的軟體實在是過於複雜,因此故障調查辦公室仍然不可能有足夠的時間或者預算來評估豐田的原始碼。這也就是為什麼我們不停地強調 NHTSA 需要編寫功能安全規範的原因——不管是出於他們自身考慮,還是國會強制要求,他們都應該寫。

我們在網上貼出了 Koopman 的初步調查結果草案(部分1 和 部分2),以及 Barr 的 庭審證詞 和 Barr 編寫的幻燈片材料 ,這些材料很長,但是絕對值得一讀,因為它不但能滿足你對此次事件的所有興趣,還能讓你瞭解怎麼才能把一個汽車行業的嵌入式系統搞砸,以及 NHTSA 的調查為什麼會誤入歧途,還有豐田的電氣架構上的軟體是如何脆弱得令人難以置信。

通常情況下,人們會把“公司保守商業機密”和“公司保護有高價值資產”這兩件事順理成章地聯絡起來。而在這個案件裡,我們認為,所謂“有價值的資產”正是技術本身,也就是豐田公司在生產製造汽車產品時所採用的“祕密配方”。我們不能把汽車製造業的“保護資產祕密”和“可口可樂保護其配方”等同起來(譯註:據說傳統可口可樂汽水有99%的成分都是公開的,無非就是水,糖分,咖啡因等。但是可口可樂有1%的“祕密成分”是不公開的,其配方是可口可樂口味獨特的決定性因素,也是公司的“最高機密”。據傳這1%成分的配方在整個可口可樂集團只有不超過10人可以有權接觸,估值上億美元。),Koopman和Barr在法庭證言中指出,豐田公司想要隱藏的,其實是一種會製造災難的“配方”。根據2007年9月在豐田公司內部員工之間的電子郵件的內容:

“’說實話,研發故障保護機制其實從來就不是豐田公司工程部門的風格,‘”,Barr在法庭上念出了郵件的原文,“接下來郵件又寫道,‘不過如果(這種風格)被解讀為:豐田公司在工程控制領域實力強勁,那麼也不失為一件好事。’(譯註:郵件的意思是,豐田公司工程部門實力很牛,產品質量很高,所以並不需要在軟體方面再搞一個故障保護機制)。”,而後,Barr又專門標出了另一段郵件中的文字:“長此以往可不是什麼好事情。”

 


網友評價:

@天天天降大大大聖:一萬個全域性變數。。。告訴我這種程式碼怎麼做重構?怎麼維護?

@小灰筆記_Grey:回覆@douzigui:沒有挑戰,比如德國博世,他們的變數比豐田還多。通常,首先拆分成一百多個模組,命名方式再用模組名+物理衡量因子+物理含義+儲存屬性(可選)的方式,命名簡單而自然。

@ysjynkpgmw:汽車程式碼的標定量必須是全域性變數,所以一萬個全域性變數很正常,標定區給到512K的都有。行業的特殊情況。日本人這樣,德國人美國人也這樣。

@小灰筆記_Grey:博世的似乎是25000個,還不是歐六。汽車電子就是這樣不是?否則,靠什麼機制進行耦合模組間的資訊互動呢?難不成寫25000個函式?

@haitao深圳:一萬個全域性變數?如果都是需求必需的:必須全域性隨時使用。。。那隻能儘量分層分類儲存了,或者設計分級的訪問介面

@重灌旗艦:嵌入式開發不都是這樣,大驚小怪,你以為一個8位的微控制器,ram不足2m能給你多大空間寫類來封裝

@肖寒_THU:全域性變數很多時候可以起到加速的作用。為了極致的速度可能會這麼做。

@hentai悠:不要把全域性變數搞得跟不能用似的。。。多少新人被嚇得一個都不敢用!儘量按檔案劃分好模組使用static全域性變數就比較明顯了啊!

@市場街的私奔諾莎:嵌入式系統這樣寫很正常吧,沒有MMU沒有堆沒有執行緒沒有系統,甚至c編譯器也不一定有成熟可靠的

@小灰筆記_Grey:回覆@__i1l__:汽車電子有一個很重要的特點,即要求實時性又要求安全性。一般只能選C這樣的語言,速度必須考慮。同時,記憶體的動態分配一般是不要不要滴!//@__i1l__: 用JS寫的嗎?

@邏輯引擎:由於行業壁壘,許多非資訊科技行業的軟體開發還處於石器時代,卻還要把那些亂麻般難維護難重用高度平臺依賴的垃圾程式碼當個寶,唯恐被競爭對手偷去了。事實上這些垃圾的重用成本太特麼高了,除非直接把完整的產品模組的軟硬體設計全搬過來,否則真沒什麼人稀罕那些破程式碼。

@邏輯引擎:Spaghetti code應譯成亂麻般的程式碼,還擁有上萬全域性變數。豐田尚且如此,我現在嚴重懷疑整個汽車行業嵌入式控制程式碼的安全性。該行業程式碼量巨大,但跟汽車的機電結構不同,對外根本不可見。行車安全嚴重依賴這些對外不可見的程式碼,我們感覺的安全完全是不明覺厲的朦朧安全

@龐擬:程式碼寫爛點會死人啊?!會!

@sumtec:你們不要笑豐田了,真的,有些汽車廠他們的汽車零部件標牌資料什麼的還是一個CSV或者Excel文件。還有些車企把自己的VIN號碼印製的事情外包以至於所有車型全部都在一個VIN車型下面無法區分。

有著 1 萬個全域性變數的一大坨程式碼

有著 1 萬個全域性變數的一大坨程式碼

有著 1 萬個全域性變數的一大坨程式碼

打賞支援我翻譯更多好文章,謝謝!

打賞譯者

打賞支援我翻譯更多好文章,謝謝!

有著 1 萬個全域性變數的一大坨程式碼

相關文章