揮発性のメモ2

http://d.hatena.ne.jp/iww/

秀丸で、カーソルを行末に移動する

秀丸で、カーソルを行末に移動するときにEndキーを使うと


ウィンドウの端で折り返す設定のとき、デフォルトでは 図1 のA(行末→と2の間)にカーソルが移動する。
しかし実際には折り返し位置なだけで行末でもなんでもない上に 見た目ではひとつ手前の中途半端な位置に見えるので 非常に使い勝手が悪い。

秀丸で行末に移動する機能は全部で3種類:

行末に移動
折り返されている場合、一番右端から1文字手前に移動する(A)
行末に移動(その2)
折り返されている場合、一番右端に移動する(B)
論理行末に移動
改行コードもしくはテキストの終わりまで移動する(C)

デフォルトでは Endキーのカーソル移動は「行末に移動」になっているので キー割り当てで「論理行末に移動」へ変更しよう

gccで、スレッド間で同じメモリを同時にアクセスしてる人を探してぶっ殺したい

gccで、スレッド間で同じメモリを同時にアクセスしてる人を探してぶっ殺したい。

そのためにはgccの TSAN(Thread Sanitizer)機能を使う

-fsanitize=thread

ソース
#include <pthread.h>
#include <stdio.h>

volatile long long A=0;

void *gettidtest( void *arg )
{
    int number = (int)(long) arg;
    long long i;

    printf("thread=%d start\n", number);
    for(i=0;i<1000000LL;i++){
        A++;     // 排他制御なし
//      __atomic_add_fetch( &A, 1, __ATOMIC_RELAXED );   // アトミック加算
    }
    printf("thread=%d end\n", number);

    return NULL;
}

int main( int argc, char **argv )
{
    int i;
    pthread_t p[10]={};

    for(i=0;i<10;i++){
        pthread_create( &p[i], NULL, gettidtest, (void*)(long)i );
    }
    for(i=0;i<10;i++){
        pthread_join( p[i], NULL );
    }
    printf("A=%lld\n",A);

    return 0;
}
コンパイル
$ gcc -g -fsanitize=thread thread.c
実行結果

競合したところを教えてくれる

問題点
  • 競合を発見しても終了しない
    -fno-sanitize-recover=all が効かず、検知後もプログラムは続行する
  • -fsanitize=address と一緒には使えない
    どちらか一方しか使えない
  • x86_64 でしかサポートしてない
    Cygwinもダメ、32bitもダメ、ARMもダメ
続きを読む

gccで、スタックを破壊している人を探してぶっ殺したい

gccで、スタックを破壊している人を探してぶっ殺したい。

#include <stdio.h>
#include <string.h>

void fn1()
{
    int c=0;

    // intは4バイト。5~12で静かに破壊。13以上でSegv
    memset(&c, 0x55, 16);
    // memsetが最適化で消えないように
    printf("c=0x%x\n", c);
}

int main()
{
    fn1();
    return 0;
}
$ gcc -g -fstack-protector-all stack.c

$ ./a.out
c=0x55555555
*** stack smashing detected ***: terminated
Aborted (core dumped)

スタックを破壊するとAbortしてくれる。 ulimitを設定しておけばcoreも吐く。

ただし、「マジックナンバーを敷き詰めておいて、誰かが破壊したあとに照合して検知する」という方法らしいので、派手に破壊されたときはcoreを見ても意味がない。

-fsanitize=address でもスタックオーバーフローは検知してくれるしこれで検知できたときはスタックの中身がピンピンしてるので、 -fstack-protector は使わないかも

依存ライブラリが無いので、ASANが使えない化石みたいに古い環境なら利用価値があるかも

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

gccで、配列の範囲を超えてアクセスしてる人を探してぶっ殺したい - 揮発性のメモ2

gccで、構造体の中の配列の範囲を超えてアクセスしてる人を探してぶっ殺したい。
しかし、 -fsanitize=address で検知できるのは「構造体をはみ出たかどうか」のみである。
配列の範囲は -fsanitize=bounds で検知する

サンプルコード

#include <stdio.h>

struct hoge {
    int i;
    char a[15];
};


int main()
{
    struct hoge hoge = {};
    printf("sizeof(hoge)=%ld\n", sizeof(struct hoge));
    
    
    hoge.a[15] = 1; // ★A
    printf("%d\n", hoge.a[15]);
    hoge.a[16] = 1; // ★B
    printf("%d\n", hoge.a[16]);
    
    return 0;
}
gcc -g -fsanitize=address sani.c

構造体が配列にぴっちりしているとき

struct hoge {
    char a[15];
};

struct hoge のサイズは 15バイト(char 15バイト)となる。
よって、hoge.a[15] は配列を超えているし構造体もはみ出ているので検知される。

構造体がアライメントに合わされてパディングがついてるとき

struct hoge {
    int i;
    char a[15];
};

struct hoge のサイズは 20バイト(int 4バイト + char 15バイト + パディング 1バイト)となる。
よって、hoge.a[15] は配列を超えてはいるが構造体の中なので 検知されない。
hoge.a[16] は構造体をはみ出ているので検知される。

構造体で配列の定義が先になっているとき

struct hoge {
    char a[15];
    int i;
};

struct hoge のサイズは 同じく20バイトとなる。
しかし、配列を超えても超えても次の変数があるので
hoge.a[15] も hoge.a[16] も構造体の中であるため検知されない。

-fsanitize=bounds

sanitize=bounds ならどこの配列だろうと範囲越えをチェックできる

gcc -g -fsanitize=address -fsanitize=bounds sani.c

やったぜ

gccで、どんな最適化がかかっているかを見る

gcc -Q --help=optimizers

このコマンドで、どんなオプティマイズオプションがあるか、どれが有効になっているか を確認できる

$ gcc -Q --help=optimizers
The following options control optimizations:
  -O<number>
  -Ofast
  -Og
  -Os
  -Oz
  -faggressive-loop-optimizations       [enabled]
  -falign-functions                     [disabled]
  -falign-functions=
  -falign-jumps                         [disabled]
(後略)
-fno-omit-frame-pointer

レジスタEBPを汎用レジスタに使わせないように指示するオプション。
レジスタEBPは本来はスタック用のポインタだけど、-O2とかの最適化オプションを指定すると勝手に使いまわされちゃって、死んだときcore見てもうまくバックトレースできないことがあって嫌なので、それを抑止する

なにも指定しないとdisabled

$ gcc -Q --help=optimizers | grep omit
  -fomit-frame-pointer                  [disabled]

-O2をつけると自動でenabledになる

$ gcc -Q --help=optimizers -O2 | grep omit
  -fomit-frame-pointer                  [enabled]

-fno-omit-frame-pointer でdisabledにおさえつける

$ gcc -Q --help=optimizers -O2 -fno-omit-frame-pointer | grep omit
  -fomit-frame-pointer                  [disabled]

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倍くらい遅くなる気がする