Skip to content

Commit 8bec0a5

Browse files
committed
Auto merge of #3246 - saethlin:mmap-ices, r=RalfJung
Fix integer overflow ICEs from round_up_to_next_multiple_of Turns out the standard library _does_ have the function we need. So I swapped us to using the checked version in mmap/munmap where we can return an error, and we're still using the ICEy version in SIMD. I found one of the ICE cases by running this test: https://github.com/wbcchsyn/rust-mmap-allocator/blob/765bcaab6e3bfd1cb1e6eaac80ac7e821fb5979b/src/mmap_allocator.rs#L195-L210
2 parents a63fd5e + 4da47a4 commit 8bec0a5

File tree

5 files changed

+44
-17
lines changed

5 files changed

+44
-17
lines changed

src/tools/miri/src/helpers.rs

-8
Original file line numberDiff line numberDiff line change
@@ -1233,11 +1233,3 @@ pub(crate) fn simd_element_to_bool(elem: ImmTy<'_, Provenance>) -> InterpResult<
12331233
_ => throw_ub_format!("each element of a SIMD mask must be all-0-bits or all-1-bits"),
12341234
})
12351235
}
1236-
1237-
// This looks like something that would be nice to have in the standard library...
1238-
pub(crate) fn round_to_next_multiple_of(x: u64, divisor: u64) -> u64 {
1239-
assert_ne!(divisor, 0);
1240-
// divisor is nonzero; multiplication cannot overflow since we just divided
1241-
#[allow(clippy::arithmetic_side_effects)]
1242-
return (x.checked_add(divisor - 1).unwrap() / divisor) * divisor;
1243-
}

src/tools/miri/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#![feature(round_ties_even)]
1212
#![feature(let_chains)]
1313
#![feature(lint_reasons)]
14+
#![feature(int_roundings)]
1415
// Configure clippy and other lints
1516
#![allow(
1617
clippy::collapsible_else_if,

src/tools/miri/src/shims/intrinsics/simd.rs

+3-6
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,7 @@ use rustc_middle::{mir, ty, ty::FloatTy};
44
use rustc_span::{sym, Symbol};
55
use rustc_target::abi::{Endian, HasDataLayout};
66

7-
use crate::helpers::{
8-
bool_to_simd_element, check_arg_count, round_to_next_multiple_of, simd_element_to_bool, ToHost,
9-
ToSoft,
10-
};
7+
use crate::helpers::{bool_to_simd_element, check_arg_count, simd_element_to_bool, ToHost, ToSoft};
118
use crate::*;
129

1310
#[derive(Copy, Clone)]
@@ -407,7 +404,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
407404
let (yes, yes_len) = this.operand_to_simd(yes)?;
408405
let (no, no_len) = this.operand_to_simd(no)?;
409406
let (dest, dest_len) = this.place_to_simd(dest)?;
410-
let bitmask_len = round_to_next_multiple_of(dest_len, 8);
407+
let bitmask_len = dest_len.next_multiple_of(8);
411408

412409
// The mask must be an integer or an array.
413410
assert!(
@@ -453,7 +450,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
453450
"bitmask" => {
454451
let [op] = check_arg_count(args)?;
455452
let (op, op_len) = this.operand_to_simd(op)?;
456-
let bitmask_len = round_to_next_multiple_of(op_len, 8);
453+
let bitmask_len = op_len.next_multiple_of(8);
457454

458455
// Returns either an unsigned integer or array of `u8`.
459456
assert!(

src/tools/miri/src/shims/unix/mem.rs

+19-3
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
//! munmap shim which would partily unmap a region of address space previously mapped by mmap will
1515
//! report UB.
1616
17-
use crate::{helpers::round_to_next_multiple_of, *};
17+
use crate::*;
1818
use rustc_target::abi::Size;
1919

2020
impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
@@ -96,7 +96,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
9696
}
9797

9898
let align = this.machine.page_align();
99-
let map_length = round_to_next_multiple_of(length, this.machine.page_size);
99+
let Some(map_length) = length.checked_next_multiple_of(this.machine.page_size) else {
100+
this.set_last_error(Scalar::from_i32(this.eval_libc_i32("EINVAL")))?;
101+
return Ok(this.eval_libc("MAP_FAILED"));
102+
};
103+
if map_length > this.target_usize_max() {
104+
this.set_last_error(Scalar::from_i32(this.eval_libc_i32("EINVAL")))?;
105+
return Ok(this.eval_libc("MAP_FAILED"));
106+
}
100107

101108
let ptr =
102109
this.allocate_ptr(Size::from_bytes(map_length), align, MiriMemoryKind::Mmap.into())?;
@@ -129,7 +136,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
129136
return Ok(Scalar::from_i32(-1));
130137
}
131138

132-
let length = Size::from_bytes(round_to_next_multiple_of(length, this.machine.page_size));
139+
let Some(length) = length.checked_next_multiple_of(this.machine.page_size) else {
140+
this.set_last_error(Scalar::from_i32(this.eval_libc_i32("EINVAL")))?;
141+
return Ok(Scalar::from_i32(-1));
142+
};
143+
if length > this.target_usize_max() {
144+
this.set_last_error(Scalar::from_i32(this.eval_libc_i32("EINVAL")))?;
145+
return Ok(this.eval_libc("MAP_FAILED"));
146+
}
147+
148+
let length = Size::from_bytes(length);
133149
this.deallocate_ptr(
134150
addr,
135151
Some((length, this.machine.page_align())),

src/tools/miri/tests/pass-dep/shims/mmap.rs

+21
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,30 @@ fn test_mmap() {
9090
assert_eq!(Error::last_os_error().raw_os_error().unwrap(), libc::ENOTSUP);
9191
}
9292

93+
// We report an error for mappings whose length cannot be rounded up to a multiple of
94+
// the page size.
95+
let ptr = unsafe {
96+
libc::mmap(
97+
ptr::null_mut(),
98+
usize::MAX - 1,
99+
libc::PROT_READ | libc::PROT_WRITE,
100+
libc::MAP_PRIVATE | libc::MAP_ANONYMOUS,
101+
-1,
102+
0,
103+
)
104+
};
105+
assert_eq!(ptr, libc::MAP_FAILED);
106+
107+
// We report an error when trying to munmap an address which is not a multiple of the page size
93108
let res = unsafe { libc::munmap(ptr::invalid_mut(1), page_size) };
94109
assert_eq!(res, -1);
95110
assert_eq!(Error::last_os_error().raw_os_error().unwrap(), libc::EINVAL);
111+
112+
// We report an error when trying to munmap a length that cannot be rounded up to a multiple of
113+
// the page size.
114+
let res = unsafe { libc::munmap(ptr::invalid_mut(page_size), usize::MAX - 1) };
115+
assert_eq!(res, -1);
116+
assert_eq!(Error::last_os_error().raw_os_error().unwrap(), libc::EINVAL);
96117
}
97118

98119
#[cfg(target_os = "linux")]

0 commit comments

Comments
 (0)