【嵌入式】“野指针”和“悬空指针”的奇淫拙劣
- 1. 前言
- 1.1 授权须知
- 2. 野指针和悬空指针
- 3.举例说明
- 3.1 示例一:free 之后,没有让指针指向NULL
- 3.1.1 代码解析
- 3.1.2 运行代码的结果
- 3.1.3 程序崩溃在哪?
- 3.2 悬空指针–释放后使用攻击
1. 前言
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_39217004/article/details/137683500
1.1 授权须知
转载文章,禁止声明原创;不允许直接二次转载,转载请根据原文链接联系作者
若无需改版,在文首清楚标注作者及来源/原文链接,并删除【原创声明】,即可直接转载。
但对于未注明转载来源/原文链接的文章,我将保留追述的权利。
作者:积跬步、至千里
若需要修改文章的排版,请根据原文链接联系作者
再次感谢您的认可,转载请遵守如上转载须知!
———————————————————————————————————————————
2. 野指针和悬空指针
-
悬空指针(
Dangling Pointer
):是指一个指针变量保存了一个 无效的内存地址的情况。- 情况1: 指针被释放后,没有指向
NULL
,则该指针大概率是还指向之前指向的地址,然而该地址接下来已经被其他变量使用 - 情况2:指针指向了一个非静态局部变量的地址; 如数组越界,如函数中返回了局部变量的地址
- 情况1: 指针被释放后,没有指向
-
野指针(
Wild Pointer
):是指一个未初始化或未赋值的指针,它 不知道所指向的内存地址。野指针可能包含任意值,它没有被正确地分配或初始化。
关于野指针的描述,参考: 【嵌入式】让人又爱又恨的“指针”
建议调用 free
函数的时候,自己写一个宏定义
#define FREE(p) free(p); \
p = NULL;
3.举例说明
3.1 示例一:free 之后,没有让指针指向NULL
void fun(char **p){
*p = (char *)malloc(100);
}
int main{
char *p = NULL;
fun(&p);
free(p);
if(p!=NULL){
strcpy(p,"hello world");
printf(p);
}
}
3.1.1 代码解析
main
函数定义了一个指针变量p
,然后将其地址传递给fun
函数fun
函数使用malloc
函数在堆上分配了100
个字节的空间,并把这块内存的地址赋值给了p
。- 回到
main
函数中,紧接着调用free
函数释放刚刚分配的内存。
free§之后,指针变量p没有及时置空,仍然还是指向着这片内存地址;但p指向的这片内存已经被回收了,这时候使用strcpy向其写入数据,到底会造成什么后果就难以预料了。
3.1.2 运行代码的结果
【结果难以预料】
- 运气好的话,字符串能够成功复制,也能成功打印出"hello world"字符串,
- 运气不好的话,会报错,
Segmentation fault
3.1.3 程序崩溃在哪?
崩溃是在哪一行呢?是strcpy
写入数据的时候崩溃,还是printf
打印输出的时候崩溃呢?
答案是printf
的时候崩溃了
- 虽然通过调用free把这块内存释放了,但要注意,这个释放只是C语言运行时库层面的释放(因为free函数是C语言的库函数),C语言运行时库里的算法把它回收回去,在编程语言的层面上,这块内存是不应该再访问的了。
- 但在操作系统的层面上,这块内存依然是可以访问的,它依然位于某个具有可读可写的4KB内存页中。因为C语言的堆内存分配算法,不会每次释放内存都调用系统级的函数(如VirtualFree)去真正释放内存页面,这是一个很重的操作。
这里所谓的free
,仅仅是告诉C语言运行时库,这块内存我不用了,你回收回去统一管理吧。
所以,当调用strcpy
的时候,是能够正常复制的。
但要注意,这块内存能写,不代表你能乱写。在操作系统层面上,内存页面可读可写,那你写没有问题。
但站在C语言运行时库的视角来看,这个地址的内容我已经回收了,现在这里面的内容对于我管理堆内存非常重要,你别乱写,乱写是要出乱子的。
这不,这样一strcpy,哦豁,堆内存里面的一些管理用的设施被破坏了(比如一些指针),等到后面调用printf的时候,里面同样要从堆分配内存,这个时候前面留下的问题就暴露出来了。
3.2 悬空指针–释放后使用攻击
void fun(char **p){
*p = (char *)malloc(100);
}
int main()
{
char *p = NULL;
fun(&p);
strcpy(p,"hello world \r\n");
printf(p);
free(p);
char *q = NULL;
fun(&q);
strcpy(q,"zhang san \r\n");
printf(p);
}
- 我先给指针p分配了100个字节,里面填充了"hello, world"之后,打印输出,随后释放指针p的内存。
- 但要注意,我释放后,没有把p置空
- 紧接着,我又调用malloc分配了100个字节给指针q,随后给它指向的内存填充了"zhangsan"。
- 但好玩的来了,我接下来还是打印p,不是打印q,居然把指针q的内容给我打印出来了。
查看堆栈,发现 p 和 q 指向的是同一片内存地址
这是利用了C语言运行时库堆内存分配算法的特点,把上面刚刚free归还的100个字节,又分配给新的q了,而p又还没有置空,就出现了p和q同时指向了这块内存。
参考文章链接:
https://mp.weixin.qq.com/s/e5TlFOP3M7HiavoHMPH7-g