diff --git a/Cargo.toml b/Cargo.toml index f712fbc..c9d417a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,9 @@ pkg-config = '0.3' [target.'cfg(target_family = "windows")'.build-dependencies] vcpkg = '0.2' +[build-dependencies] +page_size = '0.6' + [dev-dependencies] libsodium-sys = '0.2' diff --git a/build.rs b/build.rs index e2233a0..598041d 100644 --- a/build.rs +++ b/build.rs @@ -62,6 +62,16 @@ fn main() { // naïvely println!("cargo:rustc-link-lib=dylib=sodium"); }; + + let out_dir = env::var("OUT_DIR").unwrap(); + let out_path = std::path::Path::new(&out_dir).join("secret_pad.rs"); + + // probe the page size of the current system and write it out so we + // can align `Secret` using it + std::fs::write(out_path, format!( + "#[repr(align({}))]\npub(crate) struct Padding();", + page_size::get() + )).unwrap(); } #[cfg(feature = "use-libsodium-sys")] @@ -116,4 +126,4 @@ fn link(name: &str, _version: &str) -> Option<()> { None } } -} \ No newline at end of file +} diff --git a/src/secret.rs b/src/secret.rs index 7455119..1001721 100644 --- a/src/secret.rs +++ b/src/secret.rs @@ -9,6 +9,8 @@ use std::fmt::{self, Debug, Formatter}; use std::ops::{Deref, DerefMut}; use std::thread; +include!(concat!(env!("OUT_DIR"), "/secret_pad.rs")); + /// A type for protecting secrets allocated on the stack. /// /// Stack-allocated secrets have distinct security needs from @@ -78,9 +80,13 @@ use std::thread; /// ``` /// /// [mlock]: http://man7.org/linux/man-pages/man2/mlock.2.html + pub struct Secret { /// The internal protected memory for the [`Secret`]. data: T, + + /// Additional padding to ensure we occupy a dedicated page. + _pad: Padding, } /// A mutable [`Deref`]-wrapper around a [`Secret`]'s internal @@ -121,6 +127,7 @@ impl Secret { let mut secret = Self { data: T::uninitialized(), + _pad: Padding(), }; assert!( @@ -203,13 +210,7 @@ impl Drop for Secret { /// Ensures that the [`Secret`]'s underlying memory is `munlock`ed /// and zeroed when it leaves scope. fn drop(&mut self) { - // When we call sodium_munlock on some data, it actually unlocks the entire page that - // contains the memory. If two locked items were on the same page, then the second one - // fails because it was already unlocked. On Linux, this does now throw an error. On - // Windows, it does. We'll ignore it for now, and provide a better fix later. - if unsafe { !sodium::munlock(&raw mut self.data) } - && !(cfg!(target_family = "windows") - && (std::io::Error::last_os_error().raw_os_error() == Some(158))) { + if unsafe { !sodium::munlock(&raw mut self.data) } { // [`Drop::drop`] is called during stack unwinding, so we // may be in a panic already. assert!(