Skip to content

Commit

Permalink
properly panic in panic_if_uninhabited and align_offset shims
Browse files Browse the repository at this point in the history
  • Loading branch information
RalfJung committed Mar 8, 2020
1 parent 96d080a commit 8394456
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 62 deletions.
4 changes: 2 additions & 2 deletions src/shims/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

Expand Down
34 changes: 17 additions & 17 deletions src/shims/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand All @@ -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(())
}
}
37 changes: 23 additions & 14 deletions src/shims/panic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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(())
Expand Down
58 changes: 40 additions & 18 deletions tests/run-pass/panic/catch_panic.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -47,32 +47,49 @@ 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));

// Built-in panics
test(|_old_val| { let _val = [0, 1, 2][4]; loop {} });
test(|_old_val| { let _val = 1/0; loop {} });
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; 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());

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));
Expand All @@ -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()));
}

26 changes: 15 additions & 11 deletions tests/run-pass/panic/catch_panic.stderr
Original file line number Diff line number Diff line change
@@ -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
Expand Down
File renamed without changes.
File renamed without changes.

0 comments on commit 8394456

Please sign in to comment.