realloc 引起的程序崩溃问题



  记录一下公司某产品在运行过程中产生的崩溃问题,通过调试 core文件,我们定位到是程序在调用realloc 时候出现了问题,这是一个保受争议的函数,首先来看一下程序中的那块代码段;

`

char * pData = NULL; // 实际定义在头文件并在类的构造函数中初始化,析构函数中释放

int DisplayChannel::create_canvas(int width,int height) 
{
    //省略N行 .....
    if(pData == NULL) {
        pData = (char *) memalign (32,width * height); 
    } else {
        char *ptr = (char *)relloc(pData,width * height);
        if(ptr == NULL){
           printf("relloc failed!");  
        } else {
           pData = ptr;
        }   
    }
}

`

  实际项目中和上面的代码不完全一样,但大体意思一样。上面代码实际可能会被调用多次,乍看上去也没有什么问题,其实至少存在两处不妥之处。出现这样的问题主要是没有真正理解realloc的使用,下面先来看一下realloc的具体使用:

原型:

void *realloc(void *mem_address, unsigned int newsize);

指针名=数据类型***)**realloc(要改变内存大小的指针名,新的大小)。

功能:

  先判断当前的指针是否有足够的连续空间,如果有,扩大mem_address指向的地址,并且将mem_address返回,如果空间不够,先按照 newsize 指定的大小分配空间,将原有数据从头到尾拷贝到新分配的内存区域,而后释放原来mem_address所指内存区域,同时返回新分配的内存区域的首地址。即重新分配存储器块的地址。

返回值:

如果重新分配成功则返回指向被分配内存的指针,否则返回空指针NULL。

百科中总结的关于realloc的用法

1、realloc失败的时候,返回NULL

2、realloc失败的时候,原来的内存不改变,不会释放也不会移动

3、假如原来的内存后面还有足够多剩余内存的话,realloc的内存=原来的内存+剩余内存,realloc还是返回原来内存的地址; 假如原来的内存后面没有足够多剩余内存的话,realloc将申请新的内存,然后把原来的内存数据拷贝到新内存里,原来的内存将被free掉, realloc返回新内存的地址。

4、如果size为0,效果等同于free()。

5、传递给realloc的指针必须是先前通过malloc(), calloc(), 或realloc()分配的,或者是NULL

6、传递给realloc的指针可以为空,等同于malloc



有了对realloc 的充分了解,我们再来分析以下上面代码存在的不妥之处

第一处:如果第二次调用传递的width * height(即传给realloc的) 小于第一次传递的width * height(即传给memalign的)就可能会发生数据丢失的情况,这个不是本文说明的重点,点到为止。

第二处:如果第二次调用传递的width * height = 0(即传给realloc的),就会引发第三次调用realloc崩溃。这也是我们实际项目中出问题的地方

  那为什么呢?原因就是上面说的第4点,如果size为0,效果等同于free()。可以去查看realloc 源码实现,这里需要注意的是只对指针本身进行释放,并没有将指针指向NULL,实际指向了一块”垃圾“内存,也就是说存在野指针的问题。

  知道原因了,那上面的代码块我们该如何去修改呢?修改很简单就是加一条赋值语句,即realloc 失败时将 pData = NULL 或者 直接 pData = (char *) memalign (32,width * height); 即可。

下面是一段测试代码,编辑TestRealloc.c:

#include <stdlib.h>
#include <malloc.h>
#include <stdio.h>
#include <unistd.h> //linux 下使用memalign函数头文件,Windows可以去掉

char * pData = NULL;

int create_canvas (int width, int height) 
{
    if (pData == NULL) {
        pData = (char *) memalign (32, width * height); 
        printf("memalign success pData %p \n", pData);
    } else {
        char *ptr = (char *)realloc(pData, width * height);
        if (ptr == NULL) {
           printf("relloc failed and pData %p \n",pData);  
           //pData = NULL; //加上这句就不会崩溃了
        } else {
           pData = ptr;
           printf("relloc success pData %p \n", pData);
        }  
    }
}

int main()
{
    int count = 0;

    while (++count < 5) {
        printf("count %d and ",count);
        if (1 == count)
           create_canvas(1024,768);
        else if (2 == count)
           create_canvas(0,0);
        else 
           create_canvas(1024,768); 
        sleep(1);
        //Sleep(1000); windows 下用这个   
    }
    return 0;
}

执行结果:
程序执行结果

从执行结果来看,count == 2 时,即width * height = 0,导致realloc失败,这是pData 会被free 掉,但是我们发现它的地址仍然是0x7fe9961cd040 并没有被赋值为NULL,所以成了前面说的野指针;这样当count == 3时,就会导致realloc 发生段错误(SIGNAL 11)而产生崩溃。

core 文件的分析:
core文件分析

从core文件程序崩溃的堆栈信息来看,最后是调用realloc发生了错误,也验证了我们前面分析的内容。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值