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

讲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:1

	free(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
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值