Skip to content

Commit 9776f64

Browse files
committed
Auto merge of rust-lang#3480 - RalfJung:alloc_error_handler, r=RalfJung
directly call handle_alloc_error Also test more codepaths. There's like 5 different things that can happen on allocation failure! Between `-Zoom`, `#[alloc_error_handler]`, and `set_alloc_error_hook`, we have 3 layers of behavior overrides. It's all a bit messy. rust-lang#112331 seems intended to clean this up, but has not yet reached consensus.
2 parents 14701ef + 9f156d3 commit 9776f64

16 files changed

+171
-120
lines changed

src/tools/miri/tests/fail/alloc/alloc_error_handler.rs

+1-16
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,7 @@
44
#![feature(allocator_api)]
55

66
use std::alloc::*;
7-
use std::ptr::NonNull;
8-
9-
struct BadAlloc;
10-
11-
// Create a failing allocator; Miri's native allocator never fails so this is the only way to
12-
// actually call the alloc error handler.
13-
unsafe impl Allocator for BadAlloc {
14-
fn allocate(&self, _l: Layout) -> Result<NonNull<[u8]>, AllocError> {
15-
Err(AllocError)
16-
}
17-
18-
unsafe fn deallocate(&self, _ptr: NonNull<u8>, _layout: Layout) {
19-
unreachable!();
20-
}
21-
}
227

238
fn main() {
24-
let _b = Box::new_in(0, BadAlloc);
9+
handle_alloc_error(Layout::for_value(&0));
2510
}

src/tools/miri/tests/fail/alloc/alloc_error_handler.stderr

+1-3
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,10 @@ LL | ABORT();
1212
= note: inside `std::alloc::_::__rg_oom` at RUSTLIB/std/src/alloc.rs:LL:CC
1313
= note: inside `std::alloc::handle_alloc_error::rt_error` at RUSTLIB/alloc/src/alloc.rs:LL:CC
1414
= note: inside `std::alloc::handle_alloc_error` at RUSTLIB/alloc/src/alloc.rs:LL:CC
15-
= note: inside `std::boxed::Box::<i32, BadAlloc>::new_uninit_in` at RUSTLIB/alloc/src/boxed.rs:LL:CC
16-
= note: inside `std::boxed::Box::<i32, BadAlloc>::new_in` at RUSTLIB/alloc/src/boxed.rs:LL:CC
1715
note: inside `main`
1816
--> $DIR/alloc_error_handler.rs:LL:CC
1917
|
20-
LL | let _b = Box::new_in(0, BadAlloc);
18+
LL | handle_alloc_error(Layout::for_value(&0));
2119
| ^
2220

2321
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
//@compile-flags: -Cpanic=abort
2+
#![feature(start, core_intrinsics)]
3+
#![feature(alloc_error_handler)]
4+
#![feature(allocator_api)]
5+
#![no_std]
6+
7+
extern crate alloc;
8+
9+
use alloc::alloc::*;
10+
use core::fmt::Write;
11+
12+
#[path = "../../utils/mod.no_std.rs"]
13+
mod utils;
14+
15+
#[alloc_error_handler]
16+
fn alloc_error_handler(layout: Layout) -> ! {
17+
let _ = writeln!(utils::MiriStderr, "custom alloc error handler: {layout:?}");
18+
core::intrinsics::abort(); //~ERROR: aborted
19+
}
20+
21+
// rustc requires us to provide some more things that aren't actually used by this test
22+
mod plumbing {
23+
use super::*;
24+
25+
#[panic_handler]
26+
fn panic_handler(_: &core::panic::PanicInfo) -> ! {
27+
loop {}
28+
}
29+
30+
struct NoAlloc;
31+
32+
unsafe impl GlobalAlloc for NoAlloc {
33+
unsafe fn alloc(&self, _: Layout) -> *mut u8 {
34+
unreachable!();
35+
}
36+
37+
unsafe fn dealloc(&self, _: *mut u8, _: Layout) {
38+
unreachable!();
39+
}
40+
}
41+
42+
#[global_allocator]
43+
static GLOBAL: NoAlloc = NoAlloc;
44+
}
45+
46+
#[start]
47+
fn start(_: isize, _: *const *const u8) -> isize {
48+
handle_alloc_error(Layout::for_value(&0));
49+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
custom alloc error handler: Layout { size: 4, align: 4 (1 << 2) }
2+
error: abnormal termination: the program aborted execution
3+
--> $DIR/alloc_error_handler_custom.rs:LL:CC
4+
|
5+
LL | core::intrinsics::abort();
6+
| ^^^^^^^^^^^^^^^^^^^^^^^^^ the program aborted execution
7+
|
8+
= note: BACKTRACE:
9+
= note: inside `alloc_error_handler` at $DIR/alloc_error_handler_custom.rs:LL:CC
10+
note: inside `_::__rg_oom`
11+
--> $DIR/alloc_error_handler_custom.rs:LL:CC
12+
|
13+
LL | #[alloc_error_handler]
14+
| ---------------------- in this procedural macro expansion
15+
LL | fn alloc_error_handler(layout: Layout) -> ! {
16+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
17+
= note: inside `alloc::alloc::handle_alloc_error::rt_error` at RUSTLIB/alloc/src/alloc.rs:LL:CC
18+
= note: inside `alloc::alloc::handle_alloc_error` at RUSTLIB/alloc/src/alloc.rs:LL:CC
19+
note: inside `start`
20+
--> $DIR/alloc_error_handler_custom.rs:LL:CC
21+
|
22+
LL | handle_alloc_error(Layout::for_value(&0));
23+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
24+
= note: this error originates in the attribute macro `alloc_error_handler` (in Nightly builds, run with -Z macro-backtrace for more info)
25+
26+
error: aborting due to 1 previous error
27+

src/tools/miri/tests/fail/alloc/alloc_error_handler_no_std.rs

+9-27
Original file line numberDiff line numberDiff line change
@@ -7,41 +7,24 @@
77
extern crate alloc;
88

99
use alloc::alloc::*;
10-
use alloc::boxed::Box;
11-
use core::ptr::NonNull;
10+
use core::fmt::Write;
1211

13-
struct BadAlloc;
12+
#[path = "../../utils/mod.no_std.rs"]
13+
mod utils;
1414

15-
// Create a failing allocator; that is the only way to actually call the alloc error handler.
16-
unsafe impl Allocator for BadAlloc {
17-
fn allocate(&self, _l: Layout) -> Result<NonNull<[u8]>, AllocError> {
18-
Err(AllocError)
19-
}
20-
21-
unsafe fn deallocate(&self, _ptr: NonNull<u8>, _layout: Layout) {
22-
unreachable!();
23-
}
24-
}
15+
// The default no_std alloc_error_handler is a panic.
2516

26-
#[alloc_error_handler]
27-
fn alloc_error_handler(_: Layout) -> ! {
28-
extern "Rust" {
29-
fn miri_write_to_stderr(bytes: &[u8]);
30-
}
31-
let msg = "custom alloc error handler called!\n";
32-
unsafe { miri_write_to_stderr(msg.as_bytes()) };
17+
#[panic_handler]
18+
fn panic_handler(panic_info: &core::panic::PanicInfo) -> ! {
19+
let _ = writeln!(utils::MiriStderr, "custom panic handler called!");
20+
let _ = writeln!(utils::MiriStderr, "{panic_info}");
3321
core::intrinsics::abort(); //~ERROR: aborted
3422
}
3523

3624
// rustc requires us to provide some more things that aren't actually used by this test
3725
mod plumbing {
3826
use super::*;
3927

40-
#[panic_handler]
41-
fn panic_handler(_: &core::panic::PanicInfo) -> ! {
42-
core::intrinsics::abort();
43-
}
44-
4528
struct NoAlloc;
4629

4730
unsafe impl GlobalAlloc for NoAlloc {
@@ -60,6 +43,5 @@ mod plumbing {
6043

6144
#[start]
6245
fn start(_: isize, _: *const *const u8) -> isize {
63-
let _b = Box::new_in(0, BadAlloc);
64-
0
46+
handle_alloc_error(Layout::for_value(&0));
6547
}
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,24 @@
1-
custom alloc error handler called!
1+
custom panic handler called!
2+
panicked at RUSTLIB/alloc/src/alloc.rs:LL:CC:
3+
memory allocation of 4 bytes failed
24
error: abnormal termination: the program aborted execution
35
--> $DIR/alloc_error_handler_no_std.rs:LL:CC
46
|
57
LL | core::intrinsics::abort();
68
| ^^^^^^^^^^^^^^^^^^^^^^^^^ the program aborted execution
79
|
810
= note: BACKTRACE:
9-
= note: inside `alloc_error_handler` at $DIR/alloc_error_handler_no_std.rs:LL:CC
10-
note: inside `_::__rg_oom`
11-
--> $DIR/alloc_error_handler_no_std.rs:LL:CC
12-
|
13-
LL | #[alloc_error_handler]
14-
| ---------------------- in this procedural macro expansion
15-
LL | fn alloc_error_handler(_: Layout) -> ! {
16-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
11+
= note: inside `panic_handler` at $DIR/alloc_error_handler_no_std.rs:LL:CC
12+
= note: inside `alloc::alloc::__alloc_error_handler::__rdl_oom` at RUSTLIB/alloc/src/alloc.rs:LL:CC
1713
= note: inside `alloc::alloc::handle_alloc_error::rt_error` at RUSTLIB/alloc/src/alloc.rs:LL:CC
1814
= note: inside `alloc::alloc::handle_alloc_error` at RUSTLIB/alloc/src/alloc.rs:LL:CC
19-
= note: inside `alloc::boxed::Box::<i32, BadAlloc>::new_uninit_in` at RUSTLIB/alloc/src/boxed.rs:LL:CC
20-
= note: inside `alloc::boxed::Box::<i32, BadAlloc>::new_in` at RUSTLIB/alloc/src/boxed.rs:LL:CC
2115
note: inside `start`
2216
--> $DIR/alloc_error_handler_no_std.rs:LL:CC
2317
|
24-
LL | let _b = Box::new_in(0, BadAlloc);
25-
| ^^^^^^^^^^^^^^^^^^^^^^^^
26-
= note: this error originates in the attribute macro `alloc_error_handler` (in Nightly builds, run with -Z macro-backtrace for more info)
18+
LL | handle_alloc_error(Layout::for_value(&0));
19+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
20+
21+
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
2722

2823
error: aborting due to 1 previous error
2924

+4-20
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,11 @@
1+
//@compile-flags: -Cpanic=abort
12
#![feature(start, core_intrinsics)]
23
#![no_std]
3-
//@compile-flags: -Cpanic=abort
4-
5-
// Plumbing to let us use `writeln!` to host stderr:
6-
7-
extern "Rust" {
8-
fn miri_write_to_stderr(bytes: &[u8]);
9-
}
10-
11-
struct HostErr;
124

135
use core::fmt::Write;
146

15-
impl Write for HostErr {
16-
fn write_str(&mut self, s: &str) -> core::fmt::Result {
17-
unsafe {
18-
miri_write_to_stderr(s.as_bytes());
19-
}
20-
Ok(())
21-
}
22-
}
23-
24-
// Aaaand the test:
7+
#[path = "../../utils/mod.no_std.rs"]
8+
mod utils;
259

2610
#[start]
2711
fn start(_: isize, _: *const *const u8) -> isize {
@@ -30,6 +14,6 @@ fn start(_: isize, _: *const *const u8) -> isize {
3014

3115
#[panic_handler]
3216
fn panic_handler(panic_info: &core::panic::PanicInfo) -> ! {
33-
writeln!(HostErr, "{panic_info}").ok();
17+
writeln!(utils::MiriStderr, "{panic_info}").ok();
3418
core::intrinsics::abort(); //~ ERROR: the program aborted execution
3519
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#![feature(allocator_api, alloc_error_hook)]
2+
3+
use std::alloc::*;
4+
5+
struct Bomb;
6+
impl Drop for Bomb {
7+
fn drop(&mut self) {
8+
eprintln!("yes we are unwinding!");
9+
}
10+
}
11+
12+
#[allow(unreachable_code, unused_variables)]
13+
fn main() {
14+
// This is a particularly tricky hook, since it unwinds, which the default one does not.
15+
set_alloc_error_hook(|_layout| panic!("alloc error hook called"));
16+
17+
let bomb = Bomb;
18+
handle_alloc_error(Layout::for_value(&0));
19+
std::mem::forget(bomb); // defuse unwinding bomb
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
thread 'main' panicked at $DIR/alloc_error_handler_hook.rs:LL:CC:
2+
alloc error hook called
3+
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
4+
yes we are unwinding!

src/tools/miri/tests/panic/alloc_error_handler_panic.rs

+2-16
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,6 @@
22
#![feature(allocator_api)]
33

44
use std::alloc::*;
5-
use std::ptr::NonNull;
6-
7-
struct BadAlloc;
8-
9-
// Create a failing allocator; Miri's native allocator never fails so this is the only way to
10-
// actually call the alloc error handler.
11-
unsafe impl Allocator for BadAlloc {
12-
fn allocate(&self, _l: Layout) -> Result<NonNull<[u8]>, AllocError> {
13-
Err(AllocError)
14-
}
15-
16-
unsafe fn deallocate(&self, _ptr: NonNull<u8>, _layout: Layout) {
17-
unreachable!();
18-
}
19-
}
205

216
struct Bomb;
227
impl Drop for Bomb {
@@ -25,8 +10,9 @@ impl Drop for Bomb {
2510
}
2611
}
2712

13+
#[allow(unreachable_code, unused_variables)]
2814
fn main() {
2915
let bomb = Bomb;
30-
let _b = Box::new_in(0, BadAlloc);
16+
handle_alloc_error(Layout::for_value(&0));
3117
std::mem::forget(bomb); // defuse unwinding bomb
3218
}

src/tools/miri/tests/pass/no_std.rs

+3-19
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,14 @@
22
#![feature(start)]
33
#![no_std]
44

5-
// Plumbing to let us use `writeln!` to host stdout:
6-
7-
extern "Rust" {
8-
fn miri_write_to_stdout(bytes: &[u8]);
9-
}
10-
11-
struct Host;
12-
135
use core::fmt::Write;
146

15-
impl Write for Host {
16-
fn write_str(&mut self, s: &str) -> core::fmt::Result {
17-
unsafe {
18-
miri_write_to_stdout(s.as_bytes());
19-
}
20-
Ok(())
21-
}
22-
}
23-
24-
// Aaaand the test:
7+
#[path = "../utils/mod.no_std.rs"]
8+
mod utils;
259

2610
#[start]
2711
fn start(_: isize, _: *const *const u8) -> isize {
28-
writeln!(Host, "hello, world!").unwrap();
12+
writeln!(utils::MiriStdout, "hello, world!").unwrap();
2913
0
3014
}
3115

src/tools/miri/tests/pass/tree_borrows/reserved.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,8 @@ fn main() {
2727
}
2828
}
2929

30-
unsafe fn print(msg: &str) {
31-
utils::miri_write_to_stderr(msg.as_bytes());
32-
utils::miri_write_to_stderr("\n".as_bytes());
30+
fn print(msg: &str) {
31+
eprintln!("{msg}");
3332
}
3433

3534
unsafe fn read_second<T>(x: &mut T, y: *mut u8) {

src/tools/miri/tests/utils/io.rs

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
use core::fmt::{self, Write};
2+
3+
use super::miri_extern;
4+
5+
pub struct MiriStderr;
6+
7+
impl Write for MiriStderr {
8+
fn write_str(&mut self, s: &str) -> fmt::Result {
9+
unsafe {
10+
miri_extern::miri_write_to_stderr(s.as_bytes());
11+
}
12+
Ok(())
13+
}
14+
}
15+
16+
pub struct MiriStdout;
17+
18+
impl Write for MiriStdout {
19+
fn write_str(&mut self, s: &str) -> fmt::Result {
20+
unsafe {
21+
miri_extern::miri_write_to_stdout(s.as_bytes());
22+
}
23+
Ok(())
24+
}
25+
}

src/tools/miri/tests/utils/miri_extern.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,8 @@ extern "Rust" {
133133
/// with a null terminator.
134134
/// Returns 0 if the `out` buffer was large enough, and the required size otherwise.
135135
pub fn miri_host_to_target_path(
136-
path: *const std::ffi::c_char,
137-
out: *mut std::ffi::c_char,
136+
path: *const core::ffi::c_char,
137+
out: *mut core::ffi::c_char,
138138
out_size: usize,
139139
) -> usize;
140140

0 commit comments

Comments
 (0)