C/C++中"安全"和"不安全"代码有什么区别?
我在文章中读到"C++在导致严重安全漏洞的方面不安全":Rust如何与其他语言进行比较.什么是不安全的代码不安全?
我相信John Regehr的文章"C和C++中的未定义行为指南",第1部分很好地概述了本文的内容:
编程语言通常区分正常的程序操作和错误的操作.对于图灵完备语言,我们无法可靠地确定程序是否有可能执行错误; 我们必须运行它,看看.
在安全的编程语言中,错误会在发生时被捕获.例如,Java通过其异常系统在很大程度上是安全的.在不安全的编程语言中,错误不会被困.相反,在执行错误操作之后,程序继续运行,但是以一种无声的错误方式可能会在以后产生可观察到的后果.Luca Cardelli关于类型系统的文章对这些问题有一个很好的清晰介绍.C和C++在强烈意义上是不安全的:执行错误操作会导致整个程序无意义,而不仅仅是具有不可预测结果的错误操作.在这些语言中,错误的操作被认为具有未定义的行为.
因此,一旦我们进入未定义行为的领域,我们现在就有了"不安全"的代码.另一篇涵盖未定义行为作为不安全代码的好文章是每个C程序员应该知道的关于未定义行为的内容#2/3:
在本系列的第1部分中,我们讨论了未定义的行为,以及它如何允许C和C++编译器生成比"安全"语言更高性能的应用程序.这篇文章讨论了"不安全"C的真实含义,解释了未定义行为可能导致的一些非常令人惊讶的影响.在第3部分中,我们讨论友好编译器可以做些什么来减轻一些意外,即使它们不是必需的.
我喜欢称之为"为什么未定义的行为对C程序员来说往往是一件可怕而可怕的事情".:-)
C和C++由它们各自的标准指定,我们可以在这里找到最新的链接,这些标准将许多行为指定为未定义的行为.这基本上意味着行为是不可预测的.C++标准定义了未定义的行为,如下所示:
本国际标准没有要求的行为[注意:当本国际标准忽略任何明确的行为定义或程序使用错误的结构或错误数据时,可能会出现未定义的行为.允许的未定义行为包括完全忽略不可预测的结果,在翻译或程序执行期间以环境特征(有或没有发出诊断消息)的特定行为,终止翻译或执行(发布时)一条诊断信息).许多错误的程序结构不会产生未定义的行为; 他们需要被诊断出来. - 尾注]
编译器不需要为未定义的行为提供诊断,我们可以找到许多未定义的行为导致安全漏洞的情况,其中一个更为人熟知的案例可能是Linux内核空指针检查删除:
我的想法是在C/C++编译器很聪明地利用未定义的行为时寻找变得死的代码.几年前在Linux内核中发现了这类错误的典型例子.代码基本上是:
Run Code Online (Sandbox Code Playgroud)struct foo *s = ...; int x = s->f; if (!s) return ERROR; ... use s ...问题是第2行中s的取消引用允许编译器推断s不是null(如果指针为null则函数未定义;编译器可以简单地忽略这种情况).因此,第3行中的空检查会被静默优化,如果攻击者能够找到一种使用空指针调用此代码的方法,则内核包含可利用的错误
大多数时候避免未定义的行为是一个良好的编码实践问题:
并使用正确的工具,如ubsan,但可能有一些模糊的情况,如无限循环,许多开发人员可能会发现令人惊讶.
| 归档时间: |
|
| 查看次数: |
4748 次 |
| 最近记录: |