全体の目的は、スタックオーバーフローが発生する実際の様子を観察すること。
具体的には、次の4点に関する観察を目的としました。
1点目は、自動変数の配列のサイズとエラーの関係。
2点目は、自動変数のかわりに静的変数を使用したり、mallocによるメモリの獲得を行なったときの、スタックの様子。
3点目は、1点目の裏返しで、スタックのサイズを増加させたときの様子。
4点目は、コンパイルの際に、スタックオーバーフローの検査を行なうコードを埋め込んだり(lcc.exeの「-h」オプション)、「intlib」をリンクすることの影響。
実験の材料に用いたプログラムをリスト4に示します。
[リスト4] | #include<stdio.h> | #include<malloc.h> | int main() { | /* char A[3000]; *//* パターン1 */ | /* static char A[3000]; *//* パターン2 */ | /* char *A; *//* パターン3 */ | /* A=malloc(3000); *//* パターン3 */ | A[0]='\0'; | printf("stack: %d\n", stackavail()); | /* free(A); *//* パターン3 */ | printf("done\n"); | return 0; | }
リスト4のプログラムで「パターン1」の部分を生かしたものを材料として、目的の1番と4番に関する実験を行ないました。 主要な結果は以下のとおり。
[表1] --------------------------------------------------------------- 配列Aのサイズ コンパイル条件 終了時の様子(スタック残量) --------------------------------------------------------------- 2376 (ノーマル) 正常(114) -h 正常(126) -lintlib 正常(112) -h, -lintlib 正常(124) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 2377 (ノーマル) 異常 -h 正常??(0124) -lintlib 異常 -h, -lintlib 正常(122) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 2380 (ノーマル) 異常 -h 異常 -lintlib 異常 -h, -lintlib 正常(120) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 2400 (ノーマル) 異常 -h 異常 -lintlib 異常 -h, -lintlib 異常 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 3000 (ノーマル) 異常 -h 「stack overflow」 -lintlib 異常 -h, -lintlib 「stack overflow」 -------------------------------------------------------------- 註: プログラム終了時の異常とは、フリーズを基本として、そこに表示の乱れや無関係なエラーメッセージが伴ったり伴わなかったりするもの(不定)。
次に、「パターン2」のプログラムと、「パターン3」のプログラムを材料として、目的の2番と4番に関する実験を行ないました(配列Aのサイズは3000に固定)。
[表2] -------------------------------------------------------------- メモリの獲得 コンパイル条件 終了時の様子(スタック残量) -------------------------------------------------------------- static (ノーマル) 正常(2500) -h 正常(2496) -lintlib 正常(2498) -h, -lintlib 正常(2494) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - malloc (ノーマル) 正常(2490) -h 正常(2502) -lintlib] 正常(2488) -h, -lintlib 正常(2500) --------------------------------------------------------------
最後に、「パターン1」のプログラムを材料として、目的の3番と4番に関する実験を行ないました(配列Aのサイズは3000に固定)。
[表3] ------------------------------------------------------------------ スタックのサイズ コンパイル条件 終了時の様子(スタック残量) ------------------------------------------------------------------ 3000 (ノーマル) 異常 -h 異常 -lintlib 異常 -h, -lintlib 「stack overflow」 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 3110 (ノーマル) 異常 -h 異常 -lintlib 異常 -h, -lintlib 異常 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 3120 (ノーマル) 異常 -h 異常 -lintlib 異常 -h, -lintlib 正常(120) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 3130 (ノーマル) 異常 -h 正常(132) -lintlib 正常(118) -h, -lintlib 正常(130) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 3140 (ノーマル) 正常(130) -h 正常(142) -lintlib 正常(128) -h, -lintlib 正常(140) ------------------------------------------------------------------ 註: プログラム終了時の異常とは、フリーズを基本として、そこに表示の乱れや無関係なエラーメッセージが伴ったり伴わなかったりするもの(不定)。
実験1-1と1-3の結果からは、スタックには150バイト程度の余裕がないと危険であることがわかりました(目的1,3)。
実験1-2の結果からは、静的変数やmalloc関数を使用した場合、スタックはほとんど消費されないことが確認されました(目的2)。
また、すべての実験を通じて、コンパイル時の条件がスタックオーバーフローの発生に密接なかかわりを持っていることが確認されました(目的4)。
なかでも、「-h」オプションが必ずしも有効に機能しない場合があることや(実験1-1,1-3)、intlibのリンクがエラーの発生を抑える効果を持つことは(実験1-3)、特に注意を要する点と思われます。