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