os_memlock/
lib.rs

1#![cfg_attr(docsrs, feature(doc_cfg))]
2#![cfg_attr(docsrs, deny(broken_intra_doc_links))]
3#![doc = include_str!("../README.md")]
4#![warn(missing_docs)]
5
6use std::io;
7use std::os::raw::c_void;
8
9#[inline]
10fn unsupported(msg: &'static str) -> io::Result<()> {
11    Err(io::Error::new(io::ErrorKind::Unsupported, msg))
12}
13
14#[cfg(unix)]
15mod unix {
16    use super::{c_void, io};
17
18    /// Lock the pages containing the specified memory region to prevent swapping.
19    ///
20    /// Returns:
21    /// - `Ok(())` on success
22    /// - `Err(...)` with `last_os_error()` on failure
23    /// - `Err(Unsupported)` on platforms where this call is not available
24    ///
25    /// # Safety
26    /// The caller must ensure that `(addr, len)` refers to a valid, non-null memory
27    /// region owned by this process for the duration of the call, and that the region
28    /// is not deallocated, unmapped, or remapped concurrently.
29    ///
30    /// # Examples
31    /// ```no_run
32    /// # use std::io;
33    /// # fn demo() -> io::Result<()> {
34    /// let mut buf = vec![0u8; 4096];
35    /// let ptr = buf.as_ptr() as *const std::os::raw::c_void;
36    /// let len = buf.len();
37    /// match unsafe { os_memlock::mlock(ptr, len) } {
38    ///     Ok(()) => {
39    ///         // do work...
40    ///         unsafe { os_memlock::munlock(ptr, len)?; }
41    ///     }
42    ///     Err(e) if e.kind() == std::io::ErrorKind::Unsupported => {
43    ///         // platform/build doesn't support mlock; proceed without locking
44    ///     }
45    ///     Err(e) => return Err(e),
46    /// }
47    /// # Ok(()) }
48    /// ```
49    pub unsafe fn mlock(addr: *const c_void, len: usize) -> io::Result<()> {
50        if len == 0 {
51            // Treat zero-length as a no-op success for ergonomic callers.
52            return Ok(());
53        }
54        // Safety:
55        // - We do not dereference `addr`.
56        // - Caller guarantees `(addr, len)` is a valid region they own during the call.
57        let rc = unsafe { libc::mlock(addr, len) };
58        if rc == 0 {
59            Ok(())
60        } else {
61            Err(io::Error::last_os_error())
62        }
63    }
64
65    /// Unlock the pages containing the specified memory region.
66    ///
67    /// Returns:
68    /// - `Ok(())` on success
69    /// - `Err(...)` with `last_os_error()` on failure
70    /// - `Err(Unsupported)` on platforms where this call is not available
71    ///
72    /// # Safety
73    /// The caller must ensure that `(addr, len)` refers to a valid, non-null memory
74    /// region owned by this process for the duration of the call, and that the region
75    /// is not deallocated, unmapped, or remapped concurrently.
76    pub unsafe fn munlock(addr: *const c_void, len: usize) -> io::Result<()> {
77        if len == 0 {
78            // Treat zero-length as a no-op success for ergonomic callers.
79            return Ok(());
80        }
81        // Safety: same preconditions as `mlock`.
82        let rc = unsafe { libc::munlock(addr, len) };
83        if rc == 0 {
84            Ok(())
85        } else {
86            Err(io::Error::last_os_error())
87        }
88    }
89
90    /// Best-effort advisory to exclude the memory region from core dumps.
91    ///
92    /// On Linux, this wraps `madvise(MADV_DONTDUMP)`. On other Unix targets,
93    /// this returns `Unsupported`.
94    ///
95    /// Returns:
96    /// - `Ok(())` when the hint is applied
97    /// - `Err(...)` with `last_os_error()` if the call failed
98    /// - `Err(Unsupported)` if not supported on this platform
99    ///
100    /// # Safety
101    /// The caller must ensure that `(addr, len)` denotes a valid memory mapping for
102    /// this process and that the region is not deallocated or remapped concurrently.
103    #[cfg(target_os = "linux")]
104    pub unsafe fn madvise_dontdump(addr: *mut c_void, len: usize) -> io::Result<()> {
105        if len == 0 {
106            return Ok(());
107        }
108        // Safety:
109        // - We do not dereference `addr`.
110        // - Caller guarantees `(addr, len)` is a valid region they own during the call.
111        let rc = libc::madvise(addr, len, libc::MADV_DONTDUMP);
112        if rc == 0 {
113            Ok(())
114        } else {
115            Err(io::Error::last_os_error())
116        }
117    }
118
119    /// See `madvise_dontdump` above. On non-Linux Unix targets, this is unsupported.
120    #[cfg(not(target_os = "linux"))]
121    /// # Safety
122    /// This function is marked unsafe for signature consistency. On non-Linux Unix
123    /// targets it always returns `Unsupported`; callers compiling cross-platform
124    /// must still treat `(addr, len)` as potentially unsafe inputs.
125    pub unsafe fn madvise_dontdump(_addr: *mut c_void, _len: usize) -> io::Result<()> {
126        super::unsupported("madvise(MADV_DONTDUMP) unsupported on this platform")
127    }
128}
129
130#[cfg(not(unix))]
131mod non_unix {
132    use super::{c_void, io};
133
134    /// # Safety
135    /// This function is marked unsafe for signature consistency across platforms.
136    /// On non-Unix targets it always returns `Unsupported`; callers compiling
137    /// cross-platform must still treat `(addr, len)` as potentially unsafe inputs.
138    pub unsafe fn mlock(_addr: *const c_void, _len: usize) -> io::Result<()> {
139        super::unsupported("mlock unsupported on this platform")
140    }
141
142    /// # Safety
143    /// This function is marked unsafe for signature consistency across platforms.
144    /// On non-Unix targets it always returns `Unsupported`; callers compiling
145    /// cross-platform must still treat `(addr, len)` as potentially unsafe inputs.
146    pub unsafe fn munlock(_addr: *const c_void, _len: usize) -> io::Result<()> {
147        super::unsupported("munlock unsupported on this platform")
148    }
149
150    /// # Safety
151    /// This function is marked unsafe for signature consistency across platforms.
152    /// On non-Unix targets it always returns `Unsupported`; callers compiling
153    /// cross-platform must still treat `(addr, len)` as potentially unsafe inputs.
154    pub unsafe fn madvise_dontdump(_addr: *mut c_void, _len: usize) -> io::Result<()> {
155        super::unsupported("madvise(MADV_DONTDUMP) unsupported on this platform")
156    }
157}
158
159// Re-export platform module functions at the crate root for a stable API.
160#[cfg(unix)]
161#[cfg_attr(docsrs, doc(cfg(unix)))]
162pub use unix::{madvise_dontdump, mlock, munlock};
163
164#[cfg(not(unix))]
165#[cfg_attr(docsrs, doc(cfg(not(unix))))]
166pub use non_unix::{madvise_dontdump, mlock, munlock};