Behavior considered undefined - The Rust Reference (original) (raw)

The Rust Reference

Behavior considered undefined

Rust code is incorrect if it exhibits any of the behaviors in the following list. This includes code within unsafe blocks and unsafe functions.unsafe only means that avoiding undefined behavior is on the programmer; it does not change anything about the fact that Rust programs must never cause undefined behavior.

It is the programmer’s responsibility when writing unsafe code to ensure that any safe code interacting with the unsafe code cannot trigger these behaviors. unsafe code that satisfies this property for any safe client is called sound; if unsafe code can be misused by safe code to exhibit undefined behavior, it is unsound.

Warning: The following list is not exhaustive; it may grow or shrink. There is no formal model of Rust’s semantics for what is and is not allowed in unsafe code, so there may be more behavior considered unsafe. We also reserve the right to make some of the behavior in that list defined in the future. In other words, this list does not say that anything will definitely always be undefined in all future Rust version (but we might make such commitments for some list items in the future).

Please read the Rustonomicon before writing unsafe code.

Note: Undefined behavior affects the entire program. For example, calling a function in C that exhibits undefined behavior of C means your entire program contains undefined behaviour that can also affect the Rust code. And vice versa, undefined behavior in Rust can cause adverse affects on code executed by any FFI calls to other languages.

Pointed-to bytes

The span of bytes a pointer or reference “points to” is determined by the pointer value and the size of the pointee type (using size_of_val).

Places based on misaligned pointers

A place is said to be “based on a misaligned pointer” if the last * projection during place computation was performed on a pointer that was not aligned for its type. (If there is no * projection in the place expression, then this is accessing the field of a local or static and rustc will guarantee proper alignment. If there are multiple * projection, then each of them incurs a load of the pointer-to-be-dereferenced itself from memory, and each of these loads is subject to the alignment constraint. Note that some * projections can be omitted in surface Rust syntax due to automatic dereferencing; we are considering the fully expanded place expression here.)

For instance, if ptr has type *const S where S has an alignment of 8, thenptr must be 8-aligned or else (*ptr).f is “based on an misaligned pointer”. This is true even if the type of the field f is u8 (i.e., a type with alignment 1). In other words, the alignment requirement derives from the type of the pointer that was dereferenced, not the type of the field that is being accessed.

Note that a place based on a misaligned pointer only leads to Undefined Behavior when it is loaded from or stored to.

&raw const/&raw mut on such a place is allowed.

&/&mut on a place requires the alignment of the field type (or else the program would be “producing an invalid value”), which generally is a less restrictive requirement than being based on an aligned pointer.

Taking a reference will lead to a compiler error in cases where the field type might be more aligned than the type that contains it, i.e., repr(packed). This means that being based on an aligned pointer is always sufficient to ensure that the new reference is aligned, but it is not always necessary.

Dangling pointers

A reference/pointer is “dangling” if not all of the bytes itpoints to are part of the same live allocation (so in particular they all have to be part of some allocation).

If the size is 0, then the pointer is trivially never “dangling” (even if it is a null pointer).

Note that dynamically sized types (such as slices and strings) point to their entire range, so it is important that the length metadata is never too large.

In particular, the dynamic size of a Rust value (as determined by size_of_val) must never exceed isize::MAX, since it is impossible for a single allocation to be larger than isize::MAX.

Invalid values

The Rust compiler assumes that all values produced during program execution are “valid”, and producing an invalid value is hence immediate UB.

Whether a value is valid depends on the type:

Note: Uninitialized memory is also implicitly invalid for any type that has a restricted set of valid values. In other words, the only cases in which reading uninitialized memory is permitted are inside unions and in “padding” (the gaps between the fields of a type).