前幾年鬧得沸沸揚揚的豐田剎不住事件最近又有新進展蒲跨。十月底俄克拉荷馬的一次庭審旭愧,2007年一輛2005年凱美瑞暴沖(Unintended Acceleration邢享,UA)致一死一傷事件中豐田被判有責侨赡。引起廣泛關注的是庭審中主要證人Michael Barr的證詞讓陪審團同意豐田的動力系統(tǒng)軟件存在巨大漏洞可能導致此類事件摇庙。這是豐田在同類事件中第一次被判有責。庭審過后豐田馬上同意支付300萬美元進入調(diào)解程序絮姆。
出于好奇醉冤,我漫不經(jīng)心地下載了Barr的286頁證詞,卻一下子被吸引住了篙悯。幾天內(nèi)讀完蚁阳,算是對這次事件進行了一次深入了解。下面就從外行角度總結一下這份證詞并嘗試以更簡單的語言解釋里面提到的暴沖原因以及豐田犯下的錯誤鸽照。
Barr的證詞下載自他的個人博客Barr Code螺捐,但現(xiàn)在該文已經(jīng)被刪除。稍后找地方上傳矮燎。
Michael Barr是誰定血?他是一位擁有20年以上行業(yè)經(jīng)驗的嵌入式系統(tǒng)工程師。在十八個月中诞外,有12位嵌入式系統(tǒng)專家澜沟,包Barr,受原告訴訟團所托峡谊,被關在馬里蘭州一間高度保安的房間內(nèi)對豐田動力控制系統(tǒng)軟件(主要是2005年的凱美瑞)源代碼進行深度審查茫虽。這房間沒有英特網(wǎng)铝条,沒有手機信號,他們進出不能攜帶任何紙張席噩、記錄甚至皮帶。最后的調(diào)查結果被寫入一份800頁贤壁,13章的詳細報告悼枢。而鑒于保密協(xié)議,調(diào)查內(nèi)容一直沒有公布脾拆,直至俄克拉荷馬這次庭審才首度部分公開(報告本身似乎還沒公開)馒索。
回到正題。豐田的軟件有沒有缺陷名船?根據(jù)Barr的調(diào)查绰上,答案是有。這其實是廢話渠驼,任何軟件都會有缺陷蜈块,關鍵在于是什么樣的缺陷。豐田的軟件缺陷分為三類:
非常業(yè)余的結構設計迷扇。
軟件設計的基本要求是模塊盡量簡單化百揭,因為這樣可以一來更易于閱讀二來更易于維護。但豐田的工程師顯然沒有遵循這原則蜓席。Barr使用一種工具自動根據(jù)代碼的可能分支數(shù)量評估函數(shù)的復雜度器一,結果是豐田的軟件中至少有67條函數(shù)復雜度超過50,意味著運行這個函數(shù)可能出現(xiàn)超過50種不同的執(zhí)行結果厨内,屬于“非可測”級別祈秕。因為為了測試這50個不同的結果,必須準備至少50條不同的測試用例以及相應的文檔雏胃,在生產(chǎn)環(huán)境中一般是不現(xiàn)實的请毛。作為比較,Barr表示他自己的公司嚴格執(zhí)行的其中一條規(guī)定就是任何代碼復雜度不能超過30瞭亮,否則不合格获印。而在這67條函數(shù)中還有12條復雜度超過100,達到“非可維護”級別街州,意味著一旦發(fā)現(xiàn)缺陷(Bug)也無法修復兼丰,因為實在太復雜,修復缺陷的過程中會產(chǎn)生新的缺陷唆缴。其中最復雜的一條函數(shù)有超過1300行代碼鳍征,146個可能執(zhí)行路徑——正好用于根據(jù)各傳感器數(shù)值計算節(jié)氣門開關角度。
如果你不知道什么是節(jié)氣門面徽,那么這里我稍微解釋一下艳丛。為了讓內(nèi)燃機運行匣掸,有三大要素:燃油、空氣和點火時機氮双∨鲈停空氣和燃油的混合物進入氣缸,被火花塞在正確的時間點燃推動活塞并最終推動曲軸和車輪前進戴差。在電噴技術發(fā)明以后直到2002年以前送爸,三大要素的燃油和點火時間是由電子設備控制,節(jié)氣門機械連接加速踏板暖释,由司機直接控制袭厂。節(jié)氣門大致是一個連接加速踏板的“空氣龍頭”——踩下去越多,“龍頭”打開得越大球匕,允許越多的空氣進入發(fā)動機輸出更大的動力纹磺。2002年以后,豐田引入的“電子油門”讓電子系統(tǒng)掌管了最后一個要素:空氣亮曹。加速踏板不再機械連接節(jié)氣門橄杨,而是連接一些傳感器,由行車電腦將這些傳感器數(shù)值計算成節(jié)氣門開啟角度再由馬達控制節(jié)氣門照卦。我們稍后會再討論節(jié)氣門開合讥珍。
極復雜的代碼帶來的是極復雜的功能。下面說一下被稱為“廚房洗滌盆”的Task X窄瘟。這里先解釋一下衷佃,豐田的軟件系統(tǒng)和很多別的軟件系統(tǒng)一樣,都是由一個統(tǒng)領程序(稱之為“操作系統(tǒng)”)和若干小程序(稱之為Task)組成蹄葱。就好比電腦上跑的Windows是統(tǒng)領全局的操作系統(tǒng)氏义,網(wǎng)絡瀏覽器和記事本是跑在操作系統(tǒng)上的小程序。豐田的系統(tǒng)里每個Task都有自己的名字图云,但這些名字非常敏感惯悠,敏感到每次被提及的時候律師都要求法庭內(nèi)的沒有閱讀代碼權限的人全部清場。為了減少清場次數(shù)竣况,Barr將這個最重要的小程序稱為Task X克婶。這個Task X有多重要呢?跟廚房里的洗滌盆一樣重要丹泉。它負責非常多的事情情萤,包括計算節(jié)氣門開啟角度、速度監(jiān)測和保持摹恨、定速巡航監(jiān)測等等筋岛。Task X的不正常運行被認為是暴沖事件的元兇。稍后會再繼續(xù)討論Task X晒哄。
還有一些別的匪夷所思的發(fā)現(xiàn)睁宰。比如豐田的軟件包含了超過一萬一千個全局變量肪获。如果你不知道什么是全局變量,那么只需要知道軟件設計的一般原則是要盡量少使用全局變量柒傻,因為有可能帶來無法預測的結果孝赫。這里的“少”的意思是“盡量接近零”,絕對不會是一萬一千個红符。
不符合軟件開發(fā)規(guī)范青柄。
如同很多行業(yè)一樣,汽車行業(yè)也有自己的規(guī)范违孝。更具體一點,由于汽車的危險性質泳赋,汽車控制系統(tǒng)被劃分為“安全關鍵性系統(tǒng)(Safety Critical System)”——說白了就是安全性非常重要雌桑,弄不好會死人的。為了達到這一特殊要求祖今,汽車相關軟件開發(fā)人員定期舉行會議討論并發(fā)布編程規(guī)范校坑,稱為MISRA C。該規(guī)范的2004年版的感謝列表里還能看到豐田員工的名字千诬,至少讓外界認為豐田確實也在遵循這些規(guī)范耍目。
真的嗎?根據(jù)源代碼來看徐绑,答案是否定的邪驮。對此之前的NASA報告也有所提及,豐田辯稱他們遵循的不是行業(yè)規(guī)范傲茄,而是豐田內(nèi)部編程規(guī)范毅访。這一規(guī)范與行業(yè)規(guī)范的吻合程度達到50%。但是Barr認為根據(jù)他的調(diào)查盘榨,兩個規(guī)范之間吻合度小于10%,甚至有若干規(guī)范條目相互沖突。后來發(fā)現(xiàn)豐田的代碼甚至沒有遵循豐田內(nèi)部規(guī)范铅檩,當然比起別的問題這個已經(jīng)無關緊要了眠寿。
MISRA C擁有超過100條規(guī)范,NASA的調(diào)查只使用了到其中35條進行校對山憨,發(fā)現(xiàn)超過7000處違規(guī)代碼查乒。Barr使用全部條目,對照結果是豐田的程序擁有超過80000處違規(guī)代碼郁竟。
這些數(shù)字說明了什么侣颂?根據(jù)統(tǒng)計,違規(guī)數(shù)量可以用于預測代碼中暗藏的缺陷(Bug)數(shù)量枪孩。在之前提到的汽車相關軟件開發(fā)人員會議中憔晒,有人就這一主題發(fā)表過專題演講藻肄,提出每30處違規(guī)代碼可能包含一個重大缺陷和十個輕微缺陷。諷刺的是這人是豐田員工拒担。
特別需要指出MISRA C其中一個規(guī)則的內(nèi)容是不得使用遞歸嘹屯。
如果你不知道什么是遞歸,那么這里我稍微解釋一下从撼。遞歸是一種計算方法州弟。但一般計算方法要么是自己算,要么詢問別的計算模塊索要結果低零。而遞歸則是通過問一層層問自己的方法完成計算婆翔。好處是代碼簡單,壞處是計算層數(shù)不固定掏婶】信可能會2層就出結果了,也可能會是10000層雄妥,在設計程序的時候無從得知最蕾。
軟件計算需要消耗存儲器。越繁瑣老厌、越長的計算自然需要占用越多的存儲器瘟则。遞歸的問題在于其嵌套層數(shù)無法預測,從而導致可能消耗的存儲器容量無法控制枝秤。稍后會再討論存儲器醋拧。
對關鍵變量缺乏保護。
什么是變量淀弹?變量就是存在一段存儲器的0和1的集合趁仙。這些變量既可以是一些函數(shù)的處理結果,也可以是另一些函數(shù)的處理原材料垦页。比方說前面提到有一條程序專門計算節(jié)氣門開合角度雀费,比如說是20度,這個20就是一個變量痊焊,存在存儲器的一個指定位置盏袄。另一個程序專門負責開合節(jié)氣門,它知道去那個指定位置讀取這個20薄啥,然后把節(jié)氣門開啟20度辕羽。
什么是保護?嵌入式系統(tǒng)垄惧,或者任何系統(tǒng)刁愿,都會在一定條件下發(fā)生硬件或者軟件錯誤〉窖罚客觀上這是無法避免的铣口。而且汽車作為一個時常在震動滤钱、發(fā)熱、位移的系統(tǒng)脑题,它的內(nèi)部環(huán)境不能說不惡劣件缸,發(fā)生硬件錯誤的可能性甚至更高。什么樣的硬件錯誤呢叔遂?別忘了變量都是0和1的組合他炊,這些0和1由存儲器上的高低電平代表。由于某些不可抗原因已艰,一個電平從高變成低痊末,或者反過來,那么這個變量就被更改了哩掺。這被稱為“位反轉(Bit Flip)”凿叠。為了對抗這樣的事情發(fā)生,需要對變量進行保護疮丛。保護的方法一般是鏡像法幔嫂。簡單來說就是在兩個不同的地方寫入同一個變量辆它,讀取的時候兩邊都讀誊薄,比較是不是一致。如果不一致锰茉,那么可以得知這個變量已經(jīng)不可靠呢蔫,需要進行容錯處理。
豐田的程序總體上對其上萬個變量進行了有效保護飒筑,但問題出在操作系統(tǒng)上片吊。前面提到豐田的軟件本質上分為操作系統(tǒng)和Task。Task是由豐田自己開發(fā)协屡,但是操作系統(tǒng)則是由芯片供應商提供俏脊,固化在芯片里的。豐田在這里的過失是沒有對供應商提供的代碼進行深度審核肤晓,拿到什么用什么爷贫。
另一個保護措施是錯誤校驗碼(Error Detective and Correction Codes,EDAC)补憾。這是一個硬件層面的數(shù)據(jù)保護措施漫萄。簡而言之就是給內(nèi)存中每一個字節(jié)(8比特)后面物理地增加幾比特校驗碼。這樣萬一變量出錯了盈匾,可以通過校驗碼得知腾务,甚至可以通過校驗碼修復一些輕微錯誤。這個措施十分簡單有效削饵,但是在2005年款凱美瑞的系統(tǒng)中完全沒有使用岩瘦,豐田卻告訴NASA他們用了未巫。而在2008年款凱美瑞中使用了3比特長的EDAC。Barr認為是為了節(jié)省成本担钮,否則應該使用5比特長橱赠。
還有值得一提的是,汽車相關的軟件行業(yè)有那么幾家操作系統(tǒng)供應商箫津,早已形成了一套成熟標準稱為OSEK狭姨。各供應商開發(fā)的符合OSEK認證的操作系統(tǒng)至少都能達到一定的質量。但豐田選用的操作系統(tǒng)卻沒有通過認證苏遥,讓人不解饼拍。
現(xiàn)在我們知道豐田在編寫軟件的時候至少有三種缺陷。那么重點問題:豐田的這些軟件缺陷是否會導致車輛暴沖田炭?根據(jù)Barr的調(diào)查师抄,答案是有可能。暴沖的起因需要結合上述三種缺陷來說明教硫。
汽車正常運行需要倚靠若干程序(這里叫Task)的同時運作叨吮。Task有很多,CPU只有一塊瞬矩,在任何時刻只能處理一個Task茶鉴,怎么辦呢?這需要操作系統(tǒng)的統(tǒng)籌規(guī)劃景用,合理分配CPU的任務涵叮,讓每個Task都能按時執(zhí)行。如果出現(xiàn)某種意外伞插,讓某個Task突然不執(zhí)行了割粮,那么就稱為這個Task“死亡”。Task死了媚污,自然不能執(zhí)行它的任務舀瓢。根據(jù)Barr的測試,在某些特定情況下耗美,Task X的死亡可以導致節(jié)氣門敞開——因為Task X的其中一個任務就是根據(jù)司機的操作計算節(jié)氣門開合角度京髓,它死了也就沒法重新計算這個角度,即使司機把腳挪開加速踏板幽歼,節(jié)氣門也無法關閉朵锣。此為暴沖的直接原因。更糟糕的是甸私,節(jié)氣門的開合角度這個數(shù)值诚些,被Task X算出來以后保存在一個變量中。這個特定的變量正好沒有被保護(缺陷3)。意味著萬一Task X死亡并且停止計算诬烹,這個數(shù)值有可能因為不可抗原因被改變砸烦,而程序無從得知。
那么Task X為何會死亡呢绞吁?一般是因為內(nèi)存出錯幢痘。這個出錯可能是一個小小的位反轉,也可能是內(nèi)存里的數(shù)值被別的程序錯誤覆蓋家破。同一系統(tǒng)內(nèi)同時運行了若干程序颜说,這些程序需要共享一塊內(nèi)存,內(nèi)存內(nèi)部必然要被劃分成若干塊汰聋。比如第一塊給程序1门粪,第二塊給程序2,等等烹困。如果程序1因為某些原因(比如Bug)寫到第二塊內(nèi)存上去玄妈,就會導致程序2讀取了錯誤的信息。這就是所謂的內(nèi)存出錯髓梅。豐田的系統(tǒng)里拟蜻,正好有這么兩塊相鄰的內(nèi)存塊。第一塊被稱為“堆棧(Stack)”枯饿,這是所有Task存儲它們運行狀態(tài)的地方酝锅,大小為4KB。與之相鄰的地方儲存了操作系統(tǒng)進行任務分配的記錄鸭你。那么可以想象屈张,如果很多Task給堆棧里寫入太多東西擒权,超過4KB袱巨,那么就會錯誤地寫入與之相鄰的任務分配表。這種錯誤被稱為“堆棧溢出”碳抄。操作系統(tǒng)拿到了錯誤的任務分配表愉老,就會錯誤地分配任務,造成某些Task多執(zhí)行幾次剖效,某些Task少執(zhí)行幾次嫉入,某些Task甚至就再也不執(zhí)行——死了!必須指出的是璧尸,程序死亡并不罕見咒林,甚至可以認為是正常現(xiàn)象爷光。稍后解釋處理方法垫竞。
那么堆棧為什么會溢出呢?顯然是因為要寫入的數(shù)據(jù)超過了堆棧的容量。在設計程序的時候要計算最壞的情況并且允許冗余欢瞪。即使作出了正確的設計活烙,這種錯誤也相對常見,所以NASA在他們的調(diào)查中重點排查堆棧溢出的可能性遣鼓。于是NASA問豐田啸盏,豐田的回復是最壞的情況下4KB堆棧只寫入了41%的數(shù)據(jù),換句話說發(fā)生溢出的可能性非常低骑祟。NASA直接取信了這個數(shù)字并沒有再深入調(diào)查回懦。但Barr他們發(fā)現(xiàn)豐田的回答有嚴重低估,實際上最壞的情況會達到94%次企,而且還不算遞歸粉怕。豐田在代碼中使用了遞歸(缺陷2)。因而實際數(shù)字有可能超過94%而且無法預計上限抒巢,因為遞歸計算的嵌套層數(shù)是無法預測的贫贝。所以實際情況下堆棧溢出的可能性相當可觀。一旦溢出蛉谜,相鄰的任務分配表不可避免就會遭到破壞稚晚。此為暴沖的根本原因其中之一。之所以說“其中之一”型诚,是因為堆棧溢出僅僅是損壞任務分配表的其中一個原因客燕,別的還有許多可能性并沒有被Barr在法庭上深入解釋。而且任務分配表的損壞也僅僅是導致Task死亡的原因之一狰贯。
順便提一句也搓,2005年的凱美瑞的這部分供應商是電裝,沒有搭載堆棧實時監(jiān)測功能——溢出了也不知道涵紊。同年的卡羅拉卻搭載了傍妒,因為供應商是通用。
到這里我小結一下摸柄,串鏈子颤练。左邊是原因,右邊是后果驱负。
堆棧溢出→(可能導致)→任務分配表被改寫→(可能導致)→Task X死亡→(可能導致)→節(jié)氣門敞開→(導致)→汽車暴沖
必須指出的是嗦玖,這條鏈子從最左邊一直到Task X死亡,都還算是嵌入式系統(tǒng)的常見故障跃脊。雖然程序代碼寫得不好也許導致更容易出錯宇挫,客觀上豐田并沒有特別大的過錯。只要處理得當酪术,這些故障都不會導致暴沖器瘪。
到此為止還只是前奏而已,接下來我們來看看豐田到底做錯了什么。
【第二部分】豐田之罪
上面反復提到娱局,嵌入式系統(tǒng)中內(nèi)存出錯或者程序死亡其實是一種正痴煤ィ現(xiàn)象——至少從Barr的證詞可以得出這個結論——現(xiàn)在連我們都知道了,嵌入式工程師肯定比我們更清楚才對衰齐。確實任斋,為了使系統(tǒng)正常運行不被錯誤干擾,一般的做法是設置若干層防護措施(Failsafe)耻涛,讓運行中出現(xiàn)的錯誤無法輕易突破废酷,得到妥善處理。豐田的工程師自然也懂得這一點抹缕。很可惜澈蟆,他們搞砸了。
上面那條鏈子應該修改成這樣:
(防護措施0)→堆棧溢出→(防護措施1)→(可能導致)→任務分配表被改寫→(防護措施2)→(可能導致)→Task X死亡→(防護措施3)→(可能導致)→節(jié)氣門敞開→(防護措施4)→(導致)→汽車暴沖
可以看到卓研,防護措施不可謂不多趴俘。只要處理得當,這鏈條應該是走不通的∽嘧福現(xiàn)在讓我們從左到右看一個小小的內(nèi)存錯誤是如何一層層突破防護最終導致汽車暴沖的寥闪。
首先防護措施0。這個其實上面提到了磨淌,因為設計缺陷低估了最大占用的存儲容量疲憋,并且不符合規(guī)范地使用了遞歸,最終可能導致堆棧溢出梁只。
然后到防護措施1缚柳。上面也提到了,任務分配表緊鄰堆棧搪锣。作為外行我都覺得這是個十分危險的設計——既然堆棧這么容易溢出秋忙,好歹應該將任務分配表放遠一點啊。當然我是外行淤翔,可能實際上比想象中復雜很多翰绊。這段Barr的證詞中并未特別提到佩谷,屬于我的個人理解旁壮。
防護措施2。從這里開始豐田的錯誤越發(fā)嚴重谐檀。任務表被改寫導致某些Task運行異常抡谐,在軟件層應該有若干檢測措施,比方說用特殊的監(jiān)視Task來監(jiān)視別的Task是否正常桐猬。但豐田是怎么做的呢麦撵?還記得上面的“廚房洗滌盆”Task X嗎?它是如此復雜(缺陷1),除了控制汽車運行的任務之外竟然還兼任大部分的監(jiān)視任務免胃,比如生成DTC音五。
DTC(diagnostic trouble codes),是汽車電腦系統(tǒng)會根據(jù)情況生成的錯誤代碼羔沙。有的車主可能會遇到汽車某報警燈常亮躺涝,修車師傅拿個儀器插在行車電腦上得出一個代碼,再查表就知道哪個元件壞了——這就是DTC扼雏。除了用于修車坚嗜,DTC還被用于檢測行車電腦和各傳感器的異常狀態(tài)。
可以想象诗充,這個既是運動員又是裁判的Task X一旦死亡苍蔬,軟件層的檢測措施大部分就失效了。
防護措施3蝴蜓。在這里豐田的錯誤開始到達頂峰碟绑。即使設置正確無誤,上面提到的監(jiān)視Task也只不過是另一個Task而已茎匠,與它的監(jiān)視對象算是平級——監(jiān)視Task自己同樣有可能出現(xiàn)故障蜈敢。嵌入式系統(tǒng)的一般做法是在所有程序之上再設置一道屏障,被稱為“看門狗(Watchdog)”汽抚。所謂看門狗抓狭,是一個優(yōu)先級很高的倒計時程序。別的程序需要在運行的時候特意去重置一下這個計時器讓它重新開始倒計時造烁,這個動作被稱為“喂狗”否过。如果因為程序出問題太長時間不喂狗,倒計時完成惭蟋,看門狗知道什么地方卡住了苗桂,馬上采取措施,比如重啟整個系統(tǒng)告组。重啟系統(tǒng)聽起來似乎很嚴重煤伟,實際上卻是一件相當普通的事情。嵌入式系統(tǒng)的重啟非衬痉欤快便锨,時速100公里的汽車中動力系統(tǒng)可以在半米之內(nèi)完成重啟——車上的人根本覺察不到。
通過閱讀代碼和擬真實驗我碟,Barr驚訝地發(fā)現(xiàn)上述嵌入式系統(tǒng)的常識性做法竟然在豐田軟件系統(tǒng)內(nèi)不存在放案!豐田的軟件確實有一只看門狗,但它竟然不是用于監(jiān)視Task異常矫俺,而是用于防止CPU過載吱殉。首先這個做法不能說后無來者至少算是前無古人掸冤。還記得上面提到的800頁13章的報告嗎?目瞪口呆的Barr將豐田看門狗的分析結果寫入了報告的第一章友雳,因為他實在太震驚稿湿。其次,豐田看門狗的防止CPU過載功能也相當蹩腳押赊,在擬真測試發(fā)現(xiàn)即使它正常工作缎罢,還是會允許CPU過載時間長達1.5秒——時速100公里的車能跑40米以上。CPU一旦過載考杉,就會導致所有的Task進入一種“假死”狀態(tài)策精,無法處理信息,這時司機無法控制汽車動力崇棠,十分危險
另外咽袜,豐田的工程師還犯了一個嵌入式課堂上被反復提到的經(jīng)典錯誤:使用硬件時鐘中斷喂狗。硬件中斷擁有非常高的優(yōu)先級枕稀,即使Task卡籽病(比如出現(xiàn)死循環(huán))也不能阻止硬件中斷——可想而知這樣一來看門狗就等于完全白瞎了。
這里也提一句萎坷,同年的普銳斯卻令人意外地搭載了一只運作正常的看門狗凹联,反而讓人摸不著頭腦。
還沒完哆档。這一層防護是嵌入式系統(tǒng)的關鍵陣地蔽挠。前面都是電子系統(tǒng),后面馬上進入機械運作瓜浸,足以造成災難了澳淑。所以僅僅擁有軟件級別的防護還不足夠,豐田的做法是在主CPU之外單獨設置了一塊監(jiān)視芯片插佛,從硬件級別對系統(tǒng)的運作進行監(jiān)視杠巡。監(jiān)視芯片有兩個任務。第一雇寇,它運行一種叫做系統(tǒng)衛(wèi)士(System Guard)的程序氢拥,原理上來說是專門用于防止暴沖。主CPU和監(jiān)視芯片上都會運行系統(tǒng)衛(wèi)士锨侯,可是研究發(fā)現(xiàn)Task X一旦死亡嫩海,這些系統(tǒng)衛(wèi)士統(tǒng)統(tǒng)都不起作用了。第二识腿,它運行一個被稱為“剎車回聲檢查(Brake Echo Check)”的程序出革。這個程序從代碼上來看似乎可以檢測出Task X的死亡,并且采取相應措施:關閉節(jié)氣門渡讼。聽起來像是好消息骂束,但是同樣有問題:首先這個程序不太可靠,即使正常運行成箫,理論上也有失效的可能展箱。最關鍵的是該程序不會自動運行,需要司機先對剎車踏板有“動作”才會觸發(fā)蹬昌。注意這里我特意沒用“踩剎車”這個詞混驰,因為根據(jù)分析“觸發(fā)動作”十分令人困惑。它分兩種情況:如果Task X死亡的那一刻司機的腳不在剎車踏板上皂贩,那么觸發(fā)動作是踩剎車栖榨。還算可以理解。另一種情況明刷,如果Task X死亡那一刻司機的腳踩在剎車踏板上婴栽,那么觸發(fā)動作是完全釋放剎車踏板。沒錯辈末,察覺車子在不正常加速的司機需要停止踩剎車才能讓控制系統(tǒng)關閉節(jié)氣門愚争!這種違背人類認知的行為應該不是豐田工程師特意設計的。如果是挤聘,他們到底在想什么昂渲Α?
到此為止组去,上面提到的都算是“戰(zhàn)術層面”的錯誤鞍陨,都是“小錯”。在講解這塊監(jiān)視芯片的時候从隆,可以發(fā)現(xiàn)豐田犯下最嚴重的“戰(zhàn)略層面”錯誤——基礎設計湾戳。Barr認為,如果基礎設計正確广料,上述那些小錯都完全不會導致汽車暴沖——不管代碼寫得多業(yè)余砾脑,不管內(nèi)存錯誤多嚴重,不管Task死得多頻繁艾杏,統(tǒng)統(tǒng)不會致命韧衣。讓我們回到2002年以前,沒有電子油門的時候购桑。那時候的拉線油門是由油門踏板機械連接的畅铭。當駕駛員的右腳踩下剎車,他的右腳必然不在油門踏板上勃蜘,節(jié)氣門自然而然地被關閉硕噩。這個動作如此自然,甚至算不上安全措施缭贡,僅僅因為每人只有一只右腳辉懒,不可能同時踩油門和剎車。當豐田設計電子油門的時候谍失,只要稍微有點常識眶俩,都應該從設計階段就將這一“自然而然”發(fā)生的動作考慮進去。但是很顯然快鱼,他們沒這么做颠印。監(jiān)視芯片上運行的代碼是用匯編語言(一種更加接近機器執(zhí)行代碼,遠離人類語言抹竹,更加難懂的編程語言)編寫的线罕,運行層次比主CPU的C語言更低。Barr認為如果設計得當窃判,現(xiàn)有的監(jiān)視芯片完全有能力勝任上述功能钞楼,需要的僅僅是幾百行代碼,別的什么都不用更改——不會提高任何生產(chǎn)成本兢孝。很遺憾窿凤,他們沒有做到。
防護措施4】缧罚現(xiàn)在已經(jīng)脫離電子系統(tǒng)雳殊,節(jié)氣門已經(jīng)敞開,發(fā)動機全速運轉窗轩,需要使用機械運作來組織機械運作了夯秃。如何讓向前沖的車子停下來?不開車的人都知道痢艺,剎車仓洼!現(xiàn)代汽車都裝備了剎車助力,助力來自于發(fā)動機運轉的時候產(chǎn)生的負壓堤舒。我們知道發(fā)動機需要吸入空氣色建,吸入體積等于排氣量乘以轉速。節(jié)氣門又是用來阻擋空氣的舌缤,那么節(jié)氣門關閉而發(fā)動機轉速相對高的時候(比如高速丟油門)箕戳,發(fā)動機的實際空氣吸入量比它能吸入的體積要少,那么從節(jié)氣門到氣缸進氣口之間會形成明顯低氣壓(所謂負壓国撵,比大氣壓力辛晡)。剎車助力就是利用了這個負壓推動氣鼓產(chǎn)生更大的推力帶動剎車片抓緊剎車盤介牙。但是如果節(jié)氣門敞開讓空氣隨便進來壮虫,低氣壓就不存在了,這時剎車助力大大減弱环础,剎車效率也大大降低囚似。這就是為什么暴沖事件當事人都說全油門的時候根本剎不住的重要原因剩拢。這個現(xiàn)象稱為“真空損失(Vacuum Loss)”,存在于所有自然吸氣的汽油發(fā)動機汽車(柴油和增壓發(fā)動機沒影響)谆构,不算豐田的錯裸扶。但豐田遲遲不搭載剎車優(yōu)先系統(tǒng)(Brake Override System)允許剎車的同時敞開節(jié)氣門框都,毫無疑問是這個現(xiàn)象的幫兇搬素。
所謂剎車優(yōu)先系統(tǒng),指的是保證同時踩下剎車油門兩個踏板的時候無條件關閉節(jié)氣門的功能魏保。這么做很顯然主要是為了降低發(fā)動機輸出栽惶,同時也保證剎車助力忆蚀。豐田在2010年的凱美瑞上終于搭載了剎車優(yōu)先系統(tǒng),但是別高興得太早。根據(jù)Barr的調(diào)查酒朵,豐田竟然將如此重要的修改“理所當然”地寫入了他們的“廚房洗滌盆”——Task X。我只能“啞然失笑舀寓,扼腕嘆息”驯绎。
好了,到此為止都還是Barr的一面之詞刻蚯,而且大部分都是在那間守衛(wèi)森嚴的房間內(nèi)進行擬真測試得出的“理論結果”绊含。那么實車測試情況如何呢?豐田對Barr的證詞如何反駁呢炊汹?
先說說實車測試躬充。為了證明理論,他們把2008年和2005年的凱美瑞放在馬力機上讨便,固定車身架起前輪模擬車輛運行情況充甚。他們的做法是首先讓車子運行在時速68英里(110公里),啟動巡航霸褒,腳離開油門踏板伴找。然后暫停巡航,速度開始下降废菱。下降到一定程度恢復巡航技矮,速度開始上升。在到達68英里的設定時速以前昙啄,他們用一臺連接行車電腦的筆記本“注入”錯誤穆役。所謂注入錯誤,就是人為地翻轉一個特定字節(jié)——將0改成1梳凛,或者反過來——模擬內(nèi)存損壞耿币。結果完全符合理論,時速超過68英里也不停止加速韧拒,直至時速90英里(145公里)淹接,測試人員踩下剎車十性。大約1秒以后節(jié)氣門被關閉,Barr認為這是上述“剎車回聲檢查”的功勞塑悼。
實車測試證明了Barr的理論劲适,卻并不是全無破綻。豐田辯護律師就兩點提出質疑:
實車測試使用人工注入錯誤的方法厢蒜,并不能證明現(xiàn)實中這種錯誤就一定會發(fā)生霞势。
對此Barr的回答是測試的局限性。因為測試時間斑鸦、樣本有限愕贡,而待測試的樣本空間無窮大。如果要等待那個特定的錯誤自然出現(xiàn)巷屿,可能需要成百萬上億小時的不間斷測試固以,顯然是不現(xiàn)實的。更何況從科學上而言嘱巾,沒有辦法對這個錯誤證偽——就好比無法證明宇宙里沒有外星人憨琳,最多只能證明火星上找不到而已。但是這個測試足以證明一個小小的位翻轉確實可以突破重重障礙最終導致暴沖旬昭,足以證明豐田的軟件存在不能容忍的隱患篙螟。
Bookout女士(本案原告)聲稱,在她駕駛汽車離開高速的時候發(fā)現(xiàn)不受控加速稳懒,她拼命反復踩下剎車并且拉起手剎闲擦,現(xiàn)場留下了剎車痕跡。但并沒有跡象表明發(fā)動機動力中斷——換言之“剎車回聲檢測”沒起作用场梆。暗指Barr的理論站不住墅冷。
對此Barr的回答是首先盡管在實車測試中每次都生效,但代碼分析表明“剎車回聲檢查”這一功能在理論上靠不住或油。其次這一功能的另外一個觸發(fā)動作是要讓腳完完全全離開剎車踏板寞忿。試想車子正在不受控地往前沖,任何人都會不由自主地踩剎車顶岸,讓人完全不踩剎車踏板根本就是違背認知的腔彰。Bookout女士即使如同她所稱反復踩剎車,很可能只是一直將腳放在踏板上往復運動辖佣,從未完全挪開霹抛。Barr還引用一位豐田自己的軟件專家的證詞。該專家承認卷谈,如果發(fā)生暴沖的時刻腳正好接觸到剎車踏板杯拐,并且之后一直沒挪開,那么汽車的暴沖距離“取決于還剩多少汽油”。
最后順帶說一下那份800頁端逼,13章的詳細報告完成后朗兵,Barr將其提交給了豐田的軟件部門,等待他們的反駁顶滩。最終結果是“非常少(Verylittle)”余掖,13章中的11章,包括堆棧溢出的部分礁鲁、代碼混亂的部分盐欺、違反開發(fā)規(guī)范的部分、TaskX過于臃腫甚至兼任節(jié)氣門控制和防護措施的部分救氯、看門狗形同虛設的部分找田、無EDAC的部分歌憨、重要變量缺乏保護的部分着憨、使用了非標準化操作系統(tǒng)的部分,全部沒受到任何形式的反駁务嫡。
【第三部分】后記
寫到這里甲抖,談談人們比較關心的幾點。當然還是外行眼光心铃。
NASA / NHTSA怎么沒發(fā)現(xiàn)這些問題准谚?
NHTSA本身不具備檢驗電子系統(tǒng)的能力,于是委托NASA去扣。NASA檢驗的是整個電控系統(tǒng)柱衔,包括電控傳動部分,范圍比較寬愉棱,只有很少一部分資源被用于檢驗軟件系統(tǒng)唆铐,也沒有投入足夠的人力進行逐行代碼審閱。更何況在很多關鍵問題奔滑,比如之前提到的EDAC的使用艾岂、堆棧的設計,NASA都直接采信豐田的回復朋其,最終被證明不正確王浴。甚至NASA從來都沒拿到過監(jiān)視芯片的源代碼,豐田的說法是“他們沒說要啊”梅猿。NASA報告雖然沒能找到軟件系統(tǒng)導致暴沖的確切原因氓辣,但沒有否定其可能性。與之相比Barr的團隊全部都是嵌入式系統(tǒng)專家袱蚓,投入上千小時钞啸,深入程度甚至超過豐田自身對這個系統(tǒng)的理解(比如豐田沒看過供應商的OS代碼,Barr看了)。
能否100%確定本案就是由軟件錯誤造成的爽撒?
不能入蛆。并沒有直接證據(jù)。訴訟團認為硕勿,軟件錯誤造成該事故的可能性比軟件錯誤沒造成該事故的可能性大(原文:more likely than not)哨毁。
這里再提一句,2005年款的凱美瑞沒有搭載行車數(shù)據(jù)記錄器(俗稱“黑盒子”)源武,后來的車款漸漸開始搭載扼褪。但是Barr發(fā)現(xiàn)這個記錄功能并不可靠,完全有可能記錄錯誤信息粱栖。比如司機踩剎車了可能會被記錄成沒踩话浇。
本案的意義
之前雖然豐田賠了不少錢,但是從未在涉及人身傷害的案件上承責闹究。所以本案意義在于開先例幔崖。美國的法律又特別注重先例,今后豐田的法務部門要頭疼了渣淤。
本案提到的有缺陷軟件涉及了哪些車型赏寇?
全部是美國的車型。Barr的調(diào)查重點是2005年款凱美瑞价认,另外審閱過的包括雷克薩斯ES嗅定、Tacoma、卡羅拉和普銳斯等等用踩,生產(chǎn)年份大致在2002年(電子油門元年)與2010年之間渠退。其中凱美瑞、雷克薩斯ES和Tacoma使用的軟件系統(tǒng)大致接近(原文:Substantiallysimilar)脐彩。另外根據(jù)統(tǒng)計碎乃,汽車暴沖投訴中與2004年款以后的凱美瑞有關的案件數(shù)量激增400%。
最后的最后丁屎,放上本案關鍵證人Michael Barr的獨家訪談:
我:這么看來似乎手動檔汽車更安全荠锭,你怎么認為?
Barr:很多專家都這么認為晨川,離合器至少可以物理斷開動力系統(tǒng)证九。但是我翻閱卷宗,發(fā)現(xiàn)其中有個案例是受害者開手動檔凱美瑞載著家人共虑,突然巡航系統(tǒng)失靈愧怜,無法取消。他踩下離合妈拌,同時試圖躲避前方慢速車輛結果失控沖出路面造成單車事故拥坛。幸運的是沒死人蓬蝶。
我:現(xiàn)在我們都知道豐田的軟件很糟糕〔峦铮可是你對整個汽車行業(yè)的軟件水平有什么看法丸氛?豐田的軟件在同行內(nèi)屬于什么水平?
Barr:我沒有接觸過豐田以外的軟件代碼著摔。但是請注意缓窜,這次發(fā)現(xiàn)的最嚴重問題是豐田在設計源頭上沒有考慮安全,軟件質量反倒沒有那么重要谍咆。只要一個安全為先的設計禾锤,比如剎車和關閉節(jié)氣門的可靠互動、防止節(jié)氣門開啟降低剎車效率的機制等等摹察,不管軟件有多差勁也不會造成致命結果恩掷。只是我真不知道軟件還能怎么差。