0

私は次のCコードを持っています:

int main()
{
    char s[10];

    scanf("%s", s);
}

編集:上位 C プログラム用に生成されたアセンブリは次のとおりです。

push   %rbp
mov    %rsp,%rbp
sub    $0x10,%rsp
lea    -0x10(%rbp),%rax
mov    %rax,%rsi
mov    $0x4005e4,%edi
mov    $0x0,%eax
callq  400420 <__isoc99_scanf@plt>
leaveq
retq

ユーザーが配列のサイズを超えて入力すると、他のスタック値が上書きされます。生成されたアセンブリを見ると、gcc がスタック ポインタを 10 ではなく 16 バイト (ワード アラインメント) 下げていることがわかります。そのため、16 バイトを超える値を入力すると、スタックが破損し、main が戻ったときに segfault になる可能性があります。

興味深いこの動作は発生しますが、多くの文字を入力すると発生します。17 文字で失敗しない理由はありますか?

4

3 に答える 3

2

実際の動作は、メモリがスタックに割り当てられる方法の詳細を中心に展開します (これは実装に依存し、未定義の動作につながります)。コードが入力される (呼び出される) とき、その時点でスタック オフセットが 0 であり、RSP が指しているアドレスがリターン アドレスであるとします。

アセンブラをざっと見てみると、次のことが頭に浮かぶかもしれません。

sub    $0x10,%rsp

これにより、ローカル変数用のスペースが予約されます。これは予期していたことです。この 16 バイトが、確保している唯一のスタック スペースであると考えるのは簡単です。それを超えると、戻り値が上書きされ、プロセス (または少なくともスレッド) がクラッシュします。

最初の指示を見逃すのは簡単だからです。

push   %rbp

呼び出し規則の一部としてベース ポインターを保存し (呼び出しスタックをトレースできるようにするため)、追加の 8 バイトを使用します (64 ビット アーキテクチャの場合、32 ビットでは ebp は 4 バイトのみです)。したがって、戻りアドレスの上書きを開始する前に 24 バイトあります。また、24 文字を入力すると、終端の null ('\0') が 25 番目の文字として保存され、これがリターン アドレスを破損することに注意してください。

また、スタックに格納されたベースポインタも上書きされますが、mainその後は使用されません。ただし、次の理由により、呼び出し元が混乱することに注意してください。

leaveq

RSP を RBP に設定し、次に POP RBP に設定します。そのため、呼び出しが戻った後に呼び出し元がローカル変数を参照すると、問題が発生する可能性があります。呼び出し元が異なっていた場合 (別のランタイムを使用していた場合)、その 17 番目の文字への書き込みが問題だった可能性があります (呼び出し元で SEGFAULT が発生する可能性があります)。

于 2013-02-04T20:33:01.290 に答える
0

まず、コードは、バッファの終わりを上書きするためではなく、システムに存在しないメモリにアクセスするためのセグメンテーション違反を取得します[または読み取り専用のメモリに書き込もうとしますが、これでは可能性が低くなります場合]。したがって、コードがmainから戻るまで、これは発生しません-文字列が長すぎてスタックで使用可能な最大アドレスを超えない限り-それを超えない場合は、おそらく数百バイトです。[もちろん、Enterキーを押すまで、入力はまったく発生しません。その時点まで、入力はバッファに保持されるだけですstdin]

第二に、それは「未定義動作」(略してUB)と呼ばれ、何が起こるかが定義されていないことを意味します。したがって、特定の動作が確実であると期待することはできません。期待する動作とは異なる動作をする可能性があります。UBは予測不可能であり、状況によっては完全に正常に機能しているように見える場合があります[上書きしたもの、または「乱用された」ものが実際にはクラッシュを引き起こすほど重要な方法で使用されていないためですが、請求書は顧客は今、それに非常に大きなドルの価値を持っています...;)

于 2013-02-04T20:16:42.903 に答える
0

スタックを上書きすると、プログラムは不安定になります。特定のコンパイラは、内部で定義された独自の方法でスタックをフォーマットします。

これを回避する方法は、入力文字数をバッファーのサイズに制限する方法で読み取るものを使用することです。

1 つの方法は、長さをフォーマットに追加して、"%9s"文字列文字の終わりの余地を残すことです。

于 2013-02-04T20:14:52.467 に答える