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