-
Notifications
You must be signed in to change notification settings - Fork 356
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
252 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
//! This is an incomplete implementation of mmap/mremap/munmap which is restricted in order to be | ||
//! implementable on top of the existing memory system. The point of these function as-written is | ||
//! to allow memory allocators written entirely in Rust to be executed by Miri. This implementation | ||
//! does not support other uses of mmap such as file mappings. | ||
//! | ||
//! mmap/mremap/munmap behave a lot like alloc/realloc/dealloc, and for simple use they are exactly | ||
//! equivalent. But the memory-mapping API provides more control. For example: | ||
//! | ||
//! * It is possible to munmap a single page in the middle of a mapped region. We do not have a way | ||
//! to express non-contiguous allocations. | ||
//! | ||
//! * With MAP_FIXED it is possible to call mmap multiple times, but create a single contiguous | ||
//! range of mapped virtual addresses. A memory allocator can then choose to carve this up into | ||
//! allocations in arbitrary ways. | ||
use crate::*; | ||
use rustc_target::abi::{Align, Size}; | ||
|
||
impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} | ||
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { | ||
fn mmap( | ||
&mut self, | ||
addr: &OpTy<'tcx, Provenance>, | ||
length: &OpTy<'tcx, Provenance>, | ||
prot: &OpTy<'tcx, Provenance>, | ||
flags: &OpTy<'tcx, Provenance>, | ||
fd: &OpTy<'tcx, Provenance>, | ||
offset: &OpTy<'tcx, Provenance>, | ||
) -> InterpResult<'tcx, Pointer<Option<Provenance>>> { | ||
let this = self.eval_context_mut(); | ||
|
||
// We do not support MAP_FIXED, so the addr argument is always ignored | ||
let _addr = this.read_pointer(addr)?.into_parts().1; | ||
let length = this.read_scalar(length)?.to_machine_usize(this)?; | ||
let prot = this.read_scalar(prot)?.to_i32()?; | ||
let flags = this.read_scalar(flags)?.to_i32()?; | ||
let fd = this.read_scalar(fd)?.to_i32()?; | ||
let offset = this.read_scalar(offset)?.to_machine_usize(this)?; | ||
|
||
let map_private = this.eval_libc_i32("MAP_PRIVATE"); | ||
let map_anonymous = this.eval_libc_i32("MAP_ANONYMOUS"); | ||
|
||
let prot_read = this.eval_libc_i32("PROT_READ"); | ||
let prot_write = this.eval_libc_i32("PROT_WRITE"); | ||
|
||
if flags != map_private | map_anonymous { | ||
throw_unsup_format!( | ||
"Miri only supports calls to mmap which set flags to MAP_PRIVATE|MAP_ANONYMOUS" | ||
); | ||
} | ||
|
||
if prot != prot_read | prot_write { | ||
throw_unsup_format!( | ||
"Miri only supports calls to mmap which set protection to PROT_READ|PROT_WRITE" | ||
); | ||
} | ||
|
||
if offset != 0 { | ||
throw_unsup_format!("Miri does not support non-zero offsets to mmap"); | ||
} | ||
|
||
if fd != -1 { | ||
throw_unsup_format!("Miri does not support file-backed memory mappings"); | ||
} | ||
|
||
if length == 0 { | ||
this.set_last_error(Scalar::from_i32(this.eval_libc_i32("EINVAL")))?; | ||
return Ok(Pointer::null()); | ||
} | ||
|
||
let align = Align::from_bytes(this.machine.page_size).unwrap(); | ||
let map_length = this.machine.round_up_to_multiple_of_page_size(length).unwrap_or(u64::MAX); | ||
|
||
let ptr = | ||
this.allocate_ptr(Size::from_bytes(map_length), align, MiriMemoryKind::Mmap.into())?; | ||
|
||
// We just allocated this, the access is definitely in-bounds and fits into our address space. | ||
// mmap guarantees new mappings are zero-init. | ||
this.write_bytes_ptr( | ||
ptr.into(), | ||
std::iter::repeat(0u8).take(usize::try_from(map_length).unwrap()), | ||
) | ||
.unwrap(); | ||
|
||
Ok(ptr.into()) | ||
} | ||
|
||
fn mremap( | ||
&mut self, | ||
old_address: &OpTy<'tcx, Provenance>, | ||
old_size: &OpTy<'tcx, Provenance>, | ||
new_size: &OpTy<'tcx, Provenance>, | ||
flags: &OpTy<'tcx, Provenance>, | ||
) -> InterpResult<'tcx, Pointer<Option<Provenance>>> { | ||
let this = self.eval_context_mut(); | ||
|
||
// This is actually a pointer but we really do not care about its provenance | ||
let old_address = this.read_pointer(old_address)?; | ||
|
||
let old_size = this.read_scalar(old_size)?.to_machine_usize(this)?; | ||
let new_size = this.read_scalar(new_size)?.to_machine_usize(this)?; | ||
let flags = this.read_scalar(flags)?.to_i32()?; | ||
|
||
if flags & this.eval_libc_i32("MREMAP_FIXED") > 0 { | ||
throw_unsup_format!("Miri does not support mremap wth MREMAP_FIXED"); | ||
} | ||
|
||
if flags & this.eval_libc_i32("MREMAP_DONTUNMAP") > 0 { | ||
throw_unsup_format!("Miri does not support mremap wth MREMAP_DONTUNMAP"); | ||
} | ||
|
||
if flags & this.eval_libc_i32("MREMAP_MAYMOVE") == 0 { | ||
// We only support MREMAP_MAYMOVE, so not passing the flag is just a failure | ||
this.set_last_error(Scalar::from_i32(this.eval_libc_i32("EINVAL")))?; | ||
return Ok(Pointer::null()); | ||
} | ||
|
||
let align = this.machine.page_align(); | ||
this.reallocate_ptr( | ||
old_address, | ||
Some((Size::from_bytes(old_size), align)), | ||
Size::from_bytes(new_size), | ||
align, | ||
MiriMemoryKind::Mmap.into(), | ||
) | ||
.map(|p| p.into()) | ||
} | ||
|
||
fn munmap( | ||
&mut self, | ||
addr: &OpTy<'tcx, Provenance>, | ||
length: &OpTy<'tcx, Provenance>, | ||
) -> InterpResult<'tcx, i32> { | ||
let this = self.eval_context_mut(); | ||
|
||
let length = this.read_scalar(length)?.to_machine_usize(this)?; | ||
|
||
// The address addr must be a multiple of the page size (but length need not be). | ||
#[allow(clippy::integer_arithmetic)] // PAGE_SIZE is nonzero | ||
if this.read_scalar(addr)?.to_machine_usize(this)? % this.machine.page_size != 0 { | ||
this.set_last_error(Scalar::from_i32(this.eval_libc_i32("EINVAL")))?; | ||
return Ok(-1); | ||
} | ||
|
||
let addr = this.read_pointer(addr)?; | ||
|
||
// All pages containing a part of the indicated range are unmapped. | ||
let length = this.machine.round_up_to_multiple_of_page_size(length).unwrap_or(u64::MAX); | ||
|
||
this.deallocate_ptr( | ||
addr, | ||
Some((Size::from_bytes(length), this.machine.page_align())), | ||
MiriMemoryKind::Mmap.into(), | ||
)?; | ||
Ok(0) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ pub mod dlsym; | |
pub mod foreign_items; | ||
|
||
mod fs; | ||
mod mem; | ||
mod sync; | ||
mod thread; | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
//@compile-flags: -Zmiri-disable-isolation | ||
//@ignore-target-windows: No libc on Windows | ||
|
||
#![feature(rustc_private)] | ||
|
||
fn main() { | ||
unsafe { | ||
let ptr = libc::mmap( | ||
std::ptr::null_mut(), | ||
4096, | ||
libc::PROT_READ | libc::PROT_WRITE, | ||
libc::MAP_PRIVATE | libc::MAP_ANONYMOUS, | ||
-1, | ||
0, | ||
); | ||
libc::free(ptr); //~ ERROR: which is mmap memory, using C heap deallocation operation | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
error: Undefined Behavior: deallocating ALLOC, which is mmap memory, using C heap deallocation operation | ||
--> $DIR/mmap_invalid_dealloc.rs:LL:CC | ||
| | ||
LL | libc::free(ptr); | ||
| ^^^^^^^^^^^^^^^ deallocating ALLOC, which is mmap memory, using C heap deallocation operation | ||
| | ||
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior | ||
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information | ||
= note: BACKTRACE: | ||
= note: inside `main` at $DIR/mmap_invalid_dealloc.rs:LL:CC | ||
|
||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace | ||
|
||
error: aborting due to previous error | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
//@ignore-target-windows: No libc on Windows | ||
//@compile-flags: -Zmiri-disable-isolation | ||
#![feature(strict_provenance)] | ||
|
||
use std::slice; | ||
|
||
fn test_mmap() { | ||
// Standard mmap to allocate memory | ||
let ptr = unsafe { | ||
libc::mmap( | ||
std::ptr::null_mut(), | ||
4096, | ||
libc::PROT_READ | libc::PROT_WRITE, | ||
libc::MAP_PRIVATE | libc::MAP_ANONYMOUS, | ||
-1, | ||
0, | ||
) | ||
}; | ||
assert!(!ptr.is_null()); | ||
let slice = unsafe { slice::from_raw_parts(ptr as *const u8, 4096) }; | ||
assert_eq!(&[0u8; 4096], slice); | ||
|
||
let res = unsafe { libc::munmap(ptr, 4096) }; | ||
assert_eq!(res, 0); | ||
} | ||
|
||
fn main() { | ||
test_mmap(); | ||
} |