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};