:
- オーバーフロー変数のアクセスを含む関数が呼び出された時点ですでにスタック上のデータの量
- 総
におけるオーバーフロー変数/配列スタックは下方を成長させることに注意してくださいに書き込まれたデータの量。私。プロセスの実行は、スタックとして使用されるメモリのエンドに近いスタックポインタから開始されます。システムの初期化コードは、ある種の「スタートアップ情報」を作成時にプロセスに渡すことを決定し、しばしばスタック上で実行することがあるためです。
これは通常です.エラーモード - オーバーフローコードを含む関数から復帰するとクラッシュします。
合計スタックのバッファに書き込まれるデータの量が、以前に(呼び出し元/初期化コード/その他の変数によって)使用されていたスタックスペースの合計よりも大きい場合、メモリアクセス最初にスタックの先頭(先頭)を超えて実行されます。クラッシュするアドレスは、スタックの先頭を超えてメモリにアクセスしているため、何もマップされていないため、ページ境界を超えた直後になります(SIGSEGV
)。
この合計が現在スタックの使用されている部分のサイズより小さい場合は、うまく動作し、の後にというクラッシュが発生します。あなたの関数から戻るときは、x86/x64ではtrueです)。これは、CPU命令ret
が実際にスタック(復帰アドレス)からワードを取り、そこで実行をリダイレクトするためです。予想されるコードの場所の代わりに、このアドレスに何らかのゴミが含まれていると、例外が発生し、プログラムが終了します。 main()
開始は、それが間、様々な目的のために、スタック上のスペースを割り当てますとき
[ esp ] <return addr to caller> (which exits/terminates process)
[ esp + 4 ] argc
[ esp + 8 ] argv
[ esp + 12 ] envp <third arg to main() on UNIX - environment variables>
[ ... ]
[ ... ] <other things - like actual strings in argv[], envp[]
[ END ] PAGE_SIZE-aligned stack top - unmapped beyond
:main()
が呼び出されると、スタックは(32ビットx86のUNIXプログラムに)次のようになります。これを説明するために
オーバーフローする配列をホストする他のものこれは、あなたがarr[2]
を超えて道を幸せにアクセスできることを意味
[ esp ] <current bottom end of stack>
[ ... ] <possibly local vars of main()>
[ esp + X ] arr[0]
[ esp + X + 4 ] arr[1]
[ esp + X + 8 ] arr[2]
[ esp + X + 12 ] <possibly other local vars of main()>
[ ... ] <possibly other things (saved regs)>
[ old esp ] <return addr to caller> (which exits/terminates process)
[ old esp + 4 ] argc
[ old esp + 8 ] argv
[ old esp + 12 ] envp <third arg to main() on UNIX - environment variables>
[ ... ]
[ ... ] <other things - like actual strings in argv[], envp[]
[ END ] PAGE_SIZE-aligned stack top - unmapped beyond
:これはそれのように見えるようになります。バッファオーバーフローに起因するさまざまなクラッシュのテイスターのために
、これを試みる:
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char **argv)
{
int i, arr[3];
for (i = 0; i < atoi(argv[1]); i++)
arr[i] = i;
do {
printf("argv[%d] = %s\n", argc, argv[argc]);
} while (--argc);
return 0;
}
、あなたは少し(たとえば、10)ビットによってバッファをオーバーフローさせたときにクラッシュがものになるかどうか異なる見ますスタックの終わりを超えてオーバーフローした場合と比較して、さまざまな最適化レベルと異なるコンパイラで試してみてください。 (例:コンパイラがi
またはargc
をスタックに入れたときにコードがそれを上書きすると、さまざまな場所、多分無限ループでさえクラッシュするだけでなく、すべて正しくargv[]
を印刷するとは限らない)ループ)。
スタックオーバーフローは、スタックから多すぎるメモリが割り当てられた場合に発生します。この場合、 'sizeof(int)== 4 'と仮定すると、スタックから12バイトの小さなバイトを割り当てたことになります。あなたのコードは配列の最後を超えて書いています。スタックオーバーフローではありません。それは_振る舞い_です。 –
RAMの残りの部分を持っているのと同じ場所から来ています。 'arr [3]'は "私の使用に利用可能なスペースの3" intを指定する "という意味で、" etherからスペースの3 "intを作る"という意味ではありません。物理的に可能です。 Davidが言うようにUBである「arr」に隣接するメモリ/アドレス(実際には隣にありますが、実際には)が何であっても、あなたはそれに書きしています。はい、それはあなたのスタックの一部です(CとC++の標準はスタックについては話しませんが、実際には自動変数がどこに行くのでしょうか)。 –
@vprajan - 注意を引く良い答えがあるので、質問を反映するためにタイトルを更新しました。 –