Skip to content

Commit cb19974

Browse files
committed
mmap/munmap/mremamp shims
1 parent 73452b3 commit cb19974

17 files changed

+559
-14
lines changed

demo.rs

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
fn main() {
2+
unsafe {
3+
let a: u8 = 0;
4+
let b: u8 = 1;
5+
6+
let addr_a = &a as *const u8 as usize;
7+
let addr_b = &b as *const u8 as usize;
8+
9+
let ptr = addr_a as *const u8;
10+
dbg!(*ptr); // ptr must have selected the provenance for a
11+
let ptr = ptr.wrapping_offset(addr_b.wrapping_sub(addr_a) as isize);
12+
dbg!(*ptr); // Oops, we can also use it to read b
13+
}
14+
}

src/concurrency/data_race.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -697,7 +697,8 @@ impl VClockAlloc {
697697
MiriMemoryKind::Rust
698698
| MiriMemoryKind::Miri
699699
| MiriMemoryKind::C
700-
| MiriMemoryKind::WinHeap,
700+
| MiriMemoryKind::WinHeap
701+
| MiriMemoryKind::Mmap,
701702
)
702703
| MemoryKind::Stack => {
703704
let (alloc_index, clocks) = global.current_thread_state(thread_mgr);

src/intptrcast.rs

+24-1
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,12 @@ impl<'mir, 'tcx> GlobalStateInner {
163163
}
164164

165165
fn alloc_base_addr(ecx: &MiriInterpCx<'mir, 'tcx>, alloc_id: AllocId) -> u64 {
166+
for (offset, map) in ecx.machine.mappings.maps() {
167+
if map.alloc_id == alloc_id {
168+
return offset.bytes();
169+
}
170+
}
171+
166172
let mut global_state = ecx.machine.intptrcast.borrow_mut();
167173
let global_state = &mut *global_state;
168174

@@ -180,6 +186,10 @@ impl<'mir, 'tcx> GlobalStateInner {
180186
// This means that `(global_state.next_base_addr + slack) % 16` is uniformly distributed.
181187
rng.gen_range(0..16)
182188
};
189+
190+
// If this would collide with a Mapping, bump the next_base_addr up until it
191+
// doesn't.
192+
183193
// From next_base_addr + slack, round up to adjust for alignment.
184194
let base_addr = global_state.next_base_addr.checked_add(slack).unwrap();
185195
let base_addr = Self::align_addr(base_addr, align.bytes());
@@ -210,7 +220,20 @@ impl<'mir, 'tcx> GlobalStateInner {
210220
/// Convert a relative (tcx) pointer to an absolute address.
211221
pub fn rel_ptr_to_addr(ecx: &MiriInterpCx<'mir, 'tcx>, ptr: Pointer<AllocId>) -> u64 {
212222
let (alloc_id, offset) = ptr.into_parts(); // offset is relative (AllocId provenance)
213-
let base_addr = GlobalStateInner::alloc_base_addr(ecx, alloc_id);
223+
224+
// If this AllocId is for a Mapping, return the base address of that, otherwise look up
225+
// this allocation in the normal way.
226+
let base_addr = ecx
227+
.machine
228+
.mappings
229+
.maps()
230+
.find_map(
231+
|(map_start, map)| {
232+
if map.alloc_id == alloc_id { Some(map_start.bytes()) } else { None }
233+
},
234+
)
235+
.or_else(|| ecx.machine.mappings.pending.as_ref().map(|r| r.start.bytes()))
236+
.unwrap_or_else(|| GlobalStateInner::alloc_base_addr(ecx, alloc_id));
214237

215238
// Add offset with the right kind of pointer-overflowing arithmetic.
216239
let dl = ecx.data_layout();

src/machine.rs

+61-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
use std::borrow::Cow;
55
use std::cell::RefCell;
66
use std::fmt;
7+
use std::ops::Range;
78

89
use rand::rngs::StdRng;
910
use rand::SeedableRng;
@@ -104,6 +105,8 @@ pub enum MiriMemoryKind {
104105
/// Memory for thread-local statics.
105106
/// This memory may leak.
106107
Tls,
108+
/// Memory mapped directly by the program
109+
Mmap,
107110
}
108111

109112
impl From<MiriMemoryKind> for MemoryKind<MiriMemoryKind> {
@@ -119,7 +122,7 @@ impl MayLeak for MiriMemoryKind {
119122
use self::MiriMemoryKind::*;
120123
match self {
121124
Rust | Miri | C | WinHeap | Runtime => false,
122-
Machine | Global | ExternStatic | Tls => true,
125+
Machine | Global | ExternStatic | Tls | Mmap => true,
123126
}
124127
}
125128
}
@@ -137,6 +140,7 @@ impl fmt::Display for MiriMemoryKind {
137140
Global => write!(f, "global (static or const)"),
138141
ExternStatic => write!(f, "extern static"),
139142
Tls => write!(f, "thread-local static"),
143+
Mmap => write!(f, "mmap"),
140144
}
141145
}
142146
}
@@ -342,6 +346,39 @@ impl<'mir, 'tcx: 'mir> PrimitiveLayouts<'tcx> {
342346
}
343347
}
344348

349+
#[derive(Clone, Debug, PartialEq, Eq)]
350+
pub struct Mapping {
351+
pub alloc_id: AllocId,
352+
pub can_read: bool,
353+
pub can_write: bool,
354+
}
355+
356+
#[derive(Debug)]
357+
pub struct Mappings {
358+
all_memory: RangeMap<Option<Mapping>>,
359+
pub pending: Option<Range<Size>>,
360+
}
361+
362+
impl Mappings {
363+
fn new() -> Self {
364+
Mappings { all_memory: RangeMap::new(Size::from_bytes(u64::MAX), None), pending: None }
365+
}
366+
367+
pub fn iter_mut(
368+
&mut self,
369+
start: Size,
370+
end: Size,
371+
) -> impl Iterator<Item = (Size, &mut Option<Mapping>)> {
372+
self.all_memory.iter_mut(start, end)
373+
}
374+
375+
pub fn maps(&self) -> impl Iterator<Item = (Size, &Mapping)> {
376+
self.all_memory.iter_all().filter_map(|(offset, chunk)| {
377+
chunk.as_ref().map(|c| (Size::from_bytes(offset.start), c))
378+
})
379+
}
380+
}
381+
345382
/// The machine itself.
346383
///
347384
/// If you add anything here that stores machine values, remember to update
@@ -376,6 +413,9 @@ pub struct MiriMachine<'mir, 'tcx> {
376413
/// TLS state.
377414
pub(crate) tls: TlsData<'tcx>,
378415

416+
/// Mappings established through mmap
417+
pub(crate) mappings: Mappings,
418+
379419
/// What should Miri do when an op requires communicating with the host,
380420
/// such as accessing host env vars, random number generation, and
381421
/// file system access.
@@ -518,6 +558,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
518558
argv: None,
519559
cmd_line: None,
520560
tls: TlsData::default(),
561+
mappings: Mappings::new(),
521562
isolated_op: config.isolated_op,
522563
validate: config.validate,
523564
enforce_abi: config.check_abi,
@@ -674,6 +715,14 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
674715
let def_id = frame.instance.def_id();
675716
def_id.is_local() || self.local_crates.contains(&def_id.krate)
676717
}
718+
719+
pub(crate) fn get_mapping(&self, alloc_id: AllocId) -> Option<&Mapping> {
720+
self.mappings
721+
.all_memory
722+
.iter_all()
723+
.filter_map(|(_offset, map)| map.as_ref())
724+
.find(|m| m.alloc_id == alloc_id)
725+
}
677726
}
678727

679728
impl VisitTags for MiriMachine<'_, '_> {
@@ -722,6 +771,7 @@ impl VisitTags for MiriMachine<'_, '_> {
722771
page_size: _,
723772
stack_addr: _,
724773
stack_size: _,
774+
mappings: _,
725775
} = self;
726776

727777
threads.visit_tags(visit);
@@ -1046,6 +1096,11 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
10461096
(alloc_id, prov_extra): (AllocId, Self::ProvenanceExtra),
10471097
range: AllocRange,
10481098
) -> InterpResult<'tcx> {
1099+
if let Some(map) = machine.get_mapping(alloc_id) {
1100+
if !map.can_read {
1101+
throw_ub_format!("{alloc_id:?} is a mapping that does not allow reads");
1102+
}
1103+
}
10491104
if let Some(data_race) = &alloc_extra.data_race {
10501105
data_race.read(alloc_id, range, machine)?;
10511106
}
@@ -1066,6 +1121,11 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
10661121
(alloc_id, prov_extra): (AllocId, Self::ProvenanceExtra),
10671122
range: AllocRange,
10681123
) -> InterpResult<'tcx> {
1124+
if let Some(map) = machine.get_mapping(alloc_id) {
1125+
if !map.can_write {
1126+
throw_ub_format!("{alloc_id:?} is a mapping that does not allow writes");
1127+
}
1128+
}
10691129
if let Some(data_race) = &mut alloc_extra.data_race {
10701130
data_race.write(alloc_id, range, machine)?;
10711131
}

src/shims/unix/foreign_items.rs

+25-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use rustc_target::spec::abi::Abi;
1010
use crate::*;
1111
use shims::foreign_items::EmulateByNameResult;
1212
use shims::unix::fs::EvalContextExt as _;
13+
use shims::unix::mem::EvalContextExt as _;
1314
use shims::unix::sync::EvalContextExt as _;
1415
use shims::unix::thread::EvalContextExt as _;
1516

@@ -213,6 +214,30 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
213214
}
214215
}
215216

217+
"mmap" => {
218+
let [addr, length, prot, flags, fd, offset] = this.check_shim(abi, Abi::C {unwind: false}, link_name, args)?;
219+
if let Some(ptr) = this.mmap(addr, length, prot, flags, fd, offset)? {
220+
this.write_pointer(ptr, dest)?;
221+
} else {
222+
this.write_null(dest)?;
223+
}
224+
}
225+
"mremap" => {
226+
let [old_address, old_size, new_size, flags] = this.check_shim(abi, Abi::C {unwind: false}, link_name, args)?;
227+
let result = this.mremap(old_address, old_size, new_size, flags)?;
228+
this.write_pointer(result, dest)?;
229+
}
230+
"munmap" => {
231+
let [addr, length] = this.check_shim(abi, Abi::C {unwind: false}, link_name, args)?;
232+
let result = this.munmap(addr, length)?;
233+
this.write_scalar(Scalar::from_i32(result), dest)?;
234+
}
235+
"mprotect" => {
236+
let [addr, length, prot] = this.check_shim(abi, Abi::C {unwind: false}, link_name, args)?;
237+
let result = this.mprotect(addr, length, prot)?;
238+
this.write_scalar(Scalar::from_i32(result), dest)?;
239+
}
240+
216241
// Dynamic symbol loading
217242
"dlsym" => {
218243
let [handle, symbol] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
@@ -544,7 +569,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
544569
this.write_null(dest)?;
545570
}
546571
| "sigaction"
547-
| "mprotect"
548572
if this.frame_in_std() => {
549573
let [_, _, _] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
550574
this.write_null(dest)?;

src/shims/unix/macos/foreign_items.rs

-10
Original file line numberDiff line numberDiff line change
@@ -197,16 +197,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
197197
this.write_scalar(res, dest)?;
198198
}
199199

200-
// Incomplete shims that we "stub out" just to get pre-main initialization code to work.
201-
// These shims are enabled only when the caller is in the standard library.
202-
"mmap" if this.frame_in_std() => {
203-
// 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.
204-
let [addr, _, _, _, _, _] =
205-
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
206-
let addr = this.read_scalar(addr)?;
207-
this.write_scalar(addr, dest)?;
208-
}
209-
210200
_ => return Ok(EmulateByNameResult::NotSupported),
211201
};
212202

0 commit comments

Comments
 (0)