@@ -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