質問
と考え、下記のプログラム
char str[5];
strcpy(str,"Hello12345678");
printf("%s",str);
起動時にこのプログラムは分割ます。
がの場合strcpyに交換され、プログラムです。
strcpy(str,"Hello1234567");
ではではクラッシュしようとした場合にコピー strその他の文字列以上の5字長さです。
なぜなぶための"Hello1234567"みぶための"Hello12345678"ieの文字列の長さ13以上13.
このプログラムのために、32ビット機です。
解決
あなたが興味あるべき水準行動の3種類があります。
1 / の定義された動作の。これは、すべての準拠の実装で動作します。自由にこれを使用します。
2 / の処理系定義の動作の。述べたように、それは実装に依存しますが、少なくともそれはまだ定義されています。実装は、彼らはこれらの例に何をすべきか文書化する必要があります。あなたは移植性を気にしない場合は、これを使用します。
3 / の未定義の動作の。何でも起れる。そして、私たちは、の何でもの、最高およびコンピュータ全体を裸の特異点に崩壊し、自分自身を嚥下など、あなたとあなたの同僚の大部分を意味します。これを使用することはありません。今まで!真剣に!私はそこに来てはいけません。
char[5]
によりその4つの文字と0バイトをコピーすると、未定義の動作である。
、あなたはほぼ確実にスタックし、あなたのプログラムのいくつかの非クラッシュ情報を上書きしている可能性が高いとにかく間違った結果を生成します。少なくとも、それはあなたがおそらく悪い効果に頼って停止しますので、実際には、クラッシュが優れています。
(利用可能な情報と、この場合にchar[14]
)より適切なものにアレイのサイズを増やすか、対応することができるいくつかの他のデータ構造を使用します。
のアップデート:の
あなたは、なぜ、余分な7つの文字を見つけるととても心配に見えるので問題が、8つの文字を起こさない、のはmain()
に入る上で可能なスタックレイアウトを想定せありません。実際のレイアウトは、あなたのコンパイラが使用する呼び出し規約に依存しますので、私は「可能」と言います。 Cスタートアップコードがmain()
の開始時にスタック、argc
とargv
でmain()
を呼び出すので、char[5]
のためのスペースを割り当てた後、このようになります:
+------------------------------------+
| C start-up code return address (4) |
| argc (4) |
| argv (4) |
| x = char[5] (5) |
+------------------------------------+
あなたがバイトHello1234567\0
を書くときます:
strcpy (x, "Hello1234567");
x
する、それはそれは大丈夫だ、argc
からの復帰に、argv
とmain()
を上書きしますが。具体的にHello
はx
を移入し、1234
はargv
を移入し、567\0
はargc
に移入されます。
argc
および/またはargv
を使用しないでください
+------------------------------------+ Overwrites with:
| C start-up code return address (4) |
| argc (4) | '567<NUL>'
| argv (4) | '1234'
| x = char[5] (5) | 'Hello'
+------------------------------------+
あなたはHello12345678\0
を書く場合は、x
する(「8」余分に注意)、それはargc
とargv
を上書きし、のものリターンアドレスの1バイトmain()
はに戻るしようとしたとき、そのので、 Cスタートアップコードは、それが代わりに妖精の土地に消灯します:
+------------------------------------+ Overwrites with:
| C start-up code return address (4) | '<NUL>'
| argc (4) | '5678'
| argv (4) | '1234'
| x = char[5] (5) | 'Hello'
+------------------------------------+
繰り返しますが、これはコンパイラの呼び出し規約に完全に依存します。それはあなたが別の3つの文字を書いたまで、さまざまなコンパイラは常に4バイト、コードの複数のパッドアウト配列が失敗しないだろうだろうことが可能です。同じコンパイラは、アライメントが満たされる保証するために、異なるスタックフレームに変数を割り当てることができる。
これは、彼らが未定義で何を意味するかです:あなたはの起こるだろう。のかわからない。
。他のヒント
、余分なデータは、プログラムをクラッシュさせる必要がありますどのくらいのためにあなたはスタックにコピーしているので、コンパイラは、スタック上に置かれているものに依存しています。
いくつかのコンパイラは、バッファサイズを超える1バイトだけでクラッシュするコードを生成可能性がある - 行動が何であるか未定義の
。私はサイズ13はあなたの関数の戻りをクラッシュ戻りアドレス、または類似した何かを、上書きするのに十分であると思います。しかし、別のコンパイラや他のプラットフォームでは、/、異なる長さでクラッシュしますことができます。
それは長い時間のために実行された場合、それほど重要な何かが上書きされていた場合は、また、あなたのプログラムは、異なる長さとクラッシュする可能性があります。
32ビットIntelプラットフォームについての説明は以下の通りです。あなたが文字を宣言するときは、[5]コンパイラは本当にため、アライメントの8つのバイトを割り当てスタック上。
:関数は、以下のプロローグを持っているため、それは典型的ですpush ebp
mov ebp, esp
これは、スタック上のEBPのレジストリ値を保存し、その後、ESPのパラメータにアクセスするには、ESPの値を使用するためのEBPに値を登録するに移動します。これは、EBP値で占有されるように、スタック上の4バイト以上につながります。
エピローグEBPで復元されたが、その値は通常だけなので、それはほとんどの場合でけがをしないことがあり上書きし、スタックに割り当てられた関数のパラメータにアクセスするために使用されます。
あなたは以下のレイアウト(スタックがインテルに下向きに成長する)必要がありますので:あなたのアレイの8バイト、EBPのために、その後4バイト、その後、通常のリターンアドレスを
あなたがプログラムをクラッシュさせ、少なくとも13のバイトを上書きする必要がある理由このです。
上記の回答に追加するには:あなたは、このような Valgrindののようなツールでこれらのようなバグをテストすることができます。 Windowsであれば、で見て、これはSO スレッド>。
これは、「STR」の配列の後にスタック上に何があるかに依存します。あなたはその多くの文字をコピーするまで、あなただけの重大な何も踏みつけてはならない起こるます。
は、機能している他に何に依存する予定ですので、コンパイラはあなたが使用し、おそらくコンパイラオプションすぎます。
13は、STR配列の後に二つの非クリティカルな言葉があります示唆5 + 8、あり、そして重要な何か(多分リターンアドレス)
これは未定義の動作(UB)の純粋な美しさです:それは未定義の
。あなたのコード:
char str[5];
strcpy(str,"Hello12345678");
は、5バイト/文字を保持することができるstr
する14のバイト/文字を書き込みます。これはUBを呼び出します。
Q:なぜなぶための"Hello1234567"みぶための"Hello12345678"ieの文字列の長さ13以上13.
- での動作は定義されていません。利用strncpy.こちらをご覧ください。 http://en.wikipedia.org/wiki/Strcpy ます。
それは元の文字列の長さを有している場合NULL終端を追加しない> = nは先列のサイズであるので、動作は保証されませんので。 strncpyをを使用してください。このページを参照してください。 http://en.wikipedia.org/wiki/Strcpyする 詳細については、ます。
はstrncpyは危険である。
char s[5];
strncpy(s,5,"test12345");
printf("%s",s); // crash
私たちは常にこれを軽減するためにstrlcpyにしてください。