├── .github └── workflows │ └── ci.yml ├── .gitignore ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── benches ├── clear_on_drop.rs └── clear_stack_on_return.rs ├── build.rs └── src ├── clear.rs ├── clear_on_drop.rs ├── clear_stack_on_return.rs ├── fnoption.rs ├── hide.c ├── hide.rs └── lib.rs /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: tests 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | tests: 7 | name: Run tests 8 | runs-on: ${{ matrix.runner }} 9 | strategy: 10 | matrix: 11 | runner: [ubuntu-latest, windows-latest, macos-latest] 12 | toolchain: [1.34.2, 1.59.0, stable, beta, nightly] 13 | steps: 14 | - uses: actions/checkout@v3 15 | - uses: actions-rs/toolchain@v1 16 | with: 17 | profile: minimal 18 | toolchain: ${{ matrix.toolchain }} 19 | override: true 20 | - uses: actions/cache@v3 21 | with: 22 | path: | 23 | ~/.cargo/registry/index/ 24 | ~/.cargo/registry/cache/ 25 | ~/.cargo/git/db/ 26 | target/ 27 | key: ${{ runner.os }}-cargo-${{ matrix.toolchain }}-${{ hashFiles('**/Cargo.lock', '**/Cargo.toml') }} 28 | - run: cargo build --verbose 29 | - run: cargo build --verbose --features=no_cc 30 | - run: cargo build --verbose --features=nightly 31 | if: ${{ matrix.toolchain == 'nightly' }} 32 | - run: cargo build --verbose --release 33 | - run: cargo build --verbose --release --features=no_cc 34 | - run: cargo build --verbose --release --features=nightly 35 | if: ${{ matrix.toolchain == 'nightly' }} 36 | - run: cargo test --verbose 37 | if: ${{ matrix.toolchain != '1.34.2' }} 38 | - run: cargo test --verbose --features=no_cc 39 | if: ${{ matrix.toolchain != '1.34.2' }} 40 | - run: cargo test --verbose --features=nightly 41 | if: ${{ matrix.toolchain == 'nightly' }} 42 | - run: cargo test --verbose --release 43 | if: ${{ matrix.toolchain != '1.34.2' }} 44 | - run: cargo test --verbose --release --features=no_cc 45 | if: ${{ matrix.toolchain != '1.34.2' }} 46 | - run: cargo test --verbose --release --features=nightly 47 | if: ${{ matrix.toolchain == 'nightly' }} 48 | - run: cargo bench --verbose 49 | if: ${{ matrix.toolchain != '1.34.2' }} 50 | - run: cargo bench --verbose --features=no_cc 51 | if: ${{ matrix.toolchain != '1.34.2' }} 52 | - run: cargo bench --verbose --features=nightly 53 | if: ${{ matrix.toolchain == 'nightly' }} 54 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "clear_on_drop" 3 | version = "0.2.5" 4 | edition = "2018" 5 | authors = ["Cesar Eduardo Barros "] 6 | description = "Helpers for clearing sensitive data on the stack and heap" 7 | documentation = "https://docs.rs/clear_on_drop" 8 | repository = "https://github.com/cesarb/clear_on_drop" 9 | readme = "README.md" 10 | keywords = ["clear_on_drop", "clear_stack", "zeroize"] 11 | categories = ["cryptography", "no-std"] 12 | license = "MIT OR Apache-2.0" 13 | 14 | build = "build.rs" 15 | 16 | [features] 17 | no_cc = [] 18 | nightly = ["no_cc"] 19 | 20 | [build-dependencies] 21 | cc = "1.0" 22 | 23 | [dependencies] 24 | 25 | [dev-dependencies] 26 | criterion = { version = "0.3", features = ["cargo_bench_support", "html_reports"] } 27 | 28 | [[bench]] 29 | name = "clear_on_drop" 30 | harness = false 31 | 32 | [[bench]] 33 | name = "clear_stack_on_return" 34 | harness = false 35 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 The clear_on_drop Developers 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Helpers for clearing sensitive data on the stack and heap 2 | 3 | Some kinds of data should not be kept in memory any longer than 4 | they are needed. For instance, cryptographic keys and intermediate 5 | values should be erased as soon as they are no longer needed. 6 | 7 | The Rust language helps prevent the accidental reading of leftover 8 | values on the stack or the heap; however, means outside the program 9 | (for instance a debugger, or even physical access to the hardware) 10 | can still read the leftover values. For long-lived processes, key 11 | material might be found in the memory long after it should have been 12 | discarded. 13 | 14 | This crate provides two mechanisms to help minimize leftover data. 15 | 16 | The `ClearOnDrop` wrapper holds a mutable reference to sensitive 17 | data (for instance, a cipher state), and clears the data when 18 | dropped. While the mutable reference is held, the data cannot be 19 | moved, so there won't be leftovers due to moves; the wrapper itself 20 | can be freely moved. Alternatively, it can hold data on the heap 21 | (using a `Box`, or possibly a similar which allocates from a 22 | `mlock`ed heap). 23 | 24 | The `clear_stack_on_return` function calls a closure, and after it 25 | returns, overwrites several kilobytes of the stack. This can help 26 | overwrite temporary variables used by cryptographic algorithms, and 27 | is especially relevant when running on a short-lived thread, since 28 | the memory used for the thread stack cannot be easily overwritten 29 | after the thread terminates. 30 | 31 | ## Preventing compiler optimizations 32 | 33 | If the compiler determines the data is not used after being cleared, 34 | it could elide the clearing code. Aditionally, the compiler could 35 | inline a called function and the stack clearing code, using separate 36 | areas of the stack for each. This crate has three mechanisms which 37 | prevent these unwanted optimizations, selected at compile time via 38 | cargo features. 39 | 40 | The fastest mechanism uses inline assembly, which is only available 41 | on nightly Rust. It is enabled through the `nightly` feature, and 42 | does not need a working C compiler. 43 | 44 | The second mechanism, which is the default, uses a call to a dummy 45 | C function. It works on stable Rust, but needs a working C compiler. 46 | 47 | The third mechanism is a fallback, which attempts to confuse the 48 | optimizer through the use of atomic instructions. It should not be 49 | used unless necessary, since it's less reliable. It is enabled by 50 | the `no_cc` feature, works on stable Rust, and does not need a C 51 | compiler. 52 | 53 | ## License 54 | 55 | Licensed under either of 56 | 57 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 58 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 59 | 60 | at your option. 61 | 62 | ### Contribution 63 | 64 | Unless you explicitly state otherwise, any contribution intentionally 65 | submitted for inclusion in the work by you, as defined in the Apache-2.0 66 | license, shall be dual licensed as above, without any additional terms or 67 | conditions. 68 | -------------------------------------------------------------------------------- /benches/clear_on_drop.rs: -------------------------------------------------------------------------------- 1 | use criterion::{criterion_group, criterion_main, Criterion}; 2 | 3 | use clear_on_drop::ClearOnDrop; 4 | 5 | fn clear_on_drop_small(c: &mut Criterion) { 6 | #[derive(Default)] 7 | struct Data { 8 | _data: u64, 9 | } 10 | 11 | let mut place = Data::default(); 12 | c.bench_function("clear_on_drop_small", |b| { 13 | b.iter(|| { 14 | ClearOnDrop::new(&mut place); 15 | }) 16 | }); 17 | } 18 | 19 | fn clear_on_drop_medium(c: &mut Criterion) { 20 | #[derive(Default)] 21 | struct Data { 22 | _data: [u64; 32], 23 | } 24 | 25 | let mut place = Data::default(); 26 | c.bench_function("clear_on_drop_medium", |b| { 27 | b.iter(|| { 28 | ClearOnDrop::new(&mut place); 29 | }) 30 | }); 31 | } 32 | 33 | fn clear_on_drop_large(c: &mut Criterion) { 34 | #[derive(Default)] 35 | struct Data { 36 | _data: [[u64; 32]; 32], 37 | } 38 | 39 | let mut place = Data::default(); 40 | c.bench_function("clear_on_drop_large", |b| { 41 | b.iter(|| { 42 | ClearOnDrop::new(&mut place); 43 | }) 44 | }); 45 | } 46 | 47 | criterion_group!( 48 | benches, 49 | clear_on_drop_small, 50 | clear_on_drop_medium, 51 | clear_on_drop_large 52 | ); 53 | criterion_main!(benches); 54 | -------------------------------------------------------------------------------- /benches/clear_stack_on_return.rs: -------------------------------------------------------------------------------- 1 | use criterion::{criterion_group, criterion_main, Criterion}; 2 | 3 | use clear_on_drop::{clear_stack_on_return, clear_stack_on_return_fnonce}; 4 | 5 | fn clear_stack_on_return_tiny(c: &mut Criterion) { 6 | c.bench_function("clear_stack_on_return_tiny", |b| { 7 | b.iter(|| clear_stack_on_return(1, || 0x41)) 8 | }); 9 | } 10 | 11 | fn clear_stack_on_return_small(c: &mut Criterion) { 12 | c.bench_function("clear_stack_on_return_small", |b| { 13 | b.iter(|| clear_stack_on_return(2, || 0x41)) 14 | }); 15 | } 16 | 17 | fn clear_stack_on_return_fnonce_tiny(c: &mut Criterion) { 18 | c.bench_function("clear_stack_on_return_fnonce_tiny", |b| { 19 | b.iter(|| clear_stack_on_return_fnonce(1, || 0x41)) 20 | }); 21 | } 22 | 23 | fn clear_stack_on_return_fnonce_small(c: &mut Criterion) { 24 | c.bench_function("clear_stack_on_return_fnonce_small", |b| { 25 | b.iter(|| clear_stack_on_return_fnonce(2, || 0x41)) 26 | }); 27 | } 28 | 29 | criterion_group!( 30 | benches, 31 | clear_stack_on_return_tiny, 32 | clear_stack_on_return_small, 33 | clear_stack_on_return_fnonce_tiny, 34 | clear_stack_on_return_fnonce_small 35 | ); 36 | criterion_main!(benches); 37 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | if !cfg!(feature = "no_cc") { 3 | cc::Build::new() 4 | .file("src/hide.c") 5 | .compile("clear_on_drop"); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/clear.rs: -------------------------------------------------------------------------------- 1 | //! Traits to completely overwrite a value, without leaking data. 2 | //! 3 | //! # Examples 4 | //! 5 | //! Basic use: 6 | //! 7 | //! ``` 8 | //! # use clear_on_drop::clear::Clear; 9 | //! #[derive(Default)] 10 | //! struct MyData { 11 | //! value: u32, 12 | //! } 13 | //! 14 | //! let mut place = MyData { value: 0x01234567 }; 15 | //! place.clear(); 16 | //! assert_eq!(place.value, 0); 17 | //! ``` 18 | //! 19 | //! Showing no data is leaked: 20 | //! 21 | //! ``` 22 | //! # use std::mem; 23 | //! # use std::slice; 24 | //! # use clear_on_drop::clear::Clear; 25 | //! #[derive(Default)] 26 | //! struct MyData { 27 | //! value: Option, 28 | //! } 29 | //! 30 | //! let mut place = MyData { value: Some(0x41414141) }; 31 | //! place.clear(); 32 | //! assert_eq!(place.value, None); 33 | //! 34 | //! fn as_bytes(x: &T) -> &[u8] { 35 | //! unsafe { 36 | //! slice::from_raw_parts(x as *const T as *const u8, mem::size_of_val(x)) 37 | //! } 38 | //! } 39 | //! assert!(!as_bytes(&place).contains(&0x41)); 40 | //! ``` 41 | 42 | use core::mem; 43 | use core::ptr; 44 | 45 | use crate::hide::hide_mem_impl; 46 | 47 | /// An operation to completely overwrite a value, without leaking data. 48 | /// 49 | /// Do not implement this trait; implement `InitializableFromZeroed` 50 | /// instead. This trait's blanket implementation uses several tricks to 51 | /// make sure no data is leaked. 52 | pub trait Clear { 53 | /// Completely overwrites this value. 54 | fn clear(&mut self); 55 | } 56 | 57 | impl Clear for T 58 | where 59 | T: InitializableFromZeroed, 60 | { 61 | #[inline] 62 | fn clear(&mut self) { 63 | let size = mem::size_of_val(self); 64 | unsafe { 65 | let ptr = self as *mut Self; 66 | ptr::drop_in_place(ptr); 67 | ptr::write_bytes(ptr as *mut u8, 0, size); 68 | hide_mem_impl::(ptr); 69 | Self::initialize(ptr); 70 | } 71 | } 72 | } 73 | 74 | /// A type that can be initialized to a valid value, after being set to 75 | /// all-bits-zero. 76 | pub trait InitializableFromZeroed { 77 | /// Called to initialize a place to a valid value, after it is set 78 | /// to all-bits-zero. 79 | /// 80 | /// If all-bits-zero is a valid value for a place, this method can 81 | /// be left empty. 82 | unsafe fn initialize(place: *mut Self); 83 | } 84 | 85 | impl InitializableFromZeroed for T 86 | where 87 | T: Default, 88 | { 89 | #[inline] 90 | unsafe fn initialize(place: *mut Self) { 91 | ptr::write(place, Default::default()); 92 | } 93 | } 94 | 95 | impl InitializableFromZeroed for [T] 96 | where 97 | T: ZeroSafe, 98 | { 99 | #[inline] 100 | unsafe fn initialize(_place: *mut Self) {} 101 | } 102 | 103 | impl InitializableFromZeroed for str { 104 | #[inline] 105 | unsafe fn initialize(_place: *mut Self) {} 106 | } 107 | 108 | /// Unsafe trait to indicate which types are safe to set to all-bits-zero. 109 | pub unsafe trait ZeroSafe {} 110 | 111 | // Yes, this is core::nonzero::Zeroable 112 | unsafe impl ZeroSafe for *const T {} 113 | unsafe impl ZeroSafe for *mut T {} 114 | unsafe impl ZeroSafe for isize {} 115 | unsafe impl ZeroSafe for usize {} 116 | unsafe impl ZeroSafe for i8 {} 117 | unsafe impl ZeroSafe for u8 {} 118 | unsafe impl ZeroSafe for i16 {} 119 | unsafe impl ZeroSafe for u16 {} 120 | unsafe impl ZeroSafe for i32 {} 121 | unsafe impl ZeroSafe for u32 {} 122 | unsafe impl ZeroSafe for i64 {} 123 | unsafe impl ZeroSafe for u64 {} 124 | #[cfg(feature = "nightly")] 125 | unsafe impl ZeroSafe for i128 {} 126 | #[cfg(feature = "nightly")] 127 | unsafe impl ZeroSafe for u128 {} 128 | 129 | macro_rules! array_impl_zerosafe { 130 | ($($N:expr)+) => { 131 | $( 132 | unsafe impl ZeroSafe for [T; $N] {} 133 | )+ 134 | } 135 | } 136 | 137 | // Implement for fixed-size arrays of ZeroSafe up to 64 138 | array_impl_zerosafe!{ 139 | 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 140 | 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 141 | 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 142 | 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 143 | 64 144 | } 145 | -------------------------------------------------------------------------------- /src/clear_on_drop.rs: -------------------------------------------------------------------------------- 1 | use core::borrow::{Borrow, BorrowMut}; 2 | use core::cmp::Ordering; 3 | use core::fmt; 4 | use core::hash::{Hash, Hasher}; 5 | use core::mem; 6 | use core::ops::{Deref, DerefMut}; 7 | use core::ptr; 8 | 9 | use crate::clear::Clear; 10 | 11 | /// Zeroizes a storage location when dropped. 12 | /// 13 | /// This struct contains a reference to a memory location, either as a 14 | /// mutable borrow (`&mut T`), or as a owned container (`Box` or 15 | /// similar). When this struct is dropped, the referenced location is 16 | /// overwritten with its `Default` value. 17 | /// 18 | /// # Example 19 | /// 20 | /// ``` 21 | /// # use clear_on_drop::ClearOnDrop; 22 | /// #[derive(Default)] 23 | /// struct MyData { 24 | /// value: u32, 25 | /// } 26 | /// 27 | /// let mut place = MyData { value: 0 }; 28 | /// { 29 | /// let mut key = ClearOnDrop::new(&mut place); 30 | /// key.value = 0x01234567; 31 | /// // ... 32 | /// } // key is dropped here 33 | /// assert_eq!(place.value, 0); 34 | /// ``` 35 | pub struct ClearOnDrop

36 | where 37 | P: DerefMut, 38 | P::Target: Clear, 39 | { 40 | _place: P, 41 | } 42 | 43 | impl

ClearOnDrop

44 | where 45 | P: DerefMut, 46 | P::Target: Clear, 47 | { 48 | /// Creates a new `ClearOnDrop` which clears `place` on drop. 49 | /// 50 | /// The `place` parameter can be a `&mut T`, a `Box`, or other 51 | /// containers which behave like `Box`. 52 | /// 53 | /// Note: only the first level of dereference will be cleared. Do 54 | /// not use `&mut Box` or similar as the place, since the heap 55 | /// contents won't be cleared in that case. If you need the place 56 | /// back, use `ClearOnDrop::into_place(...)` instead of a borrow. 57 | #[inline] 58 | pub fn new(place: P) -> Self { 59 | ClearOnDrop { _place: place } 60 | } 61 | 62 | /// Consumes the `ClearOnDrop`, returning the `place` after clearing. 63 | /// 64 | /// Note: this is an associated function, which means that you have 65 | /// to call it as `ClearOnDrop::into_place(c)` instead of 66 | /// `c.into_place()`. This is so that there is no conflict with a 67 | /// method on the inner type. 68 | #[inline] 69 | pub fn into_place(mut c: Self) -> P { 70 | c.clear(); 71 | Self::into_uncleared_place(c) 72 | } 73 | 74 | /// Consumes the `ClearOnDrop`, returning the `place` without clearing. 75 | /// 76 | /// Note: this is an associated function, which means that you have 77 | /// to call it as `ClearOnDrop::into_uncleared_place(c)` instead of 78 | /// `c.into_uncleared_place()`. This is so that there is no conflict 79 | /// with a method on the inner type. 80 | #[inline] 81 | pub fn into_uncleared_place(c: Self) -> P { 82 | unsafe { 83 | let place = ptr::read(&c._place); 84 | mem::forget(c); 85 | place 86 | } 87 | } 88 | } 89 | 90 | impl

Clone for ClearOnDrop

91 | where 92 | P: DerefMut + Clone, 93 | P::Target: Clear, 94 | { 95 | #[inline] 96 | fn clone(&self) -> Self { 97 | ClearOnDrop { 98 | _place: Clone::clone(&self._place), 99 | } 100 | } 101 | 102 | #[inline] 103 | fn clone_from(&mut self, source: &Self) { 104 | self.clear(); 105 | Clone::clone_from(&mut self._place, &source._place) 106 | } 107 | } 108 | 109 | impl

fmt::Debug for ClearOnDrop

110 | where 111 | P: DerefMut + fmt::Debug, 112 | P::Target: Clear, 113 | { 114 | #[inline] 115 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 116 | fmt::Debug::fmt(&self._place, f) 117 | } 118 | } 119 | 120 | impl

Deref for ClearOnDrop

121 | where 122 | P: DerefMut, 123 | P::Target: Clear, 124 | { 125 | type Target = P::Target; 126 | 127 | #[inline] 128 | fn deref(&self) -> &Self::Target { 129 | Deref::deref(&self._place) 130 | } 131 | } 132 | 133 | impl

DerefMut for ClearOnDrop

134 | where 135 | P: DerefMut, 136 | P::Target: Clear, 137 | { 138 | #[inline] 139 | fn deref_mut(&mut self) -> &mut Self::Target { 140 | DerefMut::deref_mut(&mut self._place) 141 | } 142 | } 143 | 144 | impl

Drop for ClearOnDrop

145 | where 146 | P: DerefMut, 147 | P::Target: Clear, 148 | { 149 | #[inline] 150 | fn drop(&mut self) { 151 | self.clear(); 152 | } 153 | } 154 | 155 | // core::convert traits 156 | 157 | impl AsRef for ClearOnDrop

158 | where 159 | P: DerefMut + AsRef, 160 | P::Target: Clear, 161 | { 162 | #[inline] 163 | fn as_ref(&self) -> &T { 164 | AsRef::as_ref(&self._place) 165 | } 166 | } 167 | 168 | impl AsMut for ClearOnDrop

169 | where 170 | P: DerefMut + AsMut, 171 | P::Target: Clear, 172 | { 173 | #[inline] 174 | fn as_mut(&mut self) -> &mut T { 175 | AsMut::as_mut(&mut self._place) 176 | } 177 | } 178 | 179 | // core::borrow traits 180 | 181 | // The `T: Clear` bound avoids a conflict with the blanket impls 182 | // `impl Borrow for T` and `impl BorrowMut for T`, since 183 | // `ClearOnDrop<_>` is not `Clear`. 184 | 185 | impl Borrow for ClearOnDrop

186 | where 187 | P: DerefMut + Borrow, 188 | P::Target: Clear, 189 | T: Clear, 190 | { 191 | #[inline] 192 | fn borrow(&self) -> &T { 193 | Borrow::borrow(&self._place) 194 | } 195 | } 196 | 197 | impl BorrowMut for ClearOnDrop

198 | where 199 | P: DerefMut + BorrowMut, 200 | P::Target: Clear, 201 | T: Clear, 202 | { 203 | #[inline] 204 | fn borrow_mut(&mut self) -> &mut T { 205 | BorrowMut::borrow_mut(&mut self._place) 206 | } 207 | } 208 | 209 | // core::hash traits 210 | 211 | impl

Hash for ClearOnDrop

212 | where 213 | P: DerefMut + Hash, 214 | P::Target: Clear, 215 | { 216 | #[inline] 217 | fn hash(&self, state: &mut H) { 218 | Hash::hash(&self._place, state) 219 | } 220 | } 221 | 222 | // core::cmp traits 223 | 224 | impl PartialEq> for ClearOnDrop

225 | where 226 | P: DerefMut + PartialEq, 227 | P::Target: Clear, 228 | Q: DerefMut, 229 | Q::Target: Clear, 230 | { 231 | #[inline] 232 | fn eq(&self, other: &ClearOnDrop) -> bool { 233 | PartialEq::eq(&self._place, &other._place) 234 | } 235 | 236 | #[inline] 237 | fn ne(&self, other: &ClearOnDrop) -> bool { 238 | PartialEq::ne(&self._place, &other._place) 239 | } 240 | } 241 | 242 | impl

Eq for ClearOnDrop

243 | where 244 | P: DerefMut + Eq, 245 | P::Target: Clear, 246 | { 247 | } 248 | 249 | impl PartialOrd> for ClearOnDrop

250 | where 251 | P: DerefMut + PartialOrd, 252 | P::Target: Clear, 253 | Q: DerefMut, 254 | Q::Target: Clear, 255 | { 256 | #[inline] 257 | fn partial_cmp(&self, other: &ClearOnDrop) -> Option { 258 | PartialOrd::partial_cmp(&self._place, &other._place) 259 | } 260 | 261 | #[inline] 262 | fn lt(&self, other: &ClearOnDrop) -> bool { 263 | PartialOrd::lt(&self._place, &other._place) 264 | } 265 | 266 | #[inline] 267 | fn le(&self, other: &ClearOnDrop) -> bool { 268 | PartialOrd::le(&self._place, &other._place) 269 | } 270 | 271 | #[inline] 272 | fn gt(&self, other: &ClearOnDrop) -> bool { 273 | PartialOrd::gt(&self._place, &other._place) 274 | } 275 | 276 | #[inline] 277 | fn ge(&self, other: &ClearOnDrop) -> bool { 278 | PartialOrd::ge(&self._place, &other._place) 279 | } 280 | } 281 | 282 | impl

Ord for ClearOnDrop

283 | where 284 | P: DerefMut + Ord, 285 | P::Target: Clear, 286 | { 287 | #[inline] 288 | fn cmp(&self, other: &Self) -> Ordering { 289 | Ord::cmp(&self._place, &other._place) 290 | } 291 | } 292 | 293 | #[cfg(test)] 294 | mod tests { 295 | use super::ClearOnDrop; 296 | 297 | #[derive(Debug, Default)] 298 | struct Place { 299 | data: [u32; 4], 300 | } 301 | 302 | const DATA: [u32; 4] = [0x01234567, 0x89abcdef, 0xfedcba98, 0x76543210]; 303 | 304 | #[test] 305 | fn on_stack() { 306 | let mut place: Place = Default::default(); 307 | { 308 | let mut clear = ClearOnDrop::new(&mut place); 309 | clear.data = DATA; 310 | assert_eq!(clear.data, DATA); 311 | } 312 | assert_eq!(place.data, [0; 4]); 313 | } 314 | 315 | #[test] 316 | fn on_box() { 317 | let place: Box = Box::new(Default::default()); 318 | let mut clear = ClearOnDrop::new(place); 319 | clear.data = DATA; 320 | assert_eq!(clear.data, DATA); 321 | } 322 | 323 | #[test] 324 | fn into_box() { 325 | let place: Box = Box::new(Default::default()); 326 | let mut clear = ClearOnDrop::new(place); 327 | clear.data = DATA; 328 | assert_eq!(clear.data, DATA); 329 | 330 | let place = ClearOnDrop::into_place(clear); 331 | assert_eq!(place.data, [0; 4]); 332 | } 333 | 334 | #[test] 335 | fn into_uncleared_box() { 336 | let place: Box = Box::new(Default::default()); 337 | let mut clear = ClearOnDrop::new(place); 338 | clear.data = DATA; 339 | assert_eq!(clear.data, DATA); 340 | 341 | let place = ClearOnDrop::into_uncleared_place(clear); 342 | assert_eq!(place.data, DATA); 343 | } 344 | 345 | #[test] 346 | fn on_fixed_size_array() { 347 | let mut place: [u32; 4] = Default::default(); 348 | { 349 | let mut clear = ClearOnDrop::new(&mut place); 350 | clear.copy_from_slice(&DATA); 351 | assert_eq!(&clear[..], DATA); 352 | } 353 | assert_eq!(place, [0; 4]); 354 | } 355 | 356 | #[test] 357 | fn on_slice() { 358 | let mut place: [u32; 4] = Default::default(); 359 | { 360 | let mut clear = ClearOnDrop::new(&mut place[..]); 361 | clear.copy_from_slice(&DATA); 362 | assert_eq!(&clear[..], DATA); 363 | } 364 | assert_eq!(place, [0; 4]); 365 | } 366 | 367 | #[test] 368 | fn on_boxed_slice() { 369 | let place: Box<[u32]> = vec![0; 4].into_boxed_slice(); 370 | let mut clear = ClearOnDrop::new(place); 371 | clear.copy_from_slice(&DATA); 372 | assert_eq!(&clear[..], DATA); 373 | } 374 | 375 | #[test] 376 | fn on_str_slice() { 377 | let mut place: Box = "test".into(); 378 | { 379 | let clear = ClearOnDrop::new(&mut place[..]); 380 | assert_eq!(&clear[..], "test"); 381 | } 382 | assert_eq!(&place[..], "\x00\x00\x00\x00"); 383 | } 384 | 385 | #[test] 386 | fn on_string() { 387 | let place: String = "test".into(); 388 | let clear = ClearOnDrop::new(place); 389 | assert_eq!(&clear[..], "test"); 390 | } 391 | 392 | #[test] 393 | fn into_string() { 394 | let place: String = "test".into(); 395 | let ptr = place.as_ptr(); 396 | 397 | let clear = ClearOnDrop::new(place); 398 | assert_eq!(&clear[..], "test"); 399 | 400 | let place = ClearOnDrop::into_place(clear); 401 | assert_eq!(place, "\x00\x00\x00\x00"); 402 | assert_eq!(place.as_ptr(), ptr); 403 | } 404 | 405 | #[test] 406 | fn into_uncleared_string() { 407 | let place: String = "test".into(); 408 | let ptr = place.as_ptr(); 409 | 410 | let clear = ClearOnDrop::new(place); 411 | assert_eq!(&clear[..], "test"); 412 | 413 | let place = ClearOnDrop::into_uncleared_place(clear); 414 | assert_eq!(place, "test"); 415 | assert_eq!(place.as_ptr(), ptr); 416 | } 417 | } 418 | -------------------------------------------------------------------------------- /src/clear_stack_on_return.rs: -------------------------------------------------------------------------------- 1 | use crate::fnoption::FnOption; 2 | use crate::hide::{hide_mem, hide_ptr}; 3 | 4 | /// Calls a closure and overwrites its stack on return. 5 | /// 6 | /// This function calls `clear_stack` after calling the passed closure, 7 | /// taking care to prevent either of them being inlined, so the stack 8 | /// used by the closure will be overwritten with zeros (as long as a 9 | /// large enough number of `pages` is used). 10 | /// 11 | /// For technical reasons, this function can be used only with `Fn` or 12 | /// `FnMut`. If all you have is a `FnOnce`, use the auxiliary function 13 | /// `clear_stack_on_return_fnonce` instead. 14 | /// 15 | /// # Example 16 | /// 17 | /// ``` 18 | /// # use clear_on_drop::clear_stack_on_return; 19 | /// # fn encrypt(input: &[u8]) -> Vec { input.to_owned() } 20 | /// let input = b"abc"; 21 | /// let result = clear_stack_on_return(1, || encrypt(input)); 22 | /// ``` 23 | #[inline] 24 | pub fn clear_stack_on_return(pages: usize, mut f: F) -> R 25 | where 26 | F: FnMut() -> R, 27 | { 28 | let _clear = ClearStackOnDrop { pages }; 29 | // Do not inline f to make sure clear_stack uses the same stack space. 30 | hide_ptr::<&mut dyn FnMut() -> R>(&mut f)() 31 | } 32 | 33 | /// Calls a closure and overwrites its stack on return. 34 | /// 35 | /// This function is a variant of `clear_stack_on_return` which also 36 | /// accepts `FnOnce`, at the cost of being slightly slower. 37 | /// 38 | /// # Example 39 | /// 40 | /// ``` 41 | /// # use clear_on_drop::clear_stack_on_return_fnonce; 42 | /// # fn encrypt(input: Vec) -> Vec { input } 43 | /// let input = vec![97, 98, 99]; 44 | /// let result = clear_stack_on_return_fnonce(1, || encrypt(input)); 45 | /// ``` 46 | #[inline] 47 | pub fn clear_stack_on_return_fnonce(pages: usize, f: F) -> R 48 | where 49 | F: FnOnce() -> R, 50 | { 51 | let mut f = FnOption::new(f); 52 | clear_stack_on_return(pages, || f.call_mut()).unwrap() 53 | } 54 | 55 | struct ClearStackOnDrop { 56 | pages: usize, 57 | } 58 | 59 | impl Drop for ClearStackOnDrop { 60 | #[inline] 61 | fn drop(&mut self) { 62 | // Do not inline clear_stack. 63 | hide_ptr::(clear_stack)(self.pages); 64 | } 65 | } 66 | 67 | /// Overwrites a few pages of stack. 68 | /// 69 | /// This function will overwrite `pages` 4096-byte blocks of the stack 70 | /// with zeros. 71 | pub fn clear_stack(pages: usize) { 72 | if pages > 0 { 73 | let mut buf = [0u8; 4096]; 74 | hide_mem(&mut buf); // prevent moving after recursive call 75 | clear_stack(pages - 1); 76 | hide_mem(&mut buf); // prevent reuse of stack space for call 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/fnoption.rs: -------------------------------------------------------------------------------- 1 | //! Wrap a `FnOnce` into something which behaves like a `FnMut`. 2 | //! 3 | //! To prevent inlining, `clear_stack_on_return` has to hide the passed 4 | //! closure behind a borrow. However, it's not possible to move out of 5 | //! a borrow, so `FnOnce` can't be used. 6 | //! 7 | //! The `FnOption` wraps the `FnOnce` with an `Option`, which can be 8 | //! moved out of. 9 | 10 | /// Wraps a `FnOnce` with an `Option`. 11 | pub struct FnOption R> { 12 | f: Option, 13 | } 14 | 15 | impl FnOption 16 | where 17 | F: FnOnce() -> R, 18 | { 19 | /// Wraps a `FnOnce` with an `Option`. 20 | #[inline] 21 | pub fn new(f: F) -> Self { 22 | FnOption { f: Some(f) } 23 | } 24 | 25 | /// Calls the `FnOnce`. This function should be called only once. 26 | /// It will always return `None` after the first time. 27 | #[inline] 28 | pub fn call_mut(&mut self) -> Option { 29 | self.f.take().map(|f| f()) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/hide.c: -------------------------------------------------------------------------------- 1 | #if defined(__GNUC__) && __GNUC__ >= 4 2 | #if !defined(_WIN32) && !defined(__CYGWIN__) 3 | __attribute__ ((visibility ("hidden"))) 4 | #endif 5 | #endif 6 | unsigned char *clear_on_drop_hide(unsigned char *ptr) { 7 | #if defined(__GNUC__) 8 | /* Not needed with MSVC, since Rust uses LLVM and LTO can't inline this. */ 9 | __asm__ volatile ("" : "=r" (ptr) : "0" (ptr) : "memory"); 10 | #endif 11 | return ptr; 12 | } 13 | -------------------------------------------------------------------------------- /src/hide.rs: -------------------------------------------------------------------------------- 1 | //! Prevent agressive code removal optimizations. 2 | //! 3 | //! The functions in this module "hide" a variable from the optimizer, 4 | //! so that it believes the variable has been read and/or modified in 5 | //! unpredictable ways, while in fact nothing happened. 6 | //! 7 | //! Inspired by/based on Linux kernel's `OPTIMIZER_HIDE_VAR`, which in 8 | //! turn was based on the earlier `RELOC_HIDE` macro. 9 | 10 | /// Make the optimizer believe the memory pointed to by `ptr` is read 11 | /// and modified arbitrarily. 12 | #[inline] 13 | pub fn hide_mem(ptr: &mut T) { 14 | hide_mem_impl(ptr); 15 | } 16 | 17 | /// Make the optimizer believe the pointer returned by this function is 18 | /// possibly unrelated (except for the lifetime) to `ptr`. 19 | #[inline] 20 | pub fn hide_ptr

(mut ptr: P) -> P { 21 | hide_mem::

(&mut ptr); 22 | ptr 23 | } 24 | 25 | pub use self::impls::hide_mem_impl; 26 | 27 | // On nightly, inline assembly can be used. 28 | #[cfg(feature = "nightly")] 29 | mod impls { 30 | use core::arch::asm; 31 | 32 | trait HideMemImpl { 33 | fn hide_mem_impl(ptr: *mut Self); 34 | } 35 | 36 | impl HideMemImpl for T { 37 | #[inline] 38 | default fn hide_mem_impl(ptr: *mut Self) { 39 | unsafe { 40 | //llvm_asm!("" : : "r" (ptr as *mut u8) : "memory"); 41 | asm!("/* {0} */", in(reg) (ptr as *mut u8), options(nostack)); 42 | } 43 | } 44 | } 45 | 46 | impl HideMemImpl for T { 47 | #[inline] 48 | fn hide_mem_impl(ptr: *mut Self) { 49 | unsafe { 50 | //llvm_asm!("" : "=*m" (ptr) : "*0" (ptr)); 51 | asm!("/* {0} */", in(reg) ptr, options(nostack)); 52 | } 53 | } 54 | } 55 | 56 | #[inline] 57 | pub fn hide_mem_impl(ptr: *mut T) { 58 | HideMemImpl::hide_mem_impl(ptr) 59 | } 60 | } 61 | 62 | // When a C compiler is available, a dummy C function can be used. 63 | #[cfg(not(feature = "no_cc"))] 64 | mod impls { 65 | extern "C" { 66 | fn clear_on_drop_hide(ptr: *mut u8) -> *mut u8; 67 | } 68 | 69 | #[inline] 70 | pub fn hide_mem_impl(ptr: *mut T) { 71 | unsafe { 72 | clear_on_drop_hide(ptr as *mut u8); 73 | } 74 | } 75 | } 76 | 77 | // When neither is available, pretend the pointer is sent to a thread, 78 | // and hope this is enough to confuse the optimizer. 79 | #[cfg(all(feature = "no_cc", not(feature = "nightly")))] 80 | mod impls { 81 | use core::sync::atomic::{AtomicUsize, Ordering}; 82 | 83 | #[inline(never)] 84 | pub fn hide_mem_impl(ptr: *mut T) { 85 | static DUMMY: AtomicUsize = AtomicUsize::new(0); 86 | DUMMY.store(ptr as *mut u8 as usize, Ordering::Release); 87 | } 88 | } 89 | 90 | #[cfg(test)] 91 | mod tests { 92 | struct Place { 93 | data: [u32; 4], 94 | } 95 | 96 | const DATA: [u32; 4] = [0x01234567, 0x89abcdef, 0xfedcba98, 0x76543210]; 97 | 98 | #[test] 99 | fn hide_mem() { 100 | let mut place = Place { data: DATA }; 101 | super::hide_mem(&mut place); 102 | assert_eq!(place.data, DATA); 103 | } 104 | 105 | #[test] 106 | fn hide_ptr() { 107 | let mut place = Place { data: DATA }; 108 | let before = &mut place as *mut _; 109 | let after = super::hide_ptr(&mut place); 110 | assert_eq!(before, after as *mut _); 111 | assert_eq!(after.data, DATA); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(test), no_std)] 2 | #![cfg_attr(feature = "nightly", feature(min_specialization))] 3 | #![deny(missing_docs)] 4 | 5 | //! Helpers for clearing sensitive data on the stack and heap. 6 | //! 7 | //! Some kinds of data should not be kept in memory any longer than 8 | //! they are needed. For instance, cryptographic keys and intermediate 9 | //! values should be erased as soon as they are no longer needed. 10 | //! 11 | //! The Rust language helps prevent the accidental reading of leftover 12 | //! values on the stack or the heap; however, means outside the program 13 | //! (for instance a debugger, or even physical access to the hardware) 14 | //! can still read the leftover values. For long-lived processes, key 15 | //! material might be found in the memory long after it should have been 16 | //! discarded. 17 | //! 18 | //! This crate provides two mechanisms to help minimize leftover data. 19 | //! 20 | //! The `ClearOnDrop` wrapper holds a mutable reference to sensitive 21 | //! data (for instance, a cipher state), and clears the data when 22 | //! dropped. While the mutable reference is held, the data cannot be 23 | //! moved, so there won't be leftovers due to moves; the wrapper itself 24 | //! can be freely moved. Alternatively, it can hold data on the heap 25 | //! (using a `Box`, or possibly a similar which allocates from a 26 | //! `mlock`ed heap). 27 | //! 28 | //! The `clear_stack_on_return` function calls a closure, and after it 29 | //! returns, overwrites several kilobytes of the stack. This can help 30 | //! overwrite temporary variables used by cryptographic algorithms, and 31 | //! is especially relevant when running on a short-lived thread, since 32 | //! the memory used for the thread stack cannot be easily overwritten 33 | //! after the thread terminates. 34 | //! 35 | //! # Preventing compiler optimizations 36 | //! 37 | //! If the compiler determines the data is not used after being cleared, 38 | //! it could elide the clearing code. Aditionally, the compiler could 39 | //! inline a called function and the stack clearing code, using separate 40 | //! areas of the stack for each. This crate has three mechanisms which 41 | //! prevent these unwanted optimizations, selected at compile time via 42 | //! cargo features. 43 | //! 44 | //! The fastest mechanism uses inline assembly, which is only available 45 | //! on nightly Rust. It is enabled through the `nightly` feature, and 46 | //! does not need a working C compiler. 47 | //! 48 | //! The second mechanism, which is the default, uses a call to a dummy 49 | //! C function. It works on stable Rust, but needs a working C compiler. 50 | //! 51 | //! The third mechanism is a fallback, which attempts to confuse the 52 | //! optimizer through the use of atomic instructions. It should not be 53 | //! used unless necessary, since it's less reliable. It is enabled by 54 | //! the `no_cc` feature, works on stable Rust, and does not need a C 55 | //! compiler. 56 | 57 | pub mod clear; 58 | mod clear_on_drop; 59 | mod clear_stack_on_return; 60 | mod fnoption; 61 | mod hide; 62 | 63 | pub use crate::clear_on_drop::*; 64 | pub use crate::clear_stack_on_return::*; 65 | --------------------------------------------------------------------------------