Rewrite mremap system call

This rewrite serves three purposes:
1. Fix some subtle bugs in the old implementation;
2. Implement mremap using mmap and munmap so that mremap can automatically
enjoy new features (e.g., mprotect and memory permissions) once mmap and
munmap support the feature.
3. Write down the invariants hold by VMManager explictly so that the correctness
of the new implementation can be reason more easily.
This commit is contained in:
Tate, Hongliang Tian 2020-06-10 17:06:38 +08:00
parent 28440b0d69
commit 70d3991ff5
6 changed files with 297 additions and 149 deletions

@ -740,8 +740,8 @@ fn do_mremap(
flags: i32, flags: i32,
new_addr: usize, new_addr: usize,
) -> Result<isize> { ) -> Result<isize> {
let flags = MRemapFlags::from_u32(flags as u32)?; let flags = MRemapFlags::from_raw(flags as u32, new_addr)?;
let addr = vm::do_mremap(old_addr, old_size, new_size, flags, new_addr)?; let addr = vm::do_mremap(old_addr, old_size, new_size, flags)?;
Ok(addr as isize) Ok(addr as isize)
} }

@ -52,15 +52,14 @@ pub fn do_mremap(
old_size: usize, old_size: usize,
new_size: usize, new_size: usize,
flags: MRemapFlags, flags: MRemapFlags,
new_addr: usize,
) -> Result<usize> { ) -> Result<usize> {
debug!( debug!(
"mremap: old_addr: {:#x}, old_size: {:#x}, new_size: {:#x}, flags: {:?}, new_addr: {:#x}", "mremap: old_addr: {:#x}, old_size: {:#x}, new_size: {:#x}, flags: {:?}",
old_addr, old_size, new_size, flags, new_addr old_addr, old_size, new_size, flags
); );
let current = current!(); let current = current!();
let mut current_vm = current.vm().lock().unwrap(); let mut current_vm = current.vm().lock().unwrap();
current_vm.mremap(old_addr, old_size, new_size, flags, new_addr) current_vm.mremap(old_addr, old_size, new_size, flags)
} }
pub fn do_brk(addr: usize) -> Result<usize> { pub fn do_brk(addr: usize) -> Result<usize> {

@ -290,7 +290,7 @@ impl ProcessVM {
if !self.process_range.range().contains(addr) { if !self.process_range.range().contains(addr) {
return_errno!(EINVAL, "Beyond valid memory range"); return_errno!(EINVAL, "Beyond valid memory range");
} }
VMMapAddr::Fixed(addr) VMMapAddr::Force(addr)
} else { } else {
if addr == 0 { if addr == 0 {
VMMapAddr::Any VMMapAddr::Any
@ -325,18 +325,14 @@ impl ProcessVM {
old_size: usize, old_size: usize,
new_size: usize, new_size: usize,
flags: MRemapFlags, flags: MRemapFlags,
new_addr: usize,
) -> Result<usize> { ) -> Result<usize> {
let new_addr_option = if flags.contains(MRemapFlags::MREMAP_FIXED) { if let Some(new_addr) = flags.new_addr() {
if !self.process_range.range().contains(new_addr) { if !self.process_range.range().contains(new_addr) {
return_errno!(EINVAL, "new_addr is beyond valid memory range"); return_errno!(EINVAL, "new_addr is beyond valid memory range");
} }
Some(new_addr) }
} else {
None let mremap_option = VMRemapOptions::new(old_addr, old_size, new_size, flags)?;
};
let mremap_option =
VMRemapOptions::new(old_addr, old_size, new_addr_option, new_size, flags)?;
self.mmap_manager.mremap(&mremap_option) self.mmap_manager.mremap(&mremap_option)
} }
@ -379,23 +375,41 @@ impl MMapFlags {
} }
} }
bitflags! { #[derive(Clone, Copy, Debug, PartialEq)]
pub struct MRemapFlags : u32 { pub enum MRemapFlags {
const MREMAP_MAYMOVE = 1; None,
const MREMAP_FIXED = 2; MayMove,
} FixedAddr(usize),
} }
impl MRemapFlags { impl MRemapFlags {
pub fn from_u32(bits: u32) -> Result<MRemapFlags> { pub fn from_raw(raw_flags: u32, new_addr: usize) -> Result<Self> {
let flags = const MREMAP_NONE: u32 = 0;
MRemapFlags::from_bits(bits).ok_or_else(|| errno!(EINVAL, "unknown mremap flags"))?; const MREMAP_MAYMOVE: u32 = 1;
if flags.contains(MRemapFlags::MREMAP_FIXED) && !flags.contains(MRemapFlags::MREMAP_MAYMOVE) const MREMAP_FIXED: u32 = 3;
{
return_errno!(EINVAL, "MREMAP_FIXED was specified without MREMAP_MAYMOVE"); #[deny(unreachable_patterns)]
} let flags = match raw_flags {
MREMAP_NONE => Self::None,
MREMAP_MAYMOVE => Self::MayMove,
MREMAP_FIXED => Self::FixedAddr(new_addr),
_ => return_errno!(EINVAL, "unsupported flags"),
};
Ok(flags) Ok(flags)
} }
pub fn new_addr(&self) -> Option<usize> {
match self {
MRemapFlags::FixedAddr(new_addr) => Some(*new_addr),
_ => None,
}
}
}
impl Default for MRemapFlags {
fn default() -> Self {
MRemapFlags::None
}
} }
bitflags! { bitflags! {

@ -4,6 +4,7 @@ use super::*;
pub enum VMInitializer { pub enum VMInitializer {
DoNothing(), DoNothing(),
FillZeros(), FillZeros(),
CopyFrom { range: VMRange },
LoadFromFile { file: FileRef, offset: usize }, LoadFromFile { file: FileRef, offset: usize },
} }
@ -24,6 +25,14 @@ impl VMInitializer {
*b = 0; *b = 0;
} }
} }
VMInitializer::CopyFrom { range } => {
let src_slice = unsafe { range.as_slice() };
let copy_len = min(buf.len(), src_slice.len());
buf[..copy_len].copy_from_slice(&src_slice[..copy_len]);
for b in &mut buf[copy_len..] {
*b = 0;
}
}
VMInitializer::LoadFromFile { file, offset } => { VMInitializer::LoadFromFile { file, offset } => {
// TODO: make sure that read_at does not move file cursor // TODO: make sure that read_at does not move file cursor
let len = file let len = file
@ -41,8 +50,9 @@ impl VMInitializer {
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
pub enum VMMapAddr { pub enum VMMapAddr {
Any, // Free to choose any address Any, // Free to choose any address
Hint(usize), // Prefer the given address Hint(usize), // Prefer the address, but can use other address
Fixed(usize), // Must be the given address Need(usize), // Need to use the address, otherwise report error
Force(usize), // Force using the address by munmap first
} }
impl Default for VMMapAddr { impl Default for VMMapAddr {
@ -88,11 +98,11 @@ impl VMMapOptionsBuilder {
let addr = align_down(addr, PAGE_SIZE); let addr = align_down(addr, PAGE_SIZE);
VMMapAddr::Hint(addr) VMMapAddr::Hint(addr)
} }
VMMapAddr::Fixed(addr) => { VMMapAddr::Need(addr_) | VMMapAddr::Force(addr_) => {
if addr % align != 0 { if addr_ % align != 0 {
return_errno!(EINVAL, "unaligned addr for fixed mmap"); return_errno!(EINVAL, "unaligned addr for fixed mmap");
} }
VMMapAddr::Fixed(addr) addr
} }
} }
}; };
@ -127,7 +137,6 @@ impl VMMapOptions {
pub struct VMRemapOptions { pub struct VMRemapOptions {
old_addr: usize, old_addr: usize,
old_size: usize, old_size: usize,
new_addr: Option<usize>,
new_size: usize, new_size: usize,
flags: MRemapFlags, flags: MRemapFlags,
} }
@ -136,7 +145,6 @@ impl VMRemapOptions {
pub fn new( pub fn new(
old_addr: usize, old_addr: usize,
old_size: usize, old_size: usize,
new_addr: Option<usize>,
new_size: usize, new_size: usize,
flags: MRemapFlags, flags: MRemapFlags,
) -> Result<Self> { ) -> Result<Self> {
@ -152,14 +160,11 @@ impl VMRemapOptions {
} else { } else {
align_up(old_size, PAGE_SIZE) align_up(old_size, PAGE_SIZE)
}; };
let new_addr = { if let Some(new_addr) = flags.new_addr() {
if let Some(addr) = new_addr { if new_addr % PAGE_SIZE != 0 {
if addr % PAGE_SIZE != 0 {
return_errno!(EINVAL, "unaligned new address"); return_errno!(EINVAL, "unaligned new address");
} }
} }
new_addr
};
let new_size = if new_size == 0 { let new_size = if new_size == 0 {
return_errno!(EINVAL, "invalid new size"); return_errno!(EINVAL, "invalid new size");
} else { } else {
@ -168,7 +173,6 @@ impl VMRemapOptions {
Ok(Self { Ok(Self {
old_addr, old_addr,
old_size, old_size,
new_addr,
new_size, new_size,
flags, flags,
}) })
@ -186,15 +190,46 @@ impl VMRemapOptions {
self.new_size self.new_size
} }
pub fn new_addr(&self) -> Option<usize> {
self.new_addr
}
pub fn flags(&self) -> MRemapFlags { pub fn flags(&self) -> MRemapFlags {
self.flags self.flags
} }
} }
/// Memory manager.
///
/// VMManager provides useful memory management APIs such as mmap, munmap, mremap, etc.
///
/// # Invariants
///
/// Behind the scene, VMManager maintains a list of VMRange that have been allocated.
/// (denoted as `self.sub_ranges`). To reason about the correctness of VMManager, we give
/// the set of invariants hold by VMManager.
///
/// 1. The rule of sentry:
/// ```
/// self.range.begin() == self.sub_ranges[0].start() == self.sub_ranges[0].end()
/// ```
/// and
/// ```
/// self.range.end() == self.sub_ranges[N-1].start() == self.sub_ranges[N-1].end()
/// ```
/// where `N = self.sub_ranges.len()`.
///
/// 2. The rule of non-emptyness:
/// ```
/// self.sub_ranges[i].size() > 0, for 1 <= i < self.sub_ranges.len() - 1
/// ```
///
/// 3. The rule of ordering:
/// ```
/// self.sub_ranges[i].end() <= self.sub_ranges[i+1].start() for 0 <= i < self.sub_ranges.len() - 1
/// ```
///
/// 4. The rule of non-mergablility:
/// ```
/// self.sub_ranges[i].end() != self.sub_ranges[i+1].start() for 1 <= i < self.sub_ranges.len() - 2
/// ```
///
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct VMManager { pub struct VMManager {
range: VMRange, range: VMRange,
@ -223,7 +258,7 @@ impl VMManager {
let addr = *options.addr(); let addr = *options.addr();
let size = *options.size(); let size = *options.size();
if let VMMapAddr::Fixed(addr) = addr { if let VMMapAddr::Force(addr) = addr {
self.munmap(addr, size)?; self.munmap(addr, size)?;
} }
@ -238,82 +273,12 @@ impl VMManager {
options.initializer.init_slice(buf)?; options.initializer.init_slice(buf)?;
} }
// After initializing, we can safely add the new subrange // After initializing, we can safely insert the new subrange
self.sub_ranges.insert(insert_idx, new_subrange); self.insert_new_subrange(insert_idx, new_subrange);
Ok(new_subrange_addr) Ok(new_subrange_addr)
} }
pub fn mremap(&mut self, options: &VMRemapOptions) -> Result<usize> {
let old_addr = options.old_addr();
let old_size = options.old_size();
let new_size = options.new_size();
let (vm_subrange, idx) = {
let idx = self.find_mmap_region_idx(old_addr)?;
let vm_subrange = self.sub_ranges[idx];
if (vm_subrange.end() - old_addr < old_size) {
// Across the vm range
return_errno!(EFAULT, "can not remap across vm range");
} else if (vm_subrange.end() - old_addr == old_size) {
// Exactly the vm range
(vm_subrange, idx)
} else {
// Part of the vm range
let old_subrange = VMRange::new(old_addr, old_addr + old_size)?;
let (subranges, offset) = {
let mut subranges = vm_subrange.subtract(&old_subrange);
let idx = subranges
.iter()
.position(|subrange| old_subrange.start() < subrange.start())
.unwrap_or_else(|| subranges.len());
subranges.insert(idx, old_subrange);
(subranges, idx)
};
self.sub_ranges.splice(idx..=idx, subranges.iter().cloned());
(old_subrange, idx + offset)
}
};
// Remap with a fixed new_addr, move it to new_addr
if let Some(new_addr) = options.new_addr() {
let new_subrange = VMRange::new(new_addr, new_addr + new_size)?;
if vm_subrange.overlap_with(&new_subrange) {
return_errno!(EINVAL, "old/new vm range overlap");
}
let new_addr = VMMapAddr::Fixed(new_addr);
let (insert_idx, free_subrange) = self.find_free_subrange(new_size, new_addr)?;
let new_subrange = self.alloc_subrange_from(new_size, new_addr, &free_subrange);
return self.move_mmap_region(&vm_subrange, (insert_idx, &new_subrange));
}
// Remap without a fixed new_addr
if old_size > new_size {
// Shrink the mmap range, just unmap the useless range
self.munmap(old_addr + new_size, old_size - new_size)?;
Ok(old_addr)
} else if old_size == new_size {
// Same size, do nothing
Ok(old_addr)
} else {
// Need to expand the mmap range, check if we can expand it
if let Some(next_subrange) = self.sub_ranges.get(idx + 1) {
let expand_size = new_size - old_size;
if next_subrange.start() - vm_subrange.end() >= expand_size {
// Memory between subranges is enough, resize it
let vm_subrange = self.sub_ranges.get_mut(idx).unwrap();
vm_subrange.resize(new_size);
return Ok(vm_subrange.start());
}
}
// Not enough memory to expand, must move it to a new place
if !options.flags().contains(MRemapFlags::MREMAP_MAYMOVE) {
return_errno!(ENOMEM, "not enough memory to expand");
}
let new_addr = VMMapAddr::Any;
let (insert_idx, free_subrange) = self.find_free_subrange(new_size, new_addr)?;
let new_subrange = self.alloc_subrange_from(new_size, new_addr, &free_subrange);
self.move_mmap_region(&vm_subrange, (insert_idx, &new_subrange))
}
}
pub fn munmap(&mut self, addr: usize, size: usize) -> Result<()> { pub fn munmap(&mut self, addr: usize, size: usize) -> Result<()> {
let size = { let size = {
if size == 0 { if size == 0 {
@ -340,18 +305,143 @@ impl VMManager {
.sub_ranges .sub_ranges
.iter() .iter()
.flat_map(|subrange| { .flat_map(|subrange| {
if subrange.size() > 0 {
subrange.subtract(&munmap_range)
} else {
// Keep the two sentry subranges intact // Keep the two sentry subranges intact
vec![*subrange] if subrange.size() == 0 {
return vec![*subrange];
} }
let unmapped_subrange = match subrange.intersect(&munmap_range) {
None => return vec![*subrange],
Some(unmapped_subrange) => unmapped_subrange,
};
subrange.subtract(&unmapped_subrange)
}) })
.collect(); .collect();
self.sub_ranges = new_sub_ranges; self.sub_ranges = new_sub_ranges;
Ok(()) Ok(())
} }
pub fn mremap(&mut self, options: &VMRemapOptions) -> Result<usize> {
let old_addr = options.old_addr();
let old_size = options.old_size();
let old_range = VMRange::new_with_size(old_addr, old_size)?;
let new_size = options.new_size();
let flags = options.flags();
#[derive(Clone, Copy, PartialEq)]
enum SizeType {
Same,
Shrinking,
Growing,
};
let size_type = if new_size == old_size {
SizeType::Same
} else if new_size < old_size {
SizeType::Shrinking
} else {
SizeType::Growing
};
// The old range must not span over multiple sub-ranges
self.find_containing_subrange_idx(&old_range)
.ok_or_else(|| errno!(EFAULT, "invalid range"))?;
// Implement mremap as one optional mmap followed by one optional munmap.
//
// The exact arguments for the mmap and munmap are determined by the values of MRemapFlags
// and SizeType. There is a total of 9 combinations between MRemapFlags and SizeType.
// As some combinations result in the same mmap and munmap operations, the following code
// only needs to match four patterns of (MRemapFlags, SizeType) and treat each case
// accordingly.
// Determine whether need to do mmap. And when possible, determine the returned address
// TODO: should fill zeros even when extending a file-backed mapping?
let (need_mmap, mut ret_addr) = match (flags, size_type) {
(MRemapFlags::None, SizeType::Growing) => {
let mmap_opts = VMMapOptionsBuilder::default()
.size(new_size - old_size)
.addr(VMMapAddr::Need(old_range.end()))
.initializer(VMInitializer::FillZeros())
.build()?;
let ret_addr = Some(old_addr);
(Some(mmap_opts), ret_addr)
}
(MRemapFlags::MayMove, SizeType::Growing) => {
let prefered_new_range =
VMRange::new_with_size(old_addr + old_size, new_size - old_size)?;
if self.is_free_range(&prefered_new_range) {
let mmap_ops = VMMapOptionsBuilder::default()
.size(prefered_new_range.size())
.addr(VMMapAddr::Need(prefered_new_range.start()))
.initializer(VMInitializer::FillZeros())
.build()?;
(Some(mmap_ops), Some(old_addr))
} else {
let mmap_ops = VMMapOptionsBuilder::default()
.size(new_size)
.addr(VMMapAddr::Any)
.initializer(VMInitializer::CopyFrom { range: old_range })
.build()?;
// Cannot determine the returned address for now, which can only be obtained after calling mmap
let ret_addr = None;
(Some(mmap_ops), ret_addr)
}
}
(MRemapFlags::FixedAddr(new_addr), _) => {
let mmap_opts = VMMapOptionsBuilder::default()
.size(new_size)
.addr(VMMapAddr::Force(new_addr))
.initializer(VMInitializer::CopyFrom { range: old_range })
.build()?;
let ret_addr = Some(new_addr);
(Some(mmap_opts), ret_addr)
}
_ => (None, Some(old_addr)),
};
let need_munmap = match (flags, size_type) {
(MRemapFlags::None, SizeType::Shrinking)
| (MRemapFlags::MayMove, SizeType::Shrinking) => {
let unmap_addr = old_addr + new_size;
let unmap_size = old_size - new_size;
Some((unmap_addr, unmap_size))
}
(MRemapFlags::MayMove, SizeType::Growing) => {
if ret_addr.is_none() {
// We must need to do mmap. Thus unmap the old range
Some((old_addr, old_size))
} else {
// We must choose to reuse the old range. Thus, no need to unmap
None
}
}
(MRemapFlags::FixedAddr(new_addr), _) => {
let new_range = VMRange::new_with_size(new_addr, new_size)?;
if new_range.overlap_with(&old_range) {
return_errno!(EINVAL, "new range cannot overlap with the old one");
}
Some((old_addr, old_size))
}
_ => None,
};
// Perform mmap and munmap if needed
if let Some(mmap_options) = need_mmap {
let mmap_addr = self.mmap(&mmap_options)?;
if ret_addr.is_none() {
ret_addr = Some(mmap_addr);
}
}
if let Some((addr, size)) = need_munmap {
self.munmap(addr, size).expect("never fail");
}
debug_assert!(ret_addr.is_some());
Ok(ret_addr.unwrap())
}
pub fn find_mmap_region(&self, addr: usize) -> Result<&VMRange> { pub fn find_mmap_region(&self, addr: usize) -> Result<&VMRange> {
self.sub_ranges self.sub_ranges
.iter() .iter()
@ -359,34 +449,24 @@ impl VMManager {
.ok_or_else(|| errno!(ESRCH, "no mmap regions that contains the address")) .ok_or_else(|| errno!(ESRCH, "no mmap regions that contains the address"))
} }
fn find_mmap_region_idx(&self, addr: usize) -> Result<usize> { // Find a subrange that contains the given range and returns the index of the subrange
fn find_containing_subrange_idx(&self, target_range: &VMRange) -> Option<usize> {
self.sub_ranges self.sub_ranges
.iter() .iter()
.position(|subrange| subrange.contains(addr)) .position(|subrange| subrange.is_superset_of(target_range))
.ok_or_else(|| errno!(ESRCH, "no mmap regions that contains the address"))
} }
fn move_mmap_region( // Returns whether the requested range is free
&mut self, fn is_free_range(&self, request_range: &VMRange) -> bool {
src_subrange: &VMRange, self.range.is_superset_of(request_range)
dst_idx_and_subrange: (usize, &VMRange), && self
) -> Result<usize> { .sub_ranges
let dst_idx = dst_idx_and_subrange.0; .iter()
let dst_subrange = dst_idx_and_subrange.1; .all(|range| range.overlap_with(request_range) == false)
unsafe {
let src_buf = src_subrange.as_slice_mut();
let dst_buf = dst_subrange.as_slice_mut();
for (d, s) in dst_buf.iter_mut().zip(src_buf.iter()) {
*d = *s;
}
}
self.sub_ranges.insert(dst_idx, *dst_subrange);
self.munmap(src_subrange.start(), src_subrange.size())?;
Ok(dst_subrange.start())
} }
// Find the free subrange that satisfies the constraints of size and address // Find the free subrange that satisfies the constraints of size and address
fn find_free_subrange(&mut self, size: usize, addr: VMMapAddr) -> Result<(usize, VMRange)> { fn find_free_subrange(&self, size: usize, addr: VMMapAddr) -> Result<(usize, VMRange)> {
// TODO: reduce the complexity from O(N) to O(log(N)), where N is // TODO: reduce the complexity from O(N) to O(log(N)), where N is
// the number of existing subranges. // the number of existing subranges.
@ -426,7 +506,7 @@ impl VMManager {
} }
} }
// Must have free_range.start == addr // Must have free_range.start == addr
VMMapAddr::Fixed(addr) => { VMMapAddr::Need(addr) | VMMapAddr::Force(addr) => {
if free_range.start() > addr { if free_range.start() > addr {
return_errno!(ENOMEM, "not enough memory for fixed mmap"); return_errno!(ENOMEM, "not enough memory for fixed mmap");
} }
@ -468,11 +548,59 @@ impl VMManager {
debug_assert!(free_subrange.size() >= size); debug_assert!(free_subrange.size() >= size);
let mut new_subrange = *free_subrange; let mut new_subrange = *free_subrange;
if let VMMapAddr::Fixed(addr) = addr {
if let VMMapAddr::Need(addr) = addr {
debug_assert!(addr == new_subrange.start());
}
if let VMMapAddr::Force(addr) = addr {
debug_assert!(addr == new_subrange.start()); debug_assert!(addr == new_subrange.start());
} }
new_subrange.resize(size); new_subrange.resize(size);
new_subrange new_subrange
} }
// Insert the new sub-range, and when possible, merge it with its neighbors.
fn insert_new_subrange(&mut self, insert_idx: usize, new_subrange: VMRange) {
// New sub-range can only be inserted between the two sentry sub-ranges
debug_assert!(0 < insert_idx && insert_idx < self.sub_ranges.len());
let left_idx = insert_idx - 1;
let right_idx = insert_idx;
// Double check the order
debug_assert!(self.sub_ranges[left_idx].end() <= new_subrange.start());
debug_assert!(new_subrange.end() <= self.sub_ranges[right_idx].start());
let left_mergable = if left_idx > 0 {
// Mergable if there is no gap between the left neighbor and the new sub-range
self.sub_ranges[left_idx].end() == new_subrange.start()
} else {
// The left sentry sub-range is NOT mergable with any sub-range
false
};
let right_mergable = if right_idx < self.sub_ranges.len() - 1 {
// Mergable if there is no gap between the right neighbor and the new sub-range
self.sub_ranges[right_idx].start() == new_subrange.end()
} else {
// The right sentry sub-range is NOT mergable with any sub-range
false
};
match (left_mergable, right_mergable) {
(false, false) => {
self.sub_ranges.insert(insert_idx, new_subrange);
}
(true, false) => {
self.sub_ranges[left_idx].end = new_subrange.end;
}
(false, true) => {
self.sub_ranges[right_idx].start = new_subrange.start;
}
(true, true) => {
self.sub_ranges[left_idx].end = self.sub_ranges[right_idx].end;
self.sub_ranges.remove(right_idx);
}
}
}
} }

@ -17,6 +17,10 @@ impl VMRange {
}) })
} }
pub fn new_with_size(start: usize, size: usize) -> Result<VMRange> {
Self::new(start, start + size)
}
pub fn new_empty(start: usize) -> Result<VMRange> { pub fn new_empty(start: usize) -> Result<VMRange> {
if start % PAGE_SIZE != 0 { if start % PAGE_SIZE != 0 {
return_errno!(EINVAL, "invalid start or end"); return_errno!(EINVAL, "invalid start or end");

@ -630,10 +630,10 @@ int test_mremap() {
if (buf == MAP_FAILED) { if (buf == MAP_FAILED) {
THROW_ERROR("mmap failed"); THROW_ERROR("mmap failed");
} }
if (check_bytes_in_buf(buf, len, 0) < 0) { if (check_bytes_in_buf(buf, len, 0) < 0) {
THROW_ERROR("the buffer is not initialized to zeros"); THROW_ERROR("the buffer is not initialized to zeros");
} }
void *expand_buf = mremap(buf, len, 2 * len, MREMAP_MAYMOVE); void *expand_buf = mremap(buf, len, 2 * len, MREMAP_MAYMOVE);
if (expand_buf == MAP_FAILED) { if (expand_buf == MAP_FAILED) {
THROW_ERROR("mremap with big size failed"); THROW_ERROR("mremap with big size failed");
@ -642,6 +642,7 @@ int test_mremap() {
THROW_ERROR("the old part of expand buffer is not zero"); THROW_ERROR("the old part of expand buffer is not zero");
} }
memset(expand_buf, 'a', len * 2); memset(expand_buf, 'a', len * 2);
void *shrink_buf = mremap(expand_buf, 2 * len, len, 0); void *shrink_buf = mremap(expand_buf, 2 * len, len, 0);
if (shrink_buf == MAP_FAILED) { if (shrink_buf == MAP_FAILED) {
THROW_ERROR("mmap with small size failed"); THROW_ERROR("mmap with small size failed");
@ -649,6 +650,7 @@ int test_mremap() {
if (check_bytes_in_buf(shrink_buf, len, 'a') < 0) { if (check_bytes_in_buf(shrink_buf, len, 'a') < 0) {
THROW_ERROR("the shrink buffer is not correct"); THROW_ERROR("the shrink buffer is not correct");
} }
int ret = munmap(shrink_buf, len); int ret = munmap(shrink_buf, len);
if (ret < 0) { if (ret < 0) {
THROW_ERROR("munmap failed"); THROW_ERROR("munmap failed");
@ -694,6 +696,7 @@ int test_mremap_subrange() {
return 0; return 0;
} }
// FIXME: may cause segfault on Linux
int test_mremap_with_fixed_addr() { int test_mremap_with_fixed_addr() {
int prot = PROT_READ | PROT_WRITE; int prot = PROT_READ | PROT_WRITE;
int flags = MAP_PRIVATE | MAP_ANONYMOUS; int flags = MAP_PRIVATE | MAP_ANONYMOUS;
@ -703,10 +706,10 @@ int test_mremap_with_fixed_addr() {
if (buf == MAP_FAILED) { if (buf == MAP_FAILED) {
THROW_ERROR("mmap failed"); THROW_ERROR("mmap failed");
} }
if (check_bytes_in_buf(buf, len, 0) < 0) { if (check_bytes_in_buf(buf, len, 0) < 0) {
THROW_ERROR("the buffer is not initialized to zeros"); THROW_ERROR("the buffer is not initialized to zeros");
} }
void *new_addr = buf + len * 2; void *new_addr = buf + len * 2;
void *new_buf = mremap(buf, len, len, MREMAP_FIXED, new_addr); void *new_buf = mremap(buf, len, len, MREMAP_FIXED, new_addr);
if (new_buf != MAP_FAILED || errno != EINVAL) { if (new_buf != MAP_FAILED || errno != EINVAL) {