From a5aabb57e57ead19266b52dc7357b741f78d883e Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 13 Jan 2026 00:36:00 +0100 Subject: [PATCH 01/28] Add 'Hardening Rust Code Against Runtime Failures' article --- content/blog/hardening-rust/index.md | 281 +++++++++++++++++++++++++ content/blog/hardening-rust/social.png | Bin 0 -> 57135 bytes 2 files changed, 281 insertions(+) create mode 100644 content/blog/hardening-rust/index.md create mode 100644 content/blog/hardening-rust/social.png diff --git a/content/blog/hardening-rust/index.md b/content/blog/hardening-rust/index.md new file mode 100644 index 00000000..41b48778 --- /dev/null +++ b/content/blog/hardening-rust/index.md @@ -0,0 +1,281 @@ ++++ +title = "Hardening Rust Code Against Runtime Failures" +date = 2026-01-13 +draft = false +template = "article.html" +[extra] +series = "Idiomatic Rust" ++++ + +We talked about Patterns for Defensive Programming in Rust before, in which implicit invariants that aren't enforced by the compiler lead to demise and misery. +But even being careful to prevent these mistakes is not enough to make your code truly robust. +What's missing is that even valid code can fail at runtime in ways that are hard to predict and control. +That's the topic of this article. + +This article is for you if you: +- need to harden your Rust code for production +- want to know how Rust code can fail in unexpected ways and how to recover from that +- want to make your code resilient at runtime + +## Panic Semantics Are Part of Your API + +Here's a question: what happens when a Rust program panics? + +There is no single correct answer because `panic!` is not a single behavior. + +For starters, there's a difference between unwind and abort. + +[`catch_unwind`](https://doc.rust-lang.org/std/panic/fn.catch_unwind.html) invokes a closure, capturing the cause of an unwinding panic if one occurs. + +```rust +let result = panic::catch_unwind(|| { + panic!("oh no!"); +}); +``` + +But the [Rustonomicon](https://doc.rust-lang.org/nomicon/unwinding.html) has the following to say about unwinding panics: + +> We would encourage you to only **do this sparingly**. In particular, Rust's current unwinding implementation is heavily optimized for the "doesn't unwind" case. If a program doesn't unwind, there should be no runtime cost for the program being ready to unwind. [...] Ideally, you should only panic for programming errors or extreme problems. + +The alternative to unwinding is aborting the entire process. That does what it says on the tin: the program immediately terminates without unwinding the stack or running destructors. +Crash and burn. +Weirdly enough, that's often the safer choice, especially when dealing with FFI boundaries or performance-critical code. + +To enable aborting on panic, add the following to your `Cargo.toml`: + +```toml +[profile.release] +panic = "abort" +``` + +And **even if** you did not explicitly configure this, catastrophic panics like stack overflows and out-of-memory errors **always abort the process**. That's because unwinding in these situations is unsafe and can lead to undefined behavior. + +- Panics that would unwind across an extern "C" boundary are defined to abort instead of unwinding, because [letting unwinding cross that boundary is undefined behavior](https://doc.rust-lang.org/nomicon/ffi.html#panic-can-be-stopped-at-an-abi-boundary). +- And if a `malloc` fails, [it aborts the process](https://news.ycombinator.com/item?id=11369457). If that's a problem, you need to proactively check for allocation sizes before allocating or avoid heap allocations altogether. + +These failures are fundamentally different from ordinary panics in that they cannot be caught or recovered from. +In order to handle them gracefully, you need to know how exactly your program will run and where and design accordingly. +For example, in the case of `malloc`, avoid unbounded user input that could lead to excessive allocations. + +Another difference is between thread-level failures and process-level crashes. + +A common misunderstanding is that `panic` terminates the entire program, but in a multi-threaded application, that is not necessarily the case. +For exmaple, a background worker thread can panic while the main thread continues running. +What sounds like a benefit can leave the system in a partially degraded state. + +This distinction becomes especially important in long-running systems (servers, workers, async runtimes,...). +A panic in a request-handling thread might only abort that one request, while the rest of the service remains available. +Whether this is acceptable depends on the invariants of the system. If a panic indicates a violated assumption confined to +a small scope, such as a request, letting the process continue may be reasonable, but if it indicates a global invariant violation, it can be outright dangerous to continue execution. + +The key insight is that panic behavior is **part of your system's failure model**. +Treating all panics as equivalent hides important distinctions and leads to fragile assumptions. +You should be explicit about whether a failure is allowed to take down a single task, a single thread, or the entire process. + +**Never panic in an uncontrolled manner.** + +## Stack Overflow As A Failure Mode + +Okay, you handle errors gracefully and you know how your system behaves on panic. +But did you account for stack overflows as well? + +Here's some simple recursive code that can quickly exhaust stack space: + +```rust +fn factorial(n: u64) -> u64 { + if n == 0 { + 1 + } else { + n * factorial(n - 1) + } +} +``` + +If you allow users to call this function with large inputs, it might crash your program. +Rust does not guarantee tail-call optimization, which is the compiler's ability to optimize certain recursive calls into loops that don't grow the stack, so deep recursion can lead to stack overflows, which is an unrecoverable crash. + +It requires some experience, but for recursive algorithms where you're not in control of the input size, it's often safer to use an iterative approach: + +```rust +fn factorial(n: u64) -> u64 { + let mut result = 1; + for i in 1..=n { + result *= i; + } + result +} +``` + +## Panic Hooks: Your Last Line of Defense + +When things go wrong, you want to know about it. +But by default, Rust panics just print to stderr and disappear into the void. +In production systems, that's not so great. + +What you need is structured logging, crash reporting, and/or centralized failure handling and that's where panic hooks come in. + +A panic hook is a function that gets called whenever a panic occurs, giving you a chance to handle it before the program terminates or unwinds. + +```rust +use std::panic; + +fn main() { + panic::set_hook(Box::new(|panic_info| { + eprintln!("Panic occurred: {}", panic_info); + // Log to your monitoring system + // Send crash reports + // Clean up resources + })); + + panic!("Something went wrong!"); +} +``` + +For example, here's a panic hook that sends structured JSON data to a crash reporting service: + +```rust +panic::set_hook(Box::new(|panic_info| { + let panic_data = serde_json::json!({ + "message": panic_info.to_string(), + "location": panic_info.location().map(|l| format!("{}:{}:{}", l.file(), l.line(), l.column())), + "timestamp": chrono::Utc::now().to_rfc3339(), + "version": env!("CARGO_PKG_VERSION"), + }); + + // Send to your crash reporting service + crash_reporter::report(panic_data); +})); +``` + +And here's [Sentry's panic hook handler](https://github.com/getsentry/sentry-rust/blob/625617015f2b64fabdf8264186911ca43873bb80/sentry-panic/src/lib.rs#L69-L77), which is even more sophisticated: + +```rust +fn setup(&self, _cfg: &mut ClientOptions) { + INIT.call_once(|| { + let next = panic::take_hook(); + panic::set_hook(Box::new(move |info| { + panic_handler(info); + next(info); + })); + }); +} +``` + +This: + +- Logs the panic information +- Preserves the previous panic hook behavior by calling `next(info)` +- Ensures the hook is only set once using `INIT.call_once`. + +But there's more to it than just logging. Panic hooks are your opportunity to prevent information leaks. +Remember that panic messages can contain sensitive data like file paths, internal state, or user information. +A well-designed panic hook sanitizes these messages before they reach logs or crash reports. + +```rust +panic::set_hook(Box::new(|panic_info| { + let sanitized_message = sanitize_panic_message(panic_info.to_string()); + log::error!("Application panic: {sanitized_message}"); +})); +``` + +Also, setting a hook is a great way to perform cleanup operations. +Before the process potentially terminates, you might want to flush logs, close network connections, or notify other systems that this instance is going down. + +But be careful these hooks run in an already-compromised environment, so avoid operations that could themselves panic. + +Also remember that panic hooks only run for unwinding panics. +If your program is configured to abort on panic, or if the panic is caused by a stack overflow or out-of-memory condition, your hook won't execute. + +My final rule is: **never rely on panic hooks for correctness.** +They're purely for observability and graceful degradation; don't try to recover from logic errors as it is very hard to know the program's state at this point. + +## Release And Debug Builds Are Two Different Programs + +One of the most dangerous assumptions in Rust development is that debug and release builds are functionally equivalent. +They're not. +In many ways, you're shipping a different program than the one you tested. + +The most obvious difference is integer overflow behavior. Debug builds panic on overflow, while release builds silently wrap around. +We covered that in [Pitfalls of Safe Rust](/blog/pitfalls-of-safe-rust/). + +But the differences run much deeper than arithmetic. +The optimizer makes assumptions about your code that can fundamentally change its behavior. + +For example, the optimizer can reorder operations in ways that break timing-sensitive code: + +```rust +fn rate_limited_operation() -> bool { + let start = std::time::Instant::now(); + + // Do some work + expensive_computation(); + + let elapsed = start.elapsed(); + if elapsed < std::time::Duration::from_millis(100) { + // Rate limiting: reject if too fast + return false; + } + + true +} +``` + +The optimizer might move the timing calculation or inline `expensive_computation()` in ways that fundamentally change the timing behavior, which could break your rate-limit logic. +One way around this is to use `black_box` from `std::hint` to prevent the optimizer from making assumptions about certain values: + +```rust +use std::hint::black_box; + +fn rate_limited_operation() -> bool { + let start = std::time::Instant::now(); + + // Do some work + black_box(expensive_computation()); + + let elapsed = start.elapsed(); + if elapsed < std::time::Duration::from_millis(100) { + // Rate limiting: reject if too fast + return false; + } + + true +} +``` + +It's telling the compiler: "Don't touch this; assume it could have side effects you don't know about." + +### Making Release Behavior Explicit + +The fact that tests pass in debug mode tells you almost nothing about production behavior. +**Run your tests against release builds**. + +```bash +# Add this to your CI pipeline +cargo test --release +``` + +Remember: if your code relies on behavior that only exists in debug builds, it's not actually tested. +The optimizer can and will eliminate code it deems unnecessary. + + +## Supply-Chain Security + +Your code is only as safe as your dependencies. +You should regularly audit your dependencies for known vulnerabilities. +Two helpful tools for that are, [`cargo-audit`](https://github.com/rustsec/rustsec/tree/main/cargo-audit) and [`cargo-deny`](https://embarkstudios.github.io/cargo-deny/). + +## Runtime Hardening Tooling + +Here are some useful tools to harden your Rust code against runtime failures: + +- [`miri`](https://github.com/rust-lang/miri) +- [`cargo-fuzz`](https://github.com/rust-fuzz/cargo-fuzz) +- [`hongg-fuzz`](https://github.com/google/honggfuzz) +- [`cargo-geiger`](https://github.com/geiger-rs/cargo-geiger) +- [`cargo-valgrind`](https://github.com/jfrimmel/cargo-valgrind) +- [`cargo-tarpaulin`](https://github.com/xd009642/tarpaulin) + +The tools above help catch undefined behavior, memory safety issues, code coverage gaps, and performance bottlenecks. +They are dynamic analysis tools that complement Rust's static guarantees. + + diff --git a/content/blog/hardening-rust/social.png b/content/blog/hardening-rust/social.png new file mode 100644 index 0000000000000000000000000000000000000000..6b8f93337f2f7b978a11955bb1937d3d69006d2f GIT binary patch literal 57135 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 i!fk15{odH Date: Tue, 13 Jan 2026 00:38:52 +0100 Subject: [PATCH 02/28] fix typos --- content/blog/hardening-rust/index.md | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/content/blog/hardening-rust/index.md b/content/blog/hardening-rust/index.md index 41b48778..24c05381 100644 --- a/content/blog/hardening-rust/index.md +++ b/content/blog/hardening-rust/index.md @@ -54,13 +54,13 @@ And **even if** you did not explicitly configure this, catastrophic panics like - And if a `malloc` fails, [it aborts the process](https://news.ycombinator.com/item?id=11369457). If that's a problem, you need to proactively check for allocation sizes before allocating or avoid heap allocations altogether. These failures are fundamentally different from ordinary panics in that they cannot be caught or recovered from. -In order to handle them gracefully, you need to know how exactly your program will run and where and design accordingly. +In order to handle them gracefully, you need to know how exactly your program will run and where, and design accordingly. For example, in the case of `malloc`, avoid unbounded user input that could lead to excessive allocations. Another difference is between thread-level failures and process-level crashes. A common misunderstanding is that `panic` terminates the entire program, but in a multi-threaded application, that is not necessarily the case. -For exmaple, a background worker thread can panic while the main thread continues running. +For example, a background worker thread can panic while the main thread continues running. What sounds like a benefit can leave the system in a partially degraded state. This distinction becomes especially important in long-running systems (servers, workers, async runtimes,...). @@ -74,7 +74,7 @@ You should be explicit about whether a failure is allowed to take down a single **Never panic in an uncontrolled manner.** -## Stack Overflow As A Failure Mode +## Stack Overflow as a Failure Mode Okay, you handle errors gracefully and you know how your system behaves on panic. But did you account for stack overflows as well? @@ -112,7 +112,7 @@ When things go wrong, you want to know about it. But by default, Rust panics just print to stderr and disappear into the void. In production systems, that's not so great. -What you need is structured logging, crash reporting, and/or centralized failure handling and that's where panic hooks come in. +What you need is structured logging, crash reporting, and/or centralized failure handling, and that's where panic hooks come in. A panic hook is a function that gets called whenever a panic occurs, giving you a chance to handle it before the program terminates or unwinds. @@ -165,7 +165,7 @@ This: - Logs the panic information - Preserves the previous panic hook behavior by calling `next(info)` -- Ensures the hook is only set once using `INIT.call_once`. +- Ensures the hook is only set once using `INIT.call_once` But there's more to it than just logging. Panic hooks are your opportunity to prevent information leaks. Remember that panic messages can contain sensitive data like file paths, internal state, or user information. @@ -181,7 +181,7 @@ panic::set_hook(Box::new(|panic_info| { Also, setting a hook is a great way to perform cleanup operations. Before the process potentially terminates, you might want to flush logs, close network connections, or notify other systems that this instance is going down. -But be careful these hooks run in an already-compromised environment, so avoid operations that could themselves panic. +But be careful—these hooks run in an already-compromised environment, so avoid operations that could themselves panic. Also remember that panic hooks only run for unwinding panics. If your program is configured to abort on panic, or if the panic is caused by a stack overflow or out-of-memory condition, your hook won't execute. @@ -189,7 +189,7 @@ If your program is configured to abort on panic, or if the panic is caused by a My final rule is: **never rely on panic hooks for correctness.** They're purely for observability and graceful degradation; don't try to recover from logic errors as it is very hard to know the program's state at this point. -## Release And Debug Builds Are Two Different Programs +## Release and Debug Builds Are Two Different Programs One of the most dangerous assumptions in Rust development is that debug and release builds are functionally equivalent. They're not. @@ -262,7 +262,7 @@ The optimizer can and will eliminate code it deems unnecessary. Your code is only as safe as your dependencies. You should regularly audit your dependencies for known vulnerabilities. -Two helpful tools for that are, [`cargo-audit`](https://github.com/rustsec/rustsec/tree/main/cargo-audit) and [`cargo-deny`](https://embarkstudios.github.io/cargo-deny/). +Two helpful tools for that are [`cargo-audit`](https://github.com/rustsec/rustsec/tree/main/cargo-audit) and [`cargo-deny`](https://embarkstudios.github.io/cargo-deny/). ## Runtime Hardening Tooling @@ -270,12 +270,10 @@ Here are some useful tools to harden your Rust code against runtime failures: - [`miri`](https://github.com/rust-lang/miri) - [`cargo-fuzz`](https://github.com/rust-fuzz/cargo-fuzz) -- [`hongg-fuzz`](https://github.com/google/honggfuzz) +- [`honggfuzz`](https://github.com/google/honggfuzz) - [`cargo-geiger`](https://github.com/geiger-rs/cargo-geiger) - [`cargo-valgrind`](https://github.com/jfrimmel/cargo-valgrind) - [`cargo-tarpaulin`](https://github.com/xd009642/tarpaulin) The tools above help catch undefined behavior, memory safety issues, code coverage gaps, and performance bottlenecks. -They are dynamic analysis tools that complement Rust's static guarantees. - - +They are dynamic analysis tools that complement Rust's static guarantees. \ No newline at end of file From cd90a1612a64799f17337e39c2ab26ef354969b0 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 13 Jan 2026 00:44:45 +0100 Subject: [PATCH 03/28] fix typos etc --- content/blog/hardening-rust/index.md | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/content/blog/hardening-rust/index.md b/content/blog/hardening-rust/index.md index 24c05381..861b1eaa 100644 --- a/content/blog/hardening-rust/index.md +++ b/content/blog/hardening-rust/index.md @@ -7,7 +7,7 @@ template = "article.html" series = "Idiomatic Rust" +++ -We talked about Patterns for Defensive Programming in Rust before, in which implicit invariants that aren't enforced by the compiler lead to demise and misery. +We talked about [patterns for defensive programming in Rust](/blog/defensive-programming) before, in which implicit invariants that aren't enforced by the compiler lead to demise and misery. But even being careful to prevent these mistakes is not enough to make your code truly robust. What's missing is that even valid code can fail at runtime in ways that are hard to predict and control. That's the topic of this article. @@ -23,6 +23,8 @@ Here's a question: what happens when a Rust program panics? There is no single correct answer because `panic!` is not a single behavior. +### Unwind vs. Abort + For starters, there's a difference between unwind and abort. [`catch_unwind`](https://doc.rust-lang.org/std/panic/fn.catch_unwind.html) invokes a closure, capturing the cause of an unwinding panic if one occurs. @@ -57,6 +59,8 @@ These failures are fundamentally different from ordinary panics in that they can In order to handle them gracefully, you need to know how exactly your program will run and where, and design accordingly. For example, in the case of `malloc`, avoid unbounded user input that could lead to excessive allocations. +### Thread-Level vs. Process-Level Failures + Another difference is between thread-level failures and process-level crashes. A common misunderstanding is that `panic` terminates the entire program, but in a multi-threaded application, that is not necessarily the case. @@ -76,6 +80,8 @@ You should be explicit about whether a failure is allowed to take down a single ## Stack Overflow as a Failure Mode +Panic behavior isn't the only runtime failure mode you need to worry about. + Okay, you handle errors gracefully and you know how your system behaves on panic. But did you account for stack overflows as well? @@ -108,6 +114,8 @@ fn factorial(n: u64) -> u64 { ## Panic Hooks: Your Last Line of Defense +Now that you understand how panics work, let's talk about observability. + When things go wrong, you want to know about it. But by default, Rust panics just print to stderr and disappear into the void. In production systems, that's not so great. @@ -147,6 +155,8 @@ panic::set_hook(Box::new(|panic_info| { })); ``` +### Preserving Existing Hooks + And here's [Sentry's panic hook handler](https://github.com/getsentry/sentry-rust/blob/625617015f2b64fabdf8264186911ca43873bb80/sentry-panic/src/lib.rs#L69-L77), which is even more sophisticated: ```rust @@ -167,6 +177,8 @@ This: - Preserves the previous panic hook behavior by calling `next(info)` - Ensures the hook is only set once using `INIT.call_once` +### Sanitizing Sensitive Data + But there's more to it than just logging. Panic hooks are your opportunity to prevent information leaks. Remember that panic messages can contain sensitive data like file paths, internal state, or user information. A well-designed panic hook sanitizes these messages before they reach logs or crash reports. @@ -178,11 +190,15 @@ panic::set_hook(Box::new(|panic_info| { })); ``` +### Cleanup Operations + Also, setting a hook is a great way to perform cleanup operations. Before the process potentially terminates, you might want to flush logs, close network connections, or notify other systems that this instance is going down. But be careful—these hooks run in an already-compromised environment, so avoid operations that could themselves panic. +### Limitations + Also remember that panic hooks only run for unwinding panics. If your program is configured to abort on panic, or if the panic is caused by a stack overflow or out-of-memory condition, your hook won't execute. @@ -257,15 +273,18 @@ cargo test --release Remember: if your code relies on behavior that only exists in debug builds, it's not actually tested. The optimizer can and will eliminate code it deems unnecessary. - ## Supply-Chain Security +Beyond runtime behavior differences, there's another vector for failures you can't ignore: your dependencies. + Your code is only as safe as your dependencies. You should regularly audit your dependencies for known vulnerabilities. Two helpful tools for that are [`cargo-audit`](https://github.com/rustsec/rustsec/tree/main/cargo-audit) and [`cargo-deny`](https://embarkstudios.github.io/cargo-deny/). ## Runtime Hardening Tooling +Finally, let's talk about the tools that help you catch problems before they hit production. + Here are some useful tools to harden your Rust code against runtime failures: - [`miri`](https://github.com/rust-lang/miri) From dd437a485e67d5e5a6b5c68a03692609ea56c55b Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 13 Jan 2026 00:45:01 +0100 Subject: [PATCH 04/28] fix heading --- content/blog/hardening-rust/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/blog/hardening-rust/index.md b/content/blog/hardening-rust/index.md index 861b1eaa..50de2e8b 100644 --- a/content/blog/hardening-rust/index.md +++ b/content/blog/hardening-rust/index.md @@ -112,7 +112,7 @@ fn factorial(n: u64) -> u64 { } ``` -## Panic Hooks: Your Last Line of Defense +## Panic Hooks Are Your Last Line of Defense Now that you understand how panics work, let's talk about observability. From a020ab513272b2fb24d650b999e4f4e9fda84d44 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 13 Jan 2026 01:02:01 +0100 Subject: [PATCH 05/28] wording, links --- content/blog/hardening-rust/index.md | 102 +++++++++++++-------------- 1 file changed, 49 insertions(+), 53 deletions(-) diff --git a/content/blog/hardening-rust/index.md b/content/blog/hardening-rust/index.md index 50de2e8b..29ab0ae5 100644 --- a/content/blog/hardening-rust/index.md +++ b/content/blog/hardening-rust/index.md @@ -8,26 +8,26 @@ series = "Idiomatic Rust" +++ We talked about [patterns for defensive programming in Rust](/blog/defensive-programming) before, in which implicit invariants that aren't enforced by the compiler lead to demise and misery. -But even being careful to prevent these mistakes is not enough to make your code truly robust. -What's missing is that even valid code can fail at runtime in ways that are hard to predict and control. -That's the topic of this article. +But being careful isn't enough. +Even valid code can fail at runtime in ways that are hard to predict and control. +That's what we're covering here. -This article is for you if you: -- need to harden your Rust code for production -- want to know how Rust code can fail in unexpected ways and how to recover from that -- want to make your code resilient at runtime +This article is for you if you want to +- make your code resilient at runtime +- harden your Rust code for production +- know how Rust code can fail in unexpected ways and how to recover from that ## Panic Semantics Are Part of Your API Here's a question: what happens when a Rust program panics? -There is no single correct answer because `panic!` is not a single behavior. +There is no single correct answer because `panic!` is not a "single behavior." ### Unwind vs. Abort For starters, there's a difference between unwind and abort. -[`catch_unwind`](https://doc.rust-lang.org/std/panic/fn.catch_unwind.html) invokes a closure, capturing the cause of an unwinding panic if one occurs. +[`catch_unwind`](https://doc.rust-lang.org/std/panic/fn.catch_unwind.html) invokes a closure, which captures the cause of an unwinding panic. ```rust let result = panic::catch_unwind(|| { @@ -56,7 +56,7 @@ And **even if** you did not explicitly configure this, catastrophic panics like - And if a `malloc` fails, [it aborts the process](https://news.ycombinator.com/item?id=11369457). If that's a problem, you need to proactively check for allocation sizes before allocating or avoid heap allocations altogether. These failures are fundamentally different from ordinary panics in that they cannot be caught or recovered from. -In order to handle them gracefully, you need to know how exactly your program will run and where, and design accordingly. +To handle them gracefully, you need to know exactly how and where your program will run, and design accordingly. For example, in the case of `malloc`, avoid unbounded user input that could lead to excessive allocations. ### Thread-Level vs. Process-Level Failures @@ -69,8 +69,9 @@ What sounds like a benefit can leave the system in a partially degraded state. This distinction becomes especially important in long-running systems (servers, workers, async runtimes,...). A panic in a request-handling thread might only abort that one request, while the rest of the service remains available. -Whether this is acceptable depends on the invariants of the system. If a panic indicates a violated assumption confined to -a small scope, such as a request, letting the process continue may be reasonable, but if it indicates a global invariant violation, it can be outright dangerous to continue execution. +Whether this is acceptable depends on the system's invariants. +If a panic indicates a violated assumption confined to a small scope, like a single request, letting the process continue may be reasonable. +But if it signals a global invariant violation, continuing execution can be outright dangerous. The key insight is that panic behavior is **part of your system's failure model**. Treating all panics as equivalent hides important distinctions and leads to fragile assumptions. @@ -80,8 +81,6 @@ You should be explicit about whether a failure is allowed to take down a single ## Stack Overflow as a Failure Mode -Panic behavior isn't the only runtime failure mode you need to worry about. - Okay, you handle errors gracefully and you know how your system behaves on panic. But did you account for stack overflows as well? @@ -98,7 +97,7 @@ fn factorial(n: u64) -> u64 { ``` If you allow users to call this function with large inputs, it might crash your program. -Rust does not guarantee tail-call optimization, which is the compiler's ability to optimize certain recursive calls into loops that don't grow the stack, so deep recursion can lead to stack overflows, which is an unrecoverable crash. +Rust doesn't guarantee tail-call optimization—the compiler's ability to turn certain recursive calls into loops that don't grow the stack. This means deep recursion can lead to stack overflows, which cause unrecoverable crashes. It requires some experience, but for recursive algorithms where you're not in control of the input size, it's often safer to use an iterative approach: @@ -112,17 +111,23 @@ fn factorial(n: u64) -> u64 { } ``` +**Panic behavior isn't the only runtime failure mode you need to worry about.** + ## Panic Hooks Are Your Last Line of Defense Now that you understand how panics work, let's talk about observability. When things go wrong, you want to know about it. -But by default, Rust panics just print to stderr and disappear into the void. +But by default, Rust panics just print to `stderr` and disappear into the void. In production systems, that's not so great. -What you need is structured logging, crash reporting, and/or centralized failure handling, and that's where panic hooks come in. - +You might prefer crash reporting, and/or centralized failure handling, and that's where panic hooks come in. A panic hook is a function that gets called whenever a panic occurs, giving you a chance to handle it before the program terminates or unwinds. +It's your last line of defense to log, report, or clean up before the inevitable. + +### Example Panic Hooks + +Here's a simple example of setting a panic hook: ```rust use std::panic; @@ -139,7 +144,7 @@ fn main() { } ``` -For example, here's a panic hook that sends structured JSON data to a crash reporting service: +And here's a panic hook that sends structured JSON data to a crash reporting service: ```rust panic::set_hook(Box::new(|panic_info| { @@ -155,9 +160,7 @@ panic::set_hook(Box::new(|panic_info| { })); ``` -### Preserving Existing Hooks - -And here's [Sentry's panic hook handler](https://github.com/getsentry/sentry-rust/blob/625617015f2b64fabdf8264186911ca43873bb80/sentry-panic/src/lib.rs#L69-L77), which is even more sophisticated: +And finally, here's [Sentry's panic hook handler](https://github.com/getsentry/sentry-rust/blob/625617015f2b64fabdf8264186911ca43873bb80/sentry-panic/src/lib.rs#L69-L77), which is even more sophisticated: ```rust fn setup(&self, _cfg: &mut ClientOptions) { @@ -171,16 +174,17 @@ fn setup(&self, _cfg: &mut ClientOptions) { } ``` -This: - +Sentry's panic hook: - Logs the panic information - Preserves the previous panic hook behavior by calling `next(info)` - Ensures the hook is only set once using `INIT.call_once` +There's a lot to learn from these few lines of code! + ### Sanitizing Sensitive Data -But there's more to it than just logging. Panic hooks are your opportunity to prevent information leaks. -Remember that panic messages can contain sensitive data like file paths, internal state, or user information. +Panic hooks are also your final opportunity to prevent information leaks. +Remember that panic messages can contain sensitive data like file paths, internal state, or user information (PII). A well-designed panic hook sanitizes these messages before they reach logs or crash reports. ```rust @@ -192,18 +196,18 @@ panic::set_hook(Box::new(|panic_info| { ### Cleanup Operations -Also, setting a hook is a great way to perform cleanup operations. -Before the process potentially terminates, you might want to flush logs, close network connections, or notify other systems that this instance is going down. - -But be careful—these hooks run in an already-compromised environment, so avoid operations that could themselves panic. +Before the process terminates, you might want to flush logs, close network connections, or notify other systems that this instance is going down. +Setting a hook is a great way to perform such cleanup operations. +But be careful: these hooks run in an already-compromised environment, so avoid operations that could panic themselves. ### Limitations -Also remember that panic hooks only run for unwinding panics. -If your program is configured to abort on panic, or if the panic is caused by a stack overflow or out-of-memory condition, your hook won't execute. +Remember that panic hooks only run for unwinding panics. +If your program aborts on panic, or if the panic is caused by a stack overflow or out-of-memory condition, **your hook won't execute**. -My final rule is: **never rely on panic hooks for correctness.** -They're purely for observability and graceful degradation; don't try to recover from logic errors as it is very hard to know the program's state at this point. +Therefore **never rely on panic hooks for correctness.** + +They're purely for observability and graceful degradation; don't try to recover from logic errors as it is very hard to rely on a system's fragile underpinnings at this stage. ## Release and Debug Builds Are Two Different Programs @@ -214,9 +218,7 @@ In many ways, you're shipping a different program than the one you tested. The most obvious difference is integer overflow behavior. Debug builds panic on overflow, while release builds silently wrap around. We covered that in [Pitfalls of Safe Rust](/blog/pitfalls-of-safe-rust/). -But the differences run much deeper than arithmetic. -The optimizer makes assumptions about your code that can fundamentally change its behavior. - +But the differences run deeper than arithmetic. For example, the optimizer can reorder operations in ways that break timing-sensitive code: ```rust @@ -236,8 +238,8 @@ fn rate_limited_operation() -> bool { } ``` -The optimizer might move the timing calculation or inline `expensive_computation()` in ways that fundamentally change the timing behavior, which could break your rate-limit logic. -One way around this is to use `black_box` from `std::hint` to prevent the optimizer from making assumptions about certain values: +The optimizer might move the timing calculation or inline `expensive_computation()` in ways that change the timing behavior and break your rate-limit logic. +One way around this is `black_box` from `std::hint`, which prevents the optimizer from making assumptions about certain values: ```rust use std::hint::black_box; @@ -260,7 +262,7 @@ fn rate_limited_operation() -> bool { It's telling the compiler: "Don't touch this; assume it could have side effects you don't know about." -### Making Release Behavior Explicit +### Testing Release Behavior The fact that tests pass in debug mode tells you almost nothing about production behavior. **Run your tests against release builds**. @@ -270,29 +272,23 @@ The fact that tests pass in debug mode tells you almost nothing about production cargo test --release ``` -Remember: if your code relies on behavior that only exists in debug builds, it's not actually tested. -The optimizer can and will eliminate code it deems unnecessary. - ## Supply-Chain Security -Beyond runtime behavior differences, there's another vector for failures you can't ignore: your dependencies. - Your code is only as safe as your dependencies. You should regularly audit your dependencies for known vulnerabilities. Two helpful tools for that are [`cargo-audit`](https://github.com/rustsec/rustsec/tree/main/cargo-audit) and [`cargo-deny`](https://embarkstudios.github.io/cargo-deny/). +It's recommended to run those as part of CI. ## Runtime Hardening Tooling Finally, let's talk about the tools that help you catch problems before they hit production. -Here are some useful tools to harden your Rust code against runtime failures: - -- [`miri`](https://github.com/rust-lang/miri) -- [`cargo-fuzz`](https://github.com/rust-fuzz/cargo-fuzz) -- [`honggfuzz`](https://github.com/google/honggfuzz) -- [`cargo-geiger`](https://github.com/geiger-rs/cargo-geiger) -- [`cargo-valgrind`](https://github.com/jfrimmel/cargo-valgrind) -- [`cargo-tarpaulin`](https://github.com/xd009642/tarpaulin) +- [`miri`](https://github.com/rust-lang/miri) -- detects undefined behavior at runtime +- [`cargo-fuzz`](https://github.com/rust-fuzz/cargo-fuzz) -- fuzz testing for Rust code +- [`honggfuzz`](https://github.com/google/honggfuzz) -- another fuzzer with Rust support +- [`cargo-geiger`](https://github.com/geiger-rs/cargo-geiger) -- detects usage of unsafe code +- [`cargo-valgrind`](https://github.com/jfrimmel/cargo-valgrind) -- runs Valgrind on Rust code to find memory errors +- [`cargo-tarpaulin`](https://github.com/xd009642/tarpaulin) -- code coverage analysis for Rust projects The tools above help catch undefined behavior, memory safety issues, code coverage gaps, and performance bottlenecks. They are dynamic analysis tools that complement Rust's static guarantees. \ No newline at end of file From 4d72d3568d20802008d2620217c13b40521380e0 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 13 Jan 2026 01:10:57 +0100 Subject: [PATCH 06/28] add resources --- content/blog/hardening-rust/index.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/content/blog/hardening-rust/index.md b/content/blog/hardening-rust/index.md index 29ab0ae5..fb319b35 100644 --- a/content/blog/hardening-rust/index.md +++ b/content/blog/hardening-rust/index.md @@ -5,6 +5,10 @@ draft = false template = "article.html" [extra] series = "Idiomatic Rust" +resources = [ + "[Patterns for Defensive Programming in Rust](/blog/defensive-programming) -- making your Rust code more robust by enforcing invariants", + "[Pitfalls of Safe Rust](/blog/pitfalls-of-safe-rust) -- common mistakes even safe Rust programmers make", +] +++ We talked about [patterns for defensive programming in Rust](/blog/defensive-programming) before, in which implicit invariants that aren't enforced by the compiler lead to demise and misery. From d78e93b7320e12cf42e966047609c5579045d0c9 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 19 Feb 2026 13:44:48 +0100 Subject: [PATCH 07/28] Add section on limiting runtime attack surface in Rust apps --- content/blog/hardening-rust/index.md | 91 ++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/content/blog/hardening-rust/index.md b/content/blog/hardening-rust/index.md index fb319b35..836a80ca 100644 --- a/content/blog/hardening-rust/index.md +++ b/content/blog/hardening-rust/index.md @@ -283,6 +283,97 @@ You should regularly audit your dependencies for known vulnerabilities. Two helpful tools for that are [`cargo-audit`](https://github.com/rustsec/rustsec/tree/main/cargo-audit) and [`cargo-deny`](https://embarkstudios.github.io/cargo-deny/). It's recommended to run those as part of CI. +## Limit Your Runtime Attack Surface + +Even well-written Rust code can be compromised through its dependencies, environment, or C FFI boundaries. +The idea is to reduce your blast radius. +Now, how you do that depends on your deployment environment, but generally people use Docker and Linux, so I thought I'd share some techniques for those; specifically, how to build minimal container images and filesystem sandboxing. + +### FROM scratch Docker images + +A `FROM scratch` image contains exactly what you put in it. +By default there is no shell, no package manager, or other utilities an attacker could abuse for lateral movement. +Combined with a statically linked musl binary, the final image is just your executable plus a handful of config files. +So even if your image was compromised, the attacker would have very limited tools at their disposal to do further damage. + +The basic pattern is as follows: + +```dockerfile +# Build stage +# In this stage, we compile our Rust code into a statically linked binary (using musl) +FROM rust:alpine AS build + +RUN apk add --no-cache musl-dev lld + +WORKDIR /app +COPY . . +RUN cargo build --release + +# Final image +# In this stage, we start from scratch and only copy the compiled binary and necessary config files +FROM scratch + +# Needed for TLS and DNS resolution +COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ +COPY --from=build /etc/passwd /etc/passwd + +# Copy the binary +COPY --from=build /app/target/release/myapp /bin/myapp + +USER nobody +ENTRYPOINT ["/bin/myapp"] +``` + +The result is an image that's often under 10 MB with no attack surface beyond the binary itself. +A fair warning, though: alpine images sometimes can lead to strange runtime issues, especially in combination with Tokio. +If you decide to go this route, make sure to test your final image thoroughly. +Alternatively, you can use a Debian base and strip it down. + +### Filesystem sandboxing with Landlock + +Even inside a minimal container, your process *still* has access to any file the container mounts. +[Landlock](https://docs.kernel.org/userspace-api/landlock.html) is a Linux security module that lets a process restrict its own filesystem access. +If your service is ever exploited, the attacker can only reach the files you explicitly allowed. + +```rust +use landlock::{ + Access, AccessFs, PathBeneath, PathFd, Ruleset, RulesetAttr, + RulesetCreatedAttr, ABI, +}; + +fn sandbox() -> Result<(), Box> { + let abi = ABI::V3; + + Ruleset::default() + .handle_access(AccessFs::from_read(abi))? + .create()? + // Allow read-only access to /etc for config files + .add_rule(PathBeneath::new(PathFd::new("/etc")?, AccessFs::from_read(abi)))? + // Allow read+write access to /var/data for your app's data + .add_rule(PathBeneath::new( + PathFd::new("/var/data")?, + AccessFs::from_all(abi), + ))? + .restrict_self()?; + + Ok(()) +} + +fn main() { + sandbox().expect("failed to apply landlock sandbox"); + + // Your service starts here — now restricted to /etc (read) and /var/data (read/write) + // Any attempt to open /tmp, /home, /proc etc. will be denied +} +``` + +Call `sandbox()` as early as possible in `main`, before spawning threads or accepting connections. +The restrictions apply to the entire process from that point forward. + +The two approaches really go hand in hand: +- `FROM scratch` limits what's *in* the container +- Landlock limits what the process can *touch* at runtime. + ## Runtime Hardening Tooling Finally, let's talk about the tools that help you catch problems before they hit production. From 2f4ca4e836cc00334c98fb35667e7dace9a8936f Mon Sep 17 00:00:00 2001 From: Matthias Endler Date: Wed, 14 Jan 2026 16:50:04 +0100 Subject: [PATCH 08/28] Update content/blog/hardening-rust/index.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Simon Brüggen --- content/blog/hardening-rust/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/blog/hardening-rust/index.md b/content/blog/hardening-rust/index.md index 836a80ca..82af9d47 100644 --- a/content/blog/hardening-rust/index.md +++ b/content/blog/hardening-rust/index.md @@ -101,7 +101,7 @@ fn factorial(n: u64) -> u64 { ``` If you allow users to call this function with large inputs, it might crash your program. -Rust doesn't guarantee tail-call optimization—the compiler's ability to turn certain recursive calls into loops that don't grow the stack. This means deep recursion can lead to stack overflows, which cause unrecoverable crashes. +Rust doesn't guarantee tail-call optimization—the compiler rewriting certain recursive calls into loops which don't grow the stack. This means deep recursion can lead to stack overflows, which cause unrecoverable crashes. It requires some experience, but for recursive algorithms where you're not in control of the input size, it's often safer to use an iterative approach: From fe30c81491095fa3518dcbecee72c71dc6f68100 Mon Sep 17 00:00:00 2001 From: Matthias Endler Date: Wed, 14 Jan 2026 16:51:33 +0100 Subject: [PATCH 09/28] Update content/blog/hardening-rust/index.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Simon Brüggen --- content/blog/hardening-rust/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/blog/hardening-rust/index.md b/content/blog/hardening-rust/index.md index 82af9d47..bbd65858 100644 --- a/content/blog/hardening-rust/index.md +++ b/content/blog/hardening-rust/index.md @@ -222,7 +222,7 @@ In many ways, you're shipping a different program than the one you tested. The most obvious difference is integer overflow behavior. Debug builds panic on overflow, while release builds silently wrap around. We covered that in [Pitfalls of Safe Rust](/blog/pitfalls-of-safe-rust/). -But the differences run deeper than arithmetic. +But the differences run deeper than arithmetics. For example, the optimizer can reorder operations in ways that break timing-sensitive code: ```rust From d3f6b244de0bd97a2892719782ce816783942443 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 19 Feb 2026 14:37:01 +0100 Subject: [PATCH 10/28] Add section on mimalloc and update Docker hardening advice --- content/blog/hardening-rust/index.md | 81 ++++++++++++++++++++-------- 1 file changed, 59 insertions(+), 22 deletions(-) diff --git a/content/blog/hardening-rust/index.md b/content/blog/hardening-rust/index.md index bbd65858..8d2e3a10 100644 --- a/content/blog/hardening-rust/index.md +++ b/content/blog/hardening-rust/index.md @@ -1,6 +1,6 @@ +++ title = "Hardening Rust Code Against Runtime Failures" -date = 2026-01-13 +date = 2026-02-19 draft = false template = "article.html" [extra] @@ -283,51 +283,84 @@ You should regularly audit your dependencies for known vulnerabilities. Two helpful tools for that are [`cargo-audit`](https://github.com/rustsec/rustsec/tree/main/cargo-audit) and [`cargo-deny`](https://embarkstudios.github.io/cargo-deny/). It's recommended to run those as part of CI. +## Secure Allocations With mimalloc + +[mimalloc] is a drop-in global allocator built by Microsoft. +What's special about it is that it also has a **secure mode**, which "adds guard pages, randomized allocation, encrypted free lists, etc." to prevent heap-based vulnerabilities. +The performance penalty is usually around 10% according to mimalloc's own benchmarks, which is typically acceptable because Rust is never the bottleneck. [^mimalloc_safe] + +To enable secure mode, put in Cargo.toml: + +```toml +[dependencies] +mimalloc = { version = "*", features = ["secure"] } +``` + +Then use it as your global allocator: + +```rust +use mimalloc::MiMalloc; + +#[global_allocator] +static GLOBAL: MiMalloc = MiMalloc; +``` + +Now, all heap allocations in your Rust program will use mimalloc's secure allocator, which will automatically catch common heap vulnerabilities like buffer overflows and use-after-free bugs at runtime. +So even if your code has a memory safety issue, it will be much harder for an attacker to exploit it. + +[mimalloc]: https://github.com/microsoft/mimalloc +[^mimalloc_safe]: https://docs.rs/mimalloc-safe/latest/mimalloc_safe/ + ## Limit Your Runtime Attack Surface Even well-written Rust code can be compromised through its dependencies, environment, or C FFI boundaries. The idea is to reduce your blast radius. Now, how you do that depends on your deployment environment, but generally people use Docker and Linux, so I thought I'd share some techniques for those; specifically, how to build minimal container images and filesystem sandboxing. -### FROM scratch Docker images +### Minimal Docker images -A `FROM scratch` image contains exactly what you put in it. -By default there is no shell, no package manager, or other utilities an attacker could abuse for lateral movement. -Combined with a statically linked musl binary, the final image is just your executable plus a handful of config files. -So even if your image was compromised, the attacker would have very limited tools at their disposal to do further damage. +A minimal production image contains exactly what you put in it. +No shell, no package manager, no utilities an attacker could abuse for lateral movement. +Even if your service is compromised, the attacker has very limited tools at their disposal to do further damage. -The basic pattern is as follows: +My recommendation is [Google's distroless images](https://github.com/GoogleContainerTools/distroless). +They are minimal Debian-based images stripped of everything unnecessary, while still including libc, TLS certificates, timezone data, and a non-root user. +Everything a typical service needs and nothing more. ```dockerfile # Build stage -# In this stage, we compile our Rust code into a statically linked binary (using musl) -FROM rust:alpine AS build +# Compile our Rust code into a statically linked binary +FROM rust:slim-bookworm AS build -RUN apk add --no-cache musl-dev lld +RUN apt-get update && apt-get install -y musl-tools lld WORKDIR /app COPY . . RUN cargo build --release # Final image -# In this stage, we start from scratch and only copy the compiled binary and necessary config files -FROM scratch +FROM gcr.io/distroless/static-debian12 -# Needed for TLS and DNS resolution -COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ -COPY --from=build /etc/passwd /etc/passwd - -# Copy the binary COPY --from=build /app/target/release/myapp /bin/myapp -USER nobody +USER nonroot ENTRYPOINT ["/bin/myapp"] ``` -The result is an image that's often under 10 MB with no attack surface beyond the binary itself. -A fair warning, though: alpine images sometimes can lead to strange runtime issues, especially in combination with Tokio. -If you decide to go this route, make sure to test your final image thoroughly. -Alternatively, you can use a Debian base and strip it down. +Make sure to look up the latest `static-debian` variant [here](https://github.com/GoogleContainerTools/distroless). +It is essentially `FROM scratch` but with CA certificates and a `nonroot` user already included. +If you need libc (e.g. for SQLite or other C dependencies), use `gcr.io/distroless/cc-debian` instead. + +{% info(title="A Note On Alpine Base Images", icon="info") %} + +Alpine base images are a well-known alternative, but I found that they can cause subtle runtime issues with Tokio and async runtimes due to musl's thread-local storage implementation. +([1](https://www.reddit.com/r/rust/comments/sq53vx/alpine_fails_to_run_my_app_what_steps_should_i/hwjloqz/) +[2](https://martinheinz.dev/blog/92) +[3](https://github.com/astral-sh/uv/issues/2732)) + +Distroless sidesteps this entirely. + +{% end %} ### Filesystem sandboxing with Landlock @@ -374,6 +407,10 @@ The two approaches really go hand in hand: - `FROM scratch` limits what's *in* the container - Landlock limits what the process can *touch* at runtime. +The big picture is that security hardening is about reducing the surface of things that can go wrong. +Every capability your process holds unnecessarily is a liability. +So everything your code manages that could be delegated to the OS, init system, or container runtime should be. + ## Runtime Hardening Tooling Finally, let's talk about the tools that help you catch problems before they hit production. From 0ca71bbfe2ed6fefccccb968b3ae6d70fff40a6d Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 19 Feb 2026 14:44:10 +0100 Subject: [PATCH 11/28] Clarify Landlock example and add note on Meta's below vulnerability --- content/blog/hardening-rust/index.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/content/blog/hardening-rust/index.md b/content/blog/hardening-rust/index.md index 8d2e3a10..661917f2 100644 --- a/content/blog/hardening-rust/index.md +++ b/content/blog/hardening-rust/index.md @@ -353,7 +353,7 @@ If you need libc (e.g. for SQLite or other C dependencies), use `gcr.io/distrole {% info(title="A Note On Alpine Base Images", icon="info") %} -Alpine base images are a well-known alternative, but I found that they can cause subtle runtime issues with Tokio and async runtimes due to musl's thread-local storage implementation. +Alpine base images are a well-known alternative, but they can cause subtle runtime issues with Tokio and async runtimes due to musl's thread-local storage implementation. ([1](https://www.reddit.com/r/rust/comments/sq53vx/alpine_fails_to_run_my_app_what_steps_should_i/hwjloqz/) [2](https://martinheinz.dev/blog/92) [3](https://github.com/astral-sh/uv/issues/2732)) @@ -366,7 +366,9 @@ Distroless sidesteps this entirely. Even inside a minimal container, your process *still* has access to any file the container mounts. [Landlock](https://docs.kernel.org/userspace-api/landlock.html) is a Linux security module that lets a process restrict its own filesystem access. -If your service is ever exploited, the attacker can only reach the files you explicitly allowed. +If your service is ever exploited, the attacker can only reach the files you explicitly allowed. [^below] + +[^below]: This approach would have prevented a [vulnerability in Meta's `below` crate](https://security.opensuse.org/2025/03/12/below-world-writable-log-dir.html), a tool for recording and displaying system data like hardware utilization and cgroup information on Linux. ```rust use landlock::{ @@ -395,8 +397,9 @@ fn sandbox() -> Result<(), Box> { fn main() { sandbox().expect("failed to apply landlock sandbox"); - // Your service starts here — now restricted to /etc (read) and /var/data (read/write) - // Any attempt to open /tmp, /home, /proc etc. will be denied + // Your service starts here. + // The service is now restricted to /etc (read) and /var/data (read/write) + // Any attempt to open /tmp, /home, /proc etc. will be denied! } ``` @@ -408,12 +411,11 @@ The two approaches really go hand in hand: - Landlock limits what the process can *touch* at runtime. The big picture is that security hardening is about reducing the surface of things that can go wrong. -Every capability your process holds unnecessarily is a liability. -So everything your code manages that could be delegated to the OS, init system, or container runtime should be. +Every capability your process holds unnecessarily is a liability and everything your code manages that could be delegated to the OS, init system, or container runtime probably should be. ## Runtime Hardening Tooling -Finally, let's talk about the tools that help you catch problems before they hit production. +Finally, here are some tools that help you catch problems before they hit production. - [`miri`](https://github.com/rust-lang/miri) -- detects undefined behavior at runtime - [`cargo-fuzz`](https://github.com/rust-fuzz/cargo-fuzz) -- fuzz testing for Rust code From b7bf0a3bf76932cf3241052f0791aad86cc8fef5 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 19 Feb 2026 14:49:56 +0100 Subject: [PATCH 12/28] Add section on using Miri to detect undefined behavior --- content/blog/hardening-rust/index.md | 40 ++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/content/blog/hardening-rust/index.md b/content/blog/hardening-rust/index.md index 661917f2..1bd7f68e 100644 --- a/content/blog/hardening-rust/index.md +++ b/content/blog/hardening-rust/index.md @@ -413,11 +413,47 @@ The two approaches really go hand in hand: The big picture is that security hardening is about reducing the surface of things that can go wrong. Every capability your process holds unnecessarily is a liability and everything your code manages that could be delegated to the OS, init system, or container runtime probably should be. +## Miri: Detecting Undefined Behavior at Runtime + +[`miri`](https://github.com/rust-lang/miri) is an interpreter for Rust's mid-level intermediate representation (MIR) that can detect undefined behavior at runtime. + +It works by executing your Rust code in a special environment that tracks memory accesses, pointer validity, and other low-level details to catch issues that the compiler can't statically guarantee against. + +More people should know about Miri, because it is really helpful for tricky to detect race conditions in multi-threaded or async code; but it can do way more than that, of course. +It detected a lot of [real-world bugs](https://github.com/rust-lang/miri?tab=readme-ov-file#bugs-found-by-miri) already, even in the standard library. + +Using it is as simple as running: + +```bash +rustup +nightly component add miri +cargo +nightly miri test +``` + +This will run your tests under Miri's interpreter. + +The docs also describe [how to add miri to CI](https://github.com/rust-lang/miri?tab=readme-ov-file#running-miri-on-ci): + +```yaml + miri: + name: "Miri" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install Miri + run: | + rustup toolchain install nightly --component miri + rustup override set nightly + cargo miri setup + - name: Test with Miri + run: cargo miri test +``` + +(Make sure to check the latest instructions in the Miri repo, as the setup process may change over time.) + ## Runtime Hardening Tooling -Finally, here are some tools that help you catch problems before they hit production. +Finally, here are some more tools that help you catch problems before they hit production: -- [`miri`](https://github.com/rust-lang/miri) -- detects undefined behavior at runtime - [`cargo-fuzz`](https://github.com/rust-fuzz/cargo-fuzz) -- fuzz testing for Rust code - [`honggfuzz`](https://github.com/google/honggfuzz) -- another fuzzer with Rust support - [`cargo-geiger`](https://github.com/geiger-rs/cargo-geiger) -- detects usage of unsafe code From cde605c97690199dbdefec76ad678d3816b10a89 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 19 Feb 2026 14:56:53 +0100 Subject: [PATCH 13/28] Add reference to 2026 Miri research paper --- content/blog/hardening-rust/index.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/content/blog/hardening-rust/index.md b/content/blog/hardening-rust/index.md index 1bd7f68e..031b1b9f 100644 --- a/content/blog/hardening-rust/index.md +++ b/content/blog/hardening-rust/index.md @@ -450,6 +450,8 @@ The docs also describe [how to add miri to CI](https://github.com/rust-lang/miri (Make sure to check the latest instructions in the Miri repo, as the setup process may change over time.) +If you'd like to learn more about Miri, there is a research paper from 2026 that goes into the design and implementation details: [Miri: Practical Undefined Behavior Detection for Rust](https://research.ralfj.de/papers/2026-popl-miri.pdf). + ## Runtime Hardening Tooling Finally, here are some more tools that help you catch problems before they hit production: From 3981f14204513cca1c693d092b41a1a61499c436 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 19 Feb 2026 15:21:01 +0100 Subject: [PATCH 14/28] Add note about tail-call optimization in Scheme and Haskell --- content/blog/hardening-rust/index.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/content/blog/hardening-rust/index.md b/content/blog/hardening-rust/index.md index 031b1b9f..04cac48e 100644 --- a/content/blog/hardening-rust/index.md +++ b/content/blog/hardening-rust/index.md @@ -115,6 +115,8 @@ fn factorial(n: u64) -> u64 { } ``` +This is exactly the transformation that compilers for languages like Scheme or Haskell perform automatically through tail-call optimization. + **Panic behavior isn't the only runtime failure mode you need to worry about.** ## Panic Hooks Are Your Last Line of Defense From a003bf1f90d8c52101eecf0e87b0a93d36c47167 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 19 Feb 2026 15:31:57 +0100 Subject: [PATCH 15/28] Expand advice on sanitizing sensitive data in panic messages --- content/blog/hardening-rust/index.md | 29 +++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/content/blog/hardening-rust/index.md b/content/blog/hardening-rust/index.md index 04cac48e..4b43a005 100644 --- a/content/blog/hardening-rust/index.md +++ b/content/blog/hardening-rust/index.md @@ -190,7 +190,7 @@ There's a lot to learn from these few lines of code! ### Sanitizing Sensitive Data Panic hooks are also your final opportunity to prevent information leaks. -Remember that panic messages can contain sensitive data like file paths, internal state, or user information (PII). +Remember that panic messages can contain sensitive data like file paths, internal state, or user information (PII) such as email addresses, IP addresses, credit card numbers, etc. A well-designed panic hook sanitizes these messages before they reach logs or crash reports. ```rust @@ -200,6 +200,33 @@ panic::set_hook(Box::new(|panic_info| { })); ``` +You can look into crates like [expunge](https://crates.io/crates/expunge) or [veil](https://github.com/primait/veil) to automatically redact sensitive information from structs: + +```rust +use veil::Redact; + +#[derive(Redact)] +pub struct Customer { + id: u64, + + #[redact(partial)] + first_name: String, + + #[redact(partial)] + last_name: String, + + #[redact] + email: Option, + + #[redact(fixed = 2)] + age: u32, + + #[redact(with = "[REDACTED]")] + address: String, +} +``` + + ### Cleanup Operations Before the process terminates, you might want to flush logs, close network connections, or notify other systems that this instance is going down. From e4f84c56b986135dccfb105315ab87308dfc9b22 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 19 Feb 2026 15:33:31 +0100 Subject: [PATCH 16/28] Expand cargo-tarpaulin description to mention untested code paths --- content/blog/hardening-rust/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/blog/hardening-rust/index.md b/content/blog/hardening-rust/index.md index 4b43a005..dcfbd0e3 100644 --- a/content/blog/hardening-rust/index.md +++ b/content/blog/hardening-rust/index.md @@ -489,7 +489,7 @@ Finally, here are some more tools that help you catch problems before they hit p - [`honggfuzz`](https://github.com/google/honggfuzz) -- another fuzzer with Rust support - [`cargo-geiger`](https://github.com/geiger-rs/cargo-geiger) -- detects usage of unsafe code - [`cargo-valgrind`](https://github.com/jfrimmel/cargo-valgrind) -- runs Valgrind on Rust code to find memory errors -- [`cargo-tarpaulin`](https://github.com/xd009642/tarpaulin) -- code coverage analysis for Rust projects +- [`cargo-tarpaulin`](https://github.com/xd009642/tarpaulin) -- code coverage analysis for Rust projects to identify untested code paths, which can help you find edge cases that might lead to runtime failures The tools above help catch undefined behavior, memory safety issues, code coverage gaps, and performance bottlenecks. They are dynamic analysis tools that complement Rust's static guarantees. \ No newline at end of file From 2b6f01815b762d6066127c0438cf4964fc7c91b6 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 19 Feb 2026 15:42:33 +0100 Subject: [PATCH 17/28] Add sections on graceful shutdown, circuit breakers, resource limits, and health checks --- content/blog/hardening-rust/index.md | 264 ++++++++++++++++++++++++++- 1 file changed, 262 insertions(+), 2 deletions(-) diff --git a/content/blog/hardening-rust/index.md b/content/blog/hardening-rust/index.md index dcfbd0e3..f4a81bf5 100644 --- a/content/blog/hardening-rust/index.md +++ b/content/blog/hardening-rust/index.md @@ -187,6 +187,12 @@ Sentry's panic hook: There's a lot to learn from these few lines of code! +{% info(title="What's Inside `PanicInfo`?" icon="info") %} + +The [`PanicInfo`](https://doc.rust-lang.org/core/panic/struct.PanicInfo.html) struct contains the panic message (via `.payload()`) and the source location where the panic occurred (via `.location()`). Be aware that both can leak sensitive information: file paths may reveal internal directory structure, and panic messages might contain interpolated user data. + +{% end %} + ### Sanitizing Sensitive Data Panic hooks are also your final opportunity to prevent information leaks. @@ -231,7 +237,12 @@ pub struct Customer { Before the process terminates, you might want to flush logs, close network connections, or notify other systems that this instance is going down. Setting a hook is a great way to perform such cleanup operations. -But be careful: these hooks run in an already-compromised environment, so avoid operations that could panic themselves. + +{% info(title="Panic Hooks Run in a Compromised Environment" icon="warning") %} + +Be careful: one of the subsystems you want to interact with might be the *cause* of the panic you're handling! For example, if your database connection pool panicked, trying to flush pending writes to that same pool will likely fail or hang. Keep cleanup operations minimal and avoid anything that could panic itself. + +{% end %} ### Limitations @@ -481,6 +492,255 @@ The docs also describe [how to add miri to CI](https://github.com/rust-lang/miri If you'd like to learn more about Miri, there is a research paper from 2026 that goes into the design and implementation details: [Miri: Practical Undefined Behavior Detection for Rust](https://research.ralfj.de/papers/2026-popl-miri.pdf). +## Graceful Shutdown Handling + +A hardened service doesn't just crash—it shuts down gracefully when asked. +This means finishing in-flight requests, flushing buffers, and releasing resources cleanly before exiting. + +The key is handling signals like `SIGTERM` (sent by Kubernetes, systemd, or `docker stop`) and `SIGINT` (Ctrl+C): + +```rust +use tokio::signal; +use tokio::sync::broadcast; + +async fn shutdown_signal() { + let ctrl_c = async { + signal::ctrl_c() + .await + .expect("failed to install Ctrl+C handler"); + }; + + #[cfg(unix)] + let terminate = async { + signal::unix::signal(signal::unix::SignalKind::terminate()) + .expect("failed to install signal handler") + .recv() + .await; + }; + + #[cfg(not(unix))] + let terminate = std::future::pending::<()>(); + + tokio::select! { + _ = ctrl_c => {}, + _ = terminate => {}, + } +} + +async fn run_server(mut shutdown_rx: broadcast::Receiver<()>) { + loop { + tokio::select! { + // Handle requests... + _ = accept_connection() => { /* ... */ } + // ...until shutdown signal received + _ = shutdown_rx.recv() => { + println!("Shutting down gracefully..."); + break; + } + } + } + // Finish in-flight work, flush buffers, close connections +} +``` + +The pattern is: listen for shutdown signals, stop accepting new work, drain existing work, then exit. +Many frameworks like Axum and Actix have built-in graceful shutdown support—use it! + +## Circuit Breakers for External Dependencies + +When an external service (database, API, cache) starts failing, you don't want to keep hammering it with requests. +A circuit breaker tracks failures and "trips" when a threshold is reached, failing fast for a cooldown period before trying again. + +```rust +use std::sync::atomic::{AtomicU32, AtomicU64, Ordering}; +use std::time::{Duration, Instant}; + +pub struct CircuitBreaker { + failure_count: AtomicU32, + failure_threshold: u32, + last_failure: AtomicU64, // Unix timestamp in millis + cooldown: Duration, +} + +impl CircuitBreaker { + pub fn new(failure_threshold: u32, cooldown: Duration) -> Self { + Self { + failure_count: AtomicU32::new(0), + failure_threshold, + last_failure: AtomicU64::new(0), + cooldown, + } + } + + pub fn is_open(&self) -> bool { + let failures = self.failure_count.load(Ordering::Relaxed); + if failures < self.failure_threshold { + return false; + } + + // Check if cooldown has passed + let last = self.last_failure.load(Ordering::Relaxed); + let now = Instant::now().elapsed().as_millis() as u64; + now - last < self.cooldown.as_millis() as u64 + } + + pub fn record_success(&self) { + self.failure_count.store(0, Ordering::Relaxed); + } + + pub fn record_failure(&self) { + self.failure_count.fetch_add(1, Ordering::Relaxed); + let now = Instant::now().elapsed().as_millis() as u64; + self.last_failure.store(now, Ordering::Relaxed); + } +} + +// Usage +async fn call_external_service(cb: &CircuitBreaker) -> Result { + if cb.is_open() { + return Err(Error::CircuitOpen); + } + + match do_request().await { + Ok(resp) => { + cb.record_success(); + Ok(resp) + } + Err(e) => { + cb.record_failure(); + Err(e) + } + } +} +``` + +For production use, consider crates like [`recloser`](https://crates.io/crates/recloser) or [`failsafe`](https://crates.io/crates/failsafe) which handle the state machine properly. + +## Resource Limits + +Unbounded resources are a common source of runtime failures. +Set explicit limits on everything: + +```rust +// Limit concurrent connections +let semaphore = Arc::new(Semaphore::new(100)); // max 100 concurrent + +async fn handle_connection(sem: Arc) { + let _permit = sem.acquire().await.expect("semaphore closed"); + // Connection is now counted against the limit + // Permit is released when dropped +} + +// Limit request body size (Axum example) +use axum::extract::DefaultBodyLimit; + +let app = Router::new() + .route("/upload", post(upload_handler)) + .layer(DefaultBodyLimit::max(1024 * 1024 * 10)); // 10 MB max + +// Limit queue depth +use tokio::sync::mpsc; + +let (tx, rx) = mpsc::channel::(1000); // bounded channel, max 1000 pending + +// Set timeouts on everything external +let client = reqwest::Client::builder() + .connect_timeout(Duration::from_secs(5)) + .timeout(Duration::from_secs(30)) + .build()?; +``` + +At the OS level, you can also set limits via `setrlimit` or container resource constraints: + +```rust +use rlimit::{setrlimit, Resource}; + +// Limit open file descriptors +setrlimit(Resource::NOFILE, 1024, 1024)?; + +// Limit memory (in bytes) +setrlimit(Resource::AS, 1_000_000_000, 1_000_000_000)?; // 1 GB +``` + +The key insight: **every unbounded resource is a potential DoS vector**. +Explicit limits turn catastrophic failures into graceful rejections. + +## Health Checks and Self-Healing + +Production services need to answer the question: "Are you healthy?" +Health checks let load balancers, orchestrators, and monitoring systems know when something is wrong. + +A typical setup has two endpoints: + +```rust +use axum::{routing::get, Router, Json}; +use serde::Serialize; + +#[derive(Serialize)] +struct HealthStatus { + status: &'static str, + database: bool, + cache: bool, + version: &'static str, +} + +// Liveness: "Is the process alive?" +// Should always return 200 if the server can respond at all +async fn liveness() -> &'static str { + "OK" +} + +// Readiness: "Can you handle traffic?" +// Check dependencies before saying yes +async fn readiness( + db: Extension, + cache: Extension, +) -> Json { + let db_ok = db.ping().await.is_ok(); + let cache_ok = cache.ping().await.is_ok(); + + Json(HealthStatus { + status: if db_ok && cache_ok { "healthy" } else { "degraded" }, + database: db_ok, + cache: cache_ok, + version: env!("CARGO_PKG_VERSION"), + }) +} + +let app = Router::new() + .route("/health/live", get(liveness)) + .route("/health/ready", get(readiness)); +``` + +For Kubernetes, these map to `livenessProbe` and `readinessProbe`: + +```yaml +livenessProbe: + httpGet: + path: /health/live + port: 8080 + initialDelaySeconds: 5 + periodSeconds: 10 + +readinessProbe: + httpGet: + path: /health/ready + port: 8080 + initialDelaySeconds: 5 + periodSeconds: 5 +``` + +The distinction matters: +- **Liveness failure** → Kubernetes restarts your pod (self-healing!) +- **Readiness failure** → Kubernetes stops sending traffic (graceful degradation) + +For self-healing beyond restarts, consider: +- Automatic reconnection to databases with exponential backoff +- Periodic cache warming +- Background tasks that repair inconsistent state + +The goal is a system that recovers from transient failures without human intervention. + ## Runtime Hardening Tooling Finally, here are some more tools that help you catch problems before they hit production: @@ -492,4 +752,4 @@ Finally, here are some more tools that help you catch problems before they hit p - [`cargo-tarpaulin`](https://github.com/xd009642/tarpaulin) -- code coverage analysis for Rust projects to identify untested code paths, which can help you find edge cases that might lead to runtime failures The tools above help catch undefined behavior, memory safety issues, code coverage gaps, and performance bottlenecks. -They are dynamic analysis tools that complement Rust's static guarantees. \ No newline at end of file +They are dynamic analysis tools that complement Rust's static guarantees. From 3ca62905ce954b60043a111e89dda688a1ed419b Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 19 Feb 2026 16:12:47 +0100 Subject: [PATCH 18/28] Rewrite graceful shutdown, circuit breaker, and health check sections --- content/blog/hardening-rust/index.md | 246 +++++++++++---------------- 1 file changed, 96 insertions(+), 150 deletions(-) diff --git a/content/blog/hardening-rust/index.md b/content/blog/hardening-rust/index.md index f4a81bf5..365e734c 100644 --- a/content/blog/hardening-rust/index.md +++ b/content/blog/hardening-rust/index.md @@ -494,193 +494,140 @@ If you'd like to learn more about Miri, there is a research paper from 2026 that ## Graceful Shutdown Handling -A hardened service doesn't just crash—it shuts down gracefully when asked. -This means finishing in-flight requests, flushing buffers, and releasing resources cleanly before exiting. +A hardened service doesn't just crash. +Instead, it shuts down gracefully when asked. -The key is handling signals like `SIGTERM` (sent by Kubernetes, systemd, or `docker stop`) and `SIGINT` (Ctrl+C): +Aim to finish in-flight requests, flush your buffers, and release resources cleanly before you exit. +The pattern is: listen for shutdown signals, stop accepting new work, drain existing work, then exit. -```rust -use tokio::signal; -use tokio::sync::broadcast; - -async fn shutdown_signal() { - let ctrl_c = async { - signal::ctrl_c() - .await - .expect("failed to install Ctrl+C handler"); - }; +Framework like Axum have [built-in support for graceful shutdown](https://github.com/tokio-rs/axum/blob/main/examples/tls-graceful-shutdown/src/main.rs). Use it! - #[cfg(unix)] - let terminate = async { - signal::unix::signal(signal::unix::SignalKind::terminate()) - .expect("failed to install signal handler") - .recv() - .await; - }; +The key is handling signals like `SIGTERM` (sent by Kubernetes, systemd, or `docker stop`) and `SIGINT` (Ctrl+C). +Here's a minimal example using [tokio-graceful-shutdown](https://crates.io/crates/tokio-graceful-shutdown), which is a crate that provides good signal handling without much boilerplate. +It introduces a concept of "subsystems" that can run concurrently and listen for shutdown requests. - #[cfg(not(unix))] - let terminate = std::future::pending::<()>(); +```rust +use tokio_graceful_shutdown::{SubsystemHandle, Toplevel}; - tokio::select! { - _ = ctrl_c => {}, - _ = terminate => {}, - } +async fn subsys1(subsys: &mut SubsystemHandle) -> Result<()> +{ + log::info!("Subsystem1 started."); + subsys.on_shutdown_requested().await; + log::info!("Subsystem1 stopped."); + Ok(()) } -async fn run_server(mut shutdown_rx: broadcast::Receiver<()>) { - loop { - tokio::select! { - // Handle requests... - _ = accept_connection() => { /* ... */ } - // ...until shutdown signal received - _ = shutdown_rx.recv() => { - println!("Shutting down gracefully..."); - break; - } - } - } - // Finish in-flight work, flush buffers, close connections +#[tokio::main] +async fn main() -> Result<()> { + Toplevel::new(async |s: &mut SubsystemHandle| { + s.start(SubsystemBuilder::new("Subsys1", subsys1)) + }) + .catch_signals() + .handle_shutdown_requests(Duration::from_millis(1000)) + .await + .map_err(Into::into) } ``` -The pattern is: listen for shutdown signals, stop accepting new work, drain existing work, then exit. -Many frameworks like Axum and Actix have built-in graceful shutdown support—use it! - ## Circuit Breakers for External Dependencies When an external service (database, API, cache) starts failing, you don't want to keep hammering it with requests. -A circuit breaker tracks failures and "trips" when a threshold is reached, failing fast for a cooldown period before trying again. - -```rust -use std::sync::atomic::{AtomicU32, AtomicU64, Ordering}; -use std::time::{Duration, Instant}; - -pub struct CircuitBreaker { - failure_count: AtomicU32, - failure_threshold: u32, - last_failure: AtomicU64, // Unix timestamp in millis - cooldown: Duration, -} +A circuit breaker tracks failures and "trips" when a threshold is reached. -impl CircuitBreaker { - pub fn new(failure_threshold: u32, cooldown: Duration) -> Self { - Self { - failure_count: AtomicU32::new(0), - failure_threshold, - last_failure: AtomicU64::new(0), - cooldown, - } - } +For production use, consider crates like [`recloser`](https://crates.io/crates/recloser) or [`failsafe`](https://crates.io/crates/failsafe). - pub fn is_open(&self) -> bool { - let failures = self.failure_count.load(Ordering::Relaxed); - if failures < self.failure_threshold { - return false; - } +## Resource Limits - // Check if cooldown has passed - let last = self.last_failure.load(Ordering::Relaxed); - let now = Instant::now().elapsed().as_millis() as u64; - now - last < self.cooldown.as_millis() as u64 - } +Unbounded resources are a common source of runtime failures. +Everybody who was oncall for a production service will tell you this. - pub fn record_success(&self) { - self.failure_count.store(0, Ordering::Relaxed); - } +Set explicit limits on everything. +SREs will thank you for it! - pub fn record_failure(&self) { - self.failure_count.fetch_add(1, Ordering::Relaxed); - let now = Instant::now().elapsed().as_millis() as u64; - self.last_failure.store(now, Ordering::Relaxed); - } -} +Apart from the fact that it makes your service more robust, it also makes it easier to immediately detect misconfigurations in code. -// Usage -async fn call_external_service(cb: &CircuitBreaker) -> Result { - if cb.is_open() { - return Err(Error::CircuitOpen); - } +Common things you should limit include: +- Upper bound on any user input (upload file size, parameter bounds, etc.) +- request body size +- timeouts on external calls +- concurrent connections to external services +- queue depth for background jobs +- number of threads +- DB connection pool size - match do_request().await { - Ok(resp) => { - cb.record_success(); - Ok(resp) - } - Err(e) => { - cb.record_failure(); - Err(e) - } - } -} -``` +Here are some examples on how to do these in practice: -For production use, consider crates like [`recloser`](https://crates.io/crates/recloser) or [`failsafe`](https://crates.io/crates/failsafe) which handle the state machine properly. +### Request body size limits -## Resource Limits - -Unbounded resources are a common source of runtime failures. -Set explicit limits on everything: +See [Axum's `DefaultBodyLimit`](https://docs.rs/axum/latest/axum/extract/struct.DefaultBodyLimit.html): ```rust -// Limit concurrent connections -let semaphore = Arc::new(Semaphore::new(100)); // max 100 concurrent - -async fn handle_connection(sem: Arc) { - let _permit = sem.acquire().await.expect("semaphore closed"); - // Connection is now counted against the limit - // Permit is released when dropped -} +let app = Router::new() + .route("/", post(|request: Request| async {})) + .layer(DefaultBodyLimit::max(1024)); +``` -// Limit request body size (Axum example) -use axum::extract::DefaultBodyLimit; +### Limit queue depth -let app = Router::new() - .route("/upload", post(upload_handler)) - .layer(DefaultBodyLimit::max(1024 * 1024 * 10)); // 10 MB max +Bound the number of items in every queue or channel in your system. -// Limit queue depth +```rust use tokio::sync::mpsc; - let (tx, rx) = mpsc::channel::(1000); // bounded channel, max 1000 pending +``` -// Set timeouts on everything external +### Set timeouts on everything external + +```rust let client = reqwest::Client::builder() .connect_timeout(Duration::from_secs(5)) .timeout(Duration::from_secs(30)) .build()?; ``` -At the OS level, you can also set limits via `setrlimit` or container resource constraints: - -```rust -use rlimit::{setrlimit, Resource}; - -// Limit open file descriptors -setrlimit(Resource::NOFILE, 1024, 1024)?; - -// Limit memory (in bytes) -setrlimit(Resource::AS, 1_000_000_000, 1_000_000_000)?; // 1 GB -``` - -The key insight: **every unbounded resource is a potential DoS vector**. -Explicit limits turn catastrophic failures into graceful rejections. +The key insight is that **every unbounded resource is a potential DoS vector**. +Explicit limits will turn those catastrophic failures into (annoying but harmless) graceful rejections. ## Health Checks and Self-Healing -Production services need to answer the question: "Are you healthy?" -Health checks let load balancers, orchestrators, and monitoring systems know when something is wrong. +Ideally, your system should be able to recover from transient failures without human intervention. +Health checks let load balancers, orchestrators, and monitoring systems know when something is wrong and act accordingly. + +A typical setup has two endpoints, a liveness probe and a readiness probe. +The liveness probe checks if the process is alive at all, while the readiness probe checks if the process is healthy enough to handle traffic. -A typical setup has two endpoints: +This could honestly be an entire article on its own, but here's a quick example using Axum to illustrate the concept: ```rust use axum::{routing::get, Router, Json}; use serde::Serialize; +/// Status can be "healthy", "degraded", or "unhealthy" +#[derive(Serialize)] +enum Status { + // Everything is good, all dependencies are healthy + Healthy, + // Some dependencies are degraded, + // but the service can still handle requests + Degraded, + // Critical dependencies are down + // Don't send any traffic + Unhealthy, +} + +/// This is our health status struct, +/// which we will return as JSON from +/// the readiness probe #[derive(Serialize)] struct HealthStatus { - status: &'static str, + // Overall health status of the service + status: Status + // Is the database connection healthy? database: bool, + // Is the cache connection healthy? cache: bool, + // What version of the service is running? + // (Useful for debugging and monitoring.) version: &'static str, } @@ -698,9 +645,15 @@ async fn readiness( ) -> Json { let db_ok = db.ping().await.is_ok(); let cache_ok = cache.ping().await.is_ok(); + + let status = match (db_ok, cache_ok) { + (true, true) => Status::Healthy, + (false, false) => Status::Unhealthy, + _ => Status::Degraded, + }; Json(HealthStatus { - status: if db_ok && cache_ok { "healthy" } else { "degraded" }, + status, database: db_ok, cache: cache_ok, version: env!("CARGO_PKG_VERSION"), @@ -712,7 +665,7 @@ let app = Router::new() .route("/health/ready", get(readiness)); ``` -For Kubernetes, these map to `livenessProbe` and `readinessProbe`: +What's neat about it is that this maps directly to Kubernetes' health check system: ```yaml livenessProbe: @@ -730,16 +683,9 @@ readinessProbe: periodSeconds: 5 ``` -The distinction matters: -- **Liveness failure** → Kubernetes restarts your pod (self-healing!) -- **Readiness failure** → Kubernetes stops sending traffic (graceful degradation) - -For self-healing beyond restarts, consider: -- Automatic reconnection to databases with exponential backoff -- Periodic cache warming -- Background tasks that repair inconsistent state - -The goal is a system that recovers from transient failures without human intervention. +The distinction matters because +- Kubernetes stops sending traffic (graceful degradation) if the readiness probe fails. It does not yet kill the pod. +- Kubernetes restarts your pod if the liveness probe fails (it's self-healing!) ## Runtime Hardening Tooling From 57a923de2af1e090b7234d47ba092186f5db84dd Mon Sep 17 00:00:00 2001 From: Matthias Endler Date: Sat, 21 Feb 2026 17:38:03 +0100 Subject: [PATCH 19/28] Update content/blog/hardening-rust/index.md Co-authored-by: Theodor-Alexandru Irimia <11174371+tirimia@users.noreply.github.com> --- content/blog/hardening-rust/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/blog/hardening-rust/index.md b/content/blog/hardening-rust/index.md index 365e734c..4ea8c6a9 100644 --- a/content/blog/hardening-rust/index.md +++ b/content/blog/hardening-rust/index.md @@ -11,7 +11,7 @@ resources = [ ] +++ -We talked about [patterns for defensive programming in Rust](/blog/defensive-programming) before, in which implicit invariants that aren't enforced by the compiler lead to demise and misery. +We talked about [patterns for defensive programming in Rust](/blog/defensive-programming) before, in which implicit invariants that aren't enforced by the compiler lead to utter misery. But being careful isn't enough. Even valid code can fail at runtime in ways that are hard to predict and control. That's what we're covering here. From d97366693614514f2db03eb454a4bdddabc11c21 Mon Sep 17 00:00:00 2001 From: Matthias Endler Date: Sat, 21 Feb 2026 17:38:36 +0100 Subject: [PATCH 20/28] Update content/blog/hardening-rust/index.md Co-authored-by: Theodor-Alexandru Irimia <11174371+tirimia@users.noreply.github.com> --- content/blog/hardening-rust/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/blog/hardening-rust/index.md b/content/blog/hardening-rust/index.md index 4ea8c6a9..25a501bf 100644 --- a/content/blog/hardening-rust/index.md +++ b/content/blog/hardening-rust/index.md @@ -115,7 +115,7 @@ fn factorial(n: u64) -> u64 { } ``` -This is exactly the transformation that compilers for languages like Scheme or Haskell perform automatically through tail-call optimization. +This is exactly the transformation that compilers for languages like Haskell perform automatically through tail-call optimization. **Panic behavior isn't the only runtime failure mode you need to worry about.** From 40d93bdd8417976c2eeab87b78bd59039b9f124e Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 21 Jul 2026 00:08:36 +0200 Subject: [PATCH 21/28] integrate feedback --- content/blog/hardening-rust/index.md | 144 +++++++++++++-------------- 1 file changed, 72 insertions(+), 72 deletions(-) diff --git a/content/blog/hardening-rust/index.md b/content/blog/hardening-rust/index.md index 25a501bf..2714de0d 100644 --- a/content/blog/hardening-rust/index.md +++ b/content/blog/hardening-rust/index.md @@ -101,7 +101,7 @@ fn factorial(n: u64) -> u64 { ``` If you allow users to call this function with large inputs, it might crash your program. -Rust doesn't guarantee tail-call optimization—the compiler rewriting certain recursive calls into loops which don't grow the stack. This means deep recursion can lead to stack overflows, which cause unrecoverable crashes. +Rust does not guarantee tail-call optimization on stable Rust. Some compilers and languages can turn certain tail-recursive functions into loops, but you should not rely on that transformation in Rust. If recursion depth depends on user input or external data, rewrite the algorithm iteratively or put an explicit bound on the depth. It requires some experience, but for recursive algorithms where you're not in control of the input size, it's often safer to use an iterative approach: @@ -115,21 +115,19 @@ fn factorial(n: u64) -> u64 { } ``` -This is exactly the transformation that compilers for languages like Haskell perform automatically through tail-call optimization. - **Panic behavior isn't the only runtime failure mode you need to worry about.** -## Panic Hooks Are Your Last Line of Defense +## Panic Hooks Make Failures Observable -Now that you understand how panics work, let's talk about observability. +Now that you understand how panics work, let's talk about operational hardening. When things go wrong, you want to know about it. But by default, Rust panics just print to `stderr` and disappear into the void. In production systems, that's not so great. You might prefer crash reporting, and/or centralized failure handling, and that's where panic hooks come in. -A panic hook is a function that gets called whenever a panic occurs, giving you a chance to handle it before the program terminates or unwinds. -It's your last line of defense to log, report, or clean up before the inevitable. +A panic hook is a function that gets called whenever a panic occurs, giving you a chance to record the failure before the program terminates or unwinds. +It will not make an invalid state safe again. Its job is to capture enough context to debug the failure, alert someone, and shut down cleanly when possible. ### Example Panic Hooks @@ -196,8 +194,9 @@ The [`PanicInfo`](https://doc.rust-lang.org/core/panic/struct.PanicInfo.html) st ### Sanitizing Sensitive Data Panic hooks are also your final opportunity to prevent information leaks. -Remember that panic messages can contain sensitive data like file paths, internal state, or user information (PII) such as email addresses, IP addresses, credit card numbers, etc. -A well-designed panic hook sanitizes these messages before they reach logs or crash reports. +The sensitive data can come from two places: the panic payload and the panic location. The payload is whatever your code passed to `panic!`, `unwrap`, `expect`, or an assertion. That means it can contain interpolated user input, internal state from `Debug` output, request headers, tokens, email addresses, IP addresses, customer IDs, or other identifiers. The location can expose source file paths, workspace names, or CI/build machine directory layouts. + +A well-designed panic hook sanitizes these messages before they reach logs or crash reports. Better yet, avoid putting secrets or raw user data into panic messages in the first place. Prefer stable error codes, request IDs, or redacted domain types. Regexes can catch obvious patterns like email addresses, bearer tokens, UUIDs, and IP addresses, but they are a last layer of defense, not the primary control. ```rust panic::set_hook(Box::new(|panic_info| { @@ -240,7 +239,7 @@ Setting a hook is a great way to perform such cleanup operations. {% info(title="Panic Hooks Run in a Compromised Environment" icon="warning") %} -Be careful: one of the subsystems you want to interact with might be the *cause* of the panic you're handling! For example, if your database connection pool panicked, trying to flush pending writes to that same pool will likely fail or hang. Keep cleanup operations minimal and avoid anything that could panic itself. +Be careful: one of the subsystems you want to interact with might be the *cause* of the panic you're handling! For example, if your database connection pool panicked, trying to flush pending writes to that same pool will likely fail or hang. Keep cleanup operations fault-tolerant and avoid anything that can panic, block indefinitely, or depend on the subsystem that just failed. {% end %} @@ -263,56 +262,28 @@ The most obvious difference is integer overflow behavior. Debug builds panic on We covered that in [Pitfalls of Safe Rust](/blog/pitfalls-of-safe-rust/). But the differences run deeper than arithmetics. -For example, the optimizer can reorder operations in ways that break timing-sensitive code: - -```rust -fn rate_limited_operation() -> bool { - let start = std::time::Instant::now(); +Release builds remove `debug_assert!` checks, enable optimizations, and may exercise different code paths behind `cfg(debug_assertions)`. Unsafe code and FFI boundaries are especially sensitive to this: undefined behavior can appear harmless in debug mode and break only once the optimizer starts relying on Rust's aliasing and validity rules. - // Do some work - expensive_computation(); - - let elapsed = start.elapsed(); - if elapsed < std::time::Duration::from_millis(100) { - // Rate limiting: reject if too fast - return false; - } - - true -} -``` - -The optimizer might move the timing calculation or inline `expensive_computation()` in ways that change the timing behavior and break your rate-limit logic. -One way around this is `black_box` from `std::hint`, which prevents the optimizer from making assumptions about certain values: +Here is a tiny example: ```rust -use std::hint::black_box; - -fn rate_limited_operation() -> bool { - let start = std::time::Instant::now(); - - // Do some work - black_box(expensive_computation()); - - let elapsed = start.elapsed(); - if elapsed < std::time::Duration::from_millis(100) { - // Rate limiting: reject if too fast - return false; - } - - true +fn apply_discount(price: u32, percent: u32) -> u32 { + debug_assert!(percent <= 100); + price - (price * percent / 100) } ``` -It's telling the compiler: "Don't touch this; assume it could have side effects you don't know about." +In a debug build, `apply_discount(100, 150)` trips the `debug_assert!`. +In a release build, the assertion is gone. The subtraction can underflow and wrap around, turning an invalid discount into a huge number. +If the check protects a real runtime invariant, use `assert!` or return a `Result` instead of relying on `debug_assert!`. ### Testing Release Behavior -The fact that tests pass in debug mode tells you almost nothing about production behavior. -**Run your tests against release builds**. +The fact that tests pass in debug mode does not prove that production behavior is correct. +Run normal debug tests as the fast default, and add release-mode tests for critical integration tests, arithmetic-heavy code, unsafe or FFI-heavy code, and anything whose behavior depends on optimization or release-only configuration. ```bash -# Add this to your CI pipeline +# Add this to your CI pipeline in addition to regular `cargo test` cargo test --release ``` @@ -326,14 +297,15 @@ It's recommended to run those as part of CI. ## Secure Allocations With mimalloc [mimalloc] is a drop-in global allocator built by Microsoft. -What's special about it is that it also has a **secure mode**, which "adds guard pages, randomized allocation, encrypted free lists, etc." to prevent heap-based vulnerabilities. -The performance penalty is usually around 10% according to mimalloc's own benchmarks, which is typically acceptable because Rust is never the bottleneck. [^mimalloc_safe] +What's special about it is that it also has a **secure mode**, which adds mitigations like guard pages, randomized allocation, and encrypted free lists to make some heap-corruption bugs harder to exploit. [^mimalloc_safe] -To enable secure mode, put in Cargo.toml: +This is mostly defense-in-depth for programs with unsafe code, custom allocators, C/C++ dependencies, or FFI-heavy boundaries. Safe Rust already prevents most use-after-free and buffer-overflow bugs, and a secure allocator does not magically make memory-unsafe code safe. + +To enable secure mode, put this in `Cargo.toml`: ```toml [dependencies] -mimalloc = { version = "*", features = ["secure"] } +mimalloc = { version = "0.1", features = ["secure"] } ``` Then use it as your global allocator: @@ -345,8 +317,7 @@ use mimalloc::MiMalloc; static GLOBAL: MiMalloc = MiMalloc; ``` -Now, all heap allocations in your Rust program will use mimalloc's secure allocator, which will automatically catch common heap vulnerabilities like buffer overflows and use-after-free bugs at runtime. -So even if your code has a memory safety issue, it will be much harder for an attacker to exploit it. +Now, all heap allocations in your Rust program will use mimalloc's secure allocator. Measure the performance impact on your workload before rolling this out broadly; allocator choice can matter a lot for latency-sensitive services, games, packet processing, and other allocation-heavy programs. [mimalloc]: https://github.com/microsoft/mimalloc [^mimalloc_safe]: https://docs.rs/mimalloc-safe/latest/mimalloc_safe/ @@ -364,41 +335,49 @@ No shell, no package manager, no utilities an attacker could abuse for lateral m Even if your service is compromised, the attacker has very limited tools at their disposal to do further damage. My recommendation is [Google's distroless images](https://github.com/GoogleContainerTools/distroless). -They are minimal Debian-based images stripped of everything unnecessary, while still including libc, TLS certificates, timezone data, and a non-root user. -Everything a typical service needs and nothing more. +They are minimal Debian-based images stripped of everything unnecessary, while still including TLS certificates and a non-root user. +For a typical Rust web service, start with `gcr.io/distroless/cc-debian13:nonroot`: it includes the C runtime libraries that a normal Debian-built Rust binary may dynamically link against, but no shell or package manager. + +Here is a Dockerfile using [`cargo-chef`](https://github.com/LukeMathWalker/cargo-chef) for dependency caching: ```dockerfile -# Build stage -# Compile our Rust code into a statically linked binary -FROM rust:slim-bookworm AS build +# syntax=docker/dockerfile:1 -RUN apt-get update && apt-get install -y musl-tools lld +ARG RUST_VERSION=1.92 +FROM rust:${RUST_VERSION}-bookworm AS chef +RUN cargo install cargo-chef --locked WORKDIR /app + +FROM chef AS planner COPY . . -RUN cargo build --release +RUN cargo chef prepare --recipe-path recipe.json -# Final image -FROM gcr.io/distroless/static-debian12 +FROM chef AS builder +COPY --from=planner /app/recipe.json recipe.json +RUN cargo chef cook --release --recipe-path recipe.json -COPY --from=build /app/target/release/myapp /bin/myapp +COPY . . +RUN cargo build --locked --release --bin myapp -USER nonroot +FROM gcr.io/distroless/cc-debian13:nonroot AS runtime +COPY --from=builder /app/target/release/myapp /bin/myapp ENTRYPOINT ["/bin/myapp"] ``` -Make sure to look up the latest `static-debian` variant [here](https://github.com/GoogleContainerTools/distroless). -It is essentially `FROM scratch` but with CA certificates and a `nonroot` user already included. -If you need libc (e.g. for SQLite or other C dependencies), use `gcr.io/distroless/cc-debian` instead. +`cargo-chef` keeps dependency builds in a separate Docker layer, so changing your application code does not force all dependencies to rebuild. The important details are: use the same Rust version in all build stages, build with `--locked`, scope workspace builds with `--bin` when appropriate, and keep `target/`, `.git/`, and editor files out of the build context via `.dockerignore`. I covered this pattern in more detail in [Tips For Faster CI Builds](/blog/tips-for-faster-ci-builds/#use-cargo-chef-for-docker-builds). + +The current distroless README lists `cc-debian13` as the latest `cc` image family. Keep the Debian suffix explicit instead of using the unversioned tag, and pin by digest if reproducible deploys matter to you. +If you deliberately build a fully static musl binary, then `gcr.io/distroless/static-debian13:nonroot` or even `scratch` can be a better fit. But don't mix the two approaches: a glibc-linked binary needs a runtime image that provides the libraries it links against. {% info(title="A Note On Alpine Base Images", icon="info") %} -Alpine base images are a well-known alternative, but they can cause subtle runtime issues with Tokio and async runtimes due to musl's thread-local storage implementation. +Alpine base images are a well-known alternative, but they use musl instead of glibc. That can expose differences in DNS resolution, TLS/native dependencies, allocator behavior, and crates that assume a glibc-like environment. ([1](https://www.reddit.com/r/rust/comments/sq53vx/alpine_fails_to_run_my_app_what_steps_should_i/hwjloqz/) [2](https://martinheinz.dev/blog/92) [3](https://github.com/astral-sh/uv/issues/2732)) -Distroless sidesteps this entirely. +That doesn't mean Alpine or musl are wrong; just treat them as a deliberate target and test them like one. If you build on Debian and want a small runtime image, distroless `cc` is usually the less surprising default. {% end %} @@ -410,6 +389,14 @@ If your service is ever exploited, the attacker can only reach the files you exp [^below]: This approach would have prevented a [vulnerability in Meta's `below` crate](https://security.opensuse.org/2025/03/12/below-world-writable-log-dir.html), a tool for recording and displaying system data like hardware utilization and cgroup information on Linux. +{% info(title="Landlock Is Deployment-Specific" icon="info") %} + +Landlock is Linux-only and requires kernel support. It landed in Linux 5.13, but older enterprise kernels, custom cloud images, or container hosts may not enable it. Check your actual deployment target. + +Also apply the sandbox only after you know which files your process needs. If your service executes helper binaries from `/usr/bin`, reads timezone data from `/usr/share/zoneinfo`, loads certificates, opens SQLite files, reads config from `/etc`, or writes uploads to `/var/data`, those paths must be allowed explicitly. On non-Linux targets, look for equivalent sandboxing mechanisms instead of copying this exact snippet. + +{% end %} + ```rust use landlock::{ Access, AccessFs, PathBeneath, PathFd, Ruleset, RulesetAttr, @@ -447,9 +434,22 @@ Call `sandbox()` as early as possible in `main`, before spawning threads or acce The restrictions apply to the entire process from that point forward. The two approaches really go hand in hand: -- `FROM scratch` limits what's *in* the container +- minimal images limit what's *in* the container - Landlock limits what the process can *touch* at runtime. +### Drop Privileges and Capabilities + +Don't run as root in production, even inside a container. +That's one reason distroless images provide a `nonroot` user and why the example above uses the `:nonroot` tag. +If your service only needs to listen for HTTP traffic, prefer a high port like `8080` over running as root just to bind to port `80`. + +Linux capabilities are another useful lever. +Instead of giving a process full root privileges, grant only the specific capability it needs, such as `CAP_NET_BIND_SERVICE` for binding to low ports. +If a process needs elevated privileges only during startup, drop them before accepting requests. + +The details vary by platform and orchestrator, so treat Linux containers as one concrete example, not the universal model. +For systemd services, Kubernetes, FreeBSD jails, macOS sandboxing, or Windows services, look up the equivalent least-privilege and sandboxing features for that environment. + The big picture is that security hardening is about reducing the surface of things that can go wrong. Every capability your process holds unnecessarily is a liability and everything your code manages that could be delegated to the OS, init system, or container runtime probably should be. From 6659bbace858684f673a68f47725980194597795 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 21 Jul 2026 01:58:18 +0200 Subject: [PATCH 22/28] Refine hardening Rust article Signed-off-by: Matthias --- content/blog/hardening-rust/index.md | 159 +++++++++++++++++++++------ 1 file changed, 125 insertions(+), 34 deletions(-) diff --git a/content/blog/hardening-rust/index.md b/content/blog/hardening-rust/index.md index 2714de0d..d865fa94 100644 --- a/content/blog/hardening-rust/index.md +++ b/content/blog/hardening-rust/index.md @@ -1,12 +1,12 @@ +++ title = "Hardening Rust Code Against Runtime Failures" -date = 2026-02-19 +date = 2026-07-21 draft = false template = "article.html" [extra] series = "Idiomatic Rust" resources = [ - "[Patterns for Defensive Programming in Rust](/blog/defensive-programming) -- making your Rust code more robust by enforcing invariants", + "[Patterns for Defensive Programming in Rust](/blog/defensive-programming) -- enforcing invariants that Rust cannot check for you", "[Pitfalls of Safe Rust](/blog/pitfalls-of-safe-rust) -- common mistakes even safe Rust programmers make", ] +++ @@ -14,13 +14,52 @@ resources = [ We talked about [patterns for defensive programming in Rust](/blog/defensive-programming) before, in which implicit invariants that aren't enforced by the compiler lead to utter misery. But being careful isn't enough. Even valid code can fail at runtime in ways that are hard to predict and control. -That's what we're covering here. +That's what we're covering next. + +{% info(title="This article is for you if you want to...", icon="crab") %} -This article is for you if you want to - make your code resilient at runtime - harden your Rust code for production - know how Rust code can fail in unexpected ways and how to recover from that +{% end %} + +## Table of Contents + +
+ +Click here to expand the table of contents. + + +- [Panic Semantics Are Part of Your API](#panic-semantics-are-part-of-your-api) + - [Unwind vs. Abort](#unwind-vs-abort) + - [Thread-Level vs. Process-Level Failures](#thread-level-vs-process-level-failures) +- [Stack Overflow as a Failure Mode](#stack-overflow-as-a-failure-mode) +- [Observing Failures With Panic Hooks](#observing-failures-with-panic-hooks) + - [Example Panic Hooks](#example-panic-hooks) + - [Sanitizing Sensitive Data](#sanitizing-sensitive-data) + - [Cleanup Operations](#cleanup-operations) + - [Limitations](#limitations) +- [Release and Debug Builds Are Two Different Programs](#release-and-debug-builds-are-two-different-programs) + - [Testing Release Behavior](#testing-release-behavior) +- [Supply-Chain Security](#supply-chain-security) +- [Secure Allocations With mimalloc](#secure-allocations-with-mimalloc) +- [Limit Your Runtime Attack Surface](#limit-your-runtime-attack-surface) + - [Minimal Docker Images](#minimal-docker-images) + - [Filesystem Sandboxing With Landlock](#filesystem-sandboxing-with-landlock) + - [Drop Privileges and Capabilities](#drop-privileges-and-capabilities) +- [Miri: Detecting Undefined Behavior at Runtime](#miri-detecting-undefined-behavior-at-runtime) +- [Graceful Shutdown Handling](#graceful-shutdown-handling) +- [Circuit Breakers for External Dependencies](#circuit-breakers-for-external-dependencies) +- [Resource Limits](#resource-limits) + - [Request Body Size Limits](#request-body-size-limits) + - [Limit Queue Depth](#limit-queue-depth) + - [Set Timeouts on Everything External](#set-timeouts-on-everything-external) +- [Health Checks and Self-Healing](#health-checks-and-self-healing) +- [Runtime Hardening Tooling](#runtime-hardening-tooling) + +
+ ## Panic Semantics Are Part of Your API Here's a question: what happens when a Rust program panics? @@ -41,11 +80,12 @@ let result = panic::catch_unwind(|| { But the [Rustonomicon](https://doc.rust-lang.org/nomicon/unwinding.html) has the following to say about unwinding panics: -> We would encourage you to only **do this sparingly**. In particular, Rust's current unwinding implementation is heavily optimized for the "doesn't unwind" case. If a program doesn't unwind, there should be no runtime cost for the program being ready to unwind. [...] Ideally, you should only panic for programming errors or extreme problems. +> We would encourage you to only **do this sparingly**. In particular, Rust's current unwinding implementation is heavily optimized for the "doesn't unwind" case. If a program doesn't unwind, there should be no runtime cost for the program being ready to unwind. The alternative to unwinding is aborting the entire process. That does what it says on the tin: the program immediately terminates without unwinding the stack or running destructors. -Crash and burn. +Halt and catch fire. Weirdly enough, that's often the safer choice, especially when dealing with FFI boundaries or performance-critical code. +That's because unwinding across FFI boundaries is undefined behavior, and unwinding can be expensive in performance-sensitive code. To enable aborting on panic, add the following to your `Cargo.toml`: @@ -56,6 +96,8 @@ panic = "abort" And **even if** you did not explicitly configure this, catastrophic panics like stack overflows and out-of-memory errors **always abort the process**. That's because unwinding in these situations is unsafe and can lead to undefined behavior. +In practice, this shows up in two places: + - Panics that would unwind across an extern "C" boundary are defined to abort instead of unwinding, because [letting unwinding cross that boundary is undefined behavior](https://doc.rust-lang.org/nomicon/ffi.html#panic-can-be-stopped-at-an-abi-boundary). - And if a `malloc` fails, [it aborts the process](https://news.ycombinator.com/item?id=11369457). If that's a problem, you need to proactively check for allocation sizes before allocating or avoid heap allocations altogether. @@ -73,15 +115,64 @@ What sounds like a benefit can leave the system in a partially degraded state. This distinction becomes especially important in long-running systems (servers, workers, async runtimes,...). A panic in a request-handling thread might only abort that one request, while the rest of the service remains available. +Here's a small example using scoped threads ([Playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=309325417605cdb3cdd42e1b3e1a618d)): + +```rust +use std::{thread, time::Duration}; + +fn handle_request(id: u32) { + println!("request {id}: started"); + + if id == 2 { + panic!("request {id}: handler panicked"); + } + + thread::sleep(Duration::from_millis(100)); + println!("request {id}: finished"); +} + +fn main() { + thread::scope(|s| { + let requests: Vec<_> = (1..=3) + .map(|id| (id, s.spawn(move || handle_request(id)))) + .collect(); + + for (id, request) in requests { + match request.join() { + Ok(()) => println!("main: request {id} completed"), + Err(_) => println!("main: request {id} failed, but the process is still alive"), + } + } + }); + + println!("main: service keeps running"); +} +``` + +The output is noisy because Rust prints the panic message to stderr, but the interesting part is this: + +```text +request 1: finished +main: request 1 completed +main: request 2 failed, but the process is still alive +request 3: finished +main: request 3 completed +main: service keeps running +``` + +Request 2 panics, but requests 1 and 3 still finish. The panic belongs to the worker thread. The main thread sees it through `join()` and keeps running. [^unwinding] + +[^unwinding]: This only holds for unwinding panics. If you compile with `panic = "abort"`, or hit a stack overflow or out-of-memory failure, the whole process exits and `join()` never gets a chance to return `Err`. + Whether this is acceptable depends on the system's invariants. If a panic indicates a violated assumption confined to a small scope, like a single request, letting the process continue may be reasonable. But if it signals a global invariant violation, continuing execution can be outright dangerous. -The key insight is that panic behavior is **part of your system's failure model**. +Panic behavior is **part of your system's failure model**. Treating all panics as equivalent hides important distinctions and leads to fragile assumptions. -You should be explicit about whether a failure is allowed to take down a single task, a single thread, or the entire process. +Be explicit about whether a failure may take down a single task, a single thread, or the entire process. -**Never panic in an uncontrolled manner.** +Never panic in an uncontrolled manner. ## Stack Overflow as a Failure Mode @@ -115,9 +206,9 @@ fn factorial(n: u64) -> u64 { } ``` -**Panic behavior isn't the only runtime failure mode you need to worry about.** +Panic behavior isn't the only runtime failure mode you need to worry about. -## Panic Hooks Make Failures Observable +## Observing Failures With Panic Hooks Now that you understand how panics work, let's talk about operational hardening. @@ -164,6 +255,12 @@ panic::set_hook(Box::new(|panic_info| { })); ``` +{% info(title="What's Inside `PanicInfo`?" icon="info") %} + +The [`PanicInfo`](https://doc.rust-lang.org/core/panic/struct.PanicInfo.html) struct contains the panic message (via `.payload()`) and the source location where the panic occurred (via `.location()`). Be aware that both can leak sensitive information: file paths may reveal internal directory structure, and panic messages might contain interpolated user data. + +{% end %} + And finally, here's [Sentry's panic hook handler](https://github.com/getsentry/sentry-rust/blob/625617015f2b64fabdf8264186911ca43873bb80/sentry-panic/src/lib.rs#L69-L77), which is even more sophisticated: ```rust @@ -185,18 +282,12 @@ Sentry's panic hook: There's a lot to learn from these few lines of code! -{% info(title="What's Inside `PanicInfo`?" icon="info") %} - -The [`PanicInfo`](https://doc.rust-lang.org/core/panic/struct.PanicInfo.html) struct contains the panic message (via `.payload()`) and the source location where the panic occurred (via `.location()`). Be aware that both can leak sensitive information: file paths may reveal internal directory structure, and panic messages might contain interpolated user data. - -{% end %} - ### Sanitizing Sensitive Data Panic hooks are also your final opportunity to prevent information leaks. The sensitive data can come from two places: the panic payload and the panic location. The payload is whatever your code passed to `panic!`, `unwrap`, `expect`, or an assertion. That means it can contain interpolated user input, internal state from `Debug` output, request headers, tokens, email addresses, IP addresses, customer IDs, or other identifiers. The location can expose source file paths, workspace names, or CI/build machine directory layouts. -A well-designed panic hook sanitizes these messages before they reach logs or crash reports. Better yet, avoid putting secrets or raw user data into panic messages in the first place. Prefer stable error codes, request IDs, or redacted domain types. Regexes can catch obvious patterns like email addresses, bearer tokens, UUIDs, and IP addresses, but they are a last layer of defense, not the primary control. +A well-designed panic hook sanitizes these messages before they reach logs or crash reports. Better yet, avoid putting secrets or raw user data into panic messages in the first place. Prefer stable error codes, request IDs, or redacted domain types. Regexes can catch obvious patterns like email addresses and bearer tokens. UUIDs and IP addresses can also identify users. Treat those checks as your final fallback. ```rust panic::set_hook(Box::new(|panic_info| { @@ -245,10 +336,10 @@ Be careful: one of the subsystems you want to interact with might be the *cause* ### Limitations -Remember that panic hooks only run for unwinding panics. -If your program aborts on panic, or if the panic is caused by a stack overflow or out-of-memory condition, **your hook won't execute**. +Panic hooks only run for unwinding panics. +If your program aborts on panic, or if the panic is caused by a stack overflow or out-of-memory condition, your hook won't execute. -Therefore **never rely on panic hooks for correctness.** +Never rely on panic hooks for correctness. They're purely for observability and graceful degradation; don't try to recover from logic errors as it is very hard to rely on a system's fragile underpinnings at this stage. @@ -264,7 +355,7 @@ We covered that in [Pitfalls of Safe Rust](/blog/pitfalls-of-safe-rust/). But the differences run deeper than arithmetics. Release builds remove `debug_assert!` checks, enable optimizations, and may exercise different code paths behind `cfg(debug_assertions)`. Unsafe code and FFI boundaries are especially sensitive to this: undefined behavior can appear harmless in debug mode and break only once the optimizer starts relying on Rust's aliasing and validity rules. -Here is a tiny example: +Here is a trivial example: ```rust fn apply_discount(price: u32, percent: u32) -> u32 { @@ -283,7 +374,7 @@ The fact that tests pass in debug mode does not prove that production behavior i Run normal debug tests as the fast default, and add release-mode tests for critical integration tests, arithmetic-heavy code, unsafe or FFI-heavy code, and anything whose behavior depends on optimization or release-only configuration. ```bash -# Add this to your CI pipeline in addition to regular `cargo test` +# Add this to your CI pipeline alongside regular `cargo test` cargo test --release ``` @@ -299,7 +390,8 @@ It's recommended to run those as part of CI. [mimalloc] is a drop-in global allocator built by Microsoft. What's special about it is that it also has a **secure mode**, which adds mitigations like guard pages, randomized allocation, and encrypted free lists to make some heap-corruption bugs harder to exploit. [^mimalloc_safe] -This is mostly defense-in-depth for programs with unsafe code, custom allocators, C/C++ dependencies, or FFI-heavy boundaries. Safe Rust already prevents most use-after-free and buffer-overflow bugs, and a secure allocator does not magically make memory-unsafe code safe. +Safe Rust already prevents most use-after-free and buffer-overflow bugs, and a secure allocator does not magically make memory-unsafe code safe. +This is mostly defense-in-depth for programs with unsafe code, custom allocators, C/C++ dependencies, or FFI-heavy boundaries. To enable secure mode, put this in `Cargo.toml`: @@ -331,12 +423,12 @@ Now, how you do that depends on your deployment environment, but generally peopl ### Minimal Docker images A minimal production image contains exactly what you put in it. -No shell, no package manager, no utilities an attacker could abuse for lateral movement. Even if your service is compromised, the attacker has very limited tools at their disposal to do further damage. My recommendation is [Google's distroless images](https://github.com/GoogleContainerTools/distroless). They are minimal Debian-based images stripped of everything unnecessary, while still including TLS certificates and a non-root user. For a typical Rust web service, start with `gcr.io/distroless/cc-debian13:nonroot`: it includes the C runtime libraries that a normal Debian-built Rust binary may dynamically link against, but no shell or package manager. +(Check the latest version in the [distroless README](https://github.com/googlecontainertools/distroless)) Here is a Dockerfile using [`cargo-chef`](https://github.com/LukeMathWalker/cargo-chef) for dependency caching: @@ -447,7 +539,7 @@ Linux capabilities are another useful lever. Instead of giving a process full root privileges, grant only the specific capability it needs, such as `CAP_NET_BIND_SERVICE` for binding to low ports. If a process needs elevated privileges only during startup, drop them before accepting requests. -The details vary by platform and orchestrator, so treat Linux containers as one concrete example, not the universal model. +The details vary by platform and orchestrator, so treat Linux containers as one concrete setup. For systemd services, Kubernetes, FreeBSD jails, macOS sandboxing, or Windows services, look up the equivalent least-privilege and sandboxing features for that environment. The big picture is that security hardening is about reducing the surface of things that can go wrong. @@ -544,7 +636,7 @@ Everybody who was oncall for a production service will tell you this. Set explicit limits on everything. SREs will thank you for it! -Apart from the fact that it makes your service more robust, it also makes it easier to immediately detect misconfigurations in code. +Limits make your service more predictable, and they make misconfigurations obvious sooner. Common things you should limit include: - Upper bound on any user input (upload file size, parameter bounds, etc.) @@ -552,8 +644,7 @@ Common things you should limit include: - timeouts on external calls - concurrent connections to external services - queue depth for background jobs -- number of threads -- DB connection pool size +- thread count and DB connection pool size Here are some examples on how to do these in practice: @@ -585,13 +676,13 @@ let client = reqwest::Client::builder() .build()?; ``` -The key insight is that **every unbounded resource is a potential DoS vector**. -Explicit limits will turn those catastrophic failures into (annoying but harmless) graceful rejections. +Every unbounded resource is a potential DoS vector. +Explicit limits turn those catastrophic failures into (annoying but harmless) graceful rejections. ## Health Checks and Self-Healing Ideally, your system should be able to recover from transient failures without human intervention. -Health checks let load balancers, orchestrators, and monitoring systems know when something is wrong and act accordingly. +Health checks let load balancers and orchestrators know when something is wrong, so they can react. A typical setup has two endpoints, a liveness probe and a readiness probe. The liveness probe checks if the process is alive at all, while the readiness probe checks if the process is healthy enough to handle traffic. @@ -620,8 +711,8 @@ enum Status { /// the readiness probe #[derive(Serialize)] struct HealthStatus { - // Overall health status of the service - status: Status + // Health status of the service + status: Status, // Is the database connection healthy? database: bool, // Is the cache connection healthy? From 4ab3afbe6768fbd6ed52d82eb3b222cad8201a79 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 21 Jul 2026 15:24:45 +0200 Subject: [PATCH 23/28] work on post --- content/blog/hardening-rust/cargo-audit.jpg | Bin 0 -> 167394 bytes content/blog/hardening-rust/index.md | 87 ++++++++++---------- 2 files changed, 45 insertions(+), 42 deletions(-) create mode 100644 content/blog/hardening-rust/cargo-audit.jpg diff --git a/content/blog/hardening-rust/cargo-audit.jpg b/content/blog/hardening-rust/cargo-audit.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3d98fe9e16f7b573904d5180d08a453b4b0309d8 GIT binary patch literal 167394 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 literal 0 HcmV?d00001 diff --git a/content/blog/hardening-rust/index.md b/content/blog/hardening-rust/index.md index d865fa94..5185795c 100644 --- a/content/blog/hardening-rust/index.md +++ b/content/blog/hardening-rust/index.md @@ -1,5 +1,5 @@ +++ -title = "Hardening Rust Code Against Runtime Failures" +title = "Hardening Rust Code For Production" date = 2026-07-21 draft = false template = "article.html" @@ -12,7 +12,7 @@ resources = [ +++ We talked about [patterns for defensive programming in Rust](/blog/defensive-programming) before, in which implicit invariants that aren't enforced by the compiler lead to utter misery. -But being careful isn't enough. +But being careful isn't enough! Even valid code can fail at runtime in ways that are hard to predict and control. That's what we're covering next. @@ -34,12 +34,12 @@ Click here to expand the table of contents. - [Panic Semantics Are Part of Your API](#panic-semantics-are-part-of-your-api) - [Unwind vs. Abort](#unwind-vs-abort) - [Thread-Level vs. Process-Level Failures](#thread-level-vs-process-level-failures) -- [Stack Overflow as a Failure Mode](#stack-overflow-as-a-failure-mode) - [Observing Failures With Panic Hooks](#observing-failures-with-panic-hooks) - [Example Panic Hooks](#example-panic-hooks) - [Sanitizing Sensitive Data](#sanitizing-sensitive-data) - [Cleanup Operations](#cleanup-operations) - [Limitations](#limitations) +- [Stack Overflows And Runtime Behavior](#stack-overflows-and-runtime-behavior) - [Release and Debug Builds Are Two Different Programs](#release-and-debug-builds-are-two-different-programs) - [Testing Release Behavior](#testing-release-behavior) - [Supply-Chain Security](#supply-chain-security) @@ -62,8 +62,7 @@ Click here to expand the table of contents. ## Panic Semantics Are Part of Your API -Here's a question: what happens when a Rust program panics? - +What happens when a Rust program panics? There is no single correct answer because `panic!` is not a "single behavior." ### Unwind vs. Abort @@ -149,7 +148,7 @@ fn main() { } ``` -The output is noisy because Rust prints the panic message to stderr, but the interesting part is this: +The interesting part of the output is this: ```text request 1: finished @@ -160,7 +159,7 @@ main: request 3 completed main: service keeps running ``` -Request 2 panics, but requests 1 and 3 still finish. The panic belongs to the worker thread. The main thread sees it through `join()` and keeps running. [^unwinding] +Request 2 panics, but requests 1 and 3 still finish. The panic belongs to the worker thread. The main thread gets notified on `join()` but keeps running. [^unwinding] [^unwinding]: This only holds for unwinding panics. If you compile with `panic = "abort"`, or hit a stack overflow or out-of-memory failure, the whole process exits and `join()` never gets a chance to return `Err`. @@ -174,40 +173,6 @@ Be explicit about whether a failure may take down a single task, a single thread Never panic in an uncontrolled manner. -## Stack Overflow as a Failure Mode - -Okay, you handle errors gracefully and you know how your system behaves on panic. -But did you account for stack overflows as well? - -Here's some simple recursive code that can quickly exhaust stack space: - -```rust -fn factorial(n: u64) -> u64 { - if n == 0 { - 1 - } else { - n * factorial(n - 1) - } -} -``` - -If you allow users to call this function with large inputs, it might crash your program. -Rust does not guarantee tail-call optimization on stable Rust. Some compilers and languages can turn certain tail-recursive functions into loops, but you should not rely on that transformation in Rust. If recursion depth depends on user input or external data, rewrite the algorithm iteratively or put an explicit bound on the depth. - -It requires some experience, but for recursive algorithms where you're not in control of the input size, it's often safer to use an iterative approach: - -```rust -fn factorial(n: u64) -> u64 { - let mut result = 1; - for i in 1..=n { - result *= i; - } - result -} -``` - -Panic behavior isn't the only runtime failure mode you need to worry about. - ## Observing Failures With Panic Hooks Now that you understand how panics work, let's talk about operational hardening. @@ -229,7 +194,7 @@ use std::panic; fn main() { panic::set_hook(Box::new(|panic_info| { - eprintln!("Panic occurred: {}", panic_info); + eprintln!("Panic occurred: {panic_info}"); // Log to your monitoring system // Send crash reports // Clean up resources @@ -343,6 +308,42 @@ Never rely on panic hooks for correctness. They're purely for observability and graceful degradation; don't try to recover from logic errors as it is very hard to rely on a system's fragile underpinnings at this stage. +## Stack Overflows And Runtime Behavior + +Okay, you handle errors gracefully and you know how your system behaves on panic. +Panic behavior isn't the only runtime failure mode you need to worry about. + +Here's some simple recursive code. +What is wrong with it? + +```rust +fn factorial(n: u64) -> u64 { + if n == 0 { + 1 + } else { + n * factorial(n - 1) + } +} +``` + +The problem is that recursion can quickly exhaust stack space. + +If you allow users to call this function with large inputs, it might crash your program. +[Rust does not guarantee tail-call optimization on stable Rust](https://weitzel.dev/blog/rustlang-trampoline/). Some compilers and languages can turn certain tail-recursive functions into loops, but you should not rely on that transformation in Rust. If recursion depth depends on user input or external data, rewrite the algorithm iteratively or put an explicit bound on the depth. + +It requires some experience, but for recursive algorithms where you're not in control of the input size, it's often safer to use an iterative approach: + +```rust +fn factorial(n: u64) -> u64 { + let mut result = 1; + for i in 1..=n { + result *= i; + } + result +} +``` + + ## Release and Debug Builds Are Two Different Programs One of the most dangerous assumptions in Rust development is that debug and release builds are functionally equivalent. @@ -385,6 +386,8 @@ You should regularly audit your dependencies for known vulnerabilities. Two helpful tools for that are [`cargo-audit`](https://github.com/rustsec/rustsec/tree/main/cargo-audit) and [`cargo-deny`](https://embarkstudios.github.io/cargo-deny/). It's recommended to run those as part of CI. +![cargo-audit run](cargo-audit.jpg) + ## Secure Allocations With mimalloc [mimalloc] is a drop-in global allocator built by Microsoft. From ac316f21241cef17b7ff157c08bc9c960ba57f02 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 21 Jul 2026 16:21:37 +0200 Subject: [PATCH 24/28] update content --- content/blog/hardening-rust/index.md | 36 ++++++++++++++++------------ 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/content/blog/hardening-rust/index.md b/content/blog/hardening-rust/index.md index 5185795c..2e0da4db 100644 --- a/content/blog/hardening-rust/index.md +++ b/content/blog/hardening-rust/index.md @@ -48,7 +48,7 @@ Click here to expand the table of contents. - [Minimal Docker Images](#minimal-docker-images) - [Filesystem Sandboxing With Landlock](#filesystem-sandboxing-with-landlock) - [Drop Privileges and Capabilities](#drop-privileges-and-capabilities) -- [Miri: Detecting Undefined Behavior at Runtime](#miri-detecting-undefined-behavior-at-runtime) +- [Miri: Detect Unsafe Code Issues](#miri-detect-unsafe-code-issues) - [Graceful Shutdown Handling](#graceful-shutdown-handling) - [Circuit Breakers for External Dependencies](#circuit-breakers-for-external-dependencies) - [Resource Limits](#resource-limits) @@ -428,12 +428,15 @@ Now, how you do that depends on your deployment environment, but generally peopl A minimal production image contains exactly what you put in it. Even if your service is compromised, the attacker has very limited tools at their disposal to do further damage. -My recommendation is [Google's distroless images](https://github.com/GoogleContainerTools/distroless). -They are minimal Debian-based images stripped of everything unnecessary, while still including TLS certificates and a non-root user. +My recommendation is [Google's distroless images](https://github.com/GoogleContainerTools/distroless), but I recommend that you do your own research[^distroless] as I'm not an expert on this. + +[^distroless]: Data sources I found useful for this topic include [this post](https://www.minimus.io/post/best-distroless-image-alternatives-2026) and [this comparison](https://safeguard.sh/resources/blog/distroless-vs-chainguard-vs-wolfi-base-images). + +Distroless images are minimal Debian-based images stripped of everything unnecessary, while still including TLS certificates and a non-root user. For a typical Rust web service, start with `gcr.io/distroless/cc-debian13:nonroot`: it includes the C runtime libraries that a normal Debian-built Rust binary may dynamically link against, but no shell or package manager. (Check the latest version in the [distroless README](https://github.com/googlecontainertools/distroless)) -Here is a Dockerfile using [`cargo-chef`](https://github.com/LukeMathWalker/cargo-chef) for dependency caching: +Here is an example Dockerfile using [`cargo-chef`](https://github.com/LukeMathWalker/cargo-chef) for dependency caching: ```dockerfile # syntax=docker/dockerfile:1 @@ -460,9 +463,11 @@ COPY --from=builder /app/target/release/myapp /bin/myapp ENTRYPOINT ["/bin/myapp"] ``` -`cargo-chef` keeps dependency builds in a separate Docker layer, so changing your application code does not force all dependencies to rebuild. The important details are: use the same Rust version in all build stages, build with `--locked`, scope workspace builds with `--bin` when appropriate, and keep `target/`, `.git/`, and editor files out of the build context via `.dockerignore`. I covered this pattern in more detail in [Tips For Faster CI Builds](/blog/tips-for-faster-ci-builds/#use-cargo-chef-for-docker-builds). +Take this Dockerfile as a starting point, but please adapt it to your own project requirements. + +`cargo-chef` keeps dependency builds in a separate Docker layer, so changing your application code does not force all dependencies to rebuild. The important details are: use the same Rust version in all build stages, build with `--locked`, scope workspace builds with `--bin` when appropriate, and keep `target/`, `.git/`, and editor files out of the build context via `.dockerignore`. For a deep-dive on Docker images and build-time optimization, see [Tips For Faster CI Builds](/blog/tips-for-faster-ci-builds). -The current distroless README lists `cc-debian13` as the latest `cc` image family. Keep the Debian suffix explicit instead of using the unversioned tag, and pin by digest if reproducible deploys matter to you. +Keep the Debian suffix explicit instead of using the unversioned tag, and pin by digest if reproducible deploys matter to you. If you deliberately build a fully static musl binary, then `gcr.io/distroless/static-debian13:nonroot` or even `scratch` can be a better fit. But don't mix the two approaches: a glibc-linked binary needs a runtime image that provides the libraries it links against. {% info(title="A Note On Alpine Base Images", icon="info") %} @@ -548,9 +553,9 @@ For systemd services, Kubernetes, FreeBSD jails, macOS sandboxing, or Windows se The big picture is that security hardening is about reducing the surface of things that can go wrong. Every capability your process holds unnecessarily is a liability and everything your code manages that could be delegated to the OS, init system, or container runtime probably should be. -## Miri: Detecting Undefined Behavior at Runtime +## Miri: Detect Unsafe Code Issues -[`miri`](https://github.com/rust-lang/miri) is an interpreter for Rust's mid-level intermediate representation (MIR) that can detect undefined behavior at runtime. +[Miri](https://github.com/rust-lang/miri) is an interpreter for Rust's mid-level intermediate representation (MIR) that can detect undefined behavior at runtime. It works by executing your Rust code in a special environment that tracks memory accesses, pointer validity, and other low-level details to catch issues that the compiler can't statically guarantee against. @@ -629,16 +634,16 @@ async fn main() -> Result<()> { When an external service (database, API, cache) starts failing, you don't want to keep hammering it with requests. A circuit breaker tracks failures and "trips" when a threshold is reached. -For production use, consider crates like [`recloser`](https://crates.io/crates/recloser) or [`failsafe`](https://crates.io/crates/failsafe). +For production use, consider crates like [`failsafe`](https://crates.io/crates/failsafe) +or the more actively maintained [`recloser`](https://crates.io/crates/recloser), which is based on failsafe. ## Resource Limits Unbounded resources are a common source of runtime failures. Everybody who was oncall for a production service will tell you this. -Set explicit limits on everything. +**Set explicit limits on everything**. SREs will thank you for it! - Limits make your service more predictable, and they make misconfigurations obvious sooner. Common things you should limit include: @@ -713,7 +718,7 @@ enum Status { /// which we will return as JSON from /// the readiness probe #[derive(Serialize)] -struct HealthStatus { +struct HealthResponse { // Health status of the service status: Status, // Is the database connection healthy? @@ -736,7 +741,7 @@ async fn liveness() -> &'static str { async fn readiness( db: Extension, cache: Extension, -) -> Json { +) -> Json { let db_ok = db.ping().await.is_ok(); let cache_ok = cache.ping().await.is_ok(); @@ -746,7 +751,7 @@ async fn readiness( _ => Status::Degraded, }; - Json(HealthStatus { + Json(HealthResponse { status, database: db_ok, cache: cache_ok, @@ -777,7 +782,8 @@ readinessProbe: periodSeconds: 5 ``` -The distinction matters because +Do we really need both probes? Yes, because they serve different purposes: + - Kubernetes stops sending traffic (graceful degradation) if the readiness probe fails. It does not yet kill the pod. - Kubernetes restarts your pod if the liveness probe fails (it's self-healing!) From db20f015bbfffec010906f2c67a7af701e291293 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 21 Jul 2026 16:32:42 +0200 Subject: [PATCH 25/28] cross-link post --- content/blog/bugs-rust-wont-catch/index.md | 1 + content/blog/defensive-programming/index.md | 1 + content/blog/pitfalls-of-safe-rust/index.md | 3 ++- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/content/blog/bugs-rust-wont-catch/index.md b/content/blog/bugs-rust-wont-catch/index.md index 55b3c3d0..dbb34a32 100644 --- a/content/blog/bugs-rust-wont-catch/index.md +++ b/content/blog/bugs-rust-wont-catch/index.md @@ -10,6 +10,7 @@ resources = [ "[An update on rust-coreutils](https://discourse.ubuntu.com/t/an-update-on-rust-coreutils/80773): Canonical's announcement of the audit results", "[Patterns for Defensive Programming in Rust](/blog/defensive-programming/): companion post on writing more robust Rust code", "[Pitfalls of Safe Rust](/blog/pitfalls-of-safe-rust/): common mistakes even safe Rust code can make", + "[Hardening Rust Code Against Runtime Failures](/blog/hardening-rust/): runtime failure modes and production hardening", "[Sharp Edges In The Rust Standard Library](/blog/sharp-edges-in-rust-std/): surprising behaviors in `std`", "[Rust Prevents Data Races, Not Race Conditions](/blog/rust-prevents-data-races-not-race-conditions/): where Rust's concurrency safety ends", "[uutils/coreutils on GitHub](https://github.com/uutils/coreutils): the Rust reimplementation of GNU coreutils", diff --git a/content/blog/defensive-programming/index.md b/content/blog/defensive-programming/index.md index e1072c97..bcb699a0 100644 --- a/content/blog/defensive-programming/index.md +++ b/content/blog/defensive-programming/index.md @@ -12,6 +12,7 @@ reviews = [ resources = [ "[Making illegal states unrepresentable](/blog/illegal-state/)", "[Compile-time invariants in Rust](/blog/compile-time-invariants/)", + "[Hardening Rust Code Against Runtime Failures](/blog/hardening-rust/) -- what can still go wrong at runtime and how to prepare for it", ] +++ diff --git a/content/blog/pitfalls-of-safe-rust/index.md b/content/blog/pitfalls-of-safe-rust/index.md index 029c1b1a..bc64f0bb 100644 --- a/content/blog/pitfalls-of-safe-rust/index.md +++ b/content/blog/pitfalls-of-safe-rust/index.md @@ -13,7 +13,8 @@ reviews = [ ] resources = [ "[The Four Horsemen of Bad Rust Code](https://github.com/corrode/four-horsemen-talk) -- My talk at FOSDEM 2024", - "[High Assurance Rust](https://highassurance.rs/) -- developing secure and robust software with Rust" + "[High Assurance Rust](https://highassurance.rs/) -- developing secure and robust software with Rust", + "[Hardening Rust Code Against Runtime Failures](/blog/hardening-rust/) -- production failures that safe Rust alone cannot prevent" ] +++ From 4444cab74debebf30eab97b597ec706df1e86a62 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 21 Jul 2026 16:48:24 +0200 Subject: [PATCH 26/28] better word Signed-off-by: Matthias --- content/blog/bugs-rust-wont-catch/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/blog/bugs-rust-wont-catch/index.md b/content/blog/bugs-rust-wont-catch/index.md index dbb34a32..1685730e 100644 --- a/content/blog/bugs-rust-wont-catch/index.md +++ b/content/blog/bugs-rust-wont-catch/index.md @@ -201,7 +201,7 @@ UTF-8 is a great default for application strings, but it's absolutely, positivel ## Treat Every `panic!` as a Denial of Service -In a CLI, every `unwrap`, every `expect`, every slice index, every unchecked arithmetic operation, every [`from_utf8`](https://doc.rust-lang.org/std/str/fn.from_utf8.html) is a potential denial of service if an attacker can shape the input. +In a CLI, every `unwrap`, every `expect`, every slice index, every unchecked arithmetic operation, every [`from_utf8`](https://doc.rust-lang.org/std/str/fn.from_utf8.html) is a potential denial of service if an attacker can control the input. That's because a `panic!` unwinds the stack and aborts the process. If your tool is running in a cron job, a CI pipeline, or a shell script, that means the whole thing just stops working. Even worse, you could find yourself in a crash loop that paralyzes the entire system. A canonical case from the audit was `sort --files0-from` ([CVE-2026-35348](https://ubuntu.com/security/CVE-2026-35348)). The flag reads a NUL-separated list of filenames from a file, but the parser called `expect()` on a UTF-8 conversion of each name: From 752ab2cd86bcf5d12345589d70809dc2af84227b Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 21 Jul 2026 16:59:24 +0200 Subject: [PATCH 27/28] typos --- content/blog/hardening-rust/index.md | 56 ++++++++++++++-------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/content/blog/hardening-rust/index.md b/content/blog/hardening-rust/index.md index 2e0da4db..c8c5d84c 100644 --- a/content/blog/hardening-rust/index.md +++ b/content/blog/hardening-rust/index.md @@ -1,5 +1,5 @@ +++ -title = "Hardening Rust Code For Production" +title = "Hardening Rust Code For Production" date = 2026-07-21 draft = false template = "article.html" @@ -19,7 +19,7 @@ That's what we're covering next. {% info(title="This article is for you if you want to...", icon="crab") %} - make your code resilient at runtime -- harden your Rust code for production +- harden your Rust code for production - know how Rust code can fail in unexpected ways and how to recover from that {% end %} @@ -48,7 +48,7 @@ Click here to expand the table of contents. - [Minimal Docker Images](#minimal-docker-images) - [Filesystem Sandboxing With Landlock](#filesystem-sandboxing-with-landlock) - [Drop Privileges and Capabilities](#drop-privileges-and-capabilities) -- [Miri: Detect Unsafe Code Issues](#miri-detect-unsafe-code-issues) +- [Miri: Detect Unsafe Code Issues](#miri-detect-unsafe-code-issues) - [Graceful Shutdown Handling](#graceful-shutdown-handling) - [Circuit Breakers for External Dependencies](#circuit-breakers-for-external-dependencies) - [Resource Limits](#resource-limits) @@ -110,9 +110,9 @@ Another difference is between thread-level failures and process-level crashes. A common misunderstanding is that `panic` terminates the entire program, but in a multi-threaded application, that is not necessarily the case. For example, a background worker thread can panic while the main thread continues running. -What sounds like a benefit can leave the system in a partially degraded state. +What sounds like a benefit can leave the system in a partially degraded state. -This distinction becomes especially important in long-running systems (servers, workers, async runtimes,...). +This distinction becomes especially important in long-running systems (servers, workers, async runtimes, ...). A panic in a request-handling thread might only abort that one request, while the rest of the service remains available. Here's a small example using scoped threads ([Playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=309325417605cdb3cdd42e1b3e1a618d)): @@ -169,7 +169,7 @@ But if it signals a global invariant violation, continuing execution can be outr Panic behavior is **part of your system's failure model**. Treating all panics as equivalent hides important distinctions and leads to fragile assumptions. -Be explicit about whether a failure may take down a single task, a single thread, or the entire process. +Be explicit about whether a failure may take down a single task, a single thread, or the entire process. Never panic in an uncontrolled manner. @@ -179,9 +179,9 @@ Now that you understand how panics work, let's talk about operational hardening. When things go wrong, you want to know about it. But by default, Rust panics just print to `stderr` and disappear into the void. -In production systems, that's not so great. +In production systems, that's not so great. -You might prefer crash reporting, and/or centralized failure handling, and that's where panic hooks come in. +You might prefer crash reporting or centralized failure handling, and that's where panic hooks come in. A panic hook is a function that gets called whenever a panic occurs, giving you a chance to record the failure before the program terminates or unwinds. It will not make an invalid state safe again. Its job is to capture enough context to debug the failure, alert someone, and shut down cleanly when possible. @@ -241,7 +241,7 @@ fn setup(&self, _cfg: &mut ClientOptions) { ``` Sentry's panic hook: -- Logs the panic information +- Logs the panic information - Preserves the previous panic hook behavior by calling `next(info)` - Ensures the hook is only set once using `INIT.call_once` @@ -290,7 +290,7 @@ pub struct Customer { ### Cleanup Operations -Before the process terminates, you might want to flush logs, close network connections, or notify other systems that this instance is going down. +Before the process terminates, you might want to flush logs, close network connections, or notify other systems that this instance is going down. Setting a hook is a great way to perform such cleanup operations. {% info(title="Panic Hooks Run in a Compromised Environment" icon="warning") %} @@ -306,7 +306,7 @@ If your program aborts on panic, or if the panic is caused by a stack overflow o Never rely on panic hooks for correctness. -They're purely for observability and graceful degradation; don't try to recover from logic errors as it is very hard to rely on a system's fragile underpinnings at this stage. +They're purely for observability and graceful degradation; don't try to recover from logic errors as it is very hard to rely on a system's fragile underpinnings at this stage. ## Stack Overflows And Runtime Behavior @@ -331,7 +331,7 @@ The problem is that recursion can quickly exhaust stack space. If you allow users to call this function with large inputs, it might crash your program. [Rust does not guarantee tail-call optimization on stable Rust](https://weitzel.dev/blog/rustlang-trampoline/). Some compilers and languages can turn certain tail-recursive functions into loops, but you should not rely on that transformation in Rust. If recursion depth depends on user input or external data, rewrite the algorithm iteratively or put an explicit bound on the depth. -It requires some experience, but for recursive algorithms where you're not in control of the input size, it's often safer to use an iterative approach: +It takes some experience, but for recursive algorithms where you're not in control of the input size, it's often safer to use an iterative approach: ```rust fn factorial(n: u64) -> u64 { @@ -353,7 +353,7 @@ In many ways, you're shipping a different program than the one you tested. The most obvious difference is integer overflow behavior. Debug builds panic on overflow, while release builds silently wrap around. We covered that in [Pitfalls of Safe Rust](/blog/pitfalls-of-safe-rust/). -But the differences run deeper than arithmetics. +But the differences run deeper than arithmetic. Release builds remove `debug_assert!` checks, enable optimizations, and may exercise different code paths behind `cfg(debug_assertions)`. Unsafe code and FFI boundaries are especially sensitive to this: undefined behavior can appear harmless in debug mode and break only once the optimizer starts relying on Rust's aliasing and validity rules. Here is a trivial example: @@ -369,7 +369,7 @@ In a debug build, `apply_discount(100, 150)` trips the `debug_assert!`. In a release build, the assertion is gone. The subtraction can underflow and wrap around, turning an invalid discount into a huge number. If the check protects a real runtime invariant, use `assert!` or return a `Result` instead of relying on `debug_assert!`. -### Testing Release Behavior +### Testing Release Behavior The fact that tests pass in debug mode does not prove that production behavior is correct. Run normal debug tests as the fast default, and add release-mode tests for critical integration tests, arithmetic-heavy code, unsafe or FFI-heavy code, and anything whose behavior depends on optimization or release-only configuration. @@ -388,7 +388,7 @@ It's recommended to run those as part of CI. ![cargo-audit run](cargo-audit.jpg) -## Secure Allocations With mimalloc +## Secure Allocations With mimalloc [mimalloc] is a drop-in global allocator built by Microsoft. What's special about it is that it also has a **secure mode**, which adds mitigations like guard pages, randomized allocation, and encrypted free lists to make some heap-corruption bugs harder to exploit. [^mimalloc_safe] @@ -428,13 +428,13 @@ Now, how you do that depends on your deployment environment, but generally peopl A minimal production image contains exactly what you put in it. Even if your service is compromised, the attacker has very limited tools at their disposal to do further damage. -My recommendation is [Google's distroless images](https://github.com/GoogleContainerTools/distroless), but I recommend that you do your own research[^distroless] as I'm not an expert on this. +My recommendation is [Google's distroless images](https://github.com/GoogleContainerTools/distroless), but please do your own research[^distroless] as I'm not an expert on this. [^distroless]: Data sources I found useful for this topic include [this post](https://www.minimus.io/post/best-distroless-image-alternatives-2026) and [this comparison](https://safeguard.sh/resources/blog/distroless-vs-chainguard-vs-wolfi-base-images). Distroless images are minimal Debian-based images stripped of everything unnecessary, while still including TLS certificates and a non-root user. For a typical Rust web service, start with `gcr.io/distroless/cc-debian13:nonroot`: it includes the C runtime libraries that a normal Debian-built Rust binary may dynamically link against, but no shell or package manager. -(Check the latest version in the [distroless README](https://github.com/googlecontainertools/distroless)) +(Check the latest version in the [distroless README](https://github.com/googlecontainertools/distroless).) Here is an example Dockerfile using [`cargo-chef`](https://github.com/LukeMathWalker/cargo-chef) for dependency caching: @@ -463,9 +463,9 @@ COPY --from=builder /app/target/release/myapp /bin/myapp ENTRYPOINT ["/bin/myapp"] ``` -Take this Dockerfile as a starting point, but please adapt it to your own project requirements. +Take this Dockerfile as a starting point, but please adapt it to your own project requirements. -`cargo-chef` keeps dependency builds in a separate Docker layer, so changing your application code does not force all dependencies to rebuild. The important details are: use the same Rust version in all build stages, build with `--locked`, scope workspace builds with `--bin` when appropriate, and keep `target/`, `.git/`, and editor files out of the build context via `.dockerignore`. For a deep-dive on Docker images and build-time optimization, see [Tips For Faster CI Builds](/blog/tips-for-faster-ci-builds). +`cargo-chef` keeps dependency builds in a separate Docker layer, so changing your application code does not force all dependencies to rebuild. The important details are: use the same Rust version in all build stages, build with `--locked`, scope workspace builds with `--bin` when appropriate, and keep `target/`, `.git/`, and editor files out of the build context via `.dockerignore`. For a deep dive on Docker images and build-time optimization, see [Tips For Faster CI Builds](/blog/tips-for-faster-ci-builds). Keep the Debian suffix explicit instead of using the unversioned tag, and pin by digest if reproducible deploys matter to you. If you deliberately build a fully static musl binary, then `gcr.io/distroless/static-debian13:nonroot` or even `scratch` can be a better fit. But don't mix the two approaches: a glibc-linked binary needs a runtime image that provides the libraries it links against. @@ -533,7 +533,7 @@ fn main() { Call `sandbox()` as early as possible in `main`, before spawning threads or accepting connections. The restrictions apply to the entire process from that point forward. -The two approaches really go hand in hand: +The two approaches really go hand in hand: - minimal images limit what's *in* the container - Landlock limits what the process can *touch* at runtime. @@ -551,7 +551,7 @@ The details vary by platform and orchestrator, so treat Linux containers as one For systemd services, Kubernetes, FreeBSD jails, macOS sandboxing, or Windows services, look up the equivalent least-privilege and sandboxing features for that environment. The big picture is that security hardening is about reducing the surface of things that can go wrong. -Every capability your process holds unnecessarily is a liability and everything your code manages that could be delegated to the OS, init system, or container runtime probably should be. +Every capability your process holds unnecessarily is a liability and everything your code manages that could be delegated to the OS, init system, or container runtime probably should be. ## Miri: Detect Unsafe Code Issues @@ -559,8 +559,8 @@ Every capability your process holds unnecessarily is a liability and everything It works by executing your Rust code in a special environment that tracks memory accesses, pointer validity, and other low-level details to catch issues that the compiler can't statically guarantee against. -More people should know about Miri, because it is really helpful for tricky to detect race conditions in multi-threaded or async code; but it can do way more than that, of course. -It detected a lot of [real-world bugs](https://github.com/rust-lang/miri?tab=readme-ov-file#bugs-found-by-miri) already, even in the standard library. +More people should know about Miri, because it is really helpful for hard-to-detect race conditions in multi-threaded or async code; but it can do way more than that, of course. +It has already detected a lot of [real-world bugs](https://github.com/rust-lang/miri?tab=readme-ov-file#bugs-found-by-miri), even in the standard library. Using it is as simple as running: @@ -600,7 +600,7 @@ Instead, it shuts down gracefully when asked. Aim to finish in-flight requests, flush your buffers, and release resources cleanly before you exit. The pattern is: listen for shutdown signals, stop accepting new work, drain existing work, then exit. -Framework like Axum have [built-in support for graceful shutdown](https://github.com/tokio-rs/axum/blob/main/examples/tls-graceful-shutdown/src/main.rs). Use it! +Frameworks like Axum have [built-in support for graceful shutdown](https://github.com/tokio-rs/axum/blob/main/examples/tls-graceful-shutdown/src/main.rs). Use it! The key is handling signals like `SIGTERM` (sent by Kubernetes, systemd, or `docker stop`) and `SIGINT` (Ctrl+C). Here's a minimal example using [tokio-graceful-shutdown](https://crates.io/crates/tokio-graceful-shutdown), which is a crate that provides good signal handling without much boilerplate. @@ -640,7 +640,7 @@ or the more actively maintained [`recloser`](https://crates.io/crates/recloser), ## Resource Limits Unbounded resources are a common source of runtime failures. -Everybody who was oncall for a production service will tell you this. +Everybody who was on call for a production service will tell you this. **Set explicit limits on everything**. SREs will thank you for it! @@ -654,7 +654,7 @@ Common things you should limit include: - queue depth for background jobs - thread count and DB connection pool size -Here are some examples on how to do these in practice: +Here are some examples of how to do this in practice: ### Request body size limits @@ -744,7 +744,7 @@ async fn readiness( ) -> Json { let db_ok = db.ping().await.is_ok(); let cache_ok = cache.ping().await.is_ok(); - + let status = match (db_ok, cache_ok) { (true, true) => Status::Healthy, (false, false) => Status::Unhealthy, @@ -764,7 +764,7 @@ let app = Router::new() .route("/health/ready", get(readiness)); ``` -What's neat about it is that this maps directly to Kubernetes' health check system: +What's neat about it is that this maps directly to Kubernetes' health check system: ```yaml livenessProbe: From f0fd0165bf9bef8b672f7b034545a1e6ee6e2da0 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 21 Jul 2026 18:09:54 +0200 Subject: [PATCH 28/28] update links --- content/blog/bugs-rust-wont-catch/index.md | 2 +- content/blog/defensive-programming/index.md | 2 +- content/blog/pitfalls-of-safe-rust/index.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/content/blog/bugs-rust-wont-catch/index.md b/content/blog/bugs-rust-wont-catch/index.md index 1685730e..a504aff6 100644 --- a/content/blog/bugs-rust-wont-catch/index.md +++ b/content/blog/bugs-rust-wont-catch/index.md @@ -10,7 +10,7 @@ resources = [ "[An update on rust-coreutils](https://discourse.ubuntu.com/t/an-update-on-rust-coreutils/80773): Canonical's announcement of the audit results", "[Patterns for Defensive Programming in Rust](/blog/defensive-programming/): companion post on writing more robust Rust code", "[Pitfalls of Safe Rust](/blog/pitfalls-of-safe-rust/): common mistakes even safe Rust code can make", - "[Hardening Rust Code Against Runtime Failures](/blog/hardening-rust/): runtime failure modes and production hardening", + "[Hardening Rust Code For Production](/blog/hardening-rust/): runtime failure modes and production hardening", "[Sharp Edges In The Rust Standard Library](/blog/sharp-edges-in-rust-std/): surprising behaviors in `std`", "[Rust Prevents Data Races, Not Race Conditions](/blog/rust-prevents-data-races-not-race-conditions/): where Rust's concurrency safety ends", "[uutils/coreutils on GitHub](https://github.com/uutils/coreutils): the Rust reimplementation of GNU coreutils", diff --git a/content/blog/defensive-programming/index.md b/content/blog/defensive-programming/index.md index bcb699a0..f3ba13bc 100644 --- a/content/blog/defensive-programming/index.md +++ b/content/blog/defensive-programming/index.md @@ -12,7 +12,7 @@ reviews = [ resources = [ "[Making illegal states unrepresentable](/blog/illegal-state/)", "[Compile-time invariants in Rust](/blog/compile-time-invariants/)", - "[Hardening Rust Code Against Runtime Failures](/blog/hardening-rust/) -- what can still go wrong at runtime and how to prepare for it", + "[Hardening Rust Code For Production](/blog/hardening-rust/) -- what can still go wrong at runtime and how to prepare for it", ] +++ diff --git a/content/blog/pitfalls-of-safe-rust/index.md b/content/blog/pitfalls-of-safe-rust/index.md index bc64f0bb..3c6fdf00 100644 --- a/content/blog/pitfalls-of-safe-rust/index.md +++ b/content/blog/pitfalls-of-safe-rust/index.md @@ -14,7 +14,7 @@ reviews = [ resources = [ "[The Four Horsemen of Bad Rust Code](https://github.com/corrode/four-horsemen-talk) -- My talk at FOSDEM 2024", "[High Assurance Rust](https://highassurance.rs/) -- developing secure and robust software with Rust", - "[Hardening Rust Code Against Runtime Failures](/blog/hardening-rust/) -- production failures that safe Rust alone cannot prevent" + "[Hardening Rust Code For Production](/blog/hardening-rust/) -- production failures that safe Rust alone cannot prevent" ] +++