@@ -324,3 +324,144 @@ module TypeTracker {
324
324
*/
325
325
TypeTracker end ( ) { result .end ( ) }
326
326
}
327
+
328
+ private newtype TTypeBackTracker = MkTypeBackTracker ( Boolean hasReturn , OptionalAttributeName attr )
329
+
330
+ /**
331
+ * Summary of the steps needed to back-track a use of a value to a given dataflow node.
332
+ *
333
+ * This can for example be used to track callbacks that are passed to a certain API,
334
+ * so we can model specific parameters of that callback as having a certain type.
335
+ *
336
+ * Note that type back-tracking does not provide a source/sink relation, that is,
337
+ * it may determine that a node will be used in an API call somewhere, but it won't
338
+ * determine exactly where that use was, or the path that led to the use.
339
+ *
340
+ * It is recommended that all uses of this type are written in the following form,
341
+ * for back-tracking some callback type `myCallback`:
342
+ *
343
+ * ```
344
+ * DataFlow::LocalSourceNode myCallback(DataFlow::TypeBackTracker t) {
345
+ * t.start() and
346
+ * result = (< some API call >).getArgument(< n >).getALocalSource()
347
+ * or
348
+ * exists (DataFlow::TypeBackTracker t2 |
349
+ * result = myCallback(t2).backtrack(t2, t)
350
+ * )
351
+ * }
352
+ *
353
+ * DataFlow::LocalSourceNode myCallback() { result = myCallback(DataFlow::TypeBackTracker::end()) }
354
+ * ```
355
+ *
356
+ * Instead of `result = myCallback(t2).backtrack(t2, t)`, you can also use the equivalent
357
+ * `t2 = t.step(result, myCallback(t2))`. If you additionally want to track individual
358
+ * intra-procedural steps, use `t2 = t.smallstep(result, myCallback(t2))`.
359
+ */
360
+ class TypeBackTracker extends TTypeBackTracker {
361
+ Boolean hasReturn ;
362
+ string attr ;
363
+
364
+ TypeBackTracker ( ) { this = MkTypeBackTracker ( hasReturn , attr ) }
365
+
366
+ /** Gets the summary resulting from prepending `step` to this type-tracking summary. */
367
+ TypeBackTracker prepend ( StepSummary step ) {
368
+ step = LevelStep ( ) and result = this
369
+ or
370
+ step = CallStep ( ) and hasReturn = false and result = this
371
+ or
372
+ step = ReturnStep ( ) and result = MkTypeBackTracker ( true , attr )
373
+ or
374
+ exists ( string p | step = LoadStep ( p ) and attr = "" and result = MkTypeBackTracker ( hasReturn , p ) )
375
+ or
376
+ step = StoreStep ( attr ) and result = MkTypeBackTracker ( hasReturn , "" )
377
+ }
378
+
379
+ /** Gets a textual representation of this summary. */
380
+ string toString ( ) {
381
+ exists ( string withReturn , string withAttr |
382
+ ( if hasReturn = true then withReturn = "with" else withReturn = "without" ) and
383
+ ( if attr != "" then withAttr = " with attribute " + attr else withAttr = "" ) and
384
+ result = "type back-tracker " + withReturn + " return steps" + withAttr
385
+ )
386
+ }
387
+
388
+ /**
389
+ * Holds if this is the starting point of type tracking.
390
+ */
391
+ predicate start ( ) { hasReturn = false and attr = "" }
392
+
393
+ /**
394
+ * Holds if this is the end point of type tracking.
395
+ */
396
+ predicate end ( ) { attr = "" }
397
+
398
+ /**
399
+ * INTERNAL. DO NOT USE.
400
+ *
401
+ * Holds if this type has been back-tracked into a call through return edge.
402
+ */
403
+ boolean hasReturn ( ) { result = hasReturn }
404
+
405
+ /**
406
+ * Gets a type tracker that starts where this one has left off to allow continued
407
+ * tracking.
408
+ *
409
+ * This predicate is only defined if the type has not been tracked into an attribute.
410
+ */
411
+ TypeBackTracker continue ( ) { attr = "" and result = this }
412
+
413
+ /**
414
+ * Gets the summary that corresponds to having taken a backwards
415
+ * heap and/or inter-procedural step from `nodeTo` to `nodeFrom`.
416
+ */
417
+ pragma [ inline]
418
+ TypeBackTracker step ( LocalSourceNode nodeFrom , LocalSourceNode nodeTo ) {
419
+ exists ( StepSummary summary |
420
+ StepSummary:: step ( nodeFrom , nodeTo , summary ) and
421
+ this = result .prepend ( summary )
422
+ )
423
+ }
424
+
425
+ /**
426
+ * Gets the summary that corresponds to having taken a backwards
427
+ * local, heap and/or inter-procedural step from `nodeTo` to `nodeFrom`.
428
+ *
429
+ * Unlike `TypeBackTracker::step`, this predicate exposes all edges
430
+ * in the flowgraph, and not just the edges between
431
+ * `LocalSourceNode`s. It may therefore be less performant.
432
+ *
433
+ * Type tracking predicates using small steps typically take the following form:
434
+ * ```ql
435
+ * DataFlow::Node myType(DataFlow::TypeBackTracker t) {
436
+ * t.start() and
437
+ * result = < some API call >.getArgument(< n >)
438
+ * or
439
+ * exists (DataFlow::TypeBackTracker t2 |
440
+ * t = t2.smallstep(result, myType(t2))
441
+ * )
442
+ * }
443
+ *
444
+ * DataFlow::Node myType() {
445
+ * result = myType(DataFlow::TypeBackTracker::end())
446
+ * }
447
+ * ```
448
+ */
449
+ pragma [ inline]
450
+ TypeBackTracker smallstep ( Node nodeFrom , Node nodeTo ) {
451
+ exists ( StepSummary summary |
452
+ StepSummary:: smallstep ( nodeFrom , nodeTo , summary ) and
453
+ this = result .prepend ( summary )
454
+ )
455
+ or
456
+ typePreservingStep ( nodeFrom , nodeTo ) and
457
+ this = result
458
+ }
459
+ }
460
+
461
+ /** Provides predicates for implementing custom `TypeBackTracker`s. */
462
+ module TypeBackTracker {
463
+ /**
464
+ * Gets a valid end point of type back-tracking.
465
+ */
466
+ TypeBackTracker end ( ) { result .end ( ) }
467
+ }
0 commit comments