C pointer double-free fix on macOS
While debugging a C program I wrote today, I kept getting this error:
main(44466,0x10ae7ce00) malloc: *** error for object 0x7000000000000000: pointer being freed was not allocated
main(44466,0x10ae7ce00) malloc: *** set a breakpoint in malloc_error_break to debug
zsh: abort ./main test_cases/test_1.html output.txt
For some strange reason, using lldb
I was unable to reproduce the issue. Or that’s what I thought - after running the program on lldb
multiple times, it actually stopped at the malloc_error_break
breakpoint I set.
Unfortunately, the stack trace that lldb
gave me didn’t really say exactly what line caused the double-free. I tried running the program on gdb
, but the program refused to crash on the Linux instance. When I forced a crash by repeatedly running the program over and over, the stack trace there was also unhelpful, and valgrind
didn’t report anything particularly interesting.
Well, on macOS, clang
(LLVM compiler) comes with AddressSanitizer, which can be used to figure out exactly where the program is crashing. So I re-compiled my program using clang
with the AddressSanitizer flag enabled:
clang -g -fsanitize=address -o main_debug main.c (... rest of the source files ...)
And then ran the program. The AddressSanitizer immediately showed me the stack trace I was looking for:
ericswpark@Erics-MacBook-Pro Project % ./main_debug test_cases/test_1.html debug_output.txt
AddressSanitizer:DEADLYSIGNAL
=================================================================
==44732==ERROR: AddressSanitizer: SEGV on unknown address (pc 0x000102e04b98 bp 0x7ffeece1ba20 sp 0x7ffeece1b9f0 T0)
==44732==The signal is caused by a READ memory access.
==44732==Hint: this fault was caused by a dereference of a high value address (see register values below). Dissassemble the provided pc to learn which register was used.
#0 0x102e04b98 in __asan::Allocator::Deallocate(void*, unsigned long, unsigned long, __sanitizer::BufferedStackTrace*, __asan::AllocType)+0x48 (libclang_rt.asan_osx_dynamic.dylib:x86_64h+0x6b98)
#1 0x102e4732a in wrap_free+0x10a (libclang_rt.asan_osx_dynamic.dylib:x86_64h+0x4932a)
#2 0x102de8131 in html_tag_free main.c:278
#3 0x102de7f7c in stack_pop main.c:298
#4 0x102de79e5 in main main.c:219
#5 0x7fff203b9620 in start+0x0 (libdyld.dylib:x86_64+0x15620)
==44732==Register values:
rax = 0x0000000000000002 rbx = 0xbebebebebebebebe rcx = 0x0000000000000003 rdx = 0x0000000000000000
rdi = 0xbebebebebebebebe rsi = 0xbebebebebebebebe rbp = 0x00007ffeece1ba20 rsp = 0x00007ffeece1b9f0
r8 = 0x00007ffeece1ba30 r9 = 0x0000000000000001 r10 = 0xffffffffffffffff r11 = 0x00000fffffffffff
r12 = 0x0000000000000001 r13 = 0x0000000000000000 r14 = 0x00007ffeece1ba30 r15 = 0x0000000102ea2d40
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV (libclang_rt.asan_osx_dynamic.dylib:x86_64h+0x6b98) in __asan::Allocator::Deallocate(void*, unsigned long, unsigned long, __sanitizer::BufferedStackTrace*, __asan::AllocType)+0x48
==44732==ABORTING
zsh: abort ./main_debug test_cases/test_1.html debug_output.txt
This showed me the error was on line 278 of my main.c
file. Sure enough, the problem was because I wasn’t setting one of the character arrays in a struct variable to NULL
when I was creating it. The following code block:
if (var->chararr)
{
free(var->chararr); // error here
var->chararr = NULL;
}
checked to see if var->chararr
was NULL
, but since I didn’t set it to NULL
on initialization, went ahead and free
d whatever poor address space that ended up in that pointer.
So the next time you run into a weird memory bug with your program, use AddressSanitizer included with clang
to figure out exactly what part of your program is the problem!