]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: fix heap corruption during GC
authorDmitriy Vyukov <dvyukov@google.com>
Tue, 28 May 2013 15:17:47 +0000 (19:17 +0400)
committerDmitriy Vyukov <dvyukov@google.com>
Tue, 28 May 2013 15:17:47 +0000 (19:17 +0400)
The 'n' variable is used during rescan initiation in GC_END case,
but it's overwritten with chan capacity in GC_CHAN case.
As the result rescan is done with the wrong object size.
Fixes #5554.

R=golang-dev, khr
CC=golang-dev
https://golang.org/cl/9831043

src/pkg/runtime/gc_test.go
src/pkg/runtime/mgc0.c

index d40dccb788861b19917631688cfa03b24d5aadc3..a3c731ccb0521b6d9c6297ebdeff8ced7bf89158 100644 (file)
@@ -121,3 +121,31 @@ func TestGcArraySlice(t *testing.T) {
                }
        }
 }
+
+func TestGcRescan(t *testing.T) {
+       type X struct {
+               c     chan error
+               nextx *X
+       }
+       type Y struct {
+               X
+               nexty *Y
+               p     *int
+       }
+       var head *Y
+       for i := 0; i < 10; i++ {
+               p := &Y{}
+               p.c = make(chan error)
+               p.nextx = &head.X
+               p.nexty = head
+               p.p = new(int)
+               *p.p = 42
+               head = p
+               runtime.GC()
+       }
+       for p := head; p != nil; p = p.nexty {
+               if *p.p != 42 {
+                       t.Fatal("corrupted heap")
+               }
+       }
+}
index 1ea3a1482eb2ec6c0d6ed7878a753e72f742894a..11fdb1890333a6610fbb43cfe33aa962eaa8b990 100644 (file)
@@ -623,7 +623,7 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking)
        byte *b, *arena_start, *arena_used;
        uintptr n, i, end_b, elemsize, size, ti, objti, count, type;
        uintptr *pc, precise_type, nominal_size;
-       uintptr *map_ret, mapkey_size, mapval_size, mapkey_ti, mapval_ti, *chan_ret;
+       uintptr *map_ret, mapkey_size, mapval_size, mapkey_ti, mapval_ti, *chan_ret, chancap;
        void *obj;
        Type *t;
        Slice *sliceptr;
@@ -1062,13 +1062,13 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking)
                        if(!(chantype->elem->kind & KindNoPointers)) {
                                // Channel's buffer follows Hchan immediately in memory.
                                // Size of buffer (cap(c)) is second int in the chan struct.
-                               n = ((uintgo*)chan)[1];
-                               if(n > 0) {
+                               chancap = ((uintgo*)chan)[1];
+                               if(chancap > 0) {
                                        // TODO(atom): split into two chunks so that only the
                                        // in-use part of the circular buffer is scanned.
                                        // (Channel routines zero the unused part, so the current
                                        // code does not lead to leaks, it's just a little inefficient.)
-                                       *objbufpos++ = (Obj){(byte*)chan+runtime·Hchansize, n*chantype->elem->size,
+                                       *objbufpos++ = (Obj){(byte*)chan+runtime·Hchansize, chancap*chantype->elem->size,
                                                (uintptr)chantype->elem->gc | PRECISE | LOOP};
                                        if(objbufpos == objbuf_end)
                                                flushobjbuf(objbuf, &objbufpos, &wp, &wbuf, &nobj);