Skip to content

Commit 7f1e4de

Browse files
committed
Auto merge of #3220 - saethlin:strict-mmap, r=RalfJung
Make mmap not use expose semantics Fixes #3041 per #3041 (comment)
2 parents 99cd324 + ee50c15 commit 7f1e4de

File tree

8 files changed

+29
-138
lines changed

8 files changed

+29
-138
lines changed

src/shims/unix/linux/mem.rs

+2-5
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
1515
) -> InterpResult<'tcx, Scalar<Provenance>> {
1616
let this = self.eval_context_mut();
1717

18-
let old_address = this.read_target_usize(old_address)?;
18+
let old_address = this.read_pointer(old_address)?;
1919
let old_size = this.read_target_usize(old_size)?;
2020
let new_size = this.read_target_usize(new_size)?;
2121
let flags = this.read_scalar(flags)?.to_i32()?;
2222

2323
// old_address must be a multiple of the page size
2424
#[allow(clippy::arithmetic_side_effects)] // PAGE_SIZE is nonzero
25-
if old_address % this.machine.page_size != 0 || new_size == 0 {
25+
if old_address.addr().bytes() % this.machine.page_size != 0 || new_size == 0 {
2626
this.set_last_error(Scalar::from_i32(this.eval_libc_i32("EINVAL")))?;
2727
return Ok(this.eval_libc("MAP_FAILED"));
2828
}
@@ -41,7 +41,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
4141
return Ok(this.eval_libc("MAP_FAILED"));
4242
}
4343

44-
let old_address = Machine::ptr_from_addr_cast(this, old_address)?;
4544
let align = this.machine.page_align();
4645
let ptr = this.reallocate_ptr(
4746
old_address,
@@ -59,8 +58,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
5958
)
6059
.unwrap();
6160
}
62-
// Memory mappings are always exposed
63-
Machine::expose_ptr(this, ptr)?;
6461

6562
Ok(Scalar::from_pointer(ptr, this))
6663
}

src/shims/unix/mem.rs

+14-31
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@
66
//! 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.
9+
//!
10+
//! Note that in addition to only supporting malloc-like calls to mmap, we only support free-like
11+
//! calls to munmap, but for a very different reason. In principle, according to the man pages, it
12+
//! is possible to unmap arbitrary regions of address space. But in a high-level language like Rust
13+
//! this amounts to partial deallocation, which LLVM does not support. So any attempt to call our
14+
//! munmap shim which would partily unmap a region of address space previously mapped by mmap will
15+
//! report UB.
916
1017
use crate::{helpers::round_to_next_multiple_of, *};
1118
use rustc_target::abi::Size;
@@ -100,8 +107,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
100107
std::iter::repeat(0u8).take(usize::try_from(map_length).unwrap()),
101108
)
102109
.unwrap();
103-
// Memory mappings don't use provenance, and are always exposed.
104-
Machine::expose_ptr(this, ptr)?;
105110

106111
Ok(Scalar::from_pointer(ptr, this))
107112
}
@@ -113,43 +118,21 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
113118
) -> InterpResult<'tcx, Scalar<Provenance>> {
114119
let this = self.eval_context_mut();
115120

116-
let addr = this.read_target_usize(addr)?;
121+
let addr = this.read_pointer(addr)?;
117122
let length = this.read_target_usize(length)?;
118123

119-
// addr must be a multiple of the page size
124+
// addr must be a multiple of the page size, but apart from that munmap is just implemented
125+
// as a dealloc.
120126
#[allow(clippy::arithmetic_side_effects)] // PAGE_SIZE is nonzero
121-
if addr % this.machine.page_size != 0 {
127+
if addr.addr().bytes() % this.machine.page_size != 0 {
122128
this.set_last_error(Scalar::from_i32(this.eval_libc_i32("EINVAL")))?;
123129
return Ok(Scalar::from_i32(-1));
124130
}
125131

126-
let length = round_to_next_multiple_of(length, this.machine.page_size);
127-
128-
let ptr = Machine::ptr_from_addr_cast(this, addr)?;
129-
130-
let Ok(ptr) = ptr.into_pointer_or_addr() else {
131-
throw_unsup_format!("Miri only supports munmap on memory allocated directly by mmap");
132-
};
133-
let Some((alloc_id, offset, _prov)) = Machine::ptr_get_alloc(this, ptr) else {
134-
throw_unsup_format!("Miri only supports munmap on memory allocated directly by mmap");
135-
};
136-
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.
142-
let (_kind, alloc) = this.memory.alloc_map().get(alloc_id).unwrap();
143-
if offset != Size::ZERO || alloc.len() as u64 != length {
144-
throw_unsup_format!(
145-
"Miri only supports munmap calls that exactly unmap a region previously returned by mmap"
146-
);
147-
}
148-
149-
let len = Size::from_bytes(alloc.len() as u64);
132+
let length = Size::from_bytes(round_to_next_multiple_of(length, this.machine.page_size));
150133
this.deallocate_ptr(
151-
ptr.into(),
152-
Some((len, this.machine.page_align())),
134+
addr,
135+
Some((length, this.machine.page_align())),
153136
MemoryKind::Machine(MiriMemoryKind::Mmap),
154137
)?;
155138

tests/fail-dep/shims/mmap_use_after_munmap.stderr

+1-16
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,3 @@
1-
warning: integer-to-pointer cast
2-
--> $DIR/mmap_use_after_munmap.rs:LL:CC
3-
|
4-
LL | libc::munmap(ptr, 4096);
5-
| ^^^^^^^^^^^^^^^^^^^^^^^ integer-to-pointer cast
6-
|
7-
= help: This program is using integer-to-pointer casts or (equivalently) `ptr::from_exposed_addr`,
8-
= help: which means that Miri might miss pointer bugs in this program.
9-
= help: See https://doc.rust-lang.org/nightly/std/ptr/fn.from_exposed_addr.html for more details on that operation.
10-
= help: To ensure that Miri does not miss bugs in your program, use Strict Provenance APIs (https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance, https://crates.io/crates/sptr) instead.
11-
= help: You can then pass the `-Zmiri-strict-provenance` flag to Miri, to ensure you are not relying on `from_exposed_addr` semantics.
12-
= help: Alternatively, the `-Zmiri-permissive-provenance` flag disables this warning.
13-
= note: BACKTRACE:
14-
= note: inside `main` at $DIR/mmap_use_after_munmap.rs:LL:CC
15-
161
error: Undefined Behavior: memory access failed: ALLOC has been freed, so this pointer is dangling
172
--> $DIR/mmap_use_after_munmap.rs:LL:CC
183
|
@@ -43,5 +28,5 @@ LL | libc::munmap(ptr, 4096);
4328

4429
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
4530

46-
error: aborting due to 1 previous error; 1 warning emitted
31+
error: aborting due to 1 previous error
4732

tests/fail-dep/shims/munmap.rs

-22
This file was deleted.

tests/fail-dep/shims/munmap.stderr

-39
This file was deleted.

tests/fail-dep/shims/munmap_partial.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
//! Our mmap/munmap support is a thin wrapper over Interpcx::allocate_ptr. Since the underlying
2-
//! layer has much more UB than munmap does, we need to be sure we throw an unsupported error here.
1+
//! The man pages for mmap/munmap suggest that it is possible to partly unmap a previously-mapped
2+
//! region of addres space, but to LLVM that would be partial deallocation, which LLVM does not
3+
//! support. So even though the man pages say this sort of use is possible, we must report UB.
34
//@ignore-target-windows: No libc on Windows
5+
//@normalize-stderr-test: "size [0-9]+ and alignment" -> "size SIZE and alignment"
46

57
fn main() {
68
unsafe {
@@ -13,6 +15,6 @@ fn main() {
1315
0,
1416
);
1517
libc::munmap(ptr, 1);
16-
//~^ ERROR: unsupported operation
18+
//~^ ERROR: Undefined Behavior
1719
}
1820
}
+5-19
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,15 @@
1-
warning: integer-to-pointer cast
1+
error: Undefined Behavior: incorrect layout on deallocation: ALLOC has size SIZE and alignment ALIGN, but gave size SIZE and alignment ALIGN
22
--> $DIR/munmap_partial.rs:LL:CC
33
|
44
LL | libc::munmap(ptr, 1);
5-
| ^^^^^^^^^^^^^^^^^^^^ integer-to-pointer cast
5+
| ^^^^^^^^^^^^^^^^^^^^ incorrect layout on deallocation: ALLOC has size SIZE and alignment ALIGN, but gave size SIZE and alignment ALIGN
66
|
7-
= help: This program is using integer-to-pointer casts or (equivalently) `ptr::from_exposed_addr`,
8-
= help: which means that Miri might miss pointer bugs in this program.
9-
= help: See https://doc.rust-lang.org/nightly/std/ptr/fn.from_exposed_addr.html for more details on that operation.
10-
= help: To ensure that Miri does not miss bugs in your program, use Strict Provenance APIs (https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance, https://crates.io/crates/sptr) instead.
11-
= help: You can then pass the `-Zmiri-strict-provenance` flag to Miri, to ensure you are not relying on `from_exposed_addr` semantics.
12-
= help: Alternatively, the `-Zmiri-permissive-provenance` flag disables this warning.
13-
= note: BACKTRACE:
14-
= note: inside `main` at $DIR/munmap_partial.rs:LL:CC
15-
16-
error: unsupported operation: Miri only supports munmap calls that exactly unmap a region previously returned by mmap
17-
--> $DIR/munmap_partial.rs:LL:CC
18-
|
19-
LL | libc::munmap(ptr, 1);
20-
| ^^^^^^^^^^^^^^^^^^^^ Miri only supports munmap calls that exactly unmap a region previously returned by mmap
21-
|
22-
= help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support
7+
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
8+
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
239
= note: BACKTRACE:
2410
= note: inside `main` at $DIR/munmap_partial.rs:LL:CC
2511

2612
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
2713

28-
error: aborting due to 1 previous error; 1 warning emitted
14+
error: aborting due to 1 previous error
2915

tests/pass-dep/shims/mmap.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,8 @@ fn test_mmap() {
2929
}
3030
assert!(slice.iter().all(|b| *b == 1));
3131

32-
// Ensure that we can munmap with just an integer
33-
let just_an_address = ptr::invalid_mut(ptr.addr());
34-
let res = unsafe { libc::munmap(just_an_address, page_size) };
32+
// Ensure that we can munmap
33+
let res = unsafe { libc::munmap(ptr, page_size) };
3534
assert_eq!(res, 0i32);
3635

3736
// Test all of our error conditions

0 commit comments

Comments
 (0)