7

この質問は、C99 の可変長配列に関するこの質問に関連していますが、これとは異なります。

答えは、可変長配列 (または固定サイズの単なる大きな配列) をスタックに割り当てる際の 1 つの危険性は、たとえば、malloc割り当てが成功したかどうかを呼び出し元に明示的に伝える を呼び出すのとは対照的に、割り当てがサイレントに失敗する可能性があることを指摘しています。

最新の非組み込みコンパイル プラットフォームは、無効なメモリ ゾーンを使用して、追加コストなしでスタック オーバーフローを検出します (チェックは、MMU によって既に無料で行われているチェックのみです)。ローカル配列が非常に大きいと、スタック ポインタが無効な領域を飛び越える可能性があるため、上記の問題を 100% 回避することはできません。

この検出に通常割り当てられるページ数を知っている人はいますか? 少なくとも 4KiB になると思いますが、それ以上になる可能性もあります。それはコンパイラーまたは OS によって選択されたものですか? どちらの場合でも、それを変更する方法はありますか?

4

2 に答える 2

7

最も一般的な方法は、1ページ、通常は4kを使用することだと確信しています。allocaただし、優れたコンパイラは、関数エントリ(またはVLA /割り当て)のページサイズよりも大きいスタックフレームの各ページに順番にアクセスして、ガードページが確実にヒットするようにします。GCCはオプションでこれを行うことができます。http://gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html#Code-Gen-Optionsおよび-fstack-checkオプションを参照してください。

于 2011-04-04T19:51:09.843 に答える
6

Windows では、1 つの 4KB ページです (少なくとも x86 では): Windows NT ベースのアプリケーションのスタック チェックの説明を参照してください。

この自動拡張方法では、メモリのコミットされた部分に隣接する、予約済みでコミットされていないメモリ ページであるガード ページを使用します。アプリケーションがガード ページにアクセスすると、オペレーティング システムはそのページをコミットし、コミットされていない次のページが新しいガード ページになります。自動スタック拡張はガード ページに対してのみ機能し、スタック メモリは 4K または 1 ページの増分で拡張する必要があります。アプリケーションがガード ページにアクセスする前に、スタック メモリの予約されているがコミットされていない別のページにアクセスすると、通常のページ フォールト例外が発生し、予期しない動作が発生する可能性があります。

...

このエラーを回避するために、ローカル割り当てが 4K を超えるたびに、コンパイラは __chkstk() 関数を呼び出します。Windows NT __chkstk() 関数は、MS-DOS バージョンのようにスタック オーバーフローを明示的にチェックしません。現在のスタック ポインターの場所から要求された割り当てまで、4K ごとにメモリ アドレスにアクセスするだけです。これにより、適切な順序でガード ページがトリガーされ、必要に応じて追加のメモリがスタックにコミットされます。

GCC、GCC スタック チェックの場合

C99 の VLA が WinNT の動作をどのように変更するか、または変更するかどうかはわかりません。

于 2011-04-04T19:48:36.633 に答える