一个小的c程序导致“内存损坏”

时间:2017-06-30 02:26:47

标签: c debugging

我写了一个小程序来打印给定字母集的完整排列。当集合小于26时它运行良好,并且在26或更多时崩溃。崩溃日志说:

*** Error in `./a.out': malloc(): memory corruption (fast): 0x0000000000cd56a0 ***

尝试了几个小时的调试,仍然不知道根本原因。

PS:如果我删除了释放tmp_done和tmp_todo的这两行,它就不会崩溃,但结果看起来很奇怪,一个意想不到的“!”发生在结果中。

abcdefghijklmnopqtyuzxwr!sv
abcdefghijklmnopqtyuzxwr!vs
abcdefghijklmnopqtyuzxws!rv
abcdefghijklmnopqtyuzxws!vr
abcdefghijklmnopqtyuzxwv!rs

这是来源:

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


static void __permutation1(char * done, char * todo)
{
    int i = 0;

    for(i=0; i<strlen(todo); i++)
    {
        if (1 == strlen(todo))
        {
            printf("%s%s\n", done, todo); break;
        }
        char * tmp_todo = strdup(todo);
        char * tmp_done = strdup(done);
        char s[2] = {todo[i], 0};

        strcat(tmp_done, s);
        memmove(tmp_todo+i, tmp_todo+i+1, strlen(tmp_todo)-i); // null termincated!
        __permutation1(tmp_done, tmp_todo);
        free((void*)tmp_done); # if i remove these 2 lines, it won't crash
        free((void*)tmp_todo); # if i remove these 2 lines, it won't crash
    }
}


void permutation1(char * str)
{
    char * done = (char *)calloc(1, strlen(str)+1);
    char * todo = strdup(str);

    __permutation1(done, todo);

    free((void *)done);
    free((void *)todo);
}

int main(int argc, char const *argv[])
{
    permutation1("abcdefghijklmnopqrstuvwxyz");
    return 0;
}

这是完整的崩溃日志:

[xxxx@xxxx]$ ./a.out
abcdefghijklmnopqrstuvwx!yz
abcdefghijklmnopqrstuvwx!zy
*** Error in `./a.out': malloc(): memory corruption (fast): 0x0000000000cd56a0 ***
======= Backtrace: =========
/lib64/libc.so.6(+0x7ada4)[0x7f4397621da4]
/lib64/libc.so.6(+0x7ddc7)[0x7f4397624dc7]
/lib64/libc.so.6(__libc_malloc+0x4c)[0x7f4397626fbc]
/lib64/libc.so.6(__strdup+0x1a)[0x7f439762d88a]
./a.out[0x40078e]
./a.out[0x40081f]
./a.out[0x40081f]
./a.out[0x40081f]
./a.out[0x40081f]
./a.out[0x40081f]
./a.out[0x40081f]
./a.out[0x40081f]
./a.out[0x40081f]
./a.out[0x40081f]
./a.out[0x40081f]
./a.out[0x40081f]
./a.out[0x40081f]
./a.out[0x40081f]
./a.out[0x40081f]
./a.out[0x40081f]
./a.out[0x40081f]
./a.out[0x40081f]
./a.out[0x40081f]
./a.out[0x40081f]
./a.out[0x40081f]
./a.out[0x40081f]
./a.out[0x40081f]
./a.out[0x40081f]
./a.out[0x40081f]
./a.out[0x4008ad]
./a.out[0x4008ea]
/lib64/libc.so.6(__libc_start_main+0xf5)[0x7f43975c8b35]
./a.out[0x400669]
======= Memory map: ========
00400000-00401000 r-xp 00000000 fd:02 268801967                          /home/shello/workspace/oss/pincode/c_cpp/interview/a.    out
00600000-00601000 r--p 00000000 fd:02 268801967                          /home/shello/workspace/oss/pincode/c_cpp/interview/a.    out
00601000-00602000 rw-p 00001000 fd:02 268801967                          /home/shello/workspace/oss/pincode/c_cpp/interview/a.    out
00cd5000-00cf6000 rw-p 00000000 00:00 0                                  [heap]
7f4390000000-7f4390021000 rw-p 00000000 00:00 0
7f4390021000-7f4394000000 ---p 00000000 00:00 0
7f4397391000-7f43973a6000 r-xp 00000000 fd:01 201330443                  /usr/lib64/libgcc_s-4.8.5-20150702.so.1
7f43973a6000-7f43975a5000 ---p 00015000 fd:01 201330443                  /usr/lib64/libgcc_s-4.8.5-20150702.so.1
7f43975a5000-7f43975a6000 r--p 00014000 fd:01 201330443                  /usr/lib64/libgcc_s-4.8.5-20150702.so.1
7f43975a6000-7f43975a7000 rw-p 00015000 fd:01 201330443                  /usr/lib64/libgcc_s-4.8.5-20150702.so.1
7f43975a7000-7f439775d000 r-xp 00000000 fd:01 201333865                  /usr/lib64/libc-2.17.so
7f439775d000-7f439795d000 ---p 001b6000 fd:01 201333865                  /usr/lib64/libc-2.17.so
7f439795d000-7f4397961000 r--p 001b6000 fd:01 201333865                  /usr/lib64/libc-2.17.so
7f4397961000-7f4397963000 rw-p 001ba000 fd:01 201333865                  /usr/lib64/libc-2.17.so
7f4397963000-7f4397968000 rw-p 00000000 00:00 0
7f4397968000-7f4397988000 r-xp 00000000 fd:01 201327199                  /usr/lib64/ld-2.17.so
7f4397b66000-7f4397b69000 rw-p 00000000 00:00 0
7f4397b84000-7f4397b87000 rw-p 00000000 00:00 0
7f4397b87000-7f4397b88000 r--p 0001f000 fd:01 201327199                  /usr/lib64/ld-2.17.so
7f4397b88000-7f4397b89000 rw-p 00020000 fd:01 201327199                  /usr/lib64/ld-2.17.so
7f4397b89000-7f4397b8a000 rw-p 00000000 00:00 0
7ffca2b33000-7ffca2b54000 rw-p 00000000 00:00 0                          [stack]
7ffca2bb3000-7ffca2bb5000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
Aborted (core dumped)

2 个答案:

答案 0 :(得分:10)

问题是strdup()。首先你有:

char * done = (char *)calloc(1, strlen(str)+1);

这会分配一个字符串,在您的情况下为27个字节,并将它们全部设置为nul字符,因此您有\0\0\0...\0\0\0

接下来你做:

char * tmp_done = strdup(done);

这实际上是简写:

char * tmp_done = malloc(strlen(done)+1);
strcpy(tmp_done, done);

不幸的是,27 strlen个字符的\0为零,而不是27,所以你最终会得到一个字节的内存分配,而且一切都从那里开始走下坡路。

答案 1 :(得分:2)

  

我在__permutation1上花了2个小时......

如果您正在寻找一种更快找到问题的方法,可以使用clanggcc的ASan(地址清理工具)。

下面的一些输出可能看起来有点嘈杂/异国情调。但是你可以最终确定ASan首先发现问题的地方:在{18行上调用strdup()。这样您就不必为什么free()稍后报告错误而感到困惑。如果您的计算机上未安装clanggcc,则可以download and install it

$ clang -g -fsanitize=address,undefined perm.c 
$ ./a.out 
=================================================================
==15988==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60200000eff1 at pc 0x0000004a25e2 bp 0x7ffc33576490 sp 0x7ffc33575c18
WRITE of size 2 at 0x60200000eff1 thread T0
    #0 0x4a25e1 in strcat /home/development/llvm/3.9.0/final/llvm.src/projects/compiler-rt/lib/asan/asan_interceptors.cc:501:5
    #1 0x4e4d86 in __permutation1 /home/brian/tmp/go2_/perm.c:21:9
    #2 0x4e4978 in permutation1 /home/brian/tmp/go2_/perm.c:35:5
    #3 0x4e4f47 in main /home/brian/tmp/go2_/perm.c:43:5
    #4 0x7f82ec48582f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
    #5 0x418dc8 in _start (/home/brian/tmp/go2_/a.out+0x418dc8)

0x60200000eff1 is located 0 bytes to the right of 1-byte region [0x60200000eff0,0x60200000eff1)
allocated by thread T0 here:
    #0 0x4a3ea6 in strdup /home/development/llvm/3.9.0/final/llvm.src/projects/compiler-rt/lib/asan/asan_interceptors.cc:562:3
    #1 0x4e4bf1 in __permutation1 /home/brian/tmp/go2_/perm.c:18:27
    #2 0x4e4978 in permutation1 /home/brian/tmp/go2_/perm.c:35:5
    #3 0x4e4f47 in main /home/brian/tmp/go2_/perm.c:43:5
    #4 0x7f82ec48582f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)

SUMMARY: AddressSanitizer: heap-buffer-overflow /home/development/llvm/3.9.0/final/llvm.src/projects/compiler-rt/lib/asan/asan_interceptors.cc:501:5 in strcat
Shadow bytes around the buggy address:
  0x0c047fff9da0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff9db0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff9dc0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff9dd0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff9de0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
=>0x0c047fff9df0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa[01]fa
  0x0c047fff9e00: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff9e10: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff9e20: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff9e30: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff9e40: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Heap right redzone:      fb
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack partial redzone:   f4
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==15988==ABORTING