4

x86 Linux システムでスタック オーバーフローを引き起こす特定の条件があります。

  • struct my_big_object[HUGE_NUMBER]スタック上。それを歩くと、最終的に が発生しSIGSEGVます。
  • alloca()ルーチン ( と同様ですが、malloc()スタックを使用し、自動的にそれ自体を解放し、SIGSEGV大きすぎる場合は爆発します)。更新: 最初に述べたように、 alloca() は正式に廃止されたわけではありません。それはただ落胆するだけです。

特定のオブジェクトに対してローカル スタックが十分に大きいかどうかをプログラムで検出する方法はありますか? を介してスタックサイズを調整できることを知っているulimitので、方法があることを願っています(ただし、移植性はありません)。理想的には、次のようなことができるようになりたいです。

int min_stack_space_available = /* ??? */;
if (object_size < min_stack_space_available)
{
    char *foo = alloca(object_size);
    do_stuff(foo);
}
else
{
    char *foo = malloc(object_size);
    do_stuff(foo);
    free(foo);
}
4

12 に答える 12

5

プロセスのスタック スペースのサイズを確認し、使用量を差し引くことで、プロセスが使用できるスタック スペースを特定できます。

ulimit -s

Linux システムでのスタック サイズを示します。プログラムによるアプローチについては、getrlimit()を確認してください。次に、現在のスタックの深さを決定するために、スタックの一番上へのポインターを 1 から一番下まで減算します。例(テストされていないコード):

unsigned char *bottom_of_stack_ptr;

void call_function(int argc, char *argv) {
    unsigned char top_of_stack;
    unsigned int depth = (&top_of_stack > bottom_of_stack_ptr) ? 
        &top_of_stack-bottom_of_stack_ptr : 
        bottom_of_stack_ptr-&top_of_stack;

    if( depth+100 < PROGRAMMATICALLY_DETERMINED_STACK_SIZE ) {
        ...
    }
}

int main(int argc, char *argv) {
    unsigned char bottom_of_stack;
    bottom_of_stack_ptr = &bottom_of_stack;
    my_function();
    return 0;
}
于 2009-01-09T07:45:36.927 に答える
1

alloca関数は推奨ではありません。ただし、POSIXには含まれておらず、マシンおよびコンパイラーにも依存します。allocaのLinuxマンページには、「特定のアプリケーションでは、mallocを使用する場合に比べて効率が向上し、場合によっては、longjmp()またはsiglongjmp()を使用するアプリケーションでのメモリの割り当て解除も簡素化できます。その使用はお勧めしません。」

マンページには、「スタックフレームを拡張できない場合でもエラー表示はありません。ただし、割り当てに失敗すると、プログラムはSIGSEGVを受信する可能性があります」と記載されています。

mallocのパフォーマンスは、Stackoverflow Podcast#36で実際に言及されました。

(これはあなたの質問に対する適切な答えではないことを私は知っていますが、とにかく役立つかもしれないと思いました。)

于 2009-01-09T08:09:41.200 に答える
1

(Web サイトから) スタック オーバーフローが発生した場合など、ページ フォールトGNU libsigsegv処理するために使用できます。

一部のアプリケーションでは、スタック オーバーフロー ハンドラーが何らかのクリーンアップを実行するか、ユーザーに通知した後、すぐにアプリケーションを終了します。他のアプリケーションでは、スタック オーバーフロー ハンドラが longjmps でアプリケーションの中心点に戻ります。このライブラリは、両方の使用をサポートしています。2 番目のケースでは、ハンドラーは通常のシグナル マスクを復元する必要があり (ハンドラーの実行中に多くのシグナルがブロックされるため)、制御を移すために sigsegv_leave_handler() も呼び出す必要があります。それからだけ離れて longjmp することができます。

于 2009-01-10T14:27:59.803 に答える
1

alloca() は失敗すると NULL を返します。alloca(0) の動作は未定義であり、プラットフォーム バリアントであると思います。do_something() の前にそれを確認すると、SEGV に遭遇することはありません。

いくつか質問があります。

  1. なぜ、ああ、なぜ、スタック上に大きなものが必要なのですか? ほとんどのシステムのデフォルト サイズは 8M ですが、それでも小さすぎますか?
  2. alloca() を呼び出す関数がブロックする場合、mlock() / mlockall() を介して同じ量のヒープを保護すると、時間の経過とともにほぼ同じアクセス パフォーマンス (つまり、「私を交換しないでください!」) が保証されますか? よりアグレッシブな 'rt' スケジューラを使用している場合は、とにかくそれらを呼び出すことをお勧めします。

質問は興味深いですが、眉をひそめます。それは私のスクエアペグラウンドホールオメーターの針を上げます.

于 2009-01-09T07:25:28.713 に答える
1

非推奨の alloca() ルーチン (malloc() と同様ですが、スタックを使用し、自動的に解放され、大きすぎる場合は SIGSEGV で爆発します)。

alloca が非推奨になったのはなぜですか?

とにかく、あなたの場合、alloca と malloc はどのくらい高速ですか? (その価値はありますか?)

また、十分なスペースが残っていない場合、alloca から null が返されませんか? (mallocと同じ?)

また、コードがクラッシュすると、どこでクラッシュするのでしょうか? それは alloca にありますか、それとも doStuff() にありますか?

/ヨハン

于 2009-01-09T06:59:23.417 に答える
1

Open Watcom C/C++などのいくつかのコンパイラは、まさにそれを可能にする stackavail() 関数をサポートしています。

于 2009-01-09T15:14:37.947 に答える
1

これが Linux に当てはまるかどうかはわかりませんが、Windows では、成功したとしても大きなスタック割り当てでアクセス違反が発生する可能性があります!

これは、デフォルトでは、Windows の VMM が実際にはスタック RAM の上位数ページ (正確な数は不明) の 4096 バイト ページのみをページング可能 (つまり、ページファイルによってサポートされている) とマークするためです。トップ; アクセスが現在の「境界」に近づくにつれて、下と下のページがページング可能としてマークされます。ただし、これは、スタックの最上位よりもはるかに下にある初期のメモリの読み取り/書き込みが、実際にはまだ割り当てられていないため、アクセス違反を引き起こすことを意味します!

于 2009-01-09T08:50:43.097 に答える
1

スタックに割り当てたい理由についてはあまり言いませんが、魅力的なスタックメモリモデルである場合は、ヒープにもスタック割り当てを実装できます。プログラムの先頭に大量のメモリを割り当て、これへのポインタのスタックを保持します。これは、通常のスタック上のフレームに対応します。関数が戻ったときに、プライベート スタック ポインターをポップすることを忘れないでください。

于 2009-01-09T09:16:55.807 に答える
0

これがあなたの質問に対する直接の答えではない場合でも、Linux上で実行時にそのような問題を検出するための素晴らしいツールであるvalgrindの存在を知っていることを願っています。

スタックの問題に関しては、これらのオーバーフローを検出する固定プールからオブジェクトを動的に割り当てることを試みることができます。単純なマクロウィザードを使用すると、デバッグ時にこれを実行し、リリース時に実際のコードを実行することができます。したがって、(少なくとも実行しているシナリオでは)あまり多くを費やしていないことがわかります。詳細とサンプル実装へのリンクは次のとおりです。

于 2009-01-09T07:49:39.367 に答える
0

スタック領域の末尾は、OS によって動的に決定されます。仮想メモリ領域 (VMA) を OS に大きく依存する方法 ( libsigsegv/src/の stackvma* ファイルを参照) で調べることにより、スタックの「静的な」境界を見つけることができますが、さらに考慮する必要があります。

于 2016-10-14T19:35:43.777 に答える
0

私が考えることができる良い方法はありません。getrlimit() (前に提案) といくつかのポインター演算を使用することで可能でしょうか? しかし、最初に、本当にこれが必要かどうかを自問してください。

ボイド *closeToBase;

主要 () {
  int closeToBase;
  stackTop = &closeToBase;
}

int stackHasRoomFor(int バイト) {
  int currentTop;
  return getrlimit(...) - (¤tTop - closeToBase) > バイト + SomeExtra;
}

個人的には、私はこれをしません。ヒープに大きなものを割り当てます。スタックはそのためのものではありませんでした。

于 2009-01-09T09:05:47.700 に答える
-2

これが明白なことを述べている場合は申し訳ありませんが、(そのサイズの) alloca を試してスタック オーバーフロー例外をキャッチするだけで、特定のスタック割り当てサイズをテストする関数を簡単に作成できます。必要に応じて、関数スタック オーバーヘッドの事前定義された計算を使用して、関数に入れることができます。例えば:

bool CanFitOnStack( size_t num_bytes )
{
    int stack_offset_for_function = 4; // <- Determine this
    try
    {
        alloca( num_bytes - stack_offset_for_function );
    }
    catch ( ... )
    {
        return false;
    }

    return true;
}
于 2009-01-09T07:02:47.777 に答える