Skip to content

Commit f6a5b4b

Browse files
committed
PS: Resolve member function calls using the shared type-tracking library.
1 parent b6dfbc3 commit f6a5b4b

File tree

6 files changed

+79
-7
lines changed

6 files changed

+79
-7
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import semmle.code.powershell.controlflow.BasicBlocks
44
abstract private class AbstractFunction extends Ast {
55
abstract string getName();
66

7+
final predicate hasName(string name) { this.getName() = name }
8+
79
abstract ScriptBlock getBody();
810

911
abstract Parameter getFunctionParameter(int i);

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,9 @@ class InvokeMemberExpr extends @invoke_member_expression, MemberExprBase {
1717

1818
override predicate isStatic() { this.getQualifier() instanceof TypeNameExpr }
1919
}
20+
21+
class ConstructorCall extends InvokeMemberExpr {
22+
ConstructorCall() { this.isStatic() and this.getName() = "new" }
23+
24+
Type getConstructedType() { result = this.getQualifier().(TypeNameExpr).getType() }
25+
}

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,11 @@ class Type extends @type_definition, Stmt {
1010
Member getMember(int i) { type_definition_members(this, i, result) }
1111

1212
Member getAMember() { result = this.getMember(_) }
13+
14+
MemberFunction getMemberFunction(string name) {
15+
result = this.getAMember() and
16+
result.hasName(name)
17+
}
18+
19+
MemberFunction getAMemberFunction() { result = this.getMemberFunction(_) }
1320
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,7 @@ class TypeNameExpr extends @type_expression, Expr {
88
override string toString() { result = this.getName() }
99

1010
override SourceLocation getLocation() { type_expression_location(this, result) }
11+
12+
/** Gets the type referred to by this `TypeNameExpr`. */
13+
Type getType() { result.getName() = this.getName() }
1114
}

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

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ module ExprNodes {
214214

215215
override InvokeMemberChildMapping e;
216216

217-
final override InvokeMemberExpr getExpr() { result = super.getExpr() }
217+
override InvokeMemberExpr getExpr() { result = super.getExpr() }
218218

219219
final override ExprCfgNode getQualifier() { e.hasCfgChild(e.getQualifier(), this, result) }
220220

@@ -231,6 +231,15 @@ module ExprNodes {
231231
final override ExprCfgNode getCommand() { none() }
232232
}
233233

234+
/** A control-flow node that wraps an `ConstructorCall` expression. */
235+
class ConstructorCallCfgNode extends InvokeMemberCfgNode {
236+
ConstructorCallCfgNode() { super.getExpr() instanceof ConstructorCall }
237+
238+
final override ConstructorCall getExpr() { result = super.getExpr() }
239+
240+
Type getConstructedType() { result = this.getExpr().getConstructedType() }
241+
}
242+
234243
/** A control-flow node that wraps a qualifier expression. */
235244
class QualifierCfgNode extends ExprCfgNode {
236245
QualifierCfgNode() { this = any(InvokeMemberCfgNode invoke).getQualifier() }

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

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
private import powershell
22
private import semmle.code.powershell.Cfg
33
private import DataFlowPrivate
4+
private import DataFlowPublic
5+
private import semmle.code.powershell.typetracking.internal.TypeTrackingImpl
46
private import codeql.util.Boolean
57
private import codeql.util.Unit
68

@@ -123,11 +125,47 @@ class NormalCall extends DataFlowCall, TNormalCall {
123125
/** A call for which we want to compute call targets. */
124126
private class RelevantCall extends CfgNodes::CallCfgNode { }
125127

126-
/** Holds if `call` may resolve to the returned source-code method. */
127-
private DataFlowCallable viableSourceCallable(DataFlowCall call) {
128-
none() // TODO
129-
or
130-
result = any(AdditionalCallTarget t).viableTarget(call.asCall())
128+
private module TrackInstanceInput implements CallGraphConstruction::InputSig {
129+
newtype State = additional MkState(Type m, Boolean exact)
130+
131+
predicate start(Node start, State state) {
132+
exists(Type tp, boolean exact | state = MkState(tp, exact) |
133+
start.asExpr().(CfgNodes::ExprNodes::ConstructorCallCfgNode).getConstructedType() = tp
134+
)
135+
}
136+
137+
pragma[nomagic]
138+
predicate stepNoCall(Node nodeFrom, Node nodeTo, StepSummary summary) {
139+
smallStepNoCall(nodeFrom, nodeTo, summary)
140+
}
141+
142+
predicate stepCall(Node nodeFrom, Node nodeTo, StepSummary summary) {
143+
smallStepCall(nodeFrom, nodeTo, summary)
144+
}
145+
146+
class StateProj = Unit;
147+
148+
Unit stateProj(State state) { exists(state) and exists(result) }
149+
150+
predicate filter(Node n, Unit u) { none() }
151+
}
152+
153+
private predicate qualifiedCall(CfgNodes::CallCfgNode call, Node receiver, string method) {
154+
call.getQualifier() = receiver.asExpr() and
155+
call.getName() = method
156+
}
157+
158+
private Node trackInstance(Type t, boolean exact) {
159+
result =
160+
CallGraphConstruction::Make<TrackInstanceInput>::track(TrackInstanceInput::MkState(t, exact))
161+
}
162+
163+
private CfgScope getTargetInstance(CfgNodes::CallCfgNode call) {
164+
exists(Node receiver, string method, Type t |
165+
qualifiedCall(call, receiver, method) and
166+
receiver = trackInstance(t, _) and
167+
result = t.getMemberFunction(method).getBody()
168+
)
131169
}
132170

133171
/**
@@ -154,7 +192,14 @@ private module Cached {
154192

155193
/** Gets a viable run-time target for the call `call`. */
156194
cached
157-
DataFlowCallable viableCallable(DataFlowCall call) { result = viableSourceCallable(call) }
195+
DataFlowCallable viableCallable(DataFlowCall call) {
196+
result.asCfgScope() = getTargetInstance(call.asCall())
197+
or
198+
result = any(AdditionalCallTarget t).viableTarget(call.asCall())
199+
}
200+
201+
cached
202+
CfgScope getTarget(DataFlowCall call) { result = viableCallable(call).asCfgScope() }
158203

159204
cached
160205
newtype TArgumentPosition =

0 commit comments

Comments
 (0)