Skip to content

Commit 383e82a

Browse files
authored
Merge pull request #886 from hvitved/csharp/cfg/restructure
C#: Split up `ControlFlowGraph.qll` into multiple files
2 parents 5f027e1 + 23b9b1e commit 383e82a

File tree

12 files changed

+2223
-2239
lines changed

12 files changed

+2223
-2239
lines changed

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

Lines changed: 16 additions & 2234 deletions
Large diffs are not rendered by default.

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ private import semmle.code.csharp.commons.Assertions
88
private import semmle.code.csharp.commons.ComparisonTest
99
private import semmle.code.csharp.commons.StructuralComparison::Internal
1010
private import semmle.code.csharp.controlflow.BasicBlocks
11-
private import semmle.code.csharp.controlflow.Completion
11+
private import semmle.code.csharp.controlflow.internal.Completion
1212
private import semmle.code.csharp.dataflow.Nullness
1313
private import semmle.code.csharp.frameworks.System
1414

@@ -627,7 +627,8 @@ class NullGuardedDataFlowNode extends GuardedDataFlowNode {
627627

628628
/** INTERNAL: Do not use. */
629629
module Internal {
630-
private import ControlFlow::Internal
630+
private import semmle.code.csharp.controlflow.internal.PreBasicBlocks as PreBasicBlocks
631+
private import semmle.code.csharp.controlflow.internal.PreSsa as PreSsa
631632

632633
newtype TAbstractValue =
633634
TBooleanValue(boolean b) { b = true or b = false } or

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

File renamed without changes.
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
/**
2+
* INTERNAL: Do not use.
3+
*
4+
* Provides a simple analysis for identifying calls to callables that will
5+
* not return.
6+
*/
7+
8+
import csharp
9+
private import semmle.code.csharp.ExprOrStmtParent
10+
private import semmle.code.csharp.commons.Assertions
11+
private import semmle.code.csharp.frameworks.System
12+
private import semmle.code.csharp.controlflow.internal.Completion
13+
14+
/** A call that definitely does not return (conservative analysis). */
15+
abstract class NonReturningCall extends Call {
16+
/** Gets a valid completion for this non-returning call. */
17+
abstract Completion getACompletion();
18+
}
19+
20+
private class ExitingCall extends NonReturningCall {
21+
ExitingCall() {
22+
this.getTarget() instanceof ExitingCallable
23+
or
24+
exists(AssertMethod m | m = this.(FailingAssertion).getAssertMethod() |
25+
not exists(m.getExceptionClass())
26+
)
27+
}
28+
29+
override ExitCompletion getACompletion() { any() }
30+
}
31+
32+
private class ThrowingCall extends NonReturningCall {
33+
private ThrowCompletion c;
34+
35+
ThrowingCall() {
36+
c = this.getTarget().(ThrowingCallable).getACallCompletion()
37+
or
38+
exists(AssertMethod m | m = this.(FailingAssertion).getAssertMethod() |
39+
c.getExceptionClass() = m.getExceptionClass()
40+
)
41+
}
42+
43+
override ThrowCompletion getACompletion() { result = c }
44+
}
45+
46+
abstract private class NonReturningCallable extends Callable {
47+
NonReturningCallable() {
48+
not exists(ReturnStmt ret | ret.getEnclosingCallable() = this) and
49+
not hasAccessorAutoImplementation(this, _) and
50+
not exists(Virtualizable v | v.isOverridableOrImplementable() |
51+
v = this or
52+
v = this.(Accessor).getDeclaration()
53+
)
54+
}
55+
}
56+
57+
abstract private class ExitingCallable extends NonReturningCallable { }
58+
59+
private class DirectlyExitingCallable extends ExitingCallable {
60+
DirectlyExitingCallable() {
61+
this = any(Method m |
62+
m.hasQualifiedName("System.Environment", "Exit") or
63+
m.hasQualifiedName("System.Windows.Forms.Application", "Exit")
64+
)
65+
}
66+
}
67+
68+
private class IndirectlyExitingCallable extends ExitingCallable {
69+
IndirectlyExitingCallable() {
70+
forex(ControlFlowElement body | body = this.getABody() | body = getAnExitingElement())
71+
}
72+
}
73+
74+
private ControlFlowElement getAnExitingElement() {
75+
result instanceof ExitingCall
76+
or
77+
result = getAnExitingStmt()
78+
}
79+
80+
private Stmt getAnExitingStmt() {
81+
result.(ExprStmt).getExpr() = getAnExitingElement()
82+
or
83+
result.(BlockStmt).getFirstStmt() = getAnExitingElement()
84+
or
85+
exists(IfStmt ifStmt |
86+
result = ifStmt and
87+
ifStmt.getThen() = getAnExitingElement() and
88+
ifStmt.getElse() = getAnExitingElement()
89+
)
90+
}
91+
92+
private class ThrowingCallable extends NonReturningCallable {
93+
ThrowingCallable() {
94+
forex(ControlFlowElement body | body = this.getABody() | body = getAThrowingElement(_))
95+
}
96+
97+
/** Gets a valid completion for a call to this throwing callable. */
98+
ThrowCompletion getACallCompletion() { this.getABody() = getAThrowingElement(result) }
99+
}
100+
101+
private predicate directlyThrows(ThrowElement te, ThrowCompletion c) {
102+
c.getExceptionClass() = te.getThrownExceptionType() and
103+
// For stub implementations, there may exist proper implementations that are not seen
104+
// during compilation, so we conservatively rule those out
105+
not isStub(te)
106+
}
107+
108+
private ControlFlowElement getAThrowingElement(ThrowCompletion c) {
109+
c = result.(ThrowingCall).getACompletion()
110+
or
111+
directlyThrows(result, c)
112+
or
113+
result = getAThrowingStmt(c)
114+
}
115+
116+
private Stmt getAThrowingStmt(ThrowCompletion c) {
117+
directlyThrows(result, c)
118+
or
119+
result.(ExprStmt).getExpr() = getAThrowingElement(c)
120+
or
121+
result.(BlockStmt).getFirstStmt() = getAThrowingStmt(c)
122+
or
123+
exists(IfStmt ifStmt, ThrowCompletion c1, ThrowCompletion c2 |
124+
result = ifStmt and
125+
ifStmt.getThen() = getAThrowingStmt(c1) and
126+
ifStmt.getElse() = getAThrowingStmt(c2)
127+
|
128+
c = c1
129+
or
130+
c = c2
131+
)
132+
}
133+
134+
/** Holds if `throw` element `te` indicates a stub implementation. */
135+
private predicate isStub(ThrowElement te) {
136+
exists(Expr e | e = te.getExpr() |
137+
e instanceof NullLiteral or
138+
e.getType() instanceof SystemNotImplementedExceptionClass
139+
)
140+
}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
/**
2+
* INTERNAL: Do not use.
3+
*
4+
* Provides a basic block implementation on control flow elements. That is,
5+
* a "pre-CFG" where the nodes are (unsplit) control flow elements and the
6+
* successor releation is `succ = succ(pred, _)`.
7+
*
8+
* The logic is duplicated from the implementation in `BasicBlocks.qll`, and
9+
* being an internal class, all predicate documentation has been removed.
10+
*/
11+
12+
import csharp
13+
private import semmle.code.csharp.controlflow.internal.Completion
14+
private import semmle.code.csharp.controlflow.ControlFlowGraph::ControlFlow
15+
private import Internal::Successor
16+
17+
private predicate startsBB(ControlFlowElement cfe) {
18+
not cfe = succ(_, _) and
19+
(
20+
exists(succ(cfe, _))
21+
or
22+
exists(succExit(cfe, _))
23+
)
24+
or
25+
strictcount(ControlFlowElement pred, Completion c | cfe = succ(pred, c)) > 1
26+
or
27+
exists(ControlFlowElement pred, int i |
28+
cfe = succ(pred, _) and
29+
i = count(ControlFlowElement succ, Completion c | succ = succ(pred, c))
30+
|
31+
i > 1
32+
or
33+
i = 1 and
34+
exists(succExit(pred, _))
35+
)
36+
}
37+
38+
private predicate intraBBSucc(ControlFlowElement pred, ControlFlowElement succ) {
39+
succ = succ(pred, _) and
40+
not startsBB(succ)
41+
}
42+
43+
private predicate bbIndex(ControlFlowElement bbStart, ControlFlowElement cfe, int i) =
44+
shortestDistances(startsBB/1, intraBBSucc/2)(bbStart, cfe, i)
45+
46+
private predicate succBB(PreBasicBlock pred, PreBasicBlock succ) { succ = pred.getASuccessor() }
47+
48+
private predicate entryBB(PreBasicBlock bb) { bb = succEntry(_) }
49+
50+
private predicate bbIDominates(PreBasicBlock dom, PreBasicBlock bb) =
51+
idominance(entryBB/1, succBB/2)(_, dom, bb)
52+
53+
class PreBasicBlock extends ControlFlowElement {
54+
PreBasicBlock() { startsBB(this) }
55+
56+
PreBasicBlock getASuccessorByType(SuccessorType t) {
57+
result = succ(this.getLastElement(), any(Completion c | t.matchesCompletion(c)))
58+
}
59+
60+
PreBasicBlock getASuccessor() { result = this.getASuccessorByType(_) }
61+
62+
PreBasicBlock getAPredecessor() { result.getASuccessor() = this }
63+
64+
ControlFlowElement getElement(int pos) { bbIndex(this, result, pos) }
65+
66+
ControlFlowElement getAnElement() { result = this.getElement(_) }
67+
68+
ControlFlowElement getFirstElement() { result = this }
69+
70+
ControlFlowElement getLastElement() { result = this.getElement(length() - 1) }
71+
72+
int length() { result = strictcount(getAnElement()) }
73+
74+
predicate immediatelyDominates(PreBasicBlock bb) { bbIDominates(this, bb) }
75+
76+
predicate strictlyDominates(PreBasicBlock bb) { this.immediatelyDominates+(bb) }
77+
78+
predicate dominates(PreBasicBlock bb) {
79+
bb = this
80+
or
81+
this.strictlyDominates(bb)
82+
}
83+
84+
predicate inDominanceFrontier(PreBasicBlock df) {
85+
this.dominatesPredecessor(df) and
86+
not this.strictlyDominates(df)
87+
}
88+
89+
private predicate dominatesPredecessor(PreBasicBlock df) { this.dominates(df.getAPredecessor()) }
90+
}
91+
92+
class ConditionBlock extends PreBasicBlock {
93+
ConditionBlock() {
94+
strictcount(ConditionalCompletion c |
95+
exists(succ(this.getLastElement(), c))
96+
or
97+
exists(succExit(this.getLastElement(), c))
98+
) > 1
99+
}
100+
101+
private predicate immediatelyControls(PreBasicBlock succ, ConditionalCompletion c) {
102+
succ = succ(this.getLastElement(), c) and
103+
forall(PreBasicBlock pred | pred = succ.getAPredecessor() and pred != this |
104+
succ.dominates(pred)
105+
)
106+
}
107+
108+
predicate controls(PreBasicBlock controlled, SuccessorTypes::ConditionalSuccessor s) {
109+
exists(PreBasicBlock succ, ConditionalCompletion c | immediatelyControls(succ, c) |
110+
succ.dominates(controlled) and
111+
s.matchesCompletion(c)
112+
)
113+
}
114+
}

0 commit comments

Comments
 (0)