使用 Address Sanitizer 测试 libc++
[本文以略微扩展的形式 转载自 Marshall 的博客]我一直断断续续地运行 libc++ 测试。这是一个相当广泛的测试套件,但我想知道是否有测试套件没有发现的错误。在即将发布的 clang 3.3 中,有一个名为 Address Sanitizer 的新功能,它在您的可执行文件中插入一些运行时检查,以查看是否存在任何对内存的“越界”读写操作。
我一直想,如果能说 libc++ 是“ASan 清洁”的(也就是说,在使用 Address Sanitizer 运行时通过了所有测试套件),那该多好。
所以我决定这么做。
[ 所有这些工作都是在 Mac OS X 10.8.2/3 上完成的 ]
如何运行测试
有一个用于运行测试的脚本。它被称为testit
。 $ cd $LLVM/libcxx/test ; ./testit
其中 $LLVM/libcxx 是 libc++ 的签出位置。这大约需要 30 分钟才能运行。在我的系统上,没有 Address Sanitizer,libc++ 在 4348 个测试中失败了 12 个。使用 Address Sanitizer 运行测试
$ cd $LLVM/libcxx/test ; CC=/path/to/tot/clang++ OPTIONS= "-std=c++11 -stdlib=libc++ -fsanitize=address" ./testit
注意:默认选项是“-std=c++11 -stdlib=libc++”,如果您没有指定任何内容,就会得到这些选项。 这大约需要 92 分钟;是之前时间的 3 倍多。使用 Address Sanitizer,libc++ 失败了 54 个测试(同样,在 4348 个测试中)。哪些测试失败了?
- 在 11 个测试中,Address Sanitizer 检测到堆块外部的一个字节写入。所有这些都与 iostreams 相关。我创建了一个 ASan 也触发的小型测试程序,并将其发送给 Howard Hinnant(他编写了大部分 libc++ 代码),他发现了一个他错误地分配了零字节缓冲区的地方。一个错误,多个失败。他在版本 177452 中修复了这个问题。
- std::random 的 2 个测试失败了。事实证明这是测试代码中的一个越界错误,而不是 libc++ 中的错误。我在版本 177355 和 177464 中修复了这些问题。
- Address Sanitizer 检测到 4 个情况下内存分配失败。这是预期的,因为有些测试正在测试 libc++ 的内存分配系统。但是,似乎 ASan 在内存分配失败时不会调用用户提供的
new_handler
(并且可能也不会抛出std::bad_alloc
)。我已经提交了 PR15544 来跟踪这个问题。 - 25 个情况下程序由于缺少符号而无法加载。这最常见的是
std::__1::__get_sp_mut(void const *)
,但也有一些其他的。Howard 说这是在 10.8 发布后添加到 libc++ 中的,所以它不在 /usr/lib 中的 dylib 中。如果测试使用从源代码构建的 libc++ 副本运行,它们就会通过。 - 有 12 个情况在启用 Address Sanitizer 之前就失败了。
$ cd $LLVM/libcxx/test ; DYLD_LIBRARY_PATH=$LLVM/libcxx/lib CC=/path/to/tot/clang++ OPTIONS= "-std=c++11 -stdlib=libc++ -fsanitize=address" ./testit
这导致了 16 个失败。- 与内存分配失败相关的 4 个失败。
- 我们开始时的 12 个失败。