diff --git a/changelog/2559.added.md b/changelog/2559.added.md new file mode 100644 index 0000000000..6bb1d005fe --- /dev/null +++ b/changelog/2559.added.md @@ -0,0 +1 @@ +Add `sys::mman::MmapAdvise` `MADV_PAGEOUT`, `MADV_COLD`, `MADV_WIPEONFORK`, `MADV_KEEPONFORK` for Linux and Android targets diff --git a/src/sys/mman.rs b/src/sys/mman.rs index 779e9a2172..833e8e182d 100644 --- a/src/sys/mman.rs +++ b/src/sys/mman.rs @@ -316,6 +316,18 @@ libc_enum! { #[cfg(apple_targets)] #[allow(missing_docs)] MADV_CAN_REUSE, + /// Reclaim the address range when applicable. + #[cfg(linux_android)] + MADV_PAGEOUT, + /// Deactivate the address range when applicable. + #[cfg(linux_android)] + MADV_COLD, + /// After fork, the adress range is zero filled. + #[cfg(linux_android)] + MADV_WIPEONFORK, + /// Undo `MADV_WIPEONFORK` when it applied. + #[cfg(linux_android)] + MADV_KEEPONFORK, } } diff --git a/test/sys/test_mman.rs b/test/sys/test_mman.rs index c5116238de..ad70e7ed37 100644 --- a/test/sys/test_mman.rs +++ b/test/sys/test_mman.rs @@ -156,3 +156,45 @@ fn test_mremap_dontunmap() { std::slice::from_raw_parts_mut(mem.cast().as_ptr(), 10 * ONE_K) }; } + +#[test] +#[cfg(target_os = "linux")] +fn test_madv_wipeonfork() { + use nix::libc::size_t; + use nix::sys::mman::{madvise, MmapAdvise}; + use nix::unistd::{fork, ForkResult}; + use std::num::NonZeroUsize; + + const ONE_K: size_t = 1024; + let ten_one_k = NonZeroUsize::new(10 * ONE_K).unwrap(); + let slice: &mut [u8] = unsafe { + let mem = mmap_anonymous( + None, + ten_one_k, + ProtFlags::PROT_READ | ProtFlags::PROT_WRITE, + MapFlags::MAP_PRIVATE, + ) + .unwrap(); + madvise(mem, ONE_K, MmapAdvise::MADV_WIPEONFORK) + .expect("madvise failed"); + std::slice::from_raw_parts_mut(mem.as_ptr().cast(), ONE_K) + }; + slice[ONE_K - 1] = 0xFF; + let _m = crate::FORK_MTX.lock(); + + unsafe { + let res = fork().expect("fork failed"); + match res { + ForkResult::Child => { + // that s the whole point of MADV_WIPEONFORK + assert_eq!(slice[ONE_K - 1], 0x00); + libc::_exit(0); + } + ForkResult::Parent { child } => { + nix::sys::signal::kill(child, nix::sys::signal::SIGTERM) + .unwrap(); + let _ = nix::sys::wait::wait().unwrap(); + } + } + } +}