aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Gerrand <adg@golang.org>2013-06-05 11:12:46 +1000
committerAndrew Gerrand <adg@golang.org>2013-06-05 11:12:46 +1000
commitd72c550f1c7e13c323f4507b57d741ed00f0b0cd (patch)
treee56bab2a2dd510519c8f301854d522815313dad4
parent371a3ab28a91d142ea4100fe8f86a26fe300e1b7 (diff)
downloadgo-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.go28
-rw-r--r--src/pkg/runtime/mgc0.c8
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);