Skip to content

Commit 33c990f

Browse files
authored
Merge pull request github#5440 from hvitved/csharp/cil/ssa
C#: Add CIL SSA library
2 parents 0fe4bae + c96b830 commit 33c990f

File tree

13 files changed

+855
-32
lines changed

13 files changed

+855
-32
lines changed

config/identical-files.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -429,10 +429,11 @@
429429
"SSA C#": [
430430
"csharp/ql/src/semmle/code/csharp/dataflow/internal/SsaImplCommon.qll",
431431
"csharp/ql/src/semmle/code/csharp/controlflow/internal/pressa/SsaImplCommon.qll",
432-
"csharp/ql/src/semmle/code/csharp/dataflow/internal/basessa/SsaImplCommon.qll"
432+
"csharp/ql/src/semmle/code/csharp/dataflow/internal/basessa/SsaImplCommon.qll",
433+
"csharp/ql/src/semmle/code/cil/internal/SsaImplCommon.qll"
433434
],
434435
"CryptoAlgorithms Python/JS": [
435436
"javascript/ql/src/semmle/javascript/security/CryptoAlgorithms.qll",
436437
"python/ql/src/semmle/crypto/Crypto.qll"
437438
]
438-
}
439+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
lgtm,codescanning
2+
* A static single assignment (SSA) library has been added to the CIL analysis library. The SSA library replaces the existing `DefUse` module, which has been deprecated.

csharp/ql/src/semmle/code/cil/BasicBlock.qll

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,9 @@ class BasicBlock extends Cached::TBasicBlockStart {
240240

241241
/** Gets a textual representation of this basic block. */
242242
string toString() { result = getFirstNode().toString() }
243+
244+
/** Gets the location of this basic block. */
245+
Location getLocation() { result = this.getFirstNode().getLocation() }
243246
}
244247

245248
/**
@@ -305,7 +308,7 @@ class EntryBasicBlock extends BasicBlock {
305308
}
306309

307310
/** Holds if `bb` is an entry basic block. */
308-
private predicate entryBB(BasicBlock bb) { bb.getFirstNode() instanceof EntryPoint }
311+
private predicate entryBB(BasicBlock bb) { bb.getFirstNode() instanceof MethodImplementation }
309312

310313
/**
311314
* An exit basic block, that is, a basic block whose last node is

csharp/ql/src/semmle/code/cil/CIL.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,4 @@ import Attribute
2020
import Stubs
2121
import CustomModifierReceiver
2222
import Parameterizable
23+
import semmle.code.cil.Ssa

csharp/ql/src/semmle/code/cil/CallableReturns.qll

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,11 @@ private predicate alwaysNullExpr(Expr expr) {
4242
or
4343
alwaysNullMethod(expr.(StaticCall).getTarget())
4444
or
45-
forex(VariableUpdate vu | DefUse::variableUpdateUse(_, vu, expr) | alwaysNullVariableUpdate(vu))
45+
forex(Ssa::Definition def |
46+
expr = any(Ssa::Definition def0 | def = def0.getAnUltimateDefinition()).getARead()
47+
|
48+
alwaysNullVariableUpdate(def.getVariableUpdate())
49+
)
4650
}
4751

4852
pragma[noinline]
@@ -58,7 +62,9 @@ private predicate alwaysNotNullExpr(Expr expr) {
5862
or
5963
alwaysNotNullMethod(expr.(StaticCall).getTarget())
6064
or
61-
forex(VariableUpdate vu | DefUse::variableUpdateUse(_, vu, expr) |
62-
alwaysNotNullVariableUpdate(vu)
65+
forex(Ssa::Definition def |
66+
expr = any(Ssa::Definition def0 | def = def0.getAnUltimateDefinition()).getARead()
67+
|
68+
alwaysNotNullVariableUpdate(def.getVariableUpdate())
6369
)
6470
}

csharp/ql/src/semmle/code/cil/ControlFlow.qll

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ class ControlFlowNode extends @cil_controlflow_node {
99
/** Gets a textual representation of this control flow node. */
1010
string toString() { none() }
1111

12+
/** Gets the location of this control flow node. */
13+
Location getLocation() { none() }
14+
1215
/**
1316
* Gets the number of items this node pushes onto the stack.
1417
* This value is either 0 or 1, except for the instruction `dup`

csharp/ql/src/semmle/code/cil/DataFlow.qll

Lines changed: 59 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,40 @@ class Untainted extends TaintType, TExactValue { }
5757
/** A taint type where the data is tainted. */
5858
class Tainted extends TaintType, TTaintedValue { }
5959

60+
private predicate localFlowPhiInput(DataFlowNode input, Ssa::PhiNode phi) {
61+
exists(Ssa::Definition def, BasicBlock bb, int i | phi.hasLastInputRef(def, bb, i) |
62+
def.definesAt(_, bb, i) and
63+
input = def.getVariableUpdate().getSource()
64+
or
65+
input =
66+
any(ReadAccess ra |
67+
bb.getNode(i) = ra and
68+
ra.getTarget() = def.getSourceVariable()
69+
)
70+
)
71+
or
72+
exists(Ssa::PhiNode mid, BasicBlock bb, int i |
73+
localFlowPhiInput(input, mid) and
74+
phi.hasLastInputRef(mid, bb, i) and
75+
mid.definesAt(_, bb, i)
76+
)
77+
}
78+
6079
private predicate localExactStep(DataFlowNode src, DataFlowNode sink) {
6180
src = sink.(Opcodes::Dup).getAnOperand()
6281
or
63-
defUse(_, src, sink)
82+
exists(Ssa::Definition def, VariableUpdate vu |
83+
vu = def.getVariableUpdate() and
84+
src = vu.getSource() and
85+
sink = def.getAFirstRead()
86+
)
87+
or
88+
any(Ssa::Definition def).hasAdjacentReads(src, sink)
6489
or
65-
src = sink.(ParameterReadAccess).getTarget()
90+
exists(Ssa::PhiNode phi |
91+
localFlowPhiInput(src, phi) and
92+
sink = phi.getAFirstRead()
93+
)
6694
or
6795
src = sink.(Conversion).getExpr()
6896
or
@@ -73,12 +101,6 @@ private predicate localExactStep(DataFlowNode src, DataFlowNode sink) {
73101
src = sink.(Return).getExpr()
74102
or
75103
src = sink.(ConditionalBranch).getAnOperand()
76-
or
77-
src = sink.(MethodParameter).getAWrite()
78-
or
79-
exists(VariableUpdate update |
80-
update.getVariable().(Parameter) = sink and src = update.getSource()
81-
)
82104
}
83105

84106
private predicate localTaintStep(DataFlowNode src, DataFlowNode sink) {
@@ -87,8 +109,7 @@ private predicate localTaintStep(DataFlowNode src, DataFlowNode sink) {
87109
src = sink.(UnaryBitwiseOperation).getOperand()
88110
}
89111

90-
cached
91-
module DefUse {
112+
deprecated module DefUse {
92113
/**
93114
* A classification of variable references into reads and writes.
94115
*/
@@ -189,7 +210,7 @@ module DefUse {
189210

190211
/** Holds if the variable update `vu` can be used at the read `use`. */
191212
cached
192-
predicate variableUpdateUse(StackVariable target, VariableUpdate vu, ReadAccess use) {
213+
deprecated predicate variableUpdateUse(StackVariable target, VariableUpdate vu, ReadAccess use) {
193214
defReachesReadWithinBlock(target, vu, use)
194215
or
195216
exists(BasicBlock bb, int i |
@@ -202,23 +223,40 @@ module DefUse {
202223

203224
/** Holds if the update `def` can be used at the read `use`. */
204225
cached
205-
predicate defUse(StackVariable target, Expr def, ReadAccess use) {
226+
deprecated predicate defUse(StackVariable target, Expr def, ReadAccess use) {
206227
exists(VariableUpdate vu | def = vu.getSource() | variableUpdateUse(target, vu, use))
207228
}
208229
}
209230

210-
private import DefUse
211-
212-
abstract library class VariableUpdate extends Instruction {
213-
abstract Expr getSource();
231+
/** A node that updates a variable. */
232+
abstract class VariableUpdate extends DataFlowNode {
233+
/** Gets the value assigned, if any. */
234+
abstract DataFlowNode getSource();
214235

236+
/** Gets the variable that is updated. */
215237
abstract Variable getVariable();
238+
239+
/** Holds if this variable update happens at index `i` in basic block `bb`. */
240+
abstract predicate updatesAt(BasicBlock bb, int i);
241+
}
242+
243+
private class MethodParameterDef extends VariableUpdate, MethodParameter {
244+
override MethodParameter getSource() { result = this }
245+
246+
override MethodParameter getVariable() { result = this }
247+
248+
override predicate updatesAt(BasicBlock bb, int i) {
249+
bb.(EntryBasicBlock).getANode().getImplementation().getMethod() = this.getMethod() and
250+
i = -1
251+
}
216252
}
217253

218254
private class VariableWrite extends VariableUpdate, WriteAccess {
219-
override Expr getSource() { result = getExpr() }
255+
override Expr getSource() { result = this.getExpr() }
220256

221-
override Variable getVariable() { result = getTarget() }
257+
override Variable getVariable() { result = this.getTarget() }
258+
259+
override predicate updatesAt(BasicBlock bb, int i) { this = bb.getNode(i) }
222260
}
223261

224262
private class MethodOutOrRefTarget extends VariableUpdate, Call {
@@ -230,5 +268,7 @@ private class MethodOutOrRefTarget extends VariableUpdate, Call {
230268
result = this.getRawArgument(parameterIndex).(ReadAccess).getTarget()
231269
}
232270

233-
override Expr getSource() { result = this }
271+
override Expr getSource() { none() }
272+
273+
override predicate updatesAt(BasicBlock bb, int i) { this = bb.getNode(i) }
234274
}

csharp/ql/src/semmle/code/cil/Method.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class MethodImplementation extends EntryPoint, @cil_method_implementation {
1919
override MethodImplementation getImplementation() { result = this }
2020

2121
/** Gets the location of this implementation. */
22-
Assembly getLocation() { cil_method_implementation(this, _, result) }
22+
override Assembly getLocation() { cil_method_implementation(this, _, result) }
2323

2424
/** Gets the instruction at index `index`. */
2525
Instruction getInstruction(int index) { cil_instruction(result, _, index, this) }

csharp/ql/src/semmle/code/cil/Ssa.qll

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/**
2+
* Provides the module `Ssa` for working with static single assignment (SSA) form.
3+
*/
4+
5+
private import CIL
6+
7+
/**
8+
* Provides classes for working with static single assignment (SSA) form.
9+
*/
10+
module Ssa {
11+
private import internal.SsaImplCommon as SsaImpl
12+
private import internal.SsaImpl
13+
14+
/** An SSA definition. */
15+
class Definition extends SsaImpl::Definition {
16+
/** Gets a read of this SSA definition. */
17+
final ReadAccess getARead() { result = getARead(this) }
18+
19+
/** Gets the underlying variable update, if any. */
20+
final VariableUpdate getVariableUpdate() {
21+
exists(BasicBlock bb, int i |
22+
result.updatesAt(bb, i) and
23+
this.definesAt(result.getVariable(), bb, i)
24+
)
25+
}
26+
27+
/** Gets a first read of this SSA definition. */
28+
final ReadAccess getAFirstRead() { result = getAFirstRead(this) }
29+
30+
/** Holds if `first` and `second` are adjacent reads of this SSA definition. */
31+
final predicate hasAdjacentReads(ReadAccess first, ReadAccess second) {
32+
hasAdjacentReads(this, first, second)
33+
}
34+
35+
private Definition getAPhiInput() { result = this.(PhiNode).getAnInput() }
36+
37+
/**
38+
* Gets a definition that ultimately defines this SSA definition and is
39+
* not itself a phi node.
40+
*/
41+
final Definition getAnUltimateDefinition() {
42+
result = this.getAPhiInput*() and
43+
not result instanceof PhiNode
44+
}
45+
46+
/** Gets the location of this SSA definition. */
47+
Location getLocation() { result = this.getVariableUpdate().getLocation() }
48+
}
49+
50+
/** A phi node. */
51+
class PhiNode extends SsaImpl::PhiNode, Definition {
52+
final override Location getLocation() { result = this.getBasicBlock().getLocation() }
53+
54+
/** Gets an input to this phi node. */
55+
final Definition getAnInput() { result = getAPhiInput(this) }
56+
57+
/**
58+
* Holds if if `def` is an input to this phi node, and a reference to `def` at
59+
* index `i` in basic block `bb` can reach this phi node without going through
60+
* other references.
61+
*/
62+
final predicate hasLastInputRef(Definition def, BasicBlock bb, int i) {
63+
hasLastInputRef(this, def, bb, i)
64+
}
65+
}
66+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
private import semmle.code.cil.CIL
2+
private import SsaImplCommon
3+
4+
cached
5+
private module Cached {
6+
cached
7+
predicate forceCachingInSameStage() { any() }
8+
9+
cached
10+
ReadAccess getARead(Definition def) {
11+
exists(BasicBlock bb, int i |
12+
ssaDefReachesRead(_, def, bb, i) and
13+
result = bb.getNode(i)
14+
)
15+
}
16+
17+
cached
18+
ReadAccess getAFirstRead(Definition def) {
19+
exists(BasicBlock bb1, int i1, BasicBlock bb2, int i2 |
20+
def.definesAt(_, bb1, i1) and
21+
adjacentDefRead(def, bb1, i1, bb2, i2) and
22+
result = bb2.getNode(i2)
23+
)
24+
}
25+
26+
cached
27+
predicate hasAdjacentReads(Definition def, ReadAccess first, ReadAccess second) {
28+
exists(BasicBlock bb1, int i1, BasicBlock bb2, int i2 |
29+
first = bb1.getNode(i1) and
30+
adjacentDefRead(def, bb1, i1, bb2, i2) and
31+
second = bb2.getNode(i2)
32+
)
33+
}
34+
35+
cached
36+
Definition getAPhiInput(PhiNode phi) { phiHasInputFromBlock(phi, result, _) }
37+
38+
cached
39+
predicate hasLastInputRef(Definition phi, Definition def, BasicBlock bb, int i) {
40+
lastRefRedef(def, bb, i, phi) and
41+
def = getAPhiInput(phi)
42+
}
43+
}
44+
45+
import Cached

0 commit comments

Comments
 (0)