@@ -378,6 +378,194 @@ 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
+ this .getScope ( ) = f and
421
+ this .getAnAccess ( ) .getScope ( ) != f
422
+ }
423
+
424
+ Callable getCallable ( ) { result = f }
425
+
426
+ Location getLocation ( ) { result = f .getLocation ( ) }
427
+
428
+ Scope getACapturingScope ( ) {
429
+ result = this .getAnAccess ( ) .getScope ( ) .getScope * ( ) and
430
+ result .getScope + ( ) = f
431
+ }
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 ( ) {
464
+ this .getNode ( ) instanceof CallableExpr
465
+ or
466
+ this .getNode ( ) instanceof Comp
467
+ }
468
+
469
+ predicate hasBody ( Callable body ) {
470
+ body = this .getNode ( ) .( CallableExpr ) .getInnerScope ( )
471
+ or
472
+ body = this .getNode ( ) .( Comp ) .getFunction ( )
473
+ }
474
+
475
+ predicate hasAliasedAccess ( Expr f ) { closureFlowStep + ( this , f ) and not closureFlowStep ( f , _) }
476
+ }
477
+
478
+ class Callable extends Scope {
479
+ predicate isConstructor ( ) { none ( ) }
480
+ }
481
+ }
482
+
483
+ class CapturedVariable = CaptureInput:: CapturedVariable ;
484
+
485
+ class ClosureExpr = CaptureInput:: ClosureExpr ;
486
+
487
+ module Flow = Shared:: Flow< CaptureInput > ;
488
+
489
+ private Flow:: ClosureNode asClosureNode ( Node n ) {
490
+ result = n .( CaptureNode ) .getSynthesizedCaptureNode ( )
491
+ or
492
+ result .( Flow:: ExprNode ) .getExpr ( ) = n .( CfgNode ) .getNode ( )
493
+ or
494
+ result .( Flow:: VariableWriteSourceNode ) .getVariableWrite ( ) = n .( CfgNode ) .getNode ( )
495
+ or
496
+ result .( Flow:: ExprPostUpdateNode ) .getExpr ( ) =
497
+ n .( PostUpdateNode ) .getPreUpdateNode ( ) .( CfgNode ) .getNode ( )
498
+ or
499
+ result .( Flow:: ParameterNode ) .getParameter ( ) .getCfgNode ( ) = n .( CfgNode ) .getNode ( )
500
+ or
501
+ result .( Flow:: ThisParameterNode ) .getCallable ( ) = n .( LambdaSelfReferenceNode ) .getCallable ( )
502
+ }
503
+
504
+ predicate storeStep ( Node nodeFrom , CapturedVariableContent c , Node nodeTo ) {
505
+ Flow:: storeStep ( asClosureNode ( nodeFrom ) , c .getVariable ( ) , asClosureNode ( nodeTo ) )
506
+ }
507
+
508
+ predicate readStep ( Node nodeFrom , CapturedVariableContent c , Node nodeTo ) {
509
+ Flow:: readStep ( asClosureNode ( nodeFrom ) , c .getVariable ( ) , asClosureNode ( nodeTo ) )
510
+ }
511
+
512
+ predicate valueStep ( Node nodeFrom , Node nodeTo ) {
513
+ Flow:: localFlowStep ( asClosureNode ( nodeFrom ) , asClosureNode ( nodeTo ) )
514
+ }
515
+
516
+ // Note: Learn from JS, https://github.com/github/codeql/pull/14412
517
+ // - JS: Capture flow
518
+ // - JS: Disallow consecutive captured contents
519
+ private module Debug {
520
+ predicate flowStoreStep (
521
+ Node nodeFrom , Flow:: ClosureNode closureNodeFrom , CapturedVariable v ,
522
+ Flow:: ClosureNode closureNodeTo , Node nodeTo
523
+ ) {
524
+ closureNodeFrom = asClosureNode ( nodeFrom ) and
525
+ closureNodeTo = asClosureNode ( nodeTo ) and
526
+ Flow:: storeStep ( closureNodeFrom , v , closureNodeTo )
527
+ }
528
+
529
+ predicate unmappedFlowStoreStep (
530
+ Flow:: ClosureNode closureNodeFrom , CapturedVariable v , Flow:: ClosureNode closureNodeTo
531
+ ) {
532
+ Flow:: storeStep ( closureNodeFrom , v , closureNodeTo ) and
533
+ not flowStoreStep ( _, closureNodeFrom , v , closureNodeTo , _)
534
+ }
535
+
536
+ predicate flowReadStep (
537
+ Node nodeFrom , Flow:: ClosureNode closureNodeFrom , CapturedVariable v ,
538
+ Flow:: ClosureNode closureNodeTo , Node nodeTo
539
+ ) {
540
+ closureNodeFrom = asClosureNode ( nodeFrom ) and
541
+ closureNodeTo = asClosureNode ( nodeTo ) and
542
+ Flow:: readStep ( closureNodeFrom , v , closureNodeTo )
543
+ }
544
+
545
+ predicate unmappedFlowReadStep (
546
+ Flow:: ClosureNode closureNodeFrom , CapturedVariable v , Flow:: ClosureNode closureNodeTo
547
+ ) {
548
+ Flow:: readStep ( closureNodeFrom , v , closureNodeTo ) and
549
+ not flowReadStep ( _, closureNodeFrom , v , closureNodeTo , _)
550
+ }
551
+
552
+ predicate flowValueStep (
553
+ Node nodeFrom , Flow:: ClosureNode closureNodeFrom , Flow:: ClosureNode closureNodeTo , Node nodeTo
554
+ ) {
555
+ closureNodeFrom = asClosureNode ( nodeFrom ) and
556
+ closureNodeTo = asClosureNode ( nodeTo ) and
557
+ Flow:: localFlowStep ( closureNodeFrom , closureNodeTo )
558
+ }
559
+
560
+ predicate unmappedFlowValueStep (
561
+ Flow:: ClosureNode closureNodeFrom , Flow:: ClosureNode closureNodeTo
562
+ ) {
563
+ Flow:: localFlowStep ( closureNodeFrom , closureNodeTo ) and
564
+ not flowValueStep ( _, closureNodeFrom , closureNodeTo , _)
565
+ }
566
+ }
567
+ }
568
+
381
569
//--------
382
570
// Local flow
383
571
//--------
@@ -471,6 +659,8 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
471
659
simpleLocalFlowStepForTypetracking ( nodeFrom , nodeTo )
472
660
or
473
661
summaryFlowSteps ( nodeFrom , nodeTo )
662
+ or
663
+ variableCaptureFlowStep ( nodeFrom , nodeTo )
474
664
}
475
665
476
666
/**
@@ -494,6 +684,11 @@ predicate summaryFlowSteps(Node nodeFrom, Node nodeTo) {
494
684
IncludePostUpdateFlow< PhaseDependentFlow< summaryLocalStep / 2 > :: step / 2 > :: step ( nodeFrom , nodeTo )
495
685
}
496
686
687
+ predicate variableCaptureFlowStep ( Node nodeFrom , Node nodeTo ) {
688
+ IncludePostUpdateFlow< PhaseDependentFlow< VariableCapture:: valueStep / 2 > :: step / 2 > :: step ( nodeFrom ,
689
+ nodeTo )
690
+ }
691
+
497
692
/** `ModuleVariable`s are accessed via jump steps at runtime. */
498
693
predicate runtimeJumpStep ( Node nodeFrom , Node nodeTo ) {
499
694
// Module variable read
@@ -557,7 +752,7 @@ predicate compatibleTypes(DataFlowType t1, DataFlowType t2) { any() }
557
752
558
753
predicate typeStrongerThan ( DataFlowType t1 , DataFlowType t2 ) { none ( ) }
559
754
560
- predicate localMustFlowStep ( Node node1 , Node node2 ) { none ( ) }
755
+ predicate localMustFlowStep ( Node nodeFrom , Node nodeTo ) { none ( ) }
561
756
562
757
/**
563
758
* Gets the type of `node`.
@@ -661,6 +856,8 @@ predicate storeStep(Node nodeFrom, ContentSet c, Node nodeTo) {
661
856
synthStarArgsElementParameterNodeStoreStep ( nodeFrom , c , nodeTo )
662
857
or
663
858
synthDictSplatArgumentNodeStoreStep ( nodeFrom , c , nodeTo )
859
+ or
860
+ VariableCapture:: storeStep ( nodeFrom , c , nodeTo )
664
861
}
665
862
666
863
/**
@@ -864,6 +1061,8 @@ predicate readStep(Node nodeFrom, ContentSet c, Node nodeTo) {
864
1061
nodeTo .( FlowSummaryNode ) .getSummaryNode ( ) )
865
1062
or
866
1063
synthDictSplatParameterNodeReadStep ( nodeFrom , c , nodeTo )
1064
+ or
1065
+ VariableCapture:: readStep ( nodeFrom , c , nodeTo )
867
1066
}
868
1067
869
1068
/** Data flows from a sequence to a subscript of the sequence. */
@@ -993,6 +1192,8 @@ predicate nodeIsHidden(Node n) {
993
1192
n instanceof SynthDictSplatArgumentNode
994
1193
or
995
1194
n instanceof SynthDictSplatParameterNode
1195
+ // or
1196
+ // n instanceof CaptureNode
996
1197
}
997
1198
998
1199
class LambdaCallKind = Unit ;
@@ -1029,6 +1230,11 @@ predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preserves
1029
1230
*/
1030
1231
predicate allowParameterReturnInSelf ( ParameterNode p ) {
1031
1232
FlowSummaryImpl:: Private:: summaryAllowParameterReturnInSelf ( p )
1233
+ or
1234
+ exists ( Function f |
1235
+ VariableCapture:: Flow:: heuristicAllowInstanceParameterReturnInSelf ( f ) and
1236
+ p = TLambdaSelfReferenceNode ( f )
1237
+ )
1032
1238
}
1033
1239
1034
1240
/** An approximated `Content`. */
0 commit comments