diff options
author | Andrew Gerrand <adg@golang.org> | 2013-06-05 11:12:46 +1000 |
---|---|---|
committer | Andrew Gerrand <adg@golang.org> | 2013-06-05 11:12:46 +1000 |
commit | d72c550f1c7e13c323f4507b57d741ed00f0b0cd (patch) | |
tree | e56bab2a2dd510519c8f301854d522815313dad4 | |
parent | 371a3ab28a91d142ea4100fe8f86a26fe300e1b7 (diff) | |
download | go-d72c550f1c7e13c323f4507b57d741ed00f0b0cd.tar.gz go-d72c550f1c7e13c323f4507b57d741ed00f0b0cd.zip |
[release-branch.go1.1] runtime: fix heap corruption during GC
««« CL 9831043 / e84e7204b01b
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
»»»
R=dvyukov, khr, dave
CC=golang-dev
https://golang.org/cl/10028044
-rw-r--r-- | src/pkg/runtime/gc_test.go | 28 | ||||
-rw-r--r-- | 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 d40dccb788..a3c731ccb0 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 4dfcfca36f..c83f1892c7 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); |