12

Linux カーネルがスタック オーバーフローで segfault を生成するのはなぜですか? これは、一時配列の c または fortran 作成での alloca がオーバーフローしたときに、デバッグを非常に厄介なものにする可能性があります。確かに、ランタイムがより役立つエラーを生成する可能性は十分にあります。

4

6 に答える 6

41

シグナルハンドラーを使用して、スタックオーバーフローの条件を実際にキャッチできます。

これを行うには、次の2つのことを行う必要があります。

  • sigactionを使用してSIGSEGV(segfault)のシグナルハンドラーを設定します。これを行うには、SO_ONSTACKフラグを設定します。これは、シグナルを配信するときに代替スタックを使用するようにカーネルに指示します。

  • sigaltstack()を呼び出して、SIGSEGVのハンドラーが使用する代替スタックをセットアップします。

次に、スタックをオーバーフローさせると、カーネルはシグナルを配信する前に代替スタックに切り替えます。シグナルハンドラに入ると、障害の原因となったアドレスを調べて、それがスタックオーバーフローなのか、通常の障害なのかを判断できます。

于 2008-09-17T22:42:34.153 に答える
7

「カーネル」(実際にはコードを実行しているカーネルではなく、CPUです)は、コードが触れてはならないメモリをどのように参照しているかを知りません。それはあなたがそれをやろうとしたことだけを知っています。

コード:

char *x = alloca(100);
char y = x[150];

x の境界を超えてアクセスしようとすると、CPU によって実際に評価されることはありません。

次のコマンドを使用して、まったく同じアドレスにアクセスできます。

char y = *((char*)(0xdeadbeef));

ところで、スタックはヒープよりもはるかに制限される傾向があるため、alloca の使用はお勧めしません (代わりに malloc を使用してください)。

于 2008-09-17T08:48:01.087 に答える
5

スタックオーバーフローはセグメンテーション違反です。あなたが最初に割り当てられたメモリの与えられた境界を破ったように。有限サイズのスタック、そしてあなたはそれを超えました。あなたはウィキペディアでそれについてもっと読むことができます

さらに、過去にプロジェクトで行ったことの1つは、独自のシグナルハンドラーをsegfaultに書き込むことです(manページのシグナル(2)を参照)。私は通常、信号をキャッチして、コンソールに「致命的なエラーが発生しました」と書きました。チェックポイントフラグとデバッグを使用して、さらにいくつかの作業を行いました。

segfaultをデバッグするために、GDBでプログラムを実行できます。たとえば、次のCプログラムはセグメンテーション違反になります:#segfault.c #include #include

int main() 
{
        printf("Starting\n");
        void *foo=malloc(1000);
        memcpy(foo, 0, 100); //this line will segfault
        exit(0);
}

私がそのようにそれをコンパイルするならば:

gcc -g -o segfault segfault.c 

そしてそれを次のように実行します:

$ gdb ./segfault
GNU gdb 6.7.1
Copyright (C) 2007 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-pc-linux-gnu"...
Using host libthread_db library "/lib/libthread_db.so.1".
(gdb) run
Starting program: /tmp/segfault 
Starting

Program received signal SIGSEGV, Segmentation fault.
0x4ea43cbc in memcpy () from /lib/libc.so.6
(gdb) bt
#0  0x4ea43cbc in memcpy () from /lib/libc.so.6
#1  0x080484cb in main () at segfault.c:8
(gdb) 

GDBから、8行目にセグメンテーション違反があったことがわかりました。もちろん、スタックオーバーフローやその他のメモリエラーを処理するためのより複雑な方法がありますが、これで十分です。

于 2008-09-17T08:55:31.100 に答える
1

Simply use Valgrind. It will point out all your memory allocation mistakes with excruciating preciseness.

于 2008-09-17T09:06:34.160 に答える
0

スタックオーバーフローは必ずしもクラッシュを引き起こすとは限りません。プログラムのデータをサイレントに破棄する可能性がありますが、実行は継続されます。

私はSIGSEGVハンドラーの応急修理を使用しませんが、代わりに元の問題を修正します。

自動化されたヘルプが必要な場合は、gccの-Wstack-protectorオプションを使用できます。これにより、実行時にオーバーフローが検出され、プログラムが中止されます。

valgrindは動的メモリ割り当てのバグには適していますが、スタックエラーには適していません。

于 2008-09-18T20:05:18.983 に答える
0

いくつかのコメントは役に立ちますが、問題はメモリ割り当てエラーではありません。つまり、コードに間違いはありません。ランタイムがスタックに一時的な値を割り当てるのは、fortran では非常に厄介です。したがって、write(fp)x,y,z などのコマンドは、警告なしで segfault をトリガーできます。intel Fortran コンパイラのテクニカル サポートは、ランタイム ライブラリがより役立つメッセージを出力する方法はないと述べています。ただし、ミゲルが正しければ、彼が示唆するように、これは可能であるはずです。どうもありがとう。残りの問題は、最初にセグ フォールトのアドレスを見つけて、それがスタック オーバーフローまたはその他の問題によるものかどうかを判断する方法です。

この問題を見つけた他の人のために、ヒープ上の特定のサイズを超える一時変数を配置するコンパイラ フラグがあります。

于 2008-11-09T23:29:55.633 に答える