Skip to content

Commit 394563d

Browse files
authored
Merge pull request #1807 from hvitved/csharp/dataflow/barrier-guard
Approved by calumgrant
2 parents 3d3797f + 6e7ef66 commit 394563d

File tree

11 files changed

+171
-148
lines changed

11 files changed

+171
-148
lines changed

change-notes/1.23/analysis-csharp.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ The following changes in version 1.23 affect C# analysis in all applications.
1616
## Changes to QL libraries
1717

1818
* The new class `NamespaceAccess` models accesses to namespaces, for example in `nameof` expressions.
19+
* The data-flow library now makes it easier to specify barriers/sanitizers
20+
arising from guards by overriding the predicate
21+
`isBarrierGuard`/`isSanitizerGuard` on data-flow and taint-tracking
22+
configurations respectively.
1923

2024
## Changes to autobuilder
21-

csharp/ql/src/semmle/code/csharp/controlflow/Guards.qll

Lines changed: 96 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,23 @@ private import semmle.code.csharp.controlflow.BasicBlocks
1313
private import semmle.code.csharp.controlflow.internal.Completion
1414
private 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. */
1734
abstract class AbstractValue extends TAbstractValue {
1835
/** Holds if the `s` branch out of `cfe` is taken iff `e` has this value. */
@@ -481,7 +498,7 @@ class GuardedExpr extends AccessOrCallExpr {
481498
* left-most qualifier, then so must the other (accessing the same SSA
482499
* variable).
483500
*/
484-
Expr getAGuard(Expr sub, AbstractValue v) {
501+
Guard getAGuard(Expr sub, AbstractValue v) {
485502
result = g and
486503
sub = sub0 and
487504
v = v0
@@ -492,7 +509,7 @@ class GuardedExpr extends AccessOrCallExpr {
492509
* expression is guarded by a structurally equal expression having abstract
493510
* value `v`.
494511
*/
495-
predicate mustHaveValue(AbstractValue v) { exists(Expr e | e = this.getAGuard(e, v)) }
512+
predicate mustHaveValue(AbstractValue v) { g = this.getAGuard(g, v) }
496513

497514
/**
498515
* Holds if this expression is guarded by expression `cond`, which must
@@ -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
@@ -546,7 +563,7 @@ class GuardedControlFlowNode extends ControlFlow::Nodes::ElementNode {
546563
* left-most qualifier, then so must the other (accessing the same SSA
547564
* variable).
548565
*/
549-
Expr getAGuard(Expr sub, AbstractValue v) {
566+
Guard getAGuard(Expr sub, AbstractValue v) {
550567
result = g and
551568
sub = sub0 and
552569
v = v0
@@ -557,7 +574,7 @@ class GuardedControlFlowNode extends ControlFlow::Nodes::ElementNode {
557574
* control flow node is guarded by a structurally equal expression having
558575
* abstract value `v`.
559576
*/
560-
predicate mustHaveValue(AbstractValue v) { exists(Expr e | e = this.getAGuard(e, v)) }
577+
predicate mustHaveValue(AbstractValue v) { g = this.getAGuard(g, v) }
561578
}
562579

563580
/**
@@ -587,7 +604,7 @@ class GuardedDataFlowNode extends DataFlow::ExprNode {
587604

588605
GuardedDataFlowNode() {
589606
exists(ControlFlow::Nodes::ElementNode cfn | exists(this.getExprAtNode(cfn)) |
590-
isGuardedByNode(cfn, g, sub0, v0)
607+
g.controlsNode(cfn, sub0, v0)
591608
)
592609
}
593610

@@ -602,7 +619,7 @@ class GuardedDataFlowNode extends DataFlow::ExprNode {
602619
* left-most qualifier, then so must the other (accessing the same SSA
603620
* variable).
604621
*/
605-
Expr getAGuard(Expr sub, AbstractValue v) {
622+
Guard getAGuard(Expr sub, AbstractValue v) {
606623
result = g and
607624
sub = sub0 and
608625
v = v0
@@ -613,7 +630,7 @@ class GuardedDataFlowNode extends DataFlow::ExprNode {
613630
* data flow node is guarded by a structurally equal expression having
614631
* abstract value `v`.
615632
*/
616-
predicate mustHaveValue(AbstractValue v) { exists(Expr e | e = this.getAGuard(e, v)) }
633+
predicate mustHaveValue(AbstractValue v) { g = this.getAGuard(g, v) }
617634
}
618635

619636
/** An expression guarded by a `null` check. */
@@ -761,72 +778,49 @@ module Internal {
761778
e = any(BinaryArithmeticOperation bao | result = bao.getAnOperand())
762779
}
763780

764-
/** An expression whose value may control the execution of another element. */
765-
class Guard extends Expr {
766-
private AbstractValue val;
781+
/** Holds if basic block `bb` only is reached when guard `g` has abstract value `v`. */
782+
private predicate guardControls(Guard g, BasicBlock bb, AbstractValue v) {
783+
exists(ControlFlowElement cfe, ConditionalSuccessor s, AbstractValue v0, Guard g0 |
784+
cfe.controlsBlock(bb, s)
785+
|
786+
v0.branch(cfe, s, g0) and
787+
impliesSteps(g0, v0, g, v)
788+
)
789+
}
767790

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)
791+
/**
792+
* Holds if control flow node `cfn` only is reached when guard `g` evaluates to `v`,
793+
* because of an assertion.
794+
*/
795+
private predicate guardAssertionControlsNode(Guard g, ControlFlow::Node cfn, AbstractValue v) {
796+
exists(Assertion a, Guard g0, AbstractValue v0 |
797+
asserts(a, g0, v0) and
798+
impliesSteps(g0, v0, g, v)
799+
|
800+
a.strictlyDominates(cfn.getBasicBlock())
779801
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-
)
802+
exists(BasicBlock bb, int i, int j | bb.getNode(i) = a.getAControlFlowNode() |
803+
bb.getNode(j) = cfn and
804+
j > i
811805
)
812-
}
806+
)
807+
}
813808

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-
}
809+
/**
810+
* Holds if control flow element `cfe` only is reached when guard `g` evaluates to `v`,
811+
* because of an assertion.
812+
*/
813+
private predicate guardAssertionControlsElement(Guard g, ControlFlowElement cfe, AbstractValue v) {
814+
forex(ControlFlow::Node cfn | cfn = cfe.getAControlFlowNode() |
815+
guardAssertionControlsNode(g, cfn, v)
816+
)
817+
}
823818

824-
/** Same as `this.getAChildExpr*()`, but avoids `fastTC`. */
825-
Expr getAChildExprStar() {
826-
result = this
827-
or
828-
result = this.getAChildExprStar().getAChildExpr()
829-
}
819+
/** Same as `this.getAChildExpr*()`, but avoids `fastTC`. */
820+
private Expr getAChildExprStar(Guard g) {
821+
result = g
822+
or
823+
result = getAChildExprStar(g).getAChildExpr()
830824
}
831825

832826
/**
@@ -1273,8 +1267,24 @@ module Internal {
12731267
private import semmle.code.csharp.Caching
12741268

12751269
cached
1276-
predicate isCustomNullCheck(Call call, Expr arg, BooleanValue v, boolean isNull) {
1270+
predicate isGuard(Expr e, AbstractValue val) {
12771271
Stages::ControlFlowStage::forceCachingInSameStage() and
1272+
e.getType() instanceof BoolType and
1273+
not e instanceof BoolLiteral and
1274+
not e instanceof SwitchCaseExpr and
1275+
not e instanceof PatternExpr and
1276+
val = TBooleanValue(_)
1277+
or
1278+
e instanceof DereferenceableExpr and
1279+
val = TNullValue(_)
1280+
or
1281+
val.branch(_, _, e)
1282+
or
1283+
asserts(_, e, val)
1284+
}
1285+
1286+
cached
1287+
predicate isCustomNullCheck(Call call, Expr arg, BooleanValue v, boolean isNull) {
12781288
exists(Callable callable, Parameter p |
12791289
arg = call.getArgumentForParameter(any(Parameter p0 | p0.getSourceDeclaration() = p)) and
12801290
call.getTarget().getSourceDeclaration() = callable and
@@ -1355,7 +1365,7 @@ module Internal {
13551365
)
13561366
)
13571367
or
1358-
v1 = g1.getAValue() and
1368+
isGuard(g1, v1) and
13591369
v1 = any(MatchValue mv |
13601370
mv.isMatch() and
13611371
g2 = g1 and
@@ -1379,20 +1389,20 @@ module Internal {
13791389
v2 = v1
13801390
or
13811391
g2 = g1.(AssignExpr).getRValue() and
1382-
v1 = g1.getAValue() and
1392+
isGuard(g1, v1) and
13831393
v2 = v1
13841394
or
13851395
g2 = g1.(Assignment).getLValue() and
1386-
v1 = g1.getAValue() and
1396+
isGuard(g1, v1) and
13871397
v2 = v1
13881398
or
13891399
g2 = g1.(CastExpr).getExpr() and
1390-
v1 = g1.getAValue() and
1400+
isGuard(g1, v1) and
13911401
v2 = v1.(NullValue)
13921402
or
13931403
exists(PreSsa::Definition def | def.getDefinition().getSource() = g2 |
13941404
g1 = def.getARead() and
1395-
v1 = g1.getAValue() and
1405+
isGuard(g1, v1) and
13961406
v2 = v1
13971407
)
13981408
or
@@ -1473,10 +1483,10 @@ module Internal {
14731483
pragma[noinline]
14741484
private predicate candidateAux(AccessOrCallExpr e, Declaration target, BasicBlock bb) {
14751485
target = e.getTarget() and
1476-
exists(Guard g | e = g.getAChildExprStar() |
1477-
g.controls(bb, _)
1486+
exists(Guard g | e = getAChildExprStar(g) |
1487+
guardControls(g, bb, _)
14781488
or
1479-
g.assertionControlsNode(bb.getANode(), _)
1489+
guardAssertionControlsNode(g, bb.getANode(), _)
14801490
)
14811491
}
14821492
}
@@ -1492,7 +1502,7 @@ module Internal {
14921502
) {
14931503
Stages::GuardsStage::forceCachingInSameStage() and
14941504
cfn = guarded.getAControlFlowNode() and
1495-
g.controls(cfn.getBasicBlock(), v) and
1505+
guardControls(g, cfn.getBasicBlock(), v) and
14961506
exists(ConditionOnExprComparisonConfig c | c.same(sub, guarded))
14971507
}
14981508

@@ -1504,7 +1514,7 @@ module Internal {
15041514
isGuardedByNode0(cfn, guarded, g, sub, v)
15051515
)
15061516
or
1507-
g.assertionControlsElement(guarded, v) and
1517+
guardAssertionControlsElement(g, guarded, v) and
15081518
exists(ConditionOnExprComparisonConfig c | c.same(sub, guarded))
15091519
}
15101520

@@ -1513,7 +1523,7 @@ module Internal {
15131523
AccessOrCallExpr guarded, Guard g, AccessOrCallExpr sub, AbstractValue v
15141524
) {
15151525
isGuardedByExpr1(guarded, g, sub, v) and
1516-
sub = g.getAChildExprStar() and
1526+
sub = getAChildExprStar(g) and
15171527
forall(Ssa::Definition def | def = sub.getAnSsaQualifier(_) |
15181528
def = guarded.getAnSsaQualifier(_)
15191529
)
@@ -1525,7 +1535,7 @@ module Internal {
15251535
) {
15261536
isGuardedByNode0(guarded, _, g, sub, v)
15271537
or
1528-
g.assertionControlsNode(guarded, v) and
1538+
guardAssertionControlsNode(g, guarded, v) and
15291539
exists(ConditionOnExprComparisonConfig c | c.same(sub, guarded.getElement()))
15301540
}
15311541

@@ -1542,7 +1552,7 @@ module Internal {
15421552
ControlFlow::Nodes::ElementNode guarded, Guard g, AccessOrCallExpr sub, AbstractValue v
15431553
) {
15441554
isGuardedByNode1(guarded, g, sub, v) and
1545-
sub = g.getAChildExprStar() and
1555+
sub = getAChildExprStar(g) and
15461556
forall(Ssa::Definition def | def = sub.getAnSsaQualifier(_) | isGuardedByNode2(guarded, def))
15471557
}
15481558

@@ -1560,7 +1570,7 @@ module Internal {
15601570
forex(ControlFlow::Node cfn | cfn = g1.getAControlFlowNode() |
15611571
exists(Ssa::ExplicitDefinition def | def.getADefinition().getSource() = g2 |
15621572
g1 = def.getAReadAtNode(cfn) and
1563-
v1 = g1.getAValue() and
1573+
isGuard(g1, v1) and
15641574
v2 = v1
15651575
)
15661576
)
@@ -1578,7 +1588,7 @@ module Internal {
15781588
predicate preImpliesSteps(Guard g1, AbstractValue v1, Guard g2, AbstractValue v2) {
15791589
g1 = g2 and
15801590
v1 = v2 and
1581-
v1 = g1.getAValue()
1591+
isGuard(g1, v1)
15821592
or
15831593
exists(Expr mid, AbstractValue vMid | preImpliesSteps(g1, v1, mid, vMid) |
15841594
preImpliesStep(mid, vMid, g2, v2)
@@ -1595,7 +1605,7 @@ module Internal {
15951605
predicate impliesSteps(Guard g1, AbstractValue v1, Guard g2, AbstractValue v2) {
15961606
g1 = g2 and
15971607
v1 = v2 and
1598-
v1 = g1.getAValue()
1608+
isGuard(g1, v1)
15991609
or
16001610
exists(Expr mid, AbstractValue vMid | impliesSteps(g1, v1, mid, vMid) |
16011611
impliesStep(mid, vMid, g2, v2)

csharp/ql/src/semmle/code/csharp/dataflow/Nullness.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ private predicate dereferenceAt(BasicBlock bb, int i, Ssa::Definition def, Deref
127127
private predicate exprImpliesSsaDef(
128128
Expr e, G::AbstractValue vExpr, Ssa::Definition def, G::AbstractValue vDef
129129
) {
130-
exists(G::Internal::Guard g | G::Internal::impliesSteps(e, vExpr, g, vDef) |
130+
exists(G::Guard g | G::Internal::impliesSteps(e, vExpr, g, vDef) |
131131
g = def.getARead()
132132
or
133133
g = def.(Ssa::ExplicitDefinition).getADefinition().getTargetAccess()

csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -182,12 +182,15 @@ abstract class NonLocalJumpNode extends Node {
182182
*
183183
* It is important that all extending classes in scope are disjoint.
184184
*/
185-
class BarrierGuard extends Internal::Guard {
186-
/** NOT YET SUPPORTED. Holds if this guard validates `e` upon evaluating to `v`. */
187-
abstract deprecated predicate checks(Expr e, AbstractValue v);
185+
class BarrierGuard extends Guard {
186+
/** Holds if this guard validates `e` upon evaluating to `v`. */
187+
abstract predicate checks(Expr e, AbstractValue v);
188188

189189
/** Gets a node guarded by this guard. */
190-
final Node getAGuardedNode() {
191-
none() // stub
190+
final ExprNode getAGuardedNode() {
191+
exists(Expr e, AbstractValue v |
192+
this.checks(e, v) and
193+
this.controlsNode(result.getControlFlowNode(), e, v)
194+
)
192195
}
193196
}

0 commit comments

Comments
 (0)