web-dev-qa-db-ja.com

スタックスマッシングとは何ですか(C)?

コード:

int str_join(char *a,  const char *b) {
   int sz =0; 
   while(*a++) sz++;  
   char *st = a -1, c;  
   *st = (char) 32;
   while((c = *b++)) *++st = c;  
   *++st = 0;
   return sz;
}

....

char a[] = "StringA"; 
printf("string-1 length = %d, String a = %s\n", str_join(&a[0],"StringB"), a);

出力:

string-1長さ= 7、char * a = StringA StringB

***スタックスマッシングが検出されました****:/ T02が終了しました

中止(コアダンプ)

なぜ表示されているのかわかりませんstack smashing? *スタックスマッシングとは何ですか?それとも私のコンパイラのエラーですか?

5
Gonzalez

まあ、スタックスマッシングまたはスタックバッファオーバーフローはかなり詳細なトピックですここで説明します。詳細は このwikiの記事 を参照してください。

ここに示されているコードに来ると、問題は、配列aが、最終的な連結結果を保持するのに十分な大きさでないことです。

それにより、

 while((c = *b++)) *++st = c;

あなたは本質的に 未定義の動作 を呼び出す範囲外のメモリにアクセスしています。これが、プロセスに属さないメモリにアクセスしようとしているために「スタックスマッシング」の問題が発生する理由です。

これを解決するには、配列aに、最初と2番目のstringを連結して保持するのに十分なスペースが含まれていることを確認する必要があります。つまり、より大きな宛先アレイを提供する必要があります。

7
Sourav Ghosh

スタックスマッシングとは、関数のローカル変数のストレージスペースの外(「スマッシュ」過去/スルー)に記述したことを意味します(ほとんどのシステムおよびプログラミング言語では、この領域は「スタック」と呼ばれます)。 「スタックオーバーフロー」や「スタックアンダーフロー」と呼ばれるこのタイプのエラーが見つかることもあります。

あなたのコードでは、Cはおそらくaが指す文字列をスタックに入れています。あなたの場合、スタックスマッシュの原因となる場所は、元のstポインターを超えてaをインクリメントし、それが指す場所に書き込むとき、Cコンパイラーが保証する領域の外側に書き込むことですaに割り当てられた元の文字列用に予約されています。

Cで既に適切に「予約」されているメモリ領域の外側に書き込むと、それは「未定義の動作」になります(これは、C言語/標準が何が起こるかを言わないことを意味します)。通常、他の何かを上書きすることになりますプログラムのメモリ(プログラムは通常、スタック上の変数のすぐ隣に、戻りアドレスやその他の内部詳細などの他の情報を配置します)、またはオペレーティングシステムが使用を許可したメモリの外側にプログラムが書き込みを試みます。いずれにせよ、プログラムは通常、すぐに明らかに(たとえば、「セグメンテーションフォールト」エラーが発生して)壊れることがあり、非常に隠された方法で、後でないと明らかにならないことがあります。

この場合、コンパイラーは、この問題を検出するための特別な保護を備えたプログラムを構築しているため、プログラムはエラーメッセージで終了します。コンパイラがそれを行わなかった場合、プログラムは実行を継続しようとしますが、誤った処理を行ったり、クラッシュしたりする可能性があります。

解決策は、結合された文字列用に十分なメモリをコードに明示的に指示する必要があることに帰着します。これを行うには、 "a"配列の長さを両方の文字列に対して十分な長さに明示的に指定しますが、これは通常、必要なスペースが事前にわかっている単純な用途にのみ十分です。汎用的なソリューションでは、mallocのような関数を使用して、オペレーティングシステムから新しいメモリチャンクへのポインターを取得します。サイズはこれからです(freeおよび同様の関数を使い終わったら、それらから取得するポインターに対してmallocを呼び出すことを忘れないでください)。

3
mtraceur