This is a drop-in replacement for OS memory allocator that can be used to detect uses of uninitialized memory. It is designed to be used in case Memory Sanitizer is not applicable for some reason, such as:
This is not a drop-in replacement for Memory Sanitizer! It will likely require changes to your code or your testing setup, see below.
When injected into a process, this library initializes every subsequent allocated region of memory to different values. Using this library you can detect uses of uninitialized memory simply by running a certain operation twice in the same process and comparing the outputs; if they differ, then the code uses uninitialized memory somewhere.
Combine this with a fuzzer (e.g. AFL, honggfuzz) to automatically discover cases when this happens. This is called "differential fuzzing", hence the name.
Naturally, this is conditional on the same operation run twice returning the same results normally. If that is not the case in your program and you cannot make it deterministic - you're out of luck.
cargo build --release
; this will build libdiffuzz.so and put it in target/release
LD_PRELOAD=/path/to/libdiffuzz.so /path/to/your/binary
DYLD_INSERT_LIBRARIES=/path/to/libdiffuzz.so DYLD_FORCE_FLAT_NAMESPACE=1 /path/to/your/binary
AFL_PRELOAD=/path/to/libdiffuzz.so afl-fuzz ...
regardless of platform. If you're not fuzzing with AFL - you should!Note: Memory Sanitizer now works with Rust. You should probably use it instead of libdiffuzz!
If your code does not contain unsafe
blocks, you don't need to do a thing! Your code is already secure!
However, if you have read from the black book and invoked the Old Ones...
cargo build --release
; this will build libdiffuzz.so and put it in target/release
LD_PRELOAD=/path/to/libdiffuzz.so target/release/membleed
assert!
that they produce the same result. See example fuzz target for lodepng-rust for reference. A more complicated example is also available.// Use the system allocator so we can substitute it with a custom one via LD_PRELOAD
use std::alloc::System;
#[global_allocator]
static GLOBAL: System = System;
AFL_PRELOAD=/path/to/libdiffuzz.so cargo afl fuzz ...
Simply preload libdiffuzz into a binary (see "Usage" above), feed it the same input twice and compare the outputs. If they differ, it has exposes uninitialized memory in the output.
If your binary only accepts one input and then terminates, set the environment variable LIBDIFFUZZ_NONDETERMINISTIC
; this will make output differ between runs. Without that variable set libdiffuzz tries to be as deterministic as possible to make its results reproducible.
If the output varies between runs under normal conditions, try forcing the binary to use just one thread and overriding any sources of randomness it has.
Stack-based uninitialized reads are not detected.
Unlike memory sanitizer, this thing will not make your program crash as soon as a read from uninitialized memory occurs. Instead, it lets you detect that it has occurred after the fact and only if the contents of uninitialized memory leak into the output. I.e. this will help you notice security vulnerabilities, but will not really aid in debugging.
List of previously unknown (i.e. zero-day) vulnerabilities found using this tool, to show that this whole idea is not completely bonkers:
If you find bugs using libdiffuzz, please open a PR to add it here.
Valgrind, a perfectly serviceable tool to detect reads from uninitialized memory if you're willing to tolerate 20x slowdown and occasional false positives.
Dr. Memory, which claims to be an improvement over Valgrind.
MIRI, an interpreter for Rust code that detects violations of Rust's safety rules. Great for debugging but unsuitable for guided fuzzing.
libdislocator, a substitute for Address Sanitizer that also works with black-box binaries.
For background on how this project came about, see How I've found vulnerability in a popular Rust crate (and you can too).