Introduction

🎉 We presented at the 2025 Rust Verification Workshop (RV2025). Here’s our presentation and abstract.

BorrowSanitizer is a dynamic analysis tool for detecting Rust-specific aliasing bugs in multi-language applications.

the BorrowSanitizer project logo

The Rust compiler provides powerful, static safety guarantees by restricting aliasing and mutability. However, developers can bypass these restrictions by using a subset of unsafe features. These features are necessary for Rust to interoperate with other languages. However, if developers use unsafe code incorrectly, then they can break the rules of Rust’s aliasing model, which the compiler relies on to be able to optimize programs. Incorrect optimizations can introduce security vulnerabilities.

Rust developers can find aliasing bugs using Miri, an interpreter. Miri is the only tool that can find violations of Rust’s latest Tree Borrows aliasing model, but it cannot find these bugs in foreign code. Miri is also significantly slower than native execution, which makes it impractical to use techniques like fuzzing or property-based testing to find these Rust-specific bugs.

BorrowSanitizer is an LLVM sanitizer for finding aliasing violations. Our goal is for it to be fast enough for use with fuzzing tools and to have support for Rust, C, and C++ in interoperation. We intend for it to be a production-ready tool.

đźš§ Our project is still in early stages. BorrowSanitizer is not functional yet. đźš§

Join our Zulip if you are interested in contributing or if you have any additional questions about our project. You can build and test our sanitizer by following the setup instructions in the next section. All of our code is open-source and publicly available on GitHub.

Setup

The easiest way to try BorrowSanitizer is inside a Docker container. Our image supports the following platforms:

PlatformTargetDescription
linux/amd64aarch64-apple-darwinARM64 macOS (M-series)
linux/arm64x86_64-unknown-linux-gnuX86 Linux

First, pull our latest image from GitHub’s container registry.

docker pull ghcr.io/borrowsanitizer/bsan:latest

Then, launch a container and attach a shell.

docker run -it bsan:latest

Once inside the container, you can use our Cargo plugin to build and test crates using BorrowSanitizer.

cargo bsan test

Our plugin supports most of the same subcommands as Miri. When it’s used for the first time, it will perform a one-time setup step of building an instrumented sysroot. You can trigger this step manually using the setup subcommand.

Building from Source

Every single command needed to build, test, and install BorrrowSanitizer can be accessed through xb, our build script. For first-time setup, run:

xb setup

If you only want to install BorrowSanitizer, then run:

xb install

This will install a custom Rust toolchain under the name bsan. You can speed this up by building our dev container, which already has the bsan toolchain installed. We recommend using the container to avoid any environment-specific issues.

You can build and test components of the project using the build and test subcommands. For example, running xb build will build everything, but you can also pass the name of a subdirectory to build just that component, like so:

xb build bsan-rt

Nearly every subcommand can be used this way.

After making a change, you should run all of our CI steps locally using:

xb ui

This will place our binaries into Cargo’s home directory ($HOME/.cargo). You will need to have bsan set as the active toolchain (e.g. rustup default bsan) for our tool to work.

Development Guide

BorrowSanitizer has multiple components. Each can be built, linted, and tested individually via xb.

ComponentSubdirectoryLink
Cargo Plugincargo-bsan
Rustc Pluginbsan-driver
Backend Instrumentation Passbsan-pass
Runtimebsan-rt

Why do we need a custom toolchain?

We also rely on forks of both the Rust toolchain and LLVM. Initially, we developed everything in-tree within our Rust fork, but we have since found that it’s easier to iterate by externalizing everything as a plugin. This includes our LLVM pass, which is “injected” into the compilation process using Rust’s unstable -Zllvm-plugin flag. Overall, our approach is similar what worked for the Enzyme project, which also relies on an out-of-tree LLVM plugin.

Although most of BorrowSanitizer has been externalized, we do still need our custom compiler. We really wanted to avoid this, but it ended up being necessary to support inserting “retag” instructions into LLVM IR. Under each of Rust’s aliasing models, a retag updates the permission associated with a pointer, indicating that a reference has been created, reborrowed, or passed into a function. Retags are already implemented as MIR intrinsics, but we needed a way to lower them further down into LLVM IR, so that our instrumentation pass can transform them into calls into our runtime library.

We were unable to add a custom retag function via our frontend compiler plugin (bsan-driver), since Rust’s plugin interface gives mostly immutable access to the HIR; it’s not possible to add in a new type of this kind after typechecking has been completed. So, we needed a way to declare our function ahead-of-time. The best mechanism for doing this is an intrinsic. Rust’s intrinsics are provided by either the frontend or one of more of its codegen backends. Since we need retags to be available at the LLVM level, we needed to modify the LLVM codegen backend. It’s possible to add a custom codegen backend without modifying the compiler by using an out-of-tree plugin (see rustc_interface). Better yet, this approach has been successful for projects that have been widely adopted, like Kani. However, in our case, we only needed to add support for a single intrinsic while preserving the existing LLVM backend and keeping it up-to-date with nightly. For long-term maintenance, we decided that forking Rust would be easier than factoring out just the LLVM backend.

We intend to submit these changes incrementally upstream, so that other tool developers can benefit.

About

If you have questions or are interested in contributing, the best way to reach us is on Zulip. You can also send us an email and one of our team members will follow up with you. BorrowSanitizer is open source and publicly available on GitHub.

Team

Please reach out to us if you are interested in contributing to this project. Current and past contributors include (in no particular order):

License & Credits

BorrowSanitizer is dual-licensed under Apache and MIT.

Zulip Zulip sponsors free hosting for BorrowSanitizer. Zulip is an organized team chat app designed for efficient communication.