2008-09-17 19 views
12

なぜLinuxカーネルはスタックオーバーフロー時にsegfaultを生成しますか?これは、一時配列のcまたはfortran作成におけるallocaがオーバーフローしたときに、デバッグを非常に厄介にする可能性があります。確かに、ランタイムがより有用なエラーを生成する可能性はありません。Segfault on stack overflow

答えて

7

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

コード:あなたはxの境界を越えてアクセスしようとしているよう

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

は本当にCPUによって評価することはできません。あなたはとまったく同じアドレスを打つこと

:ところで

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

スタックははるかに限られたヒープよりもする傾向があるので、私は(代わりにmallocを使用)のallocaの使用を阻止します。

+0

スタックスペースはハードコア操作でははるかに高速ですが、控えめに使用してください。また、allocaを実行する前に "Getrlimit"をチェックしてください。十分なスペースが残っていることを確認してください! –

5

スタックオーバーフローがセグメンテーション違反です。あなたが当初割り当てられたメモリの境界を壊したように。有限のサイズの積み重ね、あなたはそれを超えました。詳細については、wikipedia

さらに読むことができます。過去のプロジェクトでは、自分のシグナルハンドラをsegfaultに書き込んでいます(マニュアルページsignal(2)を参照)。私は通常、信号を受け取り、コンソールに「致命的なエラーが発生しました」と書きました。私は、チェックポイントフラグとデバッグを含むいくつかの追加作業を行った。あなたが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行目にセグメンテーション違反があることが分かりました。もちろん、sta ckのオーバーフローやその他のメモリエラーが発生しますが、これで十分です。

1

単にValgrindを使用しています。それは、あなたのメモリ割り当ての間違いを徹底的に正確に指摘します。

42

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

  • セットアップこれはSO_ONSTACKフラグを設定行うには、はsigactionを使用してSIGSEGV(セグメンテーション違反)のためのシグナルハンドラ:

    これを行うには、次の2つのことを行う必要があります。これはカーネルにシグナルを送るときに別のスタックを使うように指示します。セットアップへ

  • コールSIGALTSTACK()SIGSEGVのハンドラが使用する代替スタック。あなたは、スタックオーバーフローとき

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

0

スタックオーバーフローが必ずしもクラッシュするとは限りません。それはあなたのプログラムのデータを静かにゴミ箱に入れても、実行を続けます。

私はSIGSEGVハンドラのkludgesを使用せず、代わりに元の問題を修正します。

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

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

0

コメントの一部は役に立ちますが、メモリ割り当てエラーではありません。つまり、コードに間違いはありません。ランタイムがスタックに一時的な値を割り当てるFortranではかなり厄介です。したがって、 write(fp)x、y、z などのコマンドは警告なしでsegfaultをトリガーできます。インテルFortranコンパイラーのテクニカル・サポートは、ランタイム・ライブラリーがより有用なメッセージを出力する方法はないと言います。しかし、ミゲルがそれよりも正しいとすれば、彼が示唆するように可能であるはずです。だから、ありがとう。残りの質問は、スタックオーバーフローやその他の問題が原因で発生した場合、最初にsegフォルトのアドレスと数字を見つける方法です。

この問題を発見した他の人には、ヒープ上の一時的な可変部分を一定の大きさ以上にするコンパイラフラグがあります。

関連する問題