1. 程式人生 > >malloc double free

malloc double free

最近遇到一個問題:malloc一個記憶體塊之後,memset此記憶體塊成功,然後再malloc新記憶體塊就出現:
*** glibc detected *** malloc(): memory corruption: 0x09eab988 ***
發現是由於memset越界寫引起的。

在Linux Server上不好模擬出來:不過若是先malloc,再越界memset,再free此記憶體塊,然後malloc新記憶體塊就會出現類似錯誤。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main()
{
     char *p1 = (char*)malloc(210);
     if(p1 != NULL)
     {
         printf("malloc(210) succeeded\n");
     }
     if(p1 == memset(p1,0,300))
     {
         printf("memset(p1,0,300) succeeded\n");
     }

     free(p1);
     printf("Now char *p2 = (char*)malloc(210)\n");
     char *p2 = (char*)malloc(210);
     if(p2 != NULL)
     {
         printf("memset(p2,0,210) succeeded\n");
     }
     else
     {
         printf("memset(p2,0,210) failed\n");
     }
     return 0;

}

會出現:
malloc(210) succeeded
memset(p1,0,300) succeeded
*** glibc detected *** double free or corruption (out): 0x09f0e008 ***
Abort

找了一篇類似文章:http://blog.csdn.net/tommy_lgj/archive/2008/08/18/2790452.aspx
通常我們會犯的記憶體問題大概有以下幾種:
1.記憶體重複釋放,出現double free時,通常是由於這種情況所致。
2.記憶體洩露,分配的記憶體忘了釋放。
3.記憶體越界使用,使用了不該使用的記憶體。
4.使用了無效指標。
5.空指標,對一個空指標進行操作。

對於第一種和第二種,第五種情況,就不用多說,會產生什麼後果大家應該都很清楚。
第四種情況,通常是指操作已釋放的物件,如:
1.已釋放物件,卻再次操作該指標所指物件。
2.多執行緒中某一動態分配的物件同時被兩個執行緒使用,一個執行緒釋放了該物件,而另一執行緒繼續對該物件進行操作。
我們重點探討第三種情況,相對於另幾種情況,這可以稱得上是疑難雜症了(第四種情況也可以理解成記憶體越界使用)。
記憶體越界使用,這樣的錯誤引起的問題存在極大的不確定性,有時大,有時小,有時可能不會對程式的執行產生影響,正是這種不易重現的錯誤,才是最致命的,一旦出錯破壞性極大。
什麼原因會造成記憶體越界使用呢?有以下幾種情況,可供參考:
例1:
        char buf[32] = {0};
        for(int i=0; i<n; i++)// n < 32 or n > 32
        {
            buf[i] = 'x';
        }
        ....
       
例2:
        char buf[32] = {0};
        string str = "this is a test sting !!!!";
        sprintf(buf, "this is a test buf!string:%s", str.c_str()); //out of buffer space
        ....
       
例3:
        string str = "this is a test string!!!!";
        char buf[16] = {0};
        strcpy(buf, str.c_str()); //out of buffer space
       
類似的還存在隱患的函式還有:strcat,vsprintf等,同樣,memcpy, memset, memmove等一些記憶體操作函式在使用時也一定要注意。
       
當這樣的程式碼一旦執行,錯誤就在所難免,會帶來的後果也是不確定的,通常可能會造成如下後果:
1.破壞了堆中的記憶體分配資訊資料,特別是動態分配的記憶體塊的記憶體資訊資料,因為作業系統在分配和釋放記憶體塊時需要訪問該資料,一旦該資料被破壞,以下的幾種情況都可能會出現。
   *** glibc detected *** free(): invalid pointer:
   *** glibc detected *** malloc(): memory corruption:
   *** glibc detected *** double free or corruption (out): 0x00000000005c18a0 ***
   *** glibc detected *** corrupted double-linked list: 0x00000000005ab150 ***       
2.破壞了程式自己的其他物件的記憶體空間,這種破壞會影響程式執行的不正確性,當然也會誘發coredump,如破壞了指標資料。
3.破壞了空閒記憶體塊,很幸運,這樣不會產生什麼問題,但誰知道什麼時候不幸會降臨呢?
通常,程式碼錯誤被激發也是偶然的,也就是說之前你的程式一直正常,可能由於你為類增加了兩個成員變數,或者改變了某一部分程式碼,coredump就頻繁發生,而你增加的程式碼絕不會有任何問題,這時你就應該考慮是否是某些記憶體被破壞了。
排查的原則,首先是保證能重現錯誤,根據錯誤估計可能的環節,逐步裁減程式碼,縮小排查空間。
檢查所有的記憶體操作函式,檢查記憶體越界的可能。
常用的記憶體操作函式:
   sprintf snprintf
   vsprintf vsnprintf
   strcpy strncpy strcat
   memcpy memmove memset bcopy
如果有用到自己編寫的動態庫的情況,要確保動態庫的編譯與程式編譯的環境一致。

總結的很詳細,照此情形應該是memset破壞了堆的管理資料,要搞清楚具體怎麼破壞的,還要跟一下glibc malloc的程式碼,看一下堆的管理機制。