返回信息流下面是我写的小段程序:
#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;
}
我的疑问在double free那里, 当然,不能对malloc出来的内存重复的free。不过很好奇free()究竟干了什么,既然两次free时p的地址都是一样的,*p也是可赋值的,为什么第二次free会检测到double free呢?
请大神指点~
这是一条镜像帖。来源:北邮人论坛 / cpp / #82063同步于 2014/9/2
该镜像源已超过 30 天没有更新,可能在源站已被删除。
CPP机器人发帖
【求教】free()究竟发生了什么?--关于野指针、空指针、double
wdx01
2014/9/2镜像同步16 回复
订阅后,新回复会通过你的通知中心匿名送达。
9 条回复
我好奇
如果你先对野指针进行移动 移动到附近一个莫名的地方 然后free 此时会报跟double free 一样的错么
如果不报 结果很明显
如果报了 结果也很明显
你的编译环境是啥
如果是MSVC,调试时候F11进去就可以看到 malloc 和 free 的实现了
如果是*nix环境,直接看那 glibc 的 malloc 实现吧
总之要read the fucking source,不然谁也不懂。。。。
free之后就不要用了。反正是错误的。具体malloc和free怎么实现,其实都可以。
至于double free,也许是libc中内部保存了状态,知道哪些地方有分配哪些地方没有。当然这也是和实现相关的。元数据存在哪里,不知道。
你看看操作系统对堆的管理你就知道了,我只知道windows下堆也是一种数据结构,他有堆头和数据空间,我们编程能看见的使用的只是数据空间,堆头有很多内容,其中有一个标志位标志了该堆是否在使用。
我觉得至少做了下面几件事吧。
void free(void*p) {
if (p指向的内存被标记为已使用) {
//报double free
} else {
//将p指向的内存标记为未使用
}
}
至于是如何标记的,得看堆内存是怎么组织的吧。
比如这样
--------------------------------------------------------------------
|flag| 一次malloc分配的 |flag||flag| 另一次malloc分配的 |flag|
--------------------------------------------------------------------
请轻拍。。。。
【 在 wdx01 的大作中提到: 】
: 下面是我写的小段程序:
: [code=c]
: #include <stdio.h>
: ...................
堆的结构是链表,会有头和尾,里面记录了链表的指针关系以及当前这个节点的状态,例如可用不可用,是不是已经分配了等等。
因此,你free的时候,系统只是修改了这个链表,有可能将其合并到其他大的堆块中,也有可能是依然是原样,但是已经被标记为可再次分配(可以说是堆里的碎片)
当你再次free的时候,free函数会检查当前这个地方对应的头尾指针,检测到发现这块位置并没有标记为已经分配的,因此产生异常。
get
【 在 doug (Doug) 的大作中提到: 】
: 顺便说一句,你可以精巧的设计写入堆的参数,覆盖下一个head的内容,然后,free的时候,程序就飞了。。。。 也就是堆溢出攻击的基本原理。