1. ホーム
  2. memory

[解決済み] スタックオーバーフローはどのように発生し、どのように防止するのですか?

2022-05-16 13:48:07

質問

スタックオーバーフローはどのように発生し、発生しないようにするための最善の方法、または防止する方法は何でしょうか、特に Web サーバーにおいてですが、他の例も興味深いでしょう。

どのように解決するのですか?

スタック

スタックとは、この文脈では、プログラムの実行中にデータを配置する、最後に入って最初に出てくるバッファのことです。 スタックに 2 つのアイテム、'A' と 'B' をプッシュした場合、スタックからポップオフする最初のアイテムは 'B' で、次のアイテムは 'A' になります。

コード内で関数を呼び出すと、関数呼び出しの次の命令がスタックに格納され、関数呼び出しによって上書きされる可能性のある記憶領域はすべてスタックに格納されます。 呼び出した関数は、それ自身のローカル変数のためにさらにスタックを使い果たすかもしれません。 それが終わると、それは使用したローカル変数のスタックスペースを解放し、前の関数に戻ります。

スタックのオーバーフロー

スタックオーバーフローとは、プログラムが使用する予定よりも多くのメモリをスタックに使用した場合のことです。 組み込みシステムでは、スタック用に 256 バイトしかない場合があり、各関数が 32 バイトを占有する場合、関数呼び出しは 8 個までしかできません。関数 1 は関数 2 を呼び出し、関数 3 を呼び出し、関数 4 ...を呼び出し、関数 8 を呼び出し、関数 9 を呼び出しますが、関数 9 はスタックの外のメモリを上書きしてしまいます。 これはメモリやコードなどを上書きする可能性があります。

多くのプログラマーは、関数 A を呼び出してから関数 B を呼び出し、関数 C を呼び出してから関数 A を呼び出すという間違いを犯しています。

再帰的な関数もこの原因ですが、再帰的に書いている(つまり、関数が自分自身を呼び出している)場合は、これを意識して、静的/グローバル変数を使用して、無限再帰を防ぐ必要があります。

一般的に、使用している OS とプログラミング言語がスタックを管理し、あなたの手には負えません。 コールグラフ (メインから各関数が呼び出すものを示すツリー構造) を見て、関数呼び出しの深さを確認し、意図しないサイクルと再帰を検出する必要があります。 意図的なサイクルや再帰は人為的にチェックする必要があり、互いに何度も呼び合うとエラーになります。

良いプログラミングの実践、静的および動的なテストを越えて、これらの高レベルのシステムでできることは多くありません。

組み込みシステム

組み込みの世界、特に高信頼性のコード(自動車、航空機、宇宙)では、大規模なコードレビューやチェックを行いますが、次のようなことも行います。

  • 再帰とサイクルの不許可 - ポリシーとテストによって強制される
  • コードとスタックを遠くに離す (コードはフラッシュに、スタックは RAM に、そして決して両者が出会うことはない)
  • スタックの周りにガードバンドを配置する - メモリの空の領域にマジックナンバー (通常はソフトウェア割り込み命令ですが、ここには多くのオプションがあります) を詰め、毎秒数百回または数千回、ガードバンドを見て、上書きされていないことを確認するのです。
  • メモリ保護を使用する (つまり、スタック上で実行しない、スタックのすぐ外側で読み取りまたは書き込みを行わない)
  • 割り込みは二次関数を呼び出しません - フラグを設定し、データをコピーし、アプリケーションに処理を任せます (そうしないと、関数呼び出しツリーの 8 深部まで到達し、割り込みが発生し、割り込みの中で別のいくつかの関数に行き、吹き出しを発生させる可能性があります). あなたはいくつかのコールツリーを持っています。一つはメイン処理用、もう一つは各割り込み用です。 もし、割り込みがお互いに割り込むことができれば...そう、ドラゴンがいるのです...。

高レベル言語とシステム

しかし、高レベルの言語ではOS上で動作します。

  • ローカル変数のストレージを減らす (ローカル変数はスタックに保存されます。ただし、コンパイラーはこの点についてかなり賢明で、コール ツリーが浅い場合、大きなローカル変数をヒープに置くことがあります)
  • 再帰を避ける、または厳密に制限する
  • プログラムをより小さな関数に分割しすぎない - ローカル変数を数えなくても、各関数呼び出しはスタックで 64 バイトも消費します (32 ビット プロセッサでは、CPU レジスタ、フラグなどの半分を節約します)。
  • コール ツリーを浅く保つ (上記のステートメントと同様)

ウェブサーバ

スタックを制御できるか、あるいは見ることができるかどうかは、「サンドボックス」によって異なります。 Web サーバーを他の高級言語やオペレーティング システムと同じように扱える可能性は高いです。 それは 例えば SQL サーバーのスタックを破壊することが可能です。

-Adam