当前位置: 首页 > news >正文

android 性能分析工具(04)Asan 内存检测工具

1 Asan工具简介

1.1 Asan工具历史背景

AddressSanitizer(ASan)最初由Google开发,并作为LLVM项目的一部分。ASan的设计目的是帮助开发者检测并修复内存错误,如堆栈和全局缓冲区溢出、使用已释放的内存等,这些问题可能会影响程序的正确性和安全性。ASan通过在编译时插入额外的代码来检查每次内存访问,并在运行时监控这些访问,从而发现错误。

ASan的实现基于"shadow memory"技术,它为程序的内存分配创建了一个影子区域,用于跟踪内存的使用情况。当程序试图访问未初始化或已释放的内存时,ASan能够检测到这些非法访问并报告错误。这项技术使得ASan能够在不牺牲太多性能的情况下,有效地发现难以察觉的内存错误。

随着时间的推移,ASan已经成为GCC和Clang编译器的标准组成部分,广泛用于提高软件质量,特别是在内存安全性方面。此外,ASan的集成和易用性也得到了不断改进,例如在Visual Studio中,从2019版本16.9开始,MSVC编译器和IDE支持AddressSanitizer,并且提供了与IDE的集成,使得开发者可以更容易地在Windows平台上使用ASan。

1.2 Asan工具软件定位

AddressSanitizer(ASan)主要用于C和C++程序。它通过在编译时插入额外的检查代码,运行时监控程序的内存访问,来定位常见的内存错误,包括但不限于:

  • 堆缓冲区溢出:检测堆上分配的内存是否被越界访问。
  • 栈缓冲区溢出:检测栈上分配的局部变量是否超出其声明的范围。
  • 全局变量溢出:检测全局或静态变量的内存是否被越界访问。
  • 使用已释放的内存:检测程序是否尝试访问已经被释放的内存。
  • 内存泄漏:实验性地检测内存泄漏问题。
  • 初始化顺序问题:检测由于不正确的初始化顺序导致的问题。
  • 内存越界访问:检测对内存的读写操作是否超出了其分配的范围。
  • 函数返回局部变量:检测函数返回局部变量的引用,这通常会导致悬挂指针问题。

Asan的工作原理是在编译时对代码进行插桩,即在内存访问点周围添加额外的检查代码。运行时,Asan的运行时库会接管标准的内存管理函数,如mallocfree,以跟踪内存分配和释放的状态。当检测到内存错误时,Asan会立即报告错误并提供详细的错误信息,包括错误类型、发生错误的代码位置、调用栈信息等。

Asan的使用可以显著提高软件的内存安全性,帮助开发者在开发阶段就发现并修复潜在的内存问题。然而,由于ASan会引入额外的性能开销,它通常用于调试和测试阶段,而不推荐在生产环境中启用。

总的来说,ASan是一个功能强大且广泛使用的内存错误检测工具,能够有效地帮助开发者定位和解决C/C++程序中的内存问题,提高软件的稳定性和安全性。

接下来我们看看如何使用Asan工具。

2 Asan工具使用

2.1 如何使用Asan工具

使用ASan,你需要使用支持ASan的编译器。比如Clang或GCC,并开启ASan相关的编译选项。

如果使用Clang编译器,在终端执行以下命令:

$clang -fsanitize=address -g hellp.c -o hello

如果使用GCC编译器,在终端执行以下命令:

$gcc -fsanitize=address -g hello.c -o hello

在上述命令中,-fsanitize=address是ASan的编译选项,用于开启ASan。但在大型工程(使用makefile方式进行编译)中我们一般通过环境变量的方式加入ASan编译选项,然后编译额时候需要加上环境变量,一般是CFLAGS或者CXXFLAGS

2.2 -fsanitize选项常见的sanitizer解读

以下是一些常见的sanitizer选项及其含义:

  • -fsanitize=address:这个选项启用了地址sanitizer,它是一个运行时检测工具,用于检测C/C++程序中的内存错误,如缓冲区溢出、使用后释放(use-after-free)错误等。地址sanitizer会在程序运行时监控内存操作,当检测到错误时,它会提供详细的错误报告,包括错误类型、发生位置和堆栈跟踪信息。
  • -fsanitize-recover=address:这个选项与-fsanitize=address一起使用,它告诉地址sanitizer在检测到内存错误时,尝试恢复程序的执行,而不是立即终止程序。这可以使得程序在检测到某些内存错误后继续运行,这对于调试和分析程序的行为非常有用。
  • -fsanitize=memory:MemorySanitizer(MSan)用于检测未初始化的内存读取。它通过为每个值关联一个“起源”来工作,如果一个值被读取时其起源是未知的,MSAN就会报告一个错误。
  • -fsanitize=undefined:UndefinedBehaviorSanitizer(UBSan)用于检测未定义行为,例如整数溢出、空指针解引用、除以零等问题。
  • -fsanitize=thread:ThreadSanitizer(TSan)用于检测多线程程序中的数据竞争和死锁问题。
  • -fsanitize=leak:这个选项已经被弃用,取而代之的是使用-fsanitize=address,它现在包含了内存泄漏检测功能。
  • -fsanitize=coverage:CoverageSanitizer(CovSan)用于生成代码覆盖率报告,检测程序中哪些部分被执行过。
  • -fsanitize=kernel-address:Kernel Address Sanitizer(KASan)是AddressSanitizer的一个变种,专门用于检测Linux内核中的内存错误。
  • -fsanitize=dataflow:这个选项允许开发者定义自己的数据流问题,并在运行时检测它们。
  • -fsanitize=cfi:Control Flow Integrity(CFI)Sanitizer用于检测控制流相关的安全问题,比如虚拟函数表破坏攻击。
  • -fsanitize=safe-stack:SafeStack通过将安全关键的栈数据移动到堆上来保护程序,从而防止栈溢出攻击。
  • -fsanitize=hwaddress:Hardware Address Sanitizer(HWASAN)用于检测硬件级别的内存错误,比如加载无效的地址。

这些sanitizer工具是查找隐藏bug的利器,它们可以帮助开发者在开发和测试阶段发现和修复潜在的错误。需要注意的是,这些工具会增加程序的运行时间和内存开销,因此主要用于调试和测试阶段,不推荐在生产环境中启用。

3 asan部分实战解读

这里给出一些asan的具体使用方法,当然,限于常用的一些场景。其他场景参考即可。分析问题和解决问题的思路是类似的。

3.1 堆缓冲区溢出(Heap Buffer Overflow)

案例描述:检测对堆分配的缓冲区边界以外的内存写入操作。

参考代码如下:

#include <stdio.h>
#include <stdlib.h>int main() {char *buffer = (char *)malloc(5 * sizeof(char));if (buffer == NULL) {fprintf(stderr, "Memory allocation failed\n");return 1;}sprintf(buffer, "Hello, World!");printf("Buffer contents: %s\n", buffer);free(buffer);return 0;
}

使用以下命令编译程序,并启用AddressSanitizer:

$gcc -fsanitize=address -g -o heap_overflow heap_overflow.c

然后使用gdb运行编译出的程序:

$gdb ./heap_overflow

执行后 得到如下调试信息:

[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
=================================================================
==1346454==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000000015 at pc 0x7ffff745f04f bp 0x7fffffffdaf0 sp 0x7fffffffd280
WRITE of size 14 at 0x602000000015 thread T0#0 0x7ffff745f04e in __interceptor_vsprintf ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:1687#1 0x7ffff745f27e in __interceptor_sprintf ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:1730#2 0x5555555552f2 in main /media/wangdaosheng/data/backup/tmp/test2/heap_overflow.c:14#3 0x7ffff7029d8f in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58#4 0x7ffff7029e3f in __libc_start_main_impl ../csu/libc-start.c:392#5 0x5555555551a4 in _start (/media/wangdaosheng/data/backup/tmp/test2/heap_overflow+0x11a4)0x602000000015 is located 0 bytes to the right of 5-byte region [0x602000000010,0x602000000015)
allocated by thread T0 here:#0 0x7ffff74b4887 in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:145#1 0x55555555527e in main /media/wangdaosheng/data/backup/tmp/test2/heap_overflow.c:6#2 0x7ffff7029d8f in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58SUMMARY: AddressSanitizer: heap-buffer-overflow ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:1687 in __interceptor_vsprintf
Shadow bytes around the buggy address:0x0c047fff7fb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x0c047fff7fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x0c047fff7fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x0c047fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x0c047fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c047fff8000: fa fa[05]fa fa fa fa fa fa fa fa fa fa fa fa fa0x0c047fff8010: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa0x0c047fff8020: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa0x0c047fff8030: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa0x0c047fff8040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa0x0c047fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):Addressable:           00Partially addressable: 01 02 03 04 05 06 07 Heap left redzone:       faFreed heap region:       fdStack left redzone:      f1Stack mid redzone:       f2Stack right redzone:     f3Stack after return:      f5Stack use after scope:   f8Global redzone:          f9Global init order:       f6Poisoned by user:        f7Container overflow:      fcArray cookie:            acIntra object redzone:    bbASan internal:           feLeft alloca redzone:     caRight alloca redzone:    cbShadow gap:              cc
==1346454==ABORTING
[Inferior 1 (process 1346454) exited with code 01]

这个错误信息是由 AddressSanitizer(ASan)提供的。根据提供的信息,我们可以分析出以下关键信息:

  1. 错误类型AddressSanitizer: heap-buffer-overflow 表示发生了堆缓冲区溢出。

  2. 错误位置:错误发生在地址 0x602000000015,这是一个堆内存地址。

  3. 出错指令WRITE of size 14 at 0x602000000015 表示在地址 0x602000000015 处尝试写入 14 个字节的数据。

  4. 程序计数器(PC)和基指针(BP)pc 0x7ffff745f04fbp 0x7fffffffdaf0 提供了出错时的程序计数器和基指针的值,这对于调试器来说是有用的。

  5. 出错函数:错误发生在 __interceptor_vsprintf 函数中,这是 ASan 对 vsprintf 函数的拦截实现。

  6. 调用栈:调用栈显示了错误发生时的函数调用顺序。在这里,我们看到 sprintf 函数(第2行)被 main 函数(第3行)调用,这是错误发生的源头。

  7. 内存分配位置:出错的内存是在 main 函数的第6行分配的,由 __interceptor_malloc 拦截。

  8. Shadow Bytes:Shadow Bytes 是 ASan 使用的一块内存区域,用于跟踪实际内存的状态。这里的 Shadow Bytes 显示了出错地址附近的内存状态。

  9. 错误摘要SUMMARY: AddressSanitizer: heap-buffer-overflow 再次总结了错误类型。

  10. 程序退出==1346454==ABORTING 表示由于这个严重错误,程序被ASan中止执行。

在这个案例中,错误是由于 sprintf 写入的数据超出了分配的5字节大小的缓冲区,导致堆缓冲区溢出。

3.2 栈缓冲区溢出(Stack Buffer Overflow)

案例描述:检测对栈分配的缓冲区边界以外的内存写入操作。

参考代码如下:

#include <stdio.h>
#include <string.h>int main() {char buffer[4];strcpy(buffer, "Hello");printf("Buffer contents: %s\n", buffer);return 0;
}

使用以下命令编译程序,并启用AddressSanitizer:

$gcc -fsanitize=address -g -o stack_overflow stack_overflow.c

然后使用gdb运行编译出的程序:

$gdb ./stack_overflow

执行后 得到如下调试信息:

[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
=================================================================
==1680994==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fffffffdba4 at pc 0x7ffff743a2c3 bp 0x7fffffffdb70 sp 0x7fffffffd318
WRITE of size 6 at 0x7fffffffdba4 thread T0#0 0x7ffff743a2c2 in __interceptor_memcpy ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:827#1 0x5555555552ca in main /media/wangdaosheng/data/backup/tmp/test2/stack_overflow.c:6#2 0x7ffff7029d8f in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58#3 0x7ffff7029e3f in __libc_start_main_impl ../csu/libc-start.c:392#4 0x555555555164 in _start (/media/wangdaosheng/data/backup/tmp/test2/stack_overflow+0x1164)Address 0x7fffffffdba4 is located in stack of thread T0 at offset 36 in frame#0 0x555555555238 in main /media/wangdaosheng/data/backup/tmp/test2/stack_overflow.c:4This frame has 1 object(s):[32, 36) 'buffer' (line 5) <== Memory access at offset 36 overflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork(longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:827 in __interceptor_memcpy
Shadow bytes around the buggy address:0x10007fff7b20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x10007fff7b30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x10007fff7b40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x10007fff7b50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x10007fff7b60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x10007fff7b70: f1 f1 f1 f1[04]f3 f3 f3 00 00 00 00 00 00 00 000x10007fff7b80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x10007fff7b90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x10007fff7ba0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x10007fff7bb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x10007fff7bc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):Addressable:           00Partially addressable: 01 02 03 04 05 06 07 Heap left redzone:       faFreed heap region:       fdStack left redzone:      f1Stack mid redzone:       f2Stack right redzone:     f3Stack after return:      f5Stack use after scope:   f8Global redzone:          f9Global init order:       f6Poisoned by user:        f7Container overflow:      fcArray cookie:            acIntra object redzone:    bbASan internal:           feLeft alloca redzone:     caRight alloca redzone:    cbShadow gap:              cc
==1680994==ABORTING
[Inferior 1 (process 1680994) exited with code 01]

这个错误信息是由 AddressSanitizer(ASan)提供的。根据提供的信息,我们可以分析出以下关键信息:

  1. 错误类型AddressSanitizer: stack-buffer-overflow 表示发生了栈缓冲区溢出。
  2. 错误位置:错误发生在地址 0x7fffffffdba4,这是一个栈内存地址。
  3. 出错指令WRITE of size 6 at 0x7fffffffdba4 表示在地址 0x7fffffffdba4 处尝试写入 6 个字节的数据。
  4. 程序计数器(PC)和基指针(BP)以及栈指针(SP)pc 0x7ffff743a2c3, bp 0x7fffffffdb70, sp 0x7fffffffd318 提供了出错时的程序计数器、基指针和栈指针的值,这对于调试器来说是有用的。
  5. 出错函数:错误发生在 __interceptor_memcpy 函数中,这是 ASan 对 memcpy 函数的拦截实现。
  6. 调用栈:调用栈显示了错误发生时的函数调用顺序。在这里,我们看到 memcpy 函数(第1行)被 main 函数(第2行)调用,这是错误发生的源头。
  7. 内存分配位置:出错的内存是在 main 函数的第4行分配的,由栈帧信息指出。
  8. Shadow Bytes:Shadow Bytes 是 ASan 使用的一块内存区域,用于跟踪实际内存的状态。这里的 Shadow Bytes 显示了出错地址附近的内存状态。
  9. 错误摘要SUMMARY: AddressSanitizer: stack-buffer-overflow 再次总结了错误类型。
  10. 程序退出==1680994==ABORTING 表示由于这个严重错误,程序被ASan中止执行。

在这个案例中,错误是由于 memcpy 写入的数据超出了分配的缓冲区大小,导致栈缓冲区溢出。

3.3 全局缓冲区溢出(Global Buffer Overflow)

案例描述:检测对全局变量边界以外的内存写入操作。

参考代码如下:

#include <stdio.h>
#include <string.h>char globalBuffer[5];int main() {strcpy(globalBuffer, "Hello, World!");printf("Global Buffer contents: %s\n", globalBuffer);return 0;
}

使用以下命令编译程序,并启用AddressSanitizer:

$gcc -fsanitize=address -g -o global_overflow global_overflow.c

然后运行编译出的程序:

$gdb ./global_overflow

执行后 得到如下调试信息:

[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
=================================================================
==1681772==ERROR: AddressSanitizer: global-buffer-overflow on address 0x555555558125 at pc 0x7ffff743a2c3 bp 0x7fffffffdbf0 sp 0x7fffffffd398
WRITE of size 14 at 0x555555558125 thread T0#0 0x7ffff743a2c2 in __interceptor_memcpy ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:827#1 0x55555555520e in main /media/wangdaosheng/data/backup/tmp/test2/global_overflow.c:7#2 0x7ffff7029d8f in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58#3 0x7ffff7029e3f in __libc_start_main_impl ../csu/libc-start.c:392#4 0x555555555124 in _start (/media/wangdaosheng/data/backup/tmp/test2/global_overflow+0x1124)0x555555558125 is located 0 bytes to the right of global variable 'globalBuffer' defined in 'global_overflow.c:4:6' (0x555555558120) of size 5
SUMMARY: AddressSanitizer: global-buffer-overflow ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:827 in __interceptor_memcpy
Shadow bytes around the buggy address:0x0aab2aaa2fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x0aab2aaa2fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x0aab2aaa2ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x0aab2aaa3000: 00 00 00 00 00 00 00 00 f9 f9 f9 f9 f9 f9 f9 f90x0aab2aaa3010: f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9
=>0x0aab2aaa3020: 00 00 00 00[05]f9 f9 f9 f9 f9 f9 f9 00 00 00 000x0aab2aaa3030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x0aab2aaa3040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x0aab2aaa3050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x0aab2aaa3060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x0aab2aaa3070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):Addressable:           00Partially addressable: 01 02 03 04 05 06 07 Heap left redzone:       faFreed heap region:       fdStack left redzone:      f1Stack mid redzone:       f2Stack right redzone:     f3Stack after return:      f5Stack use after scope:   f8Global redzone:          f9Global init order:       f6Poisoned by user:        f7Container overflow:      fcArray cookie:            acIntra object redzone:    bbASan internal:           feLeft alloca redzone:     caRight alloca redzone:    cbShadow gap:              cc
==1681772==ABORTING
[Inferior 1 (process 1681772) exited with code 01]

这个错误信息是由 AddressSanitizer(ASan)提供的。根据提供的信息,我们可以分析出以下关键信息:

  1. 错误类型AddressSanitizer: global-buffer-overflow 表示发生了全局缓冲区溢出。
  2. 错误位置:错误发生在地址 0x555555558125,这是一个全局变量的地址。
  3. 出错指令WRITE of size 14 at 0x555555558125 表示在地址 0x555555558125 处尝试写入 14 个字节的数据。
  4. 程序计数器(PC)、基指针(BP)和栈指针(SP)pc 0x7ffff743a2c3, bp 0x7fffffffdbf0, sp 0x7fffffffd398 提供了出错时的程序计数器、基指针和栈指针的值,这对于调试器来说是有用的。
  5. 出错函数:错误发生在 __interceptor_memcpy 函数中,这是 ASan 对 memcpy 函数的拦截实现。
  6. 调用栈:调用栈显示了错误发生时的函数调用顺序。在这里,我们看到 memcpy 函数(第1行)被 main 函数(第2行)调用,这是错误发生的源头。
  7. 内存分配位置:出错的内存是全局变量 globalBuffer,定义在 global_overlap.c:4:6,大小为 5 字节。
  8. Shadow Bytes:Shadow Bytes 是 ASan 使用的一块内存区域,用于跟踪实际内存的状态。这里的 Shadow Bytes 显示了出错地址附近的内存状态。
  9. 错误摘要SUMMARY: AddressSanitizer: global-buffer-overflow 再次总结了错误类型。
  10. 程序退出==1681772==ABORTING 表示由于这个严重错误,程序被ASan中止执行。

在这个案例中,错误是由于 memcpy 写入的数据超出了全局变量 globalBuffer 分配的大小,导致全局缓冲区溢出。

3.4 使用后释放(Use After Free)

案例描述:检测已经释放的内存被再次访问的情况。

参考代码如下:

#include <stdio.h>
#include <stdlib.h>int main() {int *ptr = (int *)malloc(sizeof(int));if (ptr == NULL) {fprintf(stderr, "Memory allocation failed\n");return 1;}*ptr = 42;printf("Value at ptr: %d\n", *ptr);free(ptr);printf("Value at ptr after free: %d\n", *ptr);return 0;
}

使用以下命令编译程序,并启用AddressSanitizer:

$gcc -fsanitize=address -g -o use_after_free use_after_free.c

然后运行编译出的程序:

$gdb ./use_after_free

执行后 得到如下调试信息:

[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Value at ptr: 42
=================================================================
==1682910==ERROR: AddressSanitizer: heap-use-after-free on address 0x602000000010 at pc 0x55555555539b bp 0x7fffffffd7a0 sp 0x7fffffffd790
READ of size 4 at 0x602000000010 thread T0#0 0x55555555539a in main /media/wangdaosheng/data/backup/tmp/test2/use_after_free.c:14#1 0x7ffff7029d8f in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58#2 0x7ffff7029e3f in __libc_start_main_impl ../csu/libc-start.c:392#3 0x5555555551c4 in _start (/media/wangdaosheng/data/backup/tmp/test2/use_after_free+0x11c4)0x602000000010 is located 0 bytes inside of 4-byte region [0x602000000010,0x602000000014)
freed by thread T0 here:#0 0x7ffff74b4537 in __interceptor_free ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:127#1 0x555555555363 in main /media/wangdaosheng/data/backup/tmp/test2/use_after_free.c:13#2 0x7ffff7029d8f in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58previously allocated by thread T0 here:#0 0x7ffff74b4887 in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:145#1 0x55555555529e in main /media/wangdaosheng/data/backup/tmp/test2/use_after_free.c:5#2 0x7ffff7029d8f in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58SUMMARY: AddressSanitizer: heap-use-after-free /media/wangdaosheng/data/backup/tmp/test2/use_after_free.c:14 in main
Shadow bytes around the buggy address:0x0c047fff7fb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x0c047fff7fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x0c047fff7fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x0c047fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x0c047fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c047fff8000: fa fa[fd]fa fa fa fa fa fa fa fa fa fa fa fa fa0x0c047fff8010: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa0x0c047fff8020: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa0x0c047fff8030: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa0x0c047fff8040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa0x0c047fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):Addressable:           00Partially addressable: 01 02 03 04 05 06 07 Heap left redzone:       faFreed heap region:       fdStack left redzone:      f1Stack mid redzone:       f2Stack right redzone:     f3Stack after return:      f5Stack use after scope:   f8Global redzone:          f9Global init order:       f6Poisoned by user:        f7Container overflow:      fcArray cookie:            acIntra object redzone:    bbASan internal:           feLeft alloca redzone:     caRight alloca redzone:    cbShadow gap:              cc
==1682910==ABORTING
[Inferior 1 (process 1682910) exited with code 01]

这个错误信息是由 AddressSanitizer(ASan)提供的。根据提供的信息,我们可以分析出以下关键信息:

  1. 错误类型AddressSanitizer: heap-use-after-free 表示发生了堆使用后释放的错误。
  2. 错误位置:错误发生在地址 0x602000000010,这是一个堆内存地址。
  3. 出错指令READ of size 4 at 0x602000000010 表示在地址 0x602000000010 处尝试读取 4 个字节的数据。
  4. 程序计数器(PC)、基指针(BP)和栈指针(SP)pc 0x55555555539b, bp 0x7fffffffd7a0, sp 0x7fffffffd790 提供了出错时的程序计数器、基指针和栈指针的值,这对于调试器来说是有用的。
  5. 出错函数:错误发生在 main 函数的第14行。
  6. 内存释放位置:出错的内存是在 main 函数的第13行被释放的。
  7. 内存分配位置:出错的内存是在 main 函数的第5行被分配的。
  8. Shadow Bytes:Shadow Bytes 是 ASan 使用的一块内存区域,用于跟踪实际内存的状态。这里的 Shadow Bytes 显示了出错地址附近的内存状态,其中 fd 表示已释放的堆内存区域。
  9. 错误摘要SUMMARY: AddressSanitizer: heap-use-after-free 再次总结了错误类型。
  10. 程序退出==1682910==ABORTING 表示由于这个严重错误,程序被ASan中止执行。

3.5 多线程数据竞争(Data Race)

案例描述:检测多线程程序中不同线程对同一数据的竞态访问。

参考代码如下:

#include <stdio.h>
#include <pthread.h>int global_var = 0;void* increment(void* arg) {int i;for (i = 0; i < 100; ++i) {global_var++; // 此处存在数据竞争}return NULL;
}int main() {pthread_t thread1, thread2;pthread_create(&thread1, NULL, increment, NULL);pthread_create(&thread2, NULL, increment, NULL);pthread_join(thread1, NULL);pthread_join(thread2, NULL);printf("Result: %d\n", global_var);return 0;
}

使用以下命令编译程序,并启用AddressSanitizer:

$gcc -fsanitize=thread -g -o data_race data_race.c -lpthread

然后运行编译出的程序:

$gdb ./data_race

执行后 得到如下调试信息:

[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[New Thread 0x7ffff51ff640 (LWP 2156986)]
[New Thread 0x7ffff49fe640 (LWP 2156987)]
[Thread 0x7ffff49fe640 (LWP 2156987) exited]
[New Thread 0x7ffff41fd640 (LWP 2156988)]
==================
WARNING: ThreadSanitizer: data race (pid=2156978)Read of size 4 at 0x555555558014 by thread T2:#0 increment /media/wangdaosheng/data/backup/tmp/test2/data_race.c:9 (data_race+0x129d)Previous write of size 4 at 0x555555558014 by thread T1:#0 increment /media/wangdaosheng/data/backup/tmp/test2/data_race.c:9 (data_race+0x12b5)Location is global 'global_var' of size 4 at 0x555555558014 (data_race+0x000000004014)Thread T2 (tid=2156988, running) created by main thread at:#0 pthread_create ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:969 (libtsan.so.0+0x605b8)#1 main /media/wangdaosheng/data/backup/tmp/test2/data_race.c:18 (data_race+0x133a)Thread T1 (tid=2156987, finished) created by main thread at:#0 pthread_create ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:969 (libtsan.so.0+0x605b8)#1 main /media/wangdaosheng/data/backup/tmp/test2/data_race.c:17 (data_race+0x131d)SUMMARY: ThreadSanitizer: data race /media/wangdaosheng/data/backup/tmp/test2/data_race.c:9 in increment
==================
Result: 200
ThreadSanitizer: reported 1 warnings
[Thread 0x7ffff41fd640 (LWP 2156988) exited]
[Thread 0x7ffff51ff640 (LWP 2156986) exited]
[Inferior 1 (process 2156978) exited with code 0102]

这个错误信息是由 ThreadSanitizer(TSan)提供的,它帮助检测多线程程序中的数据竞争问题。根据提供的信息,我们可以分析出以下关键点:

  1. 错误类型WARNING: ThreadSanitizer: data race 表示检测到数据竞争。
  2. 竞争位置:数据竞争发生在地址 0x555555558014,这是全局变量 global_var 的地址。
  3. 出错指令Read of size 4 at 0x555555558014 by thread T2Previous write of size 4 at 0x555555558014 by thread T1 表示线程 T2 读取了 global_var,而线程 T1 在之前写了 global_var
  4. 竞争变量:竞争的变量是全局变量 global_var,大小为 4 字节。
  5. 线程信息:线程 T2(tid=2156988)和线程 T1(tid=2156987)都参与了这次数据竞争。
  6. 线程创建:两个线程都是由主线程(main thread)创建的,创建位置在 main 函数中。
  7. 错误摘要SUMMARY: ThreadSanitizer: data race 再次总结了错误类型。
  8. 程序退出[Inferior 1 (process 2156978) exited with code 0102] 表示程序由于TSan报告的错误而退出。

在这个案例中,错误是由于两个线程同时访问和修改全局变量 global_var 导致的。

相关文章:

android 性能分析工具(04)Asan 内存检测工具

1 Asan工具简介 1.1 Asan工具历史背景 AddressSanitizer&#xff08;ASan&#xff09;最初由Google开发&#xff0c;并作为LLVM项目的一部分。ASan的设计目的是帮助开发者检测并修复内存错误&#xff0c;如堆栈和全局缓冲区溢出、使用已释放的内存等&#xff0c;这些问题可能…...

html中select标签的选项携带多个值

搜索参考资料&#xff1a;SELECT标签中的选项可以携带多个值吗&#xff1f; 【摘抄】&#xff1a; 它可能有一个select选项中的多个值&#xff0c;如下所示。 <select id"ddlEmployee" class"form-control"> <option value"">-- S…...

Lambda表达式如何进行调试

一、概述 Java8提供了lambda表达式&#xff0c;方便我们对数据集合进行操作&#xff0c;我们使用lambda表达式的时候&#xff0c;是不是有这样的疑问&#xff0c;如何对执行过程中的中间数据进行调试呢&#xff1f; 二、例子 在下面的例子中&#xff0c;我们实现随机最多生成…...

C++ —— 剑斩旧我 破茧成蝶—C++11

江河入海&#xff0c;知识涌动&#xff0c;这是我参与江海计划的第2篇。 目录 1. C11的发展历史 2. 列表初始化 2.1 C98传统的{} 2.2 C11中的{} 2.3 C11中的std::initializer_list 3. 右值引用和移动语义 3.1 左值和右值 3.2 左值引用和右值引用 3.3 引用延长生命周期…...

HTML5好看的音乐播放器多种风格(附源码)

文章目录 1.设计来源1.1 音乐播放器风格1效果1.2 音乐播放器风格2效果1.3 音乐播放器风格3效果1.4 音乐播放器风格4效果1.5 音乐播放器风格5效果 2.效果和源码2.1 动态效果2.2 源代码 源码下载万套模板&#xff0c;程序开发&#xff0c;在线开发&#xff0c;在线沟通 作者&…...

C++设计模式行为模式———迭代器模式中介者模式

文章目录 一、引言二、中介者模式三、总结 一、引言 中介者模式是一种行为设计模式&#xff0c; 能让你减少对象之间混乱无序的依赖关系。 该模式会限制对象之间的直接交互&#xff0c; 迫使它们通过一个中介者对象进行合作。 中介者模式可以减少对象之间混乱无序的依赖关系&…...

FFmpeg 4.3 音视频-多路H265监控录放C++开发十五,解码相关,将h264文件进行帧分隔变成avpacket

前提 前面我们学习了 将YUV数据读取到AVFrame&#xff0c;然后将AVFrame通过 h264编码器变成 AVPacket后&#xff0c;然后将avpacket直接存储到了本地就变成了h264文件。 这一节课&#xff0c;学习解码的一部分。我们需要将 本地存储的h264文件进行帧分隔&#xff0c;也就是变…...

力扣 LeetCode 104. 二叉树的最大深度(Day7:二叉树)

解题思路&#xff1a; 采用后序遍历 首先要区别好什么是高度&#xff0c;什么是深度 最大深度实际上就是根节点的高度 高度的求法是从下往上传&#xff0c;从下往上传实际上就是左右中&#xff08;后序遍历&#xff09; 深度的求法是从上往下去寻找 所以采用从下往上 本…...

如何高效实现汤臣倍健营销云数据集成到SQLServer

新版订单同步-&#xff08;Life-Space&#xff09;江油泰熙&#xff1a;汤臣倍健营销云数据集成到SQL Server 在企业信息化建设中&#xff0c;数据的高效集成和管理是提升业务运营效率的关键。本文将分享一个实际案例——如何通过新版订单同步方案&#xff0c;将汤臣倍健营销云…...

Vue3中使用:deep修改element-plus的样式无效怎么办?

前言&#xff1a;当我们用 vue3 :deep() 处理 elementui 中 el-dialog_body和el-dislog__header 的时候样式一直无法生效&#xff0c;遇到这种情况怎么办&#xff1f; 解决办法&#xff1a; 1.直接在 dialog 上面增加class 我试过&#xff0c;也不起作用&#xff0c;最后用这种…...

具身智能之Isaac Gym使用

0. 简介 Isaac Gym 是由 NVIDIA 提供的一个高性能仿真平台&#xff0c;专门用于大规模的机器人学习和强化学习&#xff08;RL&#xff09;任务。它结合了物理仿真、GPU加速、深度学习框架互操作性等特点&#xff0c;使得研究人员和开发者可以快速进行复杂的机器人仿真和训练。…...

【大数据学习 | Spark】spark-shell开发

spark的代码分为两种 本地代码在driver端直接解析执行没有后续 集群代码&#xff0c;会在driver端进行解析&#xff0c;然后让多个机器进行集群形式的执行计算 spark-shell --master spark://nn1:7077 --executor-cores 2 --executor-memory 2G sc.textFile("/home/ha…...

《Python制作动态爱心粒子特效》

一、实现思路 粒子效果&#xff1a; – 使用Pygame模拟粒子运动&#xff0c;粒子会以爱心的轨迹分布并运动。爱心公式&#xff1a; 爱心的数学公式&#xff1a; x16sin 3 (t),y13cos(t)−5cos(2t)−2cos(3t)−cos(4t) 参数 t t 的范围决定爱心形状。 动态效果&#xff1a; 粒子…...

Jmeter 如何导入证书并调用https请求

Jmeter 如何导入证书并调用https请求 通过SSL管理器添加证书文件 支持添加的文件为.p12&#xff0c;.pfx&#xff0c;.jks 如何将pem文件转换为pfx文件&#xff1f; 在公司内部通常会提供3个pem文件。 ca.pem&#xff1a;可以理解为是根证书&#xff0c;用于验证颁发的证…...

Python程序15个提速优化方法

目录 Python程序15个提速优化方法1. 引言2. 方法一&#xff1a;使用内建函数代码示例&#xff1a;解释&#xff1a; 3. 方法二&#xff1a;避免使用全局变量代码示例&#xff1a;解释&#xff1a; 4. 方法三&#xff1a;使用局部变量代码示例&#xff1a;解释&#xff1a; 5. 方…...

足球虚拟越位线技术FIFA OT(二)

足球虚拟越位线技术FIFA OT&#xff08;二&#xff09; 在FIFA认证测试过程中&#xff0c;留给VAR系统绘制越位线的时间只有90秒&#xff08;在比赛中时间可能更短&#xff09;&#xff0c;那么90秒内要做什么事呢&#xff0c;首先场地上球员做出踢球动作&#xff0c;然后VAR要…...

centos7.9单机版安装K8s

1.安装docker [rootlocalhost ~]# hostnamectl set-hostname master [rootlocalhost ~]# bash [rootmaster ~]# mv /etc/yum.repos.d/* /home [rootmaster ~]# curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo [rootmaster ~]# cu…...

图像编辑一些概念:Image Reconstruction与Image Re-generation

图像编辑本质上是在“图像重建”&#xff08;image reconstruction&#xff09;和“图像再生成”&#xff08;image re-generation&#xff09;之间寻找平衡。 1. Image Reconstruction&#xff08;图像重建&#xff09; 定义&#xff1a;图像重建通常是指从已有的图像中提取信…...

【STM32】在 STM32 USB 设备库添加新的设备类

说实话&#xff0c;我非常想吐槽 STM32 的 USB device library&#xff0c;总感觉很混乱。 USB Device library architecture 根据架构图&#xff1a; Adding a custom class 如果你想添加新的设备类&#xff0c;必须修改的文件有 usbd_desc.cusbd_conf.cusb_device.c 需要…...

【Redis】Redis实现的消息队列

一、用list实现【这是数据类型所以支持持久化】 消息基于redis存储不会因为受jvm内存上限的限制&#xff0c;支持消息的有序性&#xff0c;基于redis的持久化机制&#xff0c;只支持单一消费者订阅&#xff0c;无法避免消息丢失。 二、用PubSub【这不是数据类型&#xff0c;是…...

# Spring事务

Spring事务 什么是spring的事务&#xff1f; 在Spring框架中&#xff0c;事务管理是一种控制数据库操作执行边界的技术&#xff0c;确保一系列操作要么全部成功&#xff0c;要么全部失败&#xff0c;从而维护数据的一致性和完整性。Spring的事务管理主要关注以下几点&#xf…...

Java学习笔记--数组常见算法:数组翻转,冒泡排序,二分查找

一&#xff0c;数组翻转 1.概述:数组对称索引位置上的元素互换&#xff0c;最大值数组序号是数组长度减一 创建跳板temp&#xff0c;进行min和max的互换&#xff0c;然后min自增&#xff0c;max自减&#xff0c;当min>max的时候停止互换&#xff0c;代表到中间值 用代码实…...

ARM 架构(Advanced RISC Machine)精简指令集计算机(Reduced Instruction Set Computer)

文章目录 1、ARM 架构ARM 架构的特点ARM 架构的应用ARM 架构的未来发展 2、RISCRISC 的基本概念RISC 的优势RISC 的应用RISC 与 CISC 的对比总结 1、ARM 架构 ARM 架构是一种低功耗、高性能的处理器架构&#xff0c;广泛应用于移动设备、嵌入式系统以及越来越多的服务器和桌面…...

7.STM32之通信接口《精讲》之USART通信---多字节数据收发(数据包的模式:HEX数据包和文本数据包)

根据上一节的HEX数据包的设计完成&#xff0c;本节将完成文本数据包的编写&#xff0c;&#xff08;HEX数据包其实本质就是原始数据&#xff0c;文本数据包我么要接收到还要对照ASCll进行解析封装&#xff09; 有不懂的可参考上一节的讲解&#xff01;&#xff01;&#xff…...

基于Vue+SpringBoot的求职招聘平台

平台概述 本平台是一个高效、便捷的人才与职位匹配系统&#xff0c;旨在为求职者与招聘者提供一站式服务。平台内设三大核心角色&#xff1a;求职者、招聘者以及超级管理员&#xff0c;每个角色拥有独特的功能模块&#xff0c;确保用户能够轻松完成从信息获取到最终录用的整个…...

WebRTC 和 WebSocket

WebRTC 和 WebSocket 是两种不同的技术&#xff0c;虽然它们都用于在浏览器之间进行通信&#xff0c;但它们的设计目标和使用场景有所不同。以下是它们之间的主要区别&#xff1a; 目的和使用场景 WebRTC: 主要用于实现实时音视频通信。 支持点对点&#xff08;P2P&#xff09…...

小车综合玩法--5.画地为牢

一、实验准备 前面我们利用四路巡线模块巡线&#xff0c;现在我们利用这个特性&#xff0c;用黑线将小车围起来&#xff0c;让小车一直在我们围的圈内运动。 1.小车接线已安装&#xff0c;且安装正确 2.调试四路巡线模块遇黑线时指示灯亮。不是黑线时指示灯灭。 二、实验原理…...

数据库课程设计全流程:方法与实例解析

--- ### 一、数据库课程设计概述 数据库课程设计是学习数据库理论知识的重要实践环节&#xff0c;旨在帮助学生掌握数据库设计和应用系统开发的完整流程&#xff0c;包括需求分析、数据库设计、功能实现以及性能优化。 #### **设计目标** 1. 掌握数据库设计的基本步骤和原则…...

用Ruby编写一个自动化测试脚本,验证网站登录功能的正确性。

测试准备&#xff1a;从江河湖海到代码世界的奇妙之旅 亲爱的朋友们&#xff0c;你们好&#xff01;今天我要带你们进入一个神奇的世界——测试的世界。在这里&#xff0c;我们将会看到各种各样的测试用例&#xff0c;它们就像江河湖海一样&#xff0c;汇聚在一起&#xff0c;…...

跳表 | 基本概念 | 代码实现

文章目录 1.跳表的基本概念2.跳表的结构3.跳表的增删改查4.完整代码 1.跳表的基本概念 跳表的本质是一种查找结构&#xff0c;一般查找问题的解法分为两个大类&#xff1a;一个是基于各种平衡树&#xff0c;一个是基于哈希表&#xff0c;跳表比较的特殊&#xff0c;它独成一派…...