diff --git a/src/lib.rs b/src/lib.rs index 682e990..bc766cd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -101,9 +101,12 @@ impl Drop for SecretBox { #[cfg(windows)] unsafe { - if windows_sys::Win32::System::Memory::VirtualUnlock(secret_ptr.cast(), len) == 0 { - panic!("VirtualUnlock failed",); - } + // VirtualLock does not maintain a lock count for overlapping pages. + // Heap allocations commonly share pages, so dropping one SecretBox + // can unlock a page that still contains another SecretBox. A later + // VirtualUnlock then reports ERROR_NOT_LOCKED. Drop must still + // zeroize the secret and must never panic while unwinding. + let _ = windows_sys::Win32::System::Memory::VirtualUnlock(secret_ptr.cast(), len); } self.zeroize() @@ -363,6 +366,16 @@ mod tests { assert!(TestSecret::default().check_zero()); } + #[cfg(windows)] + #[test] + fn dropping_multiple_secrets_does_not_panic() { + let secrets = (0..64) + .map(|index| SecretString::new(Box::new(format!("secret-{index}")))) + .collect::>(); + + drop(secrets); + } + #[test] fn test_secret_box_expose_secret_mut() { let secret = Box::new(String::from("Encrypted"));