C/C++内存检测工具Sanitizers

Sanitizers介绍

Sanitizers 是谷歌开源的内存检测工具,包括AddressSanitizer、MemorySanitizer、ThreadSanitizer、LeakSanitizer。
Sanitizers是LLVM的一部分。
gcc4.8:支持Address和Thread Sanitizer。
gcc4.9:支持Leak Sanitizer和UBSanitizer。

注意:gcc不支持MemorySanitizer。

可以支持的内存检测问题:

(1)heap use after free 堆内存释放后继续使用

(2)stack use after return 栈内存函数返回后继续使用

(3)stack use after scope 栈内存在作用域范围外继续使用

(4)heap buffer overflow 堆内存溢出

(5)stack buffer overflow 栈内存溢出

(6)global buffer overflow 全局内存溢出

(7)allocation size too big 堆内存分配较大

(8)memory leaks 内存泄露

(9)double free 堆内存重复释放

(10)initialization order bugs 初始化命令错误

编译参数:
(1)(2)(3)(4)(5)(6)(7):-fsanitize=address
(8):-fsanitize=address 或者 -fsanitize=leak
(9):-fsanitize=address
(10):-fsanitize=memory

编译参数通过 -fsanitize 决定开启 sanitizer:

-fsanitize=address
开启AddressSanitizer(ASan),包括LeakSanitizer(LSan),检测:地址越界 和 内存泄漏。

-fsanitize=leak
开启LeakSanitizer(LSan),检测:内存泄漏。

-fsanitize=address 和 -fsanitize=leak 都能检测 内存泄漏。

-fsanitize=thread
开启ThreadSanitizer(TSan),检测:数据竞争和死锁。

-fsanitize=undefined
开启UndefinedBehaviorSanitizer(UBSsan),检测:未定义行为。

-fsanitize=memory
开启MemorySanitizer(MSan),检测:未初始化内存问题。(gcc不支持MemorySanitizer)

-fno-omit-frame-pointer
检测到内存错误时打印函数调用栈,这个参数一直都带上。

检测案例

编译器:gcc/g++

(1)heap use after free 堆内存释放后继续使用

#include <stdio.h>
int main (int argc, char* argv[]) {
    int* p = new int[100];
    delete [] p;
    int num = p[0];
    return 0;
}

编译命令:
g++ -o out main.cpp -g -fsanitize=address -fno-omit-frame-pointer

执行命令:
./out

=================================================================
==10606==ERROR: AddressSanitizer: heap-use-after-free on address 0x61400000fe40 at pc 0x00000040075d bp 0x7ffe7a4adfe0 sp 0x7ffe7a4adfd8
READ of size 4 at 0x61400000fe40 thread T0
    #0 0x40075c in main /home/code/main.cpp:5
    #1 0x7efec0def504 in __libc_start_main (/lib64/libc.so.6+0x22504)
    #2 0x400628  (/home/code/out+0x400628)

0x61400000fe40 is located 0 bytes inside of 400-byte region [0x61400000fe40,0x61400000ffd0)
freed by thread T0 here:
    #0 0x7efec1ac1f2a in operator delete[](void*) ../../../../libsanitizer/asan/asan_new_delete.cc:96
    #1 0x400725 in main /home/code/main.cpp:4
    #2 0x7efec0def504 in __libc_start_main (/lib64/libc.so.6+0x22504)

previously allocated by thread T0 here:
    #0 0x7efec1ac19ea in operator new[](unsigned long) ../../../../libsanitizer/asan/asan_new_delete.cc:62
    #1 0x40070e in main /home/code/main.cpp:3
    #2 0x7efec0def504 in __libc_start_main (/lib64/libc.so.6+0x22504)

SUMMARY: AddressSanitizer: heap-use-after-free /home/code/main.cpp:5 main
Shadow bytes around the buggy address:
  0x0c287fff9f70: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c287fff9f80: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c287fff9f90: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c287fff9fa0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c287fff9fb0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
=>0x0c287fff9fc0: fa fa fa fa fa fa fa fa[fd]fd fd fd fd fd fd fd
  0x0c287fff9fd0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c287fff9fe0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c287fff9ff0: fd fd fd fd fd fd fd fd fd fd fa fa fa fa fa fa
  0x0c287fffa000: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c287fffa010: 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:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:       fa
  Heap right redzone:      fb
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack partial redzone:   f4
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
==10606==ABORTING

(2)stack use after return 栈内存函数返回后继续使用

#include <stdio.h>
int* p = NULL;
void fun() {
    int a[10];
    p = a; // 或者 p = &a[0];
}
int main(int argc, char* argv[]) {
    fun();
    int num = p[0];
    return 0;
}

编译命令:
g++ -o out main.cpp -g -fsanitize=address -fno-omit-frame-pointer

执行命令:
ASAN_OPTIONS=detect_stack_use_after_return=1 ./out

注意: 默认没有开启,需要在运行时开启。

=================================================================
==18619==ERROR: AddressSanitizer: stack-use-after-return on address 0x7f4f81500020 at pc 0x0000004008ea bp 0x7fffb49f6700 sp 0x7fffb49f66f8
WRITE of size 4 at 0x7f4f81500020 thread T0
    #0 0x4008e9 in main /home/code/main.cpp:9
    #1 0x7f4f84a7a504 in __libc_start_main (/lib64/libc.so.6+0x22504)
    #2 0x400708  (/home/code/out+0x400708)

Address 0x7f4f81500020 is located in stack of thread T0 at offset 32 in frame
    #0 0x4007e5 in fun() /home/code/main.cpp:3

  This frame has 1 object(s):
    [32, 72) 'a' <== Memory access at offset 32 is inside this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext
      (longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-use-after-return /home/code/main.cpp:9 main
Shadow bytes around the buggy address:
  0x0fea70297fb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0fea70297fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0fea70297fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0fea70297fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0fea70297ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0fea70298000: f5 f5 f5 f5[f5]f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5
  0x0fea70298010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0fea70298020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0fea70298030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0fea70298040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0fea70298050: 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:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:       fa
  Heap right redzone:      fb
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack partial redzone:   f4
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
==18619==ABORTING

(3)stack use after scope 栈内存在作用域范围外继续使用

暂时还没有找到能够检测出来的样例。

(4)heap buffer overflow 堆内存溢出

#include <stdio.h>
int main (int argc, char* argv[]) {
    int* p = new int[100];
    int num = p[100];
    delete [] p;
    return 0;
}

编译命令:
g++ -o out main.cpp -g -fsanitize=address -fno-omit-frame-pointer

执行命令:
./out

=================================================================
==9226==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x61400000ffd0 at pc 0x000000400750 bp 0x7ffef70c5d80 sp 0x7ffef70c5d78
READ of size 4 at 0x61400000ffd0 thread T0
    #0 0x40074f in main /home/code/main.cpp:4
    #1 0x7f12e7482504 in __libc_start_main (/lib64/libc.so.6+0x22504)
    #2 0x400628  (/home/code/out+0x400628)

0x61400000ffd0 is located 0 bytes to the right of 400-byte region [0x61400000fe40,0x61400000ffd0)
allocated by thread T0 here:
    #0 0x7f12e81549ea in operator new[](unsigned long) ../../../../libsanitizer/asan/asan_new_delete.cc:62
    #1 0x40070e in main /home/code/main.cpp:3
    #2 0x7f12e7482504 in __libc_start_main (/lib64/libc.so.6+0x22504)

SUMMARY: AddressSanitizer: heap-buffer-overflow /home/code/main.cpp:4 main
Shadow bytes around the buggy address:
  0x0c287fff9fa0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c287fff9fb0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c287fff9fc0: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00
  0x0c287fff9fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c287fff9fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c287fff9ff0: 00 00 00 00 00 00 00 00 00 00[fa]fa fa fa fa fa
  0x0c287fffa000: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c287fffa010: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c287fffa020: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c287fffa030: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c287fffa040: 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:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:       fa
  Heap right redzone:      fb
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack partial redzone:   f4
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
==9226==ABORTING

(5)stack buffer overflow 栈内存溢出

#include <stdio.h>
int main(int argc, char* argv[]) {
    int a[2] = {100,200};
    int num = a[2];
    return 0;
}

编译命令:
g++ -o out main.cpp -g -fsanitize=address -fno-omit-frame-pointer

执行命令:
./out

=================================================================
==787==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffcade3daa8 at pc 0x0000004007df bp 0x7ffcade3da50 sp 0x7ffcade3da48
READ of size 4 at 0x7ffcade3daa8 thread T0
    #0 0x4007de in main /home/code/main.cpp:4
    #1 0x7f8c39562504 in __libc_start_main (/lib64/libc.so.6+0x22504)
    #2 0x400648  (/home/code/out+0x400648)

Address 0x7ffcade3daa8 is located in stack of thread T0 at offset 40 in frame
    #0 0x400725 in main /home/code/main.cpp:2

  This frame has 1 object(s):
    [32, 40) 'a' <== Memory access at offset 40 overflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext
      (longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow /home/code/main.cpp:4 main
Shadow bytes around the buggy address:
  0x100015bbfb00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100015bbfb10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100015bbfb20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100015bbfb30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100015bbfb40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x100015bbfb50: f1 f1 f1 f1 00[f4]f4 f4 f3 f3 f3 f3 00 00 00 00
  0x100015bbfb60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100015bbfb70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100015bbfb80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100015bbfb90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100015bbfba0: 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:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:       fa
  Heap right redzone:      fb
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack partial redzone:   f4
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
==787==ABORTING

(6)global buffer overflow 全局内存溢出

#include <stdio.h>
int a[100];
int main(int argc, char* argv[]) {
    int num = a[100];
    return 0;
}

编译命令:
g++ -o out main.cpp -g -fsanitize=address -fno-omit-frame-pointer

执行命令:
./out

=================================================================
==9628==ERROR: AddressSanitizer: global-buffer-overflow on address 0x000000601270 at pc 0x000000400755 bp 0x7ffd15bcd0d0 sp 0x7ffd15bcd0c8
READ of size 4 at 0x000000601270 thread T0
    #0 0x400754 in main /home/code/main.cpp:4
    #1 0x7fc3d46e9504 in __libc_start_main (/lib64/libc.so.6+0x22504)
    #2 0x400648  (/home/code/out+0x400648)

0x000000601270 is located 0 bytes to the right of global variable 'array' defined in 'main.cpp:2:5' (0x6010e0) of size 400
SUMMARY: AddressSanitizer: global-buffer-overflow /home/code/main.cpp:4 main
Shadow bytes around the buggy address:
  0x0000800b81f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0000800b8200: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0000800b8210: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0000800b8220: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0000800b8230: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0000800b8240: 00 00 00 00 00 00 00 00 00 00 00 00 00 00[f9]f9
  0x0000800b8250: f9 f9 f9 f9 00 00 00 00 00 00 00 00 00 00 00 00
  0x0000800b8260: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0000800b8270: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0000800b8280: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0000800b8290: 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:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:       fa
  Heap right redzone:      fb
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack partial redzone:   f4
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
==9628==ABORTING

(7)allocation-size-too-big 堆内存分配较大

#include <stdlib.h>
int main(int argc, char* argv[]) {
    int x = 1000;
    int y = 1000;
    int* p = (int*)malloc(x * y * x * y);
    free(p);
    return 0;
}

编译命令:
g++ -o out main.cpp -g -fsanitize=address -fno-omit-frame-pointer

执行命令:
./out

==8628==WARNING: AddressSanitizer failed to allocate 0xffffffffd4a51000 bytes
==8628==AddressSanitizer's allocator is terminating the process instead of returning 0
==8628==If you don't like this behavior set allocator_may_return_null=1
==8628==AddressSanitizer CHECK failed: ../../../../libsanitizer/sanitizer_common/sanitizer_allocator.cc:147 "((0)) != (0)" (0x0, 0x0)
    #0 0x7f0f1ba1e4f4 in AsanCheckFailed ../../../../libsanitizer/asan/asan_rtl.cc:68
    #1 0x7f0f1ba22f53 in __sanitizer::CheckFailed(char const*, int, char const*, unsigned long long, unsigned long long) ../../../../libsanitizer/sanitizer_common/sanitizer_common.cc:72
    #2 0x7f0f1b99ff81 in __sanitizer::AllocatorReturnNull() ../../../../libsanitizer/sanitizer_common/sanitizer_allocator.cc:147
    #3 0x7f0f1ba211d5 in __sanitizer::AllocatorReturnNull() ../../../../libsanitizer/sanitizer_common/sanitizer_allocator.cc:141
    #4 0x7f0f1b9a563d in Allocate ../../../../libsanitizer/asan/asan_allocator2.cc:298
    #5 0x7f0f1ba16b08 in __interceptor_malloc ../../../../libsanitizer/asan/asan_malloc_linux.cc:39
    #6 0x4006cb in main /home/taomengen/code/7_allocation_size_too_big/main.cpp:7
    #7 0x7f0f1ad45504 in __libc_start_main (/lib64/libc.so.6+0x22504)
    #8 0x4005c8  (/home/taomengen/code/7_allocation_size_too_big/out+0x4005c8)

(8)memory leaks 内存泄露

#include <stdlib.h>
int main(int argc, char *argv[]) {
    int* p = (int*)malloc(10 * sizeof(int));
    return 0;
}

编译命令:
g++ -o out main.cpp -g -fsanitize=address -fno-omit-frame-pointer
或者
g++ -o out main.cpp -g -fsanitize=leak -fno-omit-frame-pointer

执行命令:
./out

=================================================================
==12458==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 40 byte(s) in 1 object(s) allocated from:
    #0 0x7f36642dc5e6 in __interceptor_malloc ../../../../libsanitizer/lsan/lsan_interceptors.cc:51
    #1 0x400611 in main /home/code/main.cpp:3
    #2 0x7f3663692504 in __libc_start_main (/lib64/libc.so.6+0x22504)

SUMMARY: LeakSanitizer: 40 byte(s) leaked in 1 allocation(s).

(9)double free 堆内存重复释放

#include <stdio.h>
int main(int argc, char* argv[]) {
    int* p = new int[10];
    delete [] p;
    delete [] p;
    return 0;
}

编译命令:
g++ -o out main.cpp -g -fsanitize=address -fno-omit-frame-pointer

执行命令:
./out

=================================================================
==31497==ERROR: AddressSanitizer: attempting double-free on 0x60400000dfd0 in thread T0:
    #0 0x7fa32a746f2a in operator delete[](void*) ../../../../libsanitizer/asan/asan_new_delete.cc:96
    #1 0x4006d8 in main /home/code/main.cpp:5
    #2 0x7fa329a74504 in __libc_start_main (/lib64/libc.so.6+0x22504)
    #3 0x4005c8  (/home/code/out+0x4005c8)

0x60400000dfd0 is located 0 bytes inside of 40-byte region [0x60400000dfd0,0x60400000dff8)
freed by thread T0 here:
    #0 0x7fa32a746f2a in operator delete[](void*) ../../../../libsanitizer/asan/asan_new_delete.cc:96
    #1 0x4006c5 in main /home/code/main.cpp:4
    #2 0x7fa329a74504 in __libc_start_main (/lib64/libc.so.6+0x22504)

previously allocated by thread T0 here:
    #0 0x7fa32a7469ea in operator new[](unsigned long) ../../../../libsanitizer/asan/asan_new_delete.cc:62
    #1 0x4006ae in main /home/code/main.cpp:3
    #2 0x7fa329a74504 in __libc_start_main (/lib64/libc.so.6+0x22504)

SUMMARY: AddressSanitizer: double-free ../../../../libsanitizer/asan/asan_new_delete.cc:96 operator delete[](void*)
==31497==ABORTING

(10)initialization order bugs 初始化命令错误

暂时还没有找到能够检测出来的样例。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值