@@ -149,21 +149,63 @@ module SourceSinkInterpretationInput implements
149
149
)
150
150
}
151
151
152
+ // Note that due to embedding, which is currently implemented via some
153
+ // Methods having multiple qualified names, a given Method is liable to have
154
+ // more than one SourceOrSinkElement, one for each of the names it claims.
152
155
private newtype TSourceOrSinkElement =
153
- TEntityElement ( Entity e ) or
156
+ TMethodEntityElement ( Method m , string pkg , string type , boolean subtypes ) {
157
+ m .hasQualifiedName ( pkg , type , _) and
158
+ subtypes = [ true , false ]
159
+ } or
160
+ TFieldEntityElement ( Field f , string pkg , string type , boolean subtypes ) {
161
+ f .hasQualifiedName ( pkg , type , _) and
162
+ subtypes = [ true , false ]
163
+ } or
164
+ TOtherEntityElement ( Entity e ) {
165
+ not e instanceof Method and
166
+ not e instanceof Field
167
+ } or
154
168
TAstElement ( AstNode n )
155
169
156
170
/** An element representable by CSV modeling. */
157
171
class SourceOrSinkElement extends TSourceOrSinkElement {
158
172
/** Gets this source or sink element as an entity, if it is one. */
159
- Entity asEntity ( ) { this = TEntityElement ( result ) }
173
+ Entity asEntity ( ) {
174
+ result = [ this .asMethodEntity ( ) , this .asFieldEntity ( ) , this .asOtherEntity ( ) ]
175
+ }
176
+
177
+ /** Gets this source or sink element as a method, if it is one. */
178
+ Method asMethodEntity ( ) { this = TMethodEntityElement ( result , _, _, _) }
179
+
180
+ /** Gets this source or sink element as a field, if it is one. */
181
+ Field asFieldEntity ( ) { this = TFieldEntityElement ( result , _, _, _) }
182
+
183
+ /** Gets this source or sink element as an entity which isn't a field or method, if it is one. */
184
+ Entity asOtherEntity ( ) { this = TOtherEntityElement ( result ) }
160
185
161
186
/** Gets this source or sink element as an AST node, if it is one. */
162
187
AstNode asAstNode ( ) { this = TAstElement ( result ) }
163
188
189
+ /**
190
+ * Holds if this source or sink element is a method or field that was specified
191
+ * with the given values for `e`, `pkg`, `type` and `subtypes`.
192
+ */
193
+ predicate hasFullInfo ( Entity e , string pkg , string type , boolean subtypes ) {
194
+ this = TMethodEntityElement ( e , pkg , type , subtypes ) or
195
+ this = TFieldEntityElement ( e , pkg , type , subtypes )
196
+ }
197
+
164
198
/** Gets a textual representation of this source or sink element. */
165
199
string toString ( ) {
200
+ ( this instanceof TOtherEntityElement or this instanceof TAstElement ) and
166
201
result = "element representing " + [ this .asEntity ( ) .toString ( ) , this .asAstNode ( ) .toString ( ) ]
202
+ or
203
+ exists ( Entity e , string pkg , string name , boolean subtypes |
204
+ this .hasFullInfo ( e , pkg , name , subtypes ) and
205
+ result =
206
+ "element representing " + e .toString ( ) + " with receiver type " + pkg + "." + name +
207
+ " and subtypes=" + subtypes
208
+ )
167
209
}
168
210
169
211
/** Gets the location of this element. */
@@ -203,7 +245,17 @@ module SourceSinkInterpretationInput implements
203
245
204
246
/** Gets the target of this call, if any. */
205
247
SourceOrSinkElement getCallTarget ( ) {
206
- result .asEntity ( ) = this .asCall ( ) .getNode ( ) .( DataFlow:: CallNode ) .getTarget ( )
248
+ exists ( DataFlow:: CallNode cn , Function callTarget |
249
+ cn = this .asCall ( ) .getNode ( ) and
250
+ callTarget = cn .getTarget ( )
251
+ |
252
+ (
253
+ result .asOtherEntity ( ) = callTarget
254
+ or
255
+ callTarget instanceof Method and
256
+ result = getElementWithQualifier ( callTarget , cn .getReceiver ( ) )
257
+ )
258
+ )
207
259
}
208
260
209
261
/** Gets a textual representation of this node. */
@@ -228,6 +280,105 @@ module SourceSinkInterpretationInput implements
228
280
}
229
281
}
230
282
283
+ /**
284
+ * Gets a method or field spec for `e` which applies in the context of
285
+ * qualifier `qual`.
286
+ *
287
+ * Note that naively checking `e`'s qualified name is not correct, because
288
+ * `Method`s and `Field`s may have multiple qualified names due to embedding.
289
+ * We must instead check that the package and type name given by
290
+ * `result.hasFullInfo` refer to either `qual`'s type or to a type it embeds.
291
+ */
292
+ bindingset [ e, qual]
293
+ pragma [ inline_late]
294
+ private SourceOrSinkElement getElementWithQualifier ( Entity e , DataFlow:: Node qual ) {
295
+ exists ( boolean subtypes , Type syntacticQualBaseType , Type targetType |
296
+ syntacticQualBaseType = getSyntacticQualifierBaseType ( qual ) and
297
+ result = constructElement ( e , targetType , subtypes )
298
+ |
299
+ subtypes = [ true , false ] and
300
+ syntacticQualBaseType = targetType
301
+ or
302
+ subtypes = true and
303
+ (
304
+ // `syntacticQualBaseType`'s underlying type might be an interface type and `sse`
305
+ // might refer to a method defined on an interface embedded within it.
306
+ targetType =
307
+ syntacticQualBaseType .getUnderlyingType ( ) .( InterfaceType ) .getAnEmbeddedInterface ( )
308
+ or
309
+ // `syntacticQualBaseType`'s underlying type might be a struct type and `sse`
310
+ // might be a promoted method or field in it.
311
+ targetType = getAnIntermediateEmbeddedType ( e , syntacticQualBaseType .getUnderlyingType ( ) )
312
+ )
313
+ )
314
+ }
315
+
316
+ bindingset [ e, targetType, subtypes]
317
+ pragma [ inline_late]
318
+ private SourceOrSinkElement constructElement ( Entity e , Type targetType , boolean subtypes ) {
319
+ exists ( string pkg , string typename |
320
+ targetType .hasQualifiedName ( pkg , typename ) and
321
+ result .hasFullInfo ( e , pkg , typename , subtypes )
322
+ )
323
+ }
324
+
325
+ /**
326
+ * Gets the type of an embedded field of `st` which is on the path to `e`,
327
+ * which is a promoted method or field of `st`, or its base type if it's a
328
+ * pointer type.
329
+ */
330
+ private Type getAnIntermediateEmbeddedType ( Entity e , StructType st ) {
331
+ exists ( Field field1 , Field field2 , int depth1 , int depth2 , Type t2 |
332
+ field1 = st .getFieldAtDepth ( _, depth1 ) and
333
+ field2 = st .getFieldAtDepth ( _, depth2 ) and
334
+ result = lookThroughPointerType ( field1 .getType ( ) ) and
335
+ t2 = lookThroughPointerType ( field2 .getType ( ) ) and
336
+ (
337
+ field1 = field2
338
+ or
339
+ field2 = result .getUnderlyingType ( ) .( StructType ) .getFieldAtDepth ( _, depth2 - depth1 - 1 )
340
+ )
341
+ |
342
+ e .( Method ) .getReceiverBaseType ( ) = t2
343
+ or
344
+ e .( Field ) .getDeclaringType ( ) = t2 .getUnderlyingType ( )
345
+ )
346
+ }
347
+
348
+ /**
349
+ * Gets the base type of `underlying`, where `n` is of the form
350
+ * `implicitDeref?(underlying.implicitFieldRead1.implicitFieldRead2...)`
351
+ *
352
+ * For Go syntax like `qualifier.method()` or `qualifier.field`, this is the type of `qualifier`, before any
353
+ * implicit dereference is interposed because `qualifier` is of pointer type, or implicit field accesses
354
+ * navigate to any embedded struct types that truly host `field`.
355
+ */
356
+ private Type getSyntacticQualifierBaseType ( DataFlow:: Node n ) {
357
+ exists ( DataFlow:: Node n2 |
358
+ // look through implicit dereference, if there is one
359
+ not exists ( n .asInstruction ( ) .( IR:: EvalImplicitDerefInstruction ) .getOperand ( ) ) and
360
+ n2 = n
361
+ or
362
+ n2 .asExpr ( ) = n .asInstruction ( ) .( IR:: EvalImplicitDerefInstruction ) .getOperand ( )
363
+ |
364
+ result = lookThroughPointerType ( skipImplicitFieldReads ( n2 ) .getType ( ) )
365
+ )
366
+ }
367
+
368
+ private DataFlow:: Node skipImplicitFieldReads ( DataFlow:: Node n ) {
369
+ not exists ( lookThroughImplicitFieldRead ( n ) ) and result = n
370
+ or
371
+ result = skipImplicitFieldReads ( lookThroughImplicitFieldRead ( n ) )
372
+ }
373
+
374
+ private DataFlow:: Node lookThroughImplicitFieldRead ( DataFlow:: Node n ) {
375
+ result .asInstruction ( ) =
376
+ n .( DataFlow:: InstructionNode )
377
+ .asInstruction ( )
378
+ .( IR:: ImplicitFieldReadInstruction )
379
+ .getBaseInstruction ( )
380
+ }
381
+
231
382
/** Provides additional sink specification logic. */
232
383
bindingset [ c]
233
384
predicate interpretOutput ( string c , InterpretNode mid , InterpretNode node ) {
@@ -242,10 +393,12 @@ module SourceSinkInterpretationInput implements
242
393
e = mid .asElement ( )
243
394
|
244
395
( c = "Parameter" or c = "" ) and
245
- node . asNode ( ) . asParameter ( ) = e .asEntity ( )
396
+ n . asParameter ( ) = pragma [ only_bind_into ] ( e ) .asEntity ( )
246
397
or
247
- c = "" and
248
- n .( DataFlow:: FieldReadNode ) .getField ( ) = e .asEntity ( )
398
+ exists ( DataFlow:: FieldReadNode frn | frn = n |
399
+ c = "" and
400
+ pragma [ only_bind_into ] ( e ) = getElementWithQualifier ( frn .getField ( ) , frn .getBase ( ) )
401
+ )
249
402
)
250
403
}
251
404
@@ -259,10 +412,13 @@ module SourceSinkInterpretationInput implements
259
412
mid .asCallable ( ) = getNodeEnclosingCallable ( ret )
260
413
)
261
414
or
262
- exists ( DataFlow:: Write fw , Field f |
415
+ exists ( SourceOrSinkElement e , DataFlow:: Write fw , DataFlow:: Node base , Field f |
416
+ e = mid .asElement ( ) and
417
+ f = e .asFieldEntity ( )
418
+ |
263
419
c = "" and
264
- f = mid . asElement ( ) . asEntity ( ) and
265
- fw . writesField ( _ , f , node . asNode ( ) )
420
+ fw . writesField ( base , f , node . asNode ( ) ) and
421
+ pragma [ only_bind_into ] ( e ) = getElementWithQualifier ( f , base )
266
422
)
267
423
}
268
424
}
0 commit comments