diff --git a/src/SUMMARY.md b/src/SUMMARY.md index ff7b6239941a..3c2b54db37ba 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -207,6 +207,9 @@ - [Mutable Static Variables](unsafe-rust/mutable-static.md) - [Unions](unsafe-rust/unions.md) - [Unsafe Functions](unsafe-rust/unsafe-functions.md) + - [Unsafe External Functions](unsafe-rust/unsafe-extern-c-functions.md) + - [Unsafe Rust Functions](unsafe-rust/unsafe-rust-functions.md) + - [Calling Unsafe Functions](unsafe-rust/calling-unsafe-functions.md) - [Unsafe Traits](unsafe-rust/unsafe-traits.md) - [Exercise: FFI Wrapper](unsafe-rust/exercise.md) - [Solution](unsafe-rust/solution.md) diff --git a/src/unsafe-rust/calling-unsafe-functions.md b/src/unsafe-rust/calling-unsafe-functions.md new file mode 100644 index 000000000000..9a932894d2c5 --- /dev/null +++ b/src/unsafe-rust/calling-unsafe-functions.md @@ -0,0 +1,47 @@ +--- +minutes: 5 +--- + +# Calling Unsafe Functions + +Failing to uphold the safety requirements breaks memory safety! + +```rust,editable +#[derive(Debug)] +#[repr(C)] +struct KeyPair { + pk: [u16; 4], // 8 bytes + sk: [u16; 4], // 8 bytes +} + +const PK_BYTE_LEN: usize = 8; + +fn log_public_key(pk_ptr: *const u16) { + let pk: &[u16] = unsafe { std::slice::from_raw_parts(pk_ptr, PK_BYTE_LEN) }; + println!("{pk:?}"); +} + +fn main() { + let key_pair = KeyPair { pk: [1, 2, 3, 4], sk: [0, 0, 42, 0] }; + log_public_key(key_pair.pk.as_ptr()); +} +``` + +Always include a safety comment for each `unsafe` block. It must explain why the +code is actually safe. This example is missing a safety comment and has UB. + +
+ +Key points: + +- The second argument to `slice::from_raw_parts` is the number of _elements_, + not bytes! This example demonstrate undefined behavior by reading past the end + of one array and into another. + +- The standard library contains many low-level unsafe functions. Prefer the safe + alternatives when possible! + +- If you use an unsafe function as an optimization, make sure to add a benchmark + to demonstrate the gain. + +
diff --git a/src/unsafe-rust/unsafe-extern-c-functions.md b/src/unsafe-rust/unsafe-extern-c-functions.md new file mode 100644 index 000000000000..8b65bdceb9f3 --- /dev/null +++ b/src/unsafe-rust/unsafe-extern-c-functions.md @@ -0,0 +1,33 @@ +--- +minutes: 3 +--- + +# Unsafe External Functions + +All functions implemented in a foreign language are considered unsafe in Rust: + +```rust,editable +extern "C" { + fn abs(input: i32) -> i32; +} + +fn main() { + // SAFETY: `abs` doesn't deal with pointers and doesn't have any safety + // requirements. + unsafe { + println!("Absolute value of -3 according to C: {}", abs(-3)); + } +} +``` + +
+ +`abs` is unsafe because it is an external function (FFI). Calling external +functions is usually only a problem when those functions do things with pointers +which might violate Rust's memory model, but in general any C function might +have undefined behaviour under any arbitrary circumstances. + +The `"C"` in this example is the ABI; +[other ABIs are available too](https://doc.rust-lang.org/reference/items/external-blocks.html). + +
diff --git a/src/unsafe-rust/unsafe-functions.md b/src/unsafe-rust/unsafe-functions.md index 6503506f8fc7..aba15850afae 100644 --- a/src/unsafe-rust/unsafe-functions.md +++ b/src/unsafe-rust/unsafe-functions.md @@ -1,100 +1,19 @@ --- -minutes: 5 +minutes: 1 --- # Unsafe Functions -## Calling Unsafe Functions - A function or method can be marked `unsafe` if it has extra preconditions you -must uphold to avoid undefined behaviour: - -```rust,editable -extern "C" { - fn abs(input: i32) -> i32; -} - -fn main() { - let emojis = "🗻∈🌏"; - - // SAFETY: The indices are in the correct order, within the bounds of the - // string slice, and lie on UTF-8 sequence boundaries. - unsafe { - println!("emoji: {}", emojis.get_unchecked(0..4)); - println!("emoji: {}", emojis.get_unchecked(4..7)); - println!("emoji: {}", emojis.get_unchecked(7..11)); - } - - println!("char count: {}", count_chars(unsafe { emojis.get_unchecked(0..7) })); - - // SAFETY: `abs` doesn't deal with pointers and doesn't have any safety - // requirements. - unsafe { - println!("Absolute value of -3 according to C: {}", abs(-3)); - } - - // Not upholding the UTF-8 encoding requirement breaks memory safety! - // println!("emoji: {}", unsafe { emojis.get_unchecked(0..3) }); - // println!("char count: {}", count_chars(unsafe { - // emojis.get_unchecked(0..3) })); -} - -fn count_chars(s: &str) -> usize { - s.chars().count() -} -``` - -## Writing Unsafe Functions +must uphold to avoid undefined behaviour. -You can mark your own functions as `unsafe` if they require particular -conditions to avoid undefined behaviour. +There are two main categories: -```rust,editable -/// Swaps the values pointed to by the given pointers. -/// -/// # Safety -/// -/// The pointers must be valid and properly aligned. -unsafe fn swap(a: *mut u8, b: *mut u8) { - let temp = *a; - *a = *b; - *b = temp; -} - -fn main() { - let mut a = 42; - let mut b = 66; - - // SAFETY: ... - unsafe { - swap(&mut a, &mut b); - } - - println!("a = {}, b = {}", a, b); -} -``` +- Foreign functions in `extern "C"` blocks. +- Rust functions declared unsafe with `unsafe fn`.
-## Calling Unsafe Functions - -`get_unchecked`, like most `_unchecked` functions, is unsafe, because it can -create UB if the range is incorrect. `abs` is incorrect for a different reason: -it is an external function (FFI). Calling external functions is usually only a -problem when those functions do things with pointers which might violate Rust's -memory model, but in general any C function might have undefined behaviour under -any arbitrary circumstances. - -The `"C"` in this example is the ABI; -[other ABIs are available too](https://doc.rust-lang.org/reference/items/external-blocks.html). - -## Writing Unsafe Functions - -We wouldn't actually use pointers for a `swap` function - it can be done safely -with references. - -Note that unsafe code is allowed within an unsafe function without an `unsafe` -block. We can prohibit this with `#[deny(unsafe_op_in_unsafe_fn)]`. Try adding -it and see what happens. This will likely change in a future Rust edition. +We will look at the two kinds of unsafe functions next.
diff --git a/src/unsafe-rust/unsafe-rust-functions.md b/src/unsafe-rust/unsafe-rust-functions.md new file mode 100644 index 000000000000..4f2f5d8c972e --- /dev/null +++ b/src/unsafe-rust/unsafe-rust-functions.md @@ -0,0 +1,44 @@ +--- +minutes: 3 +--- + +# Unsafe Rust Functions + +You can mark your own functions as `unsafe` if they require particular +conditions to avoid undefined behaviour. + +```rust,editable +/// Swaps the values pointed to by the given pointers. +/// +/// # Safety +/// +/// The pointers must be valid and properly aligned. +unsafe fn swap(a: *mut u8, b: *mut u8) { + let temp = *a; + *a = *b; + *b = temp; +} + +fn main() { + let mut a = 42; + let mut b = 66; + + // SAFETY: ... + unsafe { + swap(&mut a, &mut b); + } + + println!("a = {}, b = {}", a, b); +} +``` + +
+ +We wouldn't actually use pointers for a `swap` function --- it can be done +safely with references. + +Note that unsafe code is allowed within an unsafe function without an `unsafe` +block. We can prohibit this with `#[deny(unsafe_op_in_unsafe_fn)]`. Try adding +it and see what happens. This will likely change in a future Rust edition. + +