Skip to content

Commit c7850b1

Browse files
authored
Merge pull request #109 from microsoft/powershell-call-target-resolution
PS: Resolve function calls
2 parents 953bd09 + 816aa79 commit c7850b1

15 files changed

Lines changed: 525 additions & 59 deletions

File tree

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,7 @@ class Ast extends @ast {
88

99
Location getLocation() { none() }
1010

11-
final Scope getEnclosingScope() { result = scopeOf(this) }
11+
Scope getEnclosingScope() { result = scopeOf(this) }
12+
13+
final Function getEnclosingFunction() { this.getEnclosingScope() = result.getBody() }
1214
}
Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,68 @@
11
import powershell
2+
private import semmle.code.powershell.dataflow.internal.DataFlowImplCommon
3+
private import semmle.code.powershell.dataflow.internal.DataFlowDispatch
4+
private import semmle.code.powershell.controlflow.CfgNodes
25

36
abstract private class AbstractCall extends Ast {
47
abstract Expr getCommand();
58

9+
/** Gets the i'th argument to this call. */
610
abstract Expr getArgument(int i);
711

8-
Expr getNamedArgument(string name) { none() }
12+
/** Gets the i'th positional argument to this call. */
13+
abstract Expr getPositionalArgument(int i);
914

10-
Expr getAnArgument() { result = this.getArgument(_) or result = this.getNamedArgument(_) }
15+
/** Gets the argument to this call with the name `name`. */
16+
abstract Expr getNamedArgument(string name);
1117

18+
/** Gets any argument to this call. */
19+
final Expr getAnArgument() { result = this.getArgument(_) }
20+
21+
/** Gets the qualifier of this call, if any. */
1222
Expr getQualifier() { none() }
23+
24+
/** Gets a possible runtime target of this call. */
25+
abstract Function getATarget();
1326
}
1427

1528
private class CmdCall extends AbstractCall instanceof Cmd {
1629
final override Expr getCommand() { result = Cmd.super.getCommand() }
1730

31+
final override Expr getPositionalArgument(int i) { result = Cmd.super.getPositionalArgument(i) }
32+
1833
final override Expr getArgument(int i) { result = Cmd.super.getArgument(i) }
1934

2035
final override Expr getNamedArgument(string name) { result = Cmd.super.getNamedArgument(name) }
36+
37+
final override Function getATarget() {
38+
exists(DataFlowCall call | call.asCall().(StmtNodes::CmdCfgNode).getStmt() = this |
39+
result.getBody() = viableCallableLambda(call, _).asCfgScope()
40+
or
41+
result.getBody() = getTarget(call)
42+
)
43+
}
2144
}
2245

2346
private class InvokeMemberCall extends AbstractCall instanceof InvokeMemberExpr {
2447
final override Expr getCommand() { result = super.getMember() }
2548

26-
final override Expr getArgument(int i) { result = InvokeMemberExpr.super.getArgument(i) }
49+
final override Expr getPositionalArgument(int i) {
50+
result = InvokeMemberExpr.super.getArgument(i)
51+
}
52+
53+
final override Expr getArgument(int i) { result = this.getPositionalArgument(i) }
2754

2855
final override Expr getQualifier() { result = InvokeMemberExpr.super.getQualifier() }
56+
57+
final override Expr getNamedArgument(string name) { none() }
58+
59+
final override Function getATarget() {
60+
exists(DataFlowCall call | call.asCall().(ExprNodes::InvokeMemberCfgNode).getExpr() = this |
61+
result.getBody() = viableCallableLambda(call, _).asCfgScope()
62+
or
63+
result.getBody() = getTarget(call)
64+
)
65+
}
2966
}
3067

3168
final class Call = AbstractCall;

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: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,21 @@ class InvokeMemberExpr extends @invoke_member_expression, MemberExprBase {
55

66
Expr getQualifier() { invoke_member_expression(this, result, _) }
77

8+
string getName() { result = this.getMember().(StringConstExpr).getValue().getValue() }
9+
810
CmdElement getMember() { invoke_member_expression(this, _, result) }
911

1012
Expr getArgument(int i) { invoke_member_expression_argument(this, i, result) }
1113

1214
Expr getAnArgument() { invoke_member_expression_argument(this, _, result) }
1315

14-
override string toString() { result = "call to " + this.getMember() }
16+
override string toString() { result = "call to " + this.getName() }
17+
18+
override predicate isStatic() { this.getQualifier() instanceof TypeNameExpr }
19+
}
20+
21+
class ConstructorCall extends InvokeMemberExpr {
22+
ConstructorCall() { this.isStatic() and this.getName() = "new" }
23+
24+
Type getConstructedType() { result = this.getQualifier().(TypeNameExpr).getType() }
1525
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ class MemberExpr extends @member_expression, MemberExprBase {
1212

1313
predicate isNullConditional() { member_expression(this, _, _, true, _) }
1414

15-
predicate isStatic() { member_expression(this, _, _, _, true) }
15+
override predicate isStatic() { member_expression(this, _, _, _, true) }
1616

1717
final override string toString() { result = this.getMember().toString() }
1818
}
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
import powershell
22

3-
class MemberExprBase extends @member_expression_base, Expr { }
3+
class MemberExprBase extends @member_expression_base, Expr {
4+
predicate isStatic() { none() }
5+
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import powershell
2+
private import semmle.code.powershell.controlflow.internal.Scope
23

34
class ScriptBlock extends @script_block, Ast {
45
predicate isTopLevel() { not exists(this.getParent()) }
@@ -48,4 +49,6 @@ class ScriptBlock extends @script_block, Ast {
4849
}
4950

5051
ModuleSpecification getAModuleSpecification() { result = this.getModuleSpecification(_) }
52+
53+
final override Scope getEnclosingScope() { result = this }
5154
}

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: 52 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,26 @@ abstract private class NonExprChildMapping extends ChildMapping {
125125
}
126126
}
127127

128+
abstract private class AbstractCallCfgNode extends AstCfgNode {
129+
override string getAPrimaryQlClass() { result = "CfgCall" }
130+
131+
abstract string getName();
132+
133+
ExprCfgNode getQualifier() { none() }
134+
135+
abstract ExprCfgNode getArgument(int i);
136+
137+
abstract ExprCfgNode getPositionalArgument(int i);
138+
139+
abstract ExprCfgNode getNamedArgument(string name);
140+
141+
abstract ExprCfgNode getAnArgument();
142+
143+
abstract ExprCfgNode getCommand();
144+
}
145+
146+
final class CallCfgNode = AbstractCallCfgNode;
147+
128148
/** Provides classes for control-flow nodes that wrap AST expressions. */
129149
module ExprNodes {
130150
private class VarAccessChildMapping extends ExprChildMapping, VarAccess {
@@ -189,14 +209,35 @@ module ExprNodes {
189209
}
190210

191211
/** A control-flow node that wraps an `InvokeMemberExpr` expression. */
192-
class InvokeMemberCfgNode extends ExprCfgNode {
212+
class InvokeMemberCfgNode extends ExprCfgNode, AbstractCallCfgNode {
193213
override string getAPrimaryQlClass() { result = "InvokeMemberCfgNode" }
194214

195215
override InvokeMemberChildMapping e;
196216

197-
final override InvokeMemberExpr getExpr() { result = super.getExpr() }
217+
override InvokeMemberExpr getExpr() { result = super.getExpr() }
218+
219+
final override ExprCfgNode getQualifier() { e.hasCfgChild(e.getQualifier(), this, result) }
198220

199-
final ExprCfgNode getQualifier() { e.hasCfgChild(e.getQualifier(), this, result) }
221+
final override ExprCfgNode getArgument(int i) { e.hasCfgChild(e.getArgument(i), this, result) }
222+
223+
final override ExprCfgNode getPositionalArgument(int i) { result = this.getArgument(i) }
224+
225+
final override ExprCfgNode getNamedArgument(string name) { none() }
226+
227+
final override ExprCfgNode getAnArgument() { e.hasCfgChild(e.getAnArgument(), this, result) }
228+
229+
final override string getName() { none() }
230+
231+
final override ExprCfgNode getCommand() { none() }
232+
}
233+
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() }
200241
}
201242

202243
/** A control-flow node that wraps a qualifier expression. */
@@ -265,26 +306,28 @@ module StmtNodes {
265306
}
266307

267308
/** A control-flow node that wraps a `Cmd` AST expression. */
268-
class CmdCfgNode extends StmtCfgNode {
309+
class CmdCfgNode extends StmtCfgNode, AbstractCallCfgNode {
269310
override string getAPrimaryQlClass() { result = "CmdCfgNode" }
270311

271312
override CmdChildMapping s;
272313

273314
override Cmd getStmt() { result = super.getStmt() }
274315

275-
ExprCfgNode getArgument(int i) { s.hasCfgChild(s.getArgument(i), this, result) }
316+
override ExprCfgNode getArgument(int i) { s.hasCfgChild(s.getArgument(i), this, result) }
276317

277-
ExprCfgNode getPositionalArgument(int i) {
318+
override ExprCfgNode getPositionalArgument(int i) {
278319
s.hasCfgChild(s.getPositionalArgument(i), this, result)
279320
}
280321

281-
ExprCfgNode getNamedArgument(string name) {
322+
override ExprCfgNode getNamedArgument(string name) {
282323
s.hasCfgChild(s.getNamedArgument(name), this, result)
283324
}
284325

285-
ExprCfgNode getAnArgument() { s.hasCfgChild(s.getAnArgument(), this, result) }
326+
override ExprCfgNode getAnArgument() { s.hasCfgChild(s.getAnArgument(), this, result) }
327+
328+
final override ExprCfgNode getCommand() { s.hasCfgChild(s.getCommand(), this, result) }
286329

287-
ExprCfgNode getCommand() { s.hasCfgChild(s.getCommand(), this, result) }
330+
final override string getName() { result = s.getCmdName().getValue().getValue() }
288331
}
289332

290333
private class AssignStmtChildMapping extends NonExprChildMapping, AssignStmt {

0 commit comments

Comments
 (0)