Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

make redundant StorageLive UB #79931

Merged
merged 1 commit into from
Dec 12, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 14 additions & 19 deletions compiler/rustc_mir/src/interpret/eval_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<M::PointerTag>> {
/// 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 <https://github.com/rust-lang/rust/issues/42371>.
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<M::PointerTag> {
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<M::PointerTag>,
) -> 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<M::PointerTag>) -> 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.
Expand Down
6 changes: 2 additions & 4 deletions compiler/rustc_mir/src/interpret/step.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down