Skip to content

Commit c3ab74b

Browse files
committed
auto merge of #6227 : graydon/rust/issue-6112-box-annihilator, r=graydon
during task annihilation, since it is easy to tread on freed memory.
2 parents 821979f + c15fa3a commit c3ab74b

File tree

1 file changed

+25
-6
lines changed

1 file changed

+25
-6
lines changed

src/libcore/cleanup.rs

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -126,22 +126,29 @@ struct AnnihilateStats {
126126
n_bytes_freed: uint
127127
}
128128

129-
unsafe fn each_live_alloc(f: &fn(box: *mut BoxRepr, uniq: bool) -> bool) {
129+
unsafe fn each_live_alloc(read_next_before: bool,
130+
f: &fn(box: *mut BoxRepr, uniq: bool) -> bool) {
131+
//! Walks the internal list of allocations
132+
130133
use managed;
131134

132135
let task: *Task = transmute(rustrt::rust_get_task());
133136
let box = (*task).boxed_region.live_allocs;
134137
let mut box: *mut BoxRepr = transmute(copy box);
135138
while box != mut_null() {
136-
let next = transmute(copy (*box).header.next);
139+
let next_before = transmute(copy (*box).header.next);
137140
let uniq =
138141
(*box).header.ref_count == managed::raw::RC_MANAGED_UNIQUE;
139142

140143
if ! f(box, uniq) {
141144
break
142145
}
143146

144-
box = next
147+
if read_next_before {
148+
box = next_before;
149+
} else {
150+
box = transmute(copy (*box).header.next);
151+
}
145152
}
146153
}
147154

@@ -173,7 +180,10 @@ pub unsafe fn annihilate() {
173180
};
174181

175182
// Pass 1: Make all boxes immortal.
176-
for each_live_alloc |box, uniq| {
183+
//
184+
// In this pass, nothing gets freed, so it does not matter whether
185+
// we read the next field before or after the callback.
186+
for each_live_alloc(true) |box, uniq| {
177187
stats.n_total_boxes += 1;
178188
if uniq {
179189
stats.n_unique_boxes += 1;
@@ -183,7 +193,11 @@ pub unsafe fn annihilate() {
183193
}
184194

185195
// Pass 2: Drop all boxes.
186-
for each_live_alloc |box, uniq| {
196+
//
197+
// In this pass, unique-managed boxes may get freed, but not
198+
// managed boxes, so we must read the `next` field *after* the
199+
// callback, as the original value may have been freed.
200+
for each_live_alloc(false) |box, uniq| {
187201
if !uniq {
188202
let tydesc: *TypeDesc = transmute(copy (*box).header.type_desc);
189203
let drop_glue: DropGlue = transmute(((*tydesc).drop_glue, 0));
@@ -192,7 +206,12 @@ pub unsafe fn annihilate() {
192206
}
193207

194208
// Pass 3: Free all boxes.
195-
for each_live_alloc |box, uniq| {
209+
//
210+
// In this pass, managed boxes may get freed (but not
211+
// unique-managed boxes, though I think that none of those are
212+
// left), so we must read the `next` field before, since it will
213+
// not be valid after.
214+
for each_live_alloc(true) |box, uniq| {
196215
if !uniq {
197216
stats.n_bytes_freed +=
198217
(*((*box).header.type_desc)).size

0 commit comments

Comments
 (0)