Skip to content

Commit 8fd8982

Browse files
authored
Merge pull request #97 from microsoft/powershell-integrate-ssa-into-dataflow
PS: Integrate SSA computations into dataflow
2 parents 9bdfaa0 + f14e1cc commit 8fd8982

File tree

12 files changed

+253
-20
lines changed

12 files changed

+253
-20
lines changed

powershell/ql/lib/powershell.qll

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,9 @@ import semmle.code.powershell.Pipeline
7070
import semmle.code.powershell.StringConstantExpression
7171
import semmle.code.powershell.MemberExpr
7272
import semmle.code.powershell.InvokeMemberExpression
73+
import semmle.code.powershell.Call
7374
import semmle.code.powershell.SubExpression
75+
import semmle.code.powershell.ErrorExpr
7476
import semmle.code.powershell.ConvertExpr
7577
import semmle.code.powershell.IndexExpr
7678
import semmle.code.powershell.HashTable
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import powershell
2+
3+
abstract private class AbstractCall extends Ast {
4+
abstract Expr getCommand();
5+
6+
abstract Expr getArgument(int i);
7+
8+
Expr getNamedArgument(string name) { none() }
9+
10+
Expr getQualifier() { none() }
11+
}
12+
13+
private class CmdCall extends AbstractCall instanceof Cmd {
14+
final override Expr getCommand() { result = Cmd.super.getCommand() }
15+
16+
final override Expr getArgument(int i) { result = Cmd.super.getArgument(i) }
17+
18+
final override Expr getNamedArgument(string name) { result = Cmd.super.getNamedArgument(name) }
19+
}
20+
21+
private class InvokeMemberCall extends AbstractCall instanceof InvokeMemberExpr {
22+
final override Expr getCommand() { result = super.getMember() }
23+
24+
final override Expr getArgument(int i) { result = InvokeMemberExpr.super.getArgument(i) }
25+
26+
final override Expr getQualifier() { result = InvokeMemberExpr.super.getQualifier() }
27+
}
28+
29+
final class Call = AbstractCall;

powershell/ql/lib/semmle/code/powershell/Command.qll

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ class Cmd extends @command, CmdBase {
1515

1616
CmdElement getElement(int i) { command_command_element(this, i, result) }
1717

18+
Expr getCommand() { result = this.getElement(0) }
19+
1820
StringConstExpr getCmdName() { result = this.getElement(0) }
1921

2022
Expr getArgument(int i) {
@@ -40,3 +42,20 @@ class Cmd extends @command, CmdBase {
4042

4143
Redirection getARedirection() { result = this.getRedirection(_) }
4244
}
45+
46+
/**
47+
* An argument to a command.
48+
*
49+
* The argument may be named or positional.
50+
*/
51+
class Argument extends Expr {
52+
Cmd cmd;
53+
54+
Argument() { cmd.getArgument(_) = this or cmd.getNamedArgument(_) = this }
55+
56+
Cmd getCmd() { result = cmd }
57+
58+
int getIndex() { cmd.getArgument(result) = this }
59+
60+
string getName() { cmd.getNamedArgument(result) = this }
61+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import powershell
2+
3+
class ErrorExpr extends @error_expression, Expr {
4+
final override SourceLocation getLocation() { error_expression_location(this, result) }
5+
6+
final override string toString() { result = "error" }
7+
}

powershell/ql/lib/semmle/code/powershell/InvokeMemberExpression.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import powershell
33
class InvokeMemberExpr extends @invoke_member_expression, MemberExprBase {
44
override SourceLocation getLocation() { invoke_member_expression_location(this, result) }
55

6-
Expr getBase() { invoke_member_expression(this, result, _) }
6+
Expr getQualifier() { invoke_member_expression(this, result, _) }
77

88
CmdElement getMember() { invoke_member_expression(this, _, result) }
99

powershell/ql/lib/semmle/code/powershell/controlflow/CfgNodes.qll

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,37 @@ module ExprNodes {
166166

167167
predicate isImplicitWrite() { e.isImplicit() }
168168
}
169+
170+
/** A control-flow node that wraps an argument expression. */
171+
class ArgumentCfgNode extends ExprCfgNode {
172+
override string getAPrimaryQlClass() { result = "ArgumentCfgNode" }
173+
174+
override Argument e;
175+
176+
final override Argument getExpr() { result = super.getExpr() }
177+
}
178+
179+
private class InvokeMemberChildMapping extends ExprChildMapping, InvokeMemberExpr {
180+
override predicate relevantChild(Ast n) { n = this.getQualifier() or n = this.getAnArgument() }
181+
}
182+
183+
/** A control-flow node that wraps an `InvokeMemberExpr` expression. */
184+
class InvokeMemberCfgNode extends ExprCfgNode {
185+
override string getAPrimaryQlClass() { result = "InvokeMemberCfgNode" }
186+
187+
override InvokeMemberChildMapping e;
188+
189+
final override InvokeMemberExpr getExpr() { result = super.getExpr() }
190+
191+
final ExprCfgNode getQualifier() { e.hasCfgChild(e.getQualifier(), this, result) }
192+
}
193+
194+
/** A control-flow node that wraps a qualifier expression. */
195+
class QualifierCfgNode extends ExprCfgNode {
196+
QualifierCfgNode() { this = any(InvokeMemberCfgNode invoke).getQualifier() }
197+
198+
InvokeMemberCfgNode getInvokeMember() { this = result.getQualifier() }
199+
}
169200
}
170201

171202
module StmtNodes {

powershell/ql/lib/semmle/code/powershell/controlflow/internal/Scope.qll

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,23 @@ Scope scopeOf(Ast n) {
1717
class Scope extends Ast, @script_block {
1818
/** Gets the outer scope, if any. */
1919
Scope getOuterScope() { result = scopeOf(this) }
20+
21+
/**
22+
* Gets the `i`'th paramter in this scope.
23+
*
24+
* This may be both function paramters and parameter block parameters.
25+
*/
26+
Parameter getParameter(int i) {
27+
exists(Function func |
28+
func.getBody() = this and
29+
result = func.getParameter(i)
30+
)
31+
}
32+
33+
/**
34+
* Gets a paramter in this scope.
35+
*
36+
* This may be both function paramters and parameter block parameters.
37+
*/
38+
Parameter getAParameter() { result = this.getParameter(_) }
2039
}

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

Lines changed: 130 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ private import codeql.util.Boolean
22
private import codeql.util.Unit
33
private import powershell
44
private import semmle.code.powershell.Cfg
5+
private import semmle.code.powershell.dataflow.Ssa
56
private import DataFlowPublic
67
private import DataFlowDispatch
8+
private import SsaImpl as SsaImpl
79

810
/** Gets the callable in which this node occurs. */
911
DataFlowCallable nodeGetEnclosingCallable(Node n) { result = n.(NodeImpl).getEnclosingCallable() }
@@ -39,9 +41,40 @@ private class ExprNodeImpl extends ExprNode, NodeImpl {
3941
override string toStringImpl() { result = this.getExprNode().toString() }
4042
}
4143

44+
/** Gets the SSA definition node corresponding to parameter `p`. */
45+
pragma[nomagic]
46+
SsaImpl::DefinitionExt getParameterDef(Parameter p) {
47+
exists(EntryBasicBlock bb, int i |
48+
SsaImpl::parameterWrite(bb, i, p) and
49+
result.definesAt(p, bb, i, _)
50+
)
51+
}
52+
4253
/** Provides logic related to SSA. */
4354
module SsaFlow {
44-
// TODO
55+
private module Impl = SsaImpl::DataFlowIntegration;
56+
57+
private ParameterNodeImpl toParameterNode(SsaImpl::ParameterExt p) {
58+
result = TNormalParameterNode(p.asParameter())
59+
}
60+
61+
Impl::Node asNode(Node n) {
62+
n = TSsaNode(result)
63+
or
64+
result.(Impl::ExprNode).getExpr() = n.asExpr()
65+
or
66+
result.(Impl::ExprPostUpdateNode).getExpr() = n.(PostUpdateNode).getPreUpdateNode().asExpr()
67+
or
68+
n = toParameterNode(result.(Impl::ParameterNode).getParameter())
69+
}
70+
71+
predicate localFlowStep(SsaImpl::DefinitionExt def, Node nodeFrom, Node nodeTo, boolean isUseStep) {
72+
Impl::localFlowStep(def, asNode(nodeFrom), asNode(nodeTo), isUseStep)
73+
}
74+
75+
predicate localMustFlowStep(SsaImpl::DefinitionExt def, Node nodeFrom, Node nodeTo) {
76+
Impl::localMustFlowStep(def, asNode(nodeFrom), asNode(nodeTo))
77+
}
4578
}
4679

4780
/** Provides predicates related to local data flow. */
@@ -54,19 +87,6 @@ module LocalFlow {
5487
predicate localMustFlowStep(Node node1, Node node2) { none() }
5588
}
5689

57-
/** An argument of a call (including qualifier arguments and block arguments). */
58-
private class Argument extends CfgNodes::ExprCfgNode {
59-
private CfgNodes::StmtNodes::CmdCfgNode call;
60-
private ArgumentPosition arg;
61-
62-
Argument() { none() }
63-
64-
/** Holds if this expression is the `i`th argument of `c`. */
65-
predicate isArgumentOf(CfgNodes::StmtNodes::CmdCfgNode c, ArgumentPosition pos) {
66-
c = call and pos = arg
67-
}
68-
}
69-
7090
/** Provides logic related to captured variables. */
7191
module VariableCapture {
7292
// TODO
@@ -78,8 +98,11 @@ private module Cached {
7898
cached
7999
newtype TNode =
80100
TExprNode(CfgNodes::ExprCfgNode n) or
101+
TSsaNode(SsaImpl::DataFlowIntegration::SsaNode node) or
102+
TNormalParameterNode(Parameter p) or
81103
TExprPostUpdateNode(CfgNodes::ExprCfgNode n) {
82-
none() // TODO
104+
n instanceof CfgNodes::ExprNodes::ArgumentCfgNode or
105+
n instanceof CfgNodes::ExprNodes::QualifierCfgNode
83106
}
84107

85108
cached
@@ -117,6 +140,60 @@ import Cached
117140
/** Holds if `n` should be hidden from path explanations. */
118141
predicate nodeIsHidden(Node n) { none() }
119142

143+
/** An SSA node. */
144+
abstract class SsaNode extends NodeImpl, TSsaNode {
145+
SsaImpl::DataFlowIntegration::SsaNode node;
146+
SsaImpl::DefinitionExt def;
147+
148+
SsaNode() {
149+
this = TSsaNode(node) and
150+
def = node.getDefinitionExt()
151+
}
152+
153+
SsaImpl::DefinitionExt getDefinitionExt() { result = def }
154+
155+
/** Holds if this node should be hidden from path explanations. */
156+
abstract predicate isHidden();
157+
158+
override Location getLocationImpl() { result = node.getLocation() }
159+
160+
override string toStringImpl() { result = node.toString() }
161+
}
162+
163+
/** An (extended) SSA definition, viewed as a node in a data flow graph. */
164+
class SsaDefinitionExtNode extends SsaNode {
165+
override SsaImpl::DataFlowIntegration::SsaDefinitionExtNode node;
166+
167+
/** Gets the underlying variable. */
168+
Variable getVariable() { result = def.getSourceVariable() }
169+
170+
override predicate isHidden() {
171+
not def instanceof Ssa::WriteDefinition
172+
or
173+
def = getParameterDef(_)
174+
}
175+
176+
override CfgScope getCfgScope() { result = def.getBasicBlock().getScope() }
177+
}
178+
179+
class SsaDefinitionNodeImpl extends SsaDefinitionExtNode {
180+
Ssa::Definition ssaDef;
181+
182+
SsaDefinitionNodeImpl() { ssaDef = def }
183+
184+
override Location getLocationImpl() { result = ssaDef.getLocation() }
185+
186+
override string toStringImpl() { result = ssaDef.toString() }
187+
}
188+
189+
class SsaInputNode extends SsaNode {
190+
override SsaImpl::DataFlowIntegration::SsaInputNode node;
191+
192+
override predicate isHidden() { any() }
193+
194+
override CfgScope getCfgScope() { result = node.getDefinitionExt().getBasicBlock().getScope() }
195+
}
196+
120197
private module ParameterNodes {
121198
abstract class ParameterNodeImpl extends NodeImpl {
122199
abstract Parameter getParameter();
@@ -130,7 +207,30 @@ private module ParameterNodes {
130207
)
131208
}
132209
}
133-
// TODO
210+
211+
/**
212+
* The value of a normal parameter at function entry, viewed as a node in a data
213+
* flow graph.
214+
*/
215+
class NormalParameterNode extends ParameterNodeImpl, TNormalParameterNode {
216+
Parameter parameter;
217+
218+
NormalParameterNode() { this = TNormalParameterNode(parameter) }
219+
220+
override Parameter getParameter() { result = parameter }
221+
222+
override predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) {
223+
exists(CfgScope callable, int i |
224+
callable = c.asCfgScope() and pos.isPositional(i) and callable.getParameter(i) = parameter
225+
)
226+
}
227+
228+
override CfgScope getCfgScope() { result.getAParameter() = parameter }
229+
230+
override Location getLocationImpl() { result = parameter.getLocation() }
231+
232+
override string toStringImpl() { result = parameter.toString() }
233+
}
134234
}
135235

136236
import ParameterNodes
@@ -252,7 +352,19 @@ abstract class PostUpdateNodeImpl extends Node {
252352
}
253353

254354
private module PostUpdateNodes {
255-
// TODO
355+
class ExprPostUpdateNode extends PostUpdateNodeImpl, NodeImpl, TExprPostUpdateNode {
356+
private CfgNodes::ExprCfgNode e;
357+
358+
ExprPostUpdateNode() { this = TExprPostUpdateNode(e) }
359+
360+
override ExprNode getPreUpdateNode() { e = result.getExprNode() }
361+
362+
override CfgScope getCfgScope() { result = e.getExpr().getEnclosingScope() }
363+
364+
override Location getLocationImpl() { result = e.getLocation() }
365+
366+
override string toStringImpl() { result = "[post] " + e.toString() }
367+
}
256368
}
257369

258370
private import PostUpdateNodes
@@ -276,7 +388,7 @@ class NodeRegion instanceof Unit {
276388
predicate contains(Node n) { none() }
277389

278390
/** Gets a best-effort total ordering. */
279-
int totalOrder() { none() }
391+
int totalOrder() { result = 1 }
280392
}
281393

282394
/**

powershell/ql/test/library-tests/ast/Expressions/expressions.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ query predicate cmdExpr(CmdExpr cmd, Expr e) {
1010
}
1111

1212
query predicate invokeMemoryExpression(InvokeMemberExpr invoke, Expr e, int i, Expr arg) {
13-
e = invoke.getBase() and
13+
e = invoke.getQualifier() and
1414
arg = invoke.getArgument(i)
1515
}
1616

powershell/ql/test/library-tests/dataflow/local/test.expected

Whitespace-only changes.

0 commit comments

Comments
 (0)