1*1b4853f5SAndroid Build Coastguard Worker // Copyright 2024 The ChromiumOS Authors 2*1b4853f5SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be 3*1b4853f5SAndroid Build Coastguard Worker // found in the LICENSE file. 4*1b4853f5SAndroid Build Coastguard Worker 5*1b4853f5SAndroid Build Coastguard Worker //! Provides a simple memory allocator using `memfd`. The `MemFd` crate provides the same 6*1b4853f5SAndroid Build Coastguard Worker //! functionality, but also pulls some unwanted dependencies in, so we use this simple 7*1b4853f5SAndroid Build Coastguard Worker //! implementation instead. 8*1b4853f5SAndroid Build Coastguard Worker 9*1b4853f5SAndroid Build Coastguard Worker use core::slice; 10*1b4853f5SAndroid Build Coastguard Worker use std::fs::File; 11*1b4853f5SAndroid Build Coastguard Worker use std::io; 12*1b4853f5SAndroid Build Coastguard Worker use std::num::NonZeroU64; 13*1b4853f5SAndroid Build Coastguard Worker use std::num::NonZeroUsize; 14*1b4853f5SAndroid Build Coastguard Worker use std::os::fd::AsFd; 15*1b4853f5SAndroid Build Coastguard Worker use std::os::fd::AsRawFd; 16*1b4853f5SAndroid Build Coastguard Worker use std::os::fd::BorrowedFd; 17*1b4853f5SAndroid Build Coastguard Worker use std::os::fd::RawFd; 18*1b4853f5SAndroid Build Coastguard Worker use std::ptr::NonNull; 19*1b4853f5SAndroid Build Coastguard Worker 20*1b4853f5SAndroid Build Coastguard Worker use nix::errno::Errno; 21*1b4853f5SAndroid Build Coastguard Worker use nix::sys::memfd::memfd_create; 22*1b4853f5SAndroid Build Coastguard Worker use nix::sys::memfd::MemFdCreateFlag; 23*1b4853f5SAndroid Build Coastguard Worker use nix::sys::mman; 24*1b4853f5SAndroid Build Coastguard Worker use thiserror::Error; 25*1b4853f5SAndroid Build Coastguard Worker 26*1b4853f5SAndroid Build Coastguard Worker /// A chunk of memory allocated through `memfd`. 27*1b4853f5SAndroid Build Coastguard Worker /// 28*1b4853f5SAndroid Build Coastguard Worker /// Buffers allocated this way are of fixed size, and can be manipulated as files. 29*1b4853f5SAndroid Build Coastguard Worker pub struct MemFdBuffer { 30*1b4853f5SAndroid Build Coastguard Worker file: File, 31*1b4853f5SAndroid Build Coastguard Worker size: NonZeroU64, 32*1b4853f5SAndroid Build Coastguard Worker } 33*1b4853f5SAndroid Build Coastguard Worker 34*1b4853f5SAndroid Build Coastguard Worker #[derive(Debug, Error)] 35*1b4853f5SAndroid Build Coastguard Worker pub enum NewMemFdBufferError { 36*1b4853f5SAndroid Build Coastguard Worker #[error("MemFdBuffer size cannot be zero")] 37*1b4853f5SAndroid Build Coastguard Worker ZeroSize, 38*1b4853f5SAndroid Build Coastguard Worker #[error("call to memfd_create failed: {0}")] 39*1b4853f5SAndroid Build Coastguard Worker FailedToCreate(#[from] Errno), 40*1b4853f5SAndroid Build Coastguard Worker #[error("failed to set size of memfd: {0}")] 41*1b4853f5SAndroid Build Coastguard Worker FailedToSetSize(io::Error), 42*1b4853f5SAndroid Build Coastguard Worker #[error("failed to seal memfd: {0}")] 43*1b4853f5SAndroid Build Coastguard Worker FailedToSeal(io::Error), 44*1b4853f5SAndroid Build Coastguard Worker } 45*1b4853f5SAndroid Build Coastguard Worker 46*1b4853f5SAndroid Build Coastguard Worker #[derive(Debug, Error)] 47*1b4853f5SAndroid Build Coastguard Worker pub enum MemFdMmapError { 48*1b4853f5SAndroid Build Coastguard Worker #[error("buffer size {0} larger than usize")] 49*1b4853f5SAndroid Build Coastguard Worker BufferTooLarge(u64), 50*1b4853f5SAndroid Build Coastguard Worker #[error("mmap call returned error: {0}")] 51*1b4853f5SAndroid Build Coastguard Worker Mmap(#[from] Errno), 52*1b4853f5SAndroid Build Coastguard Worker } 53*1b4853f5SAndroid Build Coastguard Worker 54*1b4853f5SAndroid Build Coastguard Worker impl MemFdBuffer { new(size: u64) -> Result<Self, NewMemFdBufferError>55*1b4853f5SAndroid Build Coastguard Worker pub fn new(size: u64) -> Result<Self, NewMemFdBufferError> { 56*1b4853f5SAndroid Build Coastguard Worker let size = NonZeroU64::new(size).ok_or(NewMemFdBufferError::ZeroSize)?; 57*1b4853f5SAndroid Build Coastguard Worker 58*1b4853f5SAndroid Build Coastguard Worker // Dummy name, we may want to support names for debugging purposes. 59*1b4853f5SAndroid Build Coastguard Worker let fd = memfd_create(c"", MemFdCreateFlag::MFD_ALLOW_SEALING)?; 60*1b4853f5SAndroid Build Coastguard Worker 61*1b4853f5SAndroid Build Coastguard Worker let file: File = fd.into(); 62*1b4853f5SAndroid Build Coastguard Worker 63*1b4853f5SAndroid Build Coastguard Worker // Allocate requested size. 64*1b4853f5SAndroid Build Coastguard Worker file.set_len(size.into()) 65*1b4853f5SAndroid Build Coastguard Worker .map_err(NewMemFdBufferError::FailedToSetSize)?; 66*1b4853f5SAndroid Build Coastguard Worker 67*1b4853f5SAndroid Build Coastguard Worker // Seal so the memory size cannot be changed. 68*1b4853f5SAndroid Build Coastguard Worker // 69*1b4853f5SAndroid Build Coastguard Worker // SAFETY: `file` is a valid file. 70*1b4853f5SAndroid Build Coastguard Worker if unsafe { 71*1b4853f5SAndroid Build Coastguard Worker libc::fcntl( 72*1b4853f5SAndroid Build Coastguard Worker file.as_raw_fd(), 73*1b4853f5SAndroid Build Coastguard Worker libc::F_ADD_SEALS, 74*1b4853f5SAndroid Build Coastguard Worker libc::F_SEAL_SHRINK | libc::F_SEAL_GROW | libc::F_SEAL_SEAL, 75*1b4853f5SAndroid Build Coastguard Worker ) 76*1b4853f5SAndroid Build Coastguard Worker } < 0 77*1b4853f5SAndroid Build Coastguard Worker { 78*1b4853f5SAndroid Build Coastguard Worker return Err(NewMemFdBufferError::FailedToSeal(io::Error::last_os_error())); 79*1b4853f5SAndroid Build Coastguard Worker } 80*1b4853f5SAndroid Build Coastguard Worker 81*1b4853f5SAndroid Build Coastguard Worker Ok(Self { file, size }) 82*1b4853f5SAndroid Build Coastguard Worker } 83*1b4853f5SAndroid Build Coastguard Worker as_file(&self) -> &File84*1b4853f5SAndroid Build Coastguard Worker pub fn as_file(&self) -> &File { 85*1b4853f5SAndroid Build Coastguard Worker &self.file 86*1b4853f5SAndroid Build Coastguard Worker } 87*1b4853f5SAndroid Build Coastguard Worker mmap(&self) -> Result<MemFdMapping, MemFdMmapError>88*1b4853f5SAndroid Build Coastguard Worker pub fn mmap(&self) -> Result<MemFdMapping, MemFdMmapError> { 89*1b4853f5SAndroid Build Coastguard Worker let size = NonZeroUsize::try_from(self.size) 90*1b4853f5SAndroid Build Coastguard Worker .map_err(|_| MemFdMmapError::BufferTooLarge(self.size.into()))?; 91*1b4853f5SAndroid Build Coastguard Worker 92*1b4853f5SAndroid Build Coastguard Worker // SAFETY: `self.file` is a valid file. 93*1b4853f5SAndroid Build Coastguard Worker let data = unsafe { 94*1b4853f5SAndroid Build Coastguard Worker mman::mmap( 95*1b4853f5SAndroid Build Coastguard Worker None, 96*1b4853f5SAndroid Build Coastguard Worker size, 97*1b4853f5SAndroid Build Coastguard Worker mman::ProtFlags::PROT_READ | mman::ProtFlags::PROT_WRITE, 98*1b4853f5SAndroid Build Coastguard Worker mman::MapFlags::MAP_SHARED, 99*1b4853f5SAndroid Build Coastguard Worker &self.file, 100*1b4853f5SAndroid Build Coastguard Worker 0, 101*1b4853f5SAndroid Build Coastguard Worker )? 102*1b4853f5SAndroid Build Coastguard Worker }; 103*1b4853f5SAndroid Build Coastguard Worker 104*1b4853f5SAndroid Build Coastguard Worker Ok(MemFdMapping { 105*1b4853f5SAndroid Build Coastguard Worker // SAFETY: `data` is non-null and obtained through a `mmap` of size `self.size`. 106*1b4853f5SAndroid Build Coastguard Worker data: unsafe { slice::from_raw_parts_mut(data.as_ptr().cast(), size.into()) }, 107*1b4853f5SAndroid Build Coastguard Worker }) 108*1b4853f5SAndroid Build Coastguard Worker } 109*1b4853f5SAndroid Build Coastguard Worker } 110*1b4853f5SAndroid Build Coastguard Worker 111*1b4853f5SAndroid Build Coastguard Worker impl AsFd for MemFdBuffer { as_fd(&self) -> BorrowedFd<'_>112*1b4853f5SAndroid Build Coastguard Worker fn as_fd(&self) -> BorrowedFd<'_> { 113*1b4853f5SAndroid Build Coastguard Worker self.file.as_fd() 114*1b4853f5SAndroid Build Coastguard Worker } 115*1b4853f5SAndroid Build Coastguard Worker } 116*1b4853f5SAndroid Build Coastguard Worker 117*1b4853f5SAndroid Build Coastguard Worker impl AsRawFd for MemFdBuffer { as_raw_fd(&self) -> RawFd118*1b4853f5SAndroid Build Coastguard Worker fn as_raw_fd(&self) -> RawFd { 119*1b4853f5SAndroid Build Coastguard Worker self.file.as_raw_fd() 120*1b4853f5SAndroid Build Coastguard Worker } 121*1b4853f5SAndroid Build Coastguard Worker } 122*1b4853f5SAndroid Build Coastguard Worker 123*1b4853f5SAndroid Build Coastguard Worker impl From<MemFdBuffer> for File { from(memfd: MemFdBuffer) -> Self124*1b4853f5SAndroid Build Coastguard Worker fn from(memfd: MemFdBuffer) -> Self { 125*1b4853f5SAndroid Build Coastguard Worker memfd.file 126*1b4853f5SAndroid Build Coastguard Worker } 127*1b4853f5SAndroid Build Coastguard Worker } 128*1b4853f5SAndroid Build Coastguard Worker 129*1b4853f5SAndroid Build Coastguard Worker /// A CPU mapping of a `MemFdBuffer`. 130*1b4853f5SAndroid Build Coastguard Worker pub struct MemFdMapping { 131*1b4853f5SAndroid Build Coastguard Worker // A mapping remains valid until we munmap it, that is, until the 132*1b4853f5SAndroid Build Coastguard Worker // PlaneMapping object is deleted. Hence the static lifetime. 133*1b4853f5SAndroid Build Coastguard Worker data: &'static mut [u8], 134*1b4853f5SAndroid Build Coastguard Worker } 135*1b4853f5SAndroid Build Coastguard Worker 136*1b4853f5SAndroid Build Coastguard Worker impl MemFdMapping { size(&self) -> usize137*1b4853f5SAndroid Build Coastguard Worker pub fn size(&self) -> usize { 138*1b4853f5SAndroid Build Coastguard Worker self.data.len() 139*1b4853f5SAndroid Build Coastguard Worker } 140*1b4853f5SAndroid Build Coastguard Worker } 141*1b4853f5SAndroid Build Coastguard Worker 142*1b4853f5SAndroid Build Coastguard Worker impl Drop for MemFdMapping { drop(&mut self)143*1b4853f5SAndroid Build Coastguard Worker fn drop(&mut self) { 144*1b4853f5SAndroid Build Coastguard Worker // Safe because the pointer and length were constructed in mmap() and 145*1b4853f5SAndroid Build Coastguard Worker // are always valid. 146*1b4853f5SAndroid Build Coastguard Worker unsafe { 147*1b4853f5SAndroid Build Coastguard Worker mman::munmap( 148*1b4853f5SAndroid Build Coastguard Worker NonNull::new_unchecked(self.data.as_mut_ptr().cast()), 149*1b4853f5SAndroid Build Coastguard Worker self.data.len(), 150*1b4853f5SAndroid Build Coastguard Worker ) 151*1b4853f5SAndroid Build Coastguard Worker } 152*1b4853f5SAndroid Build Coastguard Worker .unwrap_or_else(|e| { 153*1b4853f5SAndroid Build Coastguard Worker log::error!("error while unmapping MemFdBuffer: {:#}", e); 154*1b4853f5SAndroid Build Coastguard Worker }); 155*1b4853f5SAndroid Build Coastguard Worker } 156*1b4853f5SAndroid Build Coastguard Worker } 157*1b4853f5SAndroid Build Coastguard Worker 158*1b4853f5SAndroid Build Coastguard Worker impl AsRef<[u8]> for MemFdMapping { as_ref(&self) -> &[u8]159*1b4853f5SAndroid Build Coastguard Worker fn as_ref(&self) -> &[u8] { 160*1b4853f5SAndroid Build Coastguard Worker self.data 161*1b4853f5SAndroid Build Coastguard Worker } 162*1b4853f5SAndroid Build Coastguard Worker } 163*1b4853f5SAndroid Build Coastguard Worker 164*1b4853f5SAndroid Build Coastguard Worker impl AsMut<[u8]> for MemFdMapping { as_mut(&mut self) -> &mut [u8]165*1b4853f5SAndroid Build Coastguard Worker fn as_mut(&mut self) -> &mut [u8] { 166*1b4853f5SAndroid Build Coastguard Worker self.data 167*1b4853f5SAndroid Build Coastguard Worker } 168*1b4853f5SAndroid Build Coastguard Worker } 169