Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit d84f749

Browse files
committedJun 16, 2023
Make munmap throw unsup errors instead of trying to work
1 parent db7b7b6 commit d84f749

File tree

6 files changed

+64
-64
lines changed

6 files changed

+64
-64
lines changed
 

‎src/shims/unix/mem.rs

+28-43
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
//! else that goes beyond a basic allocation API.
99
1010
use crate::*;
11-
use rustc_target::abi::{Align, Size};
11+
use rustc_target::abi::Size;
1212

1313
impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
1414
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
@@ -88,7 +88,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
8888
throw_unsup_format!("Miri does not support non-zero offsets to mmap");
8989
}
9090

91-
let align = Align::from_bytes(this.machine.page_size).unwrap();
91+
let align = this.machine.page_align();
9292
let map_length = this.machine.round_up_to_multiple_of_page_size(length).unwrap_or(u64::MAX);
9393

9494
let ptr =
@@ -115,14 +115,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
115115
) -> InterpResult<'tcx, Scalar<Provenance>> {
116116
let this = self.eval_context_mut();
117117

118-
let old_address = this.read_pointer(old_address)?;
118+
let old_address = this.read_scalar(old_address)?.to_target_usize(this)?;
119119
let old_size = this.read_scalar(old_size)?.to_target_usize(this)?;
120120
let new_size = this.read_scalar(new_size)?.to_target_usize(this)?;
121121
let flags = this.read_scalar(flags)?.to_i32()?;
122122

123123
// old_address must be a multiple of the page size
124124
#[allow(clippy::arithmetic_side_effects)] // PAGE_SIZE is nonzero
125-
if old_address.addr().bytes() % this.machine.page_size != 0 || new_size == 0 {
125+
if old_address % this.machine.page_size != 0 || new_size == 0 {
126126
this.set_last_error(Scalar::from_i32(this.eval_libc_i32("EINVAL")))?;
127127
return Ok(this.eval_libc("MAP_FAILED"));
128128
}
@@ -141,6 +141,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
141141
return Ok(Scalar::from_maybe_pointer(Pointer::null(), this));
142142
}
143143

144+
let old_address = Machine::ptr_from_addr_cast(this, old_address)?;
144145
let align = this.machine.page_align();
145146
let ptr = this.reallocate_ptr(
146147
old_address,
@@ -171,57 +172,41 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
171172
) -> InterpResult<'tcx, Scalar<Provenance>> {
172173
let this = self.eval_context_mut();
173174

174-
let addr = this.read_pointer(addr)?;
175+
let addr = this.read_scalar(addr)?.to_target_usize(this)?;
175176
let length = this.read_scalar(length)?.to_target_usize(this)?;
176177

177178
// addr must be a multiple of the page size
178179
#[allow(clippy::arithmetic_side_effects)] // PAGE_SIZE is nonzero
179-
if addr.addr().bytes() % this.machine.page_size != 0 {
180+
if addr % this.machine.page_size != 0 {
180181
this.set_last_error(Scalar::from_i32(this.eval_libc_i32("EINVAL")))?;
181182
return Ok(Scalar::from_i32(-1));
182183
}
183184

184185
let length = this.machine.round_up_to_multiple_of_page_size(length).unwrap_or(u64::MAX);
185186

186-
let mut addr = addr.addr().bytes();
187-
let mut bytes_unmapped = 0;
188-
while bytes_unmapped < length {
189-
// munmap specifies:
190-
// It is not an error if the indicated range does not contain any mapped pages.
191-
// So we make sure that if our address is not that of an exposed allocation, we just
192-
// step forward to the next page.
193-
let ptr = Machine::ptr_from_addr_cast(this, addr)?;
194-
let Ok(ptr) = ptr.into_pointer_or_addr() else {
195-
bytes_unmapped = bytes_unmapped.checked_add(this.machine.page_size).unwrap();
196-
addr = addr.wrapping_add(this.machine.page_size);
197-
continue;
198-
};
199-
// FIXME: This should fail if the pointer is to an unexposed allocation. But it
200-
// doesn't.
201-
let Some((alloc_id, offset, _prov)) = Machine::ptr_get_alloc(this, ptr) else {
202-
bytes_unmapped = bytes_unmapped.checked_add(this.machine.page_size).unwrap();
203-
addr = addr.wrapping_add(this.machine.page_size);
204-
continue;
205-
};
206-
207-
if offset != Size::ZERO {
208-
throw_unsup_format!("Miri does not support partial munmap");
209-
}
210-
let (_kind, alloc) = this.memory.alloc_map().get(alloc_id).unwrap();
211-
let this_alloc_len = alloc.len() as u64;
212-
bytes_unmapped = bytes_unmapped.checked_add(this_alloc_len).unwrap();
213-
if bytes_unmapped > length {
214-
throw_unsup_format!("Miri does not support partial munmap");
215-
}
216-
217-
this.deallocate_ptr(
218-
Pointer::new(Some(Provenance::Wildcard), Size::from_bytes(addr)),
219-
Some((Size::from_bytes(this_alloc_len), this.machine.page_align())),
220-
MemoryKind::Machine(MiriMemoryKind::Mmap),
221-
)?;
222-
addr = addr.wrapping_add(this_alloc_len);
187+
let ptr = Machine::ptr_from_addr_cast(this, addr)?;
188+
189+
let Ok(ptr) = ptr.into_pointer_or_addr() else {
190+
throw_unsup_format!("Miri only supports munmap on memory allocated directly by mmap");
191+
};
192+
let Some((alloc_id, offset, _prov)) = Machine::ptr_get_alloc(this, ptr) else {
193+
throw_unsup_format!("Miri only supports munmap on memory allocated directly by mmap");
194+
};
195+
196+
let (_kind, alloc) = this.memory.alloc_map().get(alloc_id).unwrap();
197+
if offset != Size::ZERO || alloc.len() as u64 != length {
198+
throw_unsup_format!(
199+
"Miri only supports munmap calls that exactly unmap a region previously returned by mmap"
200+
);
223201
}
224202

203+
let len = Size::from_bytes(alloc.len() as u64);
204+
this.deallocate_ptr(
205+
Pointer::new(Some(Provenance::Wildcard), Size::from_bytes(addr)),
206+
Some((len, this.machine.page_align())),
207+
MemoryKind::Machine(MiriMemoryKind::Mmap),
208+
)?;
209+
225210
Ok(Scalar::from_i32(0))
226211
}
227212
}

‎tests/fail/munmap.rs

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//@compile-flags: -Zmiri-disable-isolation
2+
//@ignore-target-windows: No libc on Windows
3+
4+
#![feature(rustc_private)]
5+
#![feature(strict_provenance)]
6+
7+
use std::ptr;
8+
9+
fn main() {
10+
// Linux specifies that it is not an error if the specified range does not contain any pages.
11+
// But we simply do not support such calls. This test checks that we report this as
12+
// unsupported, not Undefined Behavior.
13+
let res = unsafe {
14+
libc::munmap(
15+
//~ ERROR: unsupported operation
16+
// Some high address we surely have not allocated anything at
17+
ptr::invalid_mut(1 << 30),
18+
4096,
19+
)
20+
};
21+
assert_eq!(res, 0);
22+
}

‎tests/fail/munmap.stderr

+11-5
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
warning: integer-to-pointer cast
22
--> $DIR/munmap.rs:LL:CC
33
|
4-
LL | libc::munmap(ptr, 1);
5-
| ^^^^^^^^^^^^^^^^^^^^ integer-to-pointer cast
4+
LL | / libc::munmap(
5+
LL | | ptr::invalid_mut(1 << 30), // Some high address we surely have no allocated anything at
6+
LL | | 4096,
7+
LL | | )
8+
| |_________^ integer-to-pointer cast
69
|
710
= help: This program is using integer-to-pointer casts or (equivalently) `ptr::from_exposed_addr`,
811
= help: which means that Miri might miss pointer bugs in this program.
@@ -13,11 +16,14 @@ LL | libc::munmap(ptr, 1);
1316
= note: BACKTRACE:
1417
= note: inside `main` at $DIR/munmap.rs:LL:CC
1518

16-
error: unsupported operation: Miri does not support partial munmap
19+
error: unsupported operation: Miri only supports munmap on memory allocated directly by mmap
1720
--> $DIR/munmap.rs:LL:CC
1821
|
19-
LL | libc::munmap(ptr, 1);
20-
| ^^^^^^^^^^^^^^^^^^^^ Miri does not support partial munmap
22+
LL | / libc::munmap(
23+
LL | | ptr::invalid_mut(1 << 30), // Some high address we surely have no allocated anything at
24+
LL | | 4096,
25+
LL | | )
26+
| |_________^ Miri only supports munmap on memory allocated directly by mmap
2127
|
2228
= help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support
2329
= note: BACKTRACE:

‎tests/fail/munmap_partial.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@ fn main() {
1313
0,
1414
);
1515
libc::munmap(ptr, 1);
16-
//~^ ERROR: unsupported operation: Miri does not support partial munmap
16+
//~^ ERROR: unsupported operation
1717
}
1818
}

‎tests/fail/munmap_partial.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@ LL | libc::munmap(ptr, 1);
1313
= note: BACKTRACE:
1414
= note: inside `main` at $DIR/munmap_partial.rs:LL:CC
1515

16-
error: unsupported operation: Miri does not support partial munmap
16+
error: unsupported operation: Miri only supports munmap calls that exactly unmap a region previously returned by mmap
1717
--> $DIR/munmap_partial.rs:LL:CC
1818
|
1919
LL | libc::munmap(ptr, 1);
20-
| ^^^^^^^^^^^^^^^^^^^^ Miri does not support partial munmap
20+
| ^^^^^^^^^^^^^^^^^^^^ Miri only supports munmap calls that exactly unmap a region previously returned by mmap
2121
|
2222
= help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support
2323
= note: BACKTRACE:

‎tests/pass-dep/shims/mmap.rs

-13
Original file line numberDiff line numberDiff line change
@@ -63,21 +63,8 @@ fn test_mremap() {
6363
assert_eq!(res, 0i32);
6464
}
6565

66-
fn test_munmap() {
67-
// Linux specifies that it is not an error if the specified range does not contain any pages.
68-
let res = unsafe {
69-
libc::munmap(
70-
// Some high address we surely have no allocated anything at
71-
ptr::invalid_mut(1 << 30),
72-
4096,
73-
)
74-
};
75-
assert_eq!(res, 0);
76-
}
77-
7866
fn main() {
7967
test_mmap();
80-
test_munmap();
8168
#[cfg(target_os = "linux")]
8269
test_mremap();
8370
}

0 commit comments

Comments
 (0)
Please sign in to comment.