Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit ae5fb7f

Browse files
committed
C#: Introduce BarrierGuards
1 parent c642e72 commit ae5fb7f

11 files changed

Lines changed: 223 additions & 157 deletions

File tree

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: 99 additions & 83 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. */
@@ -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)

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

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

185185
/** Gets a node guarded by this guard. */
186-
final Node getAGuardedNode() {
187-
none() // stub
186+
final ExprNode getAGuardedNode() {
187+
exists(Expr e, AbstractValue v |
188+
this.checks(e, v) and
189+
this.controlsNode(result.getControlFlowNode(), e, v)
190+
)
191+
}
192+
}
193+
194+
module BarrierGuards {
195+
/** A simple guard that checks that this expression has an abstract value. */
196+
class ValueBarrierGuard extends BarrierGuard {
197+
private AbstractValue v0;
198+
199+
ValueBarrierGuard() { this.controlsNode(_, this, v0) }
200+
201+
/**
202+
* Gets the abstract value that this expression is checked against.
203+
*
204+
* For example, in
205+
*
206+
* ```
207+
* if (x == null)
208+
* ...
209+
* ```
210+
*
211+
* `x == null` is checked against an abstract Boolean value (`BooleanValue`),
212+
* and `x` is checked against an abstract nullness value (`NullValue`).
213+
*/
214+
AbstractValue getCheckedValue() { result = v0 }
215+
216+
final override predicate checks(Expr e, AbstractValue v) { e = this and v = v0 }
217+
}
218+
219+
/** A guard that checks if this expression is non-`null`. */
220+
class NullGuard extends DataFlow::BarrierGuards::ValueBarrierGuard {
221+
NullGuard() { this.getCheckedValue() = any(AbstractValues::NullValue nv | not nv.isNull()) }
222+
}
223+
224+
/** A guard that checks if this expression is `null`. */
225+
class AntiNullGuard extends DataFlow::BarrierGuards::ValueBarrierGuard {
226+
AntiNullGuard() { this.getCheckedValue().(AbstractValues::NullValue).isNull() }
188227
}
189228
}

0 commit comments

Comments
 (0)