この問題を解決するには、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 |----------+
+--------+
| ... |
+--------+