LEFT | RIGHT |
1 // Copyright 2011 The Go Authors. All rights reserved. | 1 // Copyright 2011 The Go Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style | 2 // Use of this source code is governed by a BSD-style |
3 // license that can be found in the LICENSE file. | 3 // license that can be found in the LICENSE file. |
4 // | 4 // |
5 // The base version before this file existed, active with debug['s'] | 5 // The base version before this file existed, active with debug['s'] |
6 // == 0, assumes any node that has a reference to it created at some | 6 // == 0, assumes any node that has a reference to it created at some |
7 // point, may flow to the global scope except | 7 // point, may flow to the global scope except |
8 // - if its address is dereferenced immediately with only CONVNOPs in | 8 // - if its address is dereferenced immediately with only CONVNOPs in |
9 // between the * and the & | 9 // between the * and the & |
10 // - if it is for a closure variable and the closure executed at the | 10 // - if it is for a closure variable and the closure executed at the |
11 // place it's defined | 11 // place it's defined |
12 // | 12 // |
13 // Flag -s disables the old codepaths and switches on the code here: | 13 // Flag -s disables the old codepaths and switches on the code here: |
14 // | 14 // |
15 // First escfunc, escstmt and escexpr recurse over the ast of each | 15 // First escfunc, escstmt and escexpr recurse over the ast of each |
16 // function to dig out flow(dst,src) edges between any | 16 // function to dig out flow(dst,src) edges between any |
17 // pointer-containing nodes and store them in dst->flowsrc. For | 17 // pointer-containing nodes and store them in dst->escflowsrc. For |
18 // variables assigned to a variable in an outer scope or used as a | 18 // variables assigned to a variable in an outer scope or used as a |
19 // return value, they store a flow(theSink, src) edge to a fake node | 19 // return value, they store a flow(theSink, src) edge to a fake node |
20 // 'the Sink'. For variables referenced in closures, an edge | 20 // 'the Sink'. For variables referenced in closures, an edge |
21 // flow(closure, var) is recorded and the flow of a closure itself to | 21 // flow(closure, &var) is recorded and the flow of a closure itself to |
22 // an outer scope is tracked the same way as other variables. | 22 // an outer scope is tracked the same way as other variables. |
23 // | 23 // |
24 // The link between variables and the function that declares it is | 24 // Then escflood walks the graph starting at theSink and tags all |
25 // already in the n->dcl list.» Then escflood walks the graph starting | 25 // variables of it can reach an & node as escaping and all function |
26 // at theSink and tags all variables of it can reach an & node as | 26 // parameters it can reach as leaking. |
27 // escaping and all function parameters it can reach as leaking. | |
28 // | 27 // |
29 // Watch the variables moved to the heap and parameters tagged as | 28 // Watch the variables moved to the heap and parameters tagged as |
30 // unsafe with -m, more detailed analysis output with -mm | 29 // unsafe with -m, more detailed analysis output with -mm |
31 // | 30 // |
32 | 31 |
33 #include "go.h" | 32 #include "go.h" |
34 | 33 |
35 static void escfunc(Node *func); | 34 static void escfunc(Node *func); |
36 static void escstmtlist(NodeList *stmts); | 35 static void escstmtlist(NodeList *stmts); |
37 static void escstmt(Node *stmt); | 36 static void escstmt(Node *stmt); |
38 static void escexpr(Node *dst, Node *expr); | 37 static void escexpr(Node *dst, Node *expr); |
39 static void escexprcall(Node *dst, Node *callexpr); | 38 static void escexprcall(Node *dst, Node *callexpr); |
40 static void escflows(Node* dst, Node* src); | 39 static void escflows(Node* dst, Node* src); |
41 static void escflood(Node *dst); | 40 static void escflood(Node *dst); |
42 static void escwalk(int level, Node *dst, Node *src); | 41 static void escwalk(int level, Node *dst, Node *src); |
43 static void esctag(Node *func); | 42 static void esctag(Node *func); |
44 | 43 |
45 // Fake node that all | 44 // Fake node that all |
46 // - return values and output variables | 45 // - return values and output variables |
47 // - parameters on imported functions not marked 'safe' | 46 // - parameters on imported functions not marked 'safe' |
48 // - assignments to global variables | 47 // - assignments to global variables |
49 // flow to. | 48 // flow to. |
50 static Node theSink; | 49 static Node theSink; |
51 | 50 |
52 static NodeList* dsts; // all dst nodes | 51 static NodeList* dsts; // all dst nodes |
53 static int dstcount; // diagnostic | |
54 static int edgecount; // diagnostic | |
55 static int loopdepth; // for detecting nested loop scopes | 52 static int loopdepth; // for detecting nested loop scopes |
56 static int pdepth; // for debug printing in recursions. | 53 static int pdepth; // for debug printing in recursions. |
57 static int floodgen; // loop prevention in flood/walk | 54 static int floodgen; // loop prevention in flood/walk |
58 static Strlit* safetag; // gets slapped on safe parameters' field types
for export | 55 static Strlit* safetag; // gets slapped on safe parameters' field types
for export |
| 56 static int dstcount, edgecount; // diagnostic |
59 | 57 |
60 void | 58 void |
61 escapes(void) | 59 escapes(void) |
62 { | 60 { |
63 NodeList *l; | 61 NodeList *l; |
64 | 62 |
65 theSink.op = ONAME; | 63 theSink.op = ONAME; |
66 theSink.class = PEXTERN; | 64 theSink.class = PEXTERN; |
67 theSink.sym = lookup(".sink"); | 65 theSink.sym = lookup(".sink"); |
68 » theSink.loopdepth = -1; | 66 » theSink.escloopdepth = -1; |
69 | 67 |
70 » safetag = strlit("noflow"); | 68 » safetag = strlit("noescape"); |
71 | 69 |
72 // flow-analyze top level functions | 70 // flow-analyze top level functions |
73 for(l=xtop; l; l=l->next) | 71 for(l=xtop; l; l=l->next) |
74 if(l->n->op == ODCLFUNC || l->n->op == OCLOSURE) | 72 if(l->n->op == ODCLFUNC || l->n->op == OCLOSURE) |
75 escfunc(l->n); | 73 escfunc(l->n); |
76 | 74 |
77 // print("escapes: %d dsts, %d edges\n", dstcount, edgecount); | 75 // print("escapes: %d dsts, %d edges\n", dstcount, edgecount); |
78 | 76 |
79 // visit the updstream of each dst, mark address nodes with | 77 // visit the updstream of each dst, mark address nodes with |
80 // addrescapes, mark parameters unsafe | 78 // addrescapes, mark parameters unsafe |
81 for (l = dsts; l; l=l->next) | 79 for (l = dsts; l; l=l->next) |
82 escflood(l->n); | 80 escflood(l->n); |
83 | 81 |
84 // for all top level functions, tag the typenodes corresponding to the p
aram nodes | 82 // for all top level functions, tag the typenodes corresponding to the p
aram nodes |
85 for(l=xtop; l; l=l->next) | 83 for(l=xtop; l; l=l->next) |
86 if(l->n->op == ODCLFUNC) | 84 if(l->n->op == ODCLFUNC) |
87 esctag(l->n); | 85 esctag(l->n); |
88 } | 86 } |
89 | 87 |
90 static void | 88 static void |
91 escfunc(Node *func) | 89 escfunc(Node *func) |
92 { | 90 { |
93 » Node *savefn; | 91 » Node *savefn, *n; |
94 NodeList *ll; | 92 NodeList *ll; |
95 int saveld; | 93 int saveld; |
96 | 94 |
97 saveld = loopdepth; | 95 saveld = loopdepth; |
98 loopdepth = 1; | 96 loopdepth = 1; |
99 savefn = curfn; | 97 savefn = curfn; |
100 curfn = func; | 98 curfn = func; |
101 | 99 |
102 for(ll=curfn->dcl; ll; ll=ll->next) { | 100 for(ll=curfn->dcl; ll; ll=ll->next) { |
103 if(ll->n->op != ONAME) | 101 if(ll->n->op != ONAME) |
104 continue; | 102 continue; |
105 switch (ll->n->class) { | 103 switch (ll->n->class) { |
106 case PPARAMOUT: | 104 case PPARAMOUT: |
107 // output parameters flow to the sink | 105 // output parameters flow to the sink |
108 escflows(&theSink, ll->n); | 106 escflows(&theSink, ll->n); |
109 » » » ll->n->loopdepth = loopdepth; | 107 » » » ll->n->escloopdepth = loopdepth; |
110 break; | 108 break; |
111 case PPARAM: | 109 case PPARAM: |
112 ll->n->esc = EscNone; // prime for escflood later | 110 ll->n->esc = EscNone; // prime for escflood later |
113 » » » ll->n->loopdepth = loopdepth; | 111 » » » ll->n->escloopdepth = loopdepth; |
114 break; | 112 break; |
115 } | 113 } |
| 114 } |
| 115 |
| 116 // walk will take the address of cvar->closure later and assign it to cv
ar. |
| 117 // handle that here by linking a fake oaddr node directly to the closure
. |
| 118 for (ll=curfn->cvars; ll; ll=ll->next) { |
| 119 if(ll->n->op == OXXX) // see dcl.c:398 |
| 120 continue; |
| 121 |
| 122 n = nod(OADDR, ll->n->closure, N); |
| 123 n->lineno = ll->n->lineno; |
| 124 typecheck(&n, Erv); |
| 125 escexpr(curfn, n); |
116 } | 126 } |
117 | 127 |
118 escstmtlist(curfn->nbody); | 128 escstmtlist(curfn->nbody); |
119 curfn = savefn; | 129 curfn = savefn; |
120 loopdepth = saveld; | 130 loopdepth = saveld; |
121 } | 131 } |
122 | 132 |
123 static void | 133 static void |
124 escstmtlist(NodeList* stmts) | 134 escstmtlist(NodeList* stmts) |
125 { | 135 { |
(...skipping 15 matching lines...) Expand all Loading... |
141 | 151 |
142 if(stmt->typecheck == 0 && stmt->op != ODCL) { // TODO something with
OAS2 | 152 if(stmt->typecheck == 0 && stmt->op != ODCL) { // TODO something with
OAS2 |
143 dump("escstmt missing typecheck", stmt); | 153 dump("escstmt missing typecheck", stmt); |
144 fatal("missing typecheck."); | 154 fatal("missing typecheck."); |
145 } | 155 } |
146 | 156 |
147 // Common to almost all statements, and nil if n/a. | 157 // Common to almost all statements, and nil if n/a. |
148 escstmtlist(stmt->ninit); | 158 escstmtlist(stmt->ninit); |
149 | 159 |
150 if(debug['m'] > 1) | 160 if(debug['m'] > 1) |
151 » » print("%L:[%d] %#S statement: %#N\n", lineno, loopdepth, (curfn
&& curfn->nname) ? curfn->nname->sym : S, stmt); | 161 » » print("%L:[%d] %#S statement: %#N\n", lineno, loopdepth, |
| 162 » » (curfn && curfn->nname) ? curfn->nname->sym : S, stmt); |
152 | 163 |
153 switch(stmt->op) { | 164 switch(stmt->op) { |
154 default: | |
155 // Can get here with typecheck errors and will emit | |
156 // spurious errors, so comment out before committing | |
157 dump("escstmt", stmt); | |
158 fatal("escstmt %O", stmt->op); | |
159 break; | |
160 | |
161 case ODCL: | 165 case ODCL: |
162 case ODCLFIELD: | 166 case ODCLFIELD: |
163 // a declaration ties the node to the current | 167 // a declaration ties the node to the current |
164 // function, but we already have that edge in | 168 // function, but we already have that edge in |
165 // curfn->dcl and will follow it explicitly in | 169 // curfn->dcl and will follow it explicitly in |
166 // escflood to avoid storing redundant information | 170 // escflood to avoid storing redundant information |
167 // What does have to happen here is note if the name | 171 // What does have to happen here is note if the name |
168 // is declared inside a looping scope. | 172 // is declared inside a looping scope. |
169 » » stmt->left->loopdepth = loopdepth; | 173 » » stmt->left->escloopdepth = loopdepth; |
170 » » break; | |
171 | |
172 » case ODCLCONST: | |
173 » case ODCLTYPE: | |
174 » case OBREAK: | |
175 » case OCONTINUE: | |
176 » case OXFALL: | |
177 » case OGOTO: | |
178 » case OEMPTY: | |
179 » case ORECOVER: | |
180 » » // all harmless | |
181 break; | 174 break; |
182 | 175 |
183 case OLABEL: // TODO: new loop/scope only if there are backjumps to it. | 176 case OLABEL: // TODO: new loop/scope only if there are backjumps to it. |
184 loopdepth++; | 177 loopdepth++; |
185 break; | 178 break; |
186 | 179 |
187 case OBLOCK: | 180 case OBLOCK: |
188 escstmtlist(stmt->list); | 181 escstmtlist(stmt->list); |
189 break; | 182 break; |
190 | 183 |
(...skipping 161 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
352 | 345 |
353 case OPANIC: | 346 case OPANIC: |
354 // Argument could leak through recover. | 347 // Argument could leak through recover. |
355 escexpr(&theSink, stmt->left); | 348 escexpr(&theSink, stmt->left); |
356 break; | 349 break; |
357 } | 350 } |
358 | 351 |
359 lineno = lno; | 352 lineno = lno; |
360 } | 353 } |
361 | 354 |
362 // This is a bit messier than fortunate, pulled out of escexpr's big | |
363 // switch for clarity. We either have the paramnodes, which may be | |
364 // connected to other things throug flows or we have the parameter type | |
365 // nodes, which may be marked 'noflow'. Navigating the ast is slightly | |
366 // different for methods vs plain functions and for imported vs | |
367 // this-package | |
368 static void | |
369 escexprcall(Node *dst, Node *expr) | |
370 { | |
371 NodeList *ll, *lr; | |
372 Node *fn; | |
373 Type *t, *fntype, *thisarg, *inargs; | |
374 | |
375 fn = nil; | |
376 fntype = nil; | |
377 | |
378 switch(expr->op) { | |
379 case OCALLFUNC: | |
380 fn = expr->left; | |
381 escexpr(N, fn); | |
382 fntype = fn->type; | |
383 break; | |
384 | |
385 case OCALLMETH: | |
386 fn = expr->left->right; // ODOTxx name | |
387 fn = fn->sym->def; // resolve to definition if we have it | |
388 if(fn) | |
389 fntype = fn->type; | |
390 else | |
391 fntype = expr->left->type; | |
392 break; | |
393 | |
394 case OCALLINTER: | |
395 break; | |
396 | |
397 default: | |
398 fatal("escexprcall called with non-call expression"); | |
399 } | |
400 | |
401 if(fn && fn->ntype) { | |
402 if(debug['m'] > 2) | |
403 print("escexprcall: have param nodes: %N\n", fn->ntype); | |
404 | |
405 if(expr->op == OCALLMETH) { | |
406 if(debug['m'] > 2) | |
407 print("escexprcall: this: %N\n",fn->ntype->left-
>left); | |
408 escexpr(fn->ntype->left->left, expr->left->left); | |
409 } | |
410 | |
411 // lr->n is the dclfield, ->left is the ONAME param node | |
412 for(ll=expr->list, lr=fn->ntype->list; ll && lr; ll=ll->next) { | |
413 if(debug['m'] > 2) | |
414 print("escexprcall: field param: %N\n", lr->n->l
eft); | |
415 if (lr->n->left) | |
416 escexpr(lr->n->left, ll->n); | |
417 else | |
418 escexpr(&theSink, ll->n); | |
419 if(lr->n->left && !lr->n->left->isddd) | |
420 lr=lr->next; | |
421 } | |
422 return; | |
423 } | |
424 | |
425 if(fntype) { | |
426 if(debug['m'] > 2) | |
427 print("escexprcall: have param types: %T\n", fntype); | |
428 | |
429 if(expr->op == OCALLMETH) { | |
430 thisarg = getthisx(fntype); | |
431 t = thisarg->type; | |
432 if(debug['m'] > 2) | |
433 print("escexprcall: this: %T\n", t); | |
434 if(!t->note || strcmp(t->note->s, "noflow") != 0) | |
435 escexpr(&theSink, expr->left->left); | |
436 else | |
437 escexpr(N, expr->left->left); | |
438 } | |
439 | |
440 inargs = getinargx(fntype); | |
441 for(ll=expr->list, t=inargs->type; ll; ll=ll->next) { | |
442 if(debug['m'] > 2) | |
443 print("escexprcall: field type: %T\n", t); | |
444 if(!t->note || strcmp(t->note->s, "noflow")) | |
445 escexpr(&theSink, ll->n); | |
446 else | |
447 escexpr(N, ll->n); | |
448 if(t->down) | |
449 t=t->down; | |
450 } | |
451 | |
452 return; | |
453 } | |
454 | |
455 // fallthrough if we don't have enough information: | |
456 // can only assume all parameters are unsafe | |
457 // OCALLINTER always ends up here | |
458 | |
459 if(debug['m']>1 && expr->op != OCALLINTER) { | |
460 // dump("escexprcall", expr); | |
461 print("escexprcall: %O, no nodes, no types: %N\n", expr->op, fn)
; | |
462 } | |
463 | |
464 escexpr(&theSink, expr->left->left); // the this argument | |
465 for(ll=expr->list; ll; ll=ll->next) | |
466 escexpr(&theSink, ll->n); | |
467 } | |
468 | |
469 // Assert that expr somehow gets assigned to dst, if non nil. for | 355 // Assert that expr somehow gets assigned to dst, if non nil. for |
470 // dst==nil, any name node expr still must be marked as being | 356 // dst==nil, any name node expr still must be marked as being |
471 // evaluated in curfn. For expr==nil, dst must still be examined for | 357 // evaluated in curfn. For expr==nil, dst must still be examined for |
472 // evaluations inside it (e.g *f(x) = y) | 358 // evaluations inside it (e.g *f(x) = y) |
473 static void | 359 static void |
474 escexpr(Node *dst, Node *expr) | 360 escexpr(Node *dst, Node *expr) |
475 { | 361 { |
476 int lno; | 362 int lno; |
477 NodeList *ll; | 363 NodeList *ll; |
478 | 364 |
(...skipping 24 matching lines...) Expand all Loading... |
503 escexpr(&theSink, dst->right); // map key is put in map | 389 escexpr(&theSink, dst->right); // map key is put in map |
504 // fallthrough | 390 // fallthrough |
505 case OIND: | 391 case OIND: |
506 case ODOTPTR: | 392 case ODOTPTR: |
507 case OSLICEARR: // ->left is the OADDR of the array | 393 case OSLICEARR: // ->left is the OADDR of the array |
508 doref: | 394 doref: |
509 escexpr(N, dst->left); | 395 escexpr(N, dst->left); |
510 // assignment to dereferences: for now we lose track | 396 // assignment to dereferences: for now we lose track |
511 escexpr(&theSink, expr); | 397 escexpr(&theSink, expr); |
512 return; | 398 return; |
513 | 399 » » } |
514 » » case ONAME: | 400 |
515 » » case OARRAYLIT: | 401 » } |
516 » » case OSTRUCTLIT: | 402 |
517 » » case OMAPLIT: | 403 » if(expr == N || expr->op == ONONAME || expr->op == OXXX) |
518 » » » break; | |
519 | |
520 » » » // these can happen in the OIND recursion | |
521 » » case OADDR: | |
522 » » case OCONVNOP: | |
523 » » » break; | |
524 » » } | |
525 | |
526 » } | |
527 | |
528 » if(expr == N || expr->op == ONONAME || expr->op == OXXX) { | |
529 return; | 404 return; |
530 » } | 405 |
531 if(expr->typecheck == 0 && expr->op != OKEY) { | 406 if(expr->typecheck == 0 && expr->op != OKEY) { |
532 dump("escexpr missing typecheck", expr); | 407 dump("escexpr missing typecheck", expr); |
533 fatal("Missing typecheck."); | 408 fatal("Missing typecheck."); |
534 } | 409 } |
535 | 410 |
536 lno = setlineno(expr); | 411 lno = setlineno(expr); |
537 pdepth++; | 412 pdepth++; |
538 | 413 |
539 if(debug['m'] > 1) | 414 if(debug['m'] > 1) |
540 » » print("%L:[%d] %#S \t%hN %.*s<= %hN\n", lineno, loopdepth, (curf
n && curfn->nname) ? curfn->nname->sym : S, dst, 2*pdepth, ".\t.\t.\t.\t.\t", ex
pr); | 415 » » print("%L:[%d] %#S \t%hN %.*s<= %hN\n", lineno, loopdepth, |
| 416 » » (curfn && curfn->nname) ? curfn->nname->sym : S, dst, |
| 417 » » 2*pdepth, ".\t.\t.\t.\t.\t", expr); |
541 | 418 |
542 | 419 |
543 switch(expr->op) { | 420 switch(expr->op) { |
544 » case OADDR:» // dst = &x | 421 » case OADDR:» // dst = &x |
545 » » escflows(dst, expr); | |
546 » » escexpr(expr->left, expr->left); // figure out where x came fro
m | |
547 » » break; | |
548 | |
549 case OIND: // dst = *x | 422 case OIND: // dst = *x |
550 case ODOTPTR: // dst = (*x).f | 423 case ODOTPTR: // dst = (*x).f |
551 » » escflows(dst, expr); | 424 » » // restart the recursion at x to figure out where it came from |
552 » » escexpr(expr->left, expr->left); // figure out where x came from | 425 » » escexpr(expr->left, expr->left); |
553 » » break; | 426 » » // fallthrough |
554 | |
555 case ONAME: | 427 case ONAME: |
556 case OPARAM: | 428 case OPARAM: |
557 // loopdepth was set in the defining statement or function heade
r | 429 // loopdepth was set in the defining statement or function heade
r |
558 escflows(dst, expr); | 430 escflows(dst, expr); |
559 break; | 431 break; |
560 | 432 |
561 case OARRAYLIT: | 433 case OARRAYLIT: |
562 case OSTRUCTLIT: | 434 case OSTRUCTLIT: |
563 expr->loopdepth = loopdepth; | |
564 escflows(dst, expr); | |
565 // ll->n is key, left is fieldname or constant index | |
566 for(ll=expr->list; ll; ll=ll->next) | |
567 escexpr(expr, ll->n->right); | |
568 break; | |
569 | |
570 case OMAPLIT: | 435 case OMAPLIT: |
571 » » expr->loopdepth = loopdepth; | 436 » » expr->escloopdepth = loopdepth; |
572 escflows(dst, expr); | 437 escflows(dst, expr); |
573 for(ll=expr->list; ll; ll=ll->next) { | 438 for(ll=expr->list; ll; ll=ll->next) { |
574 escexpr(expr, ll->n->left); | 439 escexpr(expr, ll->n->left); |
575 escexpr(expr, ll->n->right); | 440 escexpr(expr, ll->n->right); |
576 } | 441 } |
577 break; | 442 break; |
578 | 443 |
579 case OMAKECHAN: | 444 case OMAKECHAN: |
580 case OMAKEMAP: | 445 case OMAKEMAP: |
581 case OMAKESLICE: | 446 case OMAKESLICE: |
582 case ONEW: | 447 case ONEW: |
583 expr->curfn = curfn; // should have been done in parse, but pat
ch it up here. | 448 expr->curfn = curfn; // should have been done in parse, but pat
ch it up here. |
584 » » expr->loopdepth = loopdepth; | 449 » » expr->escloopdepth = loopdepth; |
585 escflows(dst, expr); | 450 escflows(dst, expr); |
586 // first arg is type, all others need checking | 451 // first arg is type, all others need checking |
587 for(ll=expr->list->next; ll; ll=ll->next) | 452 for(ll=expr->list->next; ll; ll=ll->next) |
588 escexpr(N, ll->n); | 453 escexpr(N, ll->n); |
589 break; | 454 break; |
590 | 455 |
591 case OCLOSURE: | 456 case OCLOSURE: |
592 expr->curfn = curfn; // should have been done in parse, but pat
ch it up here. | 457 expr->curfn = curfn; // should have been done in parse, but pat
ch it up here. |
593 » » expr->loopdepth = loopdepth; | 458 » » expr->escloopdepth = loopdepth; |
594 escflows(dst, expr); | 459 escflows(dst, expr); |
595 escfunc(expr); | 460 escfunc(expr); |
596 break; | 461 break; |
597 | 462 |
598 // end of the leaf cases. no calls to escflows() in the cases below. | 463 // end of the leaf cases. no calls to escflows() in the cases below. |
599 | 464 |
600 » case OCOMPLIT: | 465 |
601 » case OLITERAL: | 466 » case OCONV:» // unaries that pass the value through |
602 » case ORECOVER: | |
603 » » break; | |
604 | |
605 » » // harmless unary | |
606 » case ONOT: | |
607 » case OCOM: | |
608 » case OCAP: | |
609 » case OLEN: | |
610 » case OREAL: | |
611 » case OIMAG: | |
612 » » // array/string conversions make copies | |
613 » case OARRAYBYTESTR: | |
614 » case OARRAYRUNESTR: | |
615 » case OSTRARRAYBYTE: | |
616 » case OSTRARRAYRUNE: | |
617 » case ORUNESTR: | |
618 » » escexpr(N, expr->left); | |
619 » » break; | |
620 | |
621 » » // harmless binary | |
622 » case OADD: | |
623 » case OAND: | |
624 » case OANDAND: | |
625 » case OANDNOT: | |
626 » case ODIV: | |
627 » case OEQ: | |
628 » case OGE: | |
629 » case OGT: | |
630 » case OLE: | |
631 » case OLT: | |
632 » case OLSH: | |
633 » case ORSH: | |
634 » case OMOD: | |
635 » case OMUL: | |
636 » case ONE: | |
637 » case OOR: | |
638 » case OOROR: | |
639 » case OSUB: | |
640 » case OXOR: | |
641 » case OADDSTR: | |
642 » case OASOP: | |
643 » case OCMPIFACE: | |
644 » case OCMPSTR: | |
645 » case OCOMPLEX: | |
646 » case OPLUS: | |
647 » case OMINUS: | |
648 » » escexpr(N, expr->left); | |
649 » » escexpr(N, expr->right); | |
650 » » break; | |
651 | |
652 » » // unaries that pass the value through | |
653 » case OCONV: | |
654 case OCONVIFACE: | 467 case OCONVIFACE: |
655 case OCONVNOP: | 468 case OCONVNOP: |
656 case ODOTTYPE: | 469 case ODOTTYPE: |
657 case ODOTTYPE2: | 470 case ODOTTYPE2: |
658 » case ORECV: // leaks the whole channel | 471 » case ORECV:» // leaks the whole channel |
| 472 » case ODOTMETH:» // expr->right is just the field or method name |
| 473 » case ODOTINTER: |
| 474 » case ODOT: |
659 escexpr(dst, expr->left); | 475 escexpr(dst, expr->left); |
660 break; | 476 break; |
661 | 477 |
662 case OCOPY: | 478 case OCOPY: |
663 // left leaks to right, but the return value is harmless | 479 // left leaks to right, but the return value is harmless |
664 // TODO: treat as *dst = *src, rather than as dst = src | 480 // TODO: treat as *dst = *src, rather than as dst = src |
665 escexpr(expr->left, expr->right); | 481 escexpr(expr->left, expr->right); |
666 break; | 482 break; |
667 | 483 |
668 case OAPPEND: | 484 case OAPPEND: |
669 // See TODO for OCOPY | 485 // See TODO for OCOPY |
670 escexpr(dst, expr->list->n); | 486 escexpr(dst, expr->list->n); |
671 for(ll=expr->list->next; ll; ll=ll->next) | 487 for(ll=expr->list->next; ll; ll=ll->next) |
672 escexpr(expr->list->n, ll->n); | 488 escexpr(expr->list->n, ll->n); |
673 break; | 489 break; |
674 | 490 |
675 case OCALLMETH: | 491 case OCALLMETH: |
676 case OCALLFUNC: | 492 case OCALLFUNC: |
677 case OCALLINTER: | 493 case OCALLINTER: |
678 // Moved to separate function to isolate the hair. | 494 // Moved to separate function to isolate the hair. |
679 escexprcall(dst, expr); | 495 escexprcall(dst, expr); |
680 break; | 496 break; |
681 | 497 |
682 case ODOTMETH: | |
683 case ODOTINTER: | |
684 case ODOT: | |
685 // expr->right is just the field or method name | |
686 escexpr(dst, expr->left); | |
687 break; | |
688 | |
689 case OSLICEARR: // like an implicit OIND to the underlying buffer, but
typecheck has inserted an OADDR | 498 case OSLICEARR: // like an implicit OIND to the underlying buffer, but
typecheck has inserted an OADDR |
690 case OSLICESTR: | 499 case OSLICESTR: |
691 case OSLICE: | 500 case OSLICE: |
| 501 case OINDEX: |
| 502 case OINDEXMAP: |
692 // the big thing flows, the keys just need checking | 503 // the big thing flows, the keys just need checking |
693 escexpr(dst, expr->left); | 504 escexpr(dst, expr->left); |
694 escexpr(N, expr->right); // expr->right is the OKEY | 505 escexpr(N, expr->right); // expr->right is the OKEY |
695 break; | 506 break; |
696 | 507 |
697 » case OKEY: | 508 » default: // all other harmless leaf, unary or binary cases end up here |
698 escexpr(N, expr->left); | 509 escexpr(N, expr->left); |
699 escexpr(N, expr->right); | 510 escexpr(N, expr->right); |
700 break; | 511 break; |
701 | |
702 case OINDEX: | |
703 case OINDEXMAP: | |
704 escexpr(dst, expr->left); | |
705 escexpr(N, expr->right); | |
706 break; | |
707 | |
708 default: | |
709 // if we get here, typecheck has probably already complained | |
710 fatal("escexpr ... <- %O", expr->op); | |
711 break; | |
712 } | 512 } |
713 | 513 |
714 pdepth--; | 514 pdepth--; |
715 lineno = lno; | 515 lineno = lno; |
716 } | 516 } |
717 | 517 |
| 518 |
| 519 // This is a bit messier than fortunate, pulled out of escexpr's big |
| 520 // switch for clarity. We either have the paramnodes, which may be |
| 521 // connected to other things throug flows or we have the parameter type |
| 522 // nodes, which may be marked 'n(ofloworescape)'. Navigating the ast is slightly |
| 523 // different for methods vs plain functions and for imported vs |
| 524 // this-package |
| 525 static void |
| 526 escexprcall(Node *dst, Node *expr) |
| 527 { |
| 528 NodeList *ll, *lr; |
| 529 Node *fn; |
| 530 Type *t, *fntype, *thisarg, *inargs; |
| 531 |
| 532 fn = nil; |
| 533 fntype = nil; |
| 534 |
| 535 switch(expr->op) { |
| 536 case OCALLFUNC: |
| 537 fn = expr->left; |
| 538 escexpr(N, fn); |
| 539 fntype = fn->type; |
| 540 break; |
| 541 |
| 542 case OCALLMETH: |
| 543 fn = expr->left->right; // ODOTxx name |
| 544 fn = fn->sym->def; // resolve to definition if we have it |
| 545 if(fn) |
| 546 fntype = fn->type; |
| 547 else |
| 548 fntype = expr->left->type; |
| 549 break; |
| 550 |
| 551 case OCALLINTER: |
| 552 break; |
| 553 |
| 554 default: |
| 555 fatal("escexprcall called with non-call expression"); |
| 556 } |
| 557 |
| 558 if(fn && fn->ntype) { |
| 559 if(debug['m'] > 2) |
| 560 print("escexprcall: have param nodes: %N\n", fn->ntype); |
| 561 |
| 562 if(expr->op == OCALLMETH) { |
| 563 if(debug['m'] > 2) |
| 564 print("escexprcall: this: %N\n",fn->ntype->left-
>left); |
| 565 escexpr(fn->ntype->left->left, expr->left->left); |
| 566 } |
| 567 |
| 568 // lr->n is the dclfield, ->left is the ONAME param node |
| 569 for(ll=expr->list, lr=fn->ntype->list; ll && lr; ll=ll->next) { |
| 570 if(debug['m'] > 2) |
| 571 print("escexprcall: field param: %N\n", lr->n->l
eft); |
| 572 if (lr->n->left) |
| 573 escexpr(lr->n->left, ll->n); |
| 574 else |
| 575 escexpr(&theSink, ll->n); |
| 576 if(lr->n->left && !lr->n->left->isddd) |
| 577 lr=lr->next; |
| 578 } |
| 579 return; |
| 580 } |
| 581 |
| 582 if(fntype) { |
| 583 if(debug['m'] > 2) |
| 584 print("escexprcall: have param types: %T\n", fntype); |
| 585 |
| 586 if(expr->op == OCALLMETH) { |
| 587 thisarg = getthisx(fntype); |
| 588 t = thisarg->type; |
| 589 if(debug['m'] > 2) |
| 590 print("escexprcall: this: %T\n", t); |
| 591 if(!t->note || strcmp(t->note->s, safetag->s) != 0) |
| 592 escexpr(&theSink, expr->left->left); |
| 593 else |
| 594 escexpr(N, expr->left->left); |
| 595 } |
| 596 |
| 597 inargs = getinargx(fntype); |
| 598 for(ll=expr->list, t=inargs->type; ll; ll=ll->next) { |
| 599 if(debug['m'] > 2) |
| 600 print("escexprcall: field type: %T\n", t); |
| 601 if(!t->note || strcmp(t->note->s, safetag->s)) |
| 602 escexpr(&theSink, ll->n); |
| 603 else |
| 604 escexpr(N, ll->n); |
| 605 if(t->down) |
| 606 t=t->down; |
| 607 } |
| 608 |
| 609 return; |
| 610 } |
| 611 |
| 612 // fallthrough if we don't have enough information: |
| 613 // can only assume all parameters are unsafe |
| 614 // OCALLINTER always ends up here |
| 615 |
| 616 if(debug['m']>1 && expr->op != OCALLINTER) { |
| 617 // dump("escexprcall", expr); |
| 618 print("escexprcall: %O, no nodes, no types: %N\n", expr->op, fn)
; |
| 619 } |
| 620 |
| 621 escexpr(&theSink, expr->left->left); // the this argument |
| 622 for(ll=expr->list; ll; ll=ll->next) |
| 623 escexpr(&theSink, ll->n); |
| 624 } |
| 625 |
| 626 // Store the link src->dst in dst, throwing out some quick wins. |
718 static void | 627 static void |
719 escflows(Node* dst, Node* src) | 628 escflows(Node* dst, Node* src) |
720 { | 629 { |
721 if(dst == nil || src == nil || dst == src) | 630 if(dst == nil || src == nil || dst == src) |
722 return; | 631 return; |
723 | 632 |
724 // Don't bother building a graph for scalars. | 633 // Don't bother building a graph for scalars. |
725 if (src->type && !haspointers(src->type)) | 634 if (src->type && !haspointers(src->type)) |
726 return; | 635 return; |
727 | 636 |
728 if(debug['m']>2) | 637 if(debug['m']>2) |
729 print("%L::flows:: %hN <- %hN\n", lineno, dst, src); | 638 print("%L::flows:: %hN <- %hN\n", lineno, dst, src); |
730 | 639 |
731 // Assignments to global variables get lumped into theSink. | 640 // Assignments to global variables get lumped into theSink. |
732 if (dst->op == ONAME && dst->class == PEXTERN) | 641 if (dst->op == ONAME && dst->class == PEXTERN) |
733 dst = &theSink; | 642 dst = &theSink; |
734 | 643 |
735 » if (dst->flowsrc == nil) { | 644 » if (dst->escflowsrc == nil) { |
736 dsts = list(dsts, dst); | 645 dsts = list(dsts, dst); |
737 dstcount++; | 646 dstcount++; |
738 } | 647 } |
739 edgecount++; | 648 edgecount++; |
740 | 649 |
741 » dst->flowsrc = list(dst->flowsrc, src); | 650 » dst->escflowsrc = list(dst->escflowsrc, src); |
742 } | 651 } |
743 | 652 |
744 // Whenever we hit a reference node, the level goes up by one, and whenever | 653 // Whenever we hit a reference node, the level goes up by one, and whenever |
745 // we hit an OADDR, the level goes down by one. as long as we're on a level > 0 | 654 // we hit an OADDR, the level goes down by one. as long as we're on a level > 0 |
746 // finding an OADDR just means we're following the upstream of a dereference, | 655 // finding an OADDR just means we're following the upstream of a dereference, |
747 // so this address doesn't leak (yet). | 656 // so this address doesn't leak (yet). |
748 // If level == 0, it means the /value/ of this node can reach the root of this f
lood. | 657 // If level == 0, it means the /value/ of this node can reach the root of this f
lood. |
749 // so if this node is an OADDR, it's argument should be marked as escaping iff | 658 // so if this node is an OADDR, it's argument should be marked as escaping iff |
750 // it's currfn/loopdepth are different from the flood's root. | 659 // it's currfn/loopdepth are different from the flood's root. |
751 // Once an object has been moved to the heap, all of it's upstream should be con
sidered | 660 // Once an object has been moved to the heap, all of it's upstream should be con
sidered |
752 // escaping to the global scope. | 661 // escaping to the global scope. |
753 static void | 662 static void |
754 escflood(Node *dst) | 663 escflood(Node *dst) |
755 { | 664 { |
756 NodeList *l; | 665 NodeList *l; |
757 | 666 |
758 » if (dst->op != ONAME) | 667 » switch(dst->op) { |
| 668 » case ONAME: |
| 669 » case OCLOSURE: |
| 670 » » break; |
| 671 » default: |
759 return; | 672 return; |
| 673 } |
760 | 674 |
761 if(debug['m']>1) | 675 if(debug['m']>1) |
762 print("\nescflood:%d: dst %hN scope:%#S[%d]\n", floodgen, dst, | 676 print("\nescflood:%d: dst %hN scope:%#S[%d]\n", floodgen, dst, |
763 (dst->curfn && dst->curfn->nname) ? dst->curfn->nname->sym
: S, | 677 (dst->curfn && dst->curfn->nname) ? dst->curfn->nname->sym
: S, |
764 » » dst->loopdepth); | 678 » » dst->escloopdepth); |
765 | 679 |
766 » for (l = dst->flowsrc; l; l=l->next) { | 680 » for (l = dst->escflowsrc; l; l=l->next) { |
767 floodgen++; | 681 floodgen++; |
768 escwalk(0, dst, l->n); | 682 escwalk(0, dst, l->n); |
769 } | 683 } |
770 } | 684 } |
771 | 685 |
772 static void | 686 static void |
773 escwalk(int level, Node *dst, Node *src) | 687 escwalk(int level, Node *dst, Node *src) |
774 { | 688 { |
775 NodeList* ll; | 689 NodeList* ll; |
776 int leaks; | 690 int leaks; |
777 | 691 |
778 » if (src->floodgen == floodgen) | 692 » if (src->escfloodgen == floodgen) |
779 return; | 693 return; |
780 » src->floodgen = floodgen; | 694 » src->escfloodgen = floodgen; |
781 | 695 |
782 if(debug['m']>1) | 696 if(debug['m']>1) |
783 print("escwalk: level:%d depth:%d %.*s %hN scope:%#S[%d]\n", | 697 print("escwalk: level:%d depth:%d %.*s %hN scope:%#S[%d]\n", |
784 level, pdepth, pdepth, "\t\t\t\t\t\t\t\t\t\t", src, | 698 level, pdepth, pdepth, "\t\t\t\t\t\t\t\t\t\t", src, |
785 » » (src->curfn && src->curfn->nname) ? src->curfn->nname->sym
: S, src->loopdepth); | 699 » » (src->curfn && src->curfn->nname) ? src->curfn->nname->sym
: S, src->escloopdepth); |
786 | 700 |
787 pdepth++; | 701 pdepth++; |
788 | 702 |
789 » leaks = (level <= 0) && (dst->loopdepth < src->loopdepth); | 703 » leaks = (level <= 0) && (dst->escloopdepth < src->escloopdepth); |
790 | 704 |
791 switch(src->op) { | 705 switch(src->op) { |
792 case ONAME: | 706 case ONAME: |
793 » » switch(src->class) { | 707 » » if (src->class == PPARAM && leaks && src->esc == EscNone) { |
794 » » case PPARAMREF: | 708 » » » src->esc = EscScope; |
795 » » » // handle implicit OADDR that walk will insert later | 709 » » » if(debug['m']) |
796 » » » if (leaks) | 710 » » » » print("%L:leaking param: %hN\n", src->lineno, sr
c); |
797 » » » » addrescapes(src->closure); | |
798 » » » escwalk(level-1, dst, src->closure); | |
799 » » » break; | |
800 | |
801 » » case PPARAM: | |
802 » » » if (leaks && src->esc == EscNone) { | |
803 » » » » src->esc = EscScope; | |
804 » » » » if(debug['m']) | |
805 » » » » » print("%L:leaking param: %hN\n", src->li
neno, src); | |
806 » » » } | |
807 » » } | |
808 | |
809 » » break; | |
810 | |
811 » case OCLOSURE: | |
812 » » // PPARAMOUT have been linked to the sink explicitly, | |
813 » » // handle closure vars, in-parameters and local variables here. | |
814 » » for(ll=src->dcl; ll; ll=ll->next) | |
815 » » » if(ll->n->class == PPARAM || ll->n->class == PAUTO) | |
816 » » » » escwalk(0, dst, ll->n); | |
817 | |
818 » » // the cvars will have their address taken later by walk. | |
819 » » for(ll=src->cvars; ll; ll=ll->next) { | |
820 » » » if(ll->n->op == OXXX) // see dcl.c:398 | |
821 » » » » continue; | |
822 » » » if (leaks) | |
823 » » » » addrescapes(ll->n); | |
824 » » » escwalk(0, dst, ll->n); | |
825 } | 711 } |
826 break; | 712 break; |
827 | 713 |
828 case OADDR: | 714 case OADDR: |
829 if (leaks) | 715 if (leaks) |
830 addrescapes(src->left); | 716 addrescapes(src->left); |
831 escwalk(level-1, dst, src->left); | 717 escwalk(level-1, dst, src->left); |
832 break; | 718 break; |
833 | 719 |
834 case OINDEX: | 720 case OINDEX: |
835 if(isfixedarray(src->type)) | 721 if(isfixedarray(src->type)) |
836 break; | 722 break; |
837 case OSLICE: | 723 case OSLICE: |
838 case ODOTPTR: | 724 case ODOTPTR: |
839 case OINDEXMAP: | 725 case OINDEXMAP: |
840 case OIND: | 726 case OIND: |
841 escwalk(level+1, dst, src->left); | 727 escwalk(level+1, dst, src->left); |
842 } | 728 } |
843 | 729 |
844 » for (ll=src->flowsrc; ll; ll=ll->next) | 730 » for (ll=src->escflowsrc; ll; ll=ll->next) |
845 escwalk(level, dst, ll->n); | 731 escwalk(level, dst, ll->n); |
846 | 732 |
847 pdepth--; | 733 pdepth--; |
848 } | 734 } |
849 | 735 |
850 static void | 736 static void |
851 esctag(Node *func) | 737 esctag(Node *func) |
852 { | 738 { |
853 Node *savefn; | 739 Node *savefn; |
854 NodeList *ll; | 740 NodeList *ll; |
(...skipping 12 matching lines...) Expand all Loading... |
867 case EscHeap: // touched by escflood, moved to heap | 753 case EscHeap: // touched by escflood, moved to heap |
868 case EscScope: // touched by escflood, value leaves scope | 754 case EscScope: // touched by escflood, value leaves scope |
869 break; | 755 break; |
870 default: | 756 default: |
871 fatal("messed up escape tagging: %N::%N", curfn, ll->n); | 757 fatal("messed up escape tagging: %N::%N", curfn, ll->n); |
872 } | 758 } |
873 } | 759 } |
874 | 760 |
875 curfn = savefn; | 761 curfn = savefn; |
876 } | 762 } |
LEFT | RIGHT |