#check #behavior #cargo #assertions #debugging #undefined #issue

app cargo-careful

Execute Rust code carefully, with extra checking along the way

14 releases

0.4.3 Aug 10, 2024
0.4.2 May 19, 2024
0.4.1 Jan 7, 2024
0.4.0 Sep 22, 2023
0.3.0 Nov 20, 2022

#8 in Cargo plugins

Download history 478/week @ 2024-08-18 355/week @ 2024-08-25 297/week @ 2024-09-01 469/week @ 2024-09-08 377/week @ 2024-09-15 449/week @ 2024-09-22 245/week @ 2024-09-29 241/week @ 2024-10-06 215/week @ 2024-10-13 573/week @ 2024-10-20 337/week @ 2024-10-27 284/week @ 2024-11-03 330/week @ 2024-11-10 496/week @ 2024-11-17 1307/week @ 2024-11-24 672/week @ 2024-12-01

2,821 downloads per month

MIT/Apache

28KB
424 lines

cargo-careful

cargo careful is a tool to run your Rust code extra carefully -- opting into a bunch of nightly-only extra checks that help detect Undefined Behavior, and using a standard library with debug assertions. The standard library does check for some Undefined Behavior when the program is built with debug assertions, but some of these checks are disabled because their performance impact was considered too high. For example, it will find the alignment issue in the following snippet:

fn main() {
    let arr = [1u8, 2, 3, 4];
    for n in [0, 1] {
        let val = unsafe { arr.as_ptr().add(n).cast::<u16>().read() };
        println!("The value is {val}!");
    }
}

To use cargo careful, first install it:

cargo install cargo-careful

and then run the following in your project:

cargo +nightly careful test

You can also cargo +nightly careful run to execute a binary crate. All cargo test and cargo run flags are supported.

Running cargo careful requires a recent nightly toolchain. Nightly versions from the last 3 months are supported.

The first time you run cargo careful, it needs to run some setup steps, which requires the rustc-src rustup component -- the tool will offer to install it for you if needed.

What does it do?

Detect Undefined Behavior

The most important thing cargo careful does is that it builds the standard library with debug assertions. The standard library does check for some Undefined Behavior when the program is built with debug assertions, but some of these checks are disabled because their performance impact was considered too high. Furthermore, cargo careful sets some flags that tell rustc to insert extra run-time checks.

Here are some of the checks this enables:

  • ptr.read()/ptr.write(v) check that the pointer is aligned and non-null.
  • The collection types perform plenty of internal consistency checks.
  • mem::zeroed and the deprecated mem::uninitialized panic if the type does not allow that kind of initialization (with a check that is stricter than the default). (This is -Zstrict-init-checks.)
  • Extra UB-checking is done during const-evaluation. (This is -Zextra-const-ub-checks.)

That said, there is a lot of Undefined Behavior that is not detected by cargo careful; check out Miri if you want to be more exhaustively covered. The advantage of cargo careful over Miri is that it works on all code, supports using arbitrary system and C FFI functions, and is much faster.

RUSTFLAGS

cargo careful honors the CARGO_ENCODED_RUSTFLAGS and RUSTFLAGS environment variables as well as the build.rustflags cargo setting (in that order, the first one being set is used). It currently does not honor the target.rustflags settings as that would require re-implementing all the target cfg logic from cargo. The flags are applied to both the sysroot build and the program itself.

Sanitizing

cargo careful can additionally build and run your program and standard library with a sanitizer. This feature is experimental and disabled by default.

The underlying rustc feature doesn't play well with procedural macros. If you see error messages involving procedural macros during the build, they can sometimes be solved by specifying a target (which can be the same as the host), e.g., --target=x86_64-unknown-linux-gnu.

To use a sanitizer, pass the command-line flag -Zcareful-sanitizer=<your_sanitizer> to cargo careful. The list of supported sanitizers and targets can be found here. If you pass -Zcareful-sanitizer without specifying a sanitizer, AddressSanitizer will be used.

By default, when using AddressSanitizer, cargo careful will disable memory leak checking by setting ASAN_OPTIONS=detect_leaks=0 in your program's environment, as memory leaks are not usually a soundness or correctness issue. If you set the ASAN_OPTIONS environment variable yourself (to any value, including an empty string), that will override this behavior.

Main Thread Checker

cargo careful automatically enables Apple's Main Thread Checker on macOS, iOS, tvOS and watchOS targets, whenever the user has Xcode installed.

This helps diagnosing issues with executing thread-unsafe functionality off the main thread on those platforms.

cfg flag

cargo careful sets the careful configuration flag, so you can use Rust's compile-time conditional mechanisms (#[cfg(careful)], #[cfg_attr(careful, ...)], cfg!(careful)) to check whether code is being run carefully.

Dependencies

~2–11MB
~141K SLoC