You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Corrections from pre-release review of the Rust chapter:
- 20-dynamic-analysis: fix broken cargo-llvm-cov anchor
(#exclude-function-from-coverage -> #exclude-code-from-coverage);
normalize `-Z sanitizer=` to `-Zsanitizer=` (matches -Zbuild-std elsewhere
and official docs); quote `info: running`; "most heavy" -> "heaviest";
silence unused-var warnings in example snippets (let _ / let _z).
- 50-memory-zeroization: correct ZeroizeOnDrop mechanics (derive generates a
Drop impl that zeroizes each field, skipping #[zeroize(skip)]; each field
must impl Zeroize/ZeroizeOnDrop) per zeroize_derive source; re-point the
moves-create-copies claim to benma's blog and describe it accurately
(memcpy, source not zeroed); add best-effort caveat that Drop is not
guaranteed to run (mem::forget, ManuallyDrop, Rc/Arc cycles, abort,
process::exit); `pin` feature -> `Pin` type; drop redundant "RAM memory".
- 30-static-analysis: correct string_slice lint description (flags all &str
slicing); make lint-name flags consistently underscore-cased.
- 10-security-overview: clearer overflow-method phrasing
("wrap and report the overflow", "check (returning an Option)").
- 60-model-checking: Aeneas backends are F*, Coq, HOL4, and Lean;
"constant time" -> "constant-time".
- materials/rust/coverage: rename placeholder crate tmp -> coverage-example
(builds, 6 tests pass); combine Dockerfile apt layers with
--no-install-recommends and clean apt lists.
Copy file name to clipboardExpand all lines: content/docs/languages/rust/10-security-overview.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -82,7 +82,7 @@ Dealing with numbers is safe in Rust, but some operations may produce unexpected
82
82
83
83
There are [three types of integer bugs](https://phrack.org/issues/60/10.html#article): arithmetic overflows, width overflows, and signedness errors.
84
84
85
-
Rust can handle arithmetic overflows in a few ways: [wrap over](https://doc.rust-lang.org/std/primitive.i32.html#method.wrapping_add), [wrap with information](https://doc.rust-lang.org/std/primitive.i32.html#method.overflowing_add), [check](https://doc.rust-lang.org/std/primitive.i32.html#method.checked_add), [saturate](https://doc.rust-lang.org/std/primitive.i32.html#method.saturating_add), [produce undefined behavior](https://doc.rust-lang.org/std/primitive.i32.html#method.unchecked_add), and panic.
85
+
Rust can handle arithmetic overflows in a few ways: [wrap around](https://doc.rust-lang.org/std/primitive.i32.html#method.wrapping_add), [wrap and report the overflow](https://doc.rust-lang.org/std/primitive.i32.html#method.overflowing_add), [check (returning an `Option`)](https://doc.rust-lang.org/std/primitive.i32.html#method.checked_add), [saturate](https://doc.rust-lang.org/std/primitive.i32.html#method.saturating_add), [produce undefined behavior](https://doc.rust-lang.org/std/primitive.i32.html#method.unchecked_add), and panic.
| To exclude files |`--ignore`|[`--ignore-filename-regex`](https://github.com/taiki-e/cargo-llvm-cov?tab=readme-ov-file#exclude-file-from-coverage)|`--exclude-files`|
671
-
| To exclude functions | With in-code markers and regexes |[With attributes](https://github.com/taiki-e/cargo-llvm-cov?tab=readme-ov-file#exclude-function-from-coverage)|[With attributes](https://github.com/xd009642/tarpaulin?tab=readme-ov-file#ignoring-code-in-files)|
671
+
| To exclude functions | With in-code markers and regexes |[With attributes](https://github.com/taiki-e/cargo-llvm-cov?tab=readme-ov-file#exclude-code-from-coverage)|[With attributes](https://github.com/xd009642/tarpaulin?tab=readme-ov-file#ignoring-code-in-files)|
672
672
| To exclude test coverage | No |[With external module](https://github.com/taiki-e/coverage-helper/tree/v0.2.0)|`--ignore-tests`|
673
673
| To enable coverage for C/C++ | Unknown |[`--include-ffi`](https://github.com/taiki-e/cargo-llvm-cov?tab=readme-ov-file#get-coverage-of-cc-code-linked-to-rust-librarybinary)| Unknown |
674
674
| Merges runs across different builds? | No |[Yes](https://github.com/taiki-e/cargo-llvm-cov?tab=readme-ov-file#merge-coverages-generated-under-different-test-conditions)|[Yes](https://github.com/xd009642/tarpaulin?tab=readme-ov-file#command-line) (but only shows delta) |
Copy file name to clipboardExpand all lines: content/docs/languages/rust/30-static-analysis.md
+4-4Lines changed: 4 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -48,12 +48,12 @@ cargo +nightly clippy -- \
48
48
These are our favorite lints:
49
49
50
50
*[`arithmetic_side_effects`](https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic_side_effects): Detects potential side effects of arithmetic operations (e.g., integer overflows, division by zero)
51
-
*[`string_slice`](https://rust-lang.github.io/rust-clippy/master/index.html#string_slice): Detects potential slices that do not align with Unicode scalar value
51
+
*[`string_slice`](https://rust-lang.github.io/rust-clippy/master/index.html#string_slice): Flags all slicing of `&str`, which can panic when an index does not fall on a UTF-8 character boundary
52
52
*[`must_use_candidate`](https://rust-lang.github.io/rust-clippy/master/index.html#must_use_candidate): Detects public functions that could be marked with `#[must_use]`
Copy file name to clipboardExpand all lines: content/docs/languages/rust/50-memory-zeroization.md
+9-5Lines changed: 9 additions & 5 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -25,9 +25,9 @@ to find missing zeroizations and compiler-removed wipes.
25
25
26
26
## Security level 1: Use ZeroizeOnDrop (the common practice)
27
27
28
-
Roughly, the idea is to ensure that values are zeroed whenever they fall out of scope. The [`zeroize`](https://docs.rs/zeroize/latest/zeroize/) crate is the most common way to achieve this goal. Owned values derive the `ZeroizeOnDrop` trait, causing values to call their component’s `zeroize` methods when the compiler inserts a drop on that value. This approach offers a simple model of zeroization where you do not fight the compiler and simply attempt to guarantee that each drop is covered by zeroization.
28
+
Roughly, the idea is to ensure that values are zeroed whenever they fall out of scope. The [`zeroize`](https://docs.rs/zeroize/latest/zeroize/) crate is the most common way to achieve this goal. Deriving `#[derive(ZeroizeOnDrop)]` generates a `Drop` implementation whose `drop` method calls `zeroize` on each of the value’s fields (skipping any annotated with `#[zeroize(skip)]`) when the compiler drops that value; each field must itself implement `Zeroize` or `ZeroizeOnDrop`. This approach offers a simple model of zeroization where you do not fight the compiler and simply attempt to guarantee that each drop is covered by zeroization.
29
29
30
-
Unfortunately, moves can, and often do, create copies. These copies happen specifically [when stack values are moved](https://docs.rs/zeroize/latest/zeroize/#stackheap-zeroing-notes). However, ABI constraints or optimization of heap values can also result in a copy.
30
+
Unfortunately, moves can, and often do, [create copies](https://benma.github.io/2020/10/16/rust-zeroize-move.html): a move is compiled to a `memcpy` in the general case, and the compiler does not zero the source. Additional copies can arise from stack spilling, ABI constraints, and optimizations of heap values (see the [`zeroize` stack/heap zeroing notes](https://docs.rs/zeroize/latest/zeroize/#stackheap-zeroing-notes)).
31
31
32
32
A simple case of failed zeroization is shown below. A stack value implementing the `ZeroizeOnDrop` trait is moved to the heap ("leak A"): only the heap value is zeroized; the old copy of the secret may remain on the stack.
33
33
@@ -56,11 +56,15 @@ fn main() {
56
56
}
57
57
```
58
58
59
+
{{< hint danger >}}
60
+
`Drop`-based zeroization is best-effort: running a destructor is not guaranteed. A `Drop` implementation (and therefore the zeroization it performs) is skipped by [`mem::forget`](https://doc.rust-lang.org/std/mem/fn.forget.html) and [`ManuallyDrop`](https://doc.rust-lang.org/std/mem/struct.ManuallyDrop.html), by values kept alive in reference cycles (`Rc`/`Arc`), by an [abort](https://doc.rust-lang.org/std/process/fn.abort.html) (including a panic during unwinding, or any panic under `panic = "abort"`), and by [`process::exit`](https://doc.rust-lang.org/std/process/fn.exit.html). Do not rely on `ZeroizeOnDrop` alone to erase secrets on abnormal termination.
61
+
{{< /hint >}}
62
+
59
63
## Security level 2: Zeroization target is not moved or moved explicitly
60
64
61
65
An alternative to the best-effort approach is to fight the compiler and attempt to guarantee that copies do not live in memory.
62
66
63
-
You can attempt to prevent spurious compiler-introduced copies created by moves by disallowing moves through the [`pin`](https://doc.rust-lang.org/std/pin/)feature. Using `pin` provides some compile-time safety, as shown in the example below. The "leak A" from level 1 is no longer possible (code won't compile).
67
+
You can attempt to prevent spurious compiler-introduced copies created by moves by disallowing moves through the [`Pin`](https://doc.rust-lang.org/std/pin/)type. Using `Pin` provides some compile-time safety, as shown in the example below. The "leak A" from level 1 is no longer possible (code won't compile).
64
68
65
69
```rust
66
70
usestd::{marker::PhantomPinned, pin::pin};
@@ -132,12 +136,12 @@ let key = derive_key();
132
136
SecretBox::new(Box::new(key))
133
137
```
134
138
135
-
Another shortcoming of the `secrecy` and `pin` solutions is that they do not use `mlock` or similar mechanisms to prevent the OS from writing the secrets into swap or hibernation images.
139
+
Another shortcoming of the `secrecy` and `Pin` solutions is that they do not use `mlock` or similar mechanisms to prevent the OS from writing the secrets into swap or hibernation images.
136
140
137
141
## Security level 3: Tear down processes, allocators, and the stack intermittently
138
142
139
143
To really guarantee that data is no longer in memory (though the definition of *guarantee* depends on the kernel, hardware, etc.), you can tear down processes or worker threads and clear all memory associated with them at set points where the data should leave memory (i.e., after a request has been processed). The easiest version of this approach is a worker process that only returns a result and is killed after finishing a request. A more complex version with threads would have to [clear stack and possibly other memory locations explicitly](https://docs.rs/clear_on_drop/latest/clear_on_drop/fn.clear_stack_on_return.html).
140
144
141
-
This approach effectively relies on the kernel to provide memory-level process isolation. It should prevent compromise of secrets if the main process is compromised. However, it will not prevent secrets from residing in RAM memory until overwritten at some random point in time (the data may be retrieved with specialized lab equipment).
145
+
This approach effectively relies on the kernel to provide memory-level process isolation. It should prevent compromise of secrets if the main process is compromised. However, it will not prevent secrets from residing in RAM until overwritten at some random point in time (the data may be retrieved with specialized lab equipment).
142
146
143
147
A more complex approach would be to have the code iterate over subprocesses’ and threads’ writable memory regions and overwrite them with zeros or random data just before they are killed. However, even with such an overly complex solution, you may not be sure about data zeroization because a process-level implementation cannot provide guarantees that are effectively hardware-level.
0 commit comments