From 78deacc2ec5ea47404de1f19fa7f47d53b55e1db Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 21 Nov 2020 20:23:00 +0100 Subject: [PATCH] make redundant StorageLive UB --- .../rustc_mir/src/interpret/eval_context.rs | 33 ++++++++----------- compiler/rustc_mir/src/interpret/step.rs | 6 ++-- 2 files changed, 16 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_mir/src/interpret/eval_context.rs b/compiler/rustc_mir/src/interpret/eval_context.rs index d48b7fb3b92..3d955576f0f 100644 --- a/compiler/rustc_mir/src/interpret/eval_context.rs +++ b/compiler/rustc_mir/src/interpret/eval_context.rs @@ -840,36 +840,31 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Ok(()) } - /// Mark a storage as live, killing the previous content and returning it. - /// Remember to deallocate that! - pub fn storage_live( - &mut self, - local: mir::Local, - ) -> InterpResult<'tcx, LocalValue> { + /// Mark a storage as live, killing the previous content. + pub fn storage_live(&mut self, local: mir::Local) -> InterpResult<'tcx> { assert!(local != mir::RETURN_PLACE, "Cannot make return place live"); trace!("{:?} is now live", local); let local_val = LocalValue::Uninitialized; - // StorageLive *always* kills the value that's currently stored. - // However, we do not error if the variable already is live; - // see . - Ok(mem::replace(&mut self.frame_mut().locals[local].value, local_val)) + // StorageLive expects the local to be dead, and marks it live. + let old = mem::replace(&mut self.frame_mut().locals[local].value, local_val); + if !matches!(old, LocalValue::Dead) { + throw_ub_format!("StorageLive on a local that was already live"); + } + Ok(()) } - /// Returns the old value of the local. - /// Remember to deallocate that! - pub fn storage_dead(&mut self, local: mir::Local) -> LocalValue { + pub fn storage_dead(&mut self, local: mir::Local) -> InterpResult<'tcx> { assert!(local != mir::RETURN_PLACE, "Cannot make return place dead"); trace!("{:?} is now dead", local); - mem::replace(&mut self.frame_mut().locals[local].value, LocalValue::Dead) + // It is entirely okay for this local to be already dead (at least that's how we currently generate MIR) + let old = mem::replace(&mut self.frame_mut().locals[local].value, LocalValue::Dead); + self.deallocate_local(old)?; + Ok(()) } - pub(super) fn deallocate_local( - &mut self, - local: LocalValue, - ) -> InterpResult<'tcx> { - // FIXME: should we tell the user that there was a local which was never written to? + fn deallocate_local(&mut self, local: LocalValue) -> InterpResult<'tcx> { if let LocalValue::Live(Operand::Indirect(MemPlace { ptr, .. })) = local { // All locals have a backing allocation, even if the allocation is empty // due to the local having ZST type. diff --git a/compiler/rustc_mir/src/interpret/step.rs b/compiler/rustc_mir/src/interpret/step.rs index 156da84f291..95738db1f55 100644 --- a/compiler/rustc_mir/src/interpret/step.rs +++ b/compiler/rustc_mir/src/interpret/step.rs @@ -95,14 +95,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // Mark locals as alive StorageLive(local) => { - let old_val = self.storage_live(*local)?; - self.deallocate_local(old_val)?; + self.storage_live(*local)?; } // Mark locals as dead StorageDead(local) => { - let old_val = self.storage_dead(*local); - self.deallocate_local(old_val)?; + self.storage_dead(*local)?; } // No dynamic semantics attached to `FakeRead`; MIR