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 // Theory: | 5 // The base version before this file existed, active with debug['s'] |
6 // Note: this is an attempt at something more general than I actually do below. | 6 // == 0, assumes any node that has a reference to it created at some |
| 7 // point, may flow to the global scope except |
| 8 // - if its address is dereferenced immediately with only CONVNOPs in |
| 9 // between the * and the & |
| 10 // - if it is for a closure variable and the closure executed at the |
| 11 // place it's defined |
7 // | 12 // |
8 // A subset of all AST nodes holds values. These are the | 13 // Flag -s disables the old codepaths and switches on the code here: |
9 // 'expression' nodes. The scope of the expression node is either | |
10 // 'global' or the function it is defined in. | |
11 // | 14 // |
12 // An expression node src can 'flow' to another node dst. | 15 // First escfunc, escstmt and escexpr recurse over the ast of each |
13 // Define "flow(dst, src)" iff at one point in the programs execution: | 16 // function to dig out flow(dst,src) edges between any |
14 // dst = src - dst is a scalar, struct, array, map or chan, and the valu
e of src is assigned to dst | 17 // pointer-containing nodes and store them in dst->escflowsrc. For |
15 // dst.elem = src - dst is a struct and the value of src is assigned to one o
f its fields | 18 // variables assigned to a variable in an outer scope or used as a |
16 // dst[x] = src - dst is an array or map and the value of src is assigned t
o one of its elements | 19 // return value, they store a flow(theSink, src) edge to a fake node |
17 // dst[src] = x - dst is a map and the value of src is used as a key | 20 // 'the Sink'.» For variables referenced in closures, an edge |
18 // dst <- src - dst is a channel and src is sent on it. | 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. |
19 // | 23 // |
20 // Define flow*(dst, src) as "flow(dst, src) or there is an intermediary | 24 // Then escflood walks the graph starting at theSink and tags all |
21 // Node n such that flow(dst, n) && flow*(n, src)", i.e. if there is a | 25 // variables of it can reach an & node as escaping and all function |
22 // path trough the directed graph of nodes connected by flow. | 26 // parameters it can reach as leaking. |
23 // | 27 // |
24 // If ref is a pointer or a slice element, consider | 28 // Watch the variables moved to the heap and parameters tagged as |
25 // ref = &dst ref = dst[b:e] | 29 // unsafe with -m, more detailed analysis output with -mm |
26 // ref1 = ref ref1 = ref | |
27 // ref2 = ref or ref2 = ref | |
28 // ref3 = ref ref3 = ref | |
29 // dst1 = *ref1 dst1 = ref1[y] | |
30 // *ref2 = src ref2[x] = src | |
31 // dst3 = *ref3 dst3 = ref3[y] | |
32 // | 30 // |
33 // The graph we want to end up with looks like this: | |
34 // | |
35 // src --> * <~> ref2 <~> ref <~> & <~> dst | |
36 // ^ | |
37 // dst1 <-- * <~> ref1 <~ + | |
38 // dst3 <-- * <~> ref3 <~ + | |
39 // | |
40 // resp.: | |
41 // | |
42 // src --> [x] <~> ref2 <~> ref <~> [b:e] <~> dst | |
43 // ^ | |
44 // dst1 <-- [y] <~> ref1 <~ + | |
45 // dst3 <-- [y] <~> ref3 <~ + | |
46 | |
47 // | |
48 // The <~> has to be a new kind of edge, and it has to be symmetric, | |
49 // otherwise the src can not end up in dst3 (even if that leads to a | |
50 // spurious flow(dst1, src), but that can't be helped because we don't | |
51 // want to consider the order of execution). | |
52 //· | |
53 // Call this symmetric relation "alias(p, q)", iff (...alias(q, p) or..) | |
54 // - p is an OIND and q is it's argument | |
55 // - p is of pointer type (including an OADDR) and it is assigned to q | |
56 // - p is an OADDR and q is its argument | |
57 // - p is an array and q is an OSLICE or OINDEX of it | |
58 // - p is of slice type and q is assigned to it | |
59 // ? is that it? | |
60 // | |
61 // The alias*(p,q) between two nodes is then defined as the transitive closure o
f | |
62 // alias again, and flow(src, dst) needs to be extended with: | |
63 // - there is a q, such that flow(q, src) and alias*(q, dst) | |
64 // | |
65 // At the same time, &(dst) is a value, and it flows in the first sense: | |
66 // | |
67 // * <-- ref2 <-- ref <-- &(dst) | |
68 // ^ | |
69 // * <-- ref1 <-- + | |
70 // * <-- ref3 <-- + | |
71 // | |
72 // That * has to be connected is clear when we add | |
73 // ref4 = &*ref3. | |
74 // Another interesting case is dst = *(cast(&src)), which for | |
75 // the purposes of this discussion looks like dst = * & src | |
76 // (the cast just paints the ref in the middle a different color): | |
77 // dst <-- * <~> ref <~> & <~> src | |
78 // * <-- ref <-- &(src) | |
79 // | |
80 // That is, the value of src leaks to dst, but the value of its address does no
t. | |
81 // | |
82 // what about· | |
83 // ref = &dst | |
84 // ref2 = ref | |
85 // ref2 = &dst2 | |
86 // | |
87 // dst2 = src that should not affect ref, or dest | |
88 // | |
89 // | |
90 // Now that we have a definition of flow* between nodes, we can define "leak(no
de)": | |
91 // a node n leaks the current function if | |
92 // - it is a global variable | |
93 // - it is an output variable for the function, or a node in the return expre
ssion list | |
94 // - it is used inside a go statement | |
95 // - there is another node m such that flow(m, n) and m leaks the current fun
ction· | |
96 // - there is another node m such that flow(m, n) and m is the argument to a | |
97 // function that itself leaks from /that/ function | |
98 //· | |
99 // A variable must be moved to the heap (from the stack) if it's *address* leak
s, meaning | |
100 // - the address is taken (with the unary-& operator OADDR) | |
101 // - AND this (pointer) value leaks outside the current function scope. | |
102 // | |
103 | 31 |
104 #include "go.h" | 32 #include "go.h" |
105 | 33 |
106 static void eastmtlist(Node* dst, NodeList *l); | 34 static void escfunc(Node *func); |
107 static void eastmt(Node* dst, Node *stmt); | 35 static void escstmtlist(NodeList *stmts); |
108 static void eaexpr(Node *dst, Node *expr); | 36 static void escstmt(Node *stmt); |
109 static void eawalk(Node *sink); | 37 static void escexpr(Node *dst, Node *expr); |
110 | 38 static void escexprcall(Node *dst, Node *callexpr); |
111 // Fake node that all return values, global variables and | 39 static void escflows(Node* dst, Node* src); |
112 // goroutine-used variables leak to. | 40 static void escflood(Node *dst); |
113 static Node theSink; | 41 static void escwalk(int level, Node *dst, Node *src); |
| 42 static void esctag(Node *func); |
| 43 |
| 44 // Fake node that all |
| 45 // - return values and output variables |
| 46 // - parameters on imported functions not marked 'safe' |
| 47 // - assignments to global variables |
| 48 // flow to. |
| 49 static Node» theSink; |
| 50 |
| 51 static NodeList* dsts;» » // all dst nodes |
| 52 static int» loopdepth;» // for detecting nested loop scopes |
| 53 static int» pdepth;»» // for debug printing in recursions. |
| 54 static int» floodgen;» // loop prevention in flood/walk |
| 55 static Strlit*» safetag;» // gets slapped on safe parameters' field types
for export |
| 56 static int» dstcount, edgecount;» // diagnostic |
114 | 57 |
115 void | 58 void |
116 escanalfunc(Node *func) | 59 escapes(void) |
117 { | 60 { |
| 61 » NodeList *l; |
| 62 |
| 63 » theSink.op = ONAME; |
| 64 » theSink.class = PEXTERN; |
| 65 » theSink.sym = lookup(".sink"); |
| 66 » theSink.escloopdepth = -1; |
| 67 |
| 68 » safetag = strlit("noescape"); |
| 69 |
| 70 » // flow-analyze top level functions |
| 71 » for(l=xtop; l; l=l->next) |
| 72 » » if(l->n->op == ODCLFUNC || l->n->op == OCLOSURE) |
| 73 » » » escfunc(l->n); |
| 74 |
| 75 » // print("escapes: %d dsts, %d edges\n", dstcount, edgecount); |
| 76 |
| 77 » // visit the updstream of each dst, mark address nodes with |
| 78 » // addrescapes, mark parameters unsafe |
| 79 » for (l = dsts; l; l=l->next) |
| 80 » » escflood(l->n); |
| 81 |
| 82 » // for all top level functions, tag the typenodes corresponding to the p
aram nodes |
| 83 » for(l=xtop; l; l=l->next) |
| 84 » » if(l->n->op == ODCLFUNC) |
| 85 » » » esctag(l->n); |
| 86 } |
| 87 |
| 88 static void |
| 89 escfunc(Node *func) |
| 90 { |
| 91 » Node *savefn, *n; |
| 92 » NodeList *ll; |
| 93 » int saveld; |
| 94 |
| 95 » saveld = loopdepth; |
| 96 » loopdepth = 1; |
| 97 » savefn = curfn; |
118 curfn = func; | 98 curfn = func; |
119 » eastmtlist(N, func->nbody); | 99 |
120 » curfn = nil; | 100 » for(ll=curfn->dcl; ll; ll=ll->next) { |
121 } | 101 » » if(ll->n->op != ONAME) |
122 | 102 » » » continue; |
123 void | 103 » » switch (ll->n->class) { |
124 escanalfinish() | 104 » » case PPARAMOUT: |
125 { | 105 » » » // output parameters flow to the sink |
126 » eawalk(&theSink); | 106 » » » escflows(&theSink, ll->n); |
127 } | 107 » » » ll->n->escloopdepth = loopdepth; |
128 | 108 » » » break; |
129 | 109 » » case PPARAM: |
130 static void eawalk(Node *sink) { | 110 » » » ll->n->esc = EscNone;» // prime for escflood later |
131 » NodeList *ll; | 111 » » » ll->n->escloopdepth = loopdepth; |
132 »······· | 112 » » » break; |
133 » ll = sink->asrc; | 113 » » } |
134 » sink->asrc = nil; | 114 » } |
135 » for (; ll; ll=ll->next) { | 115 |
136 » » eawalk(ll->n); | 116 » // walk will take the address of cvar->closure later and assign it to cv
ar. |
137 » » if (ll->n->op == OADDR) { | 117 » // handle that here by linking a fake oaddr node directly to the closure
. |
138 » » » addrescapes(ll->n->left); | 118 » for (ll=curfn->cvars; ll; ll=ll->next) { |
139 » » » eawalk(ll->n->left); | 119 » » if(ll->n->op == OXXX) // see dcl.c:398 |
140 » » } | 120 » » » continue; |
141 » } | 121 |
142 } | 122 » » n = nod(OADDR, ll->n->closure, N); |
143 | 123 » » n->lineno = ll->n->lineno; |
144 static void | 124 » » typecheck(&n, Erv); |
145 eastmtlist(Node* dst, NodeList* l) | 125 » » escexpr(curfn, n); |
146 { | 126 » } |
147 » for (; l; l = l->next) | 127 |
148 » » eastmt(dst, l->n); | 128 » escstmtlist(curfn->nbody); |
149 } | 129 » curfn = savefn; |
150 | 130 » loopdepth = saveld; |
151 | 131 } |
152 static void | 132 |
153 eastmt(Node* fun, Node *stmt) | 133 static void |
154 { | 134 escstmtlist(NodeList* stmts) |
155 » int cl, cr; | 135 { |
| 136 » for(; stmts; stmts=stmts->next) |
| 137 » » escstmt(stmts->n); |
| 138 } |
| 139 |
| 140 static void |
| 141 escstmt(Node *stmt) |
| 142 { |
| 143 » int cl, cr, lno; |
156 NodeList *ll, *lr; | 144 NodeList *ll, *lr; |
| 145 Node *dst; |
157 | 146 |
158 if(stmt == N) | 147 if(stmt == N) |
159 return; | 148 return; |
160 | 149 |
161 » if (stmt->trecur) | 150 » lno = setlineno(stmt); |
162 » » return; | 151 |
163 » stmt->trecur = 1; | 152 » if(stmt->typecheck == 0 && stmt->op != ODCL) {» // TODO something with
OAS2 |
164 | 153 » » dump("escstmt missing typecheck", stmt); |
165 » setlineno(stmt); | 154 » » fatal("missing typecheck."); |
166 | 155 » } |
167 » if (stmt->typecheck == 0 && stmt->op != ODCL) { // todo, something with
OAS2 | 156 |
168 » » dump("eastmt missing typecheck", stmt); | 157 » // Common to almost all statements, and nil if n/a. |
169 » » fatal("Missing typecheck."); | 158 » escstmtlist(stmt->ninit); |
170 » } | 159 |
171 | 160 » if(debug['m'] > 1) |
172 » if (fun && fun->op != OCLOSURE) fatal("eastmt fun is not a closure: %N",
fun); | 161 » » print("%L:[%d] %#S statement: %#N\n", lineno, loopdepth, |
| 162 » » (curfn && curfn->nname) ? curfn->nname->sym : S, stmt); |
173 | 163 |
174 switch(stmt->op) { | 164 switch(stmt->op) { |
175 default: | |
176 // things i haven't thought of | |
177 dump("eastmt", stmt); | |
178 // fallthrough | |
179 // things that shouldn't happen here | |
180 case OCASE: case OXCASE: | |
181 case OFALL: | |
182 case OCALL: | |
183 fatal("eastmt %O", stmt->op); | |
184 break; | |
185 | |
186 case ODCL: | 165 case ODCL: |
187 case ODCLFIELD: | 166 case ODCLFIELD: |
188 » » // a declaration ties the node to the current function | 167 » » // a declaration ties the node to the current |
189 » » eaexpr(fun, stmt->left); | 168 » » // function, but we already have that edge in |
190 » » break; | 169 » » // curfn->dcl and will follow it explicitly in |
191 | 170 » » // escflood to avoid storing redundant information |
192 » case ODCLCONST: | 171 » » // What does have to happen here is note if the name |
193 » case ODCLTYPE: | 172 » » // is declared inside a looping scope. |
194 » case OBREAK: | 173 » » stmt->left->escloopdepth = loopdepth; |
195 » case OCONTINUE: | 174 » » break; |
196 » case OXFALL: | 175 |
197 » case OGOTO: | 176 » case OLABEL: // TODO: new loop/scope only if there are backjumps to it. |
198 » case OLABEL: | 177 » » loopdepth++; |
199 » case OEMPTY: | 178 » » break; |
| 179 |
| 180 » case OBLOCK: |
| 181 » » escstmtlist(stmt->list); |
| 182 » » break; |
| 183 |
| 184 » case OFOR: |
| 185 » » if(stmt->ntest != N) { |
| 186 » » » escstmtlist(stmt->ntest->ninit); |
| 187 » » » escexpr(N, stmt->ntest); |
| 188 » » } |
| 189 » » escstmt(stmt->nincr); |
| 190 » » loopdepth++; |
| 191 » » escstmtlist(stmt->nbody); |
| 192 » » loopdepth--; |
| 193 » » break; |
| 194 |
| 195 » case ORANGE:» » // for» <list> = range <right> { <nbody> } |
| 196 » » switch(stmt->type->etype) { |
| 197 » » case TSTRING:» // never flows |
| 198 » » » escexpr(stmt->list->n, N); |
| 199 » » » if(stmt->list->next) |
| 200 » » » » escexpr(stmt->list->next->n, N); |
| 201 » » » escexpr(N, stmt->right); |
| 202 » » » break; |
| 203 » » case TARRAY:» // i, v = range sliceorarray |
| 204 » » » escexpr(stmt->list->n, N); |
| 205 » » » if(stmt->list->next) |
| 206 » » » » escexpr(stmt->list->next->n, stmt->right); |
| 207 » » » break; |
| 208 » » case TMAP:» // k [, v] = range map |
| 209 » » » escexpr(stmt->list->n, stmt->right); |
| 210 » » » if(stmt->list->next) |
| 211 » » » » escexpr(stmt->list->next->n, stmt->right); |
| 212 » » » break; |
| 213 » » case TCHAN:» // v = range chan |
| 214 » » » escexpr(stmt->list->n, stmt->right); |
| 215 » » » break; |
| 216 » » } |
| 217 » » loopdepth++; |
| 218 » » escstmtlist(stmt->nbody); |
| 219 » » loopdepth--; |
| 220 » » break; |
| 221 |
| 222 » case OIF: |
| 223 » » escexpr(N, stmt->ntest); |
| 224 » » escstmtlist(stmt->nbody); |
| 225 » » escstmtlist(stmt->nelse); |
| 226 » » break; |
| 227 |
| 228 » case OSELECT: |
| 229 » » for(ll=stmt->list; ll; ll=ll->next) { // cases |
| 230 » » » escstmt(ll->n->left); |
| 231 » » » escstmtlist(ll->n->nbody); |
| 232 » » } |
| 233 » » break; |
| 234 |
| 235 » case OSELRECV2:» // v, ok := <-ch ntest:ok |
| 236 » » escexpr(N, stmt->ntest); |
| 237 » » // fallthrough |
| 238 » case OSELRECV:» // v := <-ch» left: v right->op = ORECV |
| 239 » » escexpr(N, stmt->left); |
| 240 » » escexpr(stmt->left, stmt->right); |
| 241 » » break; |
| 242 |
| 243 » case OSWITCH: |
| 244 » » if(stmt->ntest && stmt->ntest->op == OTYPESW) { |
| 245 » » » for(ll=stmt->list; ll; ll=ll->next) { // cases |
| 246 » » » » // ntest->right is the argument of the .(type), |
| 247 » » » » // ll->n->nname is the variable per case |
| 248 » » » » escexpr(ll->n->nname, stmt->ntest->right); |
| 249 » » » » escstmtlist(ll->n->nbody); |
| 250 » » » } |
| 251 » » } else { |
| 252 » » » escexpr(N, stmt->ntest); |
| 253 » » » for(ll=stmt->list; ll; ll=ll->next) { // cases |
| 254 » » » » for(lr=ll->n->list; lr; lr=lr->next) |
| 255 » » » » » escexpr(N, lr->n); |
| 256 » » » » escstmtlist(ll->n->nbody); |
| 257 » » » } |
| 258 » » } |
| 259 » » break; |
| 260 |
| 261 » case OAS: |
| 262 » case OASOP: |
| 263 » » escexpr(stmt->left, stmt->right); |
| 264 » » break; |
| 265 |
| 266 » » // escape analysis happens after typecheck, so the |
| 267 » » // OAS2xxx have already been substituted. |
| 268 » case OAS2:» // x,y = a,b |
| 269 » » cl = count(stmt->list); |
| 270 » » cr = count(stmt->rlist); |
| 271 » » if(cl > 1 && cr == 1) { |
| 272 » » » for(ll=stmt->list; ll; ll=ll->next) |
| 273 » » » » escexpr(ll->n, stmt->rlist->n); |
| 274 » » } else { |
| 275 » » » if(cl != cr) |
| 276 » » » » fatal("escstmt: bad OAS2: %N", stmt); |
| 277 » » » for(ll=stmt->list, lr=stmt->rlist; ll; ll=ll->next, lr=l
r->next) |
| 278 » » » » escexpr(ll->n, lr->n); |
| 279 » » } |
| 280 » » break; |
| 281 |
| 282 » case OAS2RECV:» » // v, ok = <-ch |
| 283 » case OAS2MAPR:» » // v, ok = m[k] |
| 284 » case OAS2DOTTYPE:» // v, ok = x.(type) |
| 285 » » escexpr(stmt->list->n, stmt->rlist->n); |
| 286 » » escexpr(stmt->list->next->n, N); |
| 287 » » break; |
| 288 |
| 289 » case OAS2MAPW:» » // m[k] = x, ok.. stmt->list->n is the INDEXMAP,
k is handled in escexpr(dst...) |
| 290 » » escexpr(stmt->list->n, stmt->rlist->n); |
| 291 » » escexpr(N, stmt->rlist->next->n); |
| 292 » » break; |
| 293 |
| 294 » case ORECV:» » // unary <-ch as statement |
| 295 » » escexpr(N, stmt->left); |
| 296 » » break; |
| 297 |
| 298 » case OSEND:» » // ch <- x |
| 299 » » escexpr(&theSink, stmt->right);» // for now. TODO escexpr(stmt->
left, stmt->right); |
| 300 » » break; |
| 301 |
| 302 » case OCOPY:» // todo: treat as *dst=*src instead of as dst=src |
| 303 » » escexpr(stmt->left, stmt->right); |
| 304 » » break; |
| 305 |
| 306 » case OAS2FUNC:» // x,y,z = f() |
| 307 » » for(ll = stmt->list; ll; ll=ll->next) |
| 308 » » » escexpr(ll->n, N); |
| 309 » » escexpr(N, stmt->rlist->n); |
| 310 » » break; |
| 311 |
| 312 » case OCALLINTER: |
| 313 » case OCALLFUNC: |
| 314 » case OCALLMETH: |
| 315 » » escexpr(N, stmt); |
| 316 » » break; |
| 317 |
| 318 » case OPROC: |
| 319 » case ODEFER: |
| 320 » » // stmt->left is a (pseud)ocall, stmt->left->left is |
| 321 » » // the function being called. if this defer is at |
| 322 » » // loopdepth >1, everything leaks. TODO this is |
| 323 » » // overly conservative, it's enough if it leaks to a |
| 324 » » // fake node at the function's top level |
| 325 » » dst = &theSink; |
| 326 » » if (stmt->op == ODEFER && loopdepth <= 1) |
| 327 » » » dst = nil; |
| 328 » » escexpr(dst, stmt->left->left); |
| 329 » » for(ll=stmt->left->list; ll; ll=ll->next) |
| 330 » » » escexpr(dst, ll->n); |
| 331 » » break; |
| 332 |
| 333 » case ORETURN: |
| 334 » » for(ll=stmt->list; ll; ll=ll->next) |
| 335 » » » escexpr(&theSink, ll->n); |
| 336 » » break; |
| 337 |
200 case OCLOSE: | 338 case OCLOSE: |
201 case OPRINT: | 339 case OPRINT: |
202 case OPRINTN: | 340 case OPRINTN: |
203 » case ORECOVER: | 341 » » escexpr(N, stmt->left); |
204 » » // all harmless | 342 » » for(ll=stmt->list; ll; ll=ll->next) |
205 » » break; | 343 » » » escexpr(N, ll->n); |
206 | |
207 » case OBLOCK: | |
208 » » eastmtlist(fun, stmt->list); | |
209 » » break; | |
210 | |
211 » case OFOR: | |
212 » » eastmtlist(fun, stmt->ninit); | |
213 » » if(stmt->ntest != N) { | |
214 » » » eastmtlist(fun, stmt->ntest->ninit); | |
215 » » » eaexpr(N, stmt->ntest); | |
216 » » } | |
217 » » eastmt(N, stmt->nincr); | |
218 » » eastmtlist(fun, stmt->nbody); | |
219 » » break; | |
220 | |
221 » case ORANGE:» » // for <list> = range <right> { <nbody> } | |
222 » » eastmtlist(fun, stmt->ninit); | |
223 » » switch(stmt->type->etype) { | |
224 » » case TARRAY:» // i, v = range sliceorarrayorstring | |
225 » » case TSTRING: | |
226 » » » if(stmt->list->next) | |
227 » » » » eaexpr(stmt->list->next->n, stmt->right); | |
228 » » » break;» » »······· | |
229 » » case TMAP:» // k, v = range map | |
230 » » » eaexpr(stmt->list->n, stmt->right); | |
231 » » » if(stmt->list->next) | |
232 » » » » eaexpr(stmt->list->next->n, stmt->right); | |
233 » » » break; | |
234 » » case TCHAN:» // v = range chan | |
235 » » » eaexpr(stmt->list->n, stmt->right); | |
236 » » » break; | |
237 » » } | |
238 » » eastmtlist(fun, stmt->nbody); | |
239 » » break; | |
240 | |
241 » case OIF: | |
242 » » eastmtlist(fun, stmt->ninit); | |
243 » » eaexpr(N, stmt->ntest); | |
244 » » eastmtlist(fun, stmt->nbody); | |
245 » » eastmtlist(fun, stmt->nelse); | |
246 » » break; | |
247 | |
248 » case OSELECT: | |
249 » » eastmtlist(fun, stmt->ninit); | |
250 » » for(ll=stmt->list; ll; ll=ll->next) { // cases | |
251 » » » eastmt(fun, ll->n->left); | |
252 » » » eastmtlist(fun, ll->n->nbody); | |
253 » » } | |
254 » » break; | |
255 | |
256 » case OSELRECV2: | |
257 » » eaexpr(N, stmt->ntest); | |
258 » » // fallthrough | |
259 » case OSELRECV: | |
260 » » eaexpr(stmt->left, stmt->right); | |
261 » » break; | |
262 | |
263 » case OSWITCH: | |
264 » » eastmtlist(fun, stmt->ninit); | |
265 » » if(stmt->ntest && stmt->ntest->op == OTYPESW) { | |
266 » » » eaexpr(stmt->ntest->left, stmt->ntest->right); | |
267 » » » for(ll=stmt->list; ll; ll=ll->next) | |
268 » » » » eastmtlist(fun, ll->n->nbody); | |
269 » » } else { | |
270 » » » eaexpr(fun, stmt->ntest); | |
271 » » » for(ll=stmt->list; ll; ll=ll->next) { // cases | |
272 » » » » for (lr=ll->n->list; lr; lr=lr->next) { | |
273 » » » » » eaexpr(fun, lr->n); | |
274 » » » » } | |
275 » » » » eastmtlist(fun, ll->n->nbody); | |
276 » » » } | |
277 » » } | |
278 » » break; | |
279 | |
280 » case OAS:· | |
281 » case OASOP:» » // v += x etc. never applies to pointers, but l
eave it to eaexpr to figure that out. | |
282 » » eaexpr(stmt->left, stmt->right); | |
283 » » break; | |
284 | |
285 » » // escape analysis happens after typecheck, so the | |
286 » » // OAS2xxx have already been substituted. | |
287 » case OAS2: | |
288 » » cl = count(stmt->list); | |
289 » » cr = count(stmt->rlist); | |
290 » » if (cl > 1 && cr == 1) { | |
291 » » » for(ll=stmt->list; ll; ll=ll->next) | |
292 » » » » eaexpr(ll->n, stmt->rlist->n); | |
293 » » } else {· | |
294 » » » if(cl != cr) | |
295 » » » » fatal("eastmt: bad OAS2: %N", stmt); | |
296 » » » for(ll=stmt->list, lr=stmt->rlist; ll; ll=ll->next, lr=l
r->next) | |
297 » » » » eaexpr(ll->n, lr->n); | |
298 » » } | |
299 » » break; | |
300 » »······· | |
301 » case OAS2RECV:» » // v, ok = <-ch | |
302 » case OAS2MAPR:» » // v, ok = m[k] | |
303 » case OAS2DOTTYPE:» // v, ok = x.(type) | |
304 » » eaexpr(stmt->list->n, stmt->rlist->n->left); | |
305 » » break; | |
306 | |
307 » case OAS2MAPW: » // m[k] = x, ok | |
308 » » eaexpr(stmt->list->n->left, stmt->rlist->n); | |
309 » » break; | |
310 | |
311 » case ORECV:» » // unary <-ch as statement | |
312 » » eaexpr(N, stmt->left); | |
313 » » break; | |
314 | |
315 » case OSEND:» » // ch <- x | |
316 » » eaexpr(stmt->left, stmt->right); | |
317 » » break; | |
318 | |
319 » case OCOPY:» // second arguments leaks to the first | |
320 » » eaexpr(stmt->left, stmt->right); | |
321 » » break; | |
322 | |
323 » case OAPPEND: // note: append also leaks all its args in an expression | |
324 » » for (ll=stmt->list->next; ll; ll=ll->next) | |
325 » » » eaexpr(stmt->list->n, ll->n); | |
326 » » break; | |
327 | |
328 » case OAS2FUNC:» // x,y,z = f() | |
329 » » // the return values already escape because they're return value
s | |
330 » » // no need to tie them to x, y or z here. | |
331 » » eaexpr(N, stmt->rlist->n); | |
332 » » break; | |
333 » »······· | |
334 » case OCALLINTER: | |
335 » case OCALLFUNC: | |
336 » case OCALLMETH: | |
337 » » stmt->trecur = 0; | |
338 » » eaexpr(N, stmt); | |
339 » » break; | |
340 | |
341 » case ODEFER: | |
342 » » eastmt(fun, stmt->left); | |
343 » » break; | |
344 | |
345 » case ORETURN: | |
346 » » for (ll=stmt->list; ll; ll=ll->next) | |
347 » » » eaexpr(&theSink, ll->n); | |
348 » » break; | |
349 | |
350 » case OPROC: | |
351 » » // stmt->left is a (pseud)ocall, consider all arguments leaking | |
352 » » // stmt->left->left is the function being called. if it's a | |
353 » » // method call x.foo, x leaks TODO but other things not.. | |
354 » » eaexpr(&theSink, stmt->left->left); | |
355 » » for (ll=stmt->left->list; ll; ll=ll->next) | |
356 » » » eaexpr(&theSink, ll->n); | |
357 break; | 344 break; |
358 | 345 |
359 case OPANIC: | 346 case OPANIC: |
360 // Argument could leak through recover. | 347 // Argument could leak through recover. |
361 » » eaexpr(&theSink, stmt->left); | 348 » » escexpr(&theSink, stmt->left); |
362 » » break; | 349 » » break; |
363 » } | 350 » } |
364 | 351 |
365 » stmt->trecur = 0; | 352 » lineno = lno; |
366 } | 353 } |
367 | 354 |
368 | 355 // Assert that expr somehow gets assigned to dst, if non nil. for |
369 static int | 356 // dst==nil, any name node expr still must be marked as being |
370 isinteresting(Type *t) { | 357 // evaluated in curfn.» For expr==nil, dst must still be examined for |
371 » if (t == T) | 358 // evaluations inside it (e.g *f(x) = y) |
372 » » return 1; // can happen for lhs of type switch assignment | 359 static void |
373 //» » fatal("no type for isinteresting"); | 360 escexpr(Node *dst, Node *expr) |
374 | 361 { |
375 » switch(t->etype) { | 362 » int lno; |
376 » case TPTR32: | 363 » NodeList *ll; |
377 » case TPTR64: | 364 |
378 » case TINTER: | 365 » if(isblank(dst)) dst = N; |
379 » case TFUNC: | 366 |
380 » case TARRAY: | 367 » // the lhs of an assignment needs recursive analysis too |
381 » » return 1; | 368 » // these are the only interesting cases |
382 » case TCHAN: | 369 » // todo:check channel case |
383 » » if (t->type == t) | 370 » if(dst) { |
384 » » » return 1; // recursive types | 371 » » setlineno(dst); |
385 » » return isinteresting(t->type); | 372 |
386 » » break; | 373 » » switch(dst->op) { |
387 » case TMAP: | 374 » » case OINDEX: |
388 » » if (t->type == t || t->down == t) | 375 » » case OSLICE: |
389 » » » return 1; // recursive types | 376 » » » escexpr(N, dst->right); |
390 | 377 |
391 » » return isinteresting(t->type) || isinteresting(t->down); | 378 » » » // slice: "dst[x] = src" is like *(underlying array)[x
] = src |
392 » » break; | 379 » » » // TODO maybe this never occurs b/c of OSLICEARR and it'
s inserted OADDR |
393 » case TSTRUCT: | 380 » » » if(!isfixedarray(dst->left->type)) |
394 » » for(t=t->type; t!=T; t=t->down) { | 381 » » » » goto doref; |
395 » » » if(t->etype != TFIELD) | 382 |
396 » » » » fatal("isinteresting: not TFIELD: %lT", t); | 383 » » » // fallthrough;» treat "dst[x] = src" as "dst = src" |
397 » » » // can't recurse without going through TPTR | 384 » » case ODOT:» // treat "dst.x = src" as "dst = src" |
398 » » » if (isinteresting(t->type)) | 385 » » » escexpr(dst->left, expr); |
399 » » » » return 1; | 386 » » » return; |
400 » » } | 387 |
401 » » return 0; | 388 » » case OINDEXMAP: |
402 » } | 389 » » » escexpr(&theSink, dst->right);» // map key is put in map |
403 » return 0; | 390 » » » // fallthrough |
404 } | 391 » » case OIND: |
405 | 392 » » case ODOTPTR: |
406 static void | 393 » » case OSLICEARR:» // ->left is the OADDR of the array |
407 flows(Node* dst, Node* src) | 394 » » doref: |
408 { | 395 » » » escexpr(N, dst->left); |
409 » if (dst == nil || src == nil) | 396 » » » // assignment to dereferences: for now we lose track |
| 397 » » » escexpr(&theSink, expr); |
| 398 » » » return; |
| 399 » » } |
| 400 |
| 401 » } |
| 402 |
| 403 » if(expr == N || expr->op == ONONAME || expr->op == OXXX) |
410 return; | 404 return; |
411 » dst->asrc = list(dst->asrc, src); | 405 |
412 | 406 » if(expr->typecheck == 0 && expr->op != OKEY) { |
413 » if (debug['s']>1) { | 407 » » dump("escexpr missing typecheck", expr); |
414 » if (dst != &theSink) | |
415 » » print("%L %S::%N <- %N", lineno, curfn->nname->sym, dst, src); | |
416 » else | |
417 » » print("%L %S:: leaks %N", lineno, curfn->nname->sym, src); | |
418 | |
419 » if (src->op == OADDR) print(" <= %#N", src->left); | |
420 » print("\n"); | |
421 » } | |
422 } | |
423 | |
424 static void | |
425 eaexpr(Node *dst, Node *expr) | |
426 { | |
427 » NodeList *ll; | |
428 | |
429 » if(expr == N || expr == dst) | |
430 » » return; | |
431 | |
432 » setlineno(expr); | |
433 | |
434 » if (expr->trecur) | |
435 » » return; | |
436 » expr->trecur = 1; | |
437 | |
438 » if (expr->typecheck == 0 && expr->op != ONONAME) { // TODO where do
these come from | |
439 » » dump("eaexpr missing typecheck", expr); | |
440 fatal("Missing typecheck."); | 408 fatal("Missing typecheck."); |
441 } | 409 } |
442 | 410 |
443 | 411 » lno = setlineno(expr); |
444 » // if dst is not interesting, save some work down the recursion | 412 » pdepth++; |
445 » if (dst != &theSink) | 413 |
446 » if ((dst == N) || isblank(dst) || !isinteresting(dst->type)) | 414 » if(debug['m'] > 1) |
447 » » dst = N; | 415 » » print("%L:[%d] %#S \t%hN %.*s<= %hN\n", lineno, loopdepth, |
448 | 416 » » (curfn && curfn->nname) ? curfn->nname->sym : S, dst, |
449 » if (debug['s'] > 1) | 417 » » 2*pdepth, ".\t.\t.\t.\t.\t", expr); |
450 » print("%L %S:: ... examining %N <= %N\n", lineno, curfn->nname->sym, dst
, expr); | 418 |
451 | |
452 | |
453 » // here: turn dests x[k] into x, etc. see part-of in top comment | |
454 | |
455 //» if (dst && dst != &theSink && !dst->type) dump("typeless dst", dst); | |
456 »······· | |
457 » if (dst && dst->op == ONAME && dst->class == PEXTERN) | |
458 » » dst = &theSink; | |
459 | |
460 » if (dst && dst->op == OIND) dst = &theSink; // aliasing happens here | |
461 | 419 |
462 switch(expr->op) { | 420 switch(expr->op) { |
463 » default: | 421 » case OADDR:» // dst = &x |
464 » » dump("eaexpr", expr); | 422 » case OIND:» // dst = *x |
465 » » fatal("eaexpr %O", expr->op); | 423 » case ODOTPTR:» // dst = (*x).f |
466 » » break; | 424 » » // restart the recursion at x to figure out where it came from |
467 | 425 » » escexpr(expr->left, expr->left); |
468 » case OADDR: | 426 » » // fallthrough |
469 » » flows(dst, expr); // dst = &x | |
470 » » // to the right of & there can be only an array-,map- or struct-
literal, a name (not of a func),· | |
471 » » // an index or member selection operation &x[y] (OINDEX), &x.y
(ODOT).· | |
472 » » // or an indirection: &*x (OIND), &(*x).y (ODOTPTR, written as &
x.y , but x of pointer type | |
473 » » // in the last two cases, the flow is actually not interrupted, | |
474 » » // which in turn is only interesting if it is a pointer value. | |
475 » » if (expr->left->op == OIND || expr->left->op == ODOTPTR) | |
476 » » » eaexpr(dst, expr->left);···· | |
477 » » else | |
478 » » » eaexpr(N, expr->left);···· | |
479 » » break; | |
480 | |
481 » case OIND:» » » // dst = *y -> who cares, | |
482 » » // even if dst = * .... &x, that doesnt mean &x flows to dst, ju
st x | |
483 » » // if this OIND is to the right of an OADDR, things are differen
t, but that's handled above. | |
484 » » eaexpr(N, expr->left); | |
485 » » break; | |
486 | |
487 case ONAME: | 427 case ONAME: |
488 case ONONAME: // not typechecked yet? | |
489 case OPARAM: | 428 case OPARAM: |
490 » » flows(dst, expr); | 429 » » // loopdepth was set in the defining statement or function heade
r |
491 » » break; | 430 » » escflows(dst, expr); |
492 | 431 » » break; |
| 432 |
| 433 » case OARRAYLIT: |
| 434 » case OSTRUCTLIT: |
493 case OMAPLIT: | 435 case OMAPLIT: |
494 » case OSTRUCTLIT: | 436 » » expr->escloopdepth = loopdepth; |
495 » case OARRAYLIT: | 437 » » escflows(dst, expr); |
496 » » // pointer members or elements, initialized to &something? | 438 » » for(ll=expr->list; ll; ll=ll->next) { |
497 » » // i think for now they're already on the heap anyway | 439 » » » escexpr(expr, ll->n->left); |
498 » » break; | 440 » » » escexpr(expr, ll->n->right); |
499 | 441 » » } |
500 » case OCOMPLIT: | 442 » » break; |
501 » case OLITERAL: | 443 |
502 » case ORECOVER: | 444 » case OMAKECHAN: |
503 » » break; | 445 » case OMAKEMAP: |
504 | 446 » case OMAKESLICE: |
505 » // harmless unary | 447 » case ONEW: |
506 » case ONOT:» case OCOM: | 448 » » expr->curfn = curfn; // should have been done in parse, but pat
ch it up here. |
507 » case OCAP:» case OLEN: | 449 » » expr->escloopdepth = loopdepth; |
508 » case OREAL:» case OIMAG: | 450 » » escflows(dst, expr); |
509 » case OARRAYBYTESTR: | 451 » » // first arg is type, all others need checking |
510 » case OARRAYRUNESTR: | 452 » » for(ll=expr->list->next; ll; ll=ll->next) |
511 » case OSTRARRAYBYTE: | 453 » » » escexpr(N, ll->n); |
512 » case OSTRARRAYRUNE: | 454 » » break; |
513 » case ORUNESTR: | 455 |
514 » » eaexpr(N, expr->left); | 456 » case OCLOSURE: |
515 » » break; | 457 » » expr->curfn = curfn; // should have been done in parse, but pat
ch it up here. |
516 | 458 » » expr->escloopdepth = loopdepth; |
517 » // harmless binary | 459 » » escflows(dst, expr); |
518 » case OADD:» case OAND:» case OANDAND: | 460 » » escfunc(expr); |
519 » case OANDNOT:» case ODIV:» case OEQ: | 461 » » break; |
520 » case OGE:» case OGT:» case OLE: | 462 |
521 » case OLT:» case OLSH:» case ORSH: | 463 » // end of the leaf cases. no calls to escflows() in the cases below. |
522 » case OMOD:» case OMUL:» case ONE: | 464 |
523 » case OOR:» case OOROR:» case OSUB: | 465 |
524 » case OXOR:» case OADDSTR:» case OASOP: | 466 » case OCONV:» // unaries that pass the value through |
525 » case OCMPIFACE:»case OCMPSTR: » case OCOMPLEX: | |
526 » case OPLUS: case OMINUS: | |
527 » » eaexpr(N, expr->left); | |
528 » » eaexpr(N, expr->right); | |
529 » » break; | |
530 | |
531 » // unaries that pass the value through | |
532 » case OCONV: | |
533 case OCONVIFACE: | 467 case OCONVIFACE: |
534 case OCONVNOP: | 468 case OCONVNOP: |
535 case ODOTTYPE: | 469 case ODOTTYPE: |
536 case ODOTTYPE2: | 470 case ODOTTYPE2: |
537 » case ORECV: // leaks the whole channel | 471 » case ORECV:» // leaks the whole channel |
538 » » eaexpr(dst, expr->left); | 472 » case ODOTMETH:» // expr->right is just the field or method name |
| 473 » case ODOTINTER: |
| 474 » case ODOT: |
| 475 » » escexpr(dst, expr->left); |
539 break; | 476 break; |
540 | 477 |
541 case OCOPY: | 478 case OCOPY: |
542 // left leaks to right, but the return value is harmless | 479 // left leaks to right, but the return value is harmless |
543 » » eaexpr(expr->left, expr->right); | 480 » » // TODO: treat as *dst = *src, rather than as dst = src |
| 481 » » escexpr(expr->left, expr->right); |
544 break; | 482 break; |
545 | 483 |
546 case OAPPEND: | 484 case OAPPEND: |
547 » » eaexpr(dst, expr->list->n); | 485 » » // See TODO for OCOPY |
548 » » for (ll=expr->list->next; ll; ll=ll->next) | 486 » » escexpr(dst, expr->list->n); |
549 » » » eaexpr(expr->list->n, ll->n); | 487 » » for(ll=expr->list->next; ll; ll=ll->next) |
550 » » break; | 488 » » » escexpr(expr->list->n, ll->n); |
551 | 489 » » break; |
552 » // all arguments leak to the parameter nodes. | 490 |
553 » // TODO ...when we figure out how to get our hands at them. | |
554 case OCALLMETH: | 491 case OCALLMETH: |
| 492 case OCALLFUNC: |
555 case OCALLINTER: | 493 case OCALLINTER: |
556 //» » eaexpr(&theSink, expr->left->left); // DOTMETH->this or DOTINTER
->this | 494 » » // Moved to separate function to isolate the hair. |
557 » » // fallthrough | 495 » » escexprcall(dst, expr); |
| 496 » » break; |
| 497 |
| 498 » case OSLICEARR:» // like an implicit OIND to the underlying buffer, but
typecheck has inserted an OADDR |
| 499 » case OSLICESTR: |
| 500 » case OSLICE: |
| 501 » case OINDEX: |
| 502 » case OINDEXMAP: |
| 503 » » // the big thing flows, the keys just need checking |
| 504 » » escexpr(dst, expr->left); |
| 505 » » escexpr(N, expr->right); // expr->right is the OKEY |
| 506 » » break; |
| 507 |
| 508 » default: // all other harmless leaf, unary or binary cases end up here |
| 509 » » escexpr(N, expr->left); |
| 510 » » escexpr(N, expr->right); |
| 511 » » break; |
| 512 » } |
| 513 |
| 514 » pdepth--; |
| 515 » lineno = lno; |
| 516 } |
| 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) { |
558 case OCALLFUNC: | 536 case OCALLFUNC: |
559 » » eaexpr(dst, expr->left); | 537 » » fn = expr->left; |
560 » » for (ll=expr->list; ll; ll=ll->next) | 538 » » escexpr(N, fn); |
561 » » » eaexpr(&theSink , ll->n); | 539 » » fntype = fn->type; |
562 » » break; | 540 » » break; |
563 | 541 |
564 » case ODOTMETH: | 542 » case OCALLMETH: |
565 » case ODOTINTER: | 543 » » fn = expr->left->right;» // ODOTxx name |
566 » case ODOT: | 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. |
| 627 static void |
| 628 escflows(Node* dst, Node* src) |
| 629 { |
| 630 » if(dst == nil || src == nil || dst == src) |
| 631 » » return; |
| 632 |
| 633 » // Don't bother building a graph for scalars. |
| 634 » if (src->type && !haspointers(src->type)) |
| 635 » » return; |
| 636 |
| 637 » if(debug['m']>2) |
| 638 » » print("%L::flows:: %hN <- %hN\n", lineno, dst, src); |
| 639 |
| 640 » // Assignments to global variables get lumped into theSink. |
| 641 » if (dst->op == ONAME && dst->class == PEXTERN) |
| 642 » » dst = &theSink; |
| 643 |
| 644 » if (dst->escflowsrc == nil) { |
| 645 » » dsts = list(dsts, dst); |
| 646 » » dstcount++; |
| 647 » } |
| 648 » edgecount++; |
| 649 |
| 650 » dst->escflowsrc = list(dst->escflowsrc, src); |
| 651 } |
| 652 |
| 653 // Whenever we hit a reference node, the level goes up by one, and whenever |
| 654 // we hit an OADDR, the level goes down by one. as long as we're on a level > 0 |
| 655 // finding an OADDR just means we're following the upstream of a dereference, |
| 656 // so this address doesn't leak (yet). |
| 657 // If level == 0, it means the /value/ of this node can reach the root of this f
lood. |
| 658 // so if this node is an OADDR, it's argument should be marked as escaping iff |
| 659 // it's currfn/loopdepth are different from the flood's root. |
| 660 // Once an object has been moved to the heap, all of it's upstream should be con
sidered |
| 661 // escaping to the global scope. |
| 662 static void |
| 663 escflood(Node *dst) |
| 664 { |
| 665 » NodeList *l; |
| 666 |
| 667 » switch(dst->op) { |
| 668 » case ONAME: |
| 669 » case OCLOSURE: |
| 670 » » break; |
| 671 » default: |
| 672 » » return; |
| 673 » } |
| 674 |
| 675 » if(debug['m']>1) |
| 676 » » print("\nescflood:%d: dst %hN scope:%#S[%d]\n", floodgen, dst, |
| 677 » » (dst->curfn && dst->curfn->nname) ? dst->curfn->nname->sym
: S, |
| 678 » » dst->escloopdepth); |
| 679 |
| 680 » for (l = dst->escflowsrc; l; l=l->next) { |
| 681 » » floodgen++; |
| 682 » » escwalk(0, dst, l->n); |
| 683 » } |
| 684 } |
| 685 |
| 686 static void |
| 687 escwalk(int level, Node *dst, Node *src) |
| 688 { |
| 689 » NodeList* ll; |
| 690 » int leaks; |
| 691 |
| 692 » if (src->escfloodgen == floodgen) |
| 693 » » return; |
| 694 » src->escfloodgen = floodgen; |
| 695 |
| 696 » if(debug['m']>1) |
| 697 » » print("escwalk: level:%d depth:%d %.*s %hN scope:%#S[%d]\n", |
| 698 » » level, pdepth, pdepth, "\t\t\t\t\t\t\t\t\t\t", src, |
| 699 » » (src->curfn && src->curfn->nname) ? src->curfn->nname->sym
: S, src->escloopdepth); |
| 700 |
| 701 » pdepth++; |
| 702 |
| 703 » leaks = (level <= 0) && (dst->escloopdepth < src->escloopdepth); |
| 704 |
| 705 » switch(src->op) { |
| 706 » case ONAME: |
| 707 » » if (src->class == PPARAM && leaks && src->esc == EscNone) { |
| 708 » » » src->esc = EscScope; |
| 709 » » » if(debug['m']) |
| 710 » » » » print("%L:leaking param: %hN\n", src->lineno, sr
c); |
| 711 » » } |
| 712 » » break; |
| 713 |
| 714 » case OADDR: |
| 715 » » if (leaks) |
| 716 » » » addrescapes(src->left); |
| 717 » » escwalk(level-1, dst, src->left); |
| 718 » » break; |
| 719 |
| 720 » case OINDEX: |
| 721 » » if(isfixedarray(src->type)) |
| 722 » » » break; |
| 723 » case OSLICE: |
567 case ODOTPTR: | 724 case ODOTPTR: |
568 // pretend all of the struct leaked | |
569 // expr->right is just an (untypechecked) ONAME | |
570 eaexpr(dst, expr->left); | |
571 break; | |
572 | |
573 case OSLICE: // the big thing leaks, the keys just need checking | |
574 case OSLICEARR: | |
575 case OSLICESTR: | |
576 eaexpr(dst, expr->left); | |
577 eaexpr(N, expr->right->left); // the OKEY | |
578 eaexpr(N, expr->right->right); | |
579 break; | |
580 | |
581 case OINDEX:··· | |
582 case OINDEXMAP: | 725 case OINDEXMAP: |
583 » » eaexpr(dst, expr->left); | 726 » case OIND: |
584 » » eaexpr(N, expr->right); | 727 » » escwalk(level+1, dst, src->left); |
585 » » break; | 728 » } |
586 | 729 |
587 » case OMAKECHAN: | 730 » for (ll=src->escflowsrc; ll; ll=ll->next) |
588 » case OMAKEMAP: | 731 » » escwalk(level, dst, ll->n); |
589 » case OMAKESLICE: | 732 |
590 » case ONEW:» » // first arg is type, all others need checking | 733 » pdepth--; |
591 » » for (ll=expr->list->next; ll; ll=ll->next) | 734 } |
592 » » » eaexpr(N, ll->n); | 735 |
593 » » flows(dst, expr); | 736 static void |
594 » » break; | 737 esctag(Node *func) |
595 | 738 { |
596 » case OCLOSURE: | 739 » Node *savefn; |
597 » » flows(dst, expr); // the closure flows to the lhs of the assign
ment, or to theSink if we're in a return. | 740 » NodeList *ll; |
598 » » // the cvars will have their address taken by walk. introduce a
fake OADDR node so eawalk can pick that up. | 741 |
599 » » for (ll=expr->cvars; ll; ll=ll->next) | 742 » savefn = curfn; |
600 » » » flows(expr, nod(OADDR, ll->n, N)); | 743 » curfn = func; |
601 » » eastmtlist(expr, expr->nbody); // the declarations in the body
flow to the closure | 744 |
602 » » break; | 745 » for(ll=curfn->dcl; ll; ll=ll->next) { |
603 | 746 » » if(ll->n->op != ONAME || ll->n->class != PPARAM) |
604 » } | 747 » » » continue; |
605 | 748 |
606 » expr->trecur = 0; | 749 » » switch (ll->n->esc) { |
607 } | 750 » » case EscNone:» // not touched by escflood |
608 | 751 » » » if (haspointers(ll->n->type)) // don't bother tagging fo
r scalars |
609 | 752 » » » » ll->n->paramfld->note = safetag; |
610 | 753 » » case EscHeap:» // touched by escflood, moved to heap |
611 | 754 » » case EscScope:» // touched by escflood, value leaves scope |
612 #if 0 | 755 » » » break; |
613 | 756 » » default: |
614 » for(tl=tstruct->type; tl; tl=tl->down) { | 757 » » » fatal("messed up escape tagging: %N::%N", curfn, ll->n); |
615 » » t = tl->type; | 758 » » } |
616 » » if(tl->isddd) { | 759 » } |
617 » » » if(isddd) { | 760 |
618 » » » » if(nl == nil) | 761 » curfn = savefn; |
619 » » » » » goto notenough; | 762 } |
620 » » » » if(nl->next != nil) | |
621 » » » » » goto toomany; | |
622 » » » » n = nl->n; | |
623 » » » » setlineno(n); | |
624 » » » » if(n->type != T) { | |
625 » » » » » nl->n = assignconv(n, t, desc); | |
626 » » » » » if (call) argused(call, tl, n); | |
627 » » » » } | |
628 » » » » goto out; | |
629 » » » } | |
630 » » » for(; nl; nl=nl->next) { | |
631 » » » » n = nl->n; | |
632 » » » » setlineno(nl->n); | |
633 » » » » if(n->type != T) { | |
634 » » » » » nl->n = assignconv(n, t->type, desc); | |
635 » » » » » if (call) argused(call, tl, n); | |
636 » » » » } | |
637 » » » } | |
638 » » » goto out; | |
639 » » }· | |
640 | |
641 | |
642 // mark that value has been used as the argument arg | |
643 static void argused(Node* func, Type* arg, Node* val) { | |
644 //» if (arg->sym && arg->sym->pkg == localpkg) | |
645 //» » arg->note = strlit("used"); | |
646 //» print("Using %N :: %#T = %N\n", func, arg, val); | |
647 } | |
648 | |
649 | |
650 /* | |
651 * type check function definition | |
652 */ | |
653 static void | |
654 escanalfunc(Node *n) | |
655 { | |
656 //» Type *t, *t1, *rcvr; | |
657 » Type *t, *rcvr; | |
658 | |
659 » typecheck(&n->nname, Erv | Easgn); | |
660 » if((t = n->nname->type) == T) | |
661 » » return; | |
662 » n->type = t; | |
663 | |
664 » print("typecheckfunc %T\n", n->type); | |
665 » for(t1=getinargx(t)->type; t1; t1=t1->down) { | |
666 » » if (isptr[t1->type->etype]) | |
667 » » » t1->note = strlit("leaks"); | |
668 » » print("\t%T\n", t1); | |
669 | |
670 » } | |
671 | |
672 » rcvr = getthisx(t)->type; | |
673 » if(rcvr != nil && n->shortname != N && !isblank(n->shortname)) | |
674 » » addmethod(n->shortname->sym, t, 1); | |
675 } | |
676 | |
677 #endif | |
LEFT | RIGHT |