Repository navigation
Drop and leaking &mut references #31567
Copy link
Copy link
Closed
Labels
A-NLLArea: Non-lexical lifetimes (NLL)Area: Non-lexical lifetimes (NLL)C-bugCategory: This is a bug.Category: This is a bug.I-unsoundIssue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/SoundnessIssue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/SoundnessP-mediumMedium priorityMedium priorityT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.Relevant to the compiler team, which will review and decide on the PR/issue.fixed-by-NLLBugs fixed, but only when NLL is enabled.Bugs fixed, but only when NLL is enabled.
Milestone
Description
There's a way to leak a &mut member borrowed from a struct, and then manipulate that member from the Drop handler -- even while it is immutably borrowed!
struct VecWrapper<'a>(&'a mut Vec<u8>);
impl<'a> IntoIterator for VecWrapper<'a> {
type Item = &'a u8;
type IntoIter = std::slice::Iter<'a, u8>;
fn into_iter(self) -> Self::IntoIter {
self.0.iter()
}
}
impl<'a> Drop for VecWrapper<'a> {
fn drop(&mut self) {
// aggressively free memory
self.0.clear();
self.0.shrink_to_fit();
}
}
fn main() {
let mut v = vec![1, 2, 3];
for i in VecWrapper(&mut v) {
let s = "foo".to_owned(); // reused allocation
println!("{}! {} {:#x} '{}'", s, i, i, *i as char);
}
}playground output:
foo! 102 0x66 'f'
foo! 111 0x6f 'o'
foo! 111 0x6f 'o'
So this drop frees the vec's buffer, then an unrelated string allocation reuses the same memory, and those new values come out of the vec::Iter.
I think if the member was immutable &, then this "leak" from into_iter() would be fine since they can both share that reference, and Drop can't mutate it. But &mut must be exclusive, of course...
Metadata
Metadata
Assignees
Labels
A-NLLArea: Non-lexical lifetimes (NLL)Area: Non-lexical lifetimes (NLL)C-bugCategory: This is a bug.Category: This is a bug.I-unsoundIssue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/SoundnessIssue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/SoundnessP-mediumMedium priorityMedium priorityT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.Relevant to the compiler team, which will review and decide on the PR/issue.fixed-by-NLLBugs fixed, but only when NLL is enabled.Bugs fixed, but only when NLL is enabled.
Activity
nagisa commentedon Feb 11, 2016
The behaviour feels somewhat intuitive to me, but is certainly a memory safety issue. Nominating.
cc @rust-lang/lang, I think?
EDIT: affects MIR as well, without an obvious fix.
EDIT2: Also @rust-lang/libs, because this is caused by
IntoIter::into_iter.arielb1 commentedon Feb 11, 2016
Minified:
MIR borrowck would of course catch
get_danglingborrowing a pointer and dropping its owner, given that the relevant MIR isbluss commentedon Feb 11, 2016
Here's another minification (playground)
arielb1 commentedon Feb 11, 2016
I suppose we can leave this to fix-by-MIR-borrowck.
nikomatsakis commentedon Feb 11, 2016
The problem (I think) is the special case rule in the borrow checker concerning
&mutborrows whose owners go out of scope. That always seemed a bit suspicious to me. It does not consider destructors. Shouldn't be too hard to fix.nikomatsakis commentedon Feb 11, 2016
triage: P-high
pnkfelix commentedon Apr 25, 2016
so, just a quick note on some ways @nikomatsakis and I have talked about resolving this:
ExprUseVisitororborrowckto inject the effect of mutating any local binding of type carrying a destructor at the end of its scope.dropcalls that we already explicit emit in the MIR.dropcall -- see further discussion below)borrowckthat already ensures, for any borrow, that it does not outlive the lifetime of the so-called "guarantor" for the borrow.cell::RefMut::mapRefMuthas an attached destructor (since itsborrowfield implementsDrop), and theorig.valueis going to extend the lifetime of the borrowed value past the lifetime oforigitself. So check reasons that the destructor for theRefMutmight mutateorig.value, making the alias illegal.RefMutitself does not carry animpl Drop. It is non-Copy, sure, but the only thing that its destructor can do is recursively invoke the destructor for each of its fields. And that means that the only actual code that runs is on theorig.borrowfield -- it has no access to theorig.valuefield.dropck.Dropversus a type who merely carries a field that implementsDrop.cell::RefMutto go through, and thus reduce the breakage injected by the proposed change.50 remaining items
Auto merge of #52782 - pnkfelix:issue-45696-dangly-paths-for-box, r=e…
matthewjasper commentedon Sep 5, 2019
Reopening for visibility (other fixed-by-NLL bugs are kept open)