Skip to content

Commit 6ab6279

Browse files
committed
PS: Implement global dataflow for environment variable write/reads.
1 parent 2541bcd commit 6ab6279

File tree

3 files changed

+139
-10
lines changed

3 files changed

+139
-10
lines changed

powershell/ql/lib/semmle/code/powershell/dataflow/Ssa.qll

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,21 @@ module Ssa {
130130
final override Location getLocation() { result = this.getBasicBlock().getLocation() }
131131
}
132132

133+
class InitialEnvVarDefinition extends Definition, SsaImpl::WriteDefinition {
134+
private EnvVariable v;
135+
136+
InitialEnvVarDefinition() {
137+
exists(BasicBlock bb, int i |
138+
this.definesAt(v, bb, i) and
139+
SsaImpl::envVarWrite(bb, i, v)
140+
)
141+
}
142+
143+
final override string toString() { result = "<initial env var> " + v }
144+
145+
final override Location getLocation() { result = this.getBasicBlock().getLocation() }
146+
}
147+
133148
/** phi node. */
134149
class PhiNode extends Definition, SsaImpl::PhiNode {
135150
/** Gets an input of this phi node. */

powershell/ql/lib/semmle/code/powershell/dataflow/internal/DataFlowPrivate.qll

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ module SsaFlow {
6565
Impl::Node asNode(Node n) {
6666
n = TSsaNode(result)
6767
or
68-
result.(Impl::ExprNode).getExpr() = n.asExpr()
68+
result.(Impl::ExprNode).getExpr().asExprCfgNode() = n.asExpr()
6969
or
7070
exists(CfgNodes::ProcessBlockCfgNode pb, BasicBlock bb, int i |
7171
n.(ProcessNode).getProcessBlock() = pb and
@@ -86,14 +86,20 @@ module SsaFlow {
8686
result.(Impl::SsaDefinitionNode).getDefinition().definesAt(p.getIteratorVariable(), bb, i)
8787
)
8888
or
89-
result.(Impl::ExprPostUpdateNode).getExpr() = n.(PostUpdateNode).getPreUpdateNode().asExpr()
89+
result.(Impl::ExprPostUpdateNode).getExpr().asExprCfgNode() =
90+
n.(PostUpdateNode).getPreUpdateNode().asExpr()
9091
or
9192
exists(SsaImpl::ParameterExt p |
9293
n = toParameterNode(p) and
9394
p.isInitializedBy(result.(Impl::WriteDefSourceNode).getDefinition())
9495
)
9596
or
9697
result.(Impl::WriteDefSourceNode).getDefinition().(Ssa::WriteDefinition).assigns(n.asExpr())
98+
or
99+
exists(Scope scope, EnvVariable v |
100+
result.(Impl::ExprNode).getExpr().isFinalEnvVarRead(scope, v) and
101+
n = TFinalEnvVarRead(scope, v)
102+
)
97103
}
98104

99105
predicate localFlowStep(
@@ -241,7 +247,15 @@ private module Cached {
241247
// We want to prune irrelevant models before materialising data flow nodes, so types contributed
242248
// directly from CodeQL must expose their pruning info without depending on data flow nodes.
243249
(any(ModelInput::TypeModel tm).isTypeUsed("") implies any())
244-
}
250+
} or
251+
TFinalEnvVarRead(Scope scope, EnvVariable envVar) {
252+
exists(ExitBasicBlock exit |
253+
envVar.getAnAccess().getEnclosingScope() = scope and
254+
exit.getScope() = scope and
255+
SsaImpl::envVarRead(exit, _, envVar)
256+
)
257+
} or
258+
TEnvVarNode(EnvVariable envVar)
245259

246260
cached
247261
Location getLocation(NodeImpl n) { result = n.getLocationImpl() }
@@ -900,6 +914,15 @@ private module OutNodes {
900914
import OutNodes
901915

902916
predicate jumpStep(Node pred, Node succ) {
917+
// final env read -> env variable node
918+
pred.(FinalEnvVarRead).getVariable() = succ.(EnvVarNode).getVariable()
919+
or
920+
// env variable node -> initial env def
921+
exists(SsaImpl::Definition def |
922+
succ.(SsaDefinitionNodeImpl).getDefinition() = def and
923+
def.definesAt(pred.(EnvVarNode).getVariable(), any(EntryBasicBlock entry), -1)
924+
)
925+
or
903926
FlowSummaryImpl::Private::Steps::summaryJumpStep(pred.(FlowSummaryNode).getSummaryNode(),
904927
succ.(FlowSummaryNode).getSummaryNode())
905928
}
@@ -1308,6 +1331,39 @@ class ScriptBlockNode extends TScriptBlockNode, NodeImpl {
13081331
override predicate nodeIsHidden() { any() }
13091332
}
13101333

1334+
class EnvVarNode extends TEnvVarNode, NodeImpl {
1335+
private EnvVariable v;
1336+
1337+
EnvVarNode() { this = TEnvVarNode(v) }
1338+
1339+
EnvVariable getVariable() { result = v }
1340+
1341+
override CfgScope getCfgScope() { result = v.getEnclosingScope() }
1342+
1343+
override Location getLocationImpl() { result = v.getLocation() }
1344+
1345+
override string toStringImpl() { result = v.toString() }
1346+
1347+
override predicate nodeIsHidden() { any() }
1348+
}
1349+
1350+
class FinalEnvVarRead extends TFinalEnvVarRead, NodeImpl {
1351+
Scope scope;
1352+
private EnvVariable v;
1353+
1354+
FinalEnvVarRead() { this = TFinalEnvVarRead(scope, v) }
1355+
1356+
EnvVariable getVariable() { result = v }
1357+
1358+
override CfgScope getCfgScope() { result = scope }
1359+
1360+
override Location getLocationImpl() { result = scope.getLocation() }
1361+
1362+
override string toStringImpl() { result = v.toString() + " after " + scope }
1363+
1364+
override predicate nodeIsHidden() { any() }
1365+
}
1366+
13111367
/** A node that performs a type cast. */
13121368
class CastNode extends Node {
13131369
CastNode() { none() }

powershell/ql/lib/semmle/code/powershell/dataflow/internal/SsaImpl.qll

Lines changed: 65 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ module SsaInput implements SsaImplCommon::InputSig<Location> {
2929
(
3030
uninitializedWrite(bb, i, v)
3131
or
32+
envVarWrite(bb, i, v)
33+
or
3234
variableWriteActual(bb, i, v, _)
3335
or
3436
exists(ProcessBlockCfgNode processBlock | bb.getNode(i) = processBlock |
@@ -43,7 +45,11 @@ module SsaInput implements SsaImplCommon::InputSig<Location> {
4345
}
4446

4547
predicate variableRead(BasicBlock bb, int i, Variable v, boolean certain) {
46-
variableReadActual(bb, i, v) and
48+
(
49+
variableReadActual(bb, i, v)
50+
or
51+
envVarRead(bb, i, v)
52+
) and
4753
certain = true
4854
}
4955
}
@@ -66,6 +72,14 @@ predicate uninitializedWrite(Cfg::EntryBasicBlock bb, int i, Variable v) {
6672
bb.getANode().getAstNode() = v
6773
}
6874

75+
predicate envVarWrite(Cfg::EntryBasicBlock bb, int i, EnvVariable v) {
76+
exists(VarReadAccess va |
77+
va.getVariable() = v and
78+
va.getEnclosingFunction().getBody() = bb.getScope() and
79+
i = -1
80+
)
81+
}
82+
6983
predicate parameterWrite(Cfg::EntryBasicBlock bb, int i, Parameter p) {
7084
bb.getNode(i).getAstNode() = p
7185
}
@@ -78,6 +92,14 @@ private predicate variableReadActual(Cfg::BasicBlock bb, int i, Variable v) {
7892
)
7993
}
8094

95+
predicate envVarRead(Cfg::ExitBasicBlock bb, int i, EnvVariable v) {
96+
exists(VarWriteAccess va |
97+
va.getVariable() = v and
98+
va.getEnclosingFunction().getBody() = bb.getScope() and
99+
bb.getNode(i) instanceof ExitNode
100+
)
101+
}
102+
81103
cached
82104
private module Cached {
83105
/**
@@ -165,7 +187,7 @@ private module Cached {
165187
private predicate guardChecksAdjTypes(
166188
DataFlowIntegrationInput::Guard g, DataFlowIntegrationInput::Expr e, boolean branch
167189
) {
168-
guardChecks(g, e, branch)
190+
guardChecks(g, e.asExprCfgNode(), branch)
169191
}
170192

171193
private Node getABarrierNodeImpl() {
@@ -261,11 +283,48 @@ class ParameterExt extends TParameterExt {
261283
}
262284

263285
private module DataFlowIntegrationInput implements Impl::DataFlowIntegrationInputSig {
264-
class Expr extends Cfg::CfgNodes::ExprCfgNode {
265-
predicate hasCfgNode(SsaInput::BasicBlock bb, int i) { this = bb.getNode(i) }
286+
private newtype TExpr =
287+
TExprCfgNode(Cfg::CfgNodes::ExprCfgNode e) or
288+
TFinalEnvVarRead(Scope scope, EnvVariable v) {
289+
exists(Cfg::ExitBasicBlock exit |
290+
exit.getScope() = scope and
291+
envVarRead(exit, _, v)
292+
)
293+
}
294+
295+
class Expr extends TExpr {
296+
Cfg::CfgNodes::ExprCfgNode asExprCfgNode() { this = TExprCfgNode(result) }
297+
298+
predicate isFinalEnvVarRead(Scope scope, EnvVariable v) { this = TFinalEnvVarRead(scope, v) }
299+
300+
predicate hasCfgNode(SsaInput::BasicBlock bb, int i) {
301+
this.asExprCfgNode() = bb.getNode(i)
302+
or
303+
exists(EnvVariable v |
304+
this.isFinalEnvVarRead(bb.getScope(), v) and
305+
bb.getNode(i) instanceof ExitNode
306+
)
307+
}
308+
309+
string toString() {
310+
result = this.asExprCfgNode().toString()
311+
or
312+
exists(EnvVariable v |
313+
this.isFinalEnvVarRead(_, v) and
314+
result = v.toString()
315+
)
316+
}
266317
}
267318

268-
Expr getARead(Definition def) { result = Cached::getARead(def) }
319+
Expr getARead(Definition def) {
320+
result.asExprCfgNode() = Cached::getARead(def)
321+
or
322+
exists(Variable v, Cfg::BasicBlock bb, int i |
323+
Impl::ssaDefReachesRead(v, def, bb, i) and
324+
envVarRead(bb, i, v) and
325+
result.isFinalEnvVarRead(bb.getScope(), v)
326+
)
327+
}
269328

270329
predicate ssaDefHasSource(WriteDefinition def) {
271330
any(ParameterExt p).isInitializedBy(def) or def.(Ssa::WriteDefinition).assigns(_)
@@ -280,6 +339,7 @@ private module DataFlowIntegrationInput implements Impl::DataFlowIntegrationInpu
280339
predicate controlsBranchEdge(SsaInput::BasicBlock bb1, SsaInput::BasicBlock bb2, boolean branch) {
281340
this.hasBranchEdge(bb1, bb2, branch)
282341
}
342+
283343
/**
284344
* Holds if the evaluation of this guard to `branch` corresponds to the edge
285345
* from `bb1` to `bb2`.
@@ -291,8 +351,6 @@ private module DataFlowIntegrationInput implements Impl::DataFlowIntegrationInpu
291351
s.getValue() = branch
292352
)
293353
}
294-
295-
296354
}
297355

298356
/** Holds if the guard `guard` controls block `bb` upon evaluating to `branch`. */

0 commit comments

Comments
 (0)