virtual_memory/
lib.rs

1//! Library for allocating RWX memory on Unix and Windows
2//!
3//! ```
4//! use virtual_memory::*;
5//!
6//! let buf = &[
7//!     //mov eax, 1337
8//!     0xb8, 0x39, 0x05, 0x00, 0x00,
9//!     //ret
10//!     0xc3,
11//! ];
12//!
13//! let mut memory = VirtualMemory::new(buf.len()).expect("failed to allocate rwx memory");
14//! memory.copy_from_slice(buf);
15//!
16//! let f: extern "C" fn() -> u32 = unsafe { std::mem::transmute(memory.as_ptr()) };
17//! assert_eq!(f(), 1337);
18//! ```
19
20#![no_std]
21
22use core::{
23    error,
24    ffi::c_void,
25    fmt,
26    ops::{Deref, DerefMut},
27    ptr, result, slice,
28};
29
30/// Custom `Result` type that is used by this crate
31pub type Result<T, E = Error> = result::Result<T, E>;
32
33/// Describes possible errors, currently it's only memory allocation error
34#[derive(Debug)]
35pub enum Error {
36    Allocation,
37}
38
39impl fmt::Display for Error {
40    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41        match self {
42            Error::Allocation => write!(f, "memory allocation error"),
43        }
44    }
45}
46
47impl error::Error for Error {}
48
49/// Wraps OS-specific functionality related to allocation RWX memory
50pub struct VirtualMemory {
51    ptr: *mut c_void,
52    len: usize,
53}
54
55impl VirtualMemory {
56    /// Trying to allocate RWX memory
57    pub fn new(len: usize) -> Result<Self> {
58        #[cfg(unix)]
59        {
60            let ptr = unsafe {
61                libc::mmap(
62                    ptr::null_mut(),
63                    len,
64                    libc::PROT_READ | libc::PROT_WRITE | libc::PROT_EXEC,
65                    libc::MAP_PRIVATE | libc::MAP_ANON,
66                    -1,
67                    0,
68                )
69            };
70
71            if ptr == libc::MAP_FAILED {
72                return Err(Error::Allocation);
73            }
74
75            Ok(Self { ptr, len })
76        }
77
78        #[cfg(windows)]
79        {
80            use windows_sys::Win32::System::Memory::{
81                VirtualAlloc, MEM_COMMIT, MEM_RESERVE, PAGE_EXECUTE_READWRITE,
82            };
83
84            let ptr = unsafe {
85                VirtualAlloc(
86                    ptr::null(),
87                    len,
88                    MEM_COMMIT | MEM_RESERVE,
89                    PAGE_EXECUTE_READWRITE,
90                )
91            };
92
93            if ptr.is_null() {
94                return Err(Error::Allocation);
95            }
96
97            Ok(Self { ptr, len })
98        }
99    }
100
101    /// Returns pointer to this memory
102    #[inline]
103    pub fn ptr(&self) -> *const u8 {
104        self.ptr as *const u8
105    }
106
107    /// Returns mutable pointer to this memory
108    #[inline]
109    pub fn mut_ptr(&mut self) -> *mut u8 {
110        self.ptr as *mut u8
111    }
112
113    /// Returns length of allocated memory
114    #[inline]
115    #[allow(clippy::len_without_is_empty)]
116    pub fn len(&self) -> usize {
117        self.len
118    }
119}
120
121/// Allows to pass `VirtualMemory` as `&[u8]`
122impl Deref for VirtualMemory {
123    type Target = [u8];
124
125    #[inline]
126    fn deref(&self) -> &Self::Target {
127        unsafe { slice::from_raw_parts(self.ptr as _, self.len) }
128    }
129}
130
131/// Allows to pass `VirtualMemory` as `&mut [u8]`
132impl DerefMut for VirtualMemory {
133    #[inline]
134    fn deref_mut(&mut self) -> &mut Self::Target {
135        unsafe { slice::from_raw_parts_mut(self.ptr as _, self.len) }
136    }
137}
138
139/// Acts as destructor (OS-specific)
140impl Drop for VirtualMemory {
141    fn drop(&mut self) {
142        #[cfg(unix)]
143        unsafe {
144            libc::munmap(self.ptr, self.len);
145        }
146
147        #[cfg(windows)]
148        unsafe {
149            use windows_sys::Win32::System::Memory::{VirtualFree, MEM_RELEASE};
150
151            VirtualFree(self.ptr, 0, MEM_RELEASE);
152        }
153    }
154}