X86の場合-Linuxセグメントレジスタはバッファオーバーフローチェックに使用されます[スタック内のいくつかのchar配列を定義した以下のコードスニペットを参照してください]:
static void
printint(int xx, int base, int sgn)
{
char digits[] = "0123456789ABCDEF";
char buf[16];
int i, neg;
uint x;
neg = 0;
if(sgn && xx < 0){
neg = 1;
x = -xx;
} else {
x = xx;
}
i = 0;
do{
buf[i++] = digits[x % base];
}while((x /= base) != 0);
if(neg)
buf[i++] = '-';
while(--i >= 0)
my_putc(buf[i]);
}
ここで、gccで生成されたコードの逆アセンブリが表示された場合。
関数printintのアセンブラーコードのダンプ:
0x00000000004005a6 <+0>: push %rbp
0x00000000004005a7 <+1>: mov %rsp,%rbp
0x00000000004005aa <+4>: sub $0x50,%rsp
0x00000000004005ae <+8>: mov %edi,-0x44(%rbp)
0x00000000004005b1 <+11>: mov %esi,-0x48(%rbp)
0x00000000004005b4 <+14>: mov %edx,-0x4c(%rbp)
0x00000000004005b7 <+17>: mov %fs:0x28,%rax ------> obtaining an 8 byte guard from based on a fixed offset from fs segment register [from the descriptor base in the corresponding gdt entry]
0x00000000004005c0 <+26>: mov %rax,-0x8(%rbp) -----> pushing it as the first local variable on to stack
0x00000000004005c4 <+30>: xor %eax,%eax
0x00000000004005c6 <+32>: movl $0x33323130,-0x20(%rbp)
0x00000000004005cd <+39>: movl $0x37363534,-0x1c(%rbp)
0x00000000004005d4 <+46>: movl $0x42413938,-0x18(%rbp)
0x00000000004005db <+53>: movl $0x46454443,-0x14(%rbp)
...
...
// function end
0x0000000000400686 <+224>: jns 0x40066a <printint+196>
0x0000000000400688 <+226>: mov -0x8(%rbp),%rax -------> verifying if the stack was smashed
0x000000000040068c <+230>: xor %fs:0x28,%rax --> checking the value on stack is matching the original one based on fs
0x0000000000400695 <+239>: je 0x40069c <printint+246>
0x0000000000400697 <+241>: callq 0x400460 <__stack_chk_fail@plt>
0x000000000040069c <+246>: leaveq
0x000000000040069d <+247>: retq
この関数からスタックベースのchar配列を削除すると、gccはこのガードチェックを生成しません。
カーネルモジュールの場合でも、gccによって同じものが生成されるのを見てきました。基本的に、いくつかのカーネルコードをボトラップしているときにクラッシュが発生し、仮想アドレス0x28で障害が発生していました。後で、スタックポインタを正しく初期化し、プログラムを正しくロードしたと思ったのですが、gdtに正しいエントリがないため、fsベースのオフセットが有効な仮想アドレスに変換されます。
ただし、カーネルコードの場合は、__ stack_chk_fail @ plt>のようなものにジャンプする代わりに、エラーを単に無視していました。
このガードをgccに追加する関連コンパイラオプションは-fstack-protectorです。これはデフォルトで有効になっていて、ユーザーアプリをコンパイルしていると思います。
カーネルの場合、configCC_STACKPROTECTORオプションを使用してこのgccフラグを有効にできます。
構成CC_STACKPROTECTOR
699 bool「-fstack-protectorバッファオーバーフロー検出を有効にする(実験的)」
700はSUPERH32に依存します
701ヘルプ
702このオプションは、-fstack-protectorGCC機能をオンにします。これ
703機能は、関数の先頭にカナリア値を置きます
704リターンアドレスの直前のスタック、および検証
705実際に戻る直前の値。スタックベースのバッファ
706オーバーフロー(このリターンアドレスを上書きする必要がある)も
707カナリアを上書きします。カナリアは検出され、攻撃が行われます。
カーネルパニックを介して708が中和されました。
709
710この機能には、gccバージョン4.2以降が必要です。
このgs/fsがlinux/arch / x86 / include / asm/stackprotector.hである関連カーネルファイル