我知道有关组合 AddressSanitizer 和 ThreadSanitizer 的问题。我是从理论计算机科学的角度来问这个问题的,是由之前的讨论引发的。
考虑以下有缺陷的 C++ 程序:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <thread>
#include <mutex>
static std::mutex m;
static char *buf;
static void a()
{
for (;;) {
char *b;
{
std::lock_guard<std::mutex> g{m};
b = buf;
}
if (b)
puts(b);
}
}
static void b()
{
for (;;) {
{
std::lock_guard<std::mutex> g{m};
buf = strdup("foo");
}
{
std::lock_guard<std::mutex> g{m};
buf = static_cast<char*>(realloc(buf, 10000));
strcpy(buf, "barbarbar");
}
{
std::lock_guard<std::mutex> g{m};
free(buf);
buf = nullptr;
}
}
}
int main()
{
auto thread_a = std::thread(a);
auto thread_b = std::thread(b);
thread_a.join();
// unreachable
thread_b.join();
return 0;
}
当使用
-fsanitize=address
使用 GCC 14.2.0 或 Clang 20 进行编译时,程序将立即终止并出现 heap-use-after-free
错误,因为 a()
使用的是指针的过时副本。
当在没有特殊选项的情况下编译并使用
valgrind ./a.out
运行时,在我的系统上,程序将运行近 4 分钟,然后报告第一个 Invalid read of size 1
。我认为这是因为 Valgrind 通过在单个线程中抢占式调度来模拟多个线程,因此您需要一些运气才能在正确的时刻在 a()
和 b()
之间进行上下文切换,以便重现错误。
同样,当使用GCC或Clang使用
-fsanitize=thread
编译程序时,它似乎可以继续运行,没有任何问题。
我的问题:最近是否有任何学术研究可以在这方面改进ThreadSanitizer算法?
Valgrind memecheck、DRD 和 Helgrind 都检测到此代码中的错误。
对于 DRD,我建议使用
--tool=drd --fair-sched=yes --check-stack-var=yes
。
对于 Helgrind,我建议使用
--tool=helgrind --fair-sched=yes
。
对于 memcheck 只是
--fair-sched=yes
。
如果没有公平调度,在我的测试中触发错误需要更长的时间,有时需要 10 秒,而公平调度通常在几秒钟内。
公平调度仅适用于 Linux。将其移植到 FreeBSD 已在我的待办事项清单上。
只需要一次构建,而且我只使用了 GCC。