about summary refs log tree commit diff
path: root/src/libcore
diff options
context:
space:
mode:
authorElliott Slaughter <eslaughter@mozilla.com>2012-08-17 16:17:18 -0700
committerElliott Slaughter <eslaughter@mozilla.com>2012-09-07 09:21:21 -0700
commit9ea4afe5daf359c7022057774f943645d0d32043 (patch)
tree2d55895a9f3883fe7d0a63c63ce6196b0d32f531 /src/libcore
parent3f0d207b3251e598488588765b942bf6b7a4f9bb (diff)
gc: Avoid double free when pointer appears on stack multiple times.
Diffstat (limited to 'src/libcore')
-rw-r--r--src/libcore/gc.rs37
1 files changed, 23 insertions, 14 deletions
diff --git a/src/libcore/gc.rs b/src/libcore/gc.rs
index eacd0bc62d3..066fa0b2e43 100644
--- a/src/libcore/gc.rs
+++ b/src/libcore/gc.rs
@@ -1,5 +1,6 @@
 import stackwalk::Word;
 import libc::size_t;
+import send_map::linear::LinearMap;
 
 extern mod rustrt {
     fn rust_annihilate_box(ptr: *Word);
@@ -37,7 +38,7 @@ unsafe fn is_safe_point(pc: *Word) -> Option<SafePoint> {
     return None;
 }
 
-type Visitor = fn(root: **Word, tydesc: *Word);
+type Visitor = fn(root: **Word, tydesc: *Word) -> bool;
 
 unsafe fn walk_safe_point(fp: *Word, sp: SafePoint, visitor: Visitor) {
     let fp_bytes: *u8 = unsafe::reinterpret_cast(&fp);
@@ -69,7 +70,7 @@ unsafe fn walk_safe_point(fp: *Word, sp: SafePoint, visitor: Visitor) {
             } else {
                 ptr::null()
             };
-            visitor(root, tydesc);
+            if !visitor(root, tydesc) { return; }
         }
         sri += 1;
     }
@@ -94,25 +95,25 @@ const need_cleanup:    Memory = exchange_heap | stack;
 
 unsafe fn walk_gc_roots(mem: Memory, visitor: Visitor) {
     let mut last_ret: *Word = ptr::null();
-    do stackwalk::walk_stack |frame| {
+    for stackwalk::walk_stack |frame| {
         unsafe {
             if ptr::is_not_null(last_ret) {
                 let sp = is_safe_point(last_ret);
                 match sp {
                   Some(sp_info) => {
-                    do walk_safe_point(frame.fp, sp_info) |root, tydesc| {
+                    for walk_safe_point(frame.fp, sp_info) |root, tydesc| {
                         if ptr::is_null(tydesc) {
                             // Root is a generic box.
                             let refcount = **root;
                             if mem | task_local_heap != 0 && refcount != -1 {
-                                visitor(root, tydesc);
+                                if !visitor(root, tydesc) { return; }
                             } else if mem | exchange_heap != 0 {
-                                visitor(root, tydesc);
+                                if !visitor(root, tydesc) { return; }
                             }
                         } else {
                             // Root is a non-immediate.
                             if mem | stack != 0 {
-                                visitor(root, tydesc);
+                                if !visitor(root, tydesc) { return; }
                             }
                         }
                     }
@@ -122,34 +123,42 @@ unsafe fn walk_gc_roots(mem: Memory, visitor: Visitor) {
             }
             last_ret = *ptr::offset(frame.fp, 1) as *Word;
         }
-        true
     }
 }
 
 fn gc() {
     unsafe {
-        let mut i = 0;
-        do walk_gc_roots(task_local_heap) |_root, _tydesc| {
+        for walk_gc_roots(task_local_heap) |_root, _tydesc| {
             // FIXME(#2997): Walk roots and mark them.
             io::stdout().write([46]); // .
-            i += 1;
         }
     }
 }
 
+type RootSet = LinearMap<*Word,()>;
+
+fn RootSet() -> RootSet {
+    LinearMap()
+}
+
 // This should only be called from fail, as it will drop the roots
 // which are *live* on the stack, rather than dropping those that are
 // dead.
 fn cleanup_stack_for_failure() {
     unsafe {
-        let mut i = 0;
-        do walk_gc_roots(need_cleanup) |root, tydesc| {
+        let mut roots = ~RootSet();
+        for walk_gc_roots(need_cleanup) |root, tydesc| {
+            // Track roots to avoid double frees.
+            if option::is_some(roots.find(&*root)) {
+                again;
+            }
+            roots.insert(*root, ());
+
             if ptr::is_null(tydesc) {
                 rustrt::rust_annihilate_box(*root);
             } else {
                 rustrt::rust_call_tydesc_glue(*root, tydesc, 3 as size_t);
             }
-            i += 1;
         }
     }
 }