10

Khorina · 5月24日

边聊安全 | 功能安全软件架构设计

如何开展软件架构设计

写在前面:

软件架构设计是软件功能安全开发流程中非常重要的一项活动,它起到承接软件安全需求,将软件安全需求分配到具体的软件组件中去,引导软件详细设计的实现,并指导软件开发工程师来完成代码逻辑的开发的作用。同时,软件架构设计也是开展软件集成测试的重要输入文件。
image.png

标准ISO 26262 Part6明确给出了软件架构设计的活动目标,输入物以及输出产物的要求:

image.png

其中ISO 26262, Part6-7.1给出了软件架构设计的目标:

a) 开发满足软件安全需求和其他软件需求的软件架构;

b) 验证软件架构设计适用于满足相应ASIL等级的软件安全需求;

c) 支持软件的实现和软件的验证。

image.png

ISO 26262, Part6-7.3.1&7.3.2阐述了软件架构设计的必要输入物:

a)软件开发环境的文档;

b)细化的HSI 接口文档;

c)软件安全需求;

d)技术安全概念;

e)系统架构设计;

f)可用的已认证的软件组件(之前未按照ASIL等级开发,但是已经经过认证);

g)非安全相关的功能规范,软件属性和其他的软件要求。

image.png

ISO 26262, Part6-7.5要求软件架构设计的输出产物应包括:

a)软件架构设计规范;

b)软件安全分析;

c)软件DFA;

d)软件验证报告。

那究竟该如何来进行软件架构设计呢?软件架构设计需要阐述清楚软件组件的哪些要素呢?在软件组件设计中需要关注到哪些设计原则呢?本文基于标准的要求,希望将上述问题都能够解释清楚,供各位从事于功能安全软件开发的小伙伴参考,并欢迎针对文章中的细节问题开展进一步的深入交流。

一.软件架构的设计原则

简而言之,软件架构设计就是根据软件安全需求,分解软件功能,定义好各个功能所需要的软件组件以及软件组件之间的交互接口。通过一个好的软件架构设计,就可以清楚地知道各个功能、各个软件组件之间的交互关系,知道数据流怎么走,并一眼看出不同模块间的层次、逻辑和时序关系。这样,软件工程师只要根据软件组件设计方案编写代码,不同软件组件组合起来后系统就可以正常工作。

为了达到上述目标,软件架构设计需要满足一系列的约束条件和设计原则。

image.png

标准ISO 26262 Part6根据不同ASIL等级,对功能安全相关的软件架构设计提出了相关约束,包括:

1、软件架应能够承载软件安全需求。

2、软件架构设计原则,包括: 

  (1)适当分层;

  (2)限制软件组件规模和复杂度;

  (3)限制接口规模;

  (4)组内高内聚;

  (5)组间低耦合等。

image.png

3、软件架构设计验证方法,包括: 设计走查,设计检查,控制流、数据流分析,仿真等。

image.png

4、硬件安全需求应被分配至软件架构中的相关组件。

5、不同或非ASIL等级软件组件开发需满足以下原则之一: 

  (1)按最高ASIL等级;

  (2)要素共存FFI。

6、对软件安全需求和具体实施之间要保证设计的可追溯性。

1.1 分层,使用层级结构

使用层级结构的目的是通过限制子系统的局部复杂度在一定范围内,从而达到使层次结构合理的要求。其主要思路是将软件系统按照不同的职责划分为多个层次,每个层次之间通过接口进行通信,实现模块之间的解耦和复用,提高系统的可维护性、可扩展性和可重用性。AUTOSAR就是一种被普遍接受的分层结构。

image.png

1.2 限制软件组件规模和复杂度

可通过减少模型中的重复模块或者使用参考模型来达到。

1.3 限制接口规模

使用总线结构是一种常见的用于减少接口数量的方法。

要求2.1,2.2和2.3都是从降低单个软件组件的复杂度考虑的,降低组件复杂度可以降低代码编写的难度,减少代码实现过程中可能引入的人因错误,同时也能够简化测试用例的复杂度。

可以用一些量化的指标来约束软件组件的复杂度,例如圈复杂度(Cyclomatic complexity)。

1.4 组内高内聚和组件低耦合

内聚、耦合是用于描述不同软件组件间的设计原则的。

image.png

内聚,是描述一个模块内各元素彼此结合的紧密程度,是从功能角度来度量模块内的联系。内聚共有7种方式,不同内聚方式的程度有所区别。

● 偶然内聚,一个模块内的各元素之间没有任何联系,仅是恰好放在同一个模块内。

● 逻辑内聚,把几种相关的功能组合在一起,由调用方传入的参数来确定具体执行哪一种功能。

● 时间内聚,指一个模块内的组件除了在同一时间都会被执行外,相互之间没有任何关联。

● 过程内聚,指一个模块内的组件以特定次序被执行,但相互之间没有数据传递。

● 通信内聚,指一个模块内的组件以特定次序被执行,且相互之间传递和操作相同的数据。

● 顺序内聚,指一个模块内的元素以特定次序被执行,且上一步的输出被下一元素所依赖。

● 功能内聚,指一个模块内所有组件属于一个整体,完成同一个不可切分的功能,彼此缺一不可。

image.png

耦合,是描述模块(系统/模块/类/函数)之间相互联系(控制/调用/数据传递)紧密程度的一种度量。耦合也有7中不同的方式。

● 非直接耦合,两个模块之间没有直接关系,它们之间的联系完全是通过主模块控制调用来实现的,这种耦合的模块独立性最强。

● 数据耦合,一个模块访问另一个模块时,彼此之间是通过数据参数(不是控制参数、公共数据结构或外部变量)来交换输入、输出信息的。

● 印记耦合,模块之间使用复合数据结构进行通信时。

● 控制耦合,一个模块通过传送开关、标志等控制信息,明显地控制选择另一模块的功能。

● 外部耦合,指多个模块同时依赖同一个外部因素(IO设备/文件/协议/DB等)。

● 共用耦合,指不同的模块共享全局数据的信息(全局数据结构、共享的通信区、内存的公共覆盖区)。

● 内容耦合,在低级语言(汇编)中出现,高级语言从设计上已避免出现内容耦合。

image.png

通常来说,模块内的元素的职责相关性低,通常也意味着模块与外部是紧耦合的,软件组件设计呈现“低内聚、高耦合”的特点;相反,如果模块内的元素的职责相关性强,也意味着模块与外部是松耦合的,即“高内聚、低耦合”。在软件组件设计过程中,解决了耦合的问题,也就解决了内聚的问题。

如果代码是低内聚和高耦合的,其弊端很明显,如果修改一个逻辑,会导致多处代码要修改,可能影响到多个业务链路,这增加了出bug的业务风险,同时增加了测试回归的范围,导致研发成本增加。

总结:因此软件设计的总体原则是降低每个软件组件的复杂度,追求软件组件间的高内聚和低耦合。

二.软件架构要素的静态设计要求

标准ISO 26262 Part6 – 7.4.5针对软件架构要素的静态设计提出了要求,包括:

● 分级层次的软件结构;

● 数据类型和它们的特征参数;

● 软件组件的外部接口;

● 嵌入式软件的外部接口;

● 全局变量;

● 包括架构的范围和外部依赖的约束。

总结:软件架构中静态设计关注的是软件组件的相关项,比如组件、输入、输出、端口(port)、接口(interface)、约束条件等。

三.软件架构要素的动态设计要求

标准ISO 26262 Part6 – 7.4.5针对软件架构要素的静态设计提出了要求,包括:

● 事件和行为的功能链;

● 数据处理的逻辑顺序;

● 控制流和并发进程;

● 通过接口和全局变量传递的数据流;

● 时间的限制。

总结:软件架构中动态设计关注的是软件组件相关项的控制逻辑和相互关系,是对静态要素的组合描述。

四.软件架构设计视图

为了描述软件架构静态和动态特性,ISO 26262对软件架构设计的标记法进行明确规定,包括: 自然语言,非半形式,半形式(伪代码,UML, SysML,Simulink等),形式记法(可运行代码)。其中对于ASIL等级C和D软件安全需求对应的架构设计强烈推荐采用半形式法进行。

image.png

可以使用结构视图:来描述架构静态结构和接口。常见的结构视图包括,类图,组件图,包图等。

● 类图 —— 描述系统中的类,以及各个类之间的关系的静态视图。

● 组件图 —— 由组件  + 接口  + 关系  + 端口  + 连接器组成。

● 包图 —— 描述的是模型中的包及其包含的元素组合。

image.png
组件图示意图

可以通过行为视图:来描述架构动态行为,例如数据流,控制流,不同状态切换等。常见的行为视图包括,控制流图,真值表,时序图,状态图等。

● 数据流图 ——描述数据如何由输入逐步流向输出的过程。

● 控制流图 ——描述由输入经过一系列控制动作到输出的过程。

● 状态机图 ——描述系统不同状态之间的转换关系。

● 真值表 ——描述一个复杂的组合逻辑关系。

● 序列图 ——描述不同组成部分通过信息交互的时序关系。

● 结构图 ——描述组件之间的层次关系。

image.png
image.png

总结:软件架构设计应使用合适的半形式化语言来说明软件组件的静态和动态特性。

五.软件要素间的相互干扰

5.1 要素共存准则与避免干扰

标准ISO 26262 Part6 – 7.4.8针对软件架构要素的要素共存准则与避免干扰进行了说明。

image.png

如果嵌入式软件不得不实现不同ASIL等级的软件组件,或实现安全相关及非安全相关的软件组件,除非软件组件符合 ISO 26262.9第6章定义的共存准则,否则全部嵌入式软件应按照最高ASIL等级来处理。

image.png

如果同一要素中存在不同ASIL等级,包括 QM(x)的安全相关子要素,若能证明对分配给要素的每个安全要求,某个子要素不会干扰任何分配了较高ASIL等级的其他子要素,则应仅视该子要素为较低ASIL等级的子要素。否则,应将不具备免于干扰证据的共存安全相关子要素的最高ASIL等级分配给该子要素。

总结:不同ASIL等级的软件要素的开发路径有以下两种:

1. 不论软件模块实际的ASIL等级要求如何,全部统一按照最高ASIL等级开发。

2. 按照软件模块实际的ASIL等级要求开发,同时提供模块间免于干扰的证据。

image.png

路径1算得上是一种简单粗暴的开发实现方案,但从开发成本考虑,绝大多数的主机厂会采用路径2的开发实现方案。因此,我们进一步提出了以下的问题:软件要素间有哪些干扰的形式,这些干扰应当如何去避免或者控制?

5.2 软件要素间干扰的形式及措施

软件要素间干扰的形式主要有两类:

● 时序干扰;

● 空间干扰。

时序干扰可能存在以下几种形式:

● 执行阻塞;

● 死锁;

● 活锁;

● 执行时间的不正确分配;

● 软件要素间的不正确同步。

可以考虑的处理机制如:循环执行调度、固定优先级调度、时间触发调度、处理器执行时间监控、程序执行次序监控和到达(发生)率监控,一旦检测到时序干扰便及时触发安全机制。

空间干扰包括内存存储干扰以及信息交换干扰,常见的形式为:

● 内容损;

● 对已分配给其他软件要素的内存进行读写访问;

● 信息重复;

● 信息丢失;

● 信息延迟;

● 信息插入;

● 信息伪装或信息的不正确寻址;

● 信息次序不正确;

● 信息损坏;

● etc。

可以考虑的处理机制如:常见的措施是使用内存保护单元(MPU)来实现软件分区,针对信息交互干扰的循环冗余校验(CRC)和E2E保护。

六.软件架构设计的颗粒度要求

标准ISO 26262 Part6 – 7.4.4针对软件架构要素的颗粒度描述如下。

image.png

软件架构设计应被开发到能够识别出软件单元的程度。

image.png

目标: 根据软件架构设计、设计准则以及分配的软件需求,开发软件单元设计,来支持软件单元的实现。

image.png

软件架构中的最低层级(原子级别)的且可被孤立测试的软件组件。

当然,我们也可以从软件设计过程来识别软件架构设计应当满足的颗粒度要求,软件设计过程依次为:

● 识别软件需求;

● 进行软件设计、识别需求对应的软件功能,确定各软件功能的需求;

● 识别软件功能对应的软件组件,定义各软件组件的需求;

● 设计软件组件。

因此,对于绝大多数情况软件架构设计的颗粒度应细化到软件组件,并根据分配到软件组件的需求设计软件组件的实现方案。

image.png

总结:软件架构的设计应至少满足对软件安全需求的追溯和软件功能在软件组件层面的分解。软件架构设计并非追求越详细越好,应当在满足上述要求的前提下给予软件组件设计的灵活性,同时也能避免因为底层代码逻辑的修改给软件架构设计带来的反复变更工作。

七.软件安全架构设计E-GAS

经典的AUTOSAR软件架构将软件整体分为应用软件层SWC,通讯层RTE和基础软件层BSW,为了更好地描述软件安全架构设计,将功能安全和非功能安全软件进行分层设计,还可以用E-GAS三层架构来表示。

E-GAS架构将软件整体分为level1功能层,level2功能监控层和level3控制器监控层,其中功能层和功能监控层均属于AUTOSAR中应用软件层,控制器监控层属于AUTOSAR的基础软件层,二者相互统一,并不矛盾。

image.png

E-GAS架构形成了很好的分层监视框架,并有效实现了功能安全分解,通常采用 QM(ASIL X)+ASIL X(ASIL X)的安全分解策略,即将功能实现软件(Level1)按照 QM 等级开发,功能冗余软件或安全措施(Level2、Level3)按照最高的要求等级 ASIL X(ASIL X)进行开发。

Level1 功能层:

该层完成具体的功能实现,比如对于电机控制器来说,这一层实现了将请求的扭矩转换为电机的扭矩输出。

Level2 功能监控层:

该层用于监控 Level1 功能的运行是否正常。Level2 的核心是设计一套方法去判断Level1 的运行是否正常。虽然判断 Level1 运行是否正常的方法,往往跟被监控的功能相关,不同被监控功能有不同的判定方法,比如:通过软件多样化冗余。但也有一些适用范围较广的判断方法,比如:合理性校验。

Level3 控制器监控层:

该层主要由三部分功能构成。

● 电子电气系统硬件诊断:监控电子电气系统硬件故障,比如:控制器的 CPU 核故障、RAM 故障、ROM 故障等。

● 独立监控:控制器相关的故障发生后,此时控制器已经无法可靠地执行安全相关逻辑,为了保证安全性,需要外部额外的独立监控模块,来确保即使 MCU 发生严重故障后,依然能够进入安全状态。这个额外的独立监控模块,通常是集成看门狗的电源管理芯片。

● 应用程序流检查:监控 Level1 和 Level2 的监控程序是否运行正常。该监控功能通过将程序流检查和看门狗喂狗绑定实现。如果 Level1 和 Level2 相关的监控程序没有按照设定的顺序运行,或者没有在规定的时间内执行,则程序流检查失败,无法正常喂狗,从而进入系统安全状态。

image.png
图片来自:https://zhuanlan.zhihu.com/p/626334809

总结:E-GAS是自动驾驶系统安全架构的基础,也是高阶自动驾驶设计绕不开的主题。

软件架构设计并非是一个标准化的工作,虽然标准ISO 26262在执行软件架构设计时提供了一些guidelines,但是要真正做好一份软件架构设计,必然是需要丰富的功能安全知识储备和相当的实践经验积累。本文以点带面,简单阐述了软件架构设计中需要关注的要素,也欢迎志同道合的小伙伴们一起交流、共同进步。

作者:边俊
文章来源:磐时汽车安全

推荐阅读

更多物联网安全,PSA等技术干货请关注平台安全架构(PSA)专栏。欢迎添加极术小姐姐微信(id:aijishu20)加入PSA技术交流群,请备注研究方向。
推荐阅读
关注数
4560
内容数
160
Arm发布的PSA旨在为物联网安全提供一套全面的安全指导方针,使从芯片制造商到设备开发商等价值链中的每位成员都能成功实现安全运行。
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息