use libc;
pub fn harden_process() {
disable_core_dumps();
}
pub fn mlock_region(ptr: *const u8, len: usize) -> bool {
if len == 0 {
return true;
}
let rc = unsafe { libc::mlock(ptr as *const libc::c_void, len) };
if rc != 0 {
let err = std::io::Error::last_os_error();
eprintln!(
"[secretsh] WARNING: mlock({len} bytes) failed: {err} \
— secrets may be swapped to disk"
);
return false;
}
true
}
pub fn munlock_region(ptr: *const u8, len: usize) {
if len == 0 {
return;
}
let rc = unsafe { libc::munlock(ptr as *const libc::c_void, len) };
if rc != 0 {
let err = std::io::Error::last_os_error();
eprintln!("[secretsh] WARNING: munlock({len} bytes) failed: {err}");
}
}
pub fn madvise_free(ptr: *mut u8, len: usize) {
if len == 0 {
return;
}
let rc = unsafe { libc::madvise(ptr as *mut libc::c_void, len, libc::MADV_FREE) };
if rc != 0 {
let err = std::io::Error::last_os_error();
eprintln!("[secretsh] WARNING: madvise(MADV_FREE, {len} bytes) failed: {err}");
}
}
fn disable_core_dumps() {
let zero_limit = libc::rlimit {
rlim_cur: 0,
rlim_max: 0,
};
let rc = unsafe { libc::setrlimit(libc::RLIMIT_CORE, &zero_limit) };
if rc != 0 {
let err = std::io::Error::last_os_error();
eprintln!(
"[secretsh] WARNING: setrlimit(RLIMIT_CORE, 0) failed: {err} \
— core dumps are NOT suppressed"
);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_harden_process_does_not_panic() {
harden_process();
}
#[test]
fn test_core_dump_rlimit_is_zero_after_harden() {
harden_process();
let mut rl = libc::rlimit {
rlim_cur: u64::MAX,
rlim_max: u64::MAX,
};
let rc = unsafe { libc::getrlimit(libc::RLIMIT_CORE, &mut rl) };
assert_eq!(rc, 0, "getrlimit(RLIMIT_CORE) failed");
assert_eq!(rl.rlim_cur, 0, "RLIMIT_CORE soft limit should be 0");
assert_eq!(rl.rlim_max, 0, "RLIMIT_CORE hard limit should be 0");
}
#[test]
fn test_mlock_munlock_round_trip() {
let buf: Vec<u8> = vec![0xAB_u8; 64];
let ptr = buf.as_ptr();
let len = buf.len();
let locked = mlock_region(ptr, len);
munlock_region(ptr, len);
let _ = locked;
}
#[test]
fn test_zero_length_is_noop() {
let buf: Vec<u8> = vec![0u8; 8];
let ptr = buf.as_ptr();
assert!(mlock_region(ptr, 0));
munlock_region(ptr, 0);
madvise_free(buf.as_ptr() as *mut u8, 0);
}
#[test]
fn test_madvise_free_does_not_panic() {
let page_size = 4096_usize;
let buf: Vec<u8> = vec![0u8; page_size * 2];
let raw = buf.as_ptr() as usize;
let aligned = (raw + page_size - 1) & !(page_size - 1);
let offset = aligned - raw;
if offset + page_size <= buf.len() {
madvise_free(aligned as *mut u8, page_size);
} else {
madvise_free(buf.as_ptr() as *mut u8, buf.len());
}
}
}