properly panic in panic_if_uninhabited and align_offset shims

This commit is contained in:
Ralf Jung 2020-03-08 20:00:40 +01:00
parent 96d080a542
commit 8394456247
7 changed files with 96 additions and 61 deletions

View file

@ -448,8 +448,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
let ty = substs.type_at(0);
let layout = this.layout_of(ty)?;
if layout.abi.is_uninhabited() {
// FIXME: This should throw a panic in the interpreted program instead.
throw_unsup_format!("Trying to instantiate uninhabited type {}", ty)
// Return here because we paniced instead of returning normally from the intrinsic.
return this.start_panic(&format!("Attempted to instantiate uninhabited type {}", ty), unwind);
}
}

View file

@ -24,12 +24,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
// There are some more lang items we want to hook that CTFE does not hook (yet).
if this.tcx.lang_items().align_offset_fn() == Some(instance.def.def_id()) {
let (dest, ret) = ret.unwrap();
let n = this
.align_offset(args[0], args[1])?
.unwrap_or_else(|| this.truncate(u128::MAX, dest.layout));
this.write_scalar(Scalar::from_uint(n, dest.layout.size), dest)?;
this.go_to_block(ret);
this.align_offset(args[0], args[1], ret, unwind)?;
return Ok(None);
}
@ -52,35 +47,40 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
&mut self,
ptr_op: OpTy<'tcx, Tag>,
align_op: OpTy<'tcx, Tag>,
) -> InterpResult<'tcx, Option<u128>> {
ret: Option<(PlaceTy<'tcx, Tag>, mir::BasicBlock)>,
unwind: Option<mir::BasicBlock>,
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
let (dest, ret) = ret.unwrap();
let req_align = this
.force_bits(this.read_scalar(align_op)?.not_undef()?, this.pointer_size())?
as usize;
// FIXME: This should actually panic in the interpreted program
// Stop if the alignment is not a power of two.
if !req_align.is_power_of_two() {
throw_unsup_format!("Required alignment should always be a power of two")
return this.start_panic("align_offset: align is not a power-of-two", unwind);
}
let ptr_scalar = this.read_scalar(ptr_op)?.not_undef()?;
// Default: no result.
let mut result = this.truncate(u128::MAX, dest.layout);
if let Ok(ptr) = this.force_ptr(ptr_scalar) {
// Only do anything if we can identify the allocation this goes to.
let cur_align =
this.memory.get_size_and_align(ptr.alloc_id, AllocCheck::MaybeDead)?.1.bytes()
as usize;
if cur_align >= req_align {
// if the allocation alignment is at least the required alignment we use the
// If the allocation alignment is at least the required alignment we use the
// libcore implementation
return Ok(Some(
(this.force_bits(ptr_scalar, this.pointer_size())? as *const i8)
.align_offset(req_align) as u128,
));
result = (this.force_bits(ptr_scalar, this.pointer_size())? as *const i8).align_offset(req_align) as u128;
}
}
// If the allocation alignment is smaller than then required alignment or the pointer was
// actually an integer, we return `None`
Ok(None)
// Return result, and jump to caller.
this.write_scalar(Scalar::from_uint(result, dest.layout.size), dest)?;
this.go_to_block(ret);
Ok(())
}
}

View file

@ -152,6 +152,28 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
Ok(res)
}
/// Starta a panic in the interpreter with the given message as payload.
fn start_panic(
&mut self,
msg: &str,
unwind: Option<mir::BasicBlock>,
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
// First arg: message.
let msg = this.allocate_str(msg, MiriMemoryKind::Machine.into());
// Call the lang item.
let panic = this.tcx.lang_items().panic_fn().unwrap();
let panic = ty::Instance::mono(this.tcx.tcx, panic);
this.call_function(
panic,
&[msg.to_ref()],
None,
StackPopCleanup::Goto { ret: None, unwind },
)
}
fn assert_panic(
&mut self,
span: Span,
@ -184,20 +206,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
}
_ => {
// Forward everything else to `panic` lang item.
// First arg: Message.
let msg = msg.description();
let msg = this.allocate_str(msg, MiriMemoryKind::Machine.into());
// Call the lang item.
let panic = this.tcx.lang_items().panic_fn().unwrap();
let panic = ty::Instance::mono(this.tcx.tcx, panic);
this.call_function(
panic,
&[msg.to_ref()],
None,
StackPopCleanup::Goto { ret: None, unwind },
)?;
this.start_panic(msg.description(), unwind)?;
}
}
Ok(())

View file

@ -1,5 +1,5 @@
// ignore-windows: Unwind panicking does not currently work on Windows
// normalize-stderr-test "[^ ]*libcore/macros/mod.rs[0-9:]*" -> "$$LOC"
// normalize-stderr-test "[^ ]*libcore/(macros|mem)/mod.rs[0-9:]*" -> "$$LOC"
#![feature(never_type)]
#![allow(unconditional_panic)]
use std::panic::{catch_unwind, AssertUnwindSafe};
@ -47,24 +47,41 @@ fn main() {
}));
// Std panics
test(|_old_val| std::panic!("Hello from panic: std"));
test(|old_val| std::panic!(format!("Hello from panic: {:?}", old_val)));
test(|old_val| std::panic!("Hello from panic: {:?}", old_val));
test(|_old_val| std::panic!(1337));
test(None, |_old_val| std::panic!("Hello from panic: std"));
test(None, |old_val| std::panic!(format!("Hello from panic: {:?}", old_val)));
test(None, |old_val| std::panic!("Hello from panic: {:?}", old_val));
test(None, |_old_val| std::panic!(1337));
// Core panics
test(|_old_val| core::panic!("Hello from panic: core"));
test(|old_val| core::panic!(&format!("Hello from panic: {:?}", old_val)));
test(|old_val| core::panic!("Hello from panic: {:?}", old_val));
test(None, |_old_val| core::panic!("Hello from panic: core"));
test(None, |old_val| core::panic!(&format!("Hello from panic: {:?}", old_val)));
test(None, |old_val| core::panic!("Hello from panic: {:?}", old_val));
// Built-in panics
test(|_old_val| { let _val = [0, 1, 2][4]; loop {} });
test(|_old_val| { let _val = 1/0; loop {} });
// Built-in panics; also make sure the message is right.
test(
Some("index out of bounds: the len is 3 but the index is 4"),
|_old_val| { let _val = [0, 1, 2][4]; loop {} },
);
test(
Some("attempt to divide by zero"),
|_old_val| { let _val = 1/0; loop {} },
);
// libcore panics from shims.
#[allow(deprecated, invalid_value)]
test(
Some("Attempted to instantiate uninhabited type !"),
|_old_val| unsafe { std::mem::uninitialized::<!>() },
);
test(
Some("align_offset: align is not a power-of-two"),
|_old_val| { (0usize as *const u8).align_offset(3); loop {} },
);
// Assertion and debug assertion
test(|_old_val| { assert!(false); loop {} });
test(|_old_val| { debug_assert!(false); loop {} });
test(|_old_val| { unsafe { (1 as *const i32).read() }; loop {} }); // trigger debug-assertion in libstd
test(None, |_old_val| { assert!(false); loop {} });
test(None, |_old_val| { debug_assert!(false); loop {} });
test(None, |_old_val| { unsafe { (1 as *const i32).read() }; loop {} }); // trigger debug-assertion in libstd
// Cleanup: reset to default hook.
drop(std::panic::take_hook());
@ -72,7 +89,7 @@ fn main() {
eprintln!("Success!"); // Make sure we get this in stderr
}
fn test(do_panic: impl FnOnce(usize) -> !) {
fn test(expect_msg: Option<&str>, do_panic: impl FnOnce(usize) -> !) {
// Reset test flags.
DROPPED.with(|c| c.set(false));
HOOK_CALLED.with(|c| c.set(false));
@ -84,16 +101,21 @@ fn test(do_panic: impl FnOnce(usize) -> !) {
})).expect_err("do_panic() did not panic!");
// See if we can extract the panic message.
if let Some(s) = res.downcast_ref::<String>() {
let msg = if let Some(s) = res.downcast_ref::<String>() {
eprintln!("Caught panic message (String): {}", s);
Some(s.as_str())
} else if let Some(s) = res.downcast_ref::<&str>() {
eprintln!("Caught panic message (&str): {}", s);
Some(*s)
} else {
eprintln!("Failed get caught panic message.");
None
};
if let Some(expect_msg) = expect_msg {
assert_eq!(expect_msg, msg.unwrap());
}
// Test flags.
assert!(DROPPED.with(|c| c.get()));
assert!(HOOK_CALLED.with(|c| c.get()));
}

View file

@ -1,24 +1,28 @@
thread 'main' panicked at 'Hello from panic: std', $DIR/catch_panic.rs:50:21
thread 'main' panicked at 'Hello from panic: std', $DIR/catch_panic.rs:50:27
Caught panic message (&str): Hello from panic: std
thread 'main' panicked at 'Hello from panic: 1', $DIR/catch_panic.rs:51:20
thread 'main' panicked at 'Hello from panic: 1', $DIR/catch_panic.rs:51:26
Caught panic message (String): Hello from panic: 1
thread 'main' panicked at 'Hello from panic: 2', $DIR/catch_panic.rs:52:20
thread 'main' panicked at 'Hello from panic: 2', $DIR/catch_panic.rs:52:26
Caught panic message (String): Hello from panic: 2
thread 'main' panicked at 'Box<Any>', $DIR/catch_panic.rs:53:21
thread 'main' panicked at 'Box<Any>', $DIR/catch_panic.rs:53:27
Failed get caught panic message.
thread 'main' panicked at 'Hello from panic: core', $DIR/catch_panic.rs:56:21
thread 'main' panicked at 'Hello from panic: core', $DIR/catch_panic.rs:56:27
Caught panic message (String): Hello from panic: core
thread 'main' panicked at 'Hello from panic: 5', $DIR/catch_panic.rs:57:20
thread 'main' panicked at 'Hello from panic: 5', $DIR/catch_panic.rs:57:26
Caught panic message (String): Hello from panic: 5
thread 'main' panicked at 'Hello from panic: 6', $DIR/catch_panic.rs:58:20
thread 'main' panicked at 'Hello from panic: 6', $DIR/catch_panic.rs:58:26
Caught panic message (String): Hello from panic: 6
thread 'main' panicked at 'index out of bounds: the len is 3 but the index is 4', $DIR/catch_panic.rs:61:34
thread 'main' panicked at 'index out of bounds: the len is 3 but the index is 4', $DIR/catch_panic.rs:63:33
Caught panic message (String): index out of bounds: the len is 3 but the index is 4
thread 'main' panicked at 'attempt to divide by zero', $DIR/catch_panic.rs:62:34
thread 'main' panicked at 'attempt to divide by zero', $DIR/catch_panic.rs:67:33
Caught panic message (String): attempt to divide by zero
thread 'main' panicked at 'assertion failed: false', $DIR/catch_panic.rs:65:23
thread 'main' panicked at 'Attempted to instantiate uninhabited type !', $LOC
Caught panic message (String): Attempted to instantiate uninhabited type !
thread 'main' panicked at 'align_offset: align is not a power-of-two', $LOC
Caught panic message (String): align_offset: align is not a power-of-two
thread 'main' panicked at 'assertion failed: false', $DIR/catch_panic.rs:82:29
Caught panic message (&str): assertion failed: false
thread 'main' panicked at 'assertion failed: false', $DIR/catch_panic.rs:66:23
thread 'main' panicked at 'assertion failed: false', $DIR/catch_panic.rs:83:29
Caught panic message (&str): assertion failed: false
thread 'main' panicked at 'attempt to copy from unaligned or null pointer', $LOC
Caught panic message (String): attempt to copy from unaligned or null pointer