解决C可执行程序出现munmap_chunk()的错误

本周在做事的时候,我把代码提交上去后,对程序进行了下测试,出现了munmap_chunk(),程序直接崩溃。dump了core文件。gdb对core文件进行调试查看。崩溃处是因为程序调用了free接口。但是仔细看了代码,并不觉得有什么错误。查了下,stackoverflow上也是说对野指针进行了操作。也就是内存操作上有些错误吧。因此用内存调试工具valgrind,运行了一下可执行程序,结果打印出来的log也是果真是指针free操作失误了。

因为我们这边工程有自己的内存管理机制,而这次提交我代码中申请内存用的是工程自带的内存管理,但是释放内存也就是free的时候却是用系统的free()接口。导致libc库报错。由于是公司代码,这里只好写一小段代码进行模拟下。

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
    char *words;

    words = malloc( 512 );

    words = words+1; // 这里简单模拟下一个内存管理,内存管理的前一段有管理头信息
    free(words);
    return 0;
}

gcc编译后,并执行下

./a.out 
*** Error in `./a.out': munmap_chunk(): invalid pointer: 0x00000000007ca011 ***
[1]    23565 abort (core dumped)  ./a.out 

说白了,就是一个异常的地址,free的时候会对内存分页进行校验的时候,出现了错误。所以最好的解决办法就是用valgrind对内存进行检测,并查看警告的log打印。对应去找代码。即可解决问题。

其实这还没结束。本来我以为会很容易重现的。结果我试了好多次。经常报错的却是free(): invalid pointer。把上文中的words = words+1改为words+3或其余的值,当然如果你+0其实还是本来地址,这其实是合法的。执行结果如下:

./a.out 
*** Error in `./a.out': free(): invalid pointer: 0x00000000023e6013 ***
[1]    23930 abort (core dumped)  ./a.out 

偏移量不一样,竟然得到的log也不一样,并且如果申请的地址大小不一样,报错的结果也不一样。我们还是将最早的代码,将512改为256的大小。运行结果将和第二个的报错一样。因此特地去下了下libc6的代码。

apt-get source libc6

通过gdb调试core文件,可以看到报错在这里:

Core was generated by `./a.out'.
Program terminated with signal SIGABRT, Aborted.
#0  0x00007fcf9b59e267 in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:55
55      ../sysdeps/unix/sysv/linux/raise.c: 没有那个文件或目录.
(gdb) bt
#0  0x00007fcf9b59e267 in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:55
#1  0x00007fcf9b59feca in __GI_abort () at abort.c:89
#2  0x00007fcf9b5e1c53 in __libc_message (do_abort=do_abort@entry=1, fmt=fmt@entry=0x7fcf9b6fa1a8 "*** Error in `%s': %s: 0x%s ***\n")
    at ../sysdeps/posix/libc_fatal.c:175
#3  0x00007fcf9b5e9c69 in malloc_printerr (ptr=<optimized out>, str=0x7fcf9b6f62fa "free(): invalid pointer", action=1) at malloc.c:4965
#4  _int_free (av=<optimized out>, p=<optimized out>, have_lock=0) at malloc.c:3834
#5  0x00007fcf9b5ed89c in __GI___libc_free (mem=<optimized out>) at malloc.c:2950
#6  0x00000000004005b4 in main ()
(gdb)

在malloc.c的4965行,内容如下:

static void
malloc_printerr (int action, const char *str, void *ptr)
{
  if ((action & 5) == 5)
    __libc_message (action & 2, "%s\n", str);
  else if (action & 1)
    {
      char buf[2 * sizeof (uintptr_t) + 1];

      buf[sizeof (buf) - 1] = '\0';
      char *cp = _itoa_word ((uintptr_t) ptr, &buf[sizeof (buf) - 1], 16, 0);
      while (cp > buf)
        *--cp = '0';

      /* always abort (action & 1) and (on linux) if bit 1 is set,
         emit backtrace */
      __libc_message (action & 3, "*** Error in `%s': %s: 0x%s ***\n",
                      __libc_argv[0] ? : "<unknown>", str, cp);
    }
  else if (action & 2)
    abort ();
}

c库真是凶残,各种宏,单这个malloc.c就有5000+行。大概看了下malloc_printerr用来打印错误信息的,malloc.c中是会对内存进行校验的,而内存分配其实有个内存页大小的,不同的内存分配大小校验也不大一样,导致打印的错误也不一样,没打算深究下去。对于分页大小的话,这个在ubuntu上可以通过命令查看:

getconf PAGESIZE
4096

可能第一个做+1,并且申请内存为512刚好使得标识为munmap_chunk的操作,而+3或内存申请不为512时,走到另一个标识上了,也没细看这部分的代码。太多了,也太懒得看了。总之,出现这种问题,其实还算是好办的,内存检测开起来,跑一下,并且对照代码看看指针是否有哪些误操作。当然,如果工程太大,确实会比较麻烦,所以C的指针还是要用好,并且要管理好,我们这边出现这种状况,就是指针乱飞,一个地方申请,另一个地方去做释放。谁也不知道哪天哪里出现问题了,找都会找死了,尤其是那些偶现的问题。

这里再补充下valgrind进行内存检测的log吧:

valgrind ./a.out 
==26547== Memcheck, a memory error detector
==26547== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==26547== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==26547== Command: ./a.out
==26547== 
==26547== Invalid free() / delete / delete[] / realloc()
==26547==    at 0x4C2CE10: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==26547==    by 0x4005B3: main (in /tmp/a.out)
==26547==  Address 0x5202043 is 3 bytes inside a block of size 256 alloc'd
==26547==    at 0x4C2BBA0: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==26547==    by 0x40059E: main (in /tmp/a.out)
==26547== 
==26547== 
==26547== HEAP SUMMARY:
==26547==     in use at exit: 256 bytes in 1 blocks
==26547==   total heap usage: 1 allocs, 1 frees, 256 bytes allocated
==26547== 
==26547== LEAK SUMMARY:
==26547==    definitely lost: 256 bytes in 1 blocks
==26547==    indirectly lost: 0 bytes in 0 blocks
==26547==      possibly lost: 0 bytes in 0 blocks
==26547==    still reachable: 0 bytes in 0 blocks
==26547==         suppressed: 0 bytes in 0 blocks
==26547== Rerun with --leak-check=full to see details of leaked memory
==26547== 
==26547== For counts of detected and suppressed errors, rerun with: -v
==26547== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

 

BTW:看来C库对内存的维护还是很复杂的,所以有时候内存不应该频繁申请释放,这在很多开源的项目中,都有体现,有一些很不错的内存池管理开源库之类的。都是很值得去学习的。以上的测试基于ubuntu 15.04版本进行测试,C库为glibc(2.21),不同平台的话,可能多少有些差异,但是都差不多的处理方式。

参考链接:

How to solve munmap_chunk(): invalid pointer error in C++

 

 

转载请注明: 转载自elkPi.com

本文链接地址: 解决C可执行程序出现munmap_chunk()的错误

2 Comments

  1. amberkaka
    2017年11月24日

    我也遇到了同样的报错信息,在别的地方查的解决方案没用,在你这里看后把free那句删掉就行了,感谢!

    回复
    1. 米鹿π
      2017年11月24日

      呃,但是如果你确实是malloc出来的,没有free可是会内存泄漏的!

      回复

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

Scroll to top