在C/C++中,“安全”和“不安全”的代码是什么?

8

"安全"和"不安全"代码在C/C++中有什么区别?

我在文章《如何比较Rust与其他语言》中读到了"C++在某些方面是不安全的,这会导致严重的安全漏洞"。哪些地方的不安全代码是存在问题的?


14
C++并不是不安全的;不安全的代码是不安全的,与其编写语言无关。 - P.P
4
有些语言在理论上是“固有安全”的,如果系统经过正确设计和配置,则不会威胁其完整性。C/C++不属于这类语言。 - Hot Licks
6
C++必须为特定平台编译,才会使该平台存在不安全因素。而其他语言,如Java,则允许跨平台的不安全性。 - Martin James
3
我认为这是一个合理的问题,只是因为“政治”原因而被消声(C++标签中的人不喜欢听到自己的语言是“不安全”的)。我投票支持重新开放。 - Matteo Italia
4
我不认为这个问题是有意挑衅的,但一些人似乎把它当成了挑衅。但是,引用的文章提出了C和C++经常听到的批评,理解这个问题是合理的。很多人会读到那篇文章并有同样的疑问。 - Shafik Yaghmour
显示剩余5条评论
1个回答

11
我相信John Regehr的文章C和C ++中未定义行为的指南,第1部分很好地概述了该文章的要点:
编程语言通常区分正常程序操作和错误操作。对于图灵完备的语言,我们无法在离线状态下可靠地决定程序是否有可能执行错误;我们只能运行它并查看结果。
在安全的编程语言中,错误会在发生时被捕获。例如,Java通过其异常系统在很大程度上是安全的。在不安全的编程语言中,错误不会被捕获。相反,在执行错误操作后,程序会继续运行,但以一种悄无声息的错误方式进行,这可能会在以后产生可观察的后果。Luca Cardelli关于类型系统的文章对这些问题有一个很好的清晰介绍。C和C++在强烈的意义上是不安全的:执行错误操作会导致整个程序变得毫无意义,而不仅仅是错误操作具有不可预测的结果。在这些语言中,错误操作被称为具有未定义行为。
所以,一旦我们进入未定义行为的领域,我们现在就有了"不安全"的代码。另一篇涵盖了将未定义行为视为不安全代码的好文章是What Every C Programmer Should Know About Undefined Behavior #2/3

在我们的系列文章的第1部分中,我们讨论了什么是未定义行为,以及它如何使C和C++编译器能够生成比"安全"语言更高性能的应用程序。本文讨论了"C语言真正的不安全性质",解释了未定义行为可能导致的一些非常惊人的影响。在第3部分中,我们谈论友好编译器可以采取什么措施来减轻一些惊喜,即使他们不是必须的。

我喜欢称之为"为什么未定义行为对C程序员通常是可怕而可怕的事情"。 :-)

C和C++分别由它们各自的标准规定,我们可以在这里找到最新版本的链接,而这些标准留下了许多行为被指定为未定义的行为。这基本上意味着该行为是不可预测的。C++标准将未定义的行为定义如下:

对于此国际标准未强制执行要求的行为 [注:当此国际标准省略任何明确的行为定义或程序使用错误构造或错误数据时,可能会出现未定义的行为。允许的未定义行为范围从完全忽略情况并具有不可预测的结果,到以记录环境特征方式(带或不带诊断消息)在翻译或程序执行期间表现,直至终止翻译或执行(带诊断消息)。许多错误的程序结构不会引起未定义的行为;它们需要被诊断。 —结束注释]

编译器不需要为未定义的行为提供诊断,并且我们可以找到许多导致安全漏洞的未定义行为的案例,其中一个更为知名的案例可能是Linux内核空指针检查删除

The idea is to look for code that becomes dead when a C/C++ compiler is smart about exploiting undefined behavior. The classic example of this class of error was found in the Linux kernel several years ago. The code was basically:

struct foo *s = ...;
int x = s->f;
if (!s) return ERROR;
... use s ...

The problem is that the dereference of s in line 2 permits a compiler to infer that s is not null (if the pointer is null then the function is undefined; the compiler can simply ignore this case). Thus, the null check in line 3 gets silently optimized away and now the kernel contains an exploitable bug if an attacker can find a way to invoke this code with a null pointer

大多数情况下,避免未定义行为是良好编程实践的问题: 同时,使用正确的工具如ubsan也很重要,但有些晦涩的情况例如无限循环可能会令许多开发者感到惊讶。

4
"etc"是一个替代词,用于代替一长串其他令人讨厌的、极其恶劣的事情。当你在C/C++中编程时,需要小心处理这些问题。 - Gabriel

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