memsafe/ffi/
mod.rs

1use crate::MemoryError;
2
3#[cfg(unix)]
4mod unix;
5
6#[cfg(unix)]
7use libc::{MAP_ANONYMOUS, MAP_PRIVATE, PROT_NONE, PROT_READ, PROT_WRITE};
8
9#[cfg(target_os = "linux")]
10use libc::{c_void, MADV_DONTDUMP};
11
12#[cfg(windows)]
13mod win;
14#[cfg(windows)]
15use winapi::um::winnt::{MEM_COMMIT, MEM_DECOMMIT, MEM_RESERVE, PAGE_READONLY, PAGE_READWRITE};
16
17/// Allocates page-alined memory dynamically.
18///
19/// This function provides a cross-platform abstraction for memory allocation,
20/// using `mmap` on Unix-like systems and `VirtualAlloc` on Windows. The allocated
21/// memory is anonymous, meaning it is not backed by a file and is zero-initialized
22/// by the OS.
23///
24/// # Arguments
25///
26/// * `len` - The size of the memory allocation in bytes.
27///
28/// # Returns
29///
30/// * `Ok(*mut T)` - A pointer to the allocated memory if the allocation succeeds.
31/// * `Err(MemoryError)` - A memory allocation error if the operation fails.
32///
33/// # Platform-specific Behavior
34///
35/// * **Unix**: Uses `mmap` with `PROT_READ | PROT_WRITE` and `MAP_PRIVATE | MAP_ANONYMOUS`.
36/// * **Windows**: Uses `VirtualAlloc` with `MEM_COMMIT | MEM_RESERVE` and `PAGE_READWRITE`.
37///
38/// # Safety
39///
40/// The returned pointer is uninitialized and must be properly managed by the caller.
41/// Ensure that the allocated memory is deallocated appropriately to prevent memory leaks.
42pub fn mem_alloc<T>(len: usize) -> Result<*mut T, MemoryError> {
43    #[cfg(unix)]
44    {
45        unix::mmap(
46            len,
47            PROT_READ | PROT_WRITE,
48            MAP_PRIVATE | MAP_ANONYMOUS,
49            -1,
50            0,
51        )
52    }
53
54    #[cfg(windows)]
55    {
56        win::virtual_alloc(
57            std::ptr::null_mut(),
58            len,
59            MEM_COMMIT | MEM_RESERVE,
60            PAGE_READWRITE,
61        )
62    }
63}
64
65/// Deallocates previously allocated page-aligned memory.
66///
67/// This function provides a cross-platform abstraction for memory deallocation,
68/// using `munmap` on Unix-like systems and `VirtualFree` on Windows. It ensures that
69/// memory is properly released and can be reclaimed by the operating system.
70///
71/// # Arguments
72///
73/// * `ptr` - A pointer to the memory that was previously allocated.
74/// * `len` - The size of the allocated memory in bytes. This must match the size
75///   originally passed to [`mem_alloc`](fn.mem_alloc.html).
76///
77/// # Returns
78///
79/// * `Ok(())` - If the deallocation succeeds.
80/// * `Err(MemoryError)` - If the operation fails.
81///
82/// # Platform-specific Behavior
83///
84/// * **Unix**: Uses `munmap` to release the memory.
85/// * **Windows**: Uses `VirtualFree` with `MEM_DECOMMIT` to deallocate the memory.
86///
87/// # Safety
88///
89/// * The `ptr` must be a valid, non-null pointer returned by [`mem_alloc`](fn.mem_alloc.html).
90/// * The `len` must be correct, as passing an incorrect size may cause undefined behavior.
91/// * After deallocation, the pointer must not be accessed again.
92pub fn mem_dealloc<T>(ptr: *mut T, len: usize) -> Result<(), MemoryError> {
93    #[cfg(unix)]
94    {
95        unix::munmap(ptr, len)
96    }
97
98    #[cfg(windows)]
99    {
100        win::virtual_free(ptr, len, MEM_DECOMMIT)
101    }
102}
103
104/// Marks a memory region as inaccessible.
105///
106/// This function changes the protection settings of a previously allocated memory region
107/// to prevent any read, write, or execute access.
108///
109/// # Arguments
110///
111/// * `ptr` - A pointer to the memory region.
112/// * `len` - The size of the memory region in bytes.
113///
114/// # Returns
115///
116/// * `Ok(())` - If the operation succeeds.
117/// * `Err(MemoryError)` - If the operation fails.
118///
119/// # Platform-specific Behavior
120///
121/// * **Unix**: Uses `mprotect` with `PROT_NONE`, making the memory completely inaccessible.
122/// * **Windows**: Uses `VirtualProtect` with `PAGE_NOACCESS`, denying all access.
123///
124/// # Safety
125///
126/// * `ptr` must be a valid, non-null pointer to an allocated memory region.
127/// * `len` must be correct, matching the size of the allocated region.
128/// * Accessing the memory after calling this function will trigger a segmentation fault (Unix) or
129///   access violation (Windows).
130#[cfg(unix)]
131pub fn mem_noaccess<T>(ptr: *mut T, len: usize) -> Result<(), MemoryError> {
132
133    unix::mprotect(ptr, len, PROT_NONE)
134}
135
136/// Marks a memory region as read-only.
137///
138/// This function modifies the protection settings of a previously allocated memory region
139/// to allow read access while preventing writes.
140///
141/// # Arguments
142///
143/// * `ptr` - A pointer to the memory region.
144/// * `len` - The size of the memory region in bytes.
145///
146/// # Returns
147///
148/// * `Ok(())` - If the operation succeeds.
149/// * `Err(MemoryError)` - If the operation fails.
150///
151/// # Platform-specific Behavior
152///
153/// * **Unix**: Uses `mprotect` with `PROT_READ`, making the memory readable but not writable or executable.
154/// * **Windows**: Uses `VirtualProtect` with `PAGE_READONLY`, allowing only read access.
155///
156/// # Safety
157///
158/// * `ptr` must be a valid, non-null pointer to an allocated memory region.
159/// * `len` must be correct, matching the size of the allocated region.
160/// * Writing to the memory after calling this function will trigger a segmentation fault
161///   (Unix) or an access violation (Windows).
162pub fn mem_readonly<T>(ptr: *mut T, len: usize) -> Result<(), MemoryError> {
163    #[cfg(unix)]
164    {
165        unix::mprotect(ptr, len, PROT_READ)
166    }
167
168    #[cfg(windows)]
169    {
170        win::virtual_protect(ptr, len, PAGE_READONLY, &mut 0)
171    }
172}
173
174/// Marks a memory region as readable and writable.
175///
176/// This function modifies the protection settings of a previously allocated memory region
177/// to allow both read and write access.
178///
179/// # Arguments
180///
181/// * `ptr` - A pointer to the memory region.
182/// * `len` - The size of the memory region in bytes.
183///
184/// # Returns
185///
186/// * `Ok(())` - If the operation succeeds.
187/// * `Err(MemoryError)` - If the operation fails.
188///
189/// # Platform-specific Behavior
190///
191/// * **Unix**: Uses `mprotect` with `PROT_READ | PROT_WRITE`, enabling both read and write access.
192/// * **Windows**: Uses `VirtualProtect` with `PAGE_READWRITE`, allowing both read and write operations.
193///
194/// # Safety
195///
196/// * `ptr` must be a valid, non-null pointer to an allocated memory region.
197/// * `len` must be correct, matching the size of the allocated region.
198pub fn mem_readwrite<T>(ptr: *mut T, len: usize) -> Result<(), MemoryError> {
199    #[cfg(unix)]
200    {
201        unix::mprotect(ptr, len, PROT_READ | PROT_WRITE)
202    }
203
204    #[cfg(windows)]
205    {
206        win::virtual_protect(ptr, len, PAGE_READWRITE, &mut 0)
207    }
208}
209
210/// Locks a memory region to prevent it from being paged out into swap memory.
211///
212/// This function ensures that the specified memory region remains in physical
213/// Random Access Memory (RAM) and is not swapped to disk by the operating system.
214///
215/// # Arguments
216///
217/// * `ptr` - A pointer to the memory region.
218/// * `len` - The size of the memory region in bytes.
219///
220/// # Returns
221///
222/// * `Ok(())` - If the operation succeeds.
223/// * `Err(MemoryError)` - If the operation fails.
224///
225/// # Platform-specific Behavior
226///
227/// * **Unix**: Uses `mlock`, which prevents the specified memory range from being swapped out.
228/// * **Windows**: Uses `VirtualLock`, which locks the memory in RAM and prevents paging.
229///   - **Windows Limitation**:
230///     - All pages in the specified region must be committed.
231///     - Memory protected with `PAGE_NOACCESS` cannot be locked.
232///
233/// # Safety
234///
235/// * `ptr` must be a valid, non-null pointer to an allocated memory region.
236/// * `len` must be correct, matching the size of the allocated region.
237/// * Excessive use of locked memory may cause system-wide performance degradation.
238/// * On some systems, locking memory may require elevated privileges.
239pub fn mem_lock<T>(ptr: *mut T, len: usize) -> Result<(), MemoryError> {
240    #[cfg(unix)]
241    {
242        unix::mlock(ptr, len)
243    }
244
245    #[cfg(windows)]
246    {
247        win::virtual_lock(ptr, len)
248    }
249}
250
251/// Unlocks a previously locked memory region, allowing it to be paged out.
252///
253/// This function reverses the effect of [`mem_lock`](fn.mem_lock.html), allowing the operating system
254/// to move the specified memory region back into the page file or swap space if necessary.
255///
256/// # Arguments
257///
258/// * `ptr` - A pointer to the memory region.
259/// * `len` - The size of the memory region in bytes.
260///
261/// # Returns
262///
263/// * `Ok(())` - If the operation succeeds.
264/// * `Err(MemoryError)` - If the operation fails.
265///
266/// # Platform-specific Behavior
267///
268/// * **Unix**: Uses `munlock`, allowing the specified memory range to be swapped out.
269/// * **Windows**: Uses `VirtualUnlock`, allowing the memory to be paged.
270///
271/// # Safety
272///
273/// * `ptr` must be a valid, non-null pointer to an allocated memory region.
274/// * `len` must be correct, matching the size of the locked region.
275/// * Unlocking memory that was never locked may result in undefined behavior on some platforms.
276pub fn mem_unlock<T>(ptr: *mut T, len: usize) -> Result<(), MemoryError> {
277    #[cfg(unix)]
278    {
279        unix::munlock(ptr, len)
280    }
281
282    #[cfg(windows)]
283    {
284        win::virtual_unlock(ptr, len)
285    }
286}
287
288#[cfg(target_os = "linux")]
289pub fn mem_no_dump<T>(ptr: *mut T, len: usize) -> Result<(), MemoryError> {
290    unix::madvice(ptr as *mut c_void, len, MADV_DONTDUMP)
291}