Skip to main content

virtio_media/
memfd.rs

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
9use core::slice;
10use std::fs::File;
11use std::io;
12use std::num::NonZeroU64;
13use std::num::NonZeroUsize;
14use std::os::fd::AsFd;
15use std::os::fd::AsRawFd;
16use std::os::fd::BorrowedFd;
17use std::os::fd::RawFd;
18use std::ptr::NonNull;
19
20use nix::errno::Errno;
21use nix::sys::memfd::memfd_create;
22use nix::sys::memfd::MFdFlags;
23use nix::sys::mman;
24use 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.
29pub struct MemFdBuffer {
30    file: File,
31    size: NonZeroU64,
32}
33
34#[derive(Debug, Error)]
35pub 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)]
47pub 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
54impl MemFdBuffer {
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"", MFdFlags::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
84    pub fn as_file(&self) -> &File {
85        &self.file
86    }
87
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
111impl AsFd for MemFdBuffer {
112    fn as_fd(&self) -> BorrowedFd<'_> {
113        self.file.as_fd()
114    }
115}
116
117impl AsRawFd for MemFdBuffer {
118    fn as_raw_fd(&self) -> RawFd {
119        self.file.as_raw_fd()
120    }
121}
122
123impl From<MemFdBuffer> for File {
124    fn from(memfd: MemFdBuffer) -> Self {
125        memfd.file
126    }
127}
128
129/// A CPU mapping of a `MemFdBuffer`.
130pub 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
136impl MemFdMapping {
137    pub fn size(&self) -> usize {
138        self.data.len()
139    }
140}
141
142impl Drop for MemFdMapping {
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
158impl AsRef<[u8]> for MemFdMapping {
159    fn as_ref(&self) -> &[u8] {
160        self.data
161    }
162}
163
164impl AsMut<[u8]> for MemFdMapping {
165    fn as_mut(&mut self) -> &mut [u8] {
166        self.data
167    }
168}