Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 4b1376a

Browse files
committed
Auto merge of rust-lang#3682 - RalfJung:alloc-too-large, r=RalfJung
show proper UB when making a too large allocation request Fixes rust-lang/miri#3679
2 parents 3f2c50c + b8db9f0 commit 4b1376a

File tree

5 files changed

+59
-26
lines changed

5 files changed

+59
-26
lines changed

src/tools/miri/src/alloc_bytes.rs

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -64,17 +64,19 @@ impl MiriAllocBytes {
6464
/// If `size == 0` we allocate using a different `alloc_layout` with `size = 1`, to ensure each allocation has a unique address.
6565
/// Returns `Err(alloc_layout)` if the allocation function returns a `ptr` where `ptr.is_null()`.
6666
fn alloc_with(
67-
size: usize,
68-
align: usize,
67+
size: u64,
68+
align: u64,
6969
alloc_fn: impl FnOnce(Layout) -> *mut u8,
70-
) -> Result<MiriAllocBytes, Layout> {
71-
let layout = Layout::from_size_align(size, align).unwrap();
70+
) -> Result<MiriAllocBytes, ()> {
71+
let size = usize::try_from(size).map_err(|_| ())?;
72+
let align = usize::try_from(align).map_err(|_| ())?;
73+
let layout = Layout::from_size_align(size, align).map_err(|_| ())?;
7274
// When size is 0 we allocate 1 byte anyway, to ensure each allocation has a unique address.
7375
let alloc_layout =
7476
if size == 0 { Layout::from_size_align(1, align).unwrap() } else { layout };
7577
let ptr = alloc_fn(alloc_layout);
7678
if ptr.is_null() {
77-
Err(alloc_layout)
79+
Err(())
7880
} else {
7981
// SAFETY: All `MiriAllocBytes` invariants are fulfilled.
8082
Ok(Self { ptr, layout })
@@ -86,20 +88,22 @@ impl AllocBytes for MiriAllocBytes {
8688
fn from_bytes<'a>(slice: impl Into<Cow<'a, [u8]>>, align: Align) -> Self {
8789
let slice = slice.into();
8890
let size = slice.len();
89-
let align = align.bytes_usize();
91+
let align = align.bytes();
9092
// SAFETY: `alloc_fn` will only be used with `size != 0`.
9193
let alloc_fn = |layout| unsafe { alloc::alloc(layout) };
92-
let alloc_bytes = MiriAllocBytes::alloc_with(size, align, alloc_fn)
93-
.unwrap_or_else(|layout| alloc::handle_alloc_error(layout));
94+
let alloc_bytes = MiriAllocBytes::alloc_with(size.try_into().unwrap(), align, alloc_fn)
95+
.unwrap_or_else(|()| {
96+
panic!("Miri ran out of memory: cannot create allocation of {size} bytes")
97+
});
9498
// SAFETY: `alloc_bytes.ptr` and `slice.as_ptr()` are non-null, properly aligned
9599
// and valid for the `size`-many bytes to be copied.
96100
unsafe { alloc_bytes.ptr.copy_from(slice.as_ptr(), size) };
97101
alloc_bytes
98102
}
99103

100104
fn zeroed(size: Size, align: Align) -> Option<Self> {
101-
let size = size.bytes_usize();
102-
let align = align.bytes_usize();
105+
let size = size.bytes();
106+
let align = align.bytes();
103107
// SAFETY: `alloc_fn` will only be used with `size != 0`.
104108
let alloc_fn = |layout| unsafe { alloc::alloc_zeroed(layout) };
105109
MiriAllocBytes::alloc_with(size, align, alloc_fn).ok()

src/tools/miri/src/shims/alloc.rs

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,6 @@ use rustc_target::abi::{Align, Size};
55

66
use crate::*;
77

8-
/// Check some basic requirements for this allocation request:
9-
/// non-zero size, power-of-two alignment.
10-
pub(super) fn check_alloc_request<'tcx>(size: u64, align: u64) -> InterpResult<'tcx> {
11-
if size == 0 {
12-
throw_ub_format!("creating allocation with size 0");
13-
}
14-
if !align.is_power_of_two() {
15-
throw_ub_format!("creating allocation with non-power-of-two alignment {}", align);
16-
}
17-
Ok(())
18-
}
19-
208
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
219
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
2210
/// Returns the alignment that `malloc` would guarantee for requests of the given size.

src/tools/miri/src/shims/foreign_items.rs

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use rustc_target::{
1212
spec::abi::Abi,
1313
};
1414

15-
use super::alloc::{check_alloc_request, EvalContextExt as _};
15+
use super::alloc::EvalContextExt as _;
1616
use super::backtrace::EvalContextExt as _;
1717
use crate::*;
1818
use helpers::{ToHost, ToSoft};
@@ -204,6 +204,22 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
204204

205205
impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {}
206206
trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
207+
/// Check some basic requirements for this allocation request:
208+
/// non-zero size, power-of-two alignment.
209+
fn check_rustc_alloc_request(&self, size: u64, align: u64) -> InterpResult<'tcx> {
210+
let this = self.eval_context_ref();
211+
if size == 0 {
212+
throw_ub_format!("creating allocation with size 0");
213+
}
214+
if i128::from(size) > this.tcx.data_layout.pointer_size.signed_int_max() {
215+
throw_ub_format!("creating an allocation larger than half the address space");
216+
}
217+
if !align.is_power_of_two() {
218+
throw_ub_format!("creating allocation with non-power-of-two alignment {}", align);
219+
}
220+
Ok(())
221+
}
222+
207223
fn emulate_foreign_item_inner(
208224
&mut self,
209225
link_name: Symbol,
@@ -462,7 +478,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
462478
let size = this.read_target_usize(size)?;
463479
let align = this.read_target_usize(align)?;
464480

465-
check_alloc_request(size, align)?;
481+
this.check_rustc_alloc_request(size, align)?;
466482

467483
let memory_kind = match link_name.as_str() {
468484
"__rust_alloc" => MiriMemoryKind::Rust,
@@ -496,7 +512,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
496512
let size = this.read_target_usize(size)?;
497513
let align = this.read_target_usize(align)?;
498514

499-
check_alloc_request(size, align)?;
515+
this.check_rustc_alloc_request(size, align)?;
500516

501517
let ptr = this.allocate_ptr(
502518
Size::from_bytes(size),
@@ -560,7 +576,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
560576
let new_size = this.read_target_usize(new_size)?;
561577
// No need to check old_size; we anyway check that they match the allocation.
562578

563-
check_alloc_request(new_size, align)?;
579+
this.check_rustc_alloc_request(new_size, align)?;
564580

565581
let align = Align::from_bytes(align).unwrap();
566582
let new_ptr = this.reallocate_ptr(
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
extern "Rust" {
2+
fn __rust_alloc(size: usize, align: usize) -> *mut u8;
3+
}
4+
5+
fn main() {
6+
let bytes = isize::MAX as usize + 1;
7+
unsafe {
8+
__rust_alloc(bytes, 1); //~ERROR: larger than half the address space
9+
}
10+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error: Undefined Behavior: creating an allocation larger than half the address space
2+
--> $DIR/too_large.rs:LL:CC
3+
|
4+
LL | __rust_alloc(bytes, 1);
5+
| ^^^^^^^^^^^^^^^^^^^^^^ creating an allocation larger than half the address space
6+
|
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
9+
= note: BACKTRACE:
10+
= note: inside `main` at $DIR/too_large.rs:LL:CC
11+
12+
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
13+
14+
error: aborting due to 1 previous error
15+

0 commit comments

Comments
 (0)