C Pointer Double-Free Fix on MacOS

Warning: This post is over 365 days old. The information may be out of date.

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
==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
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 freed 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!