Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
5a19cad
init rust chapter
GrosQuildu May 7, 2026
653be24
coverage section done
GrosQuildu May 8, 2026
efe0e44
fix minor bugs
GrosQuildu May 8, 2026
296e4a7
fix bugs, rewrite zeroization
GrosQuildu May 13, 2026
6e56b70
fix lints
GrosQuildu May 13, 2026
4b2e485
fix more lints
GrosQuildu May 13, 2026
05bada1
restore :? for some linkgs
GrosQuildu May 13, 2026
00bc6ba
clarify zeroization
GrosQuildu May 19, 2026
863d16e
clarify gotchas
GrosQuildu May 19, 2026
ecd73f8
fix gotchas
GrosQuildu May 20, 2026
ea95c5e
more gotchas
GrosQuildu Jun 9, 2026
3e8ad64
skills
GrosQuildu Jun 23, 2026
06eaaac
fix skill ref
GrosQuildu Jun 29, 2026
692ef35
better code-as-art
GrosQuildu Jun 29, 2026
140dbeb
fix code-as-art
GrosQuildu Jun 29, 2026
effc8ce
fix small screen display for rust code
GrosQuildu Jun 29, 2026
abe0da6
fix links
GrosQuildu Jun 29, 2026
361b21c
fix markdown
GrosQuildu Jul 1, 2026
9284e83
Clarify cargo-vet safe-to-deploy wording
elopez Jul 2, 2026
e8b789a
Fix Rust gotchas typos
elopez Jul 2, 2026
33f5456
Fix Rust coverage Docker instructions
elopez Jul 2, 2026
764ead6
Fix Rust coverage analysis link
elopez Jul 2, 2026
d4a581e
fix index into <name>.md
GrosQuildu Jul 2, 2026
c975de3
fix sanitizer commands
GrosQuildu Jul 2, 2026
4bd3e91
cargo hack fix
GrosQuildu Jul 2, 2026
5b75be6
Fix zeroize-audit skill link to point to trailofbits/skills
elopez Jul 4, 2026
49f07fd
Fix obsolete strict_provenance feature gate in Clippy command
elopez Jul 4, 2026
ea2f029
Add missing proptest-derive dependency for Arbitrary example
elopez Jul 4, 2026
0c86953
Bump proptest to 1.11.0; verify both examples pass
elopez Jul 4, 2026
a4bfc79
Fix multi-source dependency detection: cargo vendor cannot report it
elopez Jul 4, 2026
67ffc15
Fix grcov branch coverage overstatement (no-op on stable toolchain)
elopez Jul 4, 2026
431ce57
Scope memory-safety guarantee to safe Rust
elopez Jul 4, 2026
50d6838
Fix OsStr mischaracterization in gotchas string-conversion list
elopez Jul 4, 2026
792d277
Clean up Rust chapter slugs: drop redundant lang-rust- prefix
elopez Jul 4, 2026
ba69125
Rust chapter: fix medium/nit review findings
elopez Jul 4, 2026
f81cd4e
Fix proptest combinator docs: prop_map/prop_filter/prop_recursive are…
elopez Jul 4, 2026
ca50696
Fix cargo vet output: add missing blank line after "Vetting Failed!"
elopez Jul 4, 2026
624158c
Fix must_use_candidate lint description (was backwards)
elopez Jul 4, 2026
cdc561c
Fix "get them all" clippy command: require nightly + explicit -W
elopez Jul 4, 2026
3e2177c
Document clippy-sarif installation (dylint-style)
elopez Jul 4, 2026
07c6c61
Clarify integer-overflow default is configurable via overflow-checks
elopez Jul 5, 2026
b33cc42
Polish Rust chapter: shortcodes, links, versions, wording, Dockerfile
elopez Jul 5, 2026
cce6cdf
Fix Miri commands to use nightly
elopez Jul 5, 2026
30276f7
Fix Rust overflow example
elopez Jul 5, 2026
8a9612a
Clarify Rust sanitizer examples
elopez Jul 5, 2026
5a196e4
Clarify Duration::new panic condition
elopez Jul 5, 2026
c4516d1
Fix supply-chain heading anchors
elopez Jul 5, 2026
31abb1d
Fix lychee shortcode glob
elopez Jul 5, 2026
2f39fbe
Fix Rust chapter links for non-root previews
elopez Jul 5, 2026
5f85098
Make Rust coverage reports rerunnable
elopez Jul 5, 2026
7f74a54
Use slugged Rust tab group IDs
elopez Jul 5, 2026
a32e409
Polish Rust chapter prose
elopez Jul 5, 2026
bb9cd50
rust: fix review findings (broken links, tech accuracy, wording)
elopez Jul 5, 2026
450cda4
Fix broken links
elopez Jul 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/markdown.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
restore-keys: cache-lychee-
- uses: lycheeverse/lychee-action@8646ba30535128ac92d33dfc9133794bfdd9b411 # v2.8.0
with:
args: -a 200..=204,403,429 --no-progress --cache --max-cache-age 1d --scheme http --scheme https './**/*.md' './layout/shortcodes/fuzzing/*.html'
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'
fail: true
# Lint Markdown files
# Uses: a custom configuration file
Expand All @@ -48,7 +48,7 @@ jobs:
persist-credentials: false
- uses: tbroadley/spellchecker-cli-action@8369e98753c0d2c3a3c76fb4519d9056d1d4b129 # v1
with:
# No need to use a dictionary file with the disabled spell plugin
# No need to use a dictionary file with the disabled spell plugin
# dictionaries: '.github/workflows/dictionary.txt'
files: "'content/**/*.md'"
quiet: true
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ Your browser will be automatically refreshed with changes whenever you save a fi
(built on top of [retext](https://github.com/retextjs/retext) and [remark](https://github.com/remarkjs/remark)).
Uses the [tbroadley/spellchecker-cli-action](https://github.com/tbroadley/spellchecker-cli-action) action.

- Familiarize yourself with the [Hugo Book theme](https://hugo-book-demo.netlify.app/)
- Familiarize yourself with the [Hugo Book theme](https://book.alxs.dev/)
as it has a couple of nice features (buttons, etc.)
- Reach out in [#testing-handbook](https://empirehacking.slack.com/archives/C06CSLSQAMB) Empire Hacking Slack if you have any questions.

Expand Down
44 changes: 44 additions & 0 deletions assets/_custom.scss
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,50 @@ p {
text-wrap: wrap;
}

#rust-banner-code {
container-type: inline-size;

.highlight {
padding: 0;
margin: 0;

pre {
background: transparent !important;
border: none;
padding-top: 0;
margin: 0;
white-space: pre;
text-wrap: nowrap;
overflow-x: auto;
font-size: 1.1rem;
line-height: 1.5;
}

pre code > span {
justify-content: center;
justify-content: safe center;
}

code:first-child::before {
display: none;
}
}

// Decorative only: hide entirely when the column can't fit the widest line.
@container (max-width: 42rem) {
.highlight {
display: none;
}
}
}

@media screen and (max-width: 44rem) {
#rust-banner-code .highlight { display: none; }
}
@media screen and (min-width: 56.0625rem) and (max-width: 76rem) {
#rust-banner-code .highlight { display: none; }
}

ul.no-bullet-point-list {
list-style-type: none;

Expand Down
2 changes: 1 addition & 1 deletion content/docs/languages/c-cpp/20-seccomp.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ weight: 20
- Consult the [kernel's syscall tables](https://github.com/torvalds/linux/blob/master/arch/x86/entry/syscalls/syscall_64.tbl).
- [ ] Syscalls enabling code execution in the kernel (e.g., `kexec_file_load`, `finit_module`) are prevented.
- A malicious kernel module can easily manipulate the seccomp sandbox.
- [ ] 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`.
- [ ] 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`.
- [ ] Old kernel versions are supported if needed:
- [ ] 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).
- [ ] 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).
Expand Down
2 changes: 2 additions & 0 deletions content/docs/languages/c-cpp/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@ bookCollapseSection: true

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.

Start your review with our [c-review](https://github.com/trailofbits/skills/tree/main/plugins/c-review) skill. It covers basic issues.

{{< section >}}
132 changes: 132 additions & 0 deletions content/docs/languages/rust/10-security-overview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
---
title: "Security overview"
slug: security-overview
weight: 10
---

# Rust security overview

## Safety and security

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.

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.

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.

{{< mermaid >}}
flowchart LR
subgraph Input[" "]
direction TB
A[Safe Rust]
C[Unsafe Rust]
end
A --> B[Is Sound]
C --> D{Sound?}
D -->|Yes| B
D -->|No| E[Not Sound]
B --> F[No Undefined Behavior]
E --> G[UB Possible]
F --> H[Vulnerabilities Possible]
G --> H

style Input fill:none,stroke:none
style A fill:#000,color:#fff,stroke:#000
style B fill:#000,color:#fff,stroke:#000
style C fill:#ad182b,color:#fff,stroke:#ad182b
style D fill:#ad182b,color:#fff,stroke:#ad182b
style E fill:#ad182b,color:#fff,stroke:#ad182b
style F fill:#000,color:#fff,stroke:#000
style G fill:#ad182b,color:#fff,stroke:#ad182b
style H fill:#ad182b,color:#fff,stroke:#ad182b
{{< /mermaid >}}

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.

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.

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:

* [General race conditions](https://doc.rust-lang.org/nomicon/races.html)
* Deadlocks (blocking bugs)
* Incorrect state synchronization (non-blocking bugs)
* Resource leaks
* Pointer exposures
* Arithmetic errors
* Nondeterminism
* Logic errors

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.

## Resource leaks

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.

Similarly, safe Rust is allowed to leak other resources like file descriptors, shared memory, database connections, and zombie threads.

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.

## Pointer exposure

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

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.

## Arithmetic errors

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

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

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

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.

| Example | Result | Description |
|-------------------------------------|-----------|----------------------------------------|
| 255u8.wrapping_add(1) | 0 | Silently wraps around to zero |
| 255u8.overflowing_add(1) | (0, true) | Wraps and returns overflow flag |
| 255u8.checked_add(1) | None | Returns Option, None on overflow |
| 255u8.saturating_add(1) | 255 | Clamps at max value |
| 255u8.unchecked_add(1) | UB | Unsafe, undefined behavior on overflow |
| x + 1, where x == u8::MAX (debug) | panic | Default behavior in debug builds |
| x + 1, where x == u8::MAX (release) | 0 | Silently wraps in release builds |

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.

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

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:

* A [checked conversion](https://doc.rust-lang.org/std/convert/trait.TryFrom.html) with overflows handled explicitly (e.g., with a panic)
* 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))

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.

## Nondeterminism

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.

There are two types of nondeterminism in Rust: introduced during compilation and during runtime.

The following are sources of compilation-time nondeterminism:

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

The following are sources of runtime nondeterminism:

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

## Logic errors

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.

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