点评TI-Hercules安全MCU——“丰田错在哪”之我思
原本对市面上的所谓安全MCU没有太多概念,没觉得跟普通的MCU有什么不同,在读完《TI Hercules开发实战手册》一书后,才对安全关键系统有了一个比较初步的概念。明白了在安全关键性系统(Safety Critical System)对控制系统软硬件的安全要求。让我不禁联想到前不久的丰田轿车暴冲的判决。其中判决的根据是嵌入式工程师对丰田汽车控制系统软件代码的分析,由于缺乏安全设计与防护措施,导致 汽车无法刹车造成暴冲。不妨结合丰田车暴冲事故和TI-Hercules进行对比,分析一下如果是我们旁观者,采用TI-Hercules的MCU,如何避免丰田的错误。
丰田汽车暴冲判决
2013年10月底,俄克拉荷马的一次庭审,2007年一辆凯美瑞暴冲(Unintended Acceleration,UA)致一死一伤事件中丰田被判有责。庭审中主要证人Michael Barr的证词让陪审团同意丰田的动力系统软件存在巨大漏洞可能导致此类事件。
具体报道请见链接:
1、[转载]丰田栽了的原因,嵌入式软件工程师都该看看
2、Toyota's killer firmware: Bad design and its consequences
(1)车辆控制系统的缺陷。
摘录关键字,丰田的软件缺陷有三类:
A. 非常业余的结构设计,包括:多个函数复杂度过高、节气门开关角度函数有超过1300行代码和146个可能执行路径、包含了超过一万一千个全局变量等问题。
B. 不符合软件开发规范,拥有超过80000处MISRA-C违规代码、使用了递归。
C. 对关键变量缺乏保护,完全没有使用RAM校验码、选用的操作系统却没有通过认证,Task堆栈跟操作系统的任务分配表紧邻,没有搭载堆栈实时监测功能,堆栈溢出可能损坏任务分配表。
(2) 软件安全防护措施缺失
(防护措施0)→堆栈溢出→(防护措施1)→(可能导致)→任务分配表被改写→(防护措施2)→(可能导致)→Task X死亡→(防护措施3)→(可能导致)→节气门敞开→(防护措施4)→(导致)→汽车暴冲
总结一句:文章提到设计缺陷(无防护措施0)导致堆栈溢出,又因为Task堆栈跟操作系统的任务分配表紧邻(缺乏防护措施1),task能够修改底层OS的任务分配表(缺乏防护措施2),导致关键Task-X无法执行,而没有任何监测(缺乏防护措施3),导致节气门持续敞开却没有刹车优先功能(缺乏防护措施4),最终导致汽车暴冲。
以下是摘自《丰田栽了的原因》分析一文,对其中可使用Hercule改进的部分进行了标红。后续对各防护措施0--4进行了一一分析。
首先防护措施0。这个其实上面提到了,因为设计缺陷低估了最大占用的存储容量,并且不符合规范地使用了递归,最终可能导致堆栈溢出。
然后到防护措施1。上面也提到了,任务分配表紧邻堆栈。作为外行我都觉得这是个十分危险的设计——既然堆栈这么容易溢出,好歹应该将任务分配表放远一点啊。当然我是外行,可能实际上比想象中复杂很多。这段Barr的证词中并未特别提到,属于我的个人理解。
防护措施2。从这里开始丰田的错误越发严重。任务表被改写导致某些Task运行异常,在软件层应该有若干检测措施,比方说用特殊的监视Task来监视别的Task是否正常。但丰田是怎么做的呢?还记得上面的“厨房洗涤盆”Task X吗?它是如此复杂(缺陷1),除了控制汽车运行的任务之外竟然还兼任大部分的监视任务,比如生成DTC。
DTC(diagnostic trouble codes),是汽车电脑系统会根据情况生成的错误代码。有的车主可能会遇到汽车某报警灯常亮,修车师傅拿个仪器插在行车电脑上得出一个代码,再查表就知道哪个元件坏了——这就是DTC。除了用于修车,DTC还被用于检测行车电脑和各传感器的异常状态。
可以想象,这个既是运动员又是裁判的Task X一旦死亡,软件层的检测措施大部分就失效了。
防护措施3。在这里丰田的错误开始到达顶峰。即使设置正确无误,上面提到的监视Task也只不过是另一个Task而已,与它的监视对象算是平级——监视Task自己同样有可能出现故障。嵌入式系统的一般做法是在所有程序之上再设置一道屏障,被称为“看门狗(Watchdog)”。所谓看门狗,是一个优先级很高的倒计时程序。别的程序需要在运行的时候特意去重置一下这个计时器让它重新开始倒计时,这个动作被称为“喂狗”。如果因为程序出问题太长时间不喂狗,倒计时完成,看门狗知道什么地方卡住了,马上采取措施,比如重启整个系统。重启系统听起来似乎很严重,实际上却是一件相当普通的事情。嵌入式系统的重启非常快,时速100公里的汽车中动力系统可以在半米之内完成重启——车上的人根本觉察不到。
通过阅读代码和拟真实验,Barr惊讶地发现上述嵌入式系统的常识性做法竟然在丰田软件系统内不存在!丰田的软件确实有一只看门狗,但它竟然不是用于监视Task异常,而是用于防止CPU过载。首先这个做法不能说后无来者至少算是前无古人。还记得上面提到的800页13章的报告吗?目瞪口呆的Barr将丰田看门狗的分析结果写入了报告的第一章,因为他实在太震惊。其次,丰田看门狗的防止CPU过载功能也相当蹩脚,在拟真测试发现即使它正常工作,还是会允许CPU过载时间长达1.5秒——时速100公里的车能跑40米以上。CPU一旦过载,就会导致所有的Task进入一种“假死”状态,无法处理信息,这时司机无法控制汽车动力,十分危险另外,丰田的工程师还犯了一个嵌入式课堂上被反复提到的经典错误:使用硬件时钟中断喂狗。硬件中断拥有非常高的优先级,即使Task卡住(比如出现死循环)也不能阻止硬件中断——可想而知这样一来看门狗就等于完全白瞎了。
这里也提一句,同年的普锐斯却令人意外地搭载了一只运作正常的看门狗,反而让人摸不着头脑。
还没完。这一层防护是嵌入式系统的关键阵地。前面都是电子系统,后面马上进入机械运作,足以造成灾难了。所以仅仅拥有软件级别的防护还不足够,丰田的做法是在主CPU之外单独设置了一块监视芯片,从硬件级别对系统的运作进行监视。监视芯片有两个任务。第一,它运行一种叫做系统卫士(System Guard)的程序,原理上来说是专门用于防止暴冲。主CPU和监视芯片上都会运行系统卫士,可是研究发现Task X一旦死亡,这些系统卫士统统都不起作用了。第二,它运行一个被称为“刹车回声检查(Brake Echo Check)”的程序。这个程序从代码上来看似乎可以检测出Task X的死亡,并且采取相应措施:关闭节气门。听起来像是好消息,但是同样有问题:首先这个程序不太可靠,即使正常运行,理论上也有失效的可能。最关键的是该程序不会自动运行,需要司机先对刹车踏板有“动作”才会触发。注意这里我特意没用“踩刹车”这个词,因为根据分析“触发动作”十分令人困惑。它分两种情况:如果Task X死亡的那一刻司机的脚不在刹车踏板上,那么触发动作是踩刹车。还算可以理解。另一种情况,如果Task X死亡那一刻司机的脚踩在刹车踏板上,那么触发动作是完全释放刹车踏板。没错,察觉车子在不正常加速的司机需要停止踩刹车才能让控制系统关闭节气门!这种违背人类认知的行为应该不是丰田工程师特意设计的。如果是,他们到底在想什么啊?
到此为止,上面提到的都算是“战术层面”的错误,都是“小错”。在讲解这块监视芯片的时候,可以发现丰田犯下最严重的“战略层面”错误——基础设计。Barr认为,如果基础设计正确,上述那些小错都完全不会导致汽车暴冲——不管代码写得多业余,不管内存错误多严重,不管Task死得多频繁,统统不会致命。让我们回到2002年 以前,没有电子油门的时候。那时候的拉线油门是由油门踏板机械连接的。当驾驶员的右脚踩下刹车,他的右脚必然不在油门踏板上,节气门自然而然地被关闭。这 个动作如此自然,甚至算不上安全措施,仅仅因为每人只有一只右脚,不可能同时踩油门和刹车。当丰田设计电子油门的时候,只要稍微有点常识,都应该从设计阶 段就将这一“自然而然”发生的动作考虑进去。但是很显然,他们没这么做。监视芯片上运行的代码是用汇编语言(一种更加接近机器执行代码,远离人类语言,更加难懂的编程语言)编写的,运行层次比主CPU的C语言更低。Barr认为如果设计得当,现有的监视芯片完全有能力胜任上述功能,需要的仅仅是几百行代码,别的什么都不用更改——不会提高任何生产成本。很遗憾,他们没有做到。
防护措施4。 现在已经脱离电子系统,节气门已经敞开,发动机全速运转,需要使用机械运作来组织机械运作了。如何让向前冲的车子停下来?不开车的人都知道,刹车!现代汽 车都装备了刹车助力,助力来自于发动机运转的时候产生的负压。我们知道发动机需要吸入空气,吸入体积等于排气量乘以转速。节气门又是用来阻挡空气的,那么 节气门关闭而发动机转速相对高的时候(比如高速丢油门),发动机的实际空气吸入量比它能吸入的体积要少,那么从节气门到气缸进气口之间会形成明显低气压 (所谓负压,比大气压力小)。刹车助力就是利用了这个负压推动气鼓产生更大的推力带动刹车片抓紧刹车盘。但是如果节气门敞开让空气随便进来,低气压就不存 在了,这时刹车助力大大减弱,刹车效率也大大降低。这就是为什么暴冲事件当事人都说全油门的时候根本刹不住的重要原因。这个现象称为“真空损失(Vacuum Loss)”,存在于所有自然吸气的汽油发动机汽车(柴油和增压发动机没影响),不算丰田的错。但丰田迟迟不搭载刹车优先系统(Brake Override System)允许刹车的同时敞开节气门,毫无疑问是这个现象的帮凶。
所谓刹车优先系统,指的是保证同时踩下刹车油门两个踏板的时候无条件关闭节气门的功能。这么做很显然主要是为了降低发动机输出,同时也保证刹车助力。丰田在2010年的凯美瑞上终于搭载了刹车优先系统,但是别高兴得太早。根据Barr的调查,丰田竟然将如此重要的修改“理所当然”地写入了他们的“厨房洗涤盆”——Task X。我只能“哑然失笑,扼腕叹息”。
(3)采用Hercules为丰田添加防护措施
在读完《《TI Hercules开发实战手册》》后,作为一名旁观者,面对他人找出来的这么多bug,自然而然想用TI的Hercules-MCU对上述bug进行改进。
(防护措施0)→堆栈溢出→(防护措施1)→(可能导致)→任务分配表被改写→(防护措施2)→(可能导致)→Task X死亡→(防护措施3)→(可能导致)→节气门敞开→(防护措施4)→(导致)→汽车暴冲
防护措施0:Hercules的存储器部分,带有高达256K的RAM,并都带有 ECC 校验,可以完成 1位纠错,2 位报错的功能,自带的硬件 BIST 模块会在启动时检测所有的 RAM 区功能。
对Task进行规划,将充足的RAM分配给堆栈,可以降低堆栈溢出的坑女性,同时Hercules可以确保RAM不会由于单比特反转(single bit flip)导致RAM错误,而多比特反转也能通过ECC校验识别。这样避免RAM出错导致后续错误。
防护措施1:Hercules 有片上存储器保护单元 MPU,每组总线主设备都有存储器保护功能,在这里我们所说的主设备包括 CPU、DMA、HTU 和 FTU 等模块。存储器单元属性可以由主设备进行配置,可配置的属性有只读,只写或者可读写。对于定义的区域以外的设备如果要对存储器进行访问只有两种模式:只读和不可以访问。如果对存储器的操作违反了它所配置的属性,就会产生错误信号送至错误信号处理模块 ESM。采用这种存储器保护机制可以防止误写或误读情况的发生。
如果采用Hercules做设计,将底层OS的RAM与Task的RAM分区,并使用MPU配置Task不能访问OS的RAM,这样保护了OS不被破坏,任务分配表不会改写导致Task-X的死亡。
防护措施2:防护措施3:Hercules 所配置的数字窗口看门狗要求应用程序必须在适当的时间内进行喂狗,从而可以作为检测CPU 是否跑飞的一个有效手段之一。数字窗口看门狗包含一个 25 位的递减计数器,支持多种窗口类型:100%, 50%, 25%, 12.5%, 3.125%。当未能及时喂狗时可以产生复位和不可屏蔽中断,并且发送错误信号到错误信号模块 ESM。数字窗口看门狗需要用特定的密钥序列((0xE51A, 0xA35C))进行喂狗操作,一旦看门狗被使用后只有系统复位或上电复位才能将它关闭。
由于可以使用Hercules的自带的安全ROM,可以减少CPU负担最高30%。采用Task-X与DTC相分离的设计方式,并且在DTC中设置看门狗代码。这样当Task-X异常时, DTC能检测到Task-X的错误,而DTC通过看门狗的正常运行来保证。形成了一条安全可靠链:
看门狗-->DTC-->Task-X
上述安全链中,看门狗保证了DTC,DTC保证了Task-X,确保了系统的安全可靠。一旦安全链基础—看门狗未能及时设置,则能在极短的时间内进行复位。保证了系统恢复正常控制。避免导致CPU过载1.5s之长。
防护措施4:新建一个Task——刹车优先系统(Brake Override System),并提高此Task的优先级,在踏下刹车后,优先执行此Task——关闭气门,闭合刹车片。
(4)后记
原本对市面上的所谓安全MCU没有太多概念,没觉得跟普通的MCU有什么不同,在读完《TI Hercules开发实战手册》一书后,才对安全关键系统有了一个比较初步的概念。同时在看到丰田轿车暴冲事件的法庭判决后,进一步感受到了安全的必要性。
以上仅仅属于个人的看法,只是作为一个口头说说的旁观者,对一个已知错误的一些结论进行一些纸上谈兵的评论,汽车控制系统必然是一个很庞大复杂的系统,丰田的架构设计与软件工程师中也必定有各种优秀的人才来改进。
[
本帖最后由 mars4zhu 于 2013-11-11 15:10 编辑 ]