撰文:徐鸿鹄 | 排版:王晓峰 |编辑:芃娘娘
公众号:几何四驱 (ID: GeometryAWD)
「这是一段儿你应该知道的故事」
在Part 1文中,我们曾经提到,Mr. Barr在网上公开的近7万字庭审笔录证言,其技术含金量之高,以远超人们想像。他们对此类专业内容通俗而简洁的口语解释,更堪称是计算机专业的教学范本。
「镜像」,「堆栈」,「递归」,「失效安全」,「故障注入」,「回声检查」,「真空损失」,「看门狗」,「系统卫士」……
这每一个抽象技术概念的背后都有一个谜题!
只有汇集超强的技术能力,无畏的雄心,远大的眼光和无穷的好奇心,才能将其一一破解。
如果你有兴趣,我将会在这篇文章里着重回顾这份珍贵材料内的上述技术内容。
现在,就让我们跟随两位“智可敌国”(丰田帝国)专家-Michael Barr和Philip Koopman的脚步,一同继续探寻Unintended Acceleration (车辆非预期加速 缩写UA)的秘密吧!
「全 文 提 纲」
01. 电子节气门控制系统(ETCS)
02. 变量保护(Variables Protection)
03. 软件错误(BUG)
04. 堆栈溢出(Stack Overflow)
05. 递归(Recursion)
06. 汽车电子开放系统及接口(OSEK)
07. 关键数据结构(Critical Structure)
08. BUG影响评估
09. TASK X 及失效安全(Fail-Safe)
10. 整车故障注入(Fault Injection)
11. 刹车回声检查(Brake Echo Check)
12. 防火墙(Firewall)
「Enjoy the “干货”」
内燃发动机的运行,主要遵循三大要素:燃油、空气和点火时机。
空气通过节气门(控制空气进入量),与燃油混合后进入气缸(燃烧发生的地方),被火花塞(控制点火时机)在正确的时间点燃,推动活塞(动力输出)并最终驱动车轮转动。
在早期的汽车设计当中,节气门和驾驶舱加速踏板是机械硬连接在一起的,由驾驶员直接控制。
节气门可以想象成连接加速踏板的“空气”水龙头 - 踩的越多,“龙头”开得越大,就能吸入更多的空气进入发动机,输出更大的动力。
▼ 发动机节气门
2002年,丰田发明了“电子油门”,也被称为“线控油门”。它不需要驾驶员操作,电子和软件系统也可以直接控制发动机空气的吸入量,这个控制单元就叫做“电子节气门控制系统”,英文缩写 ETCS。
2005款凯美瑞(即Bookout女士的汽车)上搭载的就是这个技术。
ETCS使得定速巡航成为了可能,驾驶员有两种方法来控制车辆的速度或使其更快。
一 使用油门!油门踏板踩下的越多,汽车加速越快。
二 使用定速巡航!定速巡航控制电脑和软件将接管驾驶员对油门的控制并保持行车速度不变。
至此,加速踏板不再以机械的形式连接到节气门,而是通过一些传感器,由行车电脑将这些传感器数值计算成节气门开启角度再由电机来开闭节气门。
ETCS一部分会密切关注驾驶控制系统:
油门踏板和定速巡航系统,“查看”加速踏板的高低位置并将其“转换”为节气门角度值。 而另一部分控制发动机,“执行”火花塞点火和节气门开关动作。
Mr. Barr 在丰田的代码里惊讶地发现,关键的变量没有得到有效保护。
什么是变量?
比如节气门角度值是10度,这个10就是一个变量,被放在存储器的一个指定位置。会有一个执行程序,知道去存储器(内存)的哪个指定位置读取这个20的值,然后控制电机打开节气门到20度的位置。嵌入式系统(可以理解为简易计算机系统),或者任何计算机系统,都会在一定条件下发生错误,而且无法避免。汽车是一个一直在震动、发热、运动的系统,它的内部环境相当恶劣,发生硬件错误的可能性非常高。因此变量需要通过技术手段保护起来。
变量都是存放在内存里的,以二进制的方式通过比特(Bit)位,即0和1的组合来表达,即存储器上的高电平和低电平,如果有一个电压介于高低电平之间的值或者个别位受到外部干扰而翻转,就可能造出一个错误的值,那么这个变量的值就被更改了。
怎么才能保护变量呢?
第一个方法就是回声(Echoing)/ 镜像(Mirroring)
回声,简单来说就是在两个不同的地方保存同一个变量,读取的时候两边都读,比较是不是一致。如果不一致,就认为这个变量值已经不可靠了,需要特殊对待。
镜像,顾名思义,让两个值的每一个比特位都0和1互逆,其机制比回声机制要强一些!
假设某个变量有两个拷贝,它们的位置彼此相邻,当这个存储器这个位置附近的所有数据因为外部错误都被重新写为0,那这两个变量就失去了互逆的关系了,因此程序判断这个变量不再可信。
而对数据的更好的保护方式是把两个拷贝的存放位置(更确切地说,是内存地址)分开,每次只要改动(写)其中一个拷贝的值,另外一个拷贝的值就会跟着变。读的时候,两个数据拷贝同时读,如果读出时两者不匹配,就认为这个值不可信。
对于重要的变量,必须要做镜像,这样可以识别数据的可靠程度。这个信息对于像油门角度之类的关键变量来说非常重要。
Mr. Barr推断,NASA可能并不知道丰田是否正确地使用了镜像,至少NASA的报告里没有提到这一点。 实际上,丰田对上千个变量正确地使用了镜像。 但却偏偏漏掉了一些最最关键的,比如:目标节气门角度(Target Throttle Angle),这个变量没有任何镜像,甚至连回声也没有。
如果没有其它拷贝确认变量的可靠程度,使用这个变量时,就说不清这个值是否有效。因为影响变量值的外部干扰因素实在是太多了。换句话说,如果这个变量值突然变化,那么到底是软件出了问题,还是说这就是驾驶员的本来意图呢?
保护变量还有一个重要的手段,错误校验码EDAC(Error Detection And Correction)。
这是一个硬件层面的数据保护措施,能够有效防止由于电磁干扰或环境因素导致的“比特翻转”的发生。
▼ 电压,温度,宇宙辐射都可能导致“比特翻转”
EDAC就是给变量中的每一个字节(比特的集合)后面再增加几个比特的校验码。校验码由原始数据通过某种算法得出,可以探测到原始数据是否被非法更改。这个措施简单有效但却会显著增加硬件成本。
最常见的校验码是身份证号码的最后一位。
▼ 在下面这个例子里,前面有一个比特位出错了,可以通过后面的校验码来纠正前面比特位的错误。系统运行不受影响。
▼ 在这个例子里,如果2个比特位发生错误,校验码虽然无法全部纠正,但可以判断数据不再有效,并对系统进行适当处理(比如系统复位)以降低安全风险。
有意思的是,Mr. Barr 并没有花费太多精力证实丰田的说法,确认是否使用了EDAC。他看过一份丰田提供的电子表格,上面清晰地列出了所有没有应用EDAC的丰田车型,其中就包括2005款的凯美瑞。
丰田此前误导了NASA,并让NASA相信丰田在凯美瑞当中采用了EDAC。2008年款的凯美瑞才修复了这个问题,设计了3个比特长度的EDAC。但Barr认为,这远远不够,5个比特长度的EDAC才是合理的设计。
软件中存在错误(BUG), BUG不同于之前提到的硬件错误。
它有个特点,平时是潜伏的,却会被外界条件激活,条件具备了,错误就会发生。
但是BUG并不意味着它们总是会导致故障,因为任何合理大小的程序都会有缺陷。有一些BUG只有特定的触发条件才能表现出来。
汽车软件内部包含了成千上万的变量,每个变量还可以有不同的值代表不同的软件状态。两者相乘,就可以组合出一个非常大的数字:数十亿或数万亿,甚至是无限的值。
陷入其中任意的一个状态,BUG就可能会发生。这不一定和驾驶员的操作有关系,它有可能只是软件本身进入了那个状态。也许汽车每小时走5.5公里,而不是每小时走5公里,就会触发某个BUG造成失效。
软件大概可以分为操作系统和TASK两部分。
其中,TASK可以类比成手机IPHONE里的一个APP应用程序,对于汽车上的嵌入式系统,我们叫它TASK更准确一些。BUG会导致的一个常见后果就是系统内存崩溃(Memory Corruption)。如果出现了内存崩溃,就会导致某个TASK突然不执行了,这个TASK就“死亡”了。
TASK死了,自然不能执行它所肩负的任务,就会产生不可控的后果。
堆栈溢出(Stack Overflow)是非常危险的一类BUG,对UA来说非常重要,值得我们多花一些精力好好研究一番。
首先,什么是栈(Stack)?
TASK有很多,CPU却只有一块,在任何时刻只能处理一个TASK,怎么办呢?这需要操作系统的统筹规划,合理分配CPU的任务,让每个TASK都能够执行。
当A任务运行的时候,CPU突然接到指令,要求运行优先度更高的B任务,于是CPU把A算了一半的结果先放在一旁,等先把B任务完成,CPU再回去找到A算了一半的结果完成A的计算。通过引入栈这种非常重要的数据结构,我们就可以不断取放暂时没有完成的工作,从而构建出了操作系统的多任务模式。
此外,栈也可以是一种与算法有关的数据结构,某些TASK的算法需要使用这种数据结构来工作。
栈不能无限的大,程序员必须为其设定一个大小。在计算机系统里,一个栈就代表一个地址连续的内存块。在2005款凯美瑞上,丰田设计了两个栈,一个是专门给TASK X(我们稍后再谈)使用的,它的大小为1KB。
另一个栈给TASK X以外的TASK使用,大小为4KB,在这个“通用TASK”栈里,还有一些别的东西,叫中断服务(ISR)。ISR这个东西是跟操作系统有关的,我们也可以把它理解成一类特殊的TASK。
汽车运行的每一个时刻都有TASK在运行,我们会遇到一种极端的情况:所有的TASK同时在使用栈,那么数据就会突然间爆炸式增长。左边的“通用TASK”的栈里,最多只能存储4096个字节的数据,当这种最坏的情况发生时,数据就会覆盖整个栈甚至溢出到栈的外面。这就是“堆栈溢出”。
NASA对堆栈溢出自然不陌生,也非常感兴趣,丰田提交了他们的计算结果:最坏情况下数据只会使用堆栈41%的空间,即1688个字节。而Barr的计算反驳了这一说法,Barr认为至少会占用94%的空间。
因为丰田的计算方式只考虑了所有TASK同时使用栈的最坏情况,却没有考虑到CPU在切换TASK时也是在进行栈的操作,丰田还遗漏了操作系统的中断服务程序ISR。
此外,如果代码里存在递归算法,甚至100%的栈空间都不够用,而且理论上没有上限!
递归是一种计算方法。一般的计算方法要么是自己算,要么是监督别人算。递归则是用一层层问自己的方法完成计算。好处是代码简单,坏处是计算层数不固定。可能10层就可以出结果了,也可能会是100000层。每递归一次都需要占用栈空间存储或传递一些数据。
一般说来,在这么重要的汽车控制模块里是严禁使用递归程序的。而丰田却违反了这一原则,加入了递归算法。
NASA的报告里也提到了丰田对递归函数的使用,并花了5页的篇幅详细谈论这个问题。NASA着重强调了递归会导致堆栈溢出,并提出了一种实时堆栈监测的方法:软件开发者在高地址位的内存堆栈里设置一个“水位标记”,一旦检测到“水位”超过了“标记”,就重置系统以防止堆栈溢出。
Mr. Barr发现,2005款凯美瑞有没有搭载堆栈实时监测功能,零件的供应商是电装。而同年的卡罗拉车型却搭载了这个功能,因为零件的供应商是通用。可见丰田没有对供应商的代码进行过详细的审查。
下面是另一个重要的发现:在“通用TASK”的堆栈地址之外(即4K字节以外),紧挨着存放的是主CPU的操作系统的重要数据。这种做法是不符合汽车行业通行标准要求的,即OSEK。
在凯美瑞ETCS的设计里。堆栈溢出正好会影响到这块“特殊而重要”的操作系统空间。这个空间用来存放所有TASK的运行状态。即“关键数据结构”(这个概念我们稍后再谈)。而NASA此前对此一点也不知情。
丰田使用的操作系统(代号为RX OSEK 850)来自于主CPU(代号V850)的供应商:日本电器(NEC)。
OSEK是汽车行业的通用操作系统标准,不同的供应商都可以开发符合OSEK认证的操作系统,以确保达到统一的质量和安全水平。同时这也确保了,汽车制造商的代码可以在任何一个OSEK芯片上运行,有一系列的合规测试可以确保它的通用性。
早在2002年,丰田选用的主CPU就已经有OSEK认证的操作系统存在了,但凯美瑞所使用的操作系统却没有经过OSEK的认证, 丰田并没有关注美瑞选用的操作系统的内部设计,令人匪夷所思。
OSEK操作系统里有一个叫做关键数据结构的东西(Critical Data Structure),操作系统的工作就是不断地与它保持联系,就像出租车总调度员呼叫出租车一样。通过这种设计,操作系统得以建立TASK分配机制。
在关键数据结构里,保留了TASK的ID(身份识别)信息,以及TASK运行历史的记录,类似于“从未运行过”,“有一会没运行了”,“需要马上运行”, 操作系统按照重要程度原则,利用这些信息来给它们的运行先后排序,TASK于是遵照这个排序依次调用CPU资源。
凯美瑞ETCS软件操作系统里的关键数据结构掌握着TASK分配的大权,却没有任何保护措施:不仅没有硬件保护,就连软件错误(BUG)保护也没有体现。这意味着不仅仅是TASK,就连操作系统也存在崩溃的可能!
当关键数据结构里的一个比特位因为硬件故障发生了翻转(Bit-Flip)— 也被称为单事件翻转(Single Event Upset),由于操作系统没有任何硬件防护措施,关键数据结构里的位翻转会带来一个严重后果:它会杀死这个比特位所指向的TASK。翻转发生在不同的位置,就会杀死不同的TASK。被杀死的TASK只有在汽车重新启动的时候才会重新运行。
软件错误(BUG)也会破坏关键数据结构。BUG可以存在于操作系统内部,也可以存在于操作系统外部,BUG可以感染整个内存,或者一个内存局部区域,或者只有一个比特位,一旦关键地址的数据被感染,就有可能暂时或永久杀掉至少一个甚至多个TASK。
按照Mr. Barr的估算,TASK被杀掉的可能组合数量 >1600万种。而且每一个TASK都可能在上千个软件状态下被杀死。EDAC能够修复硬件错误带来的影响,因为它的影响程度有限(单个比特位翻转),但对于大破坏力的BUG来说,EDAC就无能为力了。
在Barr提交的800页报告里,专门有一章讨论了丰田代码里的BUG,Mr. Barr 同时也统计了丰田ETCS软件里的BUG类型。
▼ 丰田ETCS软件里的BUG类型见下表,它们都可能导致系统内存崩溃
凯美瑞的软件里到底有多少个BUG呢?这个问题很难回答,但是我们可以利用一些工具来估算BUG带来的影响。
第一个工具: 函数复杂度。它可以自动根据结果的可能分支数量来评估函数的复杂程度。
丰田的ETCS软件中至少有67个函数的复杂度超过了50,这意味着运行这个函数可能出现超过50种不同的执行结果,属于“不可测”(Untestable)级别。
为了测试这50个不同的结果,就必须准备至少50条不同的测试用例以及相应的测试文档,这要花费巨大的资源。作为比较,Barr表示他自己的公司严格执行的其中一条规定就是任何函数的复杂度不能超过30。
一般来说,对于“不可测”(Untestable)级别的的代码需要进行覆盖率分析(Code Coverage Analysis)。它是软件工程上的一种度量,描述代码被测试的比例和程度。对于航空器和汽车关键零部件都要求有100%的覆盖率,但没有证据表明丰田做了这个分析。
这67个函数中,还有12个的复杂度超过了100,达到了“不可维护”(Unmaintainable)的级别,这就意味着一旦发现BUG也无法人为修复,因为函数实在太过复杂,修复BUG的过程中会产生更多新的BUG。
其中最复杂的一个函数居然有超过1300行的代码,以及146个可能的执行路径!恰巧这个函数正是用来根据传感器数值计算节气门开关角度的!
第二个工具:全局变量。 全局变量是代码找BUG最大的敌人!
在花费大量时间阅读丰田的源代码之后,Mr. Barr用一个字高度概括了丰田的代码风格:Spaghetti (意指冗长,控制结构复杂,混乱而难以理解的代码块)!
全局变量是一种特殊的变量,它不属于哪个子函数或代码块,可以被任意调用。在丰田Spaghetti的代码风格里,上千个彼此独立的代码块可以通过全局变量互相调用,缠绕在一起,没有任何结构性可言。
全局变量被认为是有害的,因为任何函数都可以访问全局变量,一旦全局变量被错误地修改,其它与之有关的函数全部都会受到影响,从而带来莫名其妙的各种BUG。
丰田ETCS系统里一共有多少个全局变量呢? 11253 个!而且全部可读写!那合理的数量应该是多少呢?所有的专家都是一致的回答:0!此外,NASA此前的分析发现,丰田ETCS软件里82%的数据对象都可以被任意的函数使用!
丰田的代码里,全局变量命名也很混乱:有的全局变量名是长度25,30的字符串,其中的一些没有元音字母,还有一些全局变量的名字几乎是一样的,只差一个字母。
Mr. Barr还发现了两个非常关键的变量也被当作全局变量来使用:一个用来控制节气门的角度,另一个用来监控发动机的转速。这两个信号被很多代码块调用,冥冥之中增加了内存崩溃的风险。
“最开始,丰田没有电子节气门控制软件,也没有操作系统,甚至都不用C语言写代码,随后他们决定开始用C语言代替汇编语言编程,接着又全新开发了操作系统,最后才加入了电子节气门控制代码。“ Barr以此来解释丰田代码混乱的缘由。
既然丰田的代码写的不好,就没有行业规范来指导一下么?
答案是肯定的,跟很多行业一样,汽车行业也有自己的代码规范,叫做MISRA C,是汽车安全关键系统中使用C语言的准则,列出了100多条要求。
该规范的2004年版的感谢列表里还能看到丰田员工的名字,至少外界认为丰田确实也在遵循这些规范。
NASA审查期间,丰田辩称他们遵循的不是行业规范,而是丰田内部的编程规范,这一规范与行业规范的吻合程度达到50%。但按照Barr的估计,两个规范的吻合度大概只有10%,实际上,两个标准里只有11条规范是一致的,甚至有5条规范相互矛盾。Barr还发现,凯美瑞源代码一共违反了32%的丰田内部编程规范!再次被打脸!
基于MISRA C规范也有一个评估BUG数量的工具:Kawana’s Bug Chart。
这是一条经验公式: 每30处违规代码就可能包含一个重大BUG和十个轻微BUG。
因此只要数出违背代码规范的违规代码数量,就可以估算出BUG的数量。 讽刺的是,这个方法是2002年,丰田汽车自己的员工提出来的。
NASA的调查报告里,只使用了MISRA C当中的35条规范对丰田的代码进行校对,就发现丰田违反了14条规范,以及7134处违规代码。Barr则使用了MISRA C全部的规范条目校对,对照的结果是有超过80000处违规代码!
Mr. Barr最后还指出,丰田内部更缺少工程纪律的有效执行,而这本身就是BUG企业文化:
没有任何有效的书面记录证明丰田按照行业要求进行了同伴校阅(Peer Review),这被看作是众所周知的成本低,见效快的方法;丰田内部也没有对BUG进行跟踪的机制,实验过程中发现的BUG也没时记录并有效处理。
此前NASA并没有在丰田的源代码里发现“由于软件BUG而导致UA”的直接证据。但Mr. Barr所在的技术团队却敏锐地发现了一个关键线索,而且它与UA有着千丝万缕的联系。
这个关键线索就是“TASK X” —丰田非常避讳在公开场合说出这个TASK真正的名字,法庭上为了避免频繁地清场,法官决定把它叫做TASK X。
TASK X负责非常非常多的事情,比如:计算节气门开启角度、车速监测和保持、定速巡航监测等等。Barr根据TASK X的特点形容它为“厨房洗涤盆”(Kitchen-Sink)。
一旦这个TASK X被杀死,就会导致节气门失去控制,此外还会影响到失效安全(Fail-Safe)。
在一般的工程实践里,“失效安全”(Fail-Safe)是一种常用的手段。指一种为减少不安全后果的预防性设计实践,当发生特定类型的故障时,内在地以对系统,环境或人员以损害最小的方式做出反应。
保险丝就是一种最常见的的Fail-Safe。
▼ 松开割草机的手柄就会停机,这也是Fail-Safe设计
嵌入式系统中内存出错或者程序死亡是一种正常现象,通过失效安全机制(比如强制复位)就可以有效化解安全危险。丰田的工程师对此非常了解,因此也设计了一系列的失效安全机制。
NASA的报告里提到了丰田ETCS代码里的三种失效安全模式: 跛行回家,怠速断油,关闭引擎。NASA发现,丰田并没有遵循必要的设计方法,而是将失效安全“理所当然”写在了“厨房洗地盆”里面!
也就是说,当TASK X被杀死的时候,这些“失效安全”(Fail-Safe)也都不起作用了,从而将汽车和驾驶员暴露在危险的情景当中。
不久之后,Mr. Barr的团队就通过计算机模拟证实了软件BUG引发的“关键数据结构”的异常可以杀死TASK X并让ETCS系统崩溃。但这还不够,Barr认为还需要更多的实车测试。
因为在计算机上只能看到软件BUG的局部推演,无法真实地模拟电路板硬件以及整车的真实环境,并最终了解汽车的失控行为。只有在整车环境下,才能够弄清楚当TASK X“死亡”的时候,失效安全(Fail-Safe)功能是如何影响车辆行驶的。
利用丰田提供的测试工具(Tech Stream),Mr. Barr的团队能够在整车测试过程中实时采集汽车状态信号,如芯片内存状态,刹车踏板角度,节气门开度,车速等。
而更重要的测试,是要通过这个设备,人为地向汽车注入特定的软件故障(Fault Injection),比如模拟“关键数据结构”里的“比特位翻转”,来精确控制杀死某个特定的TASK,然后观察汽车在既定“失效安全”策略下的行为,以证实软件BUG与UA的因果关系。
与Mr. Barr所预想的一样,软件BUG确实触发了UA,但他还发现了更不可思议的事情。
▼ 蓝色曲线代表车速,红色曲线代表节气门的角度,绿线是刹车,二进制状态分别代表开和关。横纵轴分别代表时间和车速标度。通过注入特定的故障,意外加速UA在整车测试阶段成功复现。
上图为一次测试当中采集到的汽车内部信号数据 :
-
前80秒里,汽车依靠油门踏板加速,并开启巡航控制功能,目标巡航时速位68英里(灰色水平线)。
-
随后,驾驶员取消了巡航控制,车速缓慢降低(80s-90s区间)。
-
在蓝线的最低点处(约90s),驾驶员再次开启巡航控制(Reset),让车辆恢复68mph的目标巡航速度。
-
98s处的位置有一条灰色垂直线,就在这个时间点,向汽车主动注入一个“比特位翻转”错误,这个操作导致了操作系统里TASK X被“杀死”,此后汽车开始自主加速。
-
105s时,汽车达到目标巡航速度68mph,但汽车还在继续加速!
这时TASK X已经“死亡”,却没有“失效保护”机制干预汽车关闭节气门并减速—或者保持速度不变。
再看节气门的状态(红线)- 我们发现,节气门在TASK X“死亡以后”,完全打开的状态至少保持了30s(98s-129s)之久。
129s到130s的这个空档里,驾驶员完全踩下刹车踏板,触发了一个不受TASK X控制的“失效安全”功能:刹车回声检查程序(我们稍后再来解释),车辆终于开始减速。
UA被完美复现!
在这个测试情景当中,除非驾驶员主动干预汽车并执行刹车动作(可能这时的速度已经远远超过了设置的巡航速度),否则汽车不会自动修复“自主加速”的问题。似乎可以得出结论:UA问题和巡航控制有关。
但Mr. Barr却发现事实没有这么简单,因为从第98s开始,即TASK X被“杀死以后”,油门踏板也已经不再响应驾驶员的动作了!—也就是说,TASK X的“死亡”,也“杀死”了自动巡航和油门踏板的功能!
还有更奇怪的事情,经过更多的测试,Mr. Barr还发现了更加可怕的现象:”当TASK X被“杀死”的瞬间,如果刹车踏板受力(任何角度),就连刹车踏板也会失效!”
换句话说,如果在TASK X被“杀死”的瞬间,某个“倒霉的”驾驶员正好把脚放在刹车踏板上,这辆车就完全失去了控制,油门和刹车双双失效!
这时,丰田坐不住了:我们还有一个“失效安全“功能!在TASK X被杀死之后,它还是会继续工作的,可以保证汽车安全减速 - 这就是那个叫做“刹车回声检查”的程序。
一旦检测出TASK X的死亡,这个程序就“准备好”开始工作了。“准备好”开始工作,却不是“正式”开始工作,是因为这个程序的逻辑不光很“怪”,还有点“绕”。
具体逻辑是这样的:
1. 如果TASK X“死亡”瞬间,驾驶员没采刹车踏板,刹车回声检查程序就听从驾驶员的命令,通过刹车踏板减速。(这就是上一个实验里发生的情况)
2. 如果TASK X“死亡”瞬间,驾驶员已经踩了刹车踏板,刹车回声检查程序需要一个额外的触发条件,才能听从驾驶员的命令,通过刹车踏板减速。
在第二个逻辑里,这个触发条件居然是 — “完全松开刹车踏板”!而且还得 “松开一段时间”!
想象一下,当你正常行驶并习惯性减速,右脚还放在刹车踏板上,出于不知道的原因,突然间TASK X中断,车辆开始自主加速,这时你发现踩刹车没有用,踩油门也没有用,而丰田的设计师却告诉你:我设计了一个好办法可以让汽车恢复控制,你来猜猜啊!?
天啊!这种反人类的逻辑是究竟怎么设计出来的?
丰田说,这个刹车回声检查程序非常关键,它的作用是强制关闭“僵死”了的节气门。如果不关闭节气门,就没有刹车力,节气门一直在打开状态不动,刹车系统就不起作用。
可是节气门和刹车系统有什么关系呢?我们知道节气门是用来阻挡空气的,如果节气门关闭,发动机的实际空气吸入量就会减少,减少的瞬间节气门和发动机燃烧室之间就会形成一个低气压(负压力)。刹车助力就是利用了这个负压推动气鼓放大推力带动刹车片抓紧刹车盘。但是如果节气门一直敞开让空气随便进来,低气压就不存在了,所以刹车就没用了,这个现象称为真空损失(Vacuum Loss)。
在上面第二个逻辑里,只有完全松开刹车踏板才会激活刹车系统的回声检查程序,通过程序强制关闭节气门,让刹车踏板和刹车助力重新恢复工作,控制车辆减速!无论如何,这不是一个适当设计的“故障安全”,因为首先,它要让司机先行动,而且是要靠“猜”的方式。
在庭审笔录当中,Mr. Barr还引用了一位丰田自己的软件专家的说法。该专家承认,如果发生UA的时刻驾驶员正好踩了刹车踏板,并且之后一直没挪开,那么汽车在UA状态下持续行驶距离 “取决于还剩多少汽油”!
至此,我们得以串联起整个因果链,我们发现,丰田的设计当中本应该存在的四道“防火墙”全部都被神奇地“化解”了。
第一道防火墙:关键变量保护
TASK X首先读取加速踏板的位置,并将其作为输入发给那个拥有146个路径分支的超级复杂用来计算节气门打开角度的函数,假定计算的结果是20%节气门开度。ETCS随后将这个没有“镜像”保护的变量值存放在内存里。随后负责电机控制的TASK(Motor Control)读走了这个值,并通过电机带动节气门翻板将其打开到合适的角度。
当触发BUG,TASK X“死亡”时,TASK X就不再读取油门踏板的位置了 。同时车辆也不再响应油门踏板或者巡航控制系统的指令,因为这些工作也是TASK X负责的。节气门由于得不到任何新的角度指令,于是节气门就“僵死”在打开的状态。
考虑一下更坏的情况:没有镜像保护的角度信号遭遇了“比特翻转”,它可以变成0%-100%之间的任意值。如果出现了软件BUG,不光节气门角度信号遭到破坏,更多内存地址上的未保护数据也无法幸免——它的破坏效果就像散弹枪射出的弹片一样,大范围随机杀伤。
在任何一种情况下,节气门都会是开启的(即便节气门打开角度为0%,凯美瑞的节气门也会打开6度左右,以防止发动机停转),车辆都会持续自主加速。
第二道防火墙:失效安全模式和DTC
我们前面谈到了刹车回声检查这个“失效安全”模式,无论如何,这都不是一个适当的安全设计。
一个好的“失效安全”功能应该将控制部分和监控部分分开,主CPU负责节气门的角度输出控制。加速踏板位置和节气门位置通过各自传感器同时发给主(控制)副(监控)CPU用于互相校验,一旦出现不一致的计算结果,监控CPU就立即通过“失效安全”功能来重启主CPU。
重新启动时间很快,以60英里的时速(约97公里/小时)运行的汽车,重启的过程只相当于开过11英尺(约3.35米)的距离。
此外,还要设计一套刹车优先系统(Brake Override System),在驾驶员同时踩下刹车和油门两个踏板的时候,无条件地关闭节气门,优先保证刹车助力,保证安全制动。
2010年,凯美瑞开始配备了这套刹车优先系统。——但是别高兴得太早,根据Barr的调查,丰田竟然将如此重要的修改又“理所当然”地写入了他们的“厨房洗涤盆” —TASK X!
汽车系统还有一个重要的功能:生成诊断错误代码(Diagnostic Trouble Code)。
DTC可以想象成存放在系统内部某个位置能够告诉我们哪里出过错误的系统历史记录,它就像是一个记忆碎片,只要系统不断电,我们就可以反复查看故障瞬间系统到底出了哪些问题。这是一个强大的问题反馈机制,能让我们快速锁定问题来源。
然而丰田实在是太爱这个TASK X了,又“自然而然”地把绝大部份的DTC功能都写在了TASK X里面!因此我们无法得知TASK X究竟是出于什么原因被谁杀死的,因为记录证据的那个DTC程序跟着TASK X一块都被杀死了!
第三道防火墙:看门狗(Watchdog)
硬件层上有一种有效的设计,叫做看门狗(Watchdog),它是一个具有很高优先级的倒计时程序。
如果一个TASK崩溃,最简单有效的手段就是复位系统重新让这个TASK工作。我们生活中常见到手机或电脑崩溃,唯一唤醒的办法就是重新启动它:找到复位按钮并按下。这时,人就充当了看门狗的角色。
关键TASK必须在规定时间间隔内向看门狗发送一个信号,证明“我”还“活着”,这个行为简称为“喂狗”(Feed Dog),一旦等待间隔超过了看门狗的倒计时时间,看门狗就会引发系统重启。
本质上说,Watchdog也是一种“失效安全”的机制。看门狗最擅长的工作,就是监控每一个重要TASK的运行状态。尤其是TASK X被“杀死”后,看门狗可以在第一时间复位它让它“原地复活”。
▼ 下表中列出的众多TASK一旦“死亡”,凯美瑞的看门狗都无法有效探测到!
Mr. Barr对凯美瑞看门狗的评价是:“糟透了”(Abysmal)!因为他惊讶地发现丰田的做法简直颠覆了自己的世界观!
丰田的确有看门狗,但完全不是用来监视TASK异常的,而是用来防止CPU过载的!这个做法不能说是后无来者但绝对可以说是前无古人!Barr直接将丰田看门狗的分析结论写入了800页独立报告的第一章,因为他实在太过震惊!
丰田看门狗防止CPU过载的效果也差强人意:过载时间长达1.5秒——相当于时速100公里的车跑过一个足球场的距离。如果一个CPU过载时间太长,就会导致一部分TASK“假死”,而这对汽车来说是非常危险的。
丰田的工程师还犯了一个嵌入式课堂上被反复提到的经典错误:在硬件中断中喂狗。这会导致TASK卡住(比如出环程序跑飞了出现了死循环)并阻止系统复位 ——可想而知这样一来看门狗就形同虚设了。
令人啼笑皆非的是,丰田在2005年发布的普锐斯车型上居然用对了看门狗:普锐斯是混合动力车,有两个引擎:燃烧引擎和电池引擎,燃烧引擎的看门狗设计跟凯美瑞的差不多一样糟糕,但电池引擎却搭载了一只完美运作的看门狗。
第四道防火墙:监控CPU
丰田在发动机控制模块上,主CPU之外单独设置了一块监控CPU,从硬件级别对整个系统的运作进行监控。每个处理器都有自己的软件。主CPU上的软件负责执行油门控制,燃烧,监控加速踏板和巡航控制等任务。
监控CPU有两个工作,都跟失效安全有关。其一是系统卫士(System Guard),原理上来说,是专门用于防止UA的。 可是研究发现Task X一旦死亡,系统卫士就统统都不起作用了。第二个工作就是之前提到的刹车回声检查,但程序的逻辑存在致命问题。
Mr. Barr通过阅读源代码和整车测试结果判断,在监控CPU的软件里,丰田没有设计任何一个专门程序用来监控TASK的意外“死亡”或者UA,也就是说,监控CPU的软件没有设计必要的保护机制来防止UA的发生。
丰田曾经公开声明说不太可能是软件问题导致的UA,他们研究了地垫,查看了踏板,评估了用户误操作,可是他们却从来没有考虑认真地看看监控CPU软件的源代码和安全设计是否可以阻止UA的发生。
Mr. Barr在很晚的时候才从丰田手里拿到监控CPU的源代码——调查截止日期的三周前。Barr发现,监控CPU上运行的代码是用汇编语言编写的。汇编语言,是一种更加接近机器执行代码,远离人类语言,比C语言更加难以理解的编程语言。
由于丰田并没有及时提供编译汇编语言的编译器和完整的文档说明给Mr. Barr分析,Mr. Barr差点就错失了这个重要的发现:UA与监控CPU“不作为”相关。
Mr. Barr认为,如果监控CPU的软件设计正确,即便前三道防火墙全部失效,也不会发生UA。不管代码写得多业余,不管内存错误多严重,不管Task死得多频繁,统统不会致命,至少刹车系统还可以工作。
过去的丰田车采用的是拉线油门,是机械连接,因此当驾驶员的脚离开油门时节气门会自然关闭。丰田开发电子油门系统的时候,却没有沿用机械油门系统的自然逻辑。
这个自然逻辑就是:松开加速踏板的时候,节气门也要关闭。这个逻辑如此的自然,以至于都算不上是一个“失效安全“(Fail-Safe)措施。丰田没有将这个自然逻辑正确地移植到ETCS系统当中 — 这才是引发UA的元凶。
「后 记」
虽然丰田汽车在开发阶段经历了3500万公里的整车路试,此外还有1100万小时的软件模块测试,但是车辆安全不是“蛮力”测试出来的,有太多运行条件的组合,太多的时序组合,在测试过程中不可能穷尽每一种组合并寻找故障,在工程实践里“可验证的绝对安全“是个伪命题。
大量的测试数据并不能证明安全本身,安全的系统是要打通底层的硬件设计,软件设计,从模型和架构上进行通盘考虑的,让设计和试验验证两个支柱形成闭环。
线控(油门,制动,转向)革命只是汽车智能化演进的一个缩影,在互联网造车企业放言“颠覆传统汽车”的“智能革命”时代,我们不妨保持冷静,回过头翻翻历史,看看汽车行业在“电子革命”时代所经历的阵痛。
失败的代价历历在目,历史不容忘却。
造车新军小鹏汽车CEO何小鹏也直陈:“ 汽车是人命关天的事,而且成本极高,互联网跑马圈地后技术迭代持续优化用户体验的做法在汽车业行不通,汽车不能像手机APP那样“先上线,不满意就卸载”,而是第一步就要达到很高的安全水平。 ”
展望未来,还有很多新技术新挑战。比如在无人车领域,如何利用机器学习技术(如深度学习的感知,强化学习的决策)搭建安全的系统,如何设计兼容新式传感器(光达,雷达,摄像头)的安全架构,最大的挑战在于细节,实现技术最后的10%,1%,0.001%……
在技术探索的道路上,历史不断警醒我们,不必太醉心于业界的前沿话题。与其太急,不如多花花精力把基础弄得扎实一些,没有任何一样东西仅仅因为名字就伟大,没有任何一门知识因为分类就崇高,解决实际问题的实用技术才是基本功和核心竞争力。如果安全的问题不处理好,再雄伟的上层建筑都只是水中楼台,空中楼阁。
延伸阅读 / 引文出处
[1] Analysis of Toyota ETCS-i System Hardware and Software
http://pressroom.toyota.com/article_download.cfm?article_id=3597
[2] Technical Assessment of Toyota Electronic Throttle Control (ETC) Systems
https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=4&ved=0ahUKEwjZ2u3qiqXbAhVGv7wKHcmLCycQFgg9MAM&url=http%3A%2F%2Fwww.safetyresearch.net%2FLibrary%2FNHTSA-NASA_Response_Final_052311.pdf&usg=AOvVaw08wWyDMoitiuRMOQVP9d2T
[3] Transcript of Morning Trial Proceedings From The 14th. of October,2013 https://docs.google.com/file/d/0B_sOZeAAQg0rMjhaZHlwLXM4SUE/preview
[4] Bookout V. Toyota 2005 Camry L4 Software Analysis
http://www.safetyresearch.net/Library/BarrSlides_FINAL_SCRUBBED.pdf
[5] Case Study of Toyota Unintended Acceleration and Software Safety
https://www.slideshare.net/PhilipKoopman/toyota-unintended-acceleration?ref=https://habrahabr.ru/company/pvs-studio/blog/310862/
[6] Technical Support to the National Highway Traffic Safety Administration(NHTSA) on the Reported Toyota Motor Corporation(TMC) Unintended Acceleration(UA)
http://www.nhtsa.gov/staticfiles/nvs/pdf/NASA-UA_report.pdf
[7] Toyota:81514 issues in the code
https://www.viva64.com/en/b/0439/
[8] Congressional letter to Toyota CEO
http://www.beasleyallen.com/webfiles/congressional-letter-to-toyota-coo-2010-02-02.pdf
特邀撰稿:徐鸿鹄
作者微信:honghu967935
现就职于 蒂森克虏伯 普利斯坦 (Thyssenkrupp Presta) ,负责电动转向系统 (EPS) 应用项目的开发和管理。
航天科班,主业汽车电动转向开发,纯正懒散东北人,王小波脑残粉,个人主义走狗 ,AI威胁论煽动者,民科数学家,半吊子程序狗,沉迷理论,日渐消瘦,无一精通。
更多文章推荐
关注「几何四驱」
各种高能文章,随后速速就来
本篇文章内容为作者本人原创,展示图片均来自网络,版权归图片作者本人所有!文章未经「几何四驱」或「作者本人」授权不得进行商业性转载。个人性转载请务必注明文章出处。原创不易,感谢理解!
不跟风,拒绝平庸,只做最好的自己。
「几何四驱」 All made in Germany ,Since 2016!