glibc detected (double free)

Environment: Ubuntu 10.04
C compiler: gcc version 4.4.3

如 果 C 程式出現了下列的錯誤訊息 (glibc detected .... double free ...),依據錯誤訊息的說明,它是 glibc 預防程式對同一塊記憶體重複 free 所進行的偵測,如果想要強制執行程式以觀察執行狀況,只要在執行程式前,將環境變數 MALLOC_CHECK_ 設定為 0 就可以關閉這項檢查,ex.   MALLOC_CHECK_=0 ./test (執行檔名是 test )。

寫了下列的小程式來測試,這個程式沒做什麼事情,主要只是配置了一個動態的二維記憶體空間 (10 * 32),再一一釋放記憶體,程式就結束了,這個程式就會引起 double free 的問題,在往下看結果之前,請研究這隻程式哪裡有問題? 


  1 #include <stdio.h>
  2 #include <stdlib.h>
  3
  4 int main(int argc, char *argv[])
  5 {
  6
  7         char **str = NULL;
  8         const int x=10;
  9         const int y=32;
 10         int i;
 11
 12         str = (char**) malloc ( sizeof(char) * x );
 13
 14         /* NULL pointer */
 15         if( ! str ) {
 16                 printf("NULL Pointer\n");
 17                 return -1;
 18         }
 19
 20         printf("Address of str=%x, and point to %x\n", &str, str);
 21
 22         bzero(str, x);
 23
 24         printf("---------------------------------------------------\n");
 25
 26         for( i=0; i<x; i++) {
 27
 28                 str[i] = (char *)malloc( sizeof(char) * y );
 29
 30                 if( ! str[i] ) {
 31                         printf("NULL Pointer\n");
 32                         return -1;
 33                 }
 34
 35
 36                 printf("Address of str[%d]=%x, and point to %x\n", i, &str[i], str[i]);
 37
 38                 bzero(str[i], y);
 39                 strcpy(str[i], "hello");
 40
 41         }
 42
 43
 44         printf("---------------------------------------------------\n");
 45
 46         for( i=0; i<x; i++) {
 47
 48                 if( str[i] ) {
 49                         free( str[i] );
 50                 }
 51         }
 52
 53         if(str) {
 54                 free (str);
 55         }
 56
 57         return 0;
 58 }
 59

上面的程式執行時出現了錯誤訊息


ming@linuxbox:/tmp$ gcc test.c -o test
ming@linuxbox:/tmp$ ./test

Address of str=bfe0e6ec, and point to 8884008
---------------------------------------------------
Address of str[0]=8884008, and point to 8884018
Address of str[1]=888400c, and point to 8884040
Address of str[2]=8884010, and point to 8884068
Address of str[3]=8884014, and point to 8884090
Address of str[4]=8884018, and point to 88840b8
Address of str[5]=888401c, and point to 88840e0
Address of str[6]=8884020, and point to 8884108
Address of str[7]=8884024, and point to 8884130
Address of str[8]=8884028, and point to 8884158
Address of str[9]=888402c, and point to 8884180
---------------------------------------------------
*** glibc detected *** ./6: double free or corruption (out): 0x08884018 ***
======= Backtrace: =========
/lib/tls/i686/cmov/libc.so.6(+0x6b591)[0x17b591]
/lib/tls/i686/cmov/libc.so.6(+0x6cde8)[0x17cde8]
/lib/tls/i686/cmov/libc.so.6(cfree+0x6d)[0x17fecd]
./6[0x8048696]
/lib/tls/i686/cmov/libc.so.6(__libc_start_main+0xe6)[0x126bd6]
./6[0x8048451]
======= Memory map: ========
00110000-00263000 r-xp 00000000 08:01 6685537    /lib/tls/i686/cmov/libc-2.11.1.so
00263000-00264000 ---p 00153000 08:01 6685537    /lib/tls/i686/cmov/libc-2.11.1.so
00264000-00266000 r--p 00153000 08:01 6685537    /lib/tls/i686/cmov/libc-2.11.1.so
00266000-00267000 rw-p 00155000 08:01 6685537    /lib/tls/i686/cmov/libc-2.11.1.so
00267000-0026a000 rw-p 00000000 00:00 0
003b8000-003b9000 r-xp 00000000 00:00 0          [vdso]
00ef0000-00f0d000 r-xp 00000000 08:01 6553683    /lib/libgcc_s.so.1
00f0d000-00f0e000 r--p 0001c000 08:01 6553683    /lib/libgcc_s.so.1
00f0e000-00f0f000 rw-p 0001d000 08:01 6553683    /lib/libgcc_s.so.1
00f85000-00fa0000 r-xp 00000000 08:01 6553649    /lib/ld-2.11.1.so
00fa0000-00fa1000 r--p 0001a000 08:01 6553649    /lib/ld-2.11.1.so
00fa1000-00fa2000 rw-p 0001b000 08:01 6553649    /lib/ld-2.11.1.so
08048000-08049000 r-xp 00000000 08:01 6684686    /tmp/6
08049000-0804a000 r--p 00000000 08:01 6684686    /tmp/6
0804a000-0804b000 rw-p 00001000 08:01 6684686    /tmp/6
08884000-088a5000 rw-p 00000000 00:00 0          [heap]
b7700000-b7721000 rw-p 00000000 00:00 0
b7721000-b7800000 ---p 00000000 00:00 0
b7833000-b7834000 rw-p 00000000 00:00 0
b7848000-b784b000 rw-p 00000000 00:00 0
bfdfb000-bfe10000 rw-p 00000000 00:00 0          [stack]
Aborted
 

上面的程式都有檢查所 要進行 free memory 的 pointer,正常的邏輯下不會對一個 pointer 進行兩次以上的 free,但是,將記憶體位址印出來就會發現問題,原因是在配置 pointer 的記憶體時,型態的大小配置錯誤 sizeof(char),所以其實 pointer 所指向的記憶體區塊發生了問題,產生了兩個 pointers 指向同一塊 memory 的重複情況,因此,glibc 才會偵測到有 double free 發生。




ming@linuxbox:/tmp$ gcc test.c -o test
ming@linuxbox:/tmp$ MALLOC_CHECK_=0 ./test
Address of str=bfabe5ac, and point to 85af008
---------------------------------------------------
Address of str[0]=85af008, and point to 85af018
Address of str[1]=85af00c, and point to 85af040
Address of str[2]=85af010, and point to 85af068
Address of str[3]=85af014, and point to 85af090
Address of str[4]=85af018, and point to 85af0b8
Address of str[5]=85af01c, and point to 85af0e0
Address of str[6]=85af020, and point to 85af108
Address of str[7]=85af024, and point to 85af130
Address of str[8]=85af028, and point to 85af158
Address of str[9]=85af02c, and point to 85af180
---------------------------------------------------

修改程式碼第 12 行的部分,因為這塊記憶體每個 atom 的 data type 是 pointer,因此,在 malloc memory 時要注意每個 atom 的大小是配置 size of pointer,而不是 size of data type





接著每個 str[i] 所指的記憶體每個 atom 的 data type 是 character,所以這個程式正常的配置記憶體情況將如下圖所示 (程式碼 26 ~ 41 行)





  1 #include <stdio.h>
  2 #include <stdlib.h>
  3
  4 int main(int argc, char *argv[])
  5 {
  6
  7         char **str = NULL;
  8         const int x=10;
  9         const int y=32;
 10         int i;
 11
 12         str = (char**) malloc ( sizeof(str) * x ) ;
 13
 14         /* NULL pointer */
 15         if( ! str ) {
 16                 printf("NULL Pointer\n");
 17                 return -1;
 18         }
 19
 20         printf("Address of str=%x, and point to %x\n", &str, str);
 21
 22         bzero(str, x);
 23
 24         printf("---------------------------------------------------\n");
 25
 26         for( i=0; i<x; i++) {
 27
 28                 str[i] = (char *)malloc( sizeof(char) * y );
 29
 30                 if( ! str[i] ) {
 31                         printf("NULL Pointer\n");
 32                         return -1;
 33                 }
 34
 35
 36                 printf("Address of str[%d]=%x, and point to %x\n", i, &str[i], str[i]);
 37
 38                 bzero(str[i], y);
 39                 strcpy(str[i], "hello");
 40
 41         }
 42
 43
 44         printf("---------------------------------------------------\n");
 45
 46         for( i=0; i<x; i++) {
 47
 48                 if( str[i] ) {
 49                         free( str[i] );
 50                 }
 51         }
 52
 53         if(str) {
 54                 free (str);
 55         }
 56
 57         return 0;
 58 }
 59


修正後的執行結果就正確了,程式執行時也不再出現 double free 的錯誤訊息。



ming@linuxbox:/tmp$ gcc test.c -o test
ming@linuxbox:/tmp$ ./test
Address of str=bfaa32bc, and point to 8c5c008
---------------------------------------------------
Address of str[0]=8c5c008, and point to 8c5c038
Address of str[1]=8c5c00c, and point to 8c5c060
Address of str[2]=8c5c010, and point to 8c5c088
Address of str[3]=8c5c014, and point to 8c5c0b0
Address of str[4]=8c5c018, and point to 8c5c0d8
Address of str[5]=8c5c01c, and point to 8c5c100
Address of str[6]=8c5c020, and point to 8c5c128
Address of str[7]=8c5c024, and point to 8c5c150
Address of str[8]=8c5c028, and point to 8c5c178
Address of str[9]=8c5c02c, and point to 8c5c1a0
---------------------------------------------------



Reference:
Red Hat and Fedora Core compatibility tweaks, http://dag.wieers.com/howto/compatibility/.
Comments