Skip to content

Commit bb9cd50

Browse files
committed
rust: fix review findings (broken links, tech accuracy, wording)
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.
1 parent a32e409 commit bb9cd50

7 files changed

Lines changed: 29 additions & 24 deletions

File tree

content/docs/languages/rust/10-security-overview.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ Dealing with numbers is safe in Rust, but some operations may produce unexpected
8282

8383
There are [three types of integer bugs](https://phrack.org/issues/60/10.html#article): arithmetic overflows, width overflows, and signedness errors.
8484

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.
8686

8787
| Example | Result | Description |
8888
|-------------------------------------|-----------|----------------------------------------|

content/docs/languages/rust/20-dynamic-analysis.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ cargo hack test --feature-powerset --depth 2
116116
{{< /tabs >}}
117117
118118
{{< hint info >}}
119-
Look for the info: running string in the test output to check what features were used.
119+
Look for the `info: running` string in the test output to check what features were used.
120120

121121
Use the `--print-command-list` option for a dry run.
122122

@@ -218,7 +218,7 @@ mod tests {
218218
fn int_overflow_simple() {
219219
let y_str = "2147483647";
220220
let y = y_str.parse::<i32>().unwrap();
221-
let x = do_overflow(y);
221+
let _ = do_overflow(y);
222222
}
223223
224224
#[should_panic]
@@ -227,7 +227,7 @@ mod tests {
227227
let y_str = "2147483647";
228228
let y = y_str.parse::<i32>().unwrap();
229229
println!("{}", y);
230-
let a = as_u16(y);
230+
let _ = as_u16(y);
231231
}
232232
}
233233
```
@@ -266,7 +266,7 @@ At this time, nightly toolchains must be used for sanitizers. If you use the sta
266266
```sh
267267
for sanitizer in "address" "leak" "memory" "thread"; do
268268
echo "Testing with $sanitizer"
269-
export RUSTFLAGS="-Z sanitizer=$sanitizer"
269+
export RUSTFLAGS="-Zsanitizer=$sanitizer"
270270
export RUSTDOCFLAGS="$RUSTFLAGS"
271271
cargo +nightly test -Zbuild-std --target x86_64-unknown-linux-gnu
272272
done
@@ -278,7 +278,7 @@ done
278278
```sh
279279
for sanitizer in "address" "leak" "memory" "thread"; do
280280
echo "Testing with $sanitizer"
281-
export RUSTFLAGS="-Z sanitizer=$sanitizer"
281+
export RUSTFLAGS="-Zsanitizer=$sanitizer"
282282
export RUSTDOCFLAGS="$RUSTFLAGS"
283283
cargo +nightly nextest run -Zbuild-std --target x86_64-unknown-linux-gnu
284284
done
@@ -314,7 +314,7 @@ A few tips:
314314
The test below passes, but there is actually a bug. AddressSanitizer can help us find it.
315315

316316
```sh
317-
RUSTFLAGS='-Z sanitizer=address' cargo +nightly test -Zbuild-std --target x86_64-unknown-linux-gnu
317+
RUSTFLAGS='-Zsanitizer=address' cargo +nightly test -Zbuild-std --target x86_64-unknown-linux-gnu
318318
```
319319

320320
```rust
@@ -327,7 +327,7 @@ mod tests {
327327
let a = vec![7, 3, 3, 1];
328328
let b = a.as_ptr();
329329
drop(a);
330-
let z = unsafe { *b };
330+
let _z = unsafe { *b };
331331
}
332332
}
333333
```
@@ -426,7 +426,7 @@ Keep these tips in mind while using Miri:
426426

427427
* Disable the longest-running tests if needed.
428428

429-
* Consider disabling the most heavy Miri detectors like `-Zmiri-disable-stacked-borrows` and `-Zmiri-disable-validation`.
429+
* Consider disabling the heaviest Miri detectors like `-Zmiri-disable-stacked-borrows` and `-Zmiri-disable-validation`.
430430

431431
* Use [`--test-threads` or `-j` flag with `nextest`](https://nexte.st/docs/integrations/miri/#benefits) to improve the speed.
432432

@@ -668,7 +668,7 @@ Three popular tools wrap the above engines for easier consumption in Rust projec
668668
| Coverage | Lines, functions, branches | Lines, functions, branches, regions, MC/DC | Lines |
669669
| Output format | LCOV, JSON, HTML, Cobertura, Coveralls+, Markdown, ADE | Text, LCOV, JSON, HTML, Cobertura, Codecov | Text, LCOV, JSON, HTML, XML |
670670
| 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) |
672672
| To exclude test coverage | No | [With external module](https://github.com/taiki-e/coverage-helper/tree/v0.2.0) | `--ignore-tests` |
673673
| 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 |
674674
| 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) |

content/docs/languages/rust/30-static-analysis.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,12 @@ cargo +nightly clippy -- \
4848
These are our favorite lints:
4949

5050
* [`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
5252
* [`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]`
5353

5454
```sh
5555
# WARNING: The next command modifies files!
56-
cargo clippy --fix --allow-dirty -- -W clippy::must-use-candidate
56+
cargo clippy --fix --allow-dirty -- -W clippy::must_use_candidate
5757
cargo check --all-targets
5858
```
5959

@@ -83,10 +83,10 @@ For continuous use, such as in a CI/CD pipeline, you want to minimize false posi
8383

8484
```sh
8585
cargo clippy -- \
86-
-Dwarnings -A clippy::style -W clippy::arithmetic-side-effects \
86+
-Dwarnings -A clippy::style -W clippy::arithmetic_side_effects \
8787
-W clippy::string_slice -W clippy::infinite_loop \
8888
-W clippy::float_cmp_const
89-
# then enable style and pedantic like -W clippy::same-item-push -W clippy::cast_lossless
89+
# then enable style and pedantic like -W clippy::same_item_push -W clippy::cast_lossless
9090
```
9191

9292
You can contribute new lints to Clippy. To do so, [follow the official guidance](https://doc.rust-lang.org/clippy/development/index.html).

content/docs/languages/rust/50-memory-zeroization.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@ to find missing zeroizations and compiler-removed wipes.
2525

2626
## Security level 1: Use ZeroizeOnDrop (the common practice)
2727

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.
2929

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)).
3131

3232
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.
3333

@@ -56,11 +56,15 @@ fn main() {
5656
}
5757
```
5858

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+
5963
## Security level 2: Zeroization target is not moved or moved explicitly
6064

6165
An alternative to the best-effort approach is to fight the compiler and attempt to guarantee that copies do not live in memory.
6266

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).
6468

6569
```rust
6670
use std::{marker::PhantomPinned, pin::pin};
@@ -132,12 +136,12 @@ let key = derive_key();
132136
SecretBox::new(Box::new(key))
133137
```
134138

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.
136140

137141
## Security level 3: Tear down processes, allocators, and the stack intermittently
138142

139143
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).
140144

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).
142146

143147
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.

content/docs/languages/rust/60-model-checking.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -209,11 +209,11 @@ Kani does not scale well for the following:
209209

210210
[Aeneas](https://github.com/AeneasVerif/aeneas)
211211

212-
* Converts Rust code to pure lambda calculus (LEAN, Coq, etc.)
212+
* Converts Rust code to a pure lambda calculus, with backends for F\*, Coq, HOL4, and Lean
213213

214214
[MIRAI](https://github.com/endorlabs/MIRAI)
215215

216-
* Implements abstract interpretation, taint analysis, and constant time analysis
216+
* Implements abstract interpretation, taint analysis, and constant-time analysis
217217

218218
[Stateright](https://www.stateright.rs/title-page.html)
219219

materials/rust/coverage/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
[package]
2-
name = "tmp"
2+
name = "coverage-example"
33
version = "0.1.0"
44
edition = "2021"

materials/rust/coverage/Dockerfile

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
FROM rust:1.95.0-slim-trixie
22

3-
RUN apt-get update
4-
RUN apt-get install -y pkg-config libssl-dev lcov
3+
RUN apt-get update \
4+
&& apt-get install -y --no-install-recommends pkg-config libssl-dev lcov \
5+
&& rm -rf /var/lib/apt/lists/*
56
RUN cargo install cargo-tarpaulin
67
RUN cargo install cargo-llvm-cov --locked
78
RUN cargo install llvm-cov-pretty

0 commit comments

Comments
 (0)