GCC `-fsanitize=bounds` 与`std::array` 的奇怪行为

Posted

技术标签:

【中文标题】GCC `-fsanitize=bounds` 与`std::array` 的奇怪行为【英文标题】:GCC `-fsanitize=bounds` strange behaviour with `std::array` 【发布时间】:2016-11-18 12:49:41 【问题描述】:

我正在尝试使用-fsanitize=bounds 选项在我的代码中找出越界问题,但我遇到了奇怪的行为:

例如在下面的代码中:

#include <cstdlib>
#include <array>

int main (int, char **)

    std::array <char, 1> a;
    const char b = a [X]; // X <--- put index here!

    return EXIT_SUCCESS;

使用选项编译:$ g++ -std=c++11 -fsanitize=bounds -O0 main.cpp -o main

如果我尝试访问索引大于 1 的元素,则会报告错误: /usr/include/c++/5/array:53:36: runtime error: index 2 out of bounds for type 'char [1]'.

但如果我尝试访问索引为 1 的元素,一切正常且不会报告错误。

这是预期的行为吗?可能是我错过了什么?

该示例已在以下位置进行测试:

$ g++ --version g++ (Ubuntu 5.4.0-6ubuntu1~16.04.2) 5.4.0 20160609; $ g++ --version g++ (Ubuntu 6.2.0-5ubuntu12) 6.2.0 20161005

更新 我在 GCC 6 中尝试了 -fsanitize=bounds-strict 并得到了相同的结果。

【问题讨论】:

未定义的行为是未定义的。 @AlgirdasPreidžius,我的问题是关于使用ubsan 工具而不是关于语言。 你到底为什么要使用带有 N == 1 的 std::array 并引用超出范围的索引? @foxfireee,如果它让你高兴,你可以使用 N==1024*1024 并尝试访问循环内某处的元素 1024*1024。 我在发表评论:一种可能的解决方法是使用-D_GLIBCXX_DEBUG 来诊断越界访问。默认情况下,MSVC 在调试模式下也会这样做。 【参考方案1】:

我无法立即找到有关此功能的文档(或者,实际上,任何类型的强大文档),但我觉得这很可能是该功能的实现细节。获得一个过去的指针是合法的,因此实现无法捕获这一点。似乎该实现通过查看指针来工作,而不是等待这样的指针被取消引用(如果您考虑如何创建-fsanitize=bounds,这很有意义)。

简而言之,这可能只是工具的限制。

在 GCC 6 中,您可以尝试使用 -fsanitize=bounds-strict,这会增强工具的强度(包括增加对检测 OOB 对类似成员的灵活数组的访问的支持)。

我不知道这是库还是编译器问题,但无论哪种方式都值得注意的是,同样的问题目前是一个针对 clang (#21485) 的开放错误,并且 cmets 中的假设与我上面的漫谈.

A third-party article on ubsan 还暗示这最终是预期的行为。

【讨论】:

it's worth noting that the same problem is currently an open bug against clang - 我的问题有点不同,仅使用std::array 重现(使用类似 C 的数组效果很好)。【参考方案2】:

作为@Ripi2 mentioned,数组末尾之后的第一个元素通常被视为结束迭代器。

在这种情况下,-fsanitize=address 可以检测到越界错误,当您访问数组的非法位置时,编译器可以提供更多信息。

g++ -std=c++11 -fsanitize=address -O0 main.cpp -o main

【讨论】:

【参考方案3】:

来自https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html

-fsanitize=界限 此选项启用对数组边界的检测。检测到各种越界访问。灵活的阵列成员,灵活 类似数组成员的数组,以及静态变量的初始化器 存储未检测到。

使用 X>1 会触发此检查(数组越界),正如预期的那样,因为 'a' char 声明,大小 = 1。

编辑

话虽如此,正如@Lightness Races in Orbit 所说,这不是答案。但为读者提供了一些信息。

案例 X==1 是过去结束的事实让我想到了允许过去结束的迭代器。 IOW,似乎 GCC sanitizer 获得了一个用于测试的迭代器,而不是一个引用 a[X]。

@Gluttton 确认使用类似 C 的数组而不是 std::array 的情况 a[1] 显示为错误,正如预期的那样。

【讨论】:

这并不能真正回答问题,这就是为什么X == 1 不被视为违规(正如人们所期望的那样)。 gcc.gnu.org/onlinedocs/libstdc++/manual/iterators.html 解释了为什么一过到底不会触发错误。 我认为 aX] 被认为是一个迭代器,至少对于这种检查。

以上是关于GCC `-fsanitize=bounds` 与`std::array` 的奇怪行为的主要内容,如果未能解决你的问题,请参考以下文章

静态链接库asan与gcc 4.8

stl(GCC4.9.3 in MinGW)中rbtree lower_bound/upper_bound 的实现

gcc地址清理程序核心转储出错

(dpdk f-stack)-堆栈溢出-野指针-内存泄露(问题定位)

(dpdk f-stack)-堆栈溢出-野指针-内存泄露(问题定位)

gcc AddressSanitizer