Skip to content

Commit

Permalink
Add mlockall and munlockall (#872)
Browse files Browse the repository at this point in the history
  • Loading branch information
newpavlov authored Oct 10, 2023
1 parent d012bcf commit 4bb2f4b
Show file tree
Hide file tree
Showing 6 changed files with 182 additions and 0 deletions.
21 changes: 21 additions & 0 deletions src/backend/libc/mm/syscalls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
#[cfg(not(target_os = "redox"))]
use super::types::Advice;
#[cfg(any(linux_kernel, freebsdlike, netbsdlike))]
use super::types::MlockAllFlags;
#[cfg(any(target_os = "emscripten", target_os = "linux"))]
use super::types::MremapFlags;
use super::types::{MapFlags, MprotectFlags, MsyncFlags, ProtFlags};
Expand Down Expand Up @@ -220,3 +222,22 @@ pub(crate) unsafe fn userfaultfd(flags: UserfaultfdFlags) -> io::Result<OwnedFd>
}
ret_owned_fd(userfaultfd(bitflags_bits!(flags)))
}

/// Locks all pages mapped into the address space of the calling process.
///
/// This includes the pages of the code, data and stack segment, as well as shared libraries,
/// user space kernel data, shared memory, and memory-mapped files. All mapped pages are
/// guaranteed to be resident in RAM when the call returns successfully;
/// the pages are guaranteed to stay in RAM until later unlocked.
#[inline]
#[cfg(any(linux_kernel, freebsdlike, netbsdlike))]
pub(crate) fn mlockall(flags: MlockAllFlags) -> io::Result<()> {
unsafe { ret(c::mlockall(bitflags_bits!(flags))) }
}

/// Unlocks all pages mapped into the address space of the calling process.
#[inline]
#[cfg(any(linux_kernel, freebsdlike, netbsdlike))]
pub(crate) fn munlockall() -> io::Result<()> {
unsafe { ret(c::munlockall()) }
}
25 changes: 25 additions & 0 deletions src/backend/libc/mm/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -442,3 +442,28 @@ bitflags! {
const _ = !0;
}
}

#[cfg(any(linux_kernel, freebsdlike, netbsdlike))]
bitflags! {
/// `MCL_*` flags for use with [`mlockall`].
///
/// [`mlockall`]: crate::mm::mlockall
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct MlockAllFlags: u32 {
// libc doesn't define `MCL_ONFAULT` yet.
// const ONFAULT = libc::MCL_ONFAULT;
/// Lock all pages which will become mapped into the address
/// space of the process in the future. These could be, for
/// instance, new pages required by a growing heap and stack
/// as well as new memory-mapped files or shared memory
/// regions.
const FUTURE = bitcast!(libc::MCL_FUTURE);
/// Lock all pages which are currently mapped into the address
/// space of the process.
const CURRENT = bitcast!(libc::MCL_CURRENT);

/// <https://docs.rs/bitflags/latest/bitflags/#externally-defined-flags>
const _ = !0;
}
}
9 changes: 9 additions & 0 deletions src/backend/linux_raw/conv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -621,6 +621,15 @@ impl<'a, Num: ArgNumber> From<crate::backend::mm::types::MlockFlags> for ArgReg<
}
}

#[cfg(feature = "mm")]
#[cfg(any(linux_kernel, freebsdlike, netbsdlike))]
impl<'a, Num: ArgNumber> From<crate::backend::mm::types::MlockAllFlags> for ArgReg<'a, Num> {
#[inline]
fn from(flags: crate::backend::mm::types::MlockAllFlags) -> Self {
c_uint(flags.bits())
}
}

#[cfg(feature = "mm")]
impl<'a, Num: ArgNumber> From<crate::backend::mm::types::MapFlags> for ArgReg<'a, Num> {
#[inline]
Expand Down
28 changes: 28 additions & 0 deletions src/backend/linux_raw/mm/syscalls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
#![allow(unsafe_code)]
#![allow(clippy::undocumented_unsafe_blocks)]

#[cfg(any(linux_kernel, freebsdlike, netbsdlike))]
use super::types::MlockAllFlags;
use super::types::{
Advice, MapFlags, MlockFlags, MprotectFlags, MremapFlags, MsyncFlags, ProtFlags,
UserfaultfdFlags,
Expand Down Expand Up @@ -210,3 +212,29 @@ pub(crate) unsafe fn munlock(addr: *mut c::c_void, length: usize) -> io::Result<
pub(crate) unsafe fn userfaultfd(flags: UserfaultfdFlags) -> io::Result<OwnedFd> {
ret_owned_fd(syscall_readonly!(__NR_userfaultfd, flags))
}

/// Locks all pages mapped into the address space of the calling process.
///
/// This includes the pages of the code, data and stack segment, as well as shared libraries,
/// user space kernel data, shared memory, and memory-mapped files. All mapped pages are
/// guaranteed to be resident in RAM when the call returns successfully;
/// the pages are guaranteed to stay in RAM until later unlocked.
#[inline]
#[cfg(any(linux_kernel, freebsdlike, netbsdlike))]
pub(crate) fn mlockall(flags: MlockAllFlags) -> io::Result<()> {
// When `mlockall` is used with `MCL_ONFAULT | MCL_FUTURE`, the ordering
// of `mlockall` with respect to arbitrary loads may be significant,
// because if a load happens and evokes a fault before the `mlockall`,
// the memory doesn't get locked, but if the load and therefore
// the fault happens after, then the memory does get locked.
// So to be conservative in this regard, we use `syscall` instead
// of `syscall_readonly`
unsafe { ret(syscall!(__NR_mlockall, flags)) }
}

/// Unlocks all pages mapped into the address space of the calling process.
#[inline]
#[cfg(any(linux_kernel, freebsdlike, netbsdlike))]
pub(crate) fn munlockall() -> io::Result<()> {
unsafe { ret(syscall_readonly!(__NR_munlockall)) }
}
34 changes: 34 additions & 0 deletions src/backend/linux_raw/mm/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -262,3 +262,37 @@ bitflags! {
const _ = !0;
}
}

#[cfg(any(linux_kernel, freebsdlike, netbsdlike))]
bitflags! {
/// `MCL_*` flags for use with [`mlockall`].
///
/// [`mlockall`]: crate::mm::mlockall
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct MlockAllFlags: u32 {
/// Used together with `MCL_CURRENT`, `MCL_FUTURE`, or both. Mark
/// all current (with `MCL_CURRENT`) or future (with `MCL_FUTURE`)
/// mappings to lock pages when they are faulted in. When
/// used with `MCL_CURRENT`, all present pages are locked, but
/// `mlockall()` will not fault in non-present pages. When used
/// with `MCL_FUTURE`, all future mappings will be marked to
/// lock pages when they are faulted in, but they will not be
/// populated by the lock when the mapping is created.
/// `MCL_ONFAULT` must be used with either `MCL_CURRENT` or
/// `MCL_FUTURE` or both.
const ONFAULT = linux_raw_sys::general::MCL_ONFAULT;
/// Lock all pages which will become mapped into the address
/// space of the process in the future. These could be, for
/// instance, new pages required by a growing heap and stack
/// as well as new memory-mapped files or shared memory
/// regions.
const FUTURE = linux_raw_sys::general::MCL_FUTURE;
/// Lock all pages which are currently mapped into the address
/// space of the process.
const CURRENT = linux_raw_sys::general::MCL_CURRENT;

/// <https://docs.rs/bitflags/latest/bitflags/#externally-defined-flags>
const _ = !0;
}
}
65 changes: 65 additions & 0 deletions src/mm/mmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ use crate::{backend, io};
use backend::fd::AsFd;
use core::ffi::c_void;

#[cfg(any(linux_kernel, freebsdlike, netbsdlike))]
pub use backend::mm::types::MlockAllFlags;
#[cfg(linux_kernel)]
pub use backend::mm::types::MlockFlags;
#[cfg(any(target_os = "emscripten", target_os = "linux"))]
Expand Down Expand Up @@ -340,3 +342,66 @@ pub unsafe fn mlock_with(ptr: *mut c_void, len: usize, flags: MlockFlags) -> io:
pub unsafe fn munlock(ptr: *mut c_void, len: usize) -> io::Result<()> {
backend::mm::syscalls::munlock(ptr, len)
}

/// Locks all pages mapped into the address space of the calling process.
///
/// This includes the pages of the code, data and stack segment, as well as shared libraries,
/// user space kernel data, shared memory, and memory-mapped files. All mapped pages are
/// guaranteed to be resident in RAM when the call returns successfully;
/// the pages are guaranteed to stay in RAM until later unlocked.
///
/// # References
/// - [POSIX]
/// - [Linux]
/// - [FreeBSD]
/// - [NetBSD]
/// - [OpenBSD]
/// - [DragonFly BSD]
/// - [illumos]
/// - [glibc]
///
/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/mlockall.html
/// [Linux]: https://man7.org/linux/man-pages/man2/mlockall.2.html
/// [FreeBSD]: https://man.freebsd.org/cgi/man.cgi?query=mlockall&sektion=2
/// [NetBSD]: https://man.netbsd.org/mlockall.2
/// [OpenBSD]: https://man.openbsd.org/mlockall.2
/// [DragonFly BSD]: https://man.dragonflybsd.org/?command=mlockall&section=2
/// [illumos]: https://illumos.org/man/3C/mlockall
/// [glibc]: https://www.gnu.org/software/libc/manual/html_node/Page-Lock-Functions.html#index-mlockall
#[cfg(any(linux_kernel, freebsdlike, netbsdlike))]
#[inline]
pub fn mlockall(flags: MlockAllFlags) -> io::Result<()> {
backend::mm::syscalls::mlockall(flags)
}

/// Unlocks all pages mapped into the address space of the calling process.
///
/// # Warnings
///
/// This function is aware of all the memory pages in the process, as if it were a debugger.
/// It unlocks all the pages, which could potentially compromise security assumptions made by
/// code about memory it has encapsulated.
///
/// # References
/// - [POSIX]
/// - [Linux]
/// - [FreeBSD]
/// - [NetBSD]
/// - [OpenBSD]
/// - [DragonFly BSD]
/// - [illumos]
/// - [glibc]
///
/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/munlockall.html
/// [Linux]: https://man7.org/linux/man-pages/man2/munlockall.2.html
/// [FreeBSD]: https://man.freebsd.org/cgi/man.cgi?query=munlockall&sektion=2
/// [NetBSD]: https://man.netbsd.org/munlockall.2
/// [OpenBSD]: https://man.openbsd.org/munlockall.2
/// [DragonFly BSD]: https://man.dragonflybsd.org/?command=munlockall&section=2
/// [illumos]: https://illumos.org/man/3C/munlockall
/// [glibc]: https://www.gnu.org/software/libc/manual/html_node/Page-Lock-Functions.html#index-munlockall
#[cfg(any(linux_kernel, freebsdlike, netbsdlike))]
#[inline]
pub fn munlockall() -> io::Result<()> {
backend::mm::syscalls::munlockall()
}

0 comments on commit 4bb2f4b

Please sign in to comment.