@@ -378,6 +378,167 @@ module LocalFlow {
378
378
}
379
379
}
380
380
381
+ /** Provides logic related to captured variables. */
382
+ module VariableCapture {
383
+ private import codeql.dataflow.VariableCapture as Shared
384
+
385
+ class ExprCfgNode extends ControlFlowNode {
386
+ ExprCfgNode ( ) { isExpressionNode ( this ) }
387
+ }
388
+
389
+ private predicate closureFlowStep ( ExprCfgNode e1 , ExprCfgNode e2 ) {
390
+ // simpleAstFlowStep(e1, e2)
391
+ // or
392
+ exists ( SsaVariable def |
393
+ def .getAUse ( ) = e2 and
394
+ def .getAnUltimateDefinition ( ) .getDefinition ( ) .( DefinitionNode ) .getValue ( ) = e1
395
+ )
396
+ }
397
+
398
+ private module CaptureInput implements Shared:: InputSig {
399
+ private import python as P
400
+
401
+ class Location = P:: Location ;
402
+
403
+ class BasicBlock extends P:: BasicBlock {
404
+ Callable getEnclosingCallable ( ) { result = this .getScope ( ) }
405
+
406
+ // TODO: check that this gives useful results
407
+ Location getLocation ( ) { result = super .getNode ( 0 ) .getLocation ( ) }
408
+ }
409
+
410
+ BasicBlock getImmediateBasicBlockDominator ( BasicBlock bb ) {
411
+ result = bb .getImmediateDominator ( )
412
+ }
413
+
414
+ BasicBlock getABasicBlockSuccessor ( BasicBlock bb ) { result = bb .getASuccessor ( ) }
415
+
416
+ class CapturedVariable extends LocalVariable {
417
+ Function f ;
418
+
419
+ CapturedVariable ( ) {
420
+ // exists(this.getScopeEntryDefinition())
421
+ this .getScope ( ) = f and
422
+ (
423
+ this .getALoad ( ) .getScope ( ) != f
424
+ or
425
+ this .getAStore ( ) .getScope ( ) != f
426
+ )
427
+ }
428
+
429
+ Callable getCallable ( ) { result = f }
430
+
431
+ Location getLocation ( ) { result = f .getLocation ( ) }
432
+ }
433
+
434
+ class CapturedParameter extends CapturedVariable {
435
+ CapturedParameter ( ) { this .isParameter ( ) }
436
+
437
+ ControlFlowNode getCfgNode ( ) { result .getNode ( ) .( Parameter ) = this .getAnAccess ( ) }
438
+ }
439
+
440
+ class Expr extends ExprCfgNode {
441
+ predicate hasCfgNode ( BasicBlock bb , int i ) { this = bb .getNode ( i ) }
442
+ }
443
+
444
+ class VariableWrite extends ControlFlowNode {
445
+ CapturedVariable v ;
446
+
447
+ VariableWrite ( ) { this = v .getAStore ( ) .getAFlowNode ( ) }
448
+
449
+ CapturedVariable getVariable ( ) { result = v }
450
+
451
+ predicate hasCfgNode ( BasicBlock bb , int i ) { this = bb .getNode ( i ) }
452
+ }
453
+
454
+ class VariableRead extends Expr {
455
+ CapturedVariable v ;
456
+
457
+ VariableRead ( ) { this = v .getALoad ( ) .getAFlowNode ( ) }
458
+
459
+ CapturedVariable getVariable ( ) { result = v }
460
+ }
461
+
462
+ class ClosureExpr extends Expr {
463
+ ClosureExpr ( ) { this .getNode ( ) instanceof CallableExpr }
464
+
465
+ predicate hasBody ( Callable body ) { body = this .getNode ( ) .( CallableExpr ) .getInnerScope ( ) }
466
+
467
+ predicate hasAliasedAccess ( Expr f ) { closureFlowStep + ( this , f ) and not closureFlowStep ( f , _) }
468
+ }
469
+
470
+ // TODO: Some basic blocks will not have an enclosing callable
471
+ // as their scope are not `Function`s. This leads to inconsistency failures.
472
+ class Callable extends Scope {
473
+ predicate isConstructor ( ) { none ( ) }
474
+ }
475
+ }
476
+
477
+ class CapturedVariable = CaptureInput:: CapturedVariable ;
478
+
479
+ class ClosureExpr = CaptureInput:: ClosureExpr ;
480
+
481
+ module Flow = Shared:: Flow< CaptureInput > ;
482
+
483
+ private Flow:: ClosureNode asClosureNode ( Node n ) {
484
+ result = n .( CaptureNode ) .getSynthesizedCaptureNode ( )
485
+ or
486
+ result .( Flow:: ExprNode ) .getExpr ( ) = n .( CfgNode ) .getNode ( )
487
+ or
488
+ result .( Flow:: VariableWriteSourceNode ) .getVariableWrite ( ) = n .( CfgNode ) .getNode ( )
489
+ or
490
+ result .( Flow:: ExprPostUpdateNode ) .getExpr ( ) =
491
+ n .( PostUpdateNode ) .getPreUpdateNode ( ) .( CfgNode ) .getNode ( )
492
+ or
493
+ result .( Flow:: ParameterNode ) .getParameter ( ) .getCfgNode ( ) = n .( CfgNode ) .getNode ( )
494
+ or
495
+ result .( Flow:: ThisParameterNode ) .getCallable ( ) = n .( LambdaSelfReferenceNode ) .getCallable ( )
496
+ }
497
+
498
+ predicate storeStep ( Node nodeFrom , CapturedVariableContent c , Node nodeTo ) {
499
+ Flow:: storeStep ( asClosureNode ( nodeFrom ) , c .getVariable ( ) , asClosureNode ( nodeTo ) )
500
+ }
501
+
502
+ predicate readStep ( Node nodeFrom , CapturedVariableContent c , Node nodeTo ) {
503
+ Flow:: readStep ( asClosureNode ( nodeFrom ) , c .getVariable ( ) , asClosureNode ( nodeTo ) )
504
+ }
505
+
506
+ predicate valueStep ( Node nodeFrom , Node nodeTo ) {
507
+ Flow:: localFlowStep ( asClosureNode ( nodeFrom ) , asClosureNode ( nodeTo ) )
508
+ }
509
+
510
+ // Note: Learn from JS, https://github.com/github/codeql/pull/14412
511
+ // - JS: Capture flow
512
+ // - JS: Disallow consecutive captured contents
513
+ private module Debug {
514
+ predicate flowStoreStep (
515
+ Node nodeFrom , Flow:: ClosureNode closureNodeFrom , CapturedVariable v ,
516
+ Flow:: ClosureNode closureNodeTo , Node nodeTo
517
+ ) {
518
+ closureNodeFrom = asClosureNode ( nodeFrom ) and
519
+ closureNodeTo = asClosureNode ( nodeTo ) and
520
+ Flow:: storeStep ( closureNodeFrom , v , closureNodeTo )
521
+ }
522
+
523
+ predicate flowReadStep (
524
+ Node nodeFrom , Flow:: ClosureNode closureNodeFrom , CapturedVariable v ,
525
+ Flow:: ClosureNode closureNodeTo , Node nodeTo
526
+ ) {
527
+ closureNodeFrom = asClosureNode ( nodeFrom ) and
528
+ closureNodeTo = asClosureNode ( nodeTo ) and
529
+ Flow:: readStep ( closureNodeFrom , v , closureNodeTo )
530
+ }
531
+
532
+ predicate flowValueStep (
533
+ Node nodeFrom , Flow:: ClosureNode closureNodeFrom , Flow:: ClosureNode closureNodeTo , Node nodeTo
534
+ ) {
535
+ closureNodeFrom = asClosureNode ( nodeFrom ) and
536
+ closureNodeTo = asClosureNode ( nodeTo ) and
537
+ Flow:: localFlowStep ( closureNodeFrom , closureNodeTo )
538
+ }
539
+ }
540
+ }
541
+
381
542
//--------
382
543
// Local flow
383
544
//--------
@@ -471,6 +632,8 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
471
632
simpleLocalFlowStepForTypetracking ( nodeFrom , nodeTo )
472
633
or
473
634
summaryFlowSteps ( nodeFrom , nodeTo )
635
+ or
636
+ variableCaptureFlowStep ( nodeFrom , nodeTo )
474
637
}
475
638
476
639
/**
@@ -494,6 +657,11 @@ predicate summaryFlowSteps(Node nodeFrom, Node nodeTo) {
494
657
IncludePostUpdateFlow< PhaseDependentFlow< summaryLocalStep / 2 > :: step / 2 > :: step ( nodeFrom , nodeTo )
495
658
}
496
659
660
+ predicate variableCaptureFlowStep ( Node nodeFrom , Node nodeTo ) {
661
+ IncludePostUpdateFlow< PhaseDependentFlow< VariableCapture:: valueStep / 2 > :: step / 2 > :: step ( nodeFrom ,
662
+ nodeTo )
663
+ }
664
+
497
665
/** `ModuleVariable`s are accessed via jump steps at runtime. */
498
666
predicate runtimeJumpStep ( Node nodeFrom , Node nodeTo ) {
499
667
// Module variable read
@@ -557,7 +725,7 @@ predicate compatibleTypes(DataFlowType t1, DataFlowType t2) { any() }
557
725
558
726
predicate typeStrongerThan ( DataFlowType t1 , DataFlowType t2 ) { none ( ) }
559
727
560
- predicate localMustFlowStep ( Node node1 , Node node2 ) { none ( ) }
728
+ predicate localMustFlowStep ( Node nodeFrom , Node nodeTo ) { none ( ) }
561
729
562
730
/**
563
731
* Gets the type of `node`.
@@ -661,6 +829,8 @@ predicate storeStep(Node nodeFrom, ContentSet c, Node nodeTo) {
661
829
synthStarArgsElementParameterNodeStoreStep ( nodeFrom , c , nodeTo )
662
830
or
663
831
synthDictSplatArgumentNodeStoreStep ( nodeFrom , c , nodeTo )
832
+ or
833
+ VariableCapture:: storeStep ( nodeFrom , c , nodeTo )
664
834
}
665
835
666
836
/**
@@ -864,6 +1034,8 @@ predicate readStep(Node nodeFrom, ContentSet c, Node nodeTo) {
864
1034
nodeTo .( FlowSummaryNode ) .getSummaryNode ( ) )
865
1035
or
866
1036
synthDictSplatParameterNodeReadStep ( nodeFrom , c , nodeTo )
1037
+ or
1038
+ VariableCapture:: readStep ( nodeFrom , c , nodeTo )
867
1039
}
868
1040
869
1041
/** Data flows from a sequence to a subscript of the sequence. */
@@ -993,6 +1165,8 @@ predicate nodeIsHidden(Node n) {
993
1165
n instanceof SynthDictSplatArgumentNode
994
1166
or
995
1167
n instanceof SynthDictSplatParameterNode
1168
+ // or
1169
+ // n instanceof CaptureNode
996
1170
}
997
1171
998
1172
class LambdaCallKind = Unit ;
@@ -1029,6 +1203,11 @@ predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preserves
1029
1203
*/
1030
1204
predicate allowParameterReturnInSelf ( ParameterNode p ) {
1031
1205
FlowSummaryImpl:: Private:: summaryAllowParameterReturnInSelf ( p )
1206
+ or
1207
+ exists ( Function f |
1208
+ VariableCapture:: Flow:: heuristicAllowInstanceParameterReturnInSelf ( f ) and
1209
+ p = TLambdaSelfReferenceNode ( f )
1210
+ )
1032
1211
}
1033
1212
1034
1213
/** An approximated `Content`. */
0 commit comments