Skip to content

Commit 1f45216

Browse files
committed
cmd/compile: attach OVARLIVE nodes to OCALLxxx
So we can insert theses OVARLIVE nodes right after OpStaticCall in SSA. This helps fixing issue that unsafe-uintptr arguments are not kept alive during return statement, or can be kept alive longer than expected. Fixes #24491 Change-Id: Ic04a5d1bbb5c90dcfae65bd95cdd1da393a66800 Reviewed-on: https://go-review.googlesource.com/c/go/+/254397 Run-TryBot: Cuong Manh Le <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Matthew Dempsky <[email protected]>
1 parent 95bb00d commit 1f45216

File tree

5 files changed

+63
-14
lines changed

5 files changed

+63
-14
lines changed

src/cmd/compile/internal/gc/order.go

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -288,20 +288,13 @@ func (o *Order) popTemp(mark ordermarker) {
288288
o.temp = o.temp[:mark]
289289
}
290290

291-
// cleanTempNoPop emits VARKILL and if needed VARLIVE instructions
292-
// to *out for each temporary above the mark on the temporary stack.
291+
// cleanTempNoPop emits VARKILL instructions to *out
292+
// for each temporary above the mark on the temporary stack.
293293
// It does not pop the temporaries from the stack.
294294
func (o *Order) cleanTempNoPop(mark ordermarker) []*Node {
295295
var out []*Node
296296
for i := len(o.temp) - 1; i >= int(mark); i-- {
297297
n := o.temp[i]
298-
if n.Name.Keepalive() {
299-
n.Name.SetKeepalive(false)
300-
n.Name.SetAddrtaken(true) // ensure SSA keeps the n variable
301-
live := nod(OVARLIVE, n, nil)
302-
live = typecheck(live, ctxStmt)
303-
out = append(out, live)
304-
}
305298
kill := nod(OVARKILL, n, nil)
306299
kill = typecheck(kill, ctxStmt)
307300
out = append(out, kill)
@@ -500,8 +493,9 @@ func (o *Order) call(n *Node) {
500493
// still alive when we pop the temp stack.
501494
if arg.Op == OCONVNOP && arg.Left.Type.IsUnsafePtr() {
502495
x := o.copyExpr(arg.Left, arg.Left.Type, false)
503-
x.Name.SetKeepalive(true)
504496
arg.Left = x
497+
x.Name.SetAddrtaken(true) // ensure SSA keeps the x variable
498+
n.Nbody.Append(typecheck(nod(OVARLIVE, x, nil), ctxStmt))
505499
n.SetNeedsWrapper(true)
506500
}
507501
}

src/cmd/compile/internal/gc/ssa.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4498,6 +4498,8 @@ func (s *state) call(n *Node, k callKind) *ssa.Value {
44984498
call.AuxInt = stksize // Call operations carry the argsize of the callee along with them
44994499
}
45004500
s.vars[&memVar] = call
4501+
// Insert OVARLIVE nodes
4502+
s.stmtList(n.Nbody)
45014503

45024504
// Finish block for defers
45034505
if k == callDefer || k == callDeferStack {

src/cmd/compile/internal/gc/syntax.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,6 @@ const (
374374
nameReadonly
375375
nameByval // is the variable captured by value or by reference
376376
nameNeedzero // if it contains pointers, needs to be zeroed on function entry
377-
nameKeepalive // mark value live across unknown assembly call
378377
nameAutoTemp // is the variable a temporary (implies no dwarf info. reset if escapes to heap)
379378
nameUsed // for variable declared and not used error
380379
nameIsClosureVar // PAUTOHEAP closure pseudo-variable; original at n.Name.Defn
@@ -391,7 +390,6 @@ func (n *Name) Captured() bool { return n.flags&nameCaptured != 0 }
391390
func (n *Name) Readonly() bool { return n.flags&nameReadonly != 0 }
392391
func (n *Name) Byval() bool { return n.flags&nameByval != 0 }
393392
func (n *Name) Needzero() bool { return n.flags&nameNeedzero != 0 }
394-
func (n *Name) Keepalive() bool { return n.flags&nameKeepalive != 0 }
395393
func (n *Name) AutoTemp() bool { return n.flags&nameAutoTemp != 0 }
396394
func (n *Name) Used() bool { return n.flags&nameUsed != 0 }
397395
func (n *Name) IsClosureVar() bool { return n.flags&nameIsClosureVar != 0 }
@@ -407,7 +405,6 @@ func (n *Name) SetCaptured(b bool) { n.flags.set(nameCaptured, b) }
407405
func (n *Name) SetReadonly(b bool) { n.flags.set(nameReadonly, b) }
408406
func (n *Name) SetByval(b bool) { n.flags.set(nameByval, b) }
409407
func (n *Name) SetNeedzero(b bool) { n.flags.set(nameNeedzero, b) }
410-
func (n *Name) SetKeepalive(b bool) { n.flags.set(nameKeepalive, b) }
411408
func (n *Name) SetAutoTemp(b bool) { n.flags.set(nameAutoTemp, b) }
412409
func (n *Name) SetUsed(b bool) { n.flags.set(nameUsed, b) }
413410
func (n *Name) SetIsClosureVar(b bool) { n.flags.set(nameIsClosureVar, b) }
@@ -707,6 +704,7 @@ const (
707704
// Prior to walk, they are: Left(List), where List is all regular arguments.
708705
// After walk, List is a series of assignments to temporaries,
709706
// and Rlist is an updated set of arguments.
707+
// Nbody is all OVARLIVE nodes that are attached to OCALLxxx.
710708
// TODO(josharian/khr): Use Ninit instead of List for the assignments to temporaries. See CL 114797.
711709
OCALLFUNC // Left(List/Rlist) (function call f(args))
712710
OCALLMETH // Left(List/Rlist) (direct method call x.Method(args))

test/fixedbugs/issue24491.go renamed to test/fixedbugs/issue24491a.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,18 @@ func setup() unsafe.Pointer {
2323

2424
//go:noinline
2525
//go:uintptrescapes
26-
func test(s string, p uintptr) {
26+
func test(s string, p uintptr) int {
2727
runtime.GC()
2828
if *(*string)(unsafe.Pointer(p)) != "ok" {
2929
panic(s + " return unexpected result")
3030
}
3131
done <- true
32+
return 0
33+
}
34+
35+
//go:noinline
36+
func f() int {
37+
return test("return", uintptr(setup()))
3238
}
3339

3440
func main() {
@@ -42,4 +48,7 @@ func main() {
4248
defer test("defer", uintptr(setup()))
4349
}()
4450
<-done
51+
52+
f()
53+
<-done
4554
}

test/fixedbugs/issue24491b.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// run
2+
3+
// Copyright 2020 The Go Authors. All rights reserved.
4+
// Use of this source code is governed by a BSD-style
5+
// license that can be found in the LICENSE file.
6+
7+
// This test makes sure unsafe-uintptr arguments are not
8+
// kept alive longer than expected.
9+
10+
package main
11+
12+
import (
13+
"runtime"
14+
"sync/atomic"
15+
"unsafe"
16+
)
17+
18+
var done uint32
19+
20+
func setup() unsafe.Pointer {
21+
s := "ok"
22+
runtime.SetFinalizer(&s, func(p *string) { atomic.StoreUint32(&done, 1) })
23+
return unsafe.Pointer(&s)
24+
}
25+
26+
//go:noinline
27+
//go:uintptrescapes
28+
func before(p uintptr) int {
29+
runtime.GC()
30+
if atomic.LoadUint32(&done) != 0 {
31+
panic("GC early")
32+
}
33+
return 0
34+
}
35+
36+
func after() int {
37+
runtime.GC()
38+
if atomic.LoadUint32(&done) == 0 {
39+
panic("GC late")
40+
}
41+
return 0
42+
}
43+
44+
func main() {
45+
_ = before(uintptr(setup())) + after()
46+
}

0 commit comments

Comments
 (0)