Crate checkers

source ·
Expand description


Checkers is a simple allocation sanitizer for Rust. It plugs in through the global allocator and can sanity check your unsafe Rust during integration testing. Since it plugs in through the global allocator it doesn’t require any additional dependencies and works for all platforms - but it is more limited in what it can verify.

It can check for the following things:

  • Double-frees.
  • Memory leaks.
  • Freeing regions which are not allocated.
  • Freeing only part of regions which are allocated.
  • Freeing a region with a mismatched layout.
  • That the underlying allocator produces regions adhering to the requested layout. Namely size and alignment.
  • Detailed information on memory usage.
  • Other user-defined conditions (see test).

What it can’t do:

  • Test multithreaded code. Since the allocator is global, it is difficult to scope the state for each test case.
  • Detect out-of-bounds accesses.


Add checkers as a dev-dependency to your project:

checkers = "0.6.2"

Replace the global allocator in a test file and wrap tests you wish to memory sanitise with #[checkers::test]:

static ALLOCATOR: checkers::Allocator = checkers::Allocator::system();

fn test_allocations() {
    let _ = Box::into_raw(Box::new(42));

Note that it’s important that you write your test as an integration test by adding it to your tests/ folder to isolate the use of the global allocator.


With the default feature set, this library performs diagnostics which will produce undefined behavior. Therefore, it is recommended that you only use checkers for testing, and never in any production code.

If you want to avoid this, you’ll have to disable the realloc and zeroed features, but this will also produce less actionable diagnostics.

In a future release, this behavior will be changed to be opt-in through feature flags instead of enabled by default.


The following are features available, that changes how checkers work.

  • realloc - Enabling this feature causes checker to verify that a realloc operation is correctly implemented. That bytes from the old region were faithfully transferred to the new, resized one. Since this can have a rather significant performance impact, it can be disabled. Note that this will produce undefined behavior (#1) by reading uninitialized memory, and should only be enabled to provide diagnostics on a best-effort basis.
  • zeroed - Enabling this feature causes checkers to verify that a call to alloc_zeroed produces a region where all bytes are set to zero. Note that if the underlying allocator is badly implemented this will produce undefined behavior (#1) since it could read uninitialized memory.
  • macros - Enables dependencies and re-exports of macros, like #[checkers::test].
  • backtrace - Enables the capture and rendering of backtraces. If disabled, any fields containing backtraces will be None.


It is recommended that you use checkers for integration tests, which by default lives in the ./tests directory. Each file in this directory will be compiled as a separate program, so the use of the global allocator can be more isolated.

We then use checkers by installing checkers::Allocator as the global allocator, after this we can make use of #[checkers::test] attribute macro or the checkers::with function in our tests.

static ALLOCATOR: checkers::Allocator = checkers::Allocator::system();

fn test_allocations() {
    let _ = Box::into_raw(Box::new(42));

The above would result in the following test output:

dangling region: 0x226e5784f30-0x226e5784f40 (size: 16, align: 8).
thread 'test_leak_box' panicked at 'allocation checks failed', tests\

With checkers::with, we can perform more detailed diagnostics:

static ALLOCATOR: checkers::Allocator = checkers::Allocator::system();

fn test_event_inspection() {
    let snapshot = checkers::with(|| {
        let _ = vec![1, 2, 3, 4];

    assert!([0].is_alloc_with(|r| r.size >= 16));
    assert!([1].is_free_with(|a| a.size >= 16));
    assert!( >= 16);


  • Verify the state of the allocator.


  • Description of an allocation that is zeroed by the allocator.
  • Allocator that needs to be installed.
  • Collections of events.
  • Fake machine implementation to validate an allocation history.
  • A helper guard to make sure the state is de-allocated on drop.
  • A type-erased pointer. The inner representation is specifically not a raw pointer to avoid aliasing the pointers handled by the allocator.
  • Description of a reallocation.
  • Description of a null reallocation. These are always considered errors.
  • A memory region. Including its location in memory ptr, it’s size and alignment align.
  • Metadata about an allocation request.
  • A snapshot of the state of the checkers allocator.
  • Structure containing all thread-local state required to use the single-threaded allocation checker.


  • Metadata for a single allocation or deallocation.
  • A single violation in the variants enforced by checkers.


  • Test if the crate is currently muted. The allocator is muted by default.
  • Enable muting for the duration of the guard. A guard ensures that the muted state is restored to its original value even if we are unwinding due to a panic. You should prefer to use with_unmuted when possible.
  • Run the specified closure and return a snapshot of the memory state afterwards.
  • Run the given closure while the allocator is muted. This can be used to whitelist sections of code where the allocation checker should be disabled.
  • Perform an operation, while having access to the thread-local state.
  • Run the given closure while the allocator is unmuted.

Attribute Macros

  • Run a #[test] function in checkers.