Skip to content

Commit 5803e06

Browse files
authored
Merge pull request #104 from microsoft/powershell-field-flow
PS: Add field flow
2 parents 3fa466e + 1ce4c2f commit 5803e06

File tree

7 files changed

+111
-9
lines changed

7 files changed

+111
-9
lines changed

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

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,26 @@ import powershell
33
class MemberExpr extends @member_expression, MemberExprBase {
44
final override Location getLocation() { member_expression_location(this, result) }
55

6-
Expr getExpr() { member_expression(this, result, _, _, _) }
6+
Expr getBase() { member_expression(this, result, _, _, _) }
77

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

10+
/** Gets the name of the member being looked up, if any. */
11+
string getMemberName() { result = this.getMember().(StringConstExpr).getValue().getValue() }
12+
1013
predicate isNullConditional() { member_expression(this, _, _, true, _) }
1114

1215
predicate isStatic() { member_expression(this, _, _, _, true) }
1316

1417
final override string toString() { result = this.getMember().toString() }
1518
}
19+
20+
/** A `MemberExpr` that is being written to. */
21+
class MemberExprWriteAccess extends MemberExpr {
22+
MemberExprWriteAccess() { this = any(AssignStmt assign).getLeftHandSide() }
23+
}
24+
25+
/** A `MemberExpr` that is being read from. */
26+
class MemberExprReadAccess extends MemberExpr {
27+
MemberExprReadAccess() { not this instanceof MemberExprWriteAccess }
28+
}

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

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ module ExprNodes {
191191
final ExprCfgNode getQualifier() { e.hasCfgChild(e.getQualifier(), this, result) }
192192
}
193193

194-
/** A control-flow node that wraps a qualifier expression. */
194+
/** A control-flow node that wraps a qualifier expression. */
195195
class QualifierCfgNode extends ExprCfgNode {
196196
QualifierCfgNode() { this = any(InvokeMemberCfgNode invoke).getQualifier() }
197197

@@ -220,6 +220,35 @@ module ExprNodes {
220220

221221
final ExprCfgNode getIfFalse() { e.hasCfgChild(e.getIfFalse(), this, result) }
222222
}
223+
224+
class MemberChildMapping extends ExprChildMapping, MemberExpr {
225+
override predicate relevantChild(Ast n) { n = this.getBase() or n = this.getMember() }
226+
}
227+
228+
/** A control-flow node that wraps a `MemberExpr` expression. */
229+
class MemberCfgNode extends ExprCfgNode {
230+
override string getAPrimaryQlClass() { result = "MemberCfgNode" }
231+
232+
override MemberChildMapping e;
233+
234+
final override MemberExpr getExpr() { result = super.getExpr() }
235+
236+
final ExprCfgNode getBase() { e.hasCfgChild(e.getBase(), this, result) }
237+
238+
final string getMemberName() { result = e.getMemberName() }
239+
240+
predicate isStatic() { e.isStatic() }
241+
}
242+
243+
/** A control-flow node that wraps a `MemberExpr` expression that is being written to. */
244+
class MemberCfgWriteAccessNode extends MemberCfgNode {
245+
MemberCfgWriteAccessNode() { this.getExpr() instanceof MemberExprWriteAccess }
246+
}
247+
248+
/** A control-flow node that wraps a `MemberExpr` expression that is being read from. */
249+
class MemberCfgReadAccessNode extends MemberCfgNode {
250+
MemberCfgReadAccessNode() { this.getExpr() instanceof MemberExprReadAccess }
251+
}
223252
}
224253

225254
module StmtNodes {

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

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,14 @@ private module Cached {
114114
TSsaNode(SsaImpl::DataFlowIntegration::SsaNode node) or
115115
TNormalParameterNode(Parameter p) or
116116
TExprPostUpdateNode(CfgNodes::ExprCfgNode n) {
117-
n instanceof CfgNodes::ExprNodes::ArgumentCfgNode or
117+
n instanceof CfgNodes::ExprNodes::ArgumentCfgNode
118+
or
118119
n instanceof CfgNodes::ExprNodes::QualifierCfgNode
120+
or
121+
exists(CfgNodes::ExprNodes::MemberCfgNode member |
122+
n = member.getBase() and
123+
not member.isStatic()
124+
)
119125
}
120126

121127
cached
@@ -150,10 +156,15 @@ private module Cached {
150156
newtype TContentSet = TSingletonContent(Content c)
151157

152158
cached
153-
newtype TContent = TFieldContent(string name) { none() }
159+
newtype TContent =
160+
TFieldContent(string name) {
161+
name = any(PropertyMember member).getName()
162+
or
163+
name = any(MemberExpr me).getMemberName()
164+
}
154165

155166
cached
156-
newtype TContentApprox = TNonElementContentApprox(Content c) // TODO
167+
newtype TContentApprox = TNonElementContentApprox(Content c)
157168

158169
cached
159170
newtype TDataFlowType = TUnknownDataFlowType()
@@ -309,14 +320,26 @@ predicate jumpStep(Node pred, Node succ) {
309320
* content `c`.
310321
*/
311322
predicate storeStep(Node node1, ContentSet c, Node node2) {
312-
none() // TODO
323+
node2.(PostUpdateNode).getPreUpdateNode().asExpr() =
324+
any(CfgNodes::ExprNodes::MemberCfgNode var |
325+
exists(CfgNodes::StmtNodes::AssignStmtCfgNode assign |
326+
var = assign.getLeftHandSide() and
327+
node1.asStmt() = assign.getRightHandSide()
328+
|
329+
c.isSingleton(any(Content::FieldContent ct | ct.getName() = var.getMemberName()))
330+
)
331+
).getBase()
313332
}
314333

315334
/**
316335
* Holds if there is a read step of content `c` from `node1` to `node2`.
317336
*/
318337
predicate readStep(Node node1, ContentSet c, Node node2) {
319-
none() // TODO
338+
node2.asExpr() =
339+
any(CfgNodes::ExprNodes::MemberCfgReadAccessNode var |
340+
node1.asExpr() = var.getBase() and
341+
c.isSingleton(any(Content::FieldContent ct | ct.getName() = var.getMemberName()))
342+
)
320343
}
321344

322345
/**
@@ -325,7 +348,7 @@ predicate readStep(Node node1, ContentSet c, Node node2) {
325348
* in `x.f = newValue`.
326349
*/
327350
predicate clearsContent(Node n, ContentSet c) {
328-
none() // TODO
351+
n = any(PostUpdateNode pun | storeStep(_, c, pun)).getPreUpdateNode()
329352
}
330353

331354
/**

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

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,19 @@ class Content extends TContent {
161161
}
162162

163163
/** Provides different sub classes of `Content`. */
164-
module Content { }
164+
module Content {
165+
/** A field of an object. */
166+
class FieldContent extends Content, TFieldContent {
167+
private string name;
168+
169+
FieldContent() { this = TFieldContent(name) }
170+
171+
/** Gets the name of the field. */
172+
string getName() { result = name }
173+
174+
override string toString() { result = name }
175+
}
176+
}
165177

166178
/**
167179
* An entity that represents a set of `Content`s.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
models
2+
edges
3+
nodes
4+
subpaths
5+
testFailures
6+
#select
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
$a.f = Source "1"
2+
Sink $a.f # $ MISSING: hasValueFlow=1
3+
4+
$a.f = Source "2"
5+
$a.f = 0
6+
Sink $a.f # clean
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/**
2+
* @kind path-problem
3+
*/
4+
5+
import powershell
6+
import semmle.code.powershell.dataflow.DataFlow
7+
private import TestUtilities.InlineFlowTest
8+
import DefaultFlowTest
9+
import ValueFlow::PathGraph
10+
11+
from ValueFlow::PathNode source, ValueFlow::PathNode sink
12+
where ValueFlow::flowPath(source, sink)
13+
select sink, source, sink, "$@", source, source.toString()

0 commit comments

Comments
 (0)