From 83944562476ad783f8cace5d94af3490a41a256b Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 8 Mar 2020 20:00:40 +0100 Subject: [PATCH] properly panic in panic_if_uninhabited and align_offset shims --- src/shims/intrinsics.rs | 4 +- src/shims/mod.rs | 34 +++++------ src/shims/panic.rs | 37 +++++++----- tests/run-pass/panic/catch_panic.rs | 58 +++++++++++++------ tests/run-pass/panic/catch_panic.stderr | 26 +++++---- tests/run-pass/{panic => }/transmute_fat2.rs | 0 .../{panic => }/transmute_fat2.stderr | 0 7 files changed, 97 insertions(+), 62 deletions(-) rename tests/run-pass/{panic => }/transmute_fat2.rs (100%) rename tests/run-pass/{panic => }/transmute_fat2.stderr (100%) diff --git a/src/shims/intrinsics.rs b/src/shims/intrinsics.rs index 9cae97a406..05047a6040 100644 --- a/src/shims/intrinsics.rs +++ b/src/shims/intrinsics.rs @@ -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); } } diff --git a/src/shims/mod.rs b/src/shims/mod.rs index 0437955358..6adf013858 100644 --- a/src/shims/mod.rs +++ b/src/shims/mod.rs @@ -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> { + ret: Option<(PlaceTy<'tcx, Tag>, mir::BasicBlock)>, + unwind: Option, + ) -> 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(()) } } diff --git a/src/shims/panic.rs b/src/shims/panic.rs index 11c5a882be..2968bd9b58 100644 --- a/src/shims/panic.rs +++ b/src/shims/panic.rs @@ -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, + ) -> 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(()) diff --git a/tests/run-pass/panic/catch_panic.rs b/tests/run-pass/panic/catch_panic.rs index 21a6fee7c9..33c2beb682 100644 --- a/tests/run-pass/panic/catch_panic.rs +++ b/tests/run-pass/panic/catch_panic.rs @@ -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)); - - // 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()); @@ -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::() { + let msg = if let Some(s) = res.downcast_ref::() { 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())); } - diff --git a/tests/run-pass/panic/catch_panic.stderr b/tests/run-pass/panic/catch_panic.stderr index 636179beea..4ff22fe217 100644 --- a/tests/run-pass/panic/catch_panic.stderr +++ b/tests/run-pass/panic/catch_panic.stderr @@ -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', $DIR/catch_panic.rs:53:21 +thread 'main' panicked at 'Box', $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 diff --git a/tests/run-pass/panic/transmute_fat2.rs b/tests/run-pass/transmute_fat2.rs similarity index 100% rename from tests/run-pass/panic/transmute_fat2.rs rename to tests/run-pass/transmute_fat2.rs diff --git a/tests/run-pass/panic/transmute_fat2.stderr b/tests/run-pass/transmute_fat2.stderr similarity index 100% rename from tests/run-pass/panic/transmute_fat2.stderr rename to tests/run-pass/transmute_fat2.stderr