Skip to content

Commit

Permalink
mmap/munmap/mremamp shims
Browse files Browse the repository at this point in the history
  • Loading branch information
saethlin committed Jan 15, 2023
1 parent 73452b3 commit cb19974
Show file tree
Hide file tree
Showing 17 changed files with 559 additions and 14 deletions.
14 changes: 14 additions & 0 deletions demo.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
fn main() {
unsafe {
let a: u8 = 0;
let b: u8 = 1;

let addr_a = &a as *const u8 as usize;
let addr_b = &b as *const u8 as usize;

let ptr = addr_a as *const u8;
dbg!(*ptr); // ptr must have selected the provenance for a
let ptr = ptr.wrapping_offset(addr_b.wrapping_sub(addr_a) as isize);
dbg!(*ptr); // Oops, we can also use it to read b
}
}
3 changes: 2 additions & 1 deletion src/concurrency/data_race.rs
Original file line number Diff line number Diff line change
Expand Up @@ -697,7 +697,8 @@ impl VClockAlloc {
MiriMemoryKind::Rust
| MiriMemoryKind::Miri
| MiriMemoryKind::C
| MiriMemoryKind::WinHeap,
| MiriMemoryKind::WinHeap
| MiriMemoryKind::Mmap,
)
| MemoryKind::Stack => {
let (alloc_index, clocks) = global.current_thread_state(thread_mgr);
Expand Down
25 changes: 24 additions & 1 deletion src/intptrcast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,12 @@ impl<'mir, 'tcx> GlobalStateInner {
}

fn alloc_base_addr(ecx: &MiriInterpCx<'mir, 'tcx>, alloc_id: AllocId) -> u64 {
for (offset, map) in ecx.machine.mappings.maps() {
if map.alloc_id == alloc_id {
return offset.bytes();
}
}

let mut global_state = ecx.machine.intptrcast.borrow_mut();
let global_state = &mut *global_state;

Expand All @@ -180,6 +186,10 @@ impl<'mir, 'tcx> GlobalStateInner {
// This means that `(global_state.next_base_addr + slack) % 16` is uniformly distributed.
rng.gen_range(0..16)
};

// If this would collide with a Mapping, bump the next_base_addr up until it
// doesn't.

// From next_base_addr + slack, round up to adjust for alignment.
let base_addr = global_state.next_base_addr.checked_add(slack).unwrap();
let base_addr = Self::align_addr(base_addr, align.bytes());
Expand Down Expand Up @@ -210,7 +220,20 @@ impl<'mir, 'tcx> GlobalStateInner {
/// Convert a relative (tcx) pointer to an absolute address.
pub fn rel_ptr_to_addr(ecx: &MiriInterpCx<'mir, 'tcx>, ptr: Pointer<AllocId>) -> u64 {
let (alloc_id, offset) = ptr.into_parts(); // offset is relative (AllocId provenance)
let base_addr = GlobalStateInner::alloc_base_addr(ecx, alloc_id);

// If this AllocId is for a Mapping, return the base address of that, otherwise look up
// this allocation in the normal way.
let base_addr = ecx
.machine
.mappings
.maps()
.find_map(
|(map_start, map)| {
if map.alloc_id == alloc_id { Some(map_start.bytes()) } else { None }
},
)
.or_else(|| ecx.machine.mappings.pending.as_ref().map(|r| r.start.bytes()))
.unwrap_or_else(|| GlobalStateInner::alloc_base_addr(ecx, alloc_id));

// Add offset with the right kind of pointer-overflowing arithmetic.
let dl = ecx.data_layout();
Expand Down
62 changes: 61 additions & 1 deletion src/machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
use std::borrow::Cow;
use std::cell::RefCell;
use std::fmt;
use std::ops::Range;

use rand::rngs::StdRng;
use rand::SeedableRng;
Expand Down Expand Up @@ -104,6 +105,8 @@ pub enum MiriMemoryKind {
/// Memory for thread-local statics.
/// This memory may leak.
Tls,
/// Memory mapped directly by the program
Mmap,
}

impl From<MiriMemoryKind> for MemoryKind<MiriMemoryKind> {
Expand All @@ -119,7 +122,7 @@ impl MayLeak for MiriMemoryKind {
use self::MiriMemoryKind::*;
match self {
Rust | Miri | C | WinHeap | Runtime => false,
Machine | Global | ExternStatic | Tls => true,
Machine | Global | ExternStatic | Tls | Mmap => true,
}
}
}
Expand All @@ -137,6 +140,7 @@ impl fmt::Display for MiriMemoryKind {
Global => write!(f, "global (static or const)"),
ExternStatic => write!(f, "extern static"),
Tls => write!(f, "thread-local static"),
Mmap => write!(f, "mmap"),
}
}
}
Expand Down Expand Up @@ -342,6 +346,39 @@ impl<'mir, 'tcx: 'mir> PrimitiveLayouts<'tcx> {
}
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Mapping {
pub alloc_id: AllocId,
pub can_read: bool,
pub can_write: bool,
}

#[derive(Debug)]
pub struct Mappings {
all_memory: RangeMap<Option<Mapping>>,
pub pending: Option<Range<Size>>,
}

impl Mappings {
fn new() -> Self {
Mappings { all_memory: RangeMap::new(Size::from_bytes(u64::MAX), None), pending: None }
}

pub fn iter_mut(
&mut self,
start: Size,
end: Size,
) -> impl Iterator<Item = (Size, &mut Option<Mapping>)> {
self.all_memory.iter_mut(start, end)
}

pub fn maps(&self) -> impl Iterator<Item = (Size, &Mapping)> {
self.all_memory.iter_all().filter_map(|(offset, chunk)| {
chunk.as_ref().map(|c| (Size::from_bytes(offset.start), c))
})
}
}

/// The machine itself.
///
/// If you add anything here that stores machine values, remember to update
Expand Down Expand Up @@ -376,6 +413,9 @@ pub struct MiriMachine<'mir, 'tcx> {
/// TLS state.
pub(crate) tls: TlsData<'tcx>,

/// Mappings established through mmap
pub(crate) mappings: Mappings,

/// What should Miri do when an op requires communicating with the host,
/// such as accessing host env vars, random number generation, and
/// file system access.
Expand Down Expand Up @@ -518,6 +558,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
argv: None,
cmd_line: None,
tls: TlsData::default(),
mappings: Mappings::new(),
isolated_op: config.isolated_op,
validate: config.validate,
enforce_abi: config.check_abi,
Expand Down Expand Up @@ -674,6 +715,14 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
let def_id = frame.instance.def_id();
def_id.is_local() || self.local_crates.contains(&def_id.krate)
}

pub(crate) fn get_mapping(&self, alloc_id: AllocId) -> Option<&Mapping> {
self.mappings
.all_memory
.iter_all()
.filter_map(|(_offset, map)| map.as_ref())
.find(|m| m.alloc_id == alloc_id)
}
}

impl VisitTags for MiriMachine<'_, '_> {
Expand Down Expand Up @@ -722,6 +771,7 @@ impl VisitTags for MiriMachine<'_, '_> {
page_size: _,
stack_addr: _,
stack_size: _,
mappings: _,
} = self;

threads.visit_tags(visit);
Expand Down Expand Up @@ -1046,6 +1096,11 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
(alloc_id, prov_extra): (AllocId, Self::ProvenanceExtra),
range: AllocRange,
) -> InterpResult<'tcx> {
if let Some(map) = machine.get_mapping(alloc_id) {
if !map.can_read {
throw_ub_format!("{alloc_id:?} is a mapping that does not allow reads");
}
}
if let Some(data_race) = &alloc_extra.data_race {
data_race.read(alloc_id, range, machine)?;
}
Expand All @@ -1066,6 +1121,11 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
(alloc_id, prov_extra): (AllocId, Self::ProvenanceExtra),
range: AllocRange,
) -> InterpResult<'tcx> {
if let Some(map) = machine.get_mapping(alloc_id) {
if !map.can_write {
throw_ub_format!("{alloc_id:?} is a mapping that does not allow writes");
}
}
if let Some(data_race) = &mut alloc_extra.data_race {
data_race.write(alloc_id, range, machine)?;
}
Expand Down
26 changes: 25 additions & 1 deletion src/shims/unix/foreign_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use rustc_target::spec::abi::Abi;
use crate::*;
use shims::foreign_items::EmulateByNameResult;
use shims::unix::fs::EvalContextExt as _;
use shims::unix::mem::EvalContextExt as _;
use shims::unix::sync::EvalContextExt as _;
use shims::unix::thread::EvalContextExt as _;

Expand Down Expand Up @@ -213,6 +214,30 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
}
}

"mmap" => {
let [addr, length, prot, flags, fd, offset] = this.check_shim(abi, Abi::C {unwind: false}, link_name, args)?;
if let Some(ptr) = this.mmap(addr, length, prot, flags, fd, offset)? {
this.write_pointer(ptr, dest)?;
} else {
this.write_null(dest)?;
}
}
"mremap" => {
let [old_address, old_size, new_size, flags] = this.check_shim(abi, Abi::C {unwind: false}, link_name, args)?;
let result = this.mremap(old_address, old_size, new_size, flags)?;
this.write_pointer(result, dest)?;
}
"munmap" => {
let [addr, length] = this.check_shim(abi, Abi::C {unwind: false}, link_name, args)?;
let result = this.munmap(addr, length)?;
this.write_scalar(Scalar::from_i32(result), dest)?;
}
"mprotect" => {
let [addr, length, prot] = this.check_shim(abi, Abi::C {unwind: false}, link_name, args)?;
let result = this.mprotect(addr, length, prot)?;
this.write_scalar(Scalar::from_i32(result), dest)?;
}

// Dynamic symbol loading
"dlsym" => {
let [handle, symbol] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
Expand Down Expand Up @@ -544,7 +569,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
this.write_null(dest)?;
}
| "sigaction"
| "mprotect"
if this.frame_in_std() => {
let [_, _, _] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
this.write_null(dest)?;
Expand Down
10 changes: 0 additions & 10 deletions src/shims/unix/macos/foreign_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,16 +197,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
this.write_scalar(res, dest)?;
}

// Incomplete shims that we "stub out" just to get pre-main initialization code to work.
// These shims are enabled only when the caller is in the standard library.
"mmap" if this.frame_in_std() => {
// This is a horrible hack, but since the guard page mechanism calls mmap and expects a particular return value, we just give it that value.
let [addr, _, _, _, _, _] =
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
let addr = this.read_scalar(addr)?;
this.write_scalar(addr, dest)?;
}

_ => return Ok(EmulateByNameResult::NotSupported),
};

Expand Down
Loading

0 comments on commit cb19974

Please sign in to comment.