c言語の配列の範囲外アクセス

エラーを出す方法メモ

$ gcc -Wall -Wextra -O2 -c hogehoge.c

最適化オプションが必須、他はおまけ。
O1ではでない、O2もしくはO3などではでる。特に意味もないのでO2でいいんじゃないかな(無知)。

12/1追記 debianで失敗

AdressSanitizer/LeakSanitizerというものでできるらしい

// list_boundary.c
#include<stdio.h>
#include<stdlib.h>

#define N 10

void bug(void){
    int *test_buff = (int*)malloc(sizeof(int) * N);
    test_buff[N] = 10; // error
}

int main( void ){

    printf("start\n");

    bug();

    printf("end\n");
    return 0;
}

このコードで試す。

$ gcc -g -fsanitize=address list_boundary.c
$ a.out
start
=================================================================
==35538==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6040000003f8 at pc 0x00010aa05d5b bp 0x7ffee51fa7b0 sp 0x7ffee51fa7a8
WRITE of size 4 at 0x6040000003f8 thread T0
    #0 0x10aa05d5a in bug list_boundary.c:8
    #1 0x10aa05d84 in main list_boundary.c:16
    #2 0x7fff5b0d53d4 in start (libdyld.dylib:x86_64+0x163d4)

0x6040000003f8 is located 0 bytes to the right of 40-byte region [0x6040000003d0,0x6040000003f8)
allocated by thread T0 here:
    #0 0x10aa7e83f in wrap_malloc (libasan.5.dylib:x86_64+0x7683f)
    #1 0x10aa05d11 in bug list_boundary.c:7
    #2 0x10aa05d84 in main list_boundary.c:16
    #3 0x7fff5b0d53d4 in start (libdyld.dylib:x86_64+0x163d4)

SUMMARY: AddressSanitizer: heap-buffer-overflow list_boundary.c:8 in bug
Shadow bytes around the buggy address:
  0x1c0800000020: fa fa 00 00 00 00 00 fa fa fa 00 00 00 00 00 05
  0x1c0800000030: fa fa 00 00 00 00 00 fa fa fa 00 00 00 00 00 05
  0x1c0800000040: fa fa 00 00 00 00 00 fa fa fa 00 00 00 00 00 07
  0x1c0800000050: fa fa 00 00 00 00 00 fa fa fa 00 00 00 00 00 fa
  0x1c0800000060: fa fa 00 00 00 00 00 fa fa fa 00 00 00 00 00 fa
=>0x1c0800000070: fa fa 00 00 00 00 00 05 fa fa 00 00 00 00 00[fa]
  0x1c0800000080: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x1c0800000090: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x1c08000000a0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x1c08000000b0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x1c08000000c0: 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
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  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
  Shadow gap:              cc
==35538==ABORTING
void bug(void){
    int *test_buff = (int*)malloc(sizeof(int) * N);
    test_buff[N-1] = 10; // modified
}

メモリーリークも検出できる。終了時に判定されるため、アクセスエラーを拾って終了しないように修正する。
さあ以下で実行。

$ gcc -g -fsanitize=address list_boundary.c
$ ASAN_OPTIONS=detect_leaks=1 a.out
start
end

=================================================================
==35595==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 40 byte(s) in 1 object(s) allocated from:
    #0 0x10c72e83f in wrap_malloc (libasan.5.dylib:x86_64+0x7683f)
    #1 0x10c6b4d2b in bug list_boundary.c:7
    #2 0x10c6b4d92 in main list_boundary.c:15
    #3 0x7fff5b0d53d4 in start (libdyld.dylib:x86_64+0x163d4)

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

できた。なんて便利。

参考

  • https://gcc.gnu.org/ml/gcc-patches/2013-11/msg01874.html
  • https://clang.llvm.org/docs/AddressSanitizer.html

social