Development Guide

BorrowSanitizer uses several extensions to the Rust and LLVM toolchains.

ToolchainComponent./x.py buildLink
RustCargo Plugincargo-bsan
RustRustc Pluginbsan
LLVMInstrumentation Pass-
LLVMSanitizer Interfacebsan-rt
RustSanitizer Runtime Librarybsan-rt

System Overview

Our Cargo plugin builds an instrumented sysroot by redirecting compiler invocations through our Rustc plugin, which adds the flags for enabling BorrowSanitizer. This saves time when switching between projects. Otherwise, users would need to rebuild Rust’s standard library by passing -Zbuild-std for every project that they want to use with BorrowSanitizer. Any static optimizations that require Rust-specific type information will be implemented within the Rustc plugin.

We modified the Rust compiler to accept borrow as one of the options for -Z sanitizer=.... If this option is enabled, then the compiler will emit “retag” instructions, which create new permissions for references. This has a effect similar to the flag -Zmir-emit-retag, which Miri uses to emit retags. However, Miri only emits retags for a subset of operations, the rest are executed implicitly as side-effects of the interpreter. We emit retags everywhere that they are necessary for Tree Borrows. Our retags are lowered to LLVM as calls to a new @llvm.retag intrinsic function. Our LLVM insturmentation pass replaces this intrinsic with a call to our runtime library. Its arguments will capture all of the Rust-specific type information that we need to instrument LLVM IR for detecting aliasing violations. All other run-time checks are inserted in the backend by the LLVM pass.

Our runtime library is implemented as two components. The first component is implemented in C++ using the LLVM Sanitizer API. This component redirects our runtime checks to a Rust runtime library, where we implement the semantics of Rust’s aliasing model and all necessary operations for initializing and accessing values in shadow memory. This design allows us to reuse Miri’s implementation of Tree Borrows. It will also make it easier to port our approach to other Rust backends, since developers will only need to reimplement our instrumentation pass. Eventually, we will distribute these libraries as LLVM IR, which our Cargo plugin will statically link against the source program using cross-language LTO. This will allows us to gain performance through inlining. They are dynamically linked for now, though.

Configuration

Builds of the Rust compiler are configured through a config.toml file in the root directory of the repository. This file is auto-generated by running ./x.py setup. BorrowSanitizer, you need to set the following configuration options:

[build]
extended = true
tools = ["bsan-rt", "bsan", "cargo-bsan"]

[llvm]
download-ci-llvm = false

[rust]
llvm-tools = true

This ensures that LLVM is built from source so that you have access to our instrumentation pass. We also recommend setting clang=true under this section. This builds our modified version of Clang, which can be configured to enable BorrowSanitizer for C/C++ programs.

We provide a sample configuration file for you to modify. If you are creating a distribution build, then you should reuse Rust’s default configuration.

Testing & CI

We have unit tests for our runtime library and UI tests for the entire toolchain. Our unit tests are within bsan-rt, and our UI tests are within bsan-driver. You can execute each test suite by passing the relative path to these components as an argument to ./x.py test, like so:

./x.py test src/tools/bsan/bsan-rt
./x.py test src/tools/bsan/bsan-driver

Our CI pipeline checks for formatting and executes our tests on each of our supported architectures. Run ./x.py fmt before opening a pull request. All pull requests should be made using the branch BorrowSanitizer/bsan as the base for comparison. Make sure to select the correct branch when using GitHub, since the interface defaults to submitting a pull request against the main branch of the Rust toolchain! All pull requests must pass our CI pipline. External pull requests must be reviewed by a member of the team.