1
Fork 0

const-eval: disallow unwinding across functions that !fn_can_unwind()

This commit is contained in:
hyd-dev 2021-05-21 20:40:36 +08:00
parent ed20e1e533
commit 7b3e10c751
No known key found for this signature in database
GPG key ID: 74FA7FD5B8DA14B8
4 changed files with 108 additions and 45 deletions

View file

@ -134,6 +134,15 @@ pub struct FrameInfo<'tcx> {
pub lint_root: Option<hir::HirId>,
}
/// Unwind information.
#[derive(Clone, Copy, Eq, PartialEq, Debug, HashStable)]
pub enum StackPopUnwind {
/// The cleanup block.
Cleanup(Option<mir::BasicBlock>),
/// Unwinding is not allowed (UB).
NotAllowed,
}
#[derive(Clone, Eq, PartialEq, Debug, HashStable)] // Miri debug-prints these
pub enum StackPopCleanup {
/// Jump to the next block in the caller, or cause UB if None (that's a function
@ -141,7 +150,7 @@ pub enum StackPopCleanup {
/// we can validate it at that layout.
/// `ret` stores the block we jump to on a normal return, while `unwind`
/// stores the block used for cleanup during unwinding.
Goto { ret: Option<mir::BasicBlock>, unwind: Option<mir::BasicBlock> },
Goto { ret: Option<mir::BasicBlock>, unwind: StackPopUnwind },
/// Just do nothing: Used by Main and for the `box_alloc` hook in miri.
/// `cleanup` says whether locals are deallocated. Static computation
/// wants them leaked to intern what they need (and just throw away
@ -807,9 +816,29 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// In that case, we return early. We also avoid validation in that case,
// because this is CTFE and the final value will be thoroughly validated anyway.
let (cleanup, next_block) = match frame.return_to_block {
StackPopCleanup::Goto { ret, unwind } => {
(true, Some(if unwinding { unwind } else { ret }))
}
StackPopCleanup::Goto { ret, unwind } => (
true,
Some(if unwinding {
let def_id = frame.body.source.def_id();
match unwind {
StackPopUnwind::Cleanup(unwind)
// `fn_sig()` can't be used on closures, but closures always have
// "rust-call" ABI, which always allows unwinding anyway.
if self.tcx.is_closure(def_id) || self.fn_can_unwind(
self.tcx.codegen_fn_attrs(def_id).flags,
self.tcx.fn_sig(def_id).abi(),
) =>
{
unwind
}
_ => {
throw_ub_format!("unwind past a frame that does not allow unwinding")
}
}
} else {
ret
}),
),
StackPopCleanup::None { cleanup, .. } => (cleanup, None),
};

View file

@ -18,7 +18,9 @@ mod visitor;
pub use rustc_middle::mir::interpret::*; // have all the `interpret` symbols in one place: here
pub use self::eval_context::{Frame, FrameInfo, InterpCx, LocalState, LocalValue, StackPopCleanup};
pub use self::eval_context::{
Frame, FrameInfo, InterpCx, LocalState, LocalValue, StackPopCleanup, StackPopUnwind,
};
pub use self::intern::{intern_const_alloc_recursive, InternKind};
pub use self::machine::{compile_time_machine, AllocMap, Machine, MayLeak, StackPopJump};
pub use self::memory::{AllocCheck, AllocRef, AllocRefMut, FnVal, Memory, MemoryKind};

View file

@ -1,7 +1,8 @@
use std::borrow::Cow;
use std::convert::TryFrom;
use rustc_middle::ty::layout::TyAndLayout;
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc_middle::ty::layout::{self, TyAndLayout};
use rustc_middle::ty::Instance;
use rustc_middle::{
mir,
@ -12,9 +13,19 @@ use rustc_target::spec::abi::Abi;
use super::{
FnVal, ImmTy, InterpCx, InterpResult, MPlaceTy, Machine, OpTy, PlaceTy, StackPopCleanup,
StackPopUnwind,
};
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
pub(super) fn fn_can_unwind(&self, attrs: CodegenFnAttrFlags, abi: Abi) -> bool {
layout::fn_can_unwind(
self.tcx.sess.panic_strategy(),
attrs,
layout::conv_from_spec_abi(*self.tcx, abi),
abi,
)
}
pub(super) fn eval_terminator(
&mut self,
terminator: &mir::Terminator<'tcx>,
@ -58,12 +69,16 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let old_stack = self.frame_idx();
let old_loc = self.frame().loc;
let func = self.eval_operand(func, None)?;
let (fn_val, abi) = match *func.layout.ty.kind() {
let (fn_val, abi, can_unwind) = match *func.layout.ty.kind() {
ty::FnPtr(sig) => {
let caller_abi = sig.abi();
let fn_ptr = self.read_scalar(&func)?.check_init()?;
let fn_val = self.memory.get_fn(fn_ptr)?;
(fn_val, caller_abi)
(
fn_val,
caller_abi,
self.fn_can_unwind(layout::fn_ptr_codegen_fn_attr_flags(), caller_abi),
)
}
ty::FnDef(def_id, substs) => {
let sig = func.layout.ty.fn_sig(*self.tcx);
@ -72,6 +87,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
self.resolve(ty::WithOptConstParam::unknown(def_id), substs)?,
),
sig.abi(),
self.fn_can_unwind(self.tcx.codegen_fn_attrs(def_id).flags, sig.abi()),
)
}
_ => span_bug!(
@ -89,7 +105,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
}
None => None,
};
self.eval_fn_call(fn_val, abi, &args[..], ret, *cleanup)?;
self.eval_fn_call(fn_val, abi, &args[..], ret, *cleanup, can_unwind)?;
// Sanity-check that `eval_fn_call` either pushed a new frame or
// did a jump to another block.
if self.frame_idx() == old_stack && self.frame().loc == old_loc {
@ -220,6 +236,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
args: &[OpTy<'tcx, M::PointerTag>],
ret: Option<(&PlaceTy<'tcx, M::PointerTag>, mir::BasicBlock)>,
unwind: Option<mir::BasicBlock>,
can_unwind: bool,
) -> InterpResult<'tcx> {
trace!("eval_fn_call: {:#?}", fn_val);
@ -287,7 +304,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
instance,
body,
ret.map(|p| p.0),
StackPopCleanup::Goto { ret: ret.map(|p| p.1), unwind },
StackPopCleanup::Goto {
ret: ret.map(|p| p.1),
unwind: if can_unwind {
StackPopUnwind::Cleanup(unwind)
} else {
StackPopUnwind::NotAllowed
},
},
)?;
// If an error is raised here, pop the frame again to get an accurate backtrace.
@ -432,7 +456,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
OpTy::from(ImmTy::from_immediate(receiver_place.ptr.into(), this_receiver_ptr));
trace!("Patched self operand to {:#?}", args[0]);
// recurse with concrete function
self.eval_fn_call(drop_fn, caller_abi, &args, ret, unwind)
self.eval_fn_call(drop_fn, caller_abi, &args, ret, unwind, can_unwind)
}
}
}
@ -472,6 +496,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
&[arg.into()],
Some((&dest.into(), target)),
unwind,
true,
)
}
}