GCC的ASAN可以提供与Rust相同的内存安全性吗?

问题描述 投票:5回答:3

Rust被称为内存安全语言,但GCC中有一个名为AddressSanitizer(ASAN)的安全功能:

./configure CFLAGS="-fsanitize=address -g" CXXFLAGS="-fsanitize=address -g" LDFLAGS="-fsanitize=address"
make
make check

ASAN能提供与Rust相同的内存安全性,还是Rust有更多技巧?甚至可以比较两者吗?

免责声明:我不是程序员。

gcc rust address-sanitizer
3个回答
13
投票

消毒剂

GCC和Clang都有一套消毒剂;到目前为止,它们是在Clang开发的,然后移植到GCC,因此Clang拥有最先进的版本:

  • Address Sanitizer(ASan):检测越界访问,释放后使用,使用后范围,双重自由/无效,并添加对内存泄漏的支持(预期内存开销3倍),
  • Memory Sanitizer(MemSan):检测未初始化的读取(预期减速3倍),
  • Thread Sanitizer(TSan):检测数据竞争(预期减速5x-15x,内存开销5x-10x),
  • Undefined Behavior Sanitizer(UBSan):各种局部未定义的行为,例如未对齐的指针,积分/浮点溢出等......(最小的减速,轻微的代码大小增加)。

还有一个关于Type Sanitizer的工作正在进行中。


消毒杀菌剂vs Rust

不幸的是,使用消毒剂使C ++达到Rust的安全水平是不可能的;即使将所有现有的消毒杀菌剂结合起来仍然会留下空白,众所周知它们是不完整的。

您可以在CppCon 2017上看到John Regher关于Undefined Behavior的演示文稿,幻灯片can be found on github,我们从中获得当前的报道:

enter image description here

这并不能说明消毒剂彼此不相容这一事实。也就是说,即使你愿意接受组合的减速(15x-45x?)和内存开销(15x-30x?),你仍然无法管理C ++程序和Rust一样安全。


强化与调试

消毒剂之所以如此占用CPU /内存是因为它们是调试工具;他们试图尽可能精确地为开发人员提供诊断,以便对调试最有用。

为了在生产中运行代码,您正在寻找的是强化。强化是指以尽可能低的开销消除未定义的行为。例如,Clang支持多种强化二进制的方法:

这些工具可以组合在一起,对性能影响最小(<1%)。不幸的是,它们比消毒剂的覆盖范围要小得多,最值得注意的是,它们并不试图涵盖作为攻击频繁目标的自由使用/使用后范围或数据竞赛。


结论

我没有看到任何方法将C ++提升到Rust结合的安全级别,没有:

  • 对语言的严格限制:参见MISRA / JSF指南,
  • 非常严重的性能损失:消毒剂,禁用​​优化,......
  • 对标准库和编码实践进行彻底检查,其中Core Guidelines是一个开始。

另一方面,值得注意的是Rust本身使用unsafe代码;并且它的unsafe代码也需要经过审查(参见Rust Belt project),并将受益于所有上述消毒剂/硬化仪器通过。


2
投票

不,这两个功能无法比较。

地址清理不是安全功能,也不提供内存安全性:它是一种调试工具。程序员已经有工具来检测他们编写的代码是否存在内存问题,例如释放后使用或内存泄漏。 Valgrind可能是最着名的例子。这个gcc功能提供了(某些)相同的功能:唯一的新功能是它与编译器集成,因此它更容易使用。

您不会在生产中启用此功能:它仅用于调试。您使用此标志编译测试,并自动检测由测试触发的内存错误。如果您的测试不足以触发问题,那么您仍然遇到问题,并且它仍会在生产中造成相同的安全漏洞。

Rust的所有权模型通过使包含此类缺陷的程序无效来防止这些缺陷:编译器不会编译它们。您不必担心测试不会触发问题,因为如果代码编译,则不会出现问题。

这两个功能适用于不同的问题。地址清理的一个特征是检测内存泄漏(分配内存并忽略以后释放内存)。 Rust使得编写内存泄漏比在C或C ++中更难,但它仍然可能(如果你有循环引用)。 Rust的所有权模型可防止顺序和多线程情况下的数据争用(见下文)。地址清理并不旨在发现这两种情况。

顺序代码中的数据竞争的一个示例是,如果您在一组对象上进行迭代,同时还要添加或删除元素。在C ++中,更改大多数集合将使任何迭代器无效,但是程序员必须意识到这已经发生:它未被检测到(尽管某些集合在调试版本中有额外的检查)。在Rust中,当集合上存在迭代器时,不可能改变集合,因为所有权模型会阻止它。

多线程代码中的数据争用的一个示例是具有共享对象的两个线程,其中访问由互斥锁保护。在C ++中,程序员可能会在更改对象时忘记锁定互斥锁。在Rust中,互斥锁本身拥有它保护的对象,因此不可能不安全地访问它。 (但是还有许多其他类型的并发错误,所以不要被带走!)


-1
投票

没有听说过这个选项,但听起来它修改了输出程序。换句话说,它会在程序运行时进行检查。

另一方面,Rust检查程序何时创建(或在程序员中编译),因此首先没有这些内存安全漏洞。

链接文章提到它无论如何只涵盖一个案例,返回后使用。

© www.soinside.com 2019 - 2024. All rights reserved.