5

我有一个问题,即通过动态加载的库的任何 Leak Sanitizer 回溯报告Unknown Module该库中的任何函数调用。

Direct leak of 48 byte(s) in 1 object(s) allocated from:
    #0 0x4e3e36 in malloc (/usr/sbin/radiusd+0x4e3e36)
    #1 0x7fb406e95f69  (<unknown module>)
    #2 0x7fb406eafc36  (<unknown module>)
    #3 0x7fb406eafd40  (<unknown module>)
    #4 0x7fb406ea3364  (<unknown module>)
    #5 0x7fb4063de7d4  (<unknown module>)
    #6 0x7fb4063c61c4  (<unknown module>)
    #7 0x7fb406617863  (<unknown module>)
    #8 0x7fb415620681 in dl_load_func /usr/src/debug/freeradius-server-4.0.0/src/main/dl.c:194:34
    #9 0x7fb41561edab in dl_symbol_init_walk /usr/src/debug/freeradius-server-4.0.0/src/main/dl.c:301:7
    #10 0x7fb41561df1e in dl_module /usr/src/debug/freeradius-server-4.0.0/src/main/dl.c:748:6
    #11 0x7fb41561f3db in dl_instance /usr/src/debug/freeradius-server-4.0.0/src/main/dl.c:853:20
    #12 0x7fb41564f4ab in module_bootstrap /usr/src/debug/freeradius-server-4.0.0/src/main/module.c:827:6
    #13 0x7fb41564ed56 in modules_bootstrap /usr/src/debug/freeradius-server-4.0.0/src/main/module.c:1070:14
    #14 0x5352bb in main /usr/src/debug/freeradius-server-4.0.0/src/main/radiusd.c:561:6
    #15 0x7fb41282ab34 in __libc_start_main (/lib64/libc.so.6+0x21b34)
    #16 0x4204ab in _start (/usr/sbin/radiusd+0x4204ab)

我以前遇到过与 valgrind 几乎相同的问题,我知道这是由于在退出时使用 dlclose 卸载了库,并且符号器运行时符号不可用。

使用 valgrind 修复很简单

/*
 *  Only dlclose() handle if we're *NOT* running under valgrind
 *  as it unloads the symbols valgrind needs.
 */
if (!RUNNING_ON_VALGRIND) dlclose(module->handle);        /* ignore any errors */

RUNNING_ON_VALGRIND是 valgrind 库提供的一个宏,用于检测程序是否是 valground。

我在 LSAN 文档中看不到任何关于何时ASAN_OPTIONS=detect_leaks=1设置的类似功能的内容。

有谁知道是否可以执行运行时检查以在 LSAN 下运行?

4

2 回答 2

3

LSAN 接口标头允许用户定义回调__lsan_is_turned_off以允许程序禁用泄漏检查器。此回调仅在启用 LSAN 时执行。

#include <sanitizer/lsan_interface.h>

static bool running_under_lsan = false;

int __attribute__((used)) __lsan_is_turned_off(void)
{
    running_under_lsan = true;
    return 0;
}

编辑:它实际上比这更复杂。正如@yugr 评论的那样,它似乎__lsan_is_turned_off只在进程或子进程退出时执行。

不过有办法!

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>

#include <string.h>
#include <errno.h>

#include <sanitizer/common_interface_defs.h>

static int from_child[2] = {-1, -1};
static int pid;

int __attribute__((used)) __lsan_is_turned_off(void)
{
    uint8_t ret = 1;

    /* Parent */
    if (pid != 0) return 0;

    /* Child */
    if (write(from_child[1], &ret, sizeof(ret)) < 0) {
        fprintf(stderr, "Writing LSAN status failed: %s", strerror(errno));
    }
    close(from_child[1]);
    return 0;
}

int main(int argc, char **argv)
{
    uint8_t ret = 0;

    if (pipe(from_child) < 0) {
        fprintf(stderr, "Failed opening internal pipe: %s", strerror(errno));
        exit(EXIT_FAILURE);
    }

    pid = fork();
    if (pid == -1) {
        fprintf(stderr, "Error forking: %s", strerror(errno));
        exit(EXIT_FAILURE);
    }

    /* Child */
    if (pid == 0) {
        close(from_child[0]);   /* Close parent's side */
        exit(EXIT_SUCCESS);
    }

    /* Parent */
    close(from_child[1]);       /* Close child's side */

    while ((read(from_child[0], &ret, sizeof(ret)) < 0) && (errno == EINTR));

    close(from_child[0]);       /* Close our side (so we don't leak FDs) */

    /* Collect child */
    waitpid(pid, NULL, 0);

    if (ret) {
        printf("Running under LSAN\n");
    } else {
        printf("Not running under LSAN\n");
    }

    exit(EXIT_SUCCESS);
}

例子:

clang -g3 -fsanitize=address foo.c

ASAN_OPTIONS='detect_leaks=1' ./a.out
Running under LSAN

ASAN_OPTIONS='detect_leaks=0' ./a.out
Not running under LSAN
于 2018-04-04T18:20:59.967 回答
1

首先,不打印堆栈跟踪dlclose(或打印不正确的堆栈跟踪)是所有消毒剂(不仅仅是 LSan)中的一个已知问题。

其次,到目前为止,还没有 API 可以检测在运行时是否启用了 LeakSanitizer,因此最好的办法是手动检查该程序是否与 Lsan 相关联并且detect_leaks=0未在环境中设置:

void (*__lsan_is_turned_off)() = dlsym(RTLD_DEFAULT, "__lsan_is_turned_off");
const char *lsan_opts = getenv("LSAN_OPTIONS");
const char *asan_opts = getenv("ASAN_OPTIONS");
int disable_dlclose = __lsan_is_turned_off != 0 && !__lsan_is_turned_off()
  && !(lsan_opts && (strstr(lsan_opts, "detect_leaks=0") || strstr(lsan_opts, "detect_leaks=false"))
  && !(asan_opts && (strstr(asan_opts, "detect_leaks=0") || strstr(asan_opts, "detect_leaks=false"));

__lsan_is_turned_offsanitizer/lsan_interface.h中定义)。

如果通过 启用 LSan -fsanitize=address,则可以将__lsan_is_turned_offcheck替换为#ifdef __SANITIZE_ADDRESS__

于 2018-04-04T18:17:06.350 回答