aboutsummaryrefslogtreecommitdiff
path: root/src/pkg/runtime
diff options
context:
space:
mode:
authorDmitriy Vyukov <dvyukov@google.com>2013-05-28 19:17:47 +0400
committerDmitriy Vyukov <dvyukov@google.com>2013-05-28 19:17:47 +0400
commit2f5825d4273d910b0d3c9ee82336cf041e8f02d3 (patch)
treec9111a8d9c0f7af125262d77f1812ae177cf43fe /src/pkg/runtime
parentd8fd8d89ea071c79788b34eddf31858c0e66c19b (diff)
downloadgo-2f5825d4273d910b0d3c9ee82336cf041e8f02d3.tar.xz
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
Diffstat (limited to 'src/pkg/runtime')
-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 1ea3a1482e..11fdb18903 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);