Hi all,
Okay, it's been a while since I have been looking at this issue but as I keep seeing these crashes in my fuzzing instances, I wanted to dig deeper. I was relatively sure that the code surrounding the crashes was sane, so I tried to set up a smaller test program and see if I can reproduce the issue that way. I think while doing so I actually found a different issue with libdislocator - or at least the strack trace is sufficiently different.
Step 1: The program
fuzz.cpp is attached to this message. All it does is taking the file passed to the program, and interprets the bytes inside the file as numbers of elements to allocate in a vector. Of course as a first step it's crucial to rule out that this program itself is sane (as far as fuzzing is concerned). The program allocates a couple of items on the heap and as soon as that fails with std::bad_alloc, it will bail out of the loop.
The program may of course get killed by the system if it runs out of memory, which would result in a SIGKILL (which did happen a couple of times while testing). However, what I was observing was a SIGABRT. From my understanding the code shouldn't cause any SIGABRTs, as a failure to allocate memory should rather result in std::bad_alloc being thrown in C++.
Step 2: Compilation
Using afl++3.10c compiled against clang-12 (from
https://apt.llvm.org/ on a fresh Debian Stable VM), and the following command line:
afl-clang-lto++ ./fuzz.cpp -std=c++17 -lm -fPIC -fno-strict-aliasing -fvisibility=hidden -O3 -ffast-math -fno-omit-frame-pointer -o fuzz
Step 3: Fuzzing
I'm invoking afl-fuzz as follows:
export AFL_PRELOAD=/path/to/libdislocator.so
/path/to/afl-fuzz -p coe -f infile -t 5000 -i /path/to/initial -o /path/to/findings -S fuzzer01 /path/to/fuzz infile
I kept it running for a couple of minutes in a 64-bit Debian VM with 4GB of memory.
Eventually, I got some crashes with this backtrace (with example files that are attached to this message and can be used as initial input):
Program terminated with signal SIGABRT, Aborted.
#0 __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
50 ../sysdeps/unix/sysv/linux/raise.c: No such file or directory.
(gdb) bt
#0 __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
#1 0x00007ffaeb65c535 in __GI_abort () at abort.c:79
#2 0x00007ffaebc3f6f3 in __dislocator_alloc (len=len@entry=2048) at libdislocator.so.c:240
#3 0x00007ffaebc3f912 in malloc (len=2048) at libdislocator.so.c:321
#4 0x00007ffaebb46918 in operator new(unsigned long) () from /lib/x86_64-linux-gnu/libc++abi.so.1
#5 0x0000000000205eaa in __gnu_cxx::new_allocator<std::shared_ptr<char> >::allocate (
this=0x7ffabb305fe8, __n=<optimized out>)
at /usr/lib/gcc/x86_64-linux-gnu/8/../../../../include/c++/8/ext/new_allocator.h:111
#6 std::allocator_traits<std::allocator<std::shared_ptr<char> > >::allocate (__a=...,
__n=<optimized out>)
at /usr/lib/gcc/x86_64-linux-gnu/8/../../../../include/c++/8/bits/alloc_traits.h:436
#7 std::_Vector_base<std::shared_ptr<char>, std::allocator<std::shared_ptr<char> > >::_M_allocate (
this=0x7ffabb305fe8, __n=<optimized out>)
at /usr/lib/gcc/x86_64-linux-gnu/8/../../../../include/c++/8/bits/stl_vector.h:296
#8 std::_Vector_base<std::shared_ptr<char>, std::allocator<std::shared_ptr<char> > >::_M_create_storage (this=0x7ffabb305fe8, __n=<optimized out>)
at /usr/lib/gcc/x86_64-linux-gnu/8/../../../../include/c++/8/bits/stl_vector.h:311
#9 std::_Vector_base<std::shared_ptr<char>, std::allocator<std::shared_ptr<char> > >::_Vector_base (
this=0x7ffabb305fe8, __n=<optimized out>, __a=...)
at /usr/lib/gcc/x86_64-linux-gnu/8/../../../../include/c++/8/bits/stl_vector.h:260
#10 std::vector<std::shared_ptr<char>, std::allocator<std::shared_ptr<char> > >::vector (
this=0x7ffabb305fe8, __n=<optimized out>, __a=...)
at /usr/lib/gcc/x86_64-linux-gnu/8/../../../../include/c++/8/bits/stl_vector.h:416
#11 main (argc=<optimized out>, argv=<optimized out>) at ./fuzz.c:29
You'll notice that line 240 in libdislocator.so.c is tied to hard_fail being true, which is tied to an environment variable being set that I didn't know of until today - so naturally, AFL_LD_HARD_FAIL is *not* set in my environment. I double-checked and "echo $AFL_LD_HARD_FAIL" just returns an empty line.
So, what could be happening here? Could libdislocator ironically somehow be overwriting its own memory?
- Johannes