double free or corruption (out): 0x00007fe160002b70 ***

一、遇到的问题

curlftpfs程序,遇到带有“#”的目录就会出现“double free or corruption (out): 0x00007fe160002b70 ***”,挂载目录就会卸载。

二、分析问题

  1. 收集core文件。

  2. 分析core文件,定位到ftpfs_getdir函数的free处(31行)出错。

 static int ftpfs_getdir(const char* path, fuse_cache_dirh_t h,
		                        fuse_cache_dirfil_t filler) {
		  int err = 0;
		  CURLcode curl_res;
		  char* dir_path = get_fulldir_path(path);
		
		  //将目录名(即url)中的#替换为%23
		  str_replace(dir_path, "#", "%23");
		
		  DEBUG(1, "ftpfs_getdir: %s\n", dir_path);
		  struct buffer buf;
		  buf_init(&buf);
		
		  pthread_mutex_lock(&ftpfs.lock);
		  cancel_previous_multi();
		  curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_URL, dir_path);
		  curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_WRITEDATA, &buf);
		  curl_res = curl_easy_perform(ftpfs.connection);
		  pthread_mutex_unlock(&ftpfs.lock);
		
		  if (curl_res != 0) {
		    DEBUG(1, "%s\n", error_buf);
		    DEBUG(1, "%d, dir  = %s\n", curl_res, dir_path);
		    err = -EIO;
		  } else {
		    buf_null_terminate(&buf);
		    parse_dir((char*)buf.p, dir_path + strlen(ftpfs.host) - 1,
		              NULL, NULL, NULL, 0, h, filler); 
		  }
		
		  free(dir_path);
		  buf_free(&buf);
		  return op_return(err, "ftpfs_getdir");  
	}
	
	static int str_replace(char* str_find,char* str_src, char* str_des)
	{
		char *ptr=NULL;
		char buff[1024];
		char buff2[strlen(str_find)+1];
		int i = 0;
		int num = 0;
	
		if(str_find != NULL)
		{
			strcpy(buff2, str_find);
		}
		else
		{
			DEBUG(1,"str_replace err!\n");
			return -1;
		}
		memset(buff, 0x00, sizeof(buff));
	
		while((ptr = strstr( buff2, str_src)) !=0)
		{
			if(ptr-buff2 != 0)
				memcpy(&buff[i], buff2, ptr - buff2);
			memcpy(&buff[i + ptr - buff2], str_des, strlen(str_des));
	
			i += ptr - buff2 + strlen(str_des);
	
			strcpy(buff2, ptr + strlen(str_src));
			num ++;
		}
		strcat(buff,buff2);
		str_find = realloc(str_find, strlen(str_find) + num * 3 + 1);
		strcpy(str_find,buff);
		return 0;
	}

刚开始误以为是get_fulldir_path函数内部没有执行malloc申请动态内存,而在ftpfs_getdir中执行了free才做,后来证明此想法是错的,ftpfs_getdir调用了封装有malloc的函数。

最后确认是str_replace函数有问题,str_replace执行了realloc(free后,重新malloc)操作,有时会改变str_find的值,因为调用str_replace时,传递的是str_find的一级指针,str_replace改变了str_find的值但修改无法传递回ftpfs_getdir函数中,导致ftpfs_getdir函数最后free时,释放的是已经释放的地址(realloc已经释放过了)

修改:向str_replace中传递str_find的二级指针,代码如下:

	static int str_replace(char** str_origal,char* str_src, char* str_des)
	{
		if(str_origal == NULL || NULL == str_src || NULL == str_des )
	    {
	        return -1;
	    }
	
		char *ptr=NULL;
		char buff[1024];
	    char *str_find = *str_origal;
		char buff2[strlen(str_find)+1];
		int i = 0;
		int num = 0;
	
		if(str_find != NULL )
		{
			strcpy(buff2, str_find);
		}
		else
		{
			DEBUG(1,"str_replace err!\n");
			return -1;
		}
	
	
		memset(buff, 0x00, sizeof(buff));
	
		while((ptr = strstr( buff2, str_src)) !=0)
		{
			if(ptr-buff2 != 0)
				memcpy(&buff[i], buff2, ptr - buff2);
			memcpy(&buff[i + ptr - buff2], str_des, strlen(str_des));
	
			i += ptr - buff2 + strlen(str_des);
	
			strcpy(buff2, ptr + strlen(str_src));
			num ++;
		}
		strcat(buff,buff2);
		str_find = realloc(str_find, strlen(str_find) + num * 3 + 1);
		strcpy(str_find,buff);
	    *str_origal = str_find;
		return 0;
	}

三、此类问题原因

  1. 内存写入越界,最后free的字节数超过malloc的字节数,导致报错查看案例
  2. 在调用函数中修改了指针值,但是并未将修改结果返回。 上述一、二所述。

四、定位此类问题的方法

  • 排查的原则,首先是保证能重现错误,根据错误估计可能的环节,逐步裁减代码,缩小排查空间。

  • 检查所有的内存操作函数,检查内存越界的可能。
    查找代码里所有申请动态空间的地方,把申请到的空间的地址打印出来;
    再查找代码里所有free这些空间的地方,把free的地址打印出来。
    然后查看是哪些地址发生了重复free。
    如果不存在重复free的现象,就可能是某些地方使用了已free的空间,在死机的位置确认使用了哪些动态空间,然后确认这些动态空间是不是已经被free了。

打印指针值得方法printf("%p\n",pointer); //不要取指针的地址&pointer

五、经验教训

  1. 指针变量要初始化。
    memset(p,0,100);memset(p,0,100);这样确保数据之后的字符是’\0’,不会出现读越界

  2. 指针释放后要赋值为NULL。
    因为程序对NULL进行重复释放不会出问题,也不会报错。

  3. strncpy()、strncmp()、strncat()、strnicmp()和 strnset() 相应地代替strcmp()、strcat()、stricmp()和 strset()。

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值