Skip to content

Commit

Permalink
Add wrapper for mremap
Browse files Browse the repository at this point in the history
  • Loading branch information
sporksmith committed Oct 13, 2020
1 parent c8e8def commit 6f84f14
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 0 deletions.
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);
}

0 comments on commit 6f84f14

Please sign in to comment.