2

私は機能にいくつかの問題がありfree()ます:

#include <stdio.h>
#include <stdlib.h>
int main() {
    char *a=malloc(24);
    char *b=malloc(24);
    free(a);
    free(a);
}

* glibc が検出されました * ./a.out: 二重の解放または破損」があります。2回使ったから不思議じゃないfree()

しかし、今私がそれをしたら:

#include <stdio.h>
#include <stdlib.h>
int main() {
    char *a=malloc(24);
    char *b=malloc(24);
    free(a);
    free(b);
    free(a);
}

私のコンピューターからのエラーはなく、ダブルフリーのエラーメッセージもありませんが、ご覧のとおり、私のコードにはダブルフリーがあります...

何が問題なのか知っていますか?

4

4 に答える 4

3

libcの主な目的はデフォルトでパフォーマンスを向上させることであると推測する以外に、2番目の例でダブルフリーが検出されない理由には答えられません。したがって、ダブルフリーなどのチェックは、ライブラリがデフォルトで検出するために多大な努力を払っているものではないでしょう。

MALLOC_CHECK_ただし、環境変数を 1 に設定してプログラムを実行すると、診断がオンにstderrなります (アボートの場合は変数を 2 に設定し、アボートの場合は変数を 3 に設定し、診断およびクラッシュダンプ情報が に書き込まれstderrます)。

$ gcc -o test test.c
$ ./test
$ MALLOC_CHECK_=1 ./test
*** glibc detected *** ./test: free(): invalid pointer: 0x00000000015e4010 ***

(環境変数名の末尾のアンダースコアに注意してください)。

で構成された libc のヒープ チェックの動作は、 「ヒープの一貫性チェック」ページMALLOC_CHECK_に記載されています。

malloc、realloc、および free の使用におけるバグをチェックして防止するもう 1 つの方法は、環境変数 MALLOC_CHECK_ を設定することです。MALLOC_CHECK_ が設定されている場合、単純なエラー (同じ引数での free の二重呼び出しや、1 バイトのオーバーラン (off-by-one バグ) など) に対して耐性を持つように設計された特別な (効率の低い) 実装が使用されます。ただし、このようなエラーをすべて防止できるわけではなく、メモリ リークが発生する可能性があります。MALLOC_CHECK_ が 0 に設定されている場合、検出されたヒープの破損は黙って無視されます。1 に設定すると、診断が stderr に出力されます。2 に設定すると、abort がすぐに呼び出されます。そうしないと、クラッシュがかなり後で発生する可能性があり、問題の真の原因を追跡するのが非常に困難になるため、これは便利です。

于 2013-05-22T18:16:55.573 に答える
1

この問題を解決するには、glibc の malloc メカニズムを理解している必要があります。何も知らない場合は、これを読んで glibc malloc に関する一般的な知識を得ることができます。

glibc はビンを使用して、頻繁なシステム呼び出しを避けるために解放したチャンクを管理します。小さなメモリ空間がより頻繁に割り当てられ、解放されるという事実のために、glibc は高速ビン(等距離の単一のリンクされたリスト) を使用して、 global_max_fast (デフォルトは 64B または 128B)未満のメモリ空間を管理します。glibc が行うことは、解放したチャンクのfdを高速ビンが指す場所に設定し、高速ビンがそのチャンクを指すようにすることです。

free() は、次の隣接チャンクのPREV_INUSEビットを使用して、チャンクが解放されたかどうかを確認します。ただし、解放したチャンクが高速ビンに追加されると、glibc はPREV_INUSEビットを設定しません。高速ビンのポインターが、解放したチャンクのポインターと同じかどうかを確認するコードがあります。同じである場合、プログラムが破損するため、ポインターを 2 回解放することはできませんが、2 つのポインターを解放することはできます。次に、理解に役立つ簡単な図を示します。

解放するとき(a):

+++++++++++++++++++++++
+  16  +  24  +  32  +  ...
+++++++++++++++++++++++
          |
          |
          |
          |
          +--->+--------+
               |prevsize|
               +--------+
               |  size  |
               +--------+
               |fd=NULL |
               +--------+
               |  ...   |
               +--------+

解放するとき(b):

+++++++++++++++++++++++
+  16  +  24  +  32  +  ...
+++++++++++++++++++++++
          |
          |
          |
          |
          |    +--------+<---------+
          |    |prevsize|          |
          |    +--------+          |
          |    |  size  |          |
          |    +--------+          |
          |    |fd=NULL |          |
          |    +--------+          |
          |    |  ...   |          |
          +--->+--------+          |
               |prevsize|          |
               +--------+          |
               |  size  |          |
               +--------+          |
               |   fd   |----------+
               +--------+
               |  ...   |
               +--------+

再び解放すると:

+++++++++++++++++++++++
+  16  +  24  +  32  +  ...
+++++++++++++++++++++++
          |
          |
          |
          |
          +--->+--------+<---------+
               |prevsize|          |
               +--------+          |
               |  size  |          |
               +--------+          |
               |   fd   |---+      |
               +--------+   |      |
               |  ...   |   |      |
               +--------+<--+      |
               |prevsize|          |
               +--------+          |
               |  size  |          |
               +--------+          |
               |   fd   |----------+
               +--------+
               |  ...   |
               +--------+
于 2016-12-11T14:10:12.560 に答える
0

ロギング機能をテストするために、ダブルフリー libc 例外を生成しようとしていた機能テストで、コードに同じ問題がありました。

結局のところ、リリース用にビルドされたときに、gcc はmalloc()呼び出しと両方の呼び出しを最適化していましたfree()(-O3 だと思います)。

volatileその問題を解決するために onを使用しようとしましaたが、gcc は. 次に、に割り当てようとしましたが、gcc はまだ最適化されていません。メッセージを挿入することで、ようやく機能するようになりました。volatile void*freeavolatile void* aaaaastd::cout

char* a = malloc(24);
std::cout << "a is " << a << std::endl;
free(a);
free(a);

これで、コードは希望どおりに正しく「クラッシュ」し、目的のダブルフリー メッセージとスタック ダンプが出力されます。

于 2016-11-29T23:45:41.257 に答える
0

Valgrind を使用して、二重解放または破損を検出します。valgrindリンクの使用に関するブログをチェックしてください

于 2013-11-06T23:24:37.720 に答える