揮発性のメモ2

知識をメモ書きしておく

gccで、配列の範囲を超えてアクセスしてる人を探してぶっ殺したい

gccで、配列の範囲を超えてアクセスしてる人を探してぶっ殺したい。
そのためにはgccの ASAN(Address Sanitizer)機能を使う

-fsanitize=address

ソース
#include <stdio.h>
int main()
{
    char buf[15];

    printf("MAIN\n");
    for(int i=0; i<16; i++){
        printf("%08X:buf[%d]\n", &buf[i], i);
        buf[i] = i;
    }

    return 0;
}

配列が15個しかないのに16回まわるのではみ出る、というサンプルプログラム

コンパイル
gcc -g -fsanitize=address main.c

-gオプションは無くてもよいが、つけると検知時に行番号が表示されるのでつける

実行結果
MAIN
621FF520:buf[0]
621FF521:buf[1]
(中略)
621FF52E:buf[14]
621FF52F:buf[15]
=================================================================
==1594509==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffd621ff52f at pc 0x55660474d2aa bp 0x7ffd621ff4e0 sp 0x7ffd621ff4d8
WRITE of size 1 at 0x7ffd621ff52f thread T0
    #0 0x55660474d2a9 in main /tmp/main.c:9
    #1 0x7f68a29f8189 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
    #2 0x7f68a29f8244 in __libc_start_main_impl ../csu/libc-start.c:381
    #3 0x55660474d0e0 in _start (/tmp/a.out+0x10e0)

Address 0x7ffd621ff52f is located in stack of thread T0 at offset 47 in frame
    #0 0x55660474d1b8 in main /tmp/main.c:3

  This frame has 1 object(s):
    [32, 47) 'buf' (line 4) <== Memory access at offset 47 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 /tmp/main.c:9 in main

はみ出た時点でABORTする。 ちゃんと何行目ではみ出たかを検知している。
(はみ出なければ何も起きない)

-fsanitize=bounds

配列をはみ出たことだけを検知したいときは bounds を使う。
またこのときは ABORTせずに適当にごまかして先に進めてしまうので、それを回避したいときは
-fno-sanitize-recover=all オプションも指定すると、そこでプログラムが終了する。

コンパイル
gcc -g -fno-sanitize-recover=all -fsanitize=bounds main.c
実行結果
MAIN
9ED8575D:buf[0]
9ED8575E:buf[1]
(中略)
9ED8576B:buf[14]
9ED8576C:buf[15]
main.c:9:12: runtime error: index 15 out of bounds for type 'char [15]'

情報量が無いしcoreも吐けないので これはまあ使わない

コアダンプの設定

ABORTしたときにcoreを吐かせる方法。
まずそもそもulimitでコアが出るようにした上で、環境変数ASAN_OPTIONSでcoreを吐くように設定する

実行結果
$ ulimit -c unlimited
$ export ASAN_OPTIONS=abort_on_error=1:disable_coredump=0:unmap_shadow_on_exit=1

$ ./a.out
(中略)
==1594730==ABORTING
Aborted (core dumped)

吐かれたcoreには、はみ出しを検知してから実際にABORTするまでの間に ASANの関数がいっぱい挟まるので 解析利用時には最後の8~9層くらいは読み飛ばす

処理能力

3倍くらい遅くなる気がする