私はかなり大きな再帰関数(Cで書いています)を持っていますが、スタックオーバーフローが発生するシナリオはほとんどありませんが、それでも可能です。私が疑問に思うのは、スタックがいくつかの反復でオーバーフローするかどうかを検出できるかどうかです。プログラムをクラッシュさせずに緊急停止を行うことができます。実行時にスタックオーバーフローを事前に検出する
答えて
相続人は、単純な解決策それはwin-32のために働く。実際Wossnameが既に掲載が、あまり不快何に似ている:)
unsigned int get_stack_address(void)
{
unsigned int r = 0;
__asm mov dword ptr [r], esp;
return r;
}
void rec(int x, const unsigned int begin_address)
{
// here just put 100 000 bytes of memory
if (begin_address - get_stack_address() > 100000)
{
//std::cout << "Recursion level " << x << " stack too high" << std::endl;
return;
}
rec(x + 1, begin_address);
}
int main(void)
{
int x = 0;
rec(x,get_stack_address());
}
'std :: cout'?これはCコードではありません。 – kfx
@kfxが付与されました:) – mainactual
あの、暖かく、ファジーな、仕事のセキュリティ感覚のためのインラインASM:D Nice。 – Wossname
C言語では、それは不可能です。一般に、あなたはあなたが尽きる前にスタックを使い果たしたことを簡単に知ることができません。代わりに、実装の再帰深度に構成可能なハード制限を設定することをお勧めします。深度が超過した場合は、単に中止できます。再帰を介してスタックを使用するのではなく、補助データ構造を使用するようにアルゴリズムを書き直すこともできます。これにより、メモリ不足状態を検出する柔軟性が向上します。 malloc()
は、いつ失敗するかを知らせます。両方のためのシグナルハンドラを確立し、ハードスタックリミット
- 使用
setrlimit
:ただし、UNIX系のシステムでこのような手順で同様のものを得ることができます
SIGSEGV
およびSIGBUS
を使用してスタックオーバーフローを通知します。一部のオペレーティングシステムではSIGSEGV
、それ以外の場合はSIGBUS
が生成されます。 - このような信号が得られた場合にスタックオーバーフローが発生したと判断した場合は、ソフトスタックの制限を
setrlimit
にして、これが発生したことを識別するためのグローバル変数を設定します。変数volatile
を作成して、オプティマイザが平野を覆わないようにします。 - コード内で、各再帰ステップで、この変数が設定されているかどうかを確認します。そうであれば、中断する。
これはどこでも動作するわけではなく、プラットフォーム固有のコードが必要なため、シグナルがスタックオーバーフローから発生していることがわかります。 SIGSEGV
またはSIGBUS
を取得した後で、すべてのシステム(特に、68000システムの初期のシステム)が正常な処理を継続できるわけではありません。
Bourneシェルはメモリ割り当てに同様の手法を使用しました。
似たようなハッキングはWindowsでは可能ですが、例外をサポートする言語を使用してWindows構造のスタックオーバーフロー例外をキャッチできれば、ずっと簡単です。 –
@MartinJamesとにかくこのソリューションは移植性がないので、Windowsでは問題を解決するWindows APIを使用してください。 – fuz
はここで素朴な方法だが、それは少し不快です...
初めて関数を入力するときは、その関数内で宣言あなたの変数のいずれかのアドレスを格納することができます。その値を関数外に(たとえば、グローバルに)格納します。後続の呼び出しでは、その変数の現在のアドレスとキャッシュされたコピーを比較します。これらの2つの値が離れれば離れるほど、より深いところまで再帰します。
これはコンパイラの警告(一時変数のアドレスを格納する)を引き起こす可能性がありますが、使用しているスタックの正確な量を正確に知ることができます。
本当にこれをお勧めしますが、うまくいくとは言えません。 (いくつかの安全マージン内、64キロバイトを言う)
#include <stdio.h>
char* start = NULL;
void recurse()
{
char marker = '@';
if(start == NULL)
start = ▮
printf("depth: %d\n", abs(&marker - start));
if(abs(&marker - start) < 1000)
recurse();
else
start = NULL;
}
int main()
{
recurse();
return 0;
}
別の方法は、プログラムの開始時にスタックリミットを学ぶことで、あなたの再帰関数の各時間は、この制限が近づいてきたかどうかをチェックします。もしそうなら、打ち切ります。そうでない場合は、続行します。
システムコールのgetrlimit
を使用すると、POSIXシステムのスタック制限を知ることができます。
コード例:(注:!それのコードはスタックがx86
のように、逆方向に成長することを前提として)
#include <stdio.h>
#include <sys/time.h>
#include <sys/resource.h>
void *stack_limit;
#define SAFETY_MARGIN (64 * 1024) // 64 kb
void recurse(int level)
{
void *stack_top = &stack_top;
if (stack_top <= stack_limit) {
printf("stack limit reached at recursion level %d\n", level);
return;
}
recurse(level + 1);
}
int get_max_stack_size(void)
{
struct rlimit rl;
int ret = getrlimit(RLIMIT_STACK, &rl);
if (ret != 0) {
return 1024 * 1024 * 8; // 8 MB is the default on many platforms
}
printf("max stack size: %d\n", (int)rl.rlim_cur);
return rl.rlim_cur;
}
int main (int argc, char *argv[])
{
int x;
stack_limit = (char *)&x - get_max_stack_size() + SAFETY_MARGIN;
recurse(0);
return 0;
}
出力:
max stack size: 8388608
stack limit reached at recursion level 174549
私は、ポータブルソリューションがあると思いませんが、移植性を気にしない場合は、インラインアセンブリを使用して、スタックポインタの値をチェックすることができますスタックポインタがある値よりも小さい場合は緊急出口を実行します。 –
単純に再帰深度を最大深度に制限することもできます。 –
重複はありませんが、関連しています:http://stackoverflow.com/questions/199747/how-to-detect-possible-potential-stack-overflow-problems-in-acc-program?rq=1 –