You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在运行时检测Sanitizer是否启用及适配信号处理

问题

我有各类单元测试,比如验证向进程发送SIGUSR1信号后,进程是否按预期执行相应操作。这类测试正常运行,但用Sanitizer编译时,ppoll()等函数会被中断。原因是创建线程时我屏蔽了诸多信号,其中部分是Sanitizer或Catch2依赖的信号,得让底层库别干扰这些信号。

以下是经测试的信号列表:

// SIGHUP, -- 与catch2和/或sanitizer不兼容
// SIGINT, -- 与catch2和/或sanitizer不兼容
SIGQUIT,
// SIGILL, -- 与catch2和/或sanitizer不兼容
SIGTRAP,
// SIGABRT, -- 与catch2和/或sanitizer不兼容
SIGBUS,
// SIGFPE, -- 与catch2和/或sanitizer不兼容
// SIGKILL, -- 无法忽略
SIGUSR1,
// SIGSEGV, -- 与catch2和/或sanitizer不兼容
SIGUSR2,
SIGPIPE,
SIGALRM,
// SIGTERM, -- 与catch2和/或sanitizer不兼容
SIGSTKFLT,
SIGCHLD,
// SIGCONT, -- 无法忽略
// SIGSTOP, -- 无法忽略
//SIGTSTP, -- 与catch2和/或sanitizer不兼容
SIGTTIN,
SIGTTOU,
SIGURG,
SIGXCPU,
SIGXFSZ,
SIGVTALRM,
// SIGPROF, -- 与catch2和/或sanitizer不兼容
SIGWINCH,
// SIGIO, -- 与catch2和/或sanitizer不兼容
SIGPWR,
SIGSYS,
// 32 -- pthread实现使用
// 33 -- pthread实现使用
SIGRTMIN,
SIGRTMIN+1,
SIGRTMIN+2,
SIGRTMIN+3,
SIGRTMIN+4,
SIGRTMIN+5,
SIGRTMIN+6,
SIGRTMIN+7,
SIGRTMIN+8,
SIGRTMIN+9,
SIGRTMIN+10,
SIGRTMIN+11,
SIGRTMIN+12,
SIGRTMIN+13,
SIGRTMIN+14,
SIGRTMIN+15,
SIGRTMAX-14,
SIGRTMAX-13,
SIGRTMAX-12,
SIGRTMAX-11,
SIGRTMAX-10,
SIGRTMAX-9,
SIGRTMAX-8,
SIGRTMAX-7,
SIGRTMAX-6,
SIGRTMAX-5,
SIGRTMAX-4,
SIGRTMAX-3,
SIGRTMAX-2,
SIGRTMAX-1,
SIGRTMAX,

我希望仅在单元测试处于Sanitizer环境运行时,移除列表中标记为“与catch2和/或sanitizer不兼容”的信号。但处理这些信号的库可能没按Sanitizer编译,链接时无法得知当前是否启用了Sanitizer。目前只能通过编译时#ifdef判断:

#if defined(__SANITIZE_ADDRESS__) || defined(__SANITIZE_THREAD__)
... 进程启用sanitizer时执行的代码 ...
#endif

我需要在代码中实现运行时检测,比如:

if(sanitizer_is_running())
{
    ... 从列表中移除冲突信号 ...
}

请问如何实现这个sanitizer_is_running()函数?

实现方案

方法1:检查Sanitizer环境变量

Sanitizer运行时会自动设置特定环境变量,比如ASAN_OPTIONS(AddressSanitizer)、TSAN_OPTIONS(ThreadSanitizer)、UBSAN_OPTIONS(UndefinedBehaviorSanitizer)。通过检查这些变量是否存在即可判断:

#include <stdlib.h>

int sanitizer_is_running(void) {
    // 检查AddressSanitizer
    if (getenv("ASAN_OPTIONS") != NULL) {
        return 1;
    }
    // 检查ThreadSanitizer
    if (getenv("TSAN_OPTIONS") != NULL) {
        return 1;
    }
    // 检查UndefinedBehaviorSanitizer
    if (getenv("UBSAN_OPTIONS") != NULL) {
        return 1;
    }
    return 0;
}

优点:实现简单,无需额外依赖;缺点:若用户手动清除环境变量,检测会失效,但这种场景极少。

方法2:检测进程加载的Sanitizer动态库

Linux系统下,Sanitizer会加载对应的动态库(如libasan.so、libtsan.so),可以读取/proc/self/maps文件遍历已加载库列表:

#include <stdio.h>
#include <string.h>

int sanitizer_is_running(void) {
    FILE* maps = fopen("/proc/self/maps", "r");
    if (maps == NULL) {
        return 0;
    }

    char line[256];
    while (fgets(line, sizeof(line), maps) != NULL) {
        if (strstr(line, "libasan.so") != NULL ||
            strstr(line, "libtsan.so") != NULL ||
            strstr(line, "libubsan.so") != NULL) {
            fclose(maps);
            return 1;
        }
    }

    fclose(maps);
    return 0;
}

优点:检测可靠,不受环境变量影响;缺点:仅适用于Linux系统,依赖/proc文件系统。

方法3:调用Sanitizer运行时API

部分Sanitizer提供了官方检测API,结合弱符号声明可避免未编译Sanitizer时的链接错误:

#include <stddef.h>

// 用弱符号声明,未链接Sanitizer时返回默认值
__attribute__((weak)) void* __asan_get_current_fake_stack(void) { return NULL; }
__attribute__((weak)) int __tsan_get_current_thread_id(void) { return 0; }
__attribute__((weak)) void __ubsan_get_current_report_data(void) {}

int sanitizer_is_running(void) {
    // 检测AddressSanitizer
    if (__asan_get_current_fake_stack() != NULL) {
        return 1;
    }
    // 检测ThreadSanitizer
    if (__tsan_get_current_thread_id() != 0) {
        return 1;
    }
    // 检测UndefinedBehaviorSanitizer:尝试调用API,若未崩溃则说明UBSAN在运行
    // 注:该方式需确保UBSAN不会因空调用触发报错,可根据实际情况调整
    __ubsan_get_current_report_data();
    return 1;
    return 0;
}

优点:官方支持,检测精准;缺点:不同Sanitizer的API差异较大,需要针对性适配。

内容的提问来源于stack exchange,提问作者Alexis Wilke

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 13:02:36