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

Skip to content

Commit f1d6f7c

Browse files
committed
C#: Model assertions in the CFG
1 parent 17f0ac4 commit f1d6f7c

24 files changed

Lines changed: 2251 additions & 1205 deletions

csharp/ql/src/semmle/code/csharp/commons/Assertions.qll

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ class Assertion extends MethodCall {
6262
* does not work.
6363
*/
6464
pragma[nomagic]
65-
private predicate immediatelyDominatesBlockSplit(BasicBlock succ) {
65+
deprecated private predicate immediatelyDominatesBlockSplit(BasicBlock succ) {
6666
// Only calculate dominance by explicit recursion for split nodes;
6767
// all other nodes can use regular CFG dominance
6868
this instanceof ControlFlow::Internal::SplitControlFlowElement and
@@ -78,11 +78,11 @@ class Assertion extends MethodCall {
7878
}
7979

8080
pragma[noinline]
81-
private predicate strictlyDominatesJoinBlockPredecessor(JoinBlock jb, int i) {
81+
deprecated private predicate strictlyDominatesJoinBlockPredecessor(JoinBlock jb, int i) {
8282
this.strictlyDominatesSplit(jb.getJoinBlockPredecessor(i))
8383
}
8484

85-
private predicate strictlyDominatesJoinBlockSplit(JoinBlock jb, int i) {
85+
deprecated private predicate strictlyDominatesJoinBlockSplit(JoinBlock jb, int i) {
8686
i = -1 and
8787
this.strictlyDominatesJoinBlockPredecessor(jb, _)
8888
or
@@ -95,7 +95,7 @@ class Assertion extends MethodCall {
9595
}
9696

9797
pragma[nomagic]
98-
private predicate strictlyDominatesSplit(BasicBlock bb) {
98+
deprecated private predicate strictlyDominatesSplit(BasicBlock bb) {
9999
this.immediatelyDominatesBlockSplit(bb)
100100
or
101101
// Equivalent with
@@ -121,6 +121,8 @@ class Assertion extends MethodCall {
121121
}
122122

123123
/**
124+
* DEPRECATED: Use `getExpr().controlsBlock()` instead.
125+
*
124126
* Holds if this assertion strictly dominates basic block `bb`. That is, `bb`
125127
* can only be reached from the callable entry point by going via *some* basic
126128
* block containing this element.
@@ -130,7 +132,7 @@ class Assertion extends MethodCall {
130132
* in that it takes control flow splitting into account.
131133
*/
132134
pragma[nomagic]
133-
predicate strictlyDominates(BasicBlock bb) {
135+
deprecated predicate strictlyDominates(BasicBlock bb) {
134136
this.strictlyDominatesSplit(bb)
135137
or
136138
this.getAControlFlowNode().getBasicBlock().strictlyDominates(bb)

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

Lines changed: 5 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -612,14 +612,11 @@ private Ssa::Definition getAnSsaQualifier(Expr e, ControlFlow::Node cfn) {
612612
}
613613

614614
private AssignableAccess getATrackedAccess(Ssa::Definition def, ControlFlow::Node cfn) {
615-
(
616-
result = def.getAReadAtNode(cfn)
617-
or
618-
result = def.(Ssa::ExplicitDefinition).getADefinition().getTargetAccess() and
619-
result.getAControlFlowNode() = cfn and
620-
cfn.getBasicBlock() = def.getBasicBlock()
621-
) and
615+
result = def.getAReadAtNode(cfn) and
622616
not def instanceof Ssa::ImplicitUntrackedDefinition
617+
or
618+
result = def.(Ssa::ExplicitDefinition).getADefinition().getTargetAccess() and
619+
cfn = def.getControlFlowNode()
623620
}
624621

625622
/**
@@ -962,68 +959,13 @@ module Internal {
962959
e = any(BinaryArithmeticOperation bao | result = bao.getAnOperand())
963960
}
964961

965-
pragma[noinline]
966-
private predicate assertionControlsNodeInSameBasicBlock0(
967-
Guard g, AbstractValue v, BasicBlock bb, int i
968-
) {
969-
exists(Assertion a, Guard g0, AbstractValue v0 |
970-
asserts(a, g0, v0) and
971-
impliesSteps(g0, v0, g, v) and
972-
bb.getNode(i) = a.getAControlFlowNode()
973-
)
974-
}
975-
976-
/**
977-
* Holds if control flow node `cfn` only is reached when guard `g` evaluates to `v`,
978-
* because of an assertion.
979-
*/
980-
private predicate assertionControlsNodeInSameBasicBlock(
981-
Guard g, ControlFlow::Node cfn, AbstractValue v
982-
) {
983-
exists(BasicBlock bb, int i, int j |
984-
assertionControlsNodeInSameBasicBlock0(g, v, bb, i) and
985-
bb.getNode(j) = cfn and
986-
j > i
987-
)
988-
}
989-
990-
/**
991-
* Holds if control flow element `cfe` only is reached when guard `g` evaluates to `v`,
992-
* because of an assertion.
993-
*/
994-
private predicate guardAssertionControlsElement(Guard g, ControlFlowElement cfe, AbstractValue v) {
995-
forex(ControlFlow::Node cfn | cfn = cfe.getAControlFlowNode() |
996-
assertionControlsNodeInSameBasicBlock(g, cfn, v)
997-
)
998-
}
999-
1000962
/** Same as `this.getAChildExpr*()`, but avoids `fastTC`. */
1001963
private Expr getAChildExprStar(Guard g) {
1002964
result = g
1003965
or
1004966
result = getAChildExprStar(g).getAChildExpr()
1005967
}
1006968

1007-
/**
1008-
* Holds if assertion `a` directly asserts that expression `e` evaluates to value `v`.
1009-
*/
1010-
predicate asserts(Assertion a, Expr e, AbstractValue v) {
1011-
e = a.getExpr() and
1012-
(
1013-
a.getAssertMethod() instanceof AssertTrueMethod and
1014-
v.(BooleanValue).getValue() = true
1015-
or
1016-
a.getAssertMethod() instanceof AssertFalseMethod and
1017-
v.(BooleanValue).getValue() = false
1018-
or
1019-
a.getAssertMethod() instanceof AssertNullMethod and
1020-
v.(NullValue).isNull()
1021-
or
1022-
a.getAssertMethod() instanceof AssertNonNullMethod and
1023-
v.(NullValue).isNonNull()
1024-
)
1025-
}
1026-
1027969
private Expr stripConditionalExpr(Expr e) {
1028970
e =
1029971
any(ConditionalExpr ce |
@@ -1453,8 +1395,6 @@ module Internal {
14531395
or
14541396
val.branch(_, _, e)
14551397
or
1456-
asserts(_, e, val)
1457-
or
14581398
e instanceof CollectionExpr and
14591399
val = TEmptyCollectionValue(_)
14601400
) and
@@ -1767,11 +1707,7 @@ module Internal {
17671707
pragma[noinline]
17681708
private predicate candidateAux(AccessOrCallExpr e, Declaration target, BasicBlock bb) {
17691709
target = e.getTarget() and
1770-
exists(Guard g | e = getAChildExprStar(g) |
1771-
guardControls(g, bb, _)
1772-
or
1773-
assertionControlsNodeInSameBasicBlock(g, bb.getANode(), _)
1774-
)
1710+
exists(Guard g | e = getAChildExprStar(g) | guardControls(g, bb, _))
17751711
}
17761712
}
17771713

@@ -1786,11 +1722,6 @@ module Internal {
17861722
exists(ControlFlowElement cfe, ConditionalSuccessor s |
17871723
v0.branch(cfe, s, g0) and cfe.controlsBlock(bb, s)
17881724
)
1789-
or
1790-
exists(Assertion a |
1791-
asserts(a, g0, v0) and
1792-
a.strictlyDominates(bb)
1793-
)
17941725
)
17951726
}
17961727

@@ -1812,9 +1743,6 @@ module Internal {
18121743
forex(ControlFlow::Node cfn | cfn = guarded.getAControlFlowNode() |
18131744
isGuardedByNode0(cfn, guarded, g, sub, v)
18141745
)
1815-
or
1816-
guardAssertionControlsElement(g, guarded, v) and
1817-
exists(ConditionOnExprComparisonConfig c | c.same(sub, guarded))
18181746
}
18191747

18201748
private predicate adjacentReadPairSameVarUniquePredecessor(
@@ -1848,9 +1776,6 @@ module Internal {
18481776
ControlFlow::Nodes::ElementNode guarded, Guard g, AccessOrCallExpr sub, AbstractValue v
18491777
) {
18501778
isGuardedByNode0(guarded, _, g, sub, v)
1851-
or
1852-
assertionControlsNodeInSameBasicBlock(g, guarded, v) and
1853-
exists(ConditionOnExprComparisonConfig c | c.same(sub, guarded.getElement()))
18541779
}
18551780

18561781
pragma[noinline]

csharp/ql/src/semmle/code/csharp/controlflow/internal/Completion.qll

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
*/
2121

2222
import csharp
23+
private import semmle.code.csharp.commons.Assertions
2324
private import semmle.code.csharp.commons.Constants
2425
private import semmle.code.csharp.frameworks.System
2526
private import NonReturning
@@ -98,6 +99,13 @@ class Completion extends TCompletion {
9899
cfe instanceof ThrowElement and
99100
this = TThrowCompletion(cfe.(ThrowElement).getThrownExceptionType())
100101
or
102+
exists(AssertMethod m | assertion(cfe, m, _) |
103+
this = TThrowCompletion(m.getExceptionClass())
104+
or
105+
not exists(m.getExceptionClass()) and
106+
this = TExitCompletion()
107+
)
108+
or
101109
completionIsValidForStmt(cfe, this)
102110
or
103111
mustHaveBooleanCompletion(cfe) and
@@ -382,6 +390,11 @@ private predicate invalidCastCandidate(CastExpr ce) {
382390
ce.getType() = ce.getExpr().getType().(ValueOrRefType).getASubType+()
383391
}
384392

393+
private predicate assertion(Assertion a, AssertMethod am, Expr e) {
394+
e = a.getExpr() and
395+
am = a.getAssertMethod()
396+
}
397+
385398
/**
386399
* Holds if a normal completion of `e` must be a Boolean completion.
387400
*/
@@ -409,6 +422,9 @@ private predicate inBooleanContext(Expr e, boolean isBooleanCompletionForParent)
409422
or
410423
exists(SpecificCatchClause scc | scc.getFilterClause() = e | isBooleanCompletionForParent = false)
411424
or
425+
assertion(_, [any(AssertTrueMethod m).(AssertMethod), any(AssertFalseMethod m)], e) and
426+
isBooleanCompletionForParent = false
427+
or
412428
exists(LogicalNotExpr lne | lne.getAnOperand() = e |
413429
inBooleanContext(lne, _) and
414430
isBooleanCompletionForParent = true
@@ -479,6 +495,9 @@ private predicate inNullnessContext(Expr e, boolean isNullnessCompletionForParen
479495
isNullnessCompletionForParent = false
480496
)
481497
or
498+
assertion(_, [any(AssertNullMethod m).(AssertMethod), any(AssertNonNullMethod m)], e) and
499+
isNullnessCompletionForParent = false
500+
or
482501
exists(ConditionalExpr ce | inNullnessContext(ce, _) |
483502
(e = ce.getThen() or e = ce.getElse()) and
484503
isNullnessCompletionForParent = true

csharp/ql/src/semmle/code/csharp/controlflow/internal/PreBasicBlocks.qll

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,10 +89,14 @@ class PreBasicBlock extends ControlFlowElement {
8989
private predicate dominatesPredecessor(PreBasicBlock df) { this.dominates(df.getAPredecessor()) }
9090
}
9191

92+
private Completion getConditionalCompletion(ConditionalCompletion cc) {
93+
result.getInnerCompletion() = cc
94+
}
95+
9296
class ConditionBlock extends PreBasicBlock {
9397
ConditionBlock() {
9498
strictcount(Completion c |
95-
c.getInnerCompletion() instanceof ConditionalCompletion and
99+
c = getConditionalCompletion(_) and
96100
(
97101
exists(succ(this.getLastElement(), c))
98102
or
@@ -102,9 +106,20 @@ class ConditionBlock extends PreBasicBlock {
102106
}
103107

104108
private predicate immediatelyControls(PreBasicBlock succ, ConditionalCompletion cc) {
105-
succ = succ(this.getLastElement(), any(Completion c | c.getInnerCompletion() = cc)) and
106-
forall(PreBasicBlock pred | pred = succ.getAPredecessor() and pred != this |
107-
succ.dominates(pred)
109+
exists(ControlFlowElement last, Completion c |
110+
last = this.getLastElement() and
111+
c = getConditionalCompletion(cc) and
112+
succ = succ(last, c) and
113+
// In the pre-CFG, we need to account for case where one predecessor node has
114+
// two edges to the same successor node. Assertion expressions are examples of
115+
// such nodes.
116+
not exists(Completion other |
117+
succ = succ(last, other) and
118+
other != c
119+
) and
120+
forall(PreBasicBlock pred | pred = succ.getAPredecessor() and pred != this |
121+
succ.dominates(pred)
122+
)
108123
)
109124
}
110125

0 commit comments

Comments
 (0)