@@ -13,6 +13,23 @@ private import semmle.code.csharp.controlflow.BasicBlocks
1313private import semmle.code.csharp.controlflow.internal.Completion
1414private import semmle.code.csharp.frameworks.System
1515
16+ /** An expression whose value may control the execution of another element. */
17+ class Guard extends Expr {
18+ Guard ( ) { isGuard ( this , _) }
19+
20+ /**
21+ * Holds if `cfn` is guarded by this expression having value `v`, where `sub` is
22+ * a sub expression of this expression that is structurally equal to the expression
23+ * belonging to `cfn`.
24+ *
25+ * In case `cfn` or `sub` access an SSA variable in their left-most qualifier, then
26+ * so must the other (accessing the same SSA variable).
27+ */
28+ predicate controlsNode ( ControlFlow:: Nodes:: ElementNode cfn , AccessOrCallExpr sub , AbstractValue v ) {
29+ isGuardedByNode ( cfn , this , sub , v )
30+ }
31+ }
32+
1633/** An abstract value. */
1734abstract class AbstractValue extends TAbstractValue {
1835 /** Holds if the `s` branch out of `cfe` is taken iff `e` has this value. */
@@ -533,7 +550,7 @@ class GuardedControlFlowNode extends ControlFlow::Nodes::ElementNode {
533550
534551 private AbstractValue v0 ;
535552
536- GuardedControlFlowNode ( ) { isGuardedByNode ( this , g , sub0 , v0 ) }
553+ GuardedControlFlowNode ( ) { g . controlsNode ( this , sub0 , v0 ) }
537554
538555 /**
539556 * Gets an expression that guards this control flow node. That is, this control
@@ -561,6 +578,8 @@ class GuardedControlFlowNode extends ControlFlow::Nodes::ElementNode {
561578}
562579
563580/**
581+ * DEPRECATED: Use `DataFlow::BarrierGuard` instead.
582+ *
564583 * A guarded data flow node. A guarded data flow node is like a guarded expression
565584 * (`GuardedExpr`), except control flow graph splitting is taken into account. That
566585 * is, one data flow node belonging to an expression may be guarded, while another
@@ -578,7 +597,7 @@ class GuardedControlFlowNode extends ControlFlow::Nodes::ElementNode {
578597 * In the example above, the node for `x.ToString()` is null-guarded in the
579598 * split `b == true`, but not in the split `b == false`.
580599 */
581- class GuardedDataFlowNode extends DataFlow:: ExprNode {
600+ deprecated class GuardedDataFlowNode extends DataFlow:: ExprNode {
582601 private Guard g ;
583602
584603 private AccessOrCallExpr sub0 ;
@@ -587,7 +606,7 @@ class GuardedDataFlowNode extends DataFlow::ExprNode {
587606
588607 GuardedDataFlowNode ( ) {
589608 exists ( ControlFlow:: Nodes:: ElementNode cfn | exists ( this .getExprAtNode ( cfn ) ) |
590- isGuardedByNode ( cfn , g , sub0 , v0 )
609+ g . controlsNode ( cfn , sub0 , v0 )
591610 )
592611 }
593612
@@ -621,8 +640,12 @@ class NullGuardedExpr extends GuardedExpr {
621640 NullGuardedExpr ( ) { this .mustHaveValue ( any ( NullValue v | not v .isNull ( ) ) ) }
622641}
623642
624- /** A data flow node guarded by a `null` check. */
625- class NullGuardedDataFlowNode extends GuardedDataFlowNode {
643+ /**
644+ * DEPRECATED: Use `DataFlow::BarrierGuard` instead.
645+ *
646+ * A data flow node guarded by a `null` check.
647+ */
648+ deprecated class NullGuardedDataFlowNode extends GuardedDataFlowNode {
626649 NullGuardedDataFlowNode ( ) { this .mustHaveValue ( any ( NullValue v | not v .isNull ( ) ) ) }
627650}
628651
@@ -761,72 +784,49 @@ module Internal {
761784 e = any ( BinaryArithmeticOperation bao | result = bao .getAnOperand ( ) )
762785 }
763786
764- /** An expression whose value may control the execution of another element. */
765- class Guard extends Expr {
766- private AbstractValue val ;
787+ /** Holds if basic block `bb` only is reached when guard `g` has abstract value `v`. */
788+ private predicate guardControls ( Guard g , BasicBlock bb , AbstractValue v ) {
789+ exists ( ControlFlowElement cfe , ConditionalSuccessor s , AbstractValue v0 , Guard g0 |
790+ cfe .controlsBlock ( bb , s )
791+ |
792+ v0 .branch ( cfe , s , g0 ) and
793+ impliesSteps ( g0 , v0 , g , v )
794+ )
795+ }
767796
768- Guard ( ) {
769- this .getType ( ) instanceof BoolType and
770- not this instanceof BoolLiteral and
771- not this instanceof SwitchCaseExpr and
772- not this instanceof PatternExpr and
773- val = TBooleanValue ( _)
774- or
775- this instanceof DereferenceableExpr and
776- val = TNullValue ( _)
777- or
778- val .branch ( _, _, this )
797+ /**
798+ * Holds if control flow node `cfn` only is reached when guard `g` evaluates to `v`,
799+ * because of an assertion.
800+ */
801+ private predicate guardAssertionControlsNode ( Guard g , ControlFlow:: Node cfn , AbstractValue v ) {
802+ exists ( Assertion a , Guard g0 , AbstractValue v0 |
803+ asserts ( a , g0 , v0 ) and
804+ impliesSteps ( g0 , v0 , g , v )
805+ |
806+ a .strictlyDominates ( cfn .getBasicBlock ( ) )
779807 or
780- asserts ( _, this , val )
781- }
782-
783- /** Gets an abstract value that this guard may have. */
784- AbstractValue getAValue ( ) { result = val }
785-
786- /** Holds if basic block `bb` only is reached when this guard has abstract value `v`. */
787- predicate controls ( BasicBlock bb , AbstractValue v ) {
788- exists ( ControlFlowElement cfe , ConditionalSuccessor s , AbstractValue v0 , Guard g |
789- cfe .controlsBlock ( bb , s )
790- |
791- v0 .branch ( cfe , s , g ) and
792- impliesSteps ( g , v0 , this , v )
793- )
794- }
795-
796- /**
797- * Holds if control flow node `cfn` only is reached when this guard evaluates to `v`,
798- * because of an assertion.
799- */
800- predicate assertionControlsNode ( ControlFlow:: Node cfn , AbstractValue v ) {
801- exists ( Assertion a , Guard g , AbstractValue v0 |
802- asserts ( a , g , v0 ) and
803- impliesSteps ( g , v0 , this , v )
804- |
805- a .strictlyDominates ( cfn .getBasicBlock ( ) )
806- or
807- exists ( BasicBlock bb , int i , int j | bb .getNode ( i ) = a .getAControlFlowNode ( ) |
808- bb .getNode ( j ) = cfn and
809- j > i
810- )
808+ exists ( BasicBlock bb , int i , int j | bb .getNode ( i ) = a .getAControlFlowNode ( ) |
809+ bb .getNode ( j ) = cfn and
810+ j > i
811811 )
812- }
812+ )
813+ }
813814
814- /**
815- * Holds if control flow element `cfe` only is reached when this guard evaluates to `v`,
816- * because of an assertion.
817- */
818- predicate assertionControlsElement ( ControlFlowElement cfe , AbstractValue v ) {
819- forex ( ControlFlow:: Node cfn | cfn = cfe .getAControlFlowNode ( ) |
820- this . assertionControlsNode ( cfn , v )
821- )
822- }
815+ /**
816+ * Holds if control flow element `cfe` only is reached when guard `g` evaluates to `v`,
817+ * because of an assertion.
818+ */
819+ private predicate guardAssertionControlsElement ( Guard g , ControlFlowElement cfe , AbstractValue v ) {
820+ forex ( ControlFlow:: Node cfn | cfn = cfe .getAControlFlowNode ( ) |
821+ guardAssertionControlsNode ( g , cfn , v )
822+ )
823+ }
823824
824- /** Same as `this.getAChildExpr*()`, but avoids `fastTC`. */
825- Expr getAChildExprStar ( ) {
826- result = this
827- or
828- result = this .getAChildExprStar ( ) .getAChildExpr ( )
829- }
825+ /** Same as `this.getAChildExpr*()`, but avoids `fastTC`. */
826+ private Expr getAChildExprStar ( Guard g ) {
827+ result = g
828+ or
829+ result = getAChildExprStar ( g ) .getAChildExpr ( )
830830 }
831831
832832 /**
@@ -1273,8 +1273,24 @@ module Internal {
12731273 private import semmle.code.csharp.Caching
12741274
12751275 cached
1276- predicate isCustomNullCheck ( Call call , Expr arg , BooleanValue v , boolean isNull ) {
1276+ predicate isGuard ( Expr e , AbstractValue val ) {
12771277 Stages:: ControlFlowStage:: forceCachingInSameStage ( ) and
1278+ e .getType ( ) instanceof BoolType and
1279+ not e instanceof BoolLiteral and
1280+ not e instanceof SwitchCaseExpr and
1281+ not e instanceof PatternExpr and
1282+ val = TBooleanValue ( _)
1283+ or
1284+ e instanceof DereferenceableExpr and
1285+ val = TNullValue ( _)
1286+ or
1287+ val .branch ( _, _, e )
1288+ or
1289+ asserts ( _, e , val )
1290+ }
1291+
1292+ cached
1293+ predicate isCustomNullCheck ( Call call , Expr arg , BooleanValue v , boolean isNull ) {
12781294 exists ( Callable callable , Parameter p |
12791295 arg = call .getArgumentForParameter ( any ( Parameter p0 | p0 .getSourceDeclaration ( ) = p ) ) and
12801296 call .getTarget ( ) .getSourceDeclaration ( ) = callable and
@@ -1355,7 +1371,7 @@ module Internal {
13551371 )
13561372 )
13571373 or
1358- v1 = g1 . getAValue ( ) and
1374+ isGuard ( g1 , v1 ) and
13591375 v1 = any ( MatchValue mv |
13601376 mv .isMatch ( ) and
13611377 g2 = g1 and
@@ -1379,20 +1395,20 @@ module Internal {
13791395 v2 = v1
13801396 or
13811397 g2 = g1 .( AssignExpr ) .getRValue ( ) and
1382- v1 = g1 . getAValue ( ) and
1398+ isGuard ( g1 , v1 ) and
13831399 v2 = v1
13841400 or
13851401 g2 = g1 .( Assignment ) .getLValue ( ) and
1386- v1 = g1 . getAValue ( ) and
1402+ isGuard ( g1 , v1 ) and
13871403 v2 = v1
13881404 or
13891405 g2 = g1 .( CastExpr ) .getExpr ( ) and
1390- v1 = g1 . getAValue ( ) and
1406+ isGuard ( g1 , v1 ) and
13911407 v2 = v1 .( NullValue )
13921408 or
13931409 exists ( PreSsa:: Definition def | def .getDefinition ( ) .getSource ( ) = g2 |
13941410 g1 = def .getARead ( ) and
1395- v1 = g1 . getAValue ( ) and
1411+ isGuard ( g1 , v1 ) and
13961412 v2 = v1
13971413 )
13981414 or
@@ -1473,10 +1489,10 @@ module Internal {
14731489 pragma [ noinline]
14741490 private predicate candidateAux ( AccessOrCallExpr e , Declaration target , BasicBlock bb ) {
14751491 target = e .getTarget ( ) and
1476- exists ( Guard g | e = g . getAChildExprStar ( ) |
1477- g . controls ( bb , _)
1492+ exists ( Guard g | e = getAChildExprStar ( g ) |
1493+ guardControls ( g , bb , _)
14781494 or
1479- g . assertionControlsNode ( bb .getANode ( ) , _)
1495+ guardAssertionControlsNode ( g , bb .getANode ( ) , _)
14801496 )
14811497 }
14821498 }
@@ -1492,7 +1508,7 @@ module Internal {
14921508 ) {
14931509 Stages:: GuardsStage:: forceCachingInSameStage ( ) and
14941510 cfn = guarded .getAControlFlowNode ( ) and
1495- g . controls ( cfn .getBasicBlock ( ) , v ) and
1511+ guardControls ( g , cfn .getBasicBlock ( ) , v ) and
14961512 exists ( ConditionOnExprComparisonConfig c | c .same ( sub , guarded ) )
14971513 }
14981514
@@ -1504,7 +1520,7 @@ module Internal {
15041520 isGuardedByNode0 ( cfn , guarded , g , sub , v )
15051521 )
15061522 or
1507- g . assertionControlsElement ( guarded , v ) and
1523+ guardAssertionControlsElement ( g , guarded , v ) and
15081524 exists ( ConditionOnExprComparisonConfig c | c .same ( sub , guarded ) )
15091525 }
15101526
@@ -1513,7 +1529,7 @@ module Internal {
15131529 AccessOrCallExpr guarded , Guard g , AccessOrCallExpr sub , AbstractValue v
15141530 ) {
15151531 isGuardedByExpr1 ( guarded , g , sub , v ) and
1516- sub = g . getAChildExprStar ( ) and
1532+ sub = getAChildExprStar ( g ) and
15171533 forall ( Ssa:: Definition def | def = sub .getAnSsaQualifier ( _) |
15181534 def = guarded .getAnSsaQualifier ( _)
15191535 )
@@ -1525,7 +1541,7 @@ module Internal {
15251541 ) {
15261542 isGuardedByNode0 ( guarded , _, g , sub , v )
15271543 or
1528- g . assertionControlsNode ( guarded , v ) and
1544+ guardAssertionControlsNode ( g , guarded , v ) and
15291545 exists ( ConditionOnExprComparisonConfig c | c .same ( sub , guarded .getElement ( ) ) )
15301546 }
15311547
@@ -1542,7 +1558,7 @@ module Internal {
15421558 ControlFlow:: Nodes:: ElementNode guarded , Guard g , AccessOrCallExpr sub , AbstractValue v
15431559 ) {
15441560 isGuardedByNode1 ( guarded , g , sub , v ) and
1545- sub = g . getAChildExprStar ( ) and
1561+ sub = getAChildExprStar ( g ) and
15461562 forall ( Ssa:: Definition def | def = sub .getAnSsaQualifier ( _) | isGuardedByNode2 ( guarded , def ) )
15471563 }
15481564
@@ -1560,7 +1576,7 @@ module Internal {
15601576 forex ( ControlFlow:: Node cfn | cfn = g1 .getAControlFlowNode ( ) |
15611577 exists ( Ssa:: ExplicitDefinition def | def .getADefinition ( ) .getSource ( ) = g2 |
15621578 g1 = def .getAReadAtNode ( cfn ) and
1563- v1 = g1 . getAValue ( ) and
1579+ isGuard ( g1 , v1 ) and
15641580 v2 = v1
15651581 )
15661582 )
@@ -1578,7 +1594,7 @@ module Internal {
15781594 predicate preImpliesSteps ( Guard g1 , AbstractValue v1 , Guard g2 , AbstractValue v2 ) {
15791595 g1 = g2 and
15801596 v1 = v2 and
1581- v1 = g1 . getAValue ( )
1597+ isGuard ( g1 , v1 )
15821598 or
15831599 exists ( Expr mid , AbstractValue vMid | preImpliesSteps ( g1 , v1 , mid , vMid ) |
15841600 preImpliesStep ( mid , vMid , g2 , v2 )
@@ -1595,7 +1611,7 @@ module Internal {
15951611 predicate impliesSteps ( Guard g1 , AbstractValue v1 , Guard g2 , AbstractValue v2 ) {
15961612 g1 = g2 and
15971613 v1 = v2 and
1598- v1 = g1 . getAValue ( )
1614+ isGuard ( g1 , v1 )
15991615 or
16001616 exists ( Expr mid , AbstractValue vMid | impliesSteps ( g1 , v1 , mid , vMid ) |
16011617 impliesStep ( mid , vMid , g2 , v2 )
0 commit comments