Expand description
§os-memlock
Quick links:
- Detailed guide: docs/overview.md
- API docs (docs.rs): https://docs.rs/os-memlock
- Examples:
- examples/simple.rs
- examples/locked_vec.rs
§Docs
The detailed guide covers:
- Safety model and caller obligations: docs/overview.md#safety-model-and-caller-obligations
- Platform support and behavior: docs/overview.md#cross-platform-behavior
- Usage patterns and RAII wrappers: docs/overview.md#usage-patterns
- Error model and diagnostics: docs/overview.md#error-model
- Testing and CI: docs/overview.md#testing-and-ci
- Security considerations and threat model: docs/overview.md#security-considerations
- Integration checklist: docs/overview.md#integration-checklist
Small, focused crate providing thin, unsafe wrappers around OS memory-locking syscalls:
mlock
/munlock
(prevent swapping)madvise_dontdump
(best-effort exclusion from core dumps: LinuxMADV_DONTDUMP
, FreeBSDMADV_NOCORE
)
This crate isolates the minimal unsafe FFI surface so higher-level modules can remain
#![forbid(unsafe_code)]
. The public functions are intentionally unsafe
to make
pointer-safety obligations explicit to callers.
§Purpose
- Provide a tiny, audit-friendly layer over platform syscalls used to lock memory pages and apply dump-exclusion hints.
- Keep all
unsafe
and FFI details in a single, well-documented crate so the rest of the codebase can use a safe abstraction that validates inputs before calling into this crate when appropriate. - Expose a stable, minimal API that is easy to reason about and to wrap in safer helpers.
§Crate API (surface)
The crate re-exports the platform-specific implementations at the crate root:
-
unsafe fn mlock(addr: *const std::os::raw::c_void, len: usize) -> std::io::Result<()>
- Lock the pages containing the memory region so they are not swapped out.
- On unsupported platforms, returns
Err(io::ErrorKind::Unsupported)
.
-
unsafe fn munlock(addr: *const std::os::raw::c_void, len: usize) -> std::io::Result<()>
- Unlock the pages, reversing
mlock
. - On unsupported platforms, returns
Err(io::ErrorKind::Unsupported)
.
- Unlock the pages, reversing
-
unsafe fn madvise_dontdump(addr: *mut std::os::raw::c_void, len: usize) -> std::io::Result<()>
- Best-effort hint to exclude a mapping from core dumps (Linux:
MADV_DONTDUMP
, FreeBSD:MADV_NOCORE
). - On unsupported platforms, returns
Err(io::ErrorKind::Unsupported)
.
- Best-effort hint to exclude a mapping from core dumps (Linux:
Notes on signatures:
- The functions intentionally use raw pointers and
usize
lengths to mirror the OS call semantics and to avoid hiding important safety obligations behind false safety. - Zero-length regions are treated as a no-op and return
Ok(())
for ergonomic callers.
§Safety contract
All functions are unsafe
. Callers must uphold the following preconditions for each call:
-
The
(addr, len)
pair must denote a valid memory region that the caller owns for the duration of the call and for as long as the OS considers the lock to be held.- The range must be mapped into the process address space and addressable (initialized) memory. Passing invalid pointers is undefined behavior at the OS/FFI boundary.
-
The memory region must not be concurrently deallocated, unmapped, or remapped while the system call is in-flight. Concurrent unmapping or reallocation may cause the OS call to operate on a different mapping and can lead to undefined behavior or kernel errors.
-
Callers must ensure alignment and fractional-page concerns are addressed if required by their higher-level policy; the OS operates at page granularity, but
mlock
is defined on an arbitrary address and length. -
When using
mlock
to protect secrets, callers must consider:- Handling and limiting locked memory lifetime.
- Zeroizing secrets before
munlock
/drop, where appropriate. - Observability:
mlock
failures may be transient or platform-dependent — be prepared to treatErr(Unsupported)
and other error kinds as operational signals.
-
For
madvise_dontdump
:- This is advisory and best-effort; the kernel may ignore or reject the hint.
- Use it as a privacy/operational enhancement, not a strict security boundary.
§Platform support & behavior
-
Unix (Linux, *BSD, macOS):
mlock
andmunlock
call through tolibc::mlock
andlibc::munlock
.madvise_dontdump
:- On Linux: wraps
madvise(..., MADV_DONTDUMP)
. - On FreeBSD: wraps
madvise(..., MADV_NOCORE)
. - On macOS and other Unix targets: returns
Err(io::ErrorKind::Unsupported)
.
- On Linux: wraps
-
Non-Unix platforms:
- All functions return
Err(io::ErrorKind::Unsupported)
. - The function signatures exist to preserve a consistent cross-platform API; callers
should handle
Unsupported
gracefully.
- All functions return
§Examples (usage guidance)
- Minimal unsafe call (illustrative — not a full safety wrapper):
Use mlock
to lock a buffer you control. Wrap calls in unsafe
and uphold the safety contract:
unsafe { os_memlock::mlock(buf.as_ptr() as *const _, buf.len())?; }
Later, before drop/unmapping:
unsafe { os_memlock::munlock(buf.as_ptr() as *const _, buf.len())?; }
Call madvise_dontdump
on Linux/FreeBSD to reduce chance of core dump exposure:
unsafe { os_memlock::madvise_dontdump(buf.as_mut_ptr() as *mut _, buf.len())?; }
- Higher-level recommended pattern:
- Prefer a safe wrapper in your application that:
- Accepts owned buffers (e.g., a wrapper type),
- Ensures the buffer lives for the duration of the lock,
- Calls
mlock
at allocation or when the secret is installed, - Zeroizes the content before
munlock
and ensuresmunlock
is called (via Drop).
- See
src/mem/locked.rs
in this repository for an example of a safeLockedVec
style wrapper.
- Prefer a safe wrapper in your application that:
§Error handling and diagnostics
io::ErrorKind::Unsupported
signals platform/build-time unavailability; do not treat it as a panic-worthy error unless your feature policy requires it.- Other OS errors (e.g., resource limits) will be returned as
io::Error
with kernelerrno
translated intostd::io::Error
. These must be handled by the caller or propagated with context.
§Testing notes
- Unit tests in the repository provide behavior verification in environments where syscalls are available. Tests exercise both success paths and fallback behavior.
- Where platform syscalls are unavailable or require elevated privileges, tests should mock or stub the syscall provider rather than invoking real FFI.
§macOS process-wide core-dump helper
macOS does not expose a per-region dump-exclusion advice via madvise
(there is no MADV_DONTDUMP
/MADV_NOCORE
on Darwin). To offer a practical alternative, this crate provides opt-in, process-wide helpers:
-
disable_core_dumps_for_process()
:- Platform: macOS-only behavior; on other platforms this function returns
io::ErrorKind::Unsupported
. - Effect: Sets the process
RLIMIT_CORE
soft limit to 0 to disable generation of core dumps for the process. - Safety: Exposed as a safe function because it has no pointer/lifetime obligations; it returns
io::Result<()>
on failure/success. - Scope: Process-wide and inherited by child processes. This is not a per-buffer or per-region setting.
- Privileges: Lowering the soft limit to 0 is typically permitted; raising limits back may require additional privileges or be disallowed by system policy.
- Operational notes: In sandboxed or restricted environments, changing resource limits may fail. Handle errors and decide whether to degrade gracefully or fail closed, per your policy.
- Platform: macOS-only behavior; on other platforms this function returns
-
disable_core_dumps_with_guard() -> CoreDumpsDisabledGuard
:- Platform: macOS-only; on other platforms this function returns
io::ErrorKind::Unsupported
. - Effect: Sets the process
RLIMIT_CORE
soft limit to 0 and returns a guard. When the guard is dropped, the previous limits are restored for the current process. - Scope: Process-wide while active. Child processes forked while disabled inherit the lowered limit and are not automatically “restored” by dropping the guard in the parent.
- Safety: Safe API returning
io::Result<CoreDumpsDisabledGuard>
. - Privileges & operational notes: Same as above; restoration may fail under restrictive policies (restoration errors are best-effort and should be logged by callers if needed).
- Platform: macOS-only; on other platforms this function returns
Recommended usage:
- For temporary disabling (e.g., during sensitive operations), prefer
disable_core_dumps_with_guard()
to ensure restoration even on panic or early returns. - For a whole-process policy, call
disable_core_dumps_for_process()
early in startup. - Combine with
mlock
/munlock
to reduce the risk of secrets being paged to disk. - Log or surface metrics if the helper is unsupported or fails, so you can detect drift from your intended security posture.
§Windows process-wide error-dialog helpers
Windows does not provide a per-region dump-exclusion API analogous to MADV_DONTDUMP
. To improve operational behavior (avoiding certain error UI), this crate provides opt-in, process-wide helpers:
-
set_windows_error_mode(new_mode: u32) -> io::Result<u32>
:- Platform: Windows-only; on other platforms returns
io::ErrorKind::Unsupported
. - Effect: Calls
SetErrorMode
with the provided flags and returns the previous mode. - Scope: Process-wide; inherited by child processes created after the change.
- Notes: This does not control crash dump contents and is not a security feature. It primarily suppresses certain error dialogs.
- Platform: Windows-only; on other platforms returns
-
suppress_windows_error_dialogs_for_process() -> io::Result<u32>
:- Platform: Windows-only; on other platforms returns
io::ErrorKind::Unsupported
. - Effect: Applies a common combination of flags (
SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX
) viaSetErrorMode
and returns the previous mode. - Scope: Process-wide; inherited by child processes created after the change.
- Notes: This is best-effort UX/operational control and is not equivalent to per-region dump exclusion.
- Platform: Windows-only; on other platforms returns
Recommended usage:
- Call early in process startup if you want to suppress Windows error dialogs globally.
- Save and restore the previous mode when temporarily changing settings to limit blast radius.
- Treat these helpers as operational/UX tweaks, not security controls; combine with
mlock
/munlock
for memory handling as needed.
§License
This crate is dual-licensed under Apache-2.0 OR MIT; see Cargo.toml
for details.
Structs§
- Core
Dumps Disabled Guard - RAII guard that disables core dumps on macOS and restores the previous RLIMIT_CORE on drop.
Functions§
- disable_
core_ dumps_ for_ process Non-macOS - Disable core dumps for the current process.
- disable_
core_ dumps_ with_ guard Non-macOS - madvise_
dontdump ⚠Unix - Best-effort advisory to exclude the memory region from core dumps.
- mlock⚠
Unix - Lock the pages containing the specified memory region to prevent swapping.
- munlock⚠
Unix - Unlock the pages containing the specified memory region.
- set_
windows_ error_ mode Non-Windows - Set the Windows process error mode (stub).
- suppress_
windows_ error_ dialogs_ for_ process Non-Windows - Suppress common Windows error dialogs for the current process (stub).