Analyzing the Bug

Our unit tests pass! Now lets check out what happens if we uncomment the third function.

$ cargo test
   Compiling first-targetv0.1.0 (/workspaces/documentation.security.fuzzing.libafl/first-target)
    Finished test [unoptimized + debuginfo] target(s) in 0.27s
     Running unittests src/lib.rs (target/debug/deps/first_target-49e422aa7de12c55)

running 3 tests
malloc(): corrupted top size
error: test failed, to rerun pass `--lib`

Caused by:
  process didn't exit successfully: `/workspaces/documentation.security.fuzzing.libafl/first-target/target/debug/deps/first_target-49e422aa7de12c55` (signal: 6, SIGABRT: process abort signal)

The program crashes with an error in libc malloc. We've corrupted the top chunk size, so malloc crashes. We can investigate exactly where this error occurs in the malloc source code by searching the error message, and we find out it happens here. What's happening is that we only have one allocation in our program, so the heap looks like the image below. Recall the heap grows from low to high addresses (left to right below).


The "top chunk" is a special chunk that contains all the memory that hasn't been allocated yet, and is also known as the "wilderness". When we write out of bounds of the memory we allocated for decoded, we overwrite the mchunk_size field of the metadata of the top chunk. This can lead to an attack known as House of Force. This specific attack isn't important for our fuzzing, but it's a good case study of how dangerous an error as simple as naive size calculation can be. Not to mention, we end up incorrectly handling trailing % characters!

If you don't see an error on your system, that's ok. It's somewhat dependent on what libc version is in use (if you are on Linux), and what size of overflow we end up performing. Even if this function doesn't crash on your system, our fuzzer will be able to crash it with some input it will come up with. Go ahead and re-comment that test function.