Skip to content

Commit a32e409

Browse files
committed
Polish Rust chapter prose
1 parent 7f74a54 commit a32e409

9 files changed

Lines changed: 83 additions & 81 deletions

.github/workflows/markdown.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ jobs:
4848
persist-credentials: false
4949
- uses: tbroadley/spellchecker-cli-action@8369e98753c0d2c3a3c76fb4519d9056d1d4b129 # v1
5050
with:
51-
# No need to use a dictionary file with the disabled spell plugin
51+
# No need to use a dictionary file with the disabled spell plugin
5252
# dictionaries: '.github/workflows/dictionary.txt'
5353
files: "'content/**/*.md'"
5454
quiet: true

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

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,11 @@ There’s more. Some safe (defined) behavior may result in vulnerabilities. The
4949

5050
* [General race conditions](https://doc.rust-lang.org/nomicon/races.html)
5151
* Deadlocks (blocking bugs)
52-
* Incorrect state synchronization (non-blocking bugs)
53-
* Resource leaks
54-
* Pointer exposures
55-
* Arithmetic errors
56-
* Nondeterminism
52+
* Incorrect state synchronization (non-blocking bugs)
53+
* Resource leaks
54+
* Pointer exposures
55+
* Arithmetic errors
56+
* Nondeterminism
5757
* Logic errors
5858

5959
Moreover, safe Rust may happen to be unsound in some rare cases. Check [the issues on the Rust GitHub](https://github.com/rust-lang/rust/issues?q=is%3Aissue%20state%3Aopen%20label%3AI-unsound) and the ["Counterexamples in Type Systems"](https://counterexamples.org/intro.html) resource for more information. Usually auditors don’t need to focus on these edge cases.
@@ -76,8 +76,8 @@ Pointer exposure is considered safe, because it does not make your program explo
7676

7777
Dealing with numbers is safe in Rust, but some operations may produce unexpected results. There are three main sources of bugs:
7878

79-
* [Integer overflows](https://doc.rust-lang.org/reference/expressions/operator-expr.html#overflow)
80-
* [Imprecision of float operations](https://seclists.org/oss-sec/2023/q2/99)
79+
* [Integer overflows](https://doc.rust-lang.org/reference/expressions/operator-expr.html#overflow)
80+
* [Imprecision of float operations](https://seclists.org/oss-sec/2023/q2/99)
8181
* [Rounding errors](https://github.com/crytic/roundme)
8282

8383
There are [three types of integer bugs](https://phrack.org/issues/60/10.html#article): arithmetic overflows, width overflows, and signedness errors.
@@ -100,7 +100,7 @@ You can read more about integer overflows in [RFC 560](https://github.com/rust-l
100100

101101
Width and signedness overflows can occur when converting between numeric types. Thanks to Rust’s lack of implicit conversions, unexpected overflows are easy to deal with, using one of the following:
102102

103-
* A [checked conversion](https://doc.rust-lang.org/std/convert/trait.TryFrom.html) with overflows handled explicitly (e.g., with a panic)
103+
* A [checked conversion](https://doc.rust-lang.org/std/convert/trait.TryFrom.html) with overflows handled explicitly (e.g., with a panic)
104104
* An [`as` cast](https://doc.rust-lang.org/rust-by-example/types/cast.html) that may result in a wrap-over (but is always well defined, [unlike in C](https://stackoverflow.com/questions/16188263/is-signed-integer-overflow-still-undefined-behavior-in-c))
105105

106106
The latter cast method is more error-prone and should get the same amount of scrutiny as arithmetic overflows. An [`as` cast](https://doc.rust-lang.org/rust-by-example/types/cast.html) silently truncates bigger integer types converted to smaller integer types, even in debug mode.
@@ -113,16 +113,16 @@ There are two types of nondeterminism in Rust: introduced during compilation and
113113

114114
The following are sources of compilation-time nondeterminism:
115115

116-
* Architecture-dependent integral types (like `usize` and `libc::c_char`) and pointer sizes
117-
* [Float numbers](https://internals.rust-lang.org/t/pre-rfc-dealing-with-broken-floating-point/2673)
118-
* [NaN bit representation](https://github.com/rust-lang/rfcs/blob/master/text/3514-float-semantics.md)
119-
* Struct field reordering
116+
* Architecture-dependent integral types (like `usize` and `libc::c_char`) and pointer sizes
117+
* [Float numbers](https://internals.rust-lang.org/t/pre-rfc-dealing-with-broken-floating-point/2673)
118+
* [NaN bit representation](https://github.com/rust-lang/rfcs/blob/master/text/3514-float-semantics.md)
119+
* Struct field reordering
120120
* Enum discriminant values
121121

122122
The following are sources of runtime nondeterminism:
123123

124-
* Iterations over [`HashMap`](https://dev.to/gnunicorn/hunting-down-a-non-determinism-bug-in-our-rust-wasm-build-4fk1) and `HashSet`
125-
* Struct padding
124+
* Iterations over [`HashMap`](https://dev.to/gnunicorn/hunting-down-a-non-determinism-bug-in-our-rust-wasm-build-4fk1) and `HashSet`
125+
* Struct padding
126126
* Pointers (specific memory addresses)
127127

128128
## Logic errors

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

Lines changed: 37 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -244,13 +244,15 @@ To find deep bugs, we can run tests with [various sanitizers](https://doc.rust-l
244244

245245
Examples of Rust sanitizers commonly used for security testing include:
246246

247-
* AddressSanitizer
248-
* HWAddressSanitizer
249-
* LeakSanitizer
250-
* MemorySanitizer
247+
* AddressSanitizer
248+
* HWAddressSanitizer
249+
* LeakSanitizer
250+
* MemorySanitizer
251251
* ThreadSanitizer
252252

253-
To enable them, add the `RUSTFLAGS` environment variable.
253+
To enable them, set the `RUSTFLAGS` environment variable.
254+
255+
The examples below run the subset supported by the shown `x86_64-unknown-linux-gnu` target. Sanitizers are target-specific, so adjust the sanitizer name and target triple for others like HWAddressSanitizer.
254256

255257
{{< hint warning >}}
256258
At this time, nightly toolchains must be used for sanitizers. If you use the stable toolchain, the compilation fails with the following error:
@@ -334,13 +336,13 @@ mod tests {
334336

335337
## Miri
336338

337-
[Miri](https://github.com/rust-lang/miri) is an interpreter for Rust’s "mid-level intermediate representation." Miri helps to detect undefined behaviors like these:
339+
[Miri](https://github.com/rust-lang/miri) is an interpreter for Rust’s "mid-level intermediate representation." Miri helps detect undefined behavior and related issues like these:
338340

339-
* Memory corruption bugs
340-
* Memory leaks
341-
* Uses of uninitialized data
342-
* Memory alignment issues
343-
* Issues with aliasing
341+
* Memory corruption bugs
342+
* Memory leaks
343+
* Uses of uninitialized data
344+
* Memory alignment issues
345+
* Issues with aliasing
344346
* Data races
345347

346348
To use Miri, you must point it at some executable code (it performs dynamic analysis). The easiest is to run your tests through Miri. Note that the nightly toolchain is required.
@@ -460,7 +462,7 @@ Keep these tips in mind while using Miri:
460462

461463
{{< details "Example to try: Miri in action" >}}
462464

463-
The test below should pass or fail on the assertion. Miri would detect undefined behavior.
465+
The test below may pass or fail normally, but Miri should report undefined behavior.
464466

465467
```rust
466468
fn main() { println!("Hello, world!"); }
@@ -509,17 +511,17 @@ To use proptest, you must write unit tests. But instead of hard-coding values th
509511

510512
Proptest ships with many [configurable strategies](https://docs.rs/proptest/latest/proptest/):
511513

512-
* Range-like generator for integers
513-
* Regex generator for strings
514-
* Simple generators for `bit`, `bool`, and `char` values
515-
* Random-size generators for `std::collections`
514+
* Range-like generator for integers
515+
* Regex generator for strings
516+
* Simple generators for `bit`, `bool`, and `char` values
517+
* Random-size generators for `std::collections`
516518
* Generators for `Option` and `Result`
517519

518520
The generators [can be combined together](https://proptest-rs.github.io/proptest/proptest/tutorial/macro-prop-compose.html). You can also use the following `Strategy` methods and the `prop_oneof!` macro to further combine and restrict generation:
519521

520-
* Do mapping with the `prop_map` method
521-
* Do filtering with the `prop_filter` method
522-
* Create enums with the `prop_oneof!` macro
522+
* Do mapping with the `prop_map` method
523+
* Do filtering with the `prop_filter` method
524+
* Create enums with the `prop_oneof!` macro
523525
* Do recursion with the `prop_recursive` method
524526

525527
Let’s see some example code:
@@ -608,13 +610,13 @@ cargo +nightly miri test
608610

609611
{{< /hint >}}
610612

611-
Finally, use our [`property-based-testing`](https://github.com/trailofbits/skills/tree/main/plugins/property-based-testing) Claude skill to automate the testing.
613+
Finally, use our [`property-based-testing`](https://github.com/trailofbits/skills/tree/main/plugins/property-based-testing) skill to automate the testing.
612614

613615
## Coverage
614616

615617
It is critically important to know how much coverage your tests have. Coverage gathering consists of four steps:
616618

617-
* Compile-time instrumentation
619+
* Compile-time instrumentation
618620
* Execution of tests, producing "raw" data
619621
* Merge of per-execution run results
620622
* Conversion of merged data to a usable format (like an HTML report)
@@ -680,7 +682,7 @@ While checking coverage statistics from a command line and using one of many cov
680682
| HTML output/tool | `grcov` | `llvm-cov` | `tarpaulin` |
681683
| :---- | :---- | :---- | :---- |
682684
| Examples | [Open `grcov`]({{% staticref "/languages/rust/coverage/grcov_llvm/" %}}) [Open `grcov` with `lcov`]({{% staticref "/languages/rust/coverage/grcov_llvm_lcov/" %}}) | [Open `llvm-cov`]({{% staticref "/languages/rust/coverage/llvm_cov/" %}}) [Open `llvm-cov-pretty`]({{% staticref "/languages/rust/coverage/llvm_cov_pretty/" %}}) | [Open `tarpaulin`]({{% staticref "/languages/rust/coverage/tarpaulin-report.html" %}}) |
683-
| Handles Rust’s constructions? | Yes | Yes | Yes |
685+
| Handles Rust constructs? | Yes | Yes | Yes |
684686
| Expands Rust’s generics? | No | `--show-instantiations` | No |
685687
| Includes number of hits? | Yes | Yes | Yes |
686688
| Supports multi-file output? | Yes | Yes | No |
@@ -730,11 +732,11 @@ While checking coverage statistics from a command line and using one of many cov
730732

731733
These are our general recommendations for generating test coverage:
732734

733-
* Use `llvm-cov` (with [`llvm-cov-pretty`](https://crates.io/crates/llvm-cov-pretty)) for rapid testing. It is the easiest to run, it resolves generics, and it produces pretty HTML output.
734-
* Use either `llvm-cov` or `grcov` for complex projects. Both are decent and can produce readable outputs.
735-
* Use `tarpaulin` when other tools work incorrectly. [The developers claim](https://github.com/xd009642/tarpaulin?tab=readme-ov-file#nuances-with-llvm-coverage) that this can happen in the event of the following:
736-
* The code panics unexpectedly.
737-
* There are race conditions.
735+
* Use `llvm-cov` (with [`llvm-cov-pretty`](https://crates.io/crates/llvm-cov-pretty)) for rapid testing. It is the easiest to run, it resolves generics, and it produces pretty HTML output.
736+
* Use either `llvm-cov` or `grcov` for complex projects. Both are decent and can produce readable outputs.
737+
* Use `tarpaulin` when other tools work incorrectly. [The developers claim](https://github.com/xd009642/tarpaulin?tab=readme-ov-file#nuances-with-llvm-coverage) that this can happen in the event of the following:
738+
* The code panics unexpectedly.
739+
* There are race conditions.
738740
* The code forks.
739741

740742
For profiling, consider using [`measureme`](https://github.com/rust-lang/measureme), possibly with [Miri and Chrome DevTools](https://medium.com/source-and-buggy/data-driven-performance-optimization-with-rust-and-miri-70cb6dde0d35).
@@ -756,16 +758,16 @@ For starters, you need basic but decent unit test coverage. Then, use one of the
756758

757759
[`cargo-mutants`](https://mutants.rs/welcome.html)
758760

759-
* Easy to use
760-
* Parses the AST of every file with the [`syn` library](https://docs.rs/syn/latest/syn/)
761-
* Partially type-aware
761+
* Easy to use
762+
* Parses the AST of every file with the [`syn` library](https://docs.rs/syn/latest/syn/)
763+
* Partially type-aware
762764
* Can [divide jobs](https://mutants.rs/shards.html) between multiple machines
763765

764766
[`universalmutator`](https://github.com/agroce/universalmutator)
765767

766-
* Multiple languages supported
767-
* Requires more manual setup than `cargo-mutants`
768-
* Two parsing modes: regexes and [Comby](https://github.com/comby-tools/comby)
768+
* Multiple languages supported
769+
* Requires more manual setup than `cargo-mutants`
770+
* Two parsing modes: regexes and [Comby](https://github.com/comby-tools/comby)
769771
* Trivial Compiler Equivalence (TCE) optimization to eliminate redundant mutants before test runs
770772

771773
### Bugs in existing tests
@@ -833,6 +835,6 @@ The tool produces a `necessist.db` file that can be used to resume an interrupte
833835

834836
## Resources
835837

836-
* ["The Rust Programming Language," chapter 11: Testing](https://web.mit.edu/rust-lang_v1.25/arch/amd64_ubuntu1404/share/doc/rust/html/book/second-edition/ch11-00-testing.html): The basics of unit and integration testing in Rust
837-
* [Ed Page’s "Iterating on Testing in Rust"](https://epage.github.io/blog/2023/06/iterating-on-test/): Lists potential issues with `cargo` `test` and introduces `cargo-nextest`
838+
* ["The Rust Programming Language," chapter 11: Testing](https://web.mit.edu/rust-lang_v1.25/arch/amd64_ubuntu1404/share/doc/rust/html/book/second-edition/ch11-00-testing.html): The basics of unit and integration testing in Rust
839+
* [Ed Page’s "Iterating on Testing in Rust"](https://epage.github.io/blog/2023/06/iterating-on-test/): Lists potential issues with `cargo` `test` and introduces `cargo-nextest`
838840
* [Unsafe Rust and Miri by Ralf Jung \- Rust Zürisee June 2023](https://www.youtube.com/watch?v=svR0p6fSUYY)

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

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ Clippy is the fundamental linter. Just use it.
1414

1515
[Clippy lints](https://rust-lang.github.io/rust-clippy/master/index.html) are categorized into groups and levels. Groups categorize lints by the types of issues they detect. Levels indicate what to do when a lint finds an issue:
1616

17-
* Allow: Ignore the issue
18-
* Warn: Print a message to stderr
17+
* Allow: Ignore the issue
18+
* Warn: Print a message to stderr
1919
* Deny: Return an error code (useful in CI pipelines)
2020

2121
Clippy is a wrapper over the `rustc` compiler, so when Clippy is run, it executes [its own set of lints](https://doc.rust-lang.org/rustc/lints/listing/index.html) in addition to `rustc`’s. These lints are similarly categorized and can be controlled with the same flags and configuration options.
@@ -75,10 +75,10 @@ cargo clippy --message-format=json | clippy-sarif
7575

7676
For continuous use, such as in a CI/CD pipeline, you want to minimize false positives and focus on the important lints. For that, follow these guidelines:
7777

78-
* Use the default Clippy configuration.
79-
* Enable recommended `clippy::restriction` lints (see the example below).
80-
* Enable selected lints from `clippy::pedantic` groups (these should be enabled case by case depending on your project).
81-
* Turn warnings into errors so that CI/CD fails on any issue.
78+
* Use the default Clippy configuration.
79+
* Enable recommended `clippy::restriction` lints (see the example below).
80+
* Enable selected lints from `clippy::pedantic` groups (these should be enabled case by case depending on your project).
81+
* Turn warnings into errors so that CI/CD fails on any issue.
8282
* Add `#[allow(..)]` attributes in the code to silence specific findings of enabled lints when really needed. Make sure to comment why the lint was disabled in the specific location.
8383

8484
```sh
@@ -106,9 +106,9 @@ allow-unwrap-in-tests = true
106106

107107
[Dylint](https://github.com/trailofbits/dylint) runs lints from dynamic libraries named by the user, allowing developers to maintain their own personal lint collections. While you could write new Clippy lints and send a pull request, this is not always an ideal solution:
108108

109-
* The new lints cannot be project-specific.
110-
* The new lints cannot target third-party crates.
111-
* Complex lints may not be wanted due to maintenance effort.
109+
* The new lints cannot be project-specific.
110+
* The new lints cannot target third-party crates.
111+
* Complex lints may not be wanted due to maintenance effort.
112112
* Lints with a high false-positive rate may not be wanted.
113113

114114
Moreover, writing lints with Dylint instead of forking Clippy [helps with dealing with the unstable `rustc` API and with sharing lints with other people](https://blog.trailofbits.com/2021/11/09/write-rust-lints-without-forking-clippy/).
@@ -138,8 +138,8 @@ cargo dylint \
138138

139139
You can write your own lint, but it is a nontrivial task and is beyond the scope of this chapter. At a high level, you will need to decide on the type of lint:
140140

141-
* Pre-expansion: Run on the AST before macros are expanded
142-
* Early: Run on the AST after macros have been expanded
141+
* Pre-expansion: Run on the AST before macros are expanded
142+
* Early: Run on the AST after macros have been expanded
143143
* Late: Run on the high-level intermediate representation (HIR)—that is, after names have been resolved, types have been checked, etc.
144144

145145
Check out [Samuel Moelius’s ‘Linting with Dylint’ EuroRust 2024](https://youtu.be/MjlPUA7sAmA?t=548) talk for detailed guidelines.

content/docs/languages/rust/40-gotchas.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ This section provides a checklist that can be used during manual Rust code revie
1616
- Often partial-match (`starts_with`, `ends_with`, `contains`) is used instead of equality.
1717
- Case (in)sensitivity of comparisons often results in issues.
1818
- [ ] Check string conversions to other data types (like `Vec`) and vice versa. These may come with UTF-8 encoding issues. Options for handling bytes that may not be valid UTF-8:
19-
- `from_utf8` with `unwrap`—strict, panics on non-convertible data
20-
- `from_utf8_lossy`—lossy, replaces invalid UTF-8 sequences with U+FFFD (replacement character)
19+
- `from_utf8` with `unwrap`—strict, panics on non-convertible data
20+
- `from_utf8_lossy`—lossy, replaces invalid UTF-8 sequences with U+FFFD (replacement character)
2121
- `OsStr`/`OsString`—for platform-native strings that may not be valid UTF-8 (e.g., paths, environment variables, and `argv`); it is not a UTF-8/bytes converter, and there is no portable way to get its raw bytes (`as_bytes` is Unix-only; `as_encoded_bytes` uses a non-portable encoding)
2222
- [ ] Verify that the `with_capacity` method of the `Vec`, `HashMap`, `HashSet`, and `indexmap::IndexSet` types (and possibly other types) is not called with user-controlled data. Large values can lead to denial of service.
2323
- Also check that the provided capacity is smaller than the `isize::MAX` bytes [to prevent panics](https://doc.rust-lang.org/std/vec/struct.Vec.html#panics).
@@ -62,14 +62,14 @@ Common issues to check for in unsafe code are given below. For more information,
6262

6363
{{< checklist >}}
6464

65-
- [ ] Any union access is unsafe in Rust. Verify that the union field that matches the underlying data is used.
66-
- [ ] Look for uses of libc APIs like `memset` or `memcpy`. Most of them can be replaced with safe Rust counterparts.
65+
- [ ] Any union access is unsafe in Rust. Verify that the union field that matches the underlying data is used.
66+
- [ ] Look for uses of libc APIs like `memset` or `memcpy`. Most of them can be replaced with safe Rust counterparts.
6767
- [ ] If `#[repr(packed)]` is used on a struct, then check that `read_unaligned`/[`write_unaligned`](https://doc.rust-lang.org/std/ptr/fn.write_unaligned.html#on-packed-structs) is used for unaligned fields.
6868
- [ ] Check for uses of [`std::mem::uninitialized`](https://doc.rust-lang.org/std/mem/fn.uninitialized.html) and `mem::zeroed`.
6969
- [ ] Review uses of `MaybeUninit` to ensure that all calls to `assume_init` are preceded by initialization.
7070
- Ensure that dropping of partially initialized data is implemented correctly.
71-
- [ ] Check for uses of [`std::mem::forget`](https://doc.rust-lang.org/std/mem/fn.forget.html).
72-
- [ ] Check for uses of `transmute` or `cast` from a non-mutable reference `&` to a mutable `&mut` (likely an undefined behavior).
71+
- [ ] Check for uses of [`std::mem::forget`](https://doc.rust-lang.org/std/mem/fn.forget.html).
72+
- [ ] Check for uses of `transmute` or `cast` from a non-mutable reference `&` to a mutable `&mut` (likely undefined behavior).
7373
- Using [`bytemuck`](https://docs.rs/bytemuck/latest/bytemuck/) or [`zerocopy`](https://docs.rs/zerocopy/latest/zerocopy/) crates may be a safer alternative.
7474
- [ ] Review uses of `static mut` and [recommend using synchronization instead](https://github.com/rust-lang/rust/issues/53639).
7575
- [ ] Review uses of [unsafe attributes](https://github.com/rust-lang/rfcs/blob/master/text/3325-unsafe-attributes.md) like `#[unsafe(no_mangle)]`.

0 commit comments

Comments
 (0)