Skip to main content

Module protected

Module protected 

Source
Available on crate feature protected only.
Expand description

§Memory protection utilities

Provides access to the memory locking system calls, such as mlock() and mprotect() on UNIX-like systems, VirtualLock() and VirtualProtect() on Windows. Similar to libsodium’s sodium_mlock and sodium_mprotect_* functions.

On Linux, sets MADV_DONTDUMP with madvise() on locked regions.

The protected memory features are available on Unix and Windows targets with the protected feature flag enabled. This feature is enabled by default.

§Bottom line

  • Use protected memory for long-lived secrets such as private keys, key-encryption keys, password-hash inputs, and session keys.
  • Locked memory asks the OS to keep those pages resident in RAM, reducing the chance that secret bytes are written to swap.
  • On Linux, locked memory is also marked with MADV_DONTDUMP, reducing the chance that secret bytes appear in ordinary core dumps.
  • Protected allocations are surrounded by no-access guard pages, which can turn some out-of-bounds reads or writes into immediate process faults.
  • Read-only and no-access modes change OS page permissions, so invalid reads or writes can fault instead of silently exposing or corrupting data.
  • Explicit zeroization preserves the value’s lock and page-protection state.
  • Dropping a protected value zeroizes its allocation and, if it is locked, unlocks it exactly once before releasing it.
  • If cleanup cannot make memory writable, restore its protection, or unlock it, the process aborts rather than continuing with uncertain secret-memory state.
  • It does not make bytes invisible to the current process, privileged OS tooling, debuggers, other processes with permission to inspect this process’s address space, or copies made before data enters protected memory.
  • It is heavier than ordinary allocation: each protected allocation uses page-aligned storage with guard pages, and protection changes require fallible system calls.
  • Platform behavior differs: Linux gets best-effort dump exclusion with MADV_DONTDUMP; macOS and other Unix-like targets use mlock() and mprotect() without that dump flag; Windows uses VirtualLock() and VirtualProtect().

§When to use protected memory

Protected memory is most useful for secrets that remain in memory after an operation returns. It gives the operating system more information about how those bytes should be handled and makes accidental misuse easier to catch.

The tradeoff is cost and complexity: small values can consume multiple pages of virtual memory, protection changes require system calls, and those system calls can fail because of platform limits or permissions. For short-lived buffers that are created, used, and dropped immediately, zeroizing ordinary stack or heap storage may be simpler and faster.

§What protection means in practice

These APIs reduce exposure, but they do not make secret bytes invisible to the process that owns them. Code with a valid reference can still read read-write memory, and copies made before a value enters protected memory are outside this module’s control. For example, NewLockedFromSlice::from_slice_into_locked copies the source slice into a protected allocation; callers remain responsible for the lifetime and cleanup policy of the original slice.

Protected memory also is not a cross-process isolation mechanism. Another process’s ability to inspect these bytes is determined by the operating system’s process-memory access controls, such as debugger permissions, sandbox policy, user identity, and privileges.

In practice, a protected value is an owned heap allocation whose state is tracked in the type: locked or unlocked, and read-write, read-only, or no-access. Accessor methods are only available for states where that access is valid, and direct memory access that bypasses the type system can still fault if it violates the active OS page protections.

§Platform notes

On Linux, locking a region also makes a best-effort madvise() call with MADV_DONTDUMP, and unlocking reverses that with MADV_DODUMP. This keeps the locked pages out of ordinary core dumps when the kernel accepts the advice, but it is not a general crash-reporting or privileged-debugger boundary.

On macOS and other Unix-like targets, this module uses mlock(), munlock(), and mprotect(), but it does not set a dump-exclusion flag. Locking is still subject to the process memory-locking limit, which can be low by default. If that limit is exceeded, protected allocation or locking returns an error.

On Windows, this module uses VirtualLock(), VirtualUnlock(), and VirtualProtect(). VirtualLock() pins pages in the process working set and can fail when the process exceeds the working-set limits enforced by the OS. There is no MADV_DONTDUMP equivalent in this module.

If the serde feature is enabled, the serde::Deserialize and serde::Serialize traits will be implemented for HeapBytes and HeapByteArray.

§Example

use dryoc::protected::*;

// Create a read-only, locked region of memory
let readonly_locked = HeapBytes::from_slice_into_readonly_locked(b"some locked bytes")
    .expect("failed to get locked bytes");

// ... now do stuff with `readonly_locked` ...
println!("{:?}", readonly_locked.as_slice());

§Protection features

The type safe API uses traits to guard against misuse of protected memory. For example, memory that is set as read-only can be accessed with immutable accessors (such as .as_slice() or .as_array()), but not with mutable accessors like .as_mut_slice() or .as_mut_array().

use dryoc::protected::*;

// Create a read-only, locked region of memory
let readonly_locked = HeapBytes::from_slice_into_readonly_locked(b"some locked bytes")
    .expect("failed to get locked bytes");

// Try to access the memory mutably
println!("{:?}", readonly_locked.as_mut_slice()); // fails to compile, cannot access mutably

Memory that has been protected as read-only or no-access will cause the process to crash if you attempt to access the memory improperly. To test this, try the following code (which requires an unsafe block):

use dryoc::protected::*;

// Create a read-only, locked region of memory
let readonly_locked = HeapBytes::from_slice_into_readonly_locked(b"some locked bytes")
    .expect("failed to get locked bytes");

// Write to a protected region of memory, causing a crash.
unsafe {
    std::ptr::write(readonly_locked.as_slice().as_ptr() as *mut u8, 0) // <- crash happens here
};

Running the code above produces as signal: 10, SIGBUS: access to undefined memory panic.

Re-exports§

pub use crate::types::*;
pub use ptypes::*;

Modules§

ptypes
Short-hand type aliases for protected types.

Structs§

HeapByteArray
Provides a heap-allocated, fixed-length, page-aligned memory region.
HeapBytes
Provides a heap-allocated, resizable memory region.
PageAlignedAllocator
Custom page-aligned allocator implementation. Creates blocks of page-aligned heap-allocated memory regions, with no-access pages before and after the allocated region of memory. Allocations whose requested alignment does not divide the host page size are rejected.
Protected
Holds a protected region of memory. Does not implement Copy or Debug. Accessible states implement Clone when the backing storage supports it; each clone has a distinct allocation.

Traits§

Lock
Protected region of memory that can be locked.
Lockable
A region of memory that can be locked, but is not yet protected. In order to lock the memory, it may require making a copy.
NewLocked
Bytes which can be allocated and protected.
NewLockedFromSlice
Create a new region of protected memory from a slice.
ProtectNoAccess
Protected region of memory that can be set as no-access. Must be unlocked.
ProtectReadOnly
Protected region of memory that can be set as read-only.
ProtectReadWrite
Protected region of memory that can be set as read-write.
Unlock
Protected region of memory that is already locked and can be unlocked.