Skip to content

Commit c15fa3a

Browse files
nikomatsakisgraydon
authored andcommitted
Be more careful about the order in which we read the next field
during task annihilation, since it is easy to tread on freed memory.
1 parent 79aeb52 commit c15fa3a

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)