• GCC编译选项Sanitier问题定位记录


    关键词:Address sanitizer、Use after free、Heap buffer overflow、Stack buffer overflow、Memory leak等等。

    操作系统:Ubuntu 16.04;g++ (Ubuntu 4.8.5-4ubuntu2) 4.8.5;clang version 3.8.0-2ubuntu4 (tags/RELEASE_380/final)。

    1. Sanitizer简介

    Sanitizers是谷歌发起的开源工具集,包括了AddressSanitizer, MemorySanitizer, ThreadSanitizer, LeakSanitizer,Sanitizers项目本是LLVM项目的一部分,但GNU也将该系列工具加入到了自家的GCC编译器中。GCC从4.8版本开始支持Address和Thread Sanitizer,4.9版本开始支持Leak Sanitizer和UB Sanitizer,这些都是查找隐藏Bug的利器。

    AddressSanitizer检查地址相关问题,包括释放后使用、重复释放、堆溢出、栈溢出等等问题。

    LeakSanitizer检查内存泄漏问题。

    ThreadSanitizer检查线程数据竞争和死锁问题。

    MemorySanitizer检查使用未初始化内存问题。

    其他还包括HWSANUBSan

    内核Sanitizer包括KASANKMSAN,分别提供内核中动态内存错误检查和未初始化内存使用问题检查。

    2. AddressSanitizer

    由于编译器版本不同,Address Sanitizer的支持不尽相同。下面记录部分功能测试结果。

    2.1 Use after free

    Use after free检测到使用的内存已经被释放导致的错误类型。当内存被释放后,所有数据置成0xfd。如果对应内存是0xfd表示可能使用了被释放的内存。

    使用g++ use_after_free.cc -o use_after_free -ggdb -fsanitize=address编译:

    // RUN: g++ use_after_free.cc -o use_after_free -ggdb -fsanitize=address
    
    int main(int argc, char **argv) {
      int *array = new int[100];
      delete [] array;
      return array[argc];  // BOOM
    }

    执行结果如下,其中地址和文件对应转换通过addr2line进行。

    比如0x4007f8地址,addr2line -a 0x4007f8 -e use_after_free得到结果在use_after_free.cc的第6行。

    =================================================================
    ==2708== ERROR: AddressSanitizer: heap-use-after-free on address 0x602e0001fc64 at pc 0x4007f9 bp 0x7ffd8b8b0830 sp 0x7ffd8b8b0828----错误类型说明是对堆的释放后使用,调用地址在0x4007f9.
    READ of size 4 at 0x602e0001fc64 thread T0
        #0 0x4007f8 (/home/al/temp/address_sanitizer/use_after_free+0x4007f8)---------------------------错误现场在use_after_free的0x4007f8,在第6行。
        #1 0x7fcbf515582f (/lib/x86_64-linux-gnu/libc-2.23.so+0x2082f)
        #2 0x4006b8 (/home/al/temp/address_sanitizer/use_after_free+0x4006b8)
    0x602e0001fc64 is located 4 bytes inside of 400-byte region [0x602e0001fc60,0x602e0001fdf0)
    freed by thread T0 here:
        #0 0x7fcbf551083a (/usr/lib/x86_64-linux-gnu/libasan.so.0.0.0+0x1183a)
        #1 0x4007ac (/home/al/temp/address_sanitizer/use_after_free+0x4007ac)---------------------------释放现场,通过addr2line得到对应第5行。
        #2 0x7fcbf515582f (/lib/x86_64-linux-gnu/libc-2.23.so+0x2082f)
    previously allocated by thread T0 here:
        #0 0x7fcbf551067a (/usr/lib/x86_64-linux-gnu/libasan.so.0.0.0+0x1167a)
        #1 0x400795 (/home/al/temp/address_sanitizer/use_after_free+0x400795)---------------------------申请现场,通过addr2line得到对应第4行。
        #2 0x7fcbf515582f (/lib/x86_64-linux-gnu/libc-2.23.so+0x2082f)
    Shadow bytes around the buggy address:
      0x0c063fffbf30: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
      0x0c063fffbf40: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
      0x0c063fffbf50: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
      0x0c063fffbf60: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
      0x0c063fffbf70: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
    =>0x0c063fffbf80: fa fa fa fa fa fa fa fa fa fa fa fa[fd]fd fd fd------------------------------------[]表示异常点,0xfd表示此段内存已经被释放。
      0x0c063fffbf90: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
      0x0c063fffbfa0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
      0x0c063fffbfb0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fa fa------------------------------------一个shadow字节表示8个字节,共50个0xfd,对应400个字节,也即分配的array大小。
      0x0c063fffbfc0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
      0x0c063fffbfd0: 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 righ 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
      ASan internal:         fe
    ==2708== ABORTING

    可见AddressSanitizer对内存区域进行特殊标记,如果和预期不符合则表示内存被破坏。

    2.2 Heap buffer overflow

    Heap buffer overflow表示堆内存溢出,使用0xfa和0xfb表示堆的左右边界。如果对应内存为0xfa或者0xfb表示可能是堆溢出。

    // RUN: g++ heap_buffer_overflow.cc -o heap_buffer_overflow -ggdb -fsanitize=address
    
    int main(int argc, char **argv) {
      int *array = new int[100];
      array[0] = 0;
      int res = array[argc + 100];  // BOOM
      delete [] array;
      return res;
    }

     结果如下: 

    =================================================================
    ==4852== ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602e0001fdf4 at pc 0x40087b bp 0x7fff2f0d10c0 sp 0x7fff2f0d10b8
    READ of size 4 at 0x602e0001fdf4 thread T0
        #0 0x40087a (/home/al/temp/address_sanitizer/heap_buffer_overflow+0x40087a)-------------------异常地址在0x40087a,对应第6行。
        #1 0x7fadfc16582f (/lib/x86_64-linux-gnu/libc-2.23.so+0x2082f)
        #2 0x400708 (/home/al/temp/address_sanitizer/heap_buffer_overflow+0x400708)
    0x602e0001fdf4 is located 4 bytes to the right of 400-byte region [0x602e0001fc60,0x602e0001fdf0)
    allocated by thread T0 here:
        #0 0x7fadfc52067a (/usr/lib/x86_64-linux-gnu/libasan.so.0.0.0+0x1167a)
        #1 0x4007e5 (/home/al/temp/address_sanitizer/heap_buffer_overflow+0x4007e5)
        #2 0x7fadfc16582f (/lib/x86_64-linux-gnu/libc-2.23.so+0x2082f)
    Shadow bytes around the buggy address:
      0x0c063fffbf60: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
      0x0c063fffbf70: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
      0x0c063fffbf80: fa fa fa fa fa fa fa fa fa fa fa fa 00 00 00 00
      0x0c063fffbf90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
      0x0c063fffbfa0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    =>0x0c063fffbfb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00[fa]fa------------------------0xfa是堆的redzone,访问到这里的内存表示堆越界访问了。
      0x0c063fffbfc0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
      0x0c063fffbfd0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
      0x0c063fffbfe0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
      0x0c063fffbff0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
      0x0c063fffc000: 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 righ 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
      ASan internal:         fe
    ==4852== ABORTING

    2.3 Stack buffer overflow

     Stack buffer overflow表示栈的访问超出边界,通过0xf1/0xf3/0xf4来判定。如果访问到0xf1/0xf3/0xf4则表示有可能超出栈边界的访问。

    // RUN: g++ stack_buffer_overflow.cc -o stack_buffer_overflow -ggdb -fsanitize=address
    
    int main(int argc, char **argv) {
      int stack_array[100];
      stack_array[1] = 0;
      return stack_array[argc + 100];  // BOOM
    }

     结果如下: 

    =================================================================
    ==6196== ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffe96edf094 at pc 0x400821 bp 0x7ffe96edeec0 sp 0x7ffe96edeeb8
    READ of size 4 at 0x7ffe96edf094 thread T0
        #0 0x400820 (/home/al/temp/address_sanitizer/stack_buffer_overflow+0x400820)
        #1 0x7f9f0ba9e82f (/lib/x86_64-linux-gnu/libc-2.23.so+0x2082f)
        #2 0x400678 (/home/al/temp/address_sanitizer/stack_buffer_overflow+0x400678)
    Address 0x7ffe96edf094 is located at offset 436 in frame <main> of T0's stack:
      This frame has 1 object(s):
        [32, 432) 'stack_array'
    HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext
          (longjmp and C++ exceptions *are* supported)
    Shadow bytes around the buggy address:
      0x100052dd3dc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
      0x100052dd3dd0: 00 00 00 00 00 00 00 00 00 00 00 00 f1 f1 f1 f1
      0x100052dd3de0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
      0x100052dd3df0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
      0x100052dd3e00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    =>0x100052dd3e10: 00 00[f4]f4 f3 f3 f3 f3 00 00 00 00 00 00 00 00
      0x100052dd3e20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
      0x100052dd3e30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
      0x100052dd3e40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
      0x100052dd3e50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
      0x100052dd3e60: 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 righ 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
      ASan internal:         fe
    ==6196== ABORTING

    2.4 Global buffer overflow

    Global buffer overflow表示超出全局变量访问区域,通过0xf9进行判断。如果访问的区域是0xf9则表示可能超出了全局变量访问区域。

    // RUN: g++ global_buffer_overflow.cc -o global_buffer_overflow -ggdb -fsanitize=address
    
    int global_array[100] = {-1};
    int main(int argc, char **argv) {
      return global_array[argc + 100];  // BOOM
    }

     结果如下:

    =================================================================
    ==7681== ERROR: AddressSanitizer: global-buffer-overflow on address 0x0000006011f4 at pc 0x4007f3 bp 0x7ffeb4f10630 sp 0x7ffeb4f10628
    READ of size 4 at 0x0000006011f4 thread T0
        #0 0x4007f2 (/home/al/temp/address_sanitizer/global_buffer_overflow+0x4007f2)
        #1 0x7fad3bdf982f (/lib/x86_64-linux-gnu/libc-2.23.so+0x2082f)
        #2 0x4006d8 (/home/al/temp/address_sanitizer/global_buffer_overflow+0x4006d8)
    0x0000006011f4 is located 4 bytes to the right of global variable 'global_array (global_buffer_overflow.cc)' (0x601060) of size 400
    Shadow bytes around the buggy address:
      0x0000800b81e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
      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[f9]f9
      0x0000800b8240: f9 f9 f9 f9 00 00 00 00 00 00 00 00 00 00 00 00
      0x0000800b8250: 00 00 00 00 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
    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 righ 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
      ASan internal:         fe
    ==7681== ABORTING

    2.5 Use after return

    2.6 Use after scope

    2.7 Initialization order bugs

    2.8 Memory leaks

     Memory leaks用于检查内存泄漏,结果会告知泄漏的内存在何处申请。

    //RUN: clang -fsanitize=address -g memory-leak.c -o memory-leak
    #include <stdlib.h>
    
    void *p;
    
    int main() {
      p = malloc(7);
      p = 0; // The memory is leaked here.
      return 0;
    }

     结果如下,会显示泄漏点的栈,以及一个总结:泄漏多少内存、在哪几处泄漏。

    =================================================================
    ==9089==ERROR: LeakSanitizer: detected memory leaks
    
    Direct leak of 7 byte(s) in 1 object(s) allocated from:
        #0 0x40794f  (/home/al/temp/address_sanitizer/memory-leak+0x40794f)
        #1 0x427f6a  (/home/al/temp/address_sanitizer/memory-leak+0x427f6a)
        #2 0x7fcaaef9a82f  (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
    
    SUMMARY: LeakSanitizer: 7 byte(s) leaked in 1 allocation(s).

    3. 参考资料

    Fuzzing初学者指南:利用Address Sanitizer找到更多BUG

    gcc AddressSanitizer

  • 相关阅读:
    linux 6 安装 Nagios服务
    linux 6 安装 Nginx服务
    Rsync的配置与使用
    linux 6 搭建 msyql 服务
    linux6搭建Apache服务
    Linux 7搭建NFS服务
    Linux 6 忘记root密码重置
    简单makefile
    多线程c++11编程题目
    redis 代码结构与阅读顺序
  • 原文地址:https://www.cnblogs.com/arnoldlu/p/11607916.html
Copyright © 2020-2023  润新知