[dev.garbage] runtime: make sure G.param and SudoG.elem do not hold stale pointers

In old conservative Go, this could cause memory leaks.
A new pickier collector might reasonably crash when it saw one of these.

LGTM=rlh
R=rlh
CC=golang-codereviews
https://golang.org/cl/147480043
This commit is contained in:
Russ Cox 2014-10-02 16:49:11 -04:00
parent fdb0cc6e7b
commit a3630c9e44
4 changed files with 30 additions and 2 deletions

View file

@ -140,10 +140,11 @@ func chansend(t *chantype, c *hchan, ep unsafe.Pointer, block bool, callerpc uin
unlock(&c.lock) unlock(&c.lock)
recvg := sg.g recvg := sg.g
recvg.param = unsafe.Pointer(sg)
if sg.elem != nil { if sg.elem != nil {
memmove(unsafe.Pointer(sg.elem), ep, uintptr(c.elemsize)) memmove(unsafe.Pointer(sg.elem), ep, uintptr(c.elemsize))
sg.elem = nil
} }
recvg.param = unsafe.Pointer(sg)
if sg.releasetime != 0 { if sg.releasetime != 0 {
sg.releasetime = cputicks() sg.releasetime = cputicks()
} }
@ -179,6 +180,7 @@ func chansend(t *chantype, c *hchan, ep unsafe.Pointer, block bool, callerpc uin
} }
panic("send on closed channel") panic("send on closed channel")
} }
gp.param = nil
if mysg.releasetime > 0 { if mysg.releasetime > 0 {
blockevent(int64(mysg.releasetime)-t0, 2) blockevent(int64(mysg.releasetime)-t0, 2)
} }
@ -278,6 +280,7 @@ func closechan(c *hchan) {
break break
} }
gp := sg.g gp := sg.g
sg.elem = nil
gp.param = nil gp.param = nil
if sg.releasetime != 0 { if sg.releasetime != 0 {
sg.releasetime = cputicks() sg.releasetime = cputicks()
@ -292,6 +295,7 @@ func closechan(c *hchan) {
break break
} }
gp := sg.g gp := sg.g
sg.elem = nil
gp.param = nil gp.param = nil
if sg.releasetime != 0 { if sg.releasetime != 0 {
sg.releasetime = cputicks() sg.releasetime = cputicks()
@ -372,6 +376,7 @@ func chanrecv(t *chantype, c *hchan, ep unsafe.Pointer, block bool) (selected, r
if ep != nil { if ep != nil {
memmove(ep, sg.elem, uintptr(c.elemsize)) memmove(ep, sg.elem, uintptr(c.elemsize))
} }
sg.elem = nil
gp := sg.g gp := sg.g
gp.param = unsafe.Pointer(sg) gp.param = unsafe.Pointer(sg)
if sg.releasetime != 0 { if sg.releasetime != 0 {
@ -409,9 +414,11 @@ func chanrecv(t *chantype, c *hchan, ep unsafe.Pointer, block bool) (selected, r
if mysg.releasetime > 0 { if mysg.releasetime > 0 {
blockevent(mysg.releasetime-t0, 2) blockevent(mysg.releasetime-t0, 2)
} }
haveData := gp.param != nil
gp.param = nil
releaseSudog(mysg) releaseSudog(mysg)
if gp.param != nil { if haveData {
// a sender sent us some data. It already wrote to ep. // a sender sent us some data. It already wrote to ep.
selected = true selected = true
received = true received = true

View file

@ -148,6 +148,9 @@ func acquireSudog() *sudog {
c := gomcache() c := gomcache()
s := c.sudogcache s := c.sudogcache
if s != nil { if s != nil {
if s.elem != nil {
gothrow("acquireSudog: found s.elem != nil in cache")
}
c.sudogcache = s.next c.sudogcache = s.next
return s return s
} }
@ -162,12 +165,22 @@ func acquireSudog() *sudog {
// which keeps the garbage collector from being invoked. // which keeps the garbage collector from being invoked.
mp := acquirem() mp := acquirem()
p := new(sudog) p := new(sudog)
if p.elem != nil {
gothrow("acquireSudog: found p.elem != nil after new")
}
releasem(mp) releasem(mp)
return p return p
} }
//go:nosplit //go:nosplit
func releaseSudog(s *sudog) { func releaseSudog(s *sudog) {
if s.elem != nil {
gothrow("runtime: sudog with non-nil elem")
}
gp := getg()
if gp.param != nil {
gothrow("runtime: releaseSudog with non-nil gp.param")
}
c := gomcache() c := gomcache()
s.next = c.sudogcache s.next = c.sudogcache
c.sudogcache = s c.sudogcache = s

View file

@ -368,6 +368,7 @@ loop:
// someone woke us up // someone woke us up
sellock(sel) sellock(sel)
sg = (*sudog)(gp.param) sg = (*sudog)(gp.param)
gp.param = nil
// pass 3 - dequeue from unsuccessful chans // pass 3 - dequeue from unsuccessful chans
// otherwise they stack up on quiet channels // otherwise they stack up on quiet channels
@ -376,6 +377,10 @@ loop:
// iterating through the linked list they are in reverse order. // iterating through the linked list they are in reverse order.
cas = nil cas = nil
sglist = gp.waiting sglist = gp.waiting
// Clear all elem before unlinking from gp.waiting.
for sg1 := gp.waiting; sg1 != nil; sg1 = sg1.waitlink {
sg1.elem = nil
}
gp.waiting = nil gp.waiting = nil
for i := int(sel.ncase) - 1; i >= 0; i-- { for i := int(sel.ncase) - 1; i >= 0; i-- {
k = &scases[pollorder[i]] k = &scases[pollorder[i]]
@ -506,6 +511,7 @@ syncrecv:
if cas.elem != nil { if cas.elem != nil {
memmove(cas.elem, sg.elem, uintptr(c.elemsize)) memmove(cas.elem, sg.elem, uintptr(c.elemsize))
} }
sg.elem = nil
gp = sg.g gp = sg.g
gp.param = unsafe.Pointer(sg) gp.param = unsafe.Pointer(sg)
if sg.releasetime != 0 { if sg.releasetime != 0 {
@ -541,6 +547,7 @@ syncsend:
if sg.elem != nil { if sg.elem != nil {
memmove(sg.elem, cas.elem, uintptr(c.elemsize)) memmove(sg.elem, cas.elem, uintptr(c.elemsize))
} }
sg.elem = nil
gp = sg.g gp = sg.g
gp.param = unsafe.Pointer(sg) gp.param = unsafe.Pointer(sg)
if sg.releasetime != 0 { if sg.releasetime != 0 {

View file

@ -168,6 +168,7 @@ func (root *semaRoot) dequeue(s *sudog) {
} else { } else {
root.head = s.next root.head = s.next
} }
s.elem = nil
s.next = nil s.next = nil
s.prev = nil s.prev = nil
} }