技术频道

嵌入式软件的安全设计理念

嵌入式软件应用场合、硬件平台及操作系统的多样性,使嵌入式软件在各种不同条件下可能出现未知、不可预测的状况,即其潜在风险往往比通用PC机的软件要高。由于嵌入式软件应用场合特殊,往往在无人值守的情况下运行,高可靠性和安全性自然成为嵌入式系统的重要指标。

在设计初期排查各种可能的风险,投入较低并可获得高回报。最终的产品质量也可以得到很好的控制。下面借鉴安全管理学思想,列举一些生活实例说明嵌入式软件设计的安全理念。

1 围墙问题

学校修筑围墙,有一个问题——到底需要的高度是多少?过低,很容易翻越围墙进出,起不到围墙的屏障作用;过高,翻越的人滑落容易伤亡,这也不是修筑围墙的初衷。程序设计中的程序运行异常好比非法进出校园。一方面需要防止程序异常,这就类似修了围墙。但另一方面也需要注意围墙高度:围墙过高,轻易不出问题,但一出就是大问题。比如数据通信传输程序,加入CRC冗余校验。如果数据传输出现校验错误,CRC冗余校验可能恢复错误的数据。但是如果在设计测试初期就使用CRC校验,并且程序中没有警告信息,就有可能将错误延续到产品发布阶段。产品到现场出问题那就严重了。还有一个例子,看门狗程序是为了程序异常时自动重启恢复系统。如果在程序测试期间就使用看门狗,同样会屏蔽测试期间的程序跑飞、死机等问题,是不利于发现程序缺陷的。

2 修裤脚问题

给孩子买了条裤子,试穿后发现裤子长了些,于是很精确地测量出需要截去10 cm。问题出现了,妈妈动手改好了之后,奶奶也给改短了10 cm,接下来的情景可想而知。这就是沟通问题,某成员在对某对象实施某行为的时候没有留下任何标记,使得其他成员未得到准确信息,带来下一步行为的失误。

程序设计中同样也有类似问题。比如某进程对一个临界资源进行访问,并且没有任何标记,如果另一进程也访问该资源就会造成资源访问的冲突。通过信号量互斥保护就可以解决这一问题。另一个例子是在内存申请和释放方面。比如函数funA()调用funB(),在funA()或funB()中动态申请一段内存空间,并且将指向该内存的指针传给另一函数,在funA()或funB()中都可以释放内存。但是一定注意,需要沟通在哪个函数里进行,尤其当这两个函数分别由两个人完成的时候。不能出现两个函数都释放该内存或都不释放该内存的情况。

3 优势和不足

两个游人出行,一个带伞,另一个不带伞。那天下了大雨,结果回来时带伞的人被淋得全身湿透,而不带伞的反而未被淋湿。原因何在?因为带伞的人认为自己带了伞不用躲雨,不知不觉就湿透了;不带伞的知道在雨中几秒钟就能全身湿透,所以一直注意在亭子下躲雨。

程序设计中何尝不是如此?对认为不容易出问题的代码设计投入不足,测试工作少,对易出问题的代码投入大量精力,严加测试,最后的结果反而是容易出问题的代码质量更高。这就是设计人员常常遇到的情况——能想到的错误都解决了,想不到的错误都出现了。另外一个例子是:对于RS232串口通信,考虑到通信传输距离、外界干扰等问题,采用了数据校验和错误重发机制;对于I2C、SPI总线往往是短距离、同一电路板的芯片访问,都没有任何数据校验措施。结果有可能是RS232串口数据总是正确的,I2C、SPI总线的数据受不合理的布线及电磁干扰影响反而出现错误。因此对于嵌入式系统,需要根据实际的现场情况定制程序设计,而不是因为大多数人都这么做,或以前都这么做。

4 警告和避错

电线杆上有特别亮丽的几个字,某行人好奇,爬上电线杆一看,四个大字:“油漆未干”。可见这个告示性文字反而害苦了这位行人。如果换一种方式,将电线杆周围容易被人接触到的地方围上一圈,就能很好地避免路人接触。当然这里还需要考虑成本和效用的平衡。

嵌入式系统往往不需要人员值守就能正常工作,因此依靠警告、报错不能解决所有问题。你可以想象在驾驶飞机时,导航屏幕出现类似Windows系统的“内存空间不足,请关闭部分程序”警告的情形是多么可笑。在设计这一类程序的时候,应该考虑程序如何能自动解决一些异常情况,即使有些情况下必须进行人机交互,也应该考虑这时程序是否可以自动采取一些保护措施。比如数据读取异常报错,可以考虑用一个默认的数据;通信连接不上报错则需要检测通信是否恢复正常。

以上从几个生活实例用类比的方式说明了嵌入式软件设计需要注意的一些问题,当然仅仅注意这几点对保证嵌入式软件的质量是远远不够的。文章的目的是通过几个易懂的实例强调设计安全意识以及软件产品质量意识的重要性。

文章版权归西部工控xbgk所有,未经许可不得转载。