LoginSignup
34
31

More than 5 years have passed since last update.

clang の AddressSanitizer を使って、バッファオーバーフロー/ヒープオーバーフローを検出する

Posted at

最近の clang では AddressSanitizer という機能があり、コンパイルフラグに -g -fsanitize=address -fno-omit-frame-pointer を追加することで実行時にバッファオーバーフロー/ヒープオーバーフローが検出できる。Xcode だと 7.0 以降で使えるようだ。

#include <stdio.h>
int main()
{
    char buffer[5];
    for (int i = 0; i <= 5; i++) {
        buffer[i] = i;
    }
    printf("OK\n");
}

上記コードは普通にコンパイルすると何事も無いかのように実行できる。

$ clang foo.c
$ ./a.out
OK

次に -g -fsanitize=address -fno-omit-frame-pointer を追加してコンパイルしてみる。

$ clang foo.c -g -fsanitize=address -fno-omit-frame-pointer
$ ./a.out
=================================================================
==47384==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fff5ad0c745 at pc 0x000104ef3d98 bp 0x7fff5ad0c710 sp 0x7fff5ad0c708
WRITE of size 1 at 0x7fff5ad0c745 thread T0
    #0 0x104ef3d97 in main foo.c:6
    #1 0x7fff9a7745ac in start (/usr/lib/system/libdyld.dylib+0x35ac)
    #2 0x0  (<unknown module>)

Address 0x7fff5ad0c745 is located in stack of thread T0 at offset 37 in frame
    #0 0x104ef3c5f in main foo.c:3

  This frame has 1 object(s):
    [32, 37) 'buffer' <== Memory access at offset 37 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 foo.c:6 main
Shadow bytes around the buggy address:
  0x1fffeb5a1890: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1fffeb5a18a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1fffeb5a18b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1fffeb5a18c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1fffeb5a18d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x1fffeb5a18e0: 00 00 00 00 f1 f1 f1 f1[05]f3 f3 f3 00 00 00 00
  0x1fffeb5a18f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1fffeb5a1900: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1fffeb5a1910: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1fffeb5a1920: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1fffeb5a1930: 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
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==47384==ABORTING
[1]    47384 abort      ./a.out

とバッファオーバーフローが発生していることがわかる。

以下のように malloc で確保したヒープよりも大きな文字列を strcpy でコピーするようなケースも検出できる。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
    char *buffer = malloc(5);
    strcpy(buffer, "abcdef");
    free(buffer);
    printf("OK\n");
}
$ clang bar.c -g -fsanitize=address -fno-omit-frame-pointer
$ ./a.out
=================================================================
==47833==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60200000eeb5 at pc 0x00010e0e930d bp 0x7fff51b5a7c0 sp 0x7fff51b59f70
WRITE of size 7 at 0x60200000eeb5 thread T0
    #0 0x10e0e930c in wrap_strcpy (/Applications/Xcode7.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/7.0.0/lib/darwin/libclang_rt.asan_osx_dynamic.dylib+0x3c30c)
    #1 0x10e0a5df7 in main bar.c:7
    #2 0x7fff9a7745ac in start (/usr/lib/system/libdyld.dylib+0x35ac)
    #3 0x0  (<unknown module>)

0x60200000eeb5 is located 0 bytes to the right of 5-byte region [0x60200000eeb0,0x60200000eeb5)
allocated by thread T0 here:
    #0 0x10e0ef980 in wrap_malloc (/Applications/Xcode7.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/7.0.0/lib/darwin/libclang_rt.asan_osx_dynamic.dylib+0x42980)
    #1 0x10e0a5de3 in main bar.c:6
    #2 0x7fff9a7745ac in start (/usr/lib/system/libdyld.dylib+0x35ac)
    #3 0x0  (<unknown module>)

SUMMARY: AddressSanitizer: heap-buffer-overflow ??:0 wrap_strcpy
Shadow bytes around the buggy address:
  0x1c0400001d80: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x1c0400001d90: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x1c0400001da0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x1c0400001db0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x1c0400001dc0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
=>0x1c0400001dd0: fa fa fa fa fa fa[05]fa fa fa 00 06 fa fa 00 00
  0x1c0400001de0: fa fa 00 04 fa fa 00 06 fa fa 00 06 fa fa 00 fa
  0x1c0400001df0: fa fa fd fd fa fa fd fd fa fa fd fd fa fa fd fa
  0x1c0400001e00: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x1c0400001e10: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x1c0400001e20: 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
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==47833==ABORTING
[1]    47833 abort      ./a.out
34
31
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
34
31