1
0
mirror of https://github.com/golang/go synced 2024-11-22 14:15:05 -07:00

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
This commit is contained in:
Dmitriy Vyukov 2013-05-28 19:17:47 +04:00
parent d8fd8d89ea
commit 2f5825d427
2 changed files with 32 additions and 4 deletions

View 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")
}
}
}

View File

@ -623,7 +623,7 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking)
byte *b, *arena_start, *arena_used; byte *b, *arena_start, *arena_used;
uintptr n, i, end_b, elemsize, size, ti, objti, count, type; uintptr n, i, end_b, elemsize, size, ti, objti, count, type;
uintptr *pc, precise_type, nominal_size; 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; void *obj;
Type *t; Type *t;
Slice *sliceptr; Slice *sliceptr;
@ -1062,13 +1062,13 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking)
if(!(chantype->elem->kind & KindNoPointers)) { if(!(chantype->elem->kind & KindNoPointers)) {
// Channel's buffer follows Hchan immediately in memory. // Channel's buffer follows Hchan immediately in memory.
// Size of buffer (cap(c)) is second int in the chan struct. // Size of buffer (cap(c)) is second int in the chan struct.
n = ((uintgo*)chan)[1]; chancap = ((uintgo*)chan)[1];
if(n > 0) { if(chancap > 0) {
// TODO(atom): split into two chunks so that only the // TODO(atom): split into two chunks so that only the
// in-use part of the circular buffer is scanned. // in-use part of the circular buffer is scanned.
// (Channel routines zero the unused part, so the current // (Channel routines zero the unused part, so the current
// code does not lead to leaks, it's just a little inefficient.) // 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}; (uintptr)chantype->elem->gc | PRECISE | LOOP};
if(objbufpos == objbuf_end) if(objbufpos == objbuf_end)
flushobjbuf(objbuf, &objbufpos, &wp, &wbuf, &nobj); flushobjbuf(objbuf, &objbufpos, &wp, &wbuf, &nobj);