Commit 5583060c authored by Luuk van Dijk's avatar Luuk van Dijk Committed by Russ Cox

cmd/gc: fix addresses escaping through closures called in-place.

Fixes #3545.

R=rsc
CC=golang-dev
https://golang.org/cl/6061043
parent b678c197
...@@ -131,7 +131,12 @@ escfunc(Node *func) ...@@ -131,7 +131,12 @@ escfunc(Node *func)
} }
// walk will take the address of cvar->closure later and assign it to cvar. // walk will take the address of cvar->closure later and assign it to cvar.
// handle that here by linking a fake oaddr node directly to the closure. // linking a fake oaddr node directly to the closure handles the case
// of the closure itself leaking. Following the flow of the value to th
// paramref is done in escflow, because if we did that here, it would look
// like the original is assigned out of its loop depth, whereas it's just
// assigned to something in an inner function. A paramref itself is never
// moved to the heap, only its original.
for(ll=curfn->cvars; ll; ll=ll->next) { for(ll=curfn->cvars; ll; ll=ll->next) {
if(ll->n->op == OXXX) // see dcl.c:398 if(ll->n->op == OXXX) // see dcl.c:398
continue; continue;
...@@ -221,16 +226,19 @@ esc(Node *n) ...@@ -221,16 +226,19 @@ esc(Node *n)
if(n->op == OFOR || n->op == ORANGE) if(n->op == OFOR || n->op == ORANGE)
loopdepth++; loopdepth++;
esc(n->left); if(n->op == OCLOSURE) {
esc(n->right); escfunc(n);
esc(n->ntest); } else {
esc(n->nincr); esc(n->left);
esclist(n->ninit); esc(n->right);
esclist(n->nbody); esc(n->ntest);
esclist(n->nelse); esc(n->nincr);
esclist(n->list); esclist(n->ninit);
esclist(n->rlist); esclist(n->nbody);
esclist(n->nelse);
esclist(n->list);
esclist(n->rlist);
}
if(n->op == OFOR || n->op == ORANGE) if(n->op == OFOR || n->op == ORANGE)
loopdepth--; loopdepth--;
...@@ -379,8 +387,8 @@ esc(Node *n) ...@@ -379,8 +387,8 @@ esc(Node *n)
} }
break; break;
case OADDR:
case OCLOSURE: case OCLOSURE:
case OADDR:
case OMAKECHAN: case OMAKECHAN:
case OMAKEMAP: case OMAKEMAP:
case OMAKESLICE: case OMAKESLICE:
...@@ -407,8 +415,8 @@ escassign(Node *dst, Node *src) ...@@ -407,8 +415,8 @@ escassign(Node *dst, Node *src)
return; return;
if(debug['m'] > 1) if(debug['m'] > 1)
print("%L:[%d] %S escassign: %hN = %hN\n", lineno, loopdepth, print("%L:[%d] %S escassign: %hN(%hJ) = %hN(%hJ)\n", lineno, loopdepth,
(curfn && curfn->nname) ? curfn->nname->sym : S, dst, src); (curfn && curfn->nname) ? curfn->nname->sym : S, dst, dst, src, src);
setlineno(dst); setlineno(dst);
...@@ -467,7 +475,11 @@ escassign(Node *dst, Node *src) ...@@ -467,7 +475,11 @@ escassign(Node *dst, Node *src)
case OARRAYLIT: case OARRAYLIT:
case OMAPLIT: case OMAPLIT:
case OSTRUCTLIT: case OSTRUCTLIT:
// loopdepth was set in the defining statement or function header case OMAKECHAN:
case OMAKEMAP:
case OMAKESLICE:
case ONEW:
case OCLOSURE:
escflows(dst, src); escflows(dst, src);
break; break;
...@@ -500,18 +512,6 @@ escassign(Node *dst, Node *src) ...@@ -500,18 +512,6 @@ escassign(Node *dst, Node *src)
escassign(dst, src->left); escassign(dst, src->left);
break; break;
case OMAKECHAN:
case OMAKEMAP:
case OMAKESLICE:
case ONEW:
escflows(dst, src);
break;
case OCLOSURE:
escflows(dst, src);
escfunc(src);
break;
case OADD: case OADD:
case OSUB: case OSUB:
case OOR: case OOR:
...@@ -543,7 +543,7 @@ escassign(Node *dst, Node *src) ...@@ -543,7 +543,7 @@ escassign(Node *dst, Node *src)
// This is a bit messier than fortunate, pulled out of escassign's big // This is a bit messier than fortunate, pulled out of escassign's big
// switch for clarity. We either have the paramnodes, which may be // switch for clarity. We either have the paramnodes, which may be
// connected to other things throug flows or we have the parameter type // connected to other things throug flows or we have the parameter type
// nodes, which may be marked 'n(ofloworescape)'. Navigating the ast is slightly // nodes, which may be marked "noescape". Navigating the ast is slightly
// different for methods vs plain functions and for imported vs // different for methods vs plain functions and for imported vs
// this-package // this-package
static void static void
...@@ -711,8 +711,8 @@ escwalk(int level, Node *dst, Node *src) ...@@ -711,8 +711,8 @@ escwalk(int level, Node *dst, Node *src)
src->walkgen = walkgen; src->walkgen = walkgen;
if(debug['m']>1) if(debug['m']>1)
print("escwalk: level:%d depth:%d %.*s %hN scope:%S[%d]\n", print("escwalk: level:%d depth:%d %.*s %hN(%hJ) scope:%S[%d]\n",
level, pdepth, pdepth, "\t\t\t\t\t\t\t\t\t\t", src, level, pdepth, pdepth, "\t\t\t\t\t\t\t\t\t\t", src, src,
(src->curfn && src->curfn->nname) ? src->curfn->nname->sym : S, src->escloopdepth); (src->curfn && src->curfn->nname) ? src->curfn->nname->sym : S, src->escloopdepth);
pdepth++; pdepth++;
...@@ -726,6 +726,16 @@ escwalk(int level, Node *dst, Node *src) ...@@ -726,6 +726,16 @@ escwalk(int level, Node *dst, Node *src)
if(debug['m']) if(debug['m'])
warnl(src->lineno, "leaking param: %hN", src); warnl(src->lineno, "leaking param: %hN", src);
} }
// handle the missing flow ref <- orig
// a paramref is automagically dereferenced, and taking its
// address produces the address of the original, so all we have to do here
// is keep track of the value flow, so level is unchanged.
// alternatively, we could have substituted PPARAMREFs with their ->closure in esc/escassign/flow,
if(src->class == PPARAMREF) {
if(leaks && debug['m'])
warnl(src->lineno, "leaking closure reference %hN", src);
escwalk(level, dst, src->closure);
}
break; break;
case OPTRLIT: case OPTRLIT:
......
...@@ -1051,7 +1051,7 @@ func foo122() { ...@@ -1051,7 +1051,7 @@ func foo122() {
goto L1 goto L1
L1: L1:
i = new(int) // ERROR "does not escape" i = new(int) // ERROR "new.int. does not escape"
_ = i _ = i
} }
...@@ -1060,8 +1060,141 @@ func foo123() { ...@@ -1060,8 +1060,141 @@ func foo123() {
var i *int var i *int
L1: L1:
i = new(int) // ERROR "escapes" i = new(int) // ERROR "new.int. escapes to heap"
goto L1 goto L1
_ = i _ = i
} }
func foo124(x **int) { // ERROR "x does not escape"
var i int // ERROR "moved to heap: i"
p := &i // ERROR "&i escapes"
func() { // ERROR "func literal does not escape"
*x = p // ERROR "leaking closure reference p"
}()
}
func foo125(ch chan *int) { // ERROR "does not escape"
var i int // ERROR "moved to heap"
p := &i // ERROR "&i escapes to heap"
func() { // ERROR "func literal does not escape"
ch <- p // ERROR "leaking closure reference p"
}()
}
func foo126() {
var px *int // loopdepth 0
for {
// loopdepth 1
var i int // ERROR "moved to heap"
func() { // ERROR "func literal does not escape"
px = &i // ERROR "&i escapes"
}()
}
}
var px *int
func foo127() {
var i int // ERROR "moved to heap: i"
p := &i // ERROR "&i escapes to heap"
q := p
px = q
}
func foo128() {
var i int
p := &i // ERROR "&i does not escape"
q := p
_ = q
}
func foo129() {
var i int // ERROR "moved to heap: i"
p := &i // ERROR "&i escapes to heap"
func() { // ERROR "func literal does not escape"
q := p // ERROR "leaking closure reference p"
func() { // ERROR "func literal does not escape"
r := q // ERROR "leaking closure reference q"
px = r
}()
}()
}
func foo130() {
for {
var i int // ERROR "moved to heap"
func() { // ERROR "func literal does not escape"
px = &i // ERROR "&i escapes" "leaking closure reference i"
}()
}
}
func foo131() {
var i int // ERROR "moved to heap"
func() { // ERROR "func literal does not escape"
px = &i // ERROR "&i escapes" "leaking closure reference i"
}()
}
func foo132() {
var i int // ERROR "moved to heap"
go func() { // ERROR "func literal escapes to heap"
px = &i // ERROR "&i escapes" "leaking closure reference i"
}()
}
func foo133() {
var i int // ERROR "moved to heap"
defer func() { // ERROR "func literal does not escape"
px = &i // ERROR "&i escapes" "leaking closure reference i"
}()
}
func foo134() {
var i int
p := &i // ERROR "&i does not escape"
func() { // ERROR "func literal does not escape"
q := p
func() { // ERROR "func literal does not escape"
r := q
_ = r
}()
}()
}
func foo135() {
var i int // ERROR "moved to heap: i"
p := &i // ERROR "&i escapes to heap" "moved to heap: p"
go func() { // ERROR "func literal escapes to heap"
q := p // ERROR "&p escapes to heap"
func() { // ERROR "func literal does not escape"
r := q
_ = r
}()
}()
}
func foo136() {
var i int // ERROR "moved to heap: i"
p := &i // ERROR "&i escapes to heap" "moved to heap: p"
go func() { // ERROR "func literal escapes to heap"
q := p // ERROR "&p escapes to heap" "leaking closure reference p"
func() { // ERROR "func literal does not escape"
r := q // ERROR "leaking closure reference q"
px = r
}()
}()
}
func foo137() {
var i int // ERROR "moved to heap: i"
p := &i // ERROR "&i escapes to heap"
func() { // ERROR "func literal does not escape"
q := p // ERROR "leaking closure reference p" "moved to heap: q"
go func() { // ERROR "func literal escapes to heap"
r := q // ERROR "&q escapes to heap"
_ = r
}()
}()
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment