miri engine: also check return type before calling function
This commit is contained in:
parent
d2b9b1de05
commit
616cb6356f
@ -560,6 +560,10 @@ for ::mir::interpret::EvalErrorKind<'gcx, O> {
|
|||||||
a.hash_stable(hcx, hasher);
|
a.hash_stable(hcx, hasher);
|
||||||
b.hash_stable(hcx, hasher)
|
b.hash_stable(hcx, hasher)
|
||||||
},
|
},
|
||||||
|
FunctionRetMismatch(a, b) => {
|
||||||
|
a.hash_stable(hcx, hasher);
|
||||||
|
b.hash_stable(hcx, hasher)
|
||||||
|
},
|
||||||
NoMirFor(ref s) => s.hash_stable(hcx, hasher),
|
NoMirFor(ref s) => s.hash_stable(hcx, hasher),
|
||||||
UnterminatedCString(ptr) => ptr.hash_stable(hcx, hasher),
|
UnterminatedCString(ptr) => ptr.hash_stable(hcx, hasher),
|
||||||
PointerOutOfBounds {
|
PointerOutOfBounds {
|
||||||
|
@ -186,6 +186,7 @@ pub enum EvalErrorKind<'tcx, O> {
|
|||||||
|
|
||||||
FunctionAbiMismatch(Abi, Abi),
|
FunctionAbiMismatch(Abi, Abi),
|
||||||
FunctionArgMismatch(Ty<'tcx>, Ty<'tcx>),
|
FunctionArgMismatch(Ty<'tcx>, Ty<'tcx>),
|
||||||
|
FunctionRetMismatch(Ty<'tcx>, Ty<'tcx>),
|
||||||
FunctionArgCountMismatch,
|
FunctionArgCountMismatch,
|
||||||
NoMirFor(String),
|
NoMirFor(String),
|
||||||
UnterminatedCString(Pointer),
|
UnterminatedCString(Pointer),
|
||||||
@ -294,7 +295,8 @@ impl<'tcx, O> EvalErrorKind<'tcx, O> {
|
|||||||
use self::EvalErrorKind::*;
|
use self::EvalErrorKind::*;
|
||||||
match *self {
|
match *self {
|
||||||
MachineError(ref inner) => inner,
|
MachineError(ref inner) => inner,
|
||||||
FunctionAbiMismatch(..) | FunctionArgMismatch(..) | FunctionArgCountMismatch =>
|
FunctionAbiMismatch(..) | FunctionArgMismatch(..) | FunctionRetMismatch(..)
|
||||||
|
| FunctionArgCountMismatch =>
|
||||||
"tried to call a function through a function pointer of incompatible type",
|
"tried to call a function through a function pointer of incompatible type",
|
||||||
InvalidMemoryAccess =>
|
InvalidMemoryAccess =>
|
||||||
"tried to access memory through an invalid pointer",
|
"tried to access memory through an invalid pointer",
|
||||||
@ -470,6 +472,10 @@ impl<'tcx, O: fmt::Debug> fmt::Debug for EvalErrorKind<'tcx, O> {
|
|||||||
write!(f, "tried to call a function with argument of type {:?} \
|
write!(f, "tried to call a function with argument of type {:?} \
|
||||||
passing data of type {:?}",
|
passing data of type {:?}",
|
||||||
callee_ty, caller_ty),
|
callee_ty, caller_ty),
|
||||||
|
FunctionRetMismatch(caller_ty, callee_ty) =>
|
||||||
|
write!(f, "tried to call a function with return type {:?} \
|
||||||
|
passing return place of type {:?}",
|
||||||
|
callee_ty, caller_ty),
|
||||||
FunctionArgCountMismatch =>
|
FunctionArgCountMismatch =>
|
||||||
write!(f, "tried to call a function with incorrect number of arguments"),
|
write!(f, "tried to call a function with incorrect number of arguments"),
|
||||||
BoundsCheck { ref len, ref index } =>
|
BoundsCheck { ref len, ref index } =>
|
||||||
|
@ -492,6 +492,10 @@ impl<'a, 'tcx, O: Lift<'tcx>> Lift<'tcx> for interpret::EvalErrorKind<'a, O> {
|
|||||||
tcx.lift(&a)?,
|
tcx.lift(&a)?,
|
||||||
tcx.lift(&b)?,
|
tcx.lift(&b)?,
|
||||||
),
|
),
|
||||||
|
FunctionRetMismatch(a, b) => FunctionRetMismatch(
|
||||||
|
tcx.lift(&a)?,
|
||||||
|
tcx.lift(&b)?,
|
||||||
|
),
|
||||||
FunctionArgCountMismatch => FunctionArgCountMismatch,
|
FunctionArgCountMismatch => FunctionArgCountMismatch,
|
||||||
NoMirFor(ref s) => NoMirFor(s.clone()),
|
NoMirFor(ref s) => NoMirFor(s.clone()),
|
||||||
UnterminatedCString(ptr) => UnterminatedCString(ptr),
|
UnterminatedCString(ptr) => UnterminatedCString(ptr),
|
||||||
|
@ -231,6 +231,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tc
|
|||||||
/// Mark a storage as live, killing the previous content and returning it.
|
/// Mark a storage as live, killing the previous content and returning it.
|
||||||
/// Remember to deallocate that!
|
/// Remember to deallocate that!
|
||||||
pub fn storage_live(&mut self, local: mir::Local) -> EvalResult<'tcx, LocalValue> {
|
pub fn storage_live(&mut self, local: mir::Local) -> EvalResult<'tcx, LocalValue> {
|
||||||
|
assert!(local != mir::RETURN_PLACE, "Cannot make return place live");
|
||||||
trace!("{:?} is now live", local);
|
trace!("{:?} is now live", local);
|
||||||
|
|
||||||
let layout = self.layout_of_local(self.cur_frame(), local)?;
|
let layout = self.layout_of_local(self.cur_frame(), local)?;
|
||||||
@ -242,6 +243,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tc
|
|||||||
/// Returns the old value of the local.
|
/// Returns the old value of the local.
|
||||||
/// Remember to deallocate that!
|
/// Remember to deallocate that!
|
||||||
pub fn storage_dead(&mut self, local: mir::Local) -> LocalValue {
|
pub fn storage_dead(&mut self, local: mir::Local) -> LocalValue {
|
||||||
|
assert!(local != mir::RETURN_PLACE, "Cannot make return place dead");
|
||||||
trace!("{:?} is now dead", local);
|
trace!("{:?} is now dead", local);
|
||||||
|
|
||||||
mem::replace(&mut self.frame_mut().locals[local], LocalValue::Dead)
|
mem::replace(&mut self.frame_mut().locals[local], LocalValue::Dead)
|
||||||
@ -446,6 +448,9 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tc
|
|||||||
let dummy =
|
let dummy =
|
||||||
LocalValue::Live(Operand::Immediate(Value::Scalar(ScalarMaybeUndef::Undef)));
|
LocalValue::Live(Operand::Immediate(Value::Scalar(ScalarMaybeUndef::Undef)));
|
||||||
let mut locals = IndexVec::from_elem(dummy, &mir.local_decls);
|
let mut locals = IndexVec::from_elem(dummy, &mir.local_decls);
|
||||||
|
// Return place is handled specially by the `eval_place` functions, and the
|
||||||
|
// entry in `locals` should never be used. Make it dead, to be sure.
|
||||||
|
locals[mir::RETURN_PLACE] = LocalValue::Dead;
|
||||||
// Now mark those locals as dead that we do not want to initialize
|
// Now mark those locals as dead that we do not want to initialize
|
||||||
match self.tcx.describe_def(instance.def_id()) {
|
match self.tcx.describe_def(instance.def_id()) {
|
||||||
// statics and constants don't have `Storage*` statements, no need to look for them
|
// statics and constants don't have `Storage*` statements, no need to look for them
|
||||||
|
@ -287,7 +287,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
|
|||||||
|
|
||||||
let return_place = match dest {
|
let return_place = match dest {
|
||||||
Some(place) => *place,
|
Some(place) => *place,
|
||||||
None => Place::null(&self),
|
None => Place::null(&self), // any access will error. good!
|
||||||
};
|
};
|
||||||
self.push_stack_frame(
|
self.push_stack_frame(
|
||||||
instance,
|
instance,
|
||||||
@ -373,6 +373,20 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
|
|||||||
trace!("Caller has too many args over");
|
trace!("Caller has too many args over");
|
||||||
return err!(FunctionArgCountMismatch);
|
return err!(FunctionArgCountMismatch);
|
||||||
}
|
}
|
||||||
|
// Don't forget to check the return type!
|
||||||
|
if let Some(caller_ret) = dest {
|
||||||
|
let callee_ret = self.eval_place(&mir::Place::Local(mir::RETURN_PLACE))?;
|
||||||
|
if !Self::check_argument_compat(caller_ret.layout, callee_ret.layout) {
|
||||||
|
return err!(FunctionRetMismatch(
|
||||||
|
caller_ret.layout.ty, callee_ret.layout.ty
|
||||||
|
));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// FIXME: The caller thinks this function cannot return. How do
|
||||||
|
// we verify that the callee agrees?
|
||||||
|
// On the plus side, the the callee every writes to its return place,
|
||||||
|
// that will be detected as UB (because we set that to NULL above).
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
})();
|
})();
|
||||||
match res {
|
match res {
|
||||||
|
@ -154,6 +154,7 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> {
|
|||||||
// FIXME: figure out the rules and start linting
|
// FIXME: figure out the rules and start linting
|
||||||
| FunctionAbiMismatch(..)
|
| FunctionAbiMismatch(..)
|
||||||
| FunctionArgMismatch(..)
|
| FunctionArgMismatch(..)
|
||||||
|
| FunctionRetMismatch(..)
|
||||||
| FunctionArgCountMismatch
|
| FunctionArgCountMismatch
|
||||||
// fine at runtime, might be a register address or sth
|
// fine at runtime, might be a register address or sth
|
||||||
| ReadBytesAsPointer
|
| ReadBytesAsPointer
|
||||||
|
Loading…
Reference in New Issue
Block a user