For our tlspuffin fuzzer we use Rust to implement the testing harness. The harness is statically linked to OpenSSL via the openssl-sys and
openssl-src crates, where the latter just provides a Rust Build Script.
openssl-sys runs the script, then looks at the build artifacts and statically links against them.
This is a usual setup when working with C libraries in Rust. How do we enable the wildly known AddressSanitizer?
The first step is to make the build script for OpenSSL use Clang. While GCC also supports sanitizers, Clang has a greater selection of available sanitizers. For example the SanitizerCoverage sanitizer is only available in Clang.
This can be done by setting the
CC environment variable:
configure is an instance of
std::process::Command which runs the
./Configure script with various arguments.
The next step is to append
-fsanitize=address flag to the invocation of the compiler. For OpenSSL we can do this by adding the
An other way of doing this is by appending the
sanitize option directly to the
The AddressSanitizer requires a runtime.
The previous step instruments the library and adds calls into this runtime. This means the linker is responsible for adding this runtime. Therefore,
-fsanitize=address is not only accepted by the compiler but also the linker. While it is possible pass
-lasan to the linker, it is not recommended. The AddressSanitizer, which is also known as
libasan must be the first library in the initial library list. That means it must have the possibility to overwrite function calls in other dynamically loaded libraries. The reason for this is that it needs to have control over calls to
free to detect memory issues like null pointer dereferences.
There are two ways to instrument the library and link
libasan. The two options
-shared-libsan are mentioned in the command line reference.
-static-libsan as default whereas GCC uses
-shared-libsan as default. The advantages and disadvantage of either are documented here.
In our example there is actually only one possibility. We want to instrument the library, but do not want to instrument the executable which calls into the static library. If we want to sanitize the whole Rust executable, including OpenSSL we can simply use the unstable Rust feature Sanitizers Support to link against the
If we use
-static-libsan the compiled static library of OpenSSL will have many undefined references the functions of the runtime. These need to be resolved during the final linking which
rustc does. Same goes for
-shared-libsan in our case. There will be undefined references.
Therefore, for our usecase there is no practical difference beween these two options, as we have to use the linker provided by
There are two ways. Either we tell
rustc directly to link to
The other way it to do this using the directive
cargo:rustc-link-lib in the build script of OpenSSL:
An unanswered question is how to control in which order
rustc links against shared libraries. If
libasan is not the first in the list then the executable will abort with:
If using the
RUSTFLAGS, then the linker produces the following:
Note, that the
libasan is at the very bottom. When linking using the build script,
rustc did link correctly:
Note, that the
libasan is at the very top.
One way to control it is to use
Hint: Cargo will crash if executed with
libasan as preloaded library. Therefore, this is not ideal.
If you want to take a look at my experimental changes, you can visit my fork on Github.
I’m not sure why the following attempt failed. But I’m quite certain that this is intended.
- Statically linking against
libclang_rt.asan-x86_64.a. This can not work because we are instrumenting only a library and not the executable. The linker crashes with:
TLS transition from R_X86_64_TLSLD to R_X86_64_TPOFF32 against_ZN6__asanL14fake_stack_tlsE' at 0x667 in section