LeakSanitizer 的运行时检查 (detect_leaks=1)
Runtime check for LeakSanitizer (detect_leaks=1)
我有一个问题,任何经过动态加载库的 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 下对 运行 执行运行时检查?
首先,不在 dlclose
上打印堆栈跟踪(或打印不正确的堆栈跟踪)是所有消毒剂(不仅仅是 LSan)中的 known issue。
其次,截至目前,还没有 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_off
在 sanitizer/lsan_interface.h 中定义)。
如果您通过 -fsanitize=address
启用 LSan,您可以将 __lsan_is_turned_off
检查替换为 #ifdef __SANITIZE_ADDRESS__
。
LSAN 接口 headers 允许用户定义回调 __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
仅在进程或 child 进程退出时执行。
不过还是有解决办法的!
#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
我有一个问题,任何经过动态加载库的 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 下对 运行 执行运行时检查?
首先,不在 dlclose
上打印堆栈跟踪(或打印不正确的堆栈跟踪)是所有消毒剂(不仅仅是 LSan)中的 known issue。
其次,截至目前,还没有 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_off
在 sanitizer/lsan_interface.h 中定义)。
如果您通过 -fsanitize=address
启用 LSan,您可以将 __lsan_is_turned_off
检查替换为 #ifdef __SANITIZE_ADDRESS__
。
LSAN 接口 headers 允许用户定义回调 __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
仅在进程或 child 进程退出时执行。
不过还是有解决办法的!
#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