20

したがって、ここでは、他の誰かのコードを確認したときに見つけた小さなバッファオーバーフローの問題があると思います。それはすぐに私を不正確で潜在的に危険であると感じましたが、確かに、この「間違い」の実際の結果を説明することはできませんでした。

エラーを実証するためにテストアプリを作成しましたが、オーバーフローに関係なく正しく実行されているように見えることがわかりました(残念ながら)。これは偶然だと信じたいのですが、私の考えが間違っているのか、それともテストアプリに頭が表示されていないという問題が本当にあるのかを判断するためのフィードバックが必要でした。

問題のコード(とにかくそうだと思います):

char* buffer = new char[strlen("This string is 27 char long" + 1)];
sprintf(buffer, "This string is 27 char long");

さて、これが私に目立った理由であり、バッファオーバーフローの可能性があるとしてフラグを立てたいのは、最初のが原因ですstrlen。ポインタ演算のため、の「誤った」配置により、+ 1ではなくがstrlen返されます(「彼の文字列は27文字の長さ」の長さを取る)。、私は信じています、そして27文字をバッファに出力し、バッファオーバーフローを引き起こしました。2627sprintf

それは正しい評価ですか?

私が見ているコードの人のためにこれを実証するためにテストアプリを作成しましたが、デバッガーでも文字列が正しく出力されることがわかりました。また、このコードの前後に他の変数をスタックとヒープに配置して、メモリの隣接領域に影響を与える可能性があるかどうかを確認しようとしましたが、それでも正しい出力を受け取っていました。新しく割り当てられたヒープメモリが隣接していない可能性があることを認識しています。これは、有用なオーバーフローがないことを説明しますが、これが実際に問題であるかどうかを他の人の意見で確認したかっただけです。

これは非常に単純な「質問」なので、何らかの参照を使用して回答をサポートできれば便利です。私はあなたの意見を尊重し、歓迎しますが、最終的な答えとして「はい、そうです」を受け入れるつもりはありません。よろしくお願いします。




更新:多くの追加の洞察を伴う多くの良い答え。残念ながら、すべてを受け入れることはできません。あなたの知識を共有し、私の「第二の意見」であることに感謝します。私は助けに感謝します。

4

11 に答える 11

14

あなたの評価は正しいです。[編集]ジェームズ・カランが言及した訂正を追加。[/編集]

おそらく、割り当ては4、8、または16の次の倍数(一般的な割り当ての粒度)に切り上げられるため、テストアプリは問題を示しませんでした。

これは、31文字の長さの文字列でデモンストレーションできるはずであることを意味します。

または、そのような割り当ての周囲にガードバイトを配置できる「インストルメンテーション」ネイティブメモリプロファイラーを使用します。

于 2010-07-20T14:22:31.997 に答える
6

springfが最後に文字列の終わりのNULをカウントする28文字をバッファに入れることを除いて、あなたの評価は正しいです(そのため、最初に誤って配置された「+1」が必要でした)

私の経験では、デバッガーの外部で何かが失敗したが、デバッガーのステップスルーで機能する場合、100%の時間で、ローカルバッファーをオーバーランしていることに注意してください。デバッガーはスタックにさらに多くのプッシュを行うため、重要なものが上書きされる可能性は低くなります。

于 2010-07-20T14:21:30.887 に答える
3

問題は、メモリのどこかに書き込んでいるが、スタックには書き込んでいないことです。したがって、実際に何が悪いのかを知ることは困難です。ダメージを確認したい場合は、スタックに文字列を割り当ててみてください

char buffer[strlen("This string is 27 char long" + 1)];

そしてそれを過ぎて書く。他の変数が書き込まれます。バイナリがどのように機能するかを本当に知っている場合は、実行するコードを追加することもできます。

このようなバッファオーバーフローを利用するには、必要なデータを書き込んでから、実行するこのデータに「ジャンプ」する方法を見つける必要があります。

于 2010-07-20T14:24:29.633 に答える
1

はい。それで合っています。割り当てられるバッファは、文字列を保持するには小さすぎる2バイトになります。

これはヒープに割り当てられているため、ヒープが破損する可能性があります。ただし、その可能性は、この時点より前に発生した他のメモリの割り当てと解放、および使用されているヒープマネージャによって異なります。詳細については、ヒープオーバーフローを参照してください。

于 2010-07-20T14:23:13.023 に答える
1

この例のポインタ演算は、newに渡される誤った(より短い)長さを生成するというのは正しいことです。このクラッシュを発生させることができない最も可能性の高い理由は、メモリ割り当てによって実際に提供されるバッファスペースの量に関して不確実性があるためです。

ライブラリは、要求されたよりも大きなバッファを提供できます。さらに、バッファに続くものの前に、マシンワードアラインメントルールの対象となる割り当てヘッダーが付いている可能性もあります。これは、次の割り当てヘッダーの前に最大3つのパディングバイト(プラットフォームによって異なります)が存在する可能性があることを意味します。

次の割り当てヘッダー(割り当てられたメモリブロックの管理に使用される)を上書きしても、その次のブロックの所有者がヒープに戻そうとするまで、問題として現れることはありません。

于 2010-07-20T14:26:29.133 に答える
1

多くの歴史的なmalloc実装では、割り当てられたブロックの直前および/または直後に簿記データが配置されます。そのようなデータを上書きしている可能性があります。その場合、メモリを解放しようとする(または次のブロックが発生した場合は解放する)まで、エラー/クラッシュは発生しません。同様に、後続の割り当ての簿記情報が後で文字列を上書きする可能性があります。

最近のmalloc実装では、割り当てに整合性チェックデータをパディングすることでヒープの破損を防ぐ努力をしていると思います。運が良ければ、悪いことは何も起こらないか、後の割り当て/解放操作中に警告メッセージが表示される可能性があります。

于 2010-07-20T14:32:30.293 に答える
1

ヒープ割り当てで試してみましたが、この場合、変数はメモリ内で連続していません。そのため、この場合、バッファをオーバーフローさせるのは困難です。

スタックオーバーフローで購入してみてください

#include "stdio.h"
#include "string.h"

int main()
{
     unsigned int  y      = (0xFFFFFFFF);
     char buffer[strlen("This string is 27 char long" + 1)];
      unsigned int  x      = (0xFFFFFFFF);
      sprintf(buffer, "This string is 27 char long");

      printf("X (%#x) is %#x, Y (%#x) is %#x, buffer '%s' (%#x) \n", &x, x,&y, y, buffer, buffer);
      return 0;
  }

Yが破損していることがわかります。

于 2010-07-20T14:38:46.373 に答える
0

他の人が述べているように、これは良くないと仮定することは完全に正しいです、そしてあなたがこれを見ない理由はパディングです。これを試してみてくださいvalgrind、これは間違いなくそのエラーを見つけるはずです。

于 2010-07-20T14:33:20.007 に答える
0

あなたの本当の問題はあなたが書いているということです

char* buffer = new char[strlen("This string is 27 char long" + 1)];

それ以外の

char* buffer = new char[strlen("This string is 27 char long") + 1];

つまり、最初のアドレスでstrlen()に、文字列の先頭ではないアドレスを指定しているということです

このコードを試してください:

const char szText[] = "This string is 27 char long";
char* buffer = new char[strlen(szText) + 1];
sprintf(buffer, szText);
于 2010-07-20T14:37:08.467 に答える
0

デバッガーで文字列が正常に出力される理由は、sprintfの一部として、末尾のNULL文字がメモリに書き込まれ(この場合は、割り当てたバッファを超えて)、文字列の読み取りに関してはNULL文字が存在するためです。期待どおりに文字列を終了します。

問題は、NULL文字を含むバイトが元のバイトの一部として割り当てられていないnewため、後で別の割り当てに使用される可能性があることです。この場合、後で文字列を読み取るようになると、ガベージが追加された元の文字列を取得する可能性があります。

于 2010-07-20T14:59:18.730 に答える
0

正しいステートメント。文字列の2番目の文字のアドレスをstrlen()に渡すため、結果として長さが1文字短くなります。それとは別に、主な問題はsprintf()にあり、それが安全ではない理由の1つです。

これでもコンパイルして実行します(クラッシュする可能性もあります)。

    char* x = new char;
    sprintf(x, "This is way longer than one character");
    printf("%s", x);

この危険な問題を回避するには、GCCではsnprintf()やasprintf()、MSVCではsprintf_s()などの安全なバージョンの関数を使用する必要があります。

参考までに、この点に関するGNU Cライブラリのドキュメントと、MSDNのsprintf()記事のセキュリティノートを参照してください。

于 2010-07-20T15:07:42.753 に答える