use std::io;
use std::thread;
use std::time::Duration;
struct LockedVec {
buf: Vec<u8>,
locked: bool,
}
impl LockedVec {
pub fn new(len: usize) -> io::Result<Self> {
let buf = vec![0u8; len];
let ptr = buf.as_ptr() as *const std::os::raw::c_void;
let locked = match unsafe { os_memlock::mlock(ptr, buf.len()) } {
Ok(()) => true,
Err(e) if e.kind() == io::ErrorKind::Unsupported => {
eprintln!(
"os-memlock: mlock unsupported on this platform/build; continuing unlocked"
);
false
}
Err(e) => return Err(e),
};
#[cfg(target_os = "linux")]
{
let mut_ptr = buf.as_mut_ptr() as *mut std::os::raw::c_void;
match unsafe { os_memlock::madvise_dontdump(mut_ptr, buf.len()) } {
Ok(()) => (),
Err(e) if e.kind() == io::ErrorKind::Unsupported => {
}
Err(e) => {
eprintln!("os-memlock: madvise(MADV_DONTDUMP) failed: {e}");
}
}
}
Ok(Self { buf, locked })
}
pub fn len(&self) -> usize {
self.buf.len()
}
pub fn as_mut_slice(&mut self) -> &mut [u8] {
&mut self.buf
}
pub fn is_locked(&self) -> bool {
self.locked
}
}
impl Drop for LockedVec {
fn drop(&mut self) {
for b in &mut self.buf {
*b = 0;
}
if self.locked {
let ptr = self.buf.as_ptr() as *const std::os::raw::c_void;
let len = self.buf.len();
match unsafe { os_memlock::munlock(ptr, len) } {
Ok(()) => (),
Err(e) if e.kind() == io::ErrorKind::Unsupported => {
}
Err(e) => {
eprintln!("os-memlock: munlock failed: {e}");
}
}
}
}
}
fn main() -> io::Result<()> {
const LEN: usize = 4096;
let mut secrets = LockedVec::new(LEN)?;
println!(
"LockedVec allocated: {} bytes, locked: {}",
secrets.len(),
secrets.is_locked()
);
let s = secrets.as_mut_slice();
let demo = b"super-secret-demo";
if s.len() >= demo.len() {
s[..demo.len()].copy_from_slice(demo);
}
println!("Working with secret data (simulated)...");
thread::sleep(Duration::from_millis(200));
println!("Example complete; dropping LockedVec (zeroize + munlock).");
Ok(())
}