为什么 -fsanitize=undefined 不接受(我认为是)UB?

Why doesn't -fsanitize=undefined pick up on (what I think is) UB?

提问人:doliphin 提问时间:8/28/2023 更新时间:8/28/2023 访问量:87

问:

auto vec = std::vector<int>({1, 2, 3});
std::cout << *vec.end() << std::endl;

这里有一些我认为是UB的简单代码。但是,使用以下步骤进行编译:

g++ -std=c++23 -Wall -Wextra -g -fsanitize=undefined -o ./target/src/main.cpp.o src/main.cpp
g++ -fsanitize=undefined -o ./target/main ./target/src/main.cpp.o  

产生一个非常愉快的消毒剂和输出!0

我认为连续集合迭代器在 C++ 中的工作方式是它们指向第一个越界内存位置。

{1, 2, 3} ?, ?, ?, ...
 ^ begin  ^ end

我的想法是,创建任何任意指针在技术上都是安全的,但取消引用此指针是不安全的,因此这是创建迭代器的良好方法。当然,这不太可能是问题,所以让我知道我哪里出了问题!:)fsanitize

C G++ Undefined-Behavior 清理程序

评论

3赞 NathanOliver 8/28/2023
是的,这是UB。如果编译器可以检测到UB的所有情况,那么我们的问题就会少得多,但这不是一件容易的事。最有可能的是,消毒剂只是没有抓住这一点。
2赞 Ted Lyngmo 8/28/2023
这个特殊案例是由阿桑发现的,而不是乌布桑。用-fsanitize=address,undefined
0赞 463035818_is_not_an_ai 8/28/2023
-fsanitize=addressgodbolt.org/z/85zvTneYP
1赞 n. m. could be an AI 8/28/2023
阿桑之所以能抓住它,是因为它击中了一个幸运的地方。在一般情况下没有运气演示。所以也不要指望牙山。
2赞 Marek R 8/28/2023
所有清理程序都在缓存某种未定义的行为。未定义的行为清理器会捕获其中的某些特定组:例如整数溢出。解决清理程序捕获内存相关问题。线程清理程序 - 争用条件,死锁。另请注意,所有消毒剂都不为您提供任何形式的保修 - 它们也有一些限制,可能会产生假阳性和假阴性结果。检查文档可以检测到的内容。

答:

2赞 Jan Schultke 8/28/2023 #1

你是对的;这是未定义的行为,一些消毒剂应该注意到它。Clang 的 UBSan 检测到它,而 GCC 的 ASan 可以:

#include <iostream>
#include <vector>

int main() {
    auto vec = std::vector<int>({1, 2, 3});
    std::cout << *vec.end() << std::endl;
}

这将产生以下错误:

==1==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x50200000001c at pc 0x00000040153e bp 0x7ffcd56c77c0 sp 0x7ffcd56c77b8
READ of size 4 at 0x50200000001c thread T0
    #0 0x40153d in main /app/example.cpp:6
    #1 0x7faa3ef48082 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x24082) (BuildId: 1878e6b475720c7c51969e69ab2d276fae6d1dee)
    #2 0x40120d in _start (/app/output.s+0x40120d) (BuildId: b859bb13bb93e5af8f52cb0ac4198ad4bf2002c3)

查看 Compiler Explorer 中的实时示例

通常,通常应该使用来捕获更广泛的错误。或者,使用外部工具,如 valgrind-fsanitize=address,undefined

评论

2赞 NathanOliver 8/28/2023
如果你让这个例子不那么简单,它仍然无法检测到:godbolt.org/z/TaGKcdM9W
0赞 Jan Schultke 8/28/2023
@NathanOliver我认为这可能超出了消毒程序的能力,只能通过额外的运行时断言来获取。当您添加 之后 ,它会再次拾取它。vec.shrink_to_fit()vec.clear()
1赞 user17732522 8/29/2023
要捕获 libstdc++ 中未定义库的行为,而这些行为恰好不是具体实现的核心语言未定义行为,可以使用 和 。-D_GLIBCXX_ASSERTIONS-D_GLIBCXX_DEBUG