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#."#]
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};