Skip to content

Latest commit

 

History

History
72 lines (57 loc) · 2.87 KB

unsafety-checking.md

File metadata and controls

72 lines (57 loc) · 2.87 KB

Unsafety Checking

Certain expressions in Rust are can violate memory safety and as such need to be inside an unsafe block or function. The compiler will also warn if an unsafe block is used without any corresponding unsafe operations.

Overview

The unsafety check is located in the check_unsafety module. It performs a walk over the THIR of a function and all of its closures and inline constants. It keeps track of the unsafe context: whether it has entered an unsafe block. If an unsafe operation is used outside of an unsafe block, then an error is reported. If an unsafe operation is used in an unsafe block then that block is marked as used for the unused_unsafe lint.

Most unsafe operations can be identified by checking the ExprKind in THIR and checking the type of the argument. For example, dereferences of a raw pointer correspond to ExprKind::Derefs with an argument that has a raw pointer type.

Looking for unsafe Union field accesses is a bit more complex because writing to a field of a union is safe. The checker tracks when it's visiting the left-hand side of an assignment expression and allows union fields to directly appear there, while erroring in all other cases. Union field accesses can also occur in patterns, so those have to be walked as well.

The other complicated safety check is for writes to fields of layout constrained structs (such as NonNull). These are found by looking for the borrow or assignment expression and then visiting the subexpression being borrowed or assigned with a separate visitor.

The unused_unsafe lint

The unused_unsafe lint reports unsafe blocks that can be removed. The unsafety checker records whenever it finds an operation that requires unsafe. The lint is then reported if either:

  • An unsafe block contains no unsafe operations
  • An unsafe block is within another unsafe block, and the outer block isn't considered unused
#![deny(unused_unsafe)]
let y = 0;
let x: *const u8 = core::ptr::addr_of!(y);
unsafe { // lint reported for this block
    unsafe {
        let z = *x;
    }
    let safe_expr = 123;
}
unsafe {
    unsafe { // lint reported for this block
        let z = *x;
    }
    let unsafe_expr = *x;
}

Other checks involving unsafe

Unsafe traits require an unsafe impl to be implemented, the check for this is done as part of coherence. The unsafe_code lint is run as a lint pass on the ast that searches for unsafe blocks, functions and implementations, as well as certain unsafe attributes.