From 2f5825d4273d910b0d3c9ee82336cf041e8f02d3 Mon Sep 17 00:00:00 2001 From: Dmitriy Vyukov Date: Tue, 28 May 2013 19:17:47 +0400 Subject: [PATCH] runtime: fix heap corruption during GC 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 | 28 ++++++++++++++++++++++++++++ src/pkg/runtime/mgc0.c | 8 ++++---- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/pkg/runtime/gc_test.go b/src/pkg/runtime/gc_test.go index d40dccb7888..a3c731ccb05 100644 --- a/src/pkg/runtime/gc_test.go +++ b/src/pkg/runtime/gc_test.go @@ -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") + } + } +} diff --git a/src/pkg/runtime/mgc0.c b/src/pkg/runtime/mgc0.c index 1ea3a1482eb..11fdb189033 100644 --- a/src/pkg/runtime/mgc0.c +++ b/src/pkg/runtime/mgc0.c @@ -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);