Skip to content

Commit 02a9d1c

Browse files
committed
correctly drop Array::IntoIter
If you called Array::into_iter() you could leak elements in two circumstances: 1. If you did not iterate through all elements before dropping, since they had been converted to `ManuallyDrop` 2. If the Array had multiple references when the iterator was created, but the other references were dropped before the iterator was dropped, then the existence of the iterator would prevent those Rcs from dropping their contents, but then because the iterator is of type ManuallyDrop, it wouldn't drop those elements either
1 parent 4f84cf0 commit 02a9d1c

File tree

1 file changed

+53
-20
lines changed

1 file changed

+53
-20
lines changed

core/src/term/array.rs

Lines changed: 53 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -157,29 +157,50 @@ impl IntoIterator for Array {
157157
// to the inner slice, then we can:
158158
// - Drop the elements outside our inner view,
159159
// - Move out the elements inside out inner view.
160+
// - Drop the rest of the elements when we're dropped
160161
// Otherwise, we clone everything.
161-
162-
unsafe {
163-
let mut inner: Rc<[ManuallyDrop<RichTerm>]> = transmute(self.inner);
164-
let idx = self.start;
165-
let end = self.end;
166-
167-
if let Some(slice) = Rc::get_mut(&mut inner) {
168-
for term in &mut slice[..idx] {
162+
//
163+
// If we start as a shared reference, we could become the only reference
164+
// later, but we clone everything anyways, so we don't end up in an
165+
// invalid in-between state where some terms have been freed manually
166+
// and others haven't.
167+
168+
let inner = if Rc::strong_count(&self.inner) != 1 || Rc::weak_count(&self.inner) != 0 {
169+
IntoIterInner::Shared(self.inner)
170+
} else {
171+
unsafe {
172+
let mut inner =
173+
transmute::<Rc<[RichTerm]>, Rc<[ManuallyDrop<RichTerm>]>>(self.inner);
174+
let slice =
175+
Rc::get_mut(&mut inner).expect("non-unique Rc after checking for uniqueness");
176+
for term in &mut slice[..self.start] {
169177
ManuallyDrop::drop(term)
170178
}
171-
for term in &mut slice[end..] {
179+
for term in &mut slice[self.end..] {
172180
ManuallyDrop::drop(term)
173181
}
174-
}
175182

176-
IntoIter { inner, idx, end }
183+
IntoIterInner::Owned(inner)
184+
}
185+
};
186+
IntoIter {
187+
inner,
188+
idx: self.start,
189+
end: self.end,
177190
}
178191
}
179192
}
180193

194+
enum IntoIterInner {
195+
Shared(Rc<[RichTerm]>),
196+
// This shouldn't really be an Rc. It should only ever have one reference.
197+
// There may be a way to rewrite this once unsized locals are stabilized.
198+
// But for now, there is no good way to repackage the inner array, so we
199+
// keep it in an Rc.
200+
Owned(Rc<[ManuallyDrop<RichTerm>]>),
201+
}
181202
pub struct IntoIter {
182-
inner: Rc<[ManuallyDrop<RichTerm>]>,
203+
inner: IntoIterInner,
183204
idx: usize,
184205
end: usize,
185206
}
@@ -191,14 +212,14 @@ impl Iterator for IntoIter {
191212
if self.idx == self.end {
192213
None
193214
} else {
194-
let term = match Rc::get_mut(&mut self.inner) {
195-
None => self.inner[..self.end]
196-
.get(self.idx)
197-
.cloned()
198-
.map(ManuallyDrop::into_inner),
199-
Some(slice) => slice[..self.end]
200-
.get_mut(self.idx)
201-
.map(|t| unsafe { ManuallyDrop::take(t) }),
215+
let term = match &mut self.inner {
216+
IntoIterInner::Shared(inner) => inner.get(self.idx).cloned(),
217+
IntoIterInner::Owned(inner) => unsafe {
218+
Rc::get_mut(inner)
219+
.expect("non-unique Rc after checking for uniqueness")
220+
.get_mut(self.idx)
221+
.map(|t| ManuallyDrop::take(t))
222+
},
202223
};
203224

204225
self.idx += 1;
@@ -207,6 +228,18 @@ impl Iterator for IntoIter {
207228
}
208229
}
209230

231+
impl Drop for IntoIter {
232+
fn drop(&mut self) {
233+
// drop the rest of the items we haven't iterated through
234+
if let IntoIterInner::Owned(inner) = &mut self.inner {
235+
let inner = Rc::get_mut(inner).expect("non-unique Rc after checking for uniqueness");
236+
for term in &mut inner[self.idx..self.end] {
237+
unsafe { ManuallyDrop::drop(term) }
238+
}
239+
}
240+
}
241+
}
242+
210243
impl ExactSizeIterator for IntoIter {
211244
fn len(&self) -> usize {
212245
self.end - self.idx

0 commit comments

Comments
 (0)