Skip to content

Commit 3fe2091

Browse files
authored
Merge pull request #128 from trailofbits/rust-chapter-v2
Rust chapter (updated version)
2 parents 2ee3e7c + 450cda4 commit 3fe2091

79 files changed

Lines changed: 8570 additions & 7 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/markdown.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ jobs:
2525
restore-keys: cache-lychee-
2626
- uses: lycheeverse/lychee-action@8646ba30535128ac92d33dfc9133794bfdd9b411 # v2.8.0
2727
with:
28-
args: -a 200..=204,403,429 --no-progress --cache --max-cache-age 1d --scheme http --scheme https './**/*.md' './layout/shortcodes/fuzzing/*.html'
28+
args: -a 200..=204,403,429 --no-progress --cache --max-cache-age 1d --scheme http --scheme https --root-dir "${{ github.workspace }}/content" --remap "file://${{ github.workspace }}/content/languages/ file://${{ github.workspace }}/static/languages/" './**/*.md' './layouts/shortcodes/fuzzing/*.html'
2929
fail: true
3030
# Lint Markdown files
3131
# Uses: a custom configuration file
@@ -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

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ Your browser will be automatically refreshed with changes whenever you save a fi
126126
(built on top of [retext](https://github.com/retextjs/retext) and [remark](https://github.com/remarkjs/remark)).
127127
Uses the [tbroadley/spellchecker-cli-action](https://github.com/tbroadley/spellchecker-cli-action) action.
128128
129-
- Familiarize yourself with the [Hugo Book theme](https://hugo-book-demo.netlify.app/)
129+
- Familiarize yourself with the [Hugo Book theme](https://book.alxs.dev/)
130130
as it has a couple of nice features (buttons, etc.)
131131
- Reach out in [#testing-handbook](https://empirehacking.slack.com/archives/C06CSLSQAMB) Empire Hacking Slack if you have any questions.
132132

assets/_custom.scss

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,50 @@ p {
125125
text-wrap: wrap;
126126
}
127127

128+
#rust-banner-code {
129+
container-type: inline-size;
130+
131+
.highlight {
132+
padding: 0;
133+
margin: 0;
134+
135+
pre {
136+
background: transparent !important;
137+
border: none;
138+
padding-top: 0;
139+
margin: 0;
140+
white-space: pre;
141+
text-wrap: nowrap;
142+
overflow-x: auto;
143+
font-size: 1.1rem;
144+
line-height: 1.5;
145+
}
146+
147+
pre code > span {
148+
justify-content: center;
149+
justify-content: safe center;
150+
}
151+
152+
code:first-child::before {
153+
display: none;
154+
}
155+
}
156+
157+
// Decorative only: hide entirely when the column can't fit the widest line.
158+
@container (max-width: 42rem) {
159+
.highlight {
160+
display: none;
161+
}
162+
}
163+
}
164+
165+
@media screen and (max-width: 44rem) {
166+
#rust-banner-code .highlight { display: none; }
167+
}
168+
@media screen and (min-width: 56.0625rem) and (max-width: 76rem) {
169+
#rust-banner-code .highlight { display: none; }
170+
}
171+
128172
ul.no-bullet-point-list {
129173
list-style-type: none;
130174

content/docs/languages/c-cpp/20-seccomp.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ weight: 20
2222
- Consult the [kernel's syscall tables](https://github.com/torvalds/linux/blob/master/arch/x86/entry/syscalls/syscall_64.tbl).
2323
- [ ] Syscalls enabling code execution in the kernel (e.g., `kexec_file_load`, `finit_module`) are prevented.
2424
- A malicious kernel module can easily manipulate the seccomp sandbox.
25-
- [ ] If any of the following syscalls are blocked or traced, [then the `restart_syscall` syscall is also blocked or traced](https://git.causa-arcana.com/kotovalexarian-likes-github/moby--moby/commit/5abd881883883a132f96f8adb1b07b5545af452b?style=unified&whitespace=show-all&show-outdated): `poll`, `nanosleep`, `clock_nanosleep`, or `futex`.
25+
- [ ] If any of the following syscalls are blocked or traced, [then the `restart_syscall` syscall is also blocked or traced](https://github.com/moby/moby/commit/5abd881883883a132f96f8adb1b07b5545af452b): `poll`, `nanosleep`, `clock_nanosleep`, or `futex`.
2626
- [ ] Old kernel versions are supported if needed:
2727
- [ ] For Linux kernel versions prior to 5.4, the BPF filter [checks for `compat` syscalls confusion](https://man7.org/linux/man-pages/man2/seccomp.2.html#:~:text=Additionally%2C%20kernels%20prior%20to%20Linux%205.4%20incorrectly%20permitted%20nr) (i.e., calling 64-bit ABI syscalls with the `__X32_SYSCALL_BIT` bit).
2828
- [ ] For Linux kernel versions prior to 4.8, the BPF filter [disables the use of `ptrace` for all sandboxed processes](https://www.exploit-db.com/exploits/46434).

content/docs/languages/c-cpp/_index.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,6 @@ bookCollapseSection: true
99

1010
C and C++ are two of the most-used languages for applications and system programming. This security checklist, written for security auditors and secure development practitioners, provides a wide range of security issues to look for when reviewing C and C++ code. It covers both language-specific bug classes and environment-specific security issues spanning the Linux and Windows operating systems, including usermode applications and kernelmode drivers. While no checklist can be exhaustive, we hope that this document serves as a strong starting point for the most common and impactful security issues that may be found in C and C++ code.
1111

12+
Start your review with our [c-review](https://github.com/trailofbits/skills/tree/main/plugins/c-review) skill. It covers basic issues.
13+
1214
{{< section >}}
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
---
2+
title: "Security overview"
3+
slug: security-overview
4+
weight: 10
5+
---
6+
7+
# Rust security overview
8+
9+
## Safety and security
10+
11+
The Rust compiler guarantees the memory safety of *safe* Rust: absent `unsafe` code, no undefined behavior or data race will happen during runtime, no matter the inputs. This guarantee does not extend to `unsafe` code—including `unsafe` code hidden in dependencies—which can cause undefined behavior if it is unsound.
12+
13+
Therefore, when security-testing Rust programs, it’s important to understand what is and what is not considered undefined behavior (UB). There is no sense in looking for double-free bugs in (safe) Rust, right? For the guarantees made by the Rust compiler, see the ["Behavior considered undefined"](https://doc.rust-lang.org/reference/behavior-considered-undefined.html) Rust Reference page.
14+
15+
Another important Rust concept is [*safety*](https://doc.rust-lang.org/nomicon/safe-unsafe-meaning.html). Code is marked `unsafe` when it requires special scrutiny: it may produce undefined behavior if written poorly, and it is the developer’s responsibility (not the compiler’s) to ensure the code upholds some specific contract.
16+
17+
{{< mermaid >}}
18+
flowchart LR
19+
subgraph Input[" "]
20+
direction TB
21+
A[Safe Rust]
22+
C[Unsafe Rust]
23+
end
24+
A --> B[Is Sound]
25+
C --> D{Sound?}
26+
D -->|Yes| B
27+
D -->|No| E[Not Sound]
28+
B --> F[No Undefined Behavior]
29+
E --> G[UB Possible]
30+
F --> H[Vulnerabilities Possible]
31+
G --> H
32+
33+
style Input fill:none,stroke:none
34+
style A fill:#000,color:#fff,stroke:#000
35+
style B fill:#000,color:#fff,stroke:#000
36+
style C fill:#ad182b,color:#fff,stroke:#ad182b
37+
style D fill:#ad182b,color:#fff,stroke:#ad182b
38+
style E fill:#ad182b,color:#fff,stroke:#ad182b
39+
style F fill:#000,color:#fff,stroke:#000
40+
style G fill:#ad182b,color:#fff,stroke:#ad182b
41+
style H fill:#ad182b,color:#fff,stroke:#ad182b
42+
{{< /mermaid >}}
43+
44+
Security testing would need to ensure that any `unsafe` code is [*sound*](https://docs.rs/dtolnay/0.0.7/dtolnay/macro._03__soundness_bugs.html#soundness). In a basic audit, one would check a weaker property: that the actually implemented uses of `unsafe` code do not produce undefined behavior. But advanced testing would ensure soundness: no possible safe caller can use the `unsafe` code to produce UB. In fact, unsound code is quite a common source of vulnerabilities: code that worked correctly for a long time until a specific input triggered the bug.
45+
46+
Note that detecting unsafe code in Rust is easy, which greatly reduces the security testing effort. On the other hand, some unsafe code may be “hidden” in (transitive) dependencies, which is worth keeping in mind during audits.
47+
48+
There’s more. Some safe (defined) behavior may result in vulnerabilities. The ["Behavior not considered unsafe"](https://doc.rust-lang.org/reference/behavior-not-considered-unsafe.html) list points to notable safe behaviors that are a common source of security bugs:
49+
50+
* [General race conditions](https://doc.rust-lang.org/nomicon/races.html)
51+
* Deadlocks (blocking bugs)
52+
* Incorrect state synchronization (non-blocking bugs)
53+
* Resource leaks
54+
* Pointer exposures
55+
* Arithmetic errors
56+
* Nondeterminism
57+
* Logic errors
58+
59+
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.
60+
61+
## Resource leaks
62+
63+
Although Rust's memory safety guarantees make it difficult to accidentally create memory leaks, they don’t make it impossible (according to the [Rust documentation](https://doc.rust-lang.org/book/ch15-06-reference-cycles.html)). In the worst case, a memory leak could enable a denial-of-service attack—bad, but not terrible.
64+
65+
Similarly, safe Rust is allowed to leak other resources like file descriptors, shared memory, database connections, and zombie threads.
66+
67+
Rust is also allowed to exit without calling destructors. This may be problematic when your program does an HTTP call, destroys a secret, or closes a database connection in a destructor, for example.
68+
69+
## Pointer exposure
70+
71+
Pointer exposure is a rare but interesting class of bug where [a pointer to process memory is leaked](https://codeandbitters.com/main-as-usize/). An attacker would use such data to defeat the operating system’s address space layout randomization (ASLR). This would help with low-level exploitation (of a memory corruption bug, if the attacker were able to find one).
72+
73+
Pointer exposure is considered safe, because it does not make your program exploitable or behave strangely. However, you should avoid such unnecessary data exposures just in case.
74+
75+
## Arithmetic errors
76+
77+
Dealing with numbers is safe in Rust, but some operations may produce unexpected results. There are three main sources of bugs:
78+
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)
81+
* [Rounding errors](https://github.com/crytic/roundme)
82+
83+
There are [three types of integer bugs](https://phrack.org/issues/60/10.html#article): arithmetic overflows, width overflows, and signedness errors.
84+
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.
86+
87+
| Example | Result | Description |
88+
|-------------------------------------|-----------|----------------------------------------|
89+
| 255u8.wrapping_add(1) | 0 | Silently wraps around to zero |
90+
| 255u8.overflowing_add(1) | (0, true) | Wraps and returns overflow flag |
91+
| 255u8.checked_add(1) | None | Returns Option, None on overflow |
92+
| 255u8.saturating_add(1) | 255 | Clamps at max value |
93+
| 255u8.unchecked_add(1) | UB | Unsafe, undefined behavior on overflow |
94+
| x + 1, where x == u8::MAX (debug) | panic | Default behavior in debug builds |
95+
| x + 1, where x == u8::MAX (release) | 0 | Silently wraps in release builds |
96+
97+
For runtime arithmetic, the default behavior is to wrap over in release builds and panic in debug builds. This is only the default, though—it is configurable via the [`overflow-checks`](https://doc.rust-lang.org/cargo/reference/profiles.html#overflow-checks) profile setting, so a release build can be made to panic on overflow (or a debug build to wrap). The most common assumption auditors make when reviewing Rust programs is that overflows should not happen and any integer overflow is a potential bug. If you want to make auditors' lives easier, then be explicit about arithmetic that is expected to wrap over or saturate.
98+
99+
You can read more about integer overflows in [RFC 560](https://github.com/rust-lang/rfcs/blob/ae1394021c001cae2bcdfe3d7f3098dc9e3fbd27/text/0560-integer-overflow.md) and the blog post ["Myths and Legends about Integer Overflow in Rust"](https://huonw.github.io/blog/2016/04/myths-and-legends-about-integer-overflow-in-rust/).
100+
101+
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:
102+
103+
* A [checked conversion](https://doc.rust-lang.org/std/convert/trait.TryFrom.html) with overflows handled explicitly (e.g., with a panic)
104+
* 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))
105+
106+
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.
107+
108+
## Nondeterminism
109+
110+
A Rust program that behaves differently when compiled or executed multiple times may be problematic for some kinds of systems—for example, when the program is expected to be interoperable between machines with different CPU architectures, or when data is computed and synchronized between machines as in the case of blockchain nodes.
111+
112+
There are two types of nondeterminism in Rust: introduced during compilation and during runtime.
113+
114+
The following are sources of compilation-time nondeterminism:
115+
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
120+
* Enum discriminant values
121+
122+
The following are sources of runtime nondeterminism:
123+
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
126+
* Pointers (specific memory addresses)
127+
128+
## Logic errors
129+
130+
Logic errors are a very wide topic covering areas like [traits’ logic constraints](https://doc.rust-lang.org/reference/behavior-not-considered-unsafe.html#logic-errors), weak authentication, broken cryptography, insufficient data validation, [infinite recursion](https://blog.trailofbits.com/2025/02/21/dont-recurse-on-untrusted-input/), [operating system–level TOCTOU bugs](https://blog.trailofbits.com/2020/08/12/sinter-new-user-mode-security-enforcement-for-macos/#:~:text=2.%20Mitigating%20the%20TOCTOU%20risks%20in%20real%2Dtime%20security%20decisions), error handling, unhandled [panics](https://blog.cloudflare.com/18-november-2025-outage/), and secrets exposure.
131+
132+
An interesting class of logic bugs in Rust is related to ["unwind safety"](https://doc.rust-lang.org/std/panic/trait.UnwindSafe.html#what-is-unwind-safety). A thread that panics when some data is in an invalid state may allow other threads (or the same if the `catch_unwind` mechanism is used) to observe the invalid state. This may break some logic invariants or be the cause of memory corruption (in the presence of unsafe code). If the whole program is not completely killed in the event of a panic, then reviewing for this type of safety is required.

0 commit comments

Comments
 (0)