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` 的奇怪行为的主要内容,如果未能解决你的问题,请参考以下文章
stl(GCC4.9.3 in MinGW)中rbtree lower_bound/upper_bound 的实现
(dpdk f-stack)-堆栈溢出-野指针-内存泄露(问题定位)