Skip to content

Commit 4916a77

Browse files
committed
Improve organization
1 parent 00fdee2 commit 4916a77

13 files changed

+90
-83
lines changed

src/shims/unix/foreign_items.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -219,11 +219,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
219219
let ptr = this.mmap(addr, length, prot, flags, fd, offset)?;
220220
this.write_scalar(ptr, dest)?;
221221
}
222-
"mremap" => {
223-
let [old_address, old_size, new_size, flags] = this.check_shim(abi, Abi::C {unwind: false}, link_name, args)?;
224-
let ptr = this.mremap(old_address, old_size, new_size, flags)?;
225-
this.write_scalar(ptr, dest)?;
226-
}
227222
"munmap" => {
228223
let [addr, length] = this.check_shim(abi, Abi::C {unwind: false}, link_name, args)?;
229224
let result = this.munmap(addr, length)?;

src/shims/unix/linux/foreign_items.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use crate::*;
77
use shims::foreign_items::EmulateByNameResult;
88
use shims::unix::fs::EvalContextExt as _;
99
use shims::unix::linux::fd::EvalContextExt as _;
10+
use shims::unix::linux::mem::EvalContextExt as _;
1011
use shims::unix::linux::sync::futex;
1112
use shims::unix::sync::EvalContextExt as _;
1213
use shims::unix::thread::EvalContextExt as _;
@@ -68,6 +69,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
6869
let result = this.eventfd(val, flag)?;
6970
this.write_scalar(result, dest)?;
7071
}
72+
"mremap" => {
73+
let [old_address, old_size, new_size, flags] =
74+
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
75+
let ptr = this.mremap(old_address, old_size, new_size, flags)?;
76+
this.write_scalar(ptr, dest)?;
77+
}
7178
"socketpair" => {
7279
let [domain, type_, protocol, sv] =
7380
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;

src/shims/unix/linux/mem.rs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
//! This follows the pattern in src/shims/unix/mem.rs: We only support uses of mremap that would
2+
//! correspond to valid uses of realloc.
3+
4+
use crate::*;
5+
use rustc_target::abi::Size;
6+
7+
impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
8+
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
9+
fn mremap(
10+
&mut self,
11+
old_address: &OpTy<'tcx, Provenance>,
12+
old_size: &OpTy<'tcx, Provenance>,
13+
new_size: &OpTy<'tcx, Provenance>,
14+
flags: &OpTy<'tcx, Provenance>,
15+
) -> InterpResult<'tcx, Scalar<Provenance>> {
16+
let this = self.eval_context_mut();
17+
18+
let old_address = this.read_target_usize(old_address)?;
19+
let old_size = this.read_target_usize(old_size)?;
20+
let new_size = this.read_target_usize(new_size)?;
21+
let flags = this.read_scalar(flags)?.to_i32()?;
22+
23+
// old_address must be a multiple of the page size
24+
#[allow(clippy::arithmetic_side_effects)] // PAGE_SIZE is nonzero
25+
if old_address % this.machine.page_size != 0 || new_size == 0 {
26+
this.set_last_error(Scalar::from_i32(this.eval_libc_i32("EINVAL")))?;
27+
return Ok(this.eval_libc("MAP_FAILED"));
28+
}
29+
30+
if flags & this.eval_libc_i32("MREMAP_FIXED") != 0 {
31+
throw_unsup_format!("Miri does not support mremap wth MREMAP_FIXED");
32+
}
33+
34+
if flags & this.eval_libc_i32("MREMAP_DONTUNMAP") != 0 {
35+
throw_unsup_format!("Miri does not support mremap wth MREMAP_DONTUNMAP");
36+
}
37+
38+
if flags & this.eval_libc_i32("MREMAP_MAYMOVE") == 0 {
39+
// We only support MREMAP_MAYMOVE, so not passing the flag is just a failure
40+
this.set_last_error(Scalar::from_i32(this.eval_libc_i32("EINVAL")))?;
41+
return Ok(Scalar::from_maybe_pointer(Pointer::null(), this));
42+
}
43+
44+
let old_address = Machine::ptr_from_addr_cast(this, old_address)?;
45+
let align = this.machine.page_align();
46+
let ptr = this.reallocate_ptr(
47+
old_address,
48+
Some((Size::from_bytes(old_size), align)),
49+
Size::from_bytes(new_size),
50+
align,
51+
MiriMemoryKind::Mmap.into(),
52+
)?;
53+
if let Some(increase) = new_size.checked_sub(old_size) {
54+
// We just allocated this, the access is definitely in-bounds and fits into our address space.
55+
// mmap guarantees new mappings are zero-init.
56+
this.write_bytes_ptr(
57+
ptr.offset(Size::from_bytes(old_size), this).unwrap().into(),
58+
std::iter::repeat(0u8).take(usize::try_from(increase).unwrap()),
59+
)
60+
.unwrap();
61+
}
62+
// Memory mappings are always exposed
63+
Machine::expose_ptr(this, ptr)?;
64+
65+
Ok(Scalar::from_pointer(ptr, this))
66+
}
67+
}

src/shims/unix/linux/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
pub mod dlsym;
22
pub mod fd;
33
pub mod foreign_items;
4+
pub mod mem;
45
pub mod sync;

src/shims/unix/mem.rs

Lines changed: 15 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
//! This is an incomplete implementation of mmap/mremap/munmap which is restricted in order to be
1+
//! This is an incomplete implementation of mmap/munmap which is restricted in order to be
22
//! implementable on top of the existing memory system. The point of these function as-written is
33
//! to allow memory allocators written entirely in Rust to be executed by Miri. This implementation
44
//! does not support other uses of mmap such as file mappings.
55
//!
6-
//! mmap/mremap/munmap behave a lot like alloc/realloc/dealloc, and for simple use they are exactly
6+
//! mmap/munmap behave a lot like alloc/dealloc, and for simple use they are exactly
77
//! equivalent. That is the only part we support: no MAP_FIXED or MAP_SHARED or anything
88
//! else that goes beyond a basic allocation API.
99
@@ -23,23 +23,23 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
2323
) -> InterpResult<'tcx, Scalar<Provenance>> {
2424
let this = self.eval_context_mut();
2525

26-
// We do not support MAP_FIXED, so the addr argument is always ignored
27-
let addr = this.read_pointer(addr)?;
26+
// We do not support MAP_FIXED, so the addr argument is always ignored (except for the MacOS hack)
27+
let addr = this.read_target_usize(addr)?;
2828
let length = this.read_target_usize(length)?;
2929
let prot = this.read_scalar(prot)?.to_i32()?;
3030
let flags = this.read_scalar(flags)?.to_i32()?;
3131
let fd = this.read_scalar(fd)?.to_i32()?;
32-
let offset = this.read_scalar(offset)?.to_target_usize(this)?;
32+
let offset = this.read_target_usize(offset)?;
3333

3434
let map_private = this.eval_libc_i32("MAP_PRIVATE");
3535
let map_anonymous = this.eval_libc_i32("MAP_ANONYMOUS");
3636
let map_shared = this.eval_libc_i32("MAP_SHARED");
3737
let map_fixed = this.eval_libc_i32("MAP_FIXED");
3838

39-
// This is a horrible hack, but on macos the guard page mechanism uses mmap
39+
// This is a horrible hack, but on MacOS the guard page mechanism uses mmap
4040
// in a way we do not support. We just give it the return value it expects.
4141
if this.frame_in_std() && this.tcx.sess.target.os == "macos" && (flags & map_fixed) != 0 {
42-
return Ok(Scalar::from_maybe_pointer(addr, this));
42+
return Ok(Scalar::from_maybe_pointer(Pointer::from_addr_invalid(addr), this));
4343
}
4444

4545
let prot_read = this.eval_libc_i32("PROT_READ");
@@ -106,74 +106,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
106106
Ok(Scalar::from_pointer(ptr, this))
107107
}
108108

109-
fn mremap(
110-
&mut self,
111-
old_address: &OpTy<'tcx, Provenance>,
112-
old_size: &OpTy<'tcx, Provenance>,
113-
new_size: &OpTy<'tcx, Provenance>,
114-
flags: &OpTy<'tcx, Provenance>,
115-
) -> InterpResult<'tcx, Scalar<Provenance>> {
116-
let this = self.eval_context_mut();
117-
118-
let old_address = this.read_scalar(old_address)?.to_target_usize(this)?;
119-
let old_size = this.read_scalar(old_size)?.to_target_usize(this)?;
120-
let new_size = this.read_scalar(new_size)?.to_target_usize(this)?;
121-
let flags = this.read_scalar(flags)?.to_i32()?;
122-
123-
// old_address must be a multiple of the page size
124-
#[allow(clippy::arithmetic_side_effects)] // PAGE_SIZE is nonzero
125-
if old_address % this.machine.page_size != 0 || new_size == 0 {
126-
this.set_last_error(Scalar::from_i32(this.eval_libc_i32("EINVAL")))?;
127-
return Ok(this.eval_libc("MAP_FAILED"));
128-
}
129-
130-
if flags & this.eval_libc_i32("MREMAP_FIXED") != 0 {
131-
throw_unsup_format!("Miri does not support mremap wth MREMAP_FIXED");
132-
}
133-
134-
if flags & this.eval_libc_i32("MREMAP_DONTUNMAP") != 0 {
135-
throw_unsup_format!("Miri does not support mremap wth MREMAP_DONTUNMAP");
136-
}
137-
138-
if flags & this.eval_libc_i32("MREMAP_MAYMOVE") == 0 {
139-
// We only support MREMAP_MAYMOVE, so not passing the flag is just a failure
140-
this.set_last_error(Scalar::from_i32(this.eval_libc_i32("EINVAL")))?;
141-
return Ok(Scalar::from_maybe_pointer(Pointer::null(), this));
142-
}
143-
144-
let old_address = Machine::ptr_from_addr_cast(this, old_address)?;
145-
let align = this.machine.page_align();
146-
let ptr = this.reallocate_ptr(
147-
old_address,
148-
Some((Size::from_bytes(old_size), align)),
149-
Size::from_bytes(new_size),
150-
align,
151-
MiriMemoryKind::Mmap.into(),
152-
)?;
153-
if let Some(increase) = new_size.checked_sub(old_size) {
154-
// We just allocated this, the access is definitely in-bounds and fits into our address space.
155-
// mmap guarantees new mappings are zero-init.
156-
this.write_bytes_ptr(
157-
ptr.offset(Size::from_bytes(old_size), this).unwrap().into(),
158-
std::iter::repeat(0u8).take(usize::try_from(increase).unwrap()),
159-
)
160-
.unwrap();
161-
}
162-
// Memory mappings are always exposed
163-
Machine::expose_ptr(this, ptr)?;
164-
165-
Ok(Scalar::from_pointer(ptr, this))
166-
}
167-
168109
fn munmap(
169110
&mut self,
170111
addr: &OpTy<'tcx, Provenance>,
171112
length: &OpTy<'tcx, Provenance>,
172113
) -> InterpResult<'tcx, Scalar<Provenance>> {
173114
let this = self.eval_context_mut();
174115

175-
let addr = this.read_scalar(addr)?.to_target_usize(this)?;
176-
let length = this.read_scalar(length)?.to_target_usize(this)?;
116+
let addr = this.read_target_usize(addr)?;
117+
let length = this.read_target_usize(length)?;
177118

178119
// addr must be a multiple of the page size
179120
#[allow(clippy::arithmetic_side_effects)] // PAGE_SIZE is nonzero
@@ -193,6 +134,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
193134
throw_unsup_format!("Miri only supports munmap on memory allocated directly by mmap");
194135
};
195136

137+
// Elsewhere in this function we are careful to check what we can and throw an unsupported
138+
// error instead of Undefined Behavior when use of this function falls outside of the
139+
// narrow scope we support. We deliberately do not check the MemoryKind of this allocation,
140+
// because we want to report UB on attempting to unmap memory that Rust "understands", such
141+
// the stack, heap, or statics.
196142
let (_kind, alloc) = this.memory.alloc_map().get(alloc_id).unwrap();
197143
if offset != Size::ZERO || alloc.len() as u64 != length {
198144
throw_unsup_format!(
@@ -202,20 +148,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
202148

203149
let len = Size::from_bytes(alloc.len() as u64);
204150
this.deallocate_ptr(
205-
Pointer::new(Some(Provenance::Wildcard), Size::from_bytes(addr)),
151+
ptr.into(),
206152
Some((len, this.machine.page_align())),
207153
MemoryKind::Machine(MiriMemoryKind::Mmap),
208154
)?;
209155

210156
Ok(Scalar::from_i32(0))
211157
}
212158
}
213-
214-
trait RangeExt {
215-
fn overlaps(&self, other: &Self) -> bool;
216-
}
217-
impl RangeExt for std::ops::Range<Size> {
218-
fn overlaps(&self, other: &Self) -> bool {
219-
self.start.max(other.start) <= self.end.min(other.end)
220-
}
221-
}
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)