从内存的角度看free(p) , p=NULL

来源:互联网 发布:域名纠纷 律师 编辑:程序博客网 时间:2024/05/17 09:38

讲C的书多告诉我们malloc出的内存用完要free掉,为了避免出现野指针,还要将指针置为NULL。

今天做了个小实验,从内存的角度看free()究竟做了什么、为什么最后要把指针设为NULL。

-----

看下面的例子:

#include <stdio.h>int main(int argc, char **argv) {int *p = (int *)malloc(sizeof(int));*p = 1;printf("p: %p, *p:%d\n", p, *p); // p: 0x1f1c010, *p:1free(p); // free之后,p的内存地址没有变,而p指向的内容置为0了,此时p是野指针printf("p: %p, *p:%d\n", p, *p); // p: 0x1f1c010, *p:0*p = 2;  //错误的操作野指针,观察内存:printf("p: %p, *p:%d\n", p, *p); // p: 0x1f1c010, *p:2// double free错误,这个是肯定的。//free(p);//printf("p: %d, *p:%d\n", p, *p);p = NULL; //  此时不能对p进行解引,p是空指针printf("p: %p\n", p); // p: (nil)return 0;}

---

update:


重新翻了下K&R,发现书上说的也确实如此! 
下面是stackoverflow上的一个回答(地址:http://stackoverflow.com/questions/2468853/freeing-memory-twice) 
  
Freeing memory does not set the pointer to null. The pointer remains pointing to the memory it used to own, but which has now had ownership transferred back to the heap manager. 
  
The heap manager may have since reallocated the memory your stale pointer is pointing to. 
  
Freeing it again is not the same as saying free(NULL), and will result in undefined behavior. 
  
我的理解: 
  
在第一次free()调用后,堆管理器将我们free()的内存和相邻的块重新组成了新的更大的块,以便下次malloc()调用,这时,这块内存已经不属于malloc出的内存了,对非malloc出的内存进行free,其结果是未定义的(我们这里是double free crash)。 
-- 
  
update: 
  
参考:http://code.woboq.org/userspace/glibc/malloc/malloc.c.html 
malloc出来的内存块结构: 
  
1. An allocated chunk looks like this: 


    chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
            |             Size of previous chunk, if allocated            | | 
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
            |             Size of chunk, in bytes                       |M|P| 
      mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
            |             User data starts here...                          . 
            .                                                               . 
            .             (malloc_usable_size() bytes)                      . 
            .                                                               | 
nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
            |             Size of chunk                                     | 
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+  

空闲块结构: 
2. Free chunks are stored in circular doubly-linked lists, and look like this: 

    chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
            |             Size of previous chunk                            | 
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
    `head:' |             Size of chunk, in bytes                         |P| 
      mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
            |             Forward pointer to next chunk in list             | 
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
            |             Back pointer to previous chunk in list            | 
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
            |             Unused space (may be 0 bytes long)                . 
            .                                                               . 
            .                                                               | 
nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
    `foot:' |             Size of chunk, in bytes                           | 
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 

第一次free: 

first_free.png 
第二次free: 

double_free.png 
看double free后出现了2块同样的chunk,堆管理器显然是不能同意的! 
---- 
另附blackHat上一篇关于double free()漏洞利用的paper: 
https://www.blackhat.com/presentations/bh-usa-07/Ferguson/Presentation/bh-usa-07-ferguson.pdf 


0 0