use crate::error::Result;
use libc::{c_int, c_void};
use tracing::error;
fn memfd_secret() -> Result<c_int> {
let ret = unsafe { libc::syscall(libc::SYS_memfd_secret, libc::O_CLOEXEC) };
if ret < 0 {
return Err(std::io::Error::last_os_error().into());
}
Ok(c_int::try_from(ret).expect("memfd_secret returned a file descriptor outside of c_int"))
}
fn ftruncate(fd: c_int, len: usize) -> Result<()> {
let ret = unsafe { libc::ftruncate64(fd, len as libc::off64_t) };
if ret != 0 {
return Err(std::io::Error::last_os_error().into());
}
Ok(())
}
fn mmap(fd: c_int, len: usize) -> Result<*mut c_void> {
let ret = unsafe {
libc::mmap64(
std::ptr::null_mut(),
len,
libc::PROT_READ | libc::PROT_WRITE,
libc::MAP_SHARED,
fd,
0,
)
};
if ret == libc::MAP_FAILED {
return Err(std::io::Error::last_os_error().into());
}
Ok(ret)
}
fn munmap(ptr: *mut c_void, len: usize) -> Result<()> {
let ret = unsafe { libc::munmap(ptr, len) };
if ret != 0 {
return Err(std::io::Error::last_os_error().into());
}
Ok(())
}
fn close(fd: c_int) -> Result<()> {
let ret = unsafe { libc::close(fd) };
if ret != 0 {
return Err(std::io::Error::last_os_error().into());
}
Ok(())
}
pub struct Secret {
fd: c_int,
ptr: *mut c_void,
len: usize,
}
impl Drop for Secret {
fn drop(&mut self) {
if let Err(e) = self.clear() {
error!(
"Secret failed to clean up, memory and/or file descriptor leaked: {:?}",
e
);
}
}
}
impl Default for Secret {
fn default() -> Self {
Secret {
fd: -1,
ptr: std::ptr::null_mut(),
len: 0,
}
}
}
impl Secret {
fn clear(&mut self) -> Result<()> {
if !self.ptr.is_null() {
munmap(self.ptr, self.len)?;
self.ptr = std::ptr::null_mut();
}
if self.fd != -1 {
close(self.fd)?;
self.fd = -1;
}
Ok(())
}
pub fn new() -> Self {
Self::default()
}
pub fn with_len(len: usize) -> Result<Self> {
let mut s = Secret::new();
if len > 0 {
s.fd = memfd_secret()?;
ftruncate(s.fd, len)?;
s.ptr = mmap(s.fd, len)?;
s.len = len;
}
Ok(s)
}
pub fn resize(&mut self, len: usize) -> Result<()> {
let mut next = Secret::with_len(len)?;
let copy_len = std::cmp::min(self.len, len);
let next_slice = unsafe { next.as_mut_slice() };
let self_slice = unsafe { self.as_slice() };
next_slice[..copy_len].copy_from_slice(&self_slice[..copy_len]);
self.clear()?;
*self = next;
Ok(())
}
pub fn len(&self) -> usize {
self.len
}
pub unsafe fn slice_ptr(&self) -> *mut u8 {
let ret = if self.len > 0 {
debug_assert!(!self.ptr.is_null());
self.ptr as *mut u8
} else {
std::ptr::NonNull::dangling().as_ptr()
};
debug_assert!(!ret.is_null());
ret
}
pub unsafe fn as_slice(&self) -> &[u8] {
unsafe { std::slice::from_raw_parts(self.slice_ptr(), self.len) }
}
pub unsafe fn as_mut_slice(&mut self) -> &mut [u8] {
unsafe { std::slice::from_raw_parts_mut(self.slice_ptr(), self.len) }
}
pub fn try_clone(&self) -> Result<Self> {
let mut other = Secret::with_len(self.len())?;
unsafe { other.as_mut_slice().copy_from_slice(self.as_slice()) }
Ok(other)
}
}
unsafe impl Send for Secret {}
unsafe impl Sync for Secret {}