Skip to content

Commit 8d785d1

Browse files
authored
zeroize: rustdoc improvements (#1491)
Explains when to use `Zeroize`, tries to get the documentation up-to-date with the current state of the implementation, and adds documentation for new features
1 parent d844f36 commit 8d785d1

2 files changed

Lines changed: 49 additions & 40 deletions

File tree

zeroize/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ Requires Rust **1.85** or newer.
4242
In the future, we reserve the right to change MSRV (i.e. MSRV is out-of-scope for this crate's
4343
semantic versioning guarantees).
4444

45-
## Usage
45+
## Example
4646

4747
```
4848
use zeroize::Zeroize;

zeroize/src/lib.rs

Lines changed: 48 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,33 @@
77
)]
88
#![allow(clippy::undocumented_unsafe_blocks, reason = "TODO")]
99

10-
//! ## Supported types
10+
//! ## Usage
11+
//!
12+
//! ### Traits
13+
//!
14+
//! The [`Zeroize`] trait is the core API of this crate. It's intended to be impl'd on values that
15+
//! may-or-may-not contain secrets, for example the `zeroize` crate itself defines them on the
16+
//! core integers e.g. `u8`, `u16`, `i8`, `i16`, as well as arrays thereof. Its core API is
17+
//! [`Zeroize::zeroize`], a method which takes `&mut self` and writes over the type's internal
18+
//! memory with some placeholder value, typically some form of `0`.
19+
//!
20+
//! The [`DefaultIsZeroes`] marker trait can be impl'd on types which have a [`Default`] impl that
21+
//! can be used for [`Zeroize`]. Types which implement this trait receive a blanket impl of the
22+
//! [`Zeroize`] trait.
23+
//!
24+
//! We recommend that types which always contain secrets, and especially ones which need to maintain
25+
//! complex invariants, do NOT impl the [`Zeroize`] trait, but instead provide a [`Drop`] impl which
26+
//! takes care of erasing the secret values from memory directly. Such types can mark that they're
27+
//! doing this with the [`ZeroizeOnDrop`] marker trait. Note that [`ZeroizeOnDrop`] is *just* a
28+
//! marker trait, and making it actually work requires actually providing a [`Drop`] impl which
29+
//! takes care of zeroizing secrets.
30+
//!
31+
//! Why not impl [`Zeroize`] for such types, e.g. a `SecretKey` type? The problem is [`Zeroize`]
32+
//! would effectively leave such types in an invalid state, and a sort of use-after-zeroize
33+
//! condition becomes possible. For that reason, we recommend these types automatically handle
34+
//! zeroization in their [`Drop`] handler alone.
35+
//!
36+
//! ### Supported types
1137
//!
1238
//! The [`Zeroize`] trait is impl'd on all of Rust's core scalar types including
1339
//! integers, floats, `bool`, and `char`.
@@ -31,7 +57,7 @@
3157
//! impl [`Default`], which implements [`Zeroize`] by overwriting a value with
3258
//! the default value.
3359
//!
34-
//! ## Custom Derive Support
60+
//! ### Custom Derive Support
3561
//!
3662
//! This crate has custom derive support for the `Zeroize` trait,
3763
//! gated under the `zeroize` crate's `zeroize_derive` Cargo feature,
@@ -92,7 +118,7 @@
92118
//! # }
93119
//! ```
94120
//!
95-
//! ## `Zeroizing<Z>`: wrapper for zeroizing arbitrary values on drop
121+
//! ### `Zeroizing<Z>`: wrapper for zeroizing arbitrary values on drop
96122
//!
97123
//! `Zeroizing<Z: Zeroize>` is a generic wrapper type that impls `Deref`
98124
//! and `DerefMut`, allowing access to an inner value of type `Z`, and also
@@ -117,26 +143,14 @@
117143
//!
118144
//! ## What guarantees does this crate provide?
119145
//!
120-
//! This crate guarantees the following:
146+
//! This crate guarantees the zeroing operation can't be "optimized away" by the compiler, as
147+
//! ensured by LLVM's volatile semantics.
121148
//!
122-
//! 1. The zeroing operation can't be "optimized away" by the compiler.
123-
//! 2. All subsequent reads to memory will see "zeroized" values.
124-
//!
125-
//! LLVM's volatile semantics ensure #1 is true.
126-
//!
127-
//! Additionally, thanks to work by the [Unsafe Code Guidelines Working Group],
128-
//! we can now fairly confidently say #2 is true as well. Previously there were
129-
//! worries that the approach used by this crate (mixing volatile and
149+
//! Previously there were worries that the approach used by this crate (mixing volatile and
130150
//! non-volatile accesses) was undefined behavior due to language contained
131151
//! in the documentation for `write_volatile`, however after some discussion
132-
//! [these remarks have been removed] and the specific usage pattern in this
133-
//! crate is considered to be well-defined.
134-
//!
135-
//! Additionally this crate leverages [`core::sync::atomic::compiler_fence`]
136-
//! with the strictest ordering
137-
//! ([`Ordering::SeqCst`]) as a
138-
//! precaution to help ensure reads are not reordered before memory has been
139-
//! zeroed.
152+
//! within the [Unsafe Code Guidelines Working Group], [these remarks have been removed] and the
153+
//! specific usage pattern in this crate is considered to be well-defined.
140154
//!
141155
//! All of that said, there is still potential for microarchitectural attacks
142156
//! (ala Spectre/Meltdown) to leak "zeroized" secrets through covert channels.
@@ -145,30 +159,27 @@
145159
//!
146160
//! ## Stack/Heap Zeroing Notes
147161
//!
148-
//! This crate can be used to zero values from either the stack or the heap.
162+
//! This crate can be used to zero values from either the stack or the heap. We recommend storing
163+
//! sensitive data on the heap whenever possible to reduce the potential for making copies in memory
164+
//! via Rust move semantics, however note that stack spilling and other optimizations may leave
165+
//! temporary copies of data from the heap on the stack.
149166
//!
150-
//! However, be aware several operations in Rust can unintentionally leave
151-
//! copies of data in memory. This includes but is not limited to:
167+
//! [`zeroize_stack`] can be used to zeroize stack memory.
152168
//!
153-
//! - Moves and [`Copy`]
154-
//! - Heap reallocation when using [`Vec`] and [`String`]
155-
//! - Borrowers of a reference making copies of the data
169+
//! [`Pin`][`core::pin::Pin`] can be leveraged in conjunction with this crate to ensure data kept
170+
//! on the stack isn't moved.
156171
//!
157-
//! [`Pin`][`core::pin::Pin`] can be leveraged in conjunction with this crate
158-
//! to ensure data kept on the stack isn't moved.
172+
//! The `Zeroize` impls for `Vec`, `String` and `CString` zeroize the entire capacity of their
173+
//! backing buffer, but cannot guarantee copies of the data were not previously made by buffer
174+
//! reallocation. It's therefore important when attempting to zeroize such buffers to initialize
175+
//! them to the correct capacity, and take care to prevent subsequent reallocation.
159176
//!
160-
//! The `Zeroize` impls for `Vec`, `String` and `CString` zeroize the entire
161-
//! capacity of their backing buffer, but cannot guarantee copies of the data
162-
//! were not previously made by buffer reallocation. It's therefore important
163-
//! when attempting to zeroize such buffers to initialize them to the correct
164-
//! capacity, and take care to prevent subsequent reallocation.
165-
//!
166-
//! The `secrecy` crate provides higher-level abstractions for eliminating
177+
//! The [`secrecy`] crate provides higher-level abstractions for eliminating
167178
//! usage patterns which can cause reallocations:
168179
//!
169-
//! <https://crates.io/crates/secrecy>
180+
//! [`secrecy`]: https://docs.rs/secrecy
170181
//!
171-
//! ## What about: clearing registers, mlock, mprotect, etc?
182+
//! ## What about: clearing registers, `mlock()`, `mprotect()`, etc?
172183
//!
173184
//! This crate is focused on providing simple, unobtrusive support for reliably
174185
//! zeroing memory using the best approach possible on stable Rust.
@@ -189,15 +200,13 @@
189200
//! `unsafe` memory protection systems and just trying to make the best memory
190201
//! zeroing crate available.
191202
//!
192-
//! [Zeroing memory securely is hard]: http://www.daemonology.net/blog/2014-09-04-how-to-zero-a-buffer.html
193203
//! [Unsafe Code Guidelines Working Group]: https://github.com/rust-lang/unsafe-code-guidelines
194204
//! [these remarks have been removed]: https://github.com/rust-lang/rust/pull/60972
195205
//! [good cryptographic hygiene]: https://github.com/veorq/cryptocoding#clean-memory-of-secret-data
196206
//! [`Ordering::SeqCst`]: core::sync::atomic::Ordering::SeqCst
197207
198208
#[cfg(feature = "alloc")]
199209
extern crate alloc;
200-
201210
#[cfg(feature = "std")]
202211
extern crate std;
203212

0 commit comments

Comments
 (0)