2

これは、バッファオーバーフローを示す必要があるACプログラムからの抜粋です。

void foo()
{
  char arr[8];
  printf(" enter bla bla bla");
  gets(arr);
  printf(" you entered %s\n", arr);
}

質問は、「バッファオーバーフローを作成せずに、ユーザーが最大で入力できる入力文字数」でした。

char-arrayの長さは8バイトなので、最初の答えは8でした。私の答えは正しいと確信していましたが、より多くの文字を試したところ、セグメンテーション違反が発生する前に入力できる文字の制限は11でした(VirtualBox Ubuntuでこれを実行しています)

だから私の質問は:なぜその8バイト配列に11文字を入力することが可能ですか?

4

5 に答える 5

6

あなたのキャラクターは実際には定義された配列の境界を超えており、未定義の結果につながっています。他の目的で使用されているメモリを上書きするまで、効果は表示されません。

言語とランタイムは、バッファのオーバーフローを防ぐために何もしていません。そのため、これらのバグは非常に悪く、追跡が難しい場合があります。

これらの理由により、のような関数は、データを格納する配列の長さを要求するgetsより安全な関数(この場合)のために非推奨になっています。getline

参照:http ://crasseux.com/books/ctutorial/gets.html

また、ヌルターミネータには8番目が必要なため、確実に格納できるのは7文字のみです。

于 2010-06-08T19:28:13.320 に答える
3

おそらく位置合わせおよび/またはパディングが原因です。そこには実際には使用されていない「予備の」メモリがある可能性があるため、上書きしても何も壊れません。それは、それが正しいことや機能することを意味するのではなく、コンパイラ、椅子、髪の色などを使用するそのマシンで、今すぐ失敗しないということです。

于 2010-06-08T19:28:41.947 に答える
3

(これをVirtualBox Ubuntuで実行しています)それで私の質問は次のとおりです:なぜその8バイト配列に11文字を入力できるのですか?

ゼロ終了の場合は11+1=12文字。get()がarr[8]に13文字を書き込むとIOWクラッシュが発生します。

正確なスタックトレースを投稿していませんが、私の経験から、foo()が戻った後にクラッシュするはずです。

スタックフレーム(void foo()+ Gets()の場合)は(*)のようになります:

  • <下位メモリアドレス>
  • get()ローカル変数
  • get()呼び出しの時点で保存されたスタックポインタ(いわゆる「プロローグ」)
  • 差出人住所、foo()を指す
  • foo()ローカル変数(あなたのchar arr [8])
  • get()呼び出しの時点で保存されたスタックポインタ
  • 差出人アドレス、foo()の呼び出し元を指します
  • <上位メモリアドレス>

すべての情報から、最も重要なビットはリターンアドレスと保存されたスタックポインタです。また、あなたの場合の13バイト目の書き込みは、foo()関数の保存されたスタックポインタを破損している可能性があります。スタックポインタはまだ有効であるため、次のprintf()の呼び出しは成功する可能性が高いです(gets()から戻ることによって最後に変更されます)。ただし、foo()から戻ると、foo()の保存されたスタックポインタ(現在は破損)が復元され、呼び出し元の関数内からスタックにアクセスするアクションはすべて不正なアドレスに移動します。

私の経験から、これは最も可能性の高いシナリオです。スタックが破損していると、何が起こるかを確実に判断するのは非常に困難です。

(*)スタックフレームの構築方法の正確な詳細については、アーキテクチャのABI(アプリケーションバイナリインターフェイス)を探してください。たとえば、Inteli386の場合はIA-32ABI、AMD64の場合はAMD64ABIです。

于 2010-06-08T20:50:47.037 に答える
1

たまたま、余分なデータを保存するのに十分な空きメモリがありました。決してそれに依存してはならず、常にデータを配列の範囲内に保持する必要があります。

この例で実際に許可されている文字数は、7つの「標準」文字とNULL文字(合計8文字)です。

于 2010-06-08T19:32:51.690 に答える
1

Cでは、配列の終わりを超えることを妨げるものは何もありません。文字列をメモリに取り込むと、配列がいっぱいになり、何かが実際に停止するまでメモリをいっぱいにし続けます。大規模なプログラムでは、これはメモリ内の他の変数を上書きすることを意味する可能性があり、追跡が非常に難しいバグにつながります。別の注意点として、メモリ位置に収まる文字列の大きさを決定するときに、Cが文字列の終わりを見つける方法について考えたいと思うかもしれません。

于 2010-06-08T19:32:57.913 に答える