【嵌入式】“野指针”和“悬空指针”的奇淫拙劣

1. 前言

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_39217004/article/details/137683500

1.1 授权须知

转载文章,禁止声明原创;不允许直接二次转载,转载请根据原文链接联系作者

若无需改版,在文首清楚标注作者及来源/原文链接,并删除【原创声明】,即可直接转载。
但对于未注明转载来源/原文链接的文章,我将保留追述的权利。

作者:积跬步、至千里

若需要修改文章的排版,请根据原文链接联系作者
再次感谢您的认可,转载请遵守如上转载须知!

———————————————————————————————————————————

2. 野指针和悬空指针

  • 悬空指针(Dangling Pointer):是指一个指针变量保存了一个 无效的内存地址的情况。

    • 情况1: 指针被释放后,没有指向 NULL则该指针大概率是还指向之前指向的地址,然而该地址接下来已经被其他变量使用
    • 情况2:指针指向了一个非静态局部变量的地址; 如数组越界,如函数中返回了局部变量的地址
  • 野指针(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

  • 11
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

积跬步、至千里

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值