Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add wrapper for mremap #1306

Merged
merged 1 commit into from Oct 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -5,6 +5,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased] - ReleaseDate
### Added
- Added `mremap` (#[1306](https://github.com/nix-rust/nix/pull/1306))
### Changed
### Fixed
### Removed
Expand Down
35 changes: 35 additions & 0 deletions src/sys/mman.rs
Expand Up @@ -139,6 +139,17 @@ libc_bitflags!{
}
}

#[cfg(target_os = "linux")]
libc_bitflags!{
/// Options for `mremap()`.
pub struct MRemapFlags: c_int {
/// Permit the kernel to relocate the mapping to a new virtual address, if necessary.
MREMAP_MAYMOVE;
/// Place the mapping at exactly the address specified in `new_address`.
MREMAP_FIXED;
}
}

libc_enum!{
/// Usage information for a range of memory to allow for performance optimizations by the kernel.
///
Expand Down Expand Up @@ -315,6 +326,30 @@ pub unsafe fn mmap(addr: *mut c_void, length: size_t, prot: ProtFlags, flags: Ma
}
}

/// Expands (or shrinks) an existing memory mapping, potentially moving it at
/// the same time.
///
/// # Safety
///
/// See the `mremap(2)` [man page](https://man7.org/linux/man-pages/man2/mremap.2.html) for
/// detailed requirements.
#[cfg(target_os = "linux")]
pub unsafe fn mremap(
addr: *mut c_void,
old_size: size_t,
new_size: size_t,
flags: MRemapFlags,
new_address: Option<* mut c_void>,
) -> Result<*mut c_void> {
let ret = libc::mremap(addr, old_size, new_size, flags.bits(), new_address.unwrap_or(std::ptr::null_mut()));

if ret == libc::MAP_FAILED {
Err(Error::Sys(Errno::last()))
} else {
Ok(ret)
}
}

/// remove a mapping
///
/// # Safety
Expand Down
80 changes: 80 additions & 0 deletions test/sys/test_mman.rs
@@ -0,0 +1,80 @@
use nix::Error;
use nix::libc::{c_void, size_t};
use nix::sys::mman::{mmap, MapFlags, ProtFlags};

#[cfg(target_os = "linux")]
use nix::sys::mman::{mremap, MRemapFlags};

#[test]
fn test_mmap_anonymous() {
let ref mut byte = unsafe {
let ptr = mmap(std::ptr::null_mut(), 1,
ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
MapFlags::MAP_PRIVATE | MapFlags::MAP_ANONYMOUS, -1, 0)
.unwrap();
*(ptr as * mut u8)
};
assert_eq !(*byte, 0x00u8);
*byte = 0xffu8;
assert_eq !(*byte, 0xffu8);
}

#[test]
#[cfg(target_os = "linux")]
fn test_mremap_grow() {
const ONE_K : size_t = 1024;
let slice : &mut[u8] = unsafe {
let mem = mmap(std::ptr::null_mut(), ONE_K,
ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
MapFlags::MAP_ANONYMOUS | MapFlags::MAP_PRIVATE, -1, 0)
.unwrap();
std::slice::from_raw_parts_mut(mem as * mut u8, ONE_K)
};
assert_eq !(slice[ONE_K - 1], 0x00);
slice[ONE_K - 1] = 0xFF;
assert_eq !(slice[ONE_K - 1], 0xFF);

let slice : &mut[u8] = unsafe {
let mem = mremap(slice.as_mut_ptr() as * mut c_void, ONE_K, 10 * ONE_K,
MRemapFlags::MREMAP_MAYMOVE, None)
.unwrap();
std::slice::from_raw_parts_mut(mem as * mut u8, 10 * ONE_K)
};

// The first KB should still have the old data in it.
assert_eq !(slice[ONE_K - 1], 0xFF);

// The additional range should be zero-init'd and accessible.
assert_eq !(slice[10 * ONE_K - 1], 0x00);
slice[10 * ONE_K - 1] = 0xFF;
assert_eq !(slice[10 * ONE_K - 1], 0xFF);
}

#[test]
#[cfg(target_os = "linux")]
fn test_mremap_shrink() {
const ONE_K : size_t = 1024;
let slice : &mut[u8] = unsafe {
let mem = mmap(std::ptr::null_mut(), 10 * ONE_K,
ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
MapFlags::MAP_ANONYMOUS | MapFlags::MAP_PRIVATE, -1, 0)
.unwrap();
std::slice::from_raw_parts_mut(mem as * mut u8, ONE_K)
};
assert_eq !(slice[ONE_K - 1], 0x00);
slice[ONE_K - 1] = 0xFF;
assert_eq !(slice[ONE_K - 1], 0xFF);

let slice : &mut[u8] = unsafe {
let mem = mremap(slice.as_mut_ptr() as * mut c_void, 10 * ONE_K, ONE_K,
MRemapFlags::empty(), None)
.unwrap();
// Since we didn't supply MREMAP_MAYMOVE, the address should be the
// same.
assert_eq !(mem, slice.as_mut_ptr() as * mut c_void);
std::slice::from_raw_parts_mut(mem as * mut u8, ONE_K)
};

// The first KB should still be accessible and have the old data in it.
assert_eq !(slice[ONE_K - 1], 0xFF);
}