Skip to content

Commit 645db5b

Browse files
committed
PS: Add SSA library.
1 parent 8b4e065 commit 645db5b

File tree

4 files changed

+490
-0
lines changed

4 files changed

+490
-0
lines changed

powershell/ql/lib/qlpack.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,6 @@ upgrades: upgrades
1010
dependencies:
1111
codeql/controlflow: ${workspace}
1212
codeql/dataflow: ${workspace}
13+
codeql/ssa: ${workspace}
1314
codeql/util: ${workspace}
1415
warnOnImplicitThis: true
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import powershell
2+
private import semmle.code.powershell.controlflow.internal.Scope
23

34
class Ast extends @ast {
45
string toString() { none() }
56

67
Ast getParent() { parent(this, result) }
78

89
Location getLocation() { none() }
10+
11+
final Scope getEnclosingScope() { result = scopeOf(this) }
912
}
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
/**
2+
* Provides the module `Ssa` for working with static single assignment (SSA) form.
3+
*/
4+
5+
/**
6+
* Provides classes for working with static single assignment (SSA) form.
7+
*/
8+
module Ssa {
9+
private import semmle.code.powershell.Cfg
10+
private import powershell
11+
private import internal.SsaImpl as SsaImpl
12+
private import CfgNodes::ExprNodes
13+
14+
/** A static single assignment (SSA) definition. */
15+
class Definition extends SsaImpl::Definition {
16+
/**
17+
* Gets the control flow node of this SSA definition, if any. Phi nodes are
18+
* examples of SSA definitions without a control flow node, as they are
19+
* modeled at index `-1` in the relevant basic block.
20+
*/
21+
final CfgNode getControlFlowNode() {
22+
exists(BasicBlock bb, int i | this.definesAt(_, bb, i) | result = bb.getNode(i))
23+
}
24+
25+
/** Gets a control-flow node that reads the value of this SSA definition. */
26+
final VarReadAccessCfgNode getARead() { result = SsaImpl::getARead(this) }
27+
28+
/**
29+
* Gets a first control-flow node that reads the value of this SSA definition.
30+
* That is, a read that can be reached from this definition without passing
31+
* through other reads.
32+
*/
33+
final VarReadAccessCfgNode getAFirstRead() { SsaImpl::firstRead(this, result) }
34+
35+
/**
36+
* Gets a last control-flow node that reads the value of this SSA definition.
37+
* That is, a read that can reach the end of the enclosing CFG scope, or another
38+
* SSA definition for the source variable, without passing through any other read.
39+
*/
40+
final VarReadAccessCfgNode getALastRead() { SsaImpl::lastRead(this, result) }
41+
42+
/**
43+
* Holds if `read1` and `read2` are adjacent reads of this SSA definition.
44+
* That is, `read2` can be reached from `read1` without passing through
45+
* another read.
46+
*/
47+
final predicate hasAdjacentReads(
48+
VarReadAccessCfgNode read1, VarReadAccessCfgNode read2
49+
) {
50+
SsaImpl::adjacentReadPair(this, read1, read2)
51+
}
52+
53+
/**
54+
* Gets an SSA definition whose value can flow to this one in one step. This
55+
* includes inputs to phi nodes and the prior definitions of uncertain writes.
56+
*/
57+
private Definition getAPhiInputOrPriorDefinition() { result = this.(PhiNode).getAnInput() }
58+
59+
/**
60+
* Gets a definition that ultimately defines this SSA definition and is
61+
* not itself a phi node.
62+
*/
63+
final Definition getAnUltimateDefinition() {
64+
result = this.getAPhiInputOrPriorDefinition*() and
65+
not result instanceof PhiNode
66+
}
67+
68+
override string toString() { result = this.getControlFlowNode().toString() }
69+
70+
/** Gets the scope of this SSA definition. */
71+
CfgScope getScope() { result = this.getBasicBlock().getScope() }
72+
}
73+
74+
/**
75+
* An SSA definition that corresponds to a write.
76+
*/
77+
class WriteDefinition extends Definition, SsaImpl::WriteDefinition {
78+
private VariableWriteAccessCfgNode write;
79+
80+
WriteDefinition() {
81+
exists(BasicBlock bb, int i, Variable v |
82+
this.definesAt(v, bb, i) and
83+
SsaImpl::variableWriteActual(bb, i, v, write)
84+
)
85+
}
86+
87+
/** Gets the underlying write access. */
88+
final VariableWriteAccessCfgNode getWriteAccess() { result = write }
89+
90+
/**
91+
* Holds if this SSA definition assigns `value` to the underlying variable.
92+
*/
93+
predicate assigns(CfgNodes::StmtCfgNode value) {
94+
exists(CfgNodes::StmtNodes::AssignStmtCfgNode a, BasicBlock bb, int i |
95+
this.definesAt(_, bb, i) and
96+
a = bb.getNode(i) and
97+
value = a.getRightHandSide()
98+
)
99+
}
100+
101+
final override string toString() { result = write.toString() }
102+
103+
final override Location getLocation() { result = write.getLocation() }
104+
}
105+
106+
/**
107+
* An SSA definition inserted at the beginning of a scope to represent an
108+
* uninitialized local variable.
109+
*/
110+
class UninitializedDefinition extends Definition, SsaImpl::WriteDefinition {
111+
private Variable v;
112+
113+
UninitializedDefinition() {
114+
exists(BasicBlock bb, int i |
115+
this.definesAt(v, bb, i) and
116+
SsaImpl::uninitializedWrite(bb, i, v)
117+
)
118+
}
119+
120+
final override string toString() { result = "<uninitialized> " + v }
121+
122+
final override Location getLocation() { result = this.getBasicBlock().getLocation() }
123+
}
124+
125+
/** A phi node. */
126+
class PhiNode extends Definition, SsaImpl::PhiNode {
127+
/** Gets an input of this phi node. */
128+
final Definition getAnInput() { this.hasInputFromBlock(result, _) }
129+
130+
/** Holds if `inp` is an input to this phi node along the edge originating in `bb`. */
131+
predicate hasInputFromBlock(Definition inp, BasicBlock bb) {
132+
inp = SsaImpl::phiHasInputFromBlock(this, bb)
133+
}
134+
135+
override string toString() { result = "phi" }
136+
137+
/**
138+
* The location of a phi node is the same as the location of the first node
139+
* in the basic block in which it is defined.
140+
*
141+
* Strictly speaking, the node is *before* the first node, but such a location
142+
* does not exist in the source program.
143+
*/
144+
final override Location getLocation() {
145+
result = this.getBasicBlock().getFirstNode().getLocation()
146+
}
147+
}
148+
}

0 commit comments

Comments
 (0)