为什么不推荐在太空/辐射环境中使用C++模板?

81
通过阅读这个问题,我理解到例如为什么在辐射高的环境中,比如太空或核电站中不建议使用动态分配或异常处理。
至于模板,我不明白为什么不能使用。您能否向我解释一下?
根据这个答案,它说使用模板是相当安全的。
注意:我不是在谈论复杂的标准库内容,而是特意制作的自定义模板。

14
我猜这不是因为环境的原因,而更可能是因为在资源非常有限的嵌入式系统中运行程序。模板往往会创建“膨胀”,因为模板会导致不同实例化的代码重复。 - Some programmer dude
5
有关C++在火星上的担忧出现在Rover演示文稿的第34页,与辐射无关。(你提到的答案下半部分与辐射担忧无关。) - molbdnilo
9
模板最终只是普通的类和函数。除了可能导致代码膨胀或编译时间过长等原因之外,没有理由不使用它们。 - One Man Monkey Squad
25
与辐射或代码大小无关,安全设计准则通常试图减少代码的复杂性(短函数、无间接调用、仅静态内存分配等)。许多这些指南是在LINT是进行代码分析的最佳工具的时候编写的。因此,并非所有这些规则都仍然有意义。 - user6556709
11
理论上,你可以使用一个受限的 C++ 子集来开发这些系统。但实际上,你应该尽量避免使用 C++,因为它过于复杂,而且很难相信 C++ 程序员会坚持使用安全的子集。很快你就会发现程序中到处都是模板元编程的地狱。此外,许多 C++11 及更高版本的新功能,如 "auto" 的行为,可能会给整个项目带来风险。 - Lundin
显示剩余3条评论
3个回答

122

请注意,与太空兼容(包括抗辐射航空合规)的计算设备非常昂贵(包括在太空中发射,因为它们的重量超过千克),而且一次太空任务的成本可能达到数亿欧元或美元。由于软件或计算机问题导致任务失败通常具有禁止性成本,因此是不可接受的,这就证明需要昂贵的开发方法和程序,这些方法和程序您甚至无法想象用于开发移动应用程序,建议使用概率推理和工程方法,因为宇宙射线仍然是某种“不寻常”的事件。从高层次的角度来看,宇宙射线及其产生的位翻转可以被认为是某种抽象形式的信号或输入中的噪声。您可以将“随机位翻转”问题视为信噪比问题,然后随机化算法可能会提供有用的概念框架(特别是在元级别,即分析安全关键源代码或已编译二进制文件时,但也适用于关键系统运行时,在某些复杂的内核或线程调度器中),并采用信息论视角。
为什么不建议在太空/辐射环境中使用C++模板?
该建议是对C++的MISRA C编码规则和嵌入式C++规则以及DO178C建议的概括,并且与辐射无关,而与嵌入式系统有关。由于辐射和振动的限制,任何航天火箭计算机的嵌入式硬件都必须非常小(例如出于经济和能耗原因,它更像是树莓派式的系统而不是大型x86服务器系统)。太空级别的芯片成本是民用芯片的1000倍。在太空嵌入式计算机上计算WCET仍然是一个技术难题(例如由于与CPU缓存相关的问题)。因此,在安全关键的嵌入式软件密集型系统中,堆分配不受欢迎(您如何处理这些内存不足的情况?或者您如何证明您有足够的RAM来运行所有实际运行时情况?)
在安全关键软件领域,你不仅需要“保证”或“承诺”,而且需要评估(通常使用一些巧妙的概率推理)自己软件的质量,以及用于构建它的所有软件工具的质量(特别是:编译器和链接器;波音或空客不会在没有来自例如FAADGAC的书面批准的情况下更改其用于编译飞行控制软件的GCC交叉编译器版本)。大多数软件工具需要得到某种形式的批准或认证。
请注意,在实践中,大多数C++(但肯定不是全部)模板内部使用堆。标准C++容器当然也是如此。编写永远不使用堆的模板是一个困难的练习。如果你能做到这一点,你就可以安全地使用模板(假设你信任你的C++编译器及其模板扩展机制,这是大多数最新C++编译器(例如GCCClang)的最棘手的部分)。
我猜出于类似的(工具集可靠性)原因,使用许多源代码生成工具(进行某种元编程,例如发射C ++或C代码)是不受欢迎的。例如,请注意,如果您在一些安全关键软件中使用bison(或RPCGEN)(由makegcc编译),则需要评估(并可能详尽测试)的不仅是gccmake,还有bison。这是一个工程原因,而不是科学原因。请注意,一些嵌入式系统可能会使用随机算法,特别是巧妙地处理嘈杂输入信号(甚至是由于足够罕见的宇宙射线引起的随机位翻转)。证明、测试或分析(或只是评估)这样基于随机的算法是一个非常困难的话题。
还要查看Frama-ClangCompCert,并观察以下内容:
  • C++11(或更高版本)是一种极其复杂的编程语言,它没有完整的正式语义。在C++方面足够专业的人只有全球几十个(可能大部分都在其标准委员会中)。我能够编写C++代码,但不能解释所有移动语义的微妙角落,或者C++ 内存模型。此外,C++实际上需要使用许多优化才能有效地运行。

  • 制作无错误的C++编译器非常困难,尤其是因为C++实际上需要棘手的优化,以及C++规范的复杂性。但目前的编译器(如最新的GCC或Clang)实际上相当不错,并且它们只有少量(但仍然存在)残留的编译器错误。目前还没有CompCert ++用于C ++,而制作一个需要数百万欧元或美元(但如果您能够筹集这样的金额,请通过电子邮件与联系,例如发送到basile.starynkevitch@cea.fr,我的工作电子邮件)。而航天软件行业则非常保守。

  • 制作好的C或C ++堆内存分配器很困难。编写一个是一种权衡。开个玩笑,可以考虑将此C堆内存分配器适应于C++。

  • 证明模板相关的C ++代码的安全属性(特别是缺少运行时竞争条件未定义行为,如缓冲区溢出)在2019年第二季度仍略领先于C ++代码的静态程序分析技术。我的Bismon技术报告草案(这是一份H2020可交付成果的草案,请跳过欧洲官僚的页面)有几页详细解释了这一点。请注意Rice定理

  • 整个系统的C ++嵌入式软件测试可能需要进行火箭发射(像阿丽亚娜5号501试飞那样),或者至少需要在实验室中进行复杂而繁重的实验。它非常昂贵。即使在地球上测试火星车也需要花费大量的资金。

想象一下:你正在编写一些安全关键的嵌入式软件(例如用于列车制动、自动驾驶汽车、自动无人机、大型石油平台或炼油厂、导弹等)。你天真地使用了一些C++标准容器,例如一些std::map<std::string,long>。在内存不足的情况下会发生什么?你如何“证明”,或者至少“说服”,资助一个1亿欧元太空火箭的组织中的工作人员,你的嵌入式软件(包括用于构建它的编译器)已经足够好了?十年前的规定是禁止任何形式的动态堆分配。

我所说的不是复杂的标准库内容,而是特别制作的自定义模板。

即使这些很难被证明,或者更普遍地评估它们的质量(你可能想在其中使用自己的分配器)。在空间中,代码空间是一个强约束条件。因此,您将使用例如g++ -Os -Wallclang++ -Os -Wall进行编译。但是,您如何证明或者仅仅测试所有-Os完成的微妙优化(这些是特定于您的GCC或Clang版本的)?您的空间资助组织会问您这个问题,因为嵌入式C++空间软件中的任何运行时错误都可能导致任务崩溃(再次阅读Ariane 5首飞失败-当时编码使用的是某种Ada方言,其类型系统比今天的C++17“更好”和“更安全”),但不要对欧洲人笑得太多。波音737 MAX与其MACS是一个类似的混乱

我的个人建议(但请不要太认真对待)是考虑使用Rust来编写您的嵌入式软件,因为它比C ++稍微安全一些。当然,您必须花费5至10 M€(或MUS $)在5或7年内获得适用于航天计算机的良好Rust编译器(如果您有能力在自由软件Compcert / Rust上花费如此多的话,请与我联系)。但这只是软件工程和软件项目管理的问题(阅读神话般的程序员月份胡说八道的工作以获取更多信息,还要注意Dilbert principle:它同样适用于航天软件行业或嵌入式编译器行业,也适用于其他任何行业)。

我强烈而个人地认为,欧洲委员会应该通过Horizon Europe等方式资助一个自由软件CompCert++(甚至更好的是Compcert/Rust)项目(这样的项目需要超过5年时间和超过5名顶尖的博士研究人员)。但是,我60岁时很遗憾地知道这不会发生(因为欧盟委员会的意识形态 - 主要受德国政策的明显影响 - 仍然是历史终结的幻觉,因此H2020和Horizon Europe在实践中主要是通过欧洲避税天堂为企业实施税收优化的途径),这是在与CompCert项目的几位成员私下讨论后得出的结论。我很遗憾地预计,DARPANASA更有可能资助未来的CompCert/Rust项目(而不是欧盟资助它)。
欧洲航空电子工业(主要是空中客车公司)比北美(波音公司)更多地使用形式方法方法。因此,一些(而不是全部)单元测试被避免(因为被Frama-CAstrée等工具替代进行源代码的正式证明 - 它们只针对C的子集,禁止C动态内存分配和C的几个其他功能)。这是由DO-178C(而不是前身DO-178B)批准,同时也得到了法国监管机构DGAC(以及我猜其他欧洲监管机构)的批准。
此外,请注意,许多SIGPLAN会议与OP的问题间接相关。

3
"由于在嵌入式C++空间软件中的任何运行时错误都可能导致任务崩溃(请重新阅读关于阿丽亚娜5号首次飞行失败的内容),这并不是支持在嵌入式空间中使用C的论点。C++具有更强的类型检查,在这种情况下会有所帮助。" - Tarick Welling
2
我认为关于C++语言复杂性的争论不太有说服力。如果选择的语言是C,那么这些争论就是合理的。但我在某处读到他们更喜欢Ada语言,而且它也是一种复杂的语言,我认为与C++相当(尽管我承认我从未真正使用过它,只是在80年代阅读了规范)。 - Barmar
9
我觉得你提到的C++模板示例是std::map<std::string,long>,然后你反对它的动态分配原因,而不是因为它是一个模板,这让我感到有些可疑。我猜你想详细说明动态分配,因为OP也在提及它,之前已经涉及了代码膨胀和作为导致验证难度加大的一般复杂性的模板。如果你考虑清楚你要做什么,使用模板是安全的,但肯定容易出现代码膨胀。 - Peter Cordes
5
关于安全关键系统中的Rust:https://ferrous-systems.com/blog/sealed-rust-the-pitch/本文介绍了一种名为“封闭Rust”的新技术,它可以在安全关键系统中使用Rust语言而无需手动编写安全代码。这项技术基于对编译器和标准库的修改,以及通过一个称为“密封模块”的新概念来实现。使用“密封模块”,开发人员可以定义一个模块,将其视为完整且不可变的单元,从而避免了在运行时动态加载代码时可能存在的安全漏洞。此外,该技术还包括其他功能,如禁止特定的Rust语言功能,以及更严格的内存安全检查。这个技术还处于早期阶段并在不断发展,但它有望在未来成为安全关键系统开发的有用工具。 - Sebastian Redl
12
这与模板有什么关系? - Reuven Abliyev
显示剩余20条评论

8

反对在安全代码中使用模板的论点是,它们被认为会增加代码的复杂性而没有真正的好处。如果你使用的工具不好,或者有一个传统的安全理念,这个论点是有效的。请看以下例子:

template<class T>  fun(T t){
   do_some_thing(t);
}

在传统的安全系统规范中,您必须提供代码的每个功能和结构的完整描述。这意味着您不允许具有任何未经说明的代码。这意味着您必须对模板的功能进行完整描述。由于明显的原因,这是不可能的。同样的原因也禁止类似函数的宏。如果您改变想法,以描述此模板的所有实际实例,您就可以克服此限制,但需要适当的工具来证明您确实描述了所有实例。
第二个问题是:
fun(b);

这一行不是一个独立的行。您需要查找b的类型,才能知道实际调用的函数。理解模板的适当工具可以帮助解决这个问题。但在这种情况下,手动检查代码确实变得更加困难。


4
反对在安全代码中使用模板的论点是,它们被认为会增加代码的复杂性而没有实际好处。不,那是反对在嵌入式系统总体上使用模板的论点。反对在安全代码中使用模板的论点是,在100%确定性代码中根本没有模板的用途。在这样的系统中,没有通用编程。您不能使用像std::vector这样的东西,因为很难找到符合安全标准的std库。或者如果您找到了,那将需要大量的现金。 - Lundin
3
在嵌入式领域中,通用编程是一种常见的做法,即使在深度嵌入式领域也是如此。这是因为相同的原因,在其他层次上也变得普遍:经过充分测试的算法是一件好事情。 - user6556709
3
@Lundin:模板与确定性或不确定性代码无关。它们最终只是一种在不使用动态调度(虚函数或函数指针)和不复制粘贴代码的情况下重用代码的方法,同时比宏更为安全。例如,重用相同的排序算法来对整数数组和短整数数组进行排序。而std::vector不适用于安全关键实时代码,这与其是模板无关。 - MikeMB
2
谁会这样做呢?对于一般用途的算法库的作者来说,这可能是情况,但当我们谈论安全关键的实时代码时,我们已经离开了“通用”领域,而且OP明确谈到了定制模板。 - MikeMB
2
“这意味着您必须在其一般形式中提供模板功能的完整描述。”-- 难道不是只需显式实例化将要使用的每个模板类型,然后提供每个创建的类型的功能的完整描述就足够了吗? - odougs
显示剩余5条评论

3

我对模板被视为漏洞的说法感到非常不可思议,主要有两个原因:

  • 模板被“编译消除”,即像其他函数/成员一样实例化和代码生成,并且没有特定于它们的行为。就像它们从未存在一样;

  • 任何语言中的构造都不是安全或易受攻击的;如果电离粒子更改了内存的单个位(无论是在代码还是数据中),则任何事情都有可能发生(从没有注意到的问题发生到处理器崩溃的情况)。保护系统免受此类攻击的方法是添加硬件内存错误检测/纠正功能,而不是修改代码!


...和JETCPACMPLTCPSTECSTOCS - Basile Starynkevitch
随机算法非常适合处理带有噪声的信号(在信息理论意义上,而不是Unix意义上的),内核对于由宇宙辐射引起的随机位翻转的鲁棒性是“处理带有噪声的信号”的实例。 - Basile Starynkevitch
@BasileStarynkevitch:您的信息不准确,随机算法不适合处理噪声。信号处理与内核设计完全没有任何关系。 - user1196549
但是它还没有被用于嵌入式计算。我甚至听过一个讲座,解释说在我的预期寿命之前,量子计算没有实际应用(除了量子化学模拟)。关键是实用的量子计算机只有非常少的量子位。是的,信号处理(在数学层面上,而不是在编码中)与操作系统内核可靠性技术有间接关系。 - Basile Starynkevitch
3
@BasileStarynkevitch:我们讨论的不是你个人的兴趣,而是帮助OP处理辐射的方法。 - user1196549
显示剩余18条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接