79 lines
3.3 KiB
Markdown
79 lines
3.3 KiB
Markdown
# Unsafety checking
|
|
|
|
Certain expressions in Rust 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](#the-unused_unsafe-lint).
|
|
|
|
The unsafety check needs type information so could potentially be done on the
|
|
HIR, making use of typeck results, THIR or MIR. THIR is chosen because there are
|
|
fewer cases to consider than in HIR, for example unsafe function calls and
|
|
unsafe method calls have the same representation in THIR. The check is not done
|
|
on MIR because safety checks do not depend on control flow so MIR is not
|
|
necessary to use and MIR doesn't have as precise spans for some expressions.
|
|
|
|
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::Deref`s 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.
|
|
|
|
[THIR]: ./thir.md
|
|
[`check_unsafety`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_build/check_unsafety/index.html
|
|
[`NonNull`]: https://doc.rust-lang.org/std/ptr/struct.NonNull.html
|
|
|
|
## 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
|
|
|
|
```rust
|
|
#![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.
|
|
|
|
[Unsafe traits]: https://doc.rust-lang.org/reference/items/traits.html#unsafe-traits
|
|
[coherence]: https://github.com/rust-lang/rust/blob/master/compiler/rustc_hir_analysis/src/coherence/unsafety.rs
|