xref: /aosp_15_r20/external/virtio-media/device/src/memfd.rs (revision 1b4853f54772485c5dd4001ae33a7a958bcc97a1)
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