Function autoken::assume_black_box

source ·
pub fn assume_black_box<T>(f: impl FnOnce() -> T) -> T
Expand description

Tells the AuToken static analyzer to entirely ignore the body of the provided closure. This should be a last resort for when nothing else works out.

This prevents both enumeration of unsizing coercions performed by the closure (which contribute to the set of potential dynamic dispatch targets for a given function pointer or trait type) and detection of the various borrow and unborrow function calls. This can be particularly tricky if you return a guard from the black-boxed closure since, although the call to borrow_mutably was ignored, the call to unborrow_mutably in the destructor is not:

use autoken::MutableBorrow;

// The call to `borrow_mutably` was just ignored.
let guard = autoken::assume_black_box(|| MutableBorrow::<u32>::new());

// ...but the call to `unborrow_mutably` was not!
drop(guard);

// Autoken now thinks that we have -1 mutable borrows in scope!

If we want to fix this, we need to make sure to strip_lifetime_analysis on all the guards we return:

use autoken::MutableBorrow;

// The call to `borrow_mutably` was just ignored.
let guard = autoken::assume_black_box(|| {
    MutableBorrow::<u32>::new().strip_lifetime_analysis()
});

// Luckily, in stripping its lifetime analysis, it no longer calls `unborrow_mutably` here.
drop(guard);

// Autoken now has an accurate idea of the number of guards in scope.

It bears repeating that this function is super dangerous. Please consider all the alternatives listed in the crate’s Ignoring False Positives and Making Sense of Control Flow Errors section before even thinking about reaching for this function!

In regular builds, this does nothing, but when AuToken checks a given binary, it uses calls to functions like this to determine whether a program has the possibility of virtually borrowing a global token in a way which violates XOR borrowing rules.