Reputation: 237
I wrote a small program to print full permutation of given alphabet set. It run well when the set was smaller than 26, and crash on 26 or more. the crash log said:
*** Error in `./a.out': malloc(): memory corruption (fast): 0x0000000000cd56a0 ***
Tried hours of debugging, still not know the root cause.
PS: if i remove these 2 lines that free tmp_done and tmp_todo, it won't crash, but still the result looks weird, an unexpected "!" occurs in the result.
abcdefghijklmnopqtyuzxwr!sv
abcdefghijklmnopqtyuzxwr!vs
abcdefghijklmnopqtyuzxws!rv
abcdefghijklmnopqtyuzxws!vr
abcdefghijklmnopqtyuzxwv!rs
Here's the source:
#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;
}
Here's the full crash log:
[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)
Upvotes: 5
Views: 1068
Reputation: 14619
I've spent 2 hours on __permutation1 ...
If you're looking for a way to find problems like this sooner, you can use clang
or gcc
's ASan (address sanitizer).
Some of the output below might look a little noisy/foreign. But you can spot conclusively where ASan first finds a problem: the strdup()
called on line #18. That way you don't have to puzzle through why the free()
is reporting an error much later. If you don't have clang
or gcc
installed on your machine you can 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
Upvotes: 2
Reputation: 15009
The problem is strdup()
. First you have:
char * done = (char *)calloc(1, strlen(str)+1);
This allocates a string of, in your case, 27 bytes and sets them all to the nul character, so you have \0\0\0...\0\0\0
.
Next you do:
char * tmp_done = strdup(done);
This is effectively shorthand for:
char * tmp_done = malloc(strlen(done)+1);
strcpy(tmp_done, done);
Unfortunately, the strlen
of 27 \0
characters is zero, not 27, so you end up with a one-byte memory allocation, and everything goes downhill from there.
Upvotes: 10