Skip to content

Commit 421258b

Browse files
authored
Merge pull request #112 from microsoft/powershell-more-type-flow
PS: Add more type-tracking flow
2 parents 46ead0d + b622e09 commit 421258b

File tree

17 files changed

+292
-43
lines changed

17 files changed

+292
-43
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import semmle.code.powershell.dataflow.DataFlow
2+
import semmle.code.powershell.typetracking.internal.TypeTrackingImpl
3+
4+
private module ConsistencyChecksInput implements ConsistencyChecksInputSig {
5+
predicate unreachableNodeExclude(DataFlow::Node n) { n instanceof DataFlow::PostUpdateNode }
6+
}
7+
8+
import ConsistencyChecks<ConsistencyChecksInput>

powershell/ql/lib/powershell.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ import semmle.code.powershell.StringConstantExpression
7171
import semmle.code.powershell.MemberExpr
7272
import semmle.code.powershell.InvokeMemberExpression
7373
import semmle.code.powershell.Call
74+
import semmle.code.powershell.ObjectCreation
7475
import semmle.code.powershell.SubExpression
7576
import semmle.code.powershell.ErrorExpr
7677
import semmle.code.powershell.ConvertExpr

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ abstract private class AbstractCall extends Ast {
1212
/** Gets the i'th positional argument to this call. */
1313
abstract Expr getPositionalArgument(int i);
1414

15+
/** Holds if an argument with name `name` is provided to this call. */
16+
final predicate hasNamedArgument(string name) { exists(this.getNamedArgument(name)) }
17+
1518
/** Gets the argument to this call with the name `name`. */
1619
abstract Expr getNamedArgument(string name);
1720

@@ -25,7 +28,8 @@ abstract private class AbstractCall extends Ast {
2528
abstract Function getATarget();
2629
}
2730

28-
private class CmdCall extends AbstractCall instanceof Cmd {
31+
/** A call to a command. For example, `Write-Host "Hello, world!"`. */
32+
class CmdCall extends AbstractCall instanceof Cmd {
2933
final override Expr getCommand() { result = Cmd.super.getCommand() }
3034

3135
final override Expr getPositionalArgument(int i) { result = Cmd.super.getPositionalArgument(i) }
@@ -43,7 +47,8 @@ private class CmdCall extends AbstractCall instanceof Cmd {
4347
}
4448
}
4549

46-
private class InvokeMemberCall extends AbstractCall instanceof InvokeMemberExpr {
50+
/** A call to a method on an object. For example, `$obj.ToString()`. */
51+
class MethodCall extends AbstractCall instanceof InvokeMemberExpr {
4752
final override Expr getCommand() { result = super.getMember() }
4853

4954
final override Expr getPositionalArgument(int i) {

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ class Cmd extends @command, CmdBase {
4949
)
5050
}
5151

52+
/** Holds if this call has an argument named `name`. */
53+
predicate hasNamedArgument(string name) { exists(this.getNamedArgument(name)) }
54+
5255
/** Gets the named argument with the given name. */
5356
Expr getNamedArgument(string name) {
5457
exists(int i, CmdParameter p |

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

Lines changed: 45 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,58 @@ import powershell
22
import semmle.code.powershell.controlflow.BasicBlocks
33

44
abstract private class AbstractFunction extends Ast {
5+
/** Gets the name of this function. */
56
abstract string getName();
67

8+
/** Holds if this function has name `name`. */
79
final predicate hasName(string name) { this.getName() = name }
810

11+
/** Gets the body of this function. */
912
abstract ScriptBlock getBody();
1013

14+
/**
15+
* Gets the i'th function parameter, if any.
16+
*
17+
* Note that this predicate only returns _function_ parameters.
18+
* To also get _block_ parameters use the `getParameter` predicate.
19+
*/
1120
abstract Parameter getFunctionParameter(int i);
1221

22+
/** Gets the declaring type of this function, if any. */
23+
abstract Type getDeclaringType();
24+
25+
/**
26+
* Gets any function parameter of this function.
27+
*
28+
* Note that this only gets _function_ paramters. To get any parameter
29+
* use the `getAParameter` predicate.
30+
*/
1331
final Parameter getAFunctionParameter() { result = this.getFunctionParameter(_) }
1432

33+
/** Gets the number of function parameters. */
1534
final int getNumberOfFunctionParameters() { result = count(this.getAFunctionParameter()) }
1635

36+
/** Gets the number of parameters (both function and block). */
1737
final int getNumberOfParameters() { result = count(this.getAParameter()) }
1838

39+
/** Gets the i'th parameter of this function, if any. */
1940
final Parameter getParameter(int i) {
2041
result = this.getFunctionParameter(i)
2142
or
2243
result = this.getBody().getParamBlock().getParameter(i)
2344
}
2445

46+
/** Gets any parameter of this function. */
2547
final Parameter getAParameter() { result = this.getParameter(_) }
2648

49+
/** Gets the entry point of this function in the control-flow graph. */
2750
EntryBasicBlock getEntryBasicBlock() { result.getScope() = this.getBody() }
2851
}
2952

30-
class NonMemberFunction extends @function_definition, Stmt, AbstractFunction {
53+
/**
54+
* A function definition.
55+
*/
56+
private class FunctionBase extends @function_definition, Stmt, AbstractFunction {
3157
override string toString() { result = this.getName() }
3258

3359
override SourceLocation getLocation() { function_definition_location(this, result) }
@@ -41,34 +67,32 @@ class NonMemberFunction extends @function_definition, Stmt, AbstractFunction {
4167
predicate isWorkflow() { function_definition(this, _, _, _, true) }
4268

4369
override Parameter getFunctionParameter(int i) { result.isFunctionParameter(this, i) }
44-
}
45-
46-
class MemberFunction extends @function_member, Member, AbstractFunction {
47-
override string getName() { function_member(this, _, _, _, _, _, _, result, _) }
48-
49-
override SourceLocation getLocation() { function_member_location(this, result) }
50-
51-
override string toString() { result = this.getName() }
5270

53-
override ScriptBlock getBody() { function_member(this, result, _, _, _, _, _, _, _) }
54-
55-
override predicate isHidden() { function_member(this, _, _, true, _, _, _, _, _) }
56-
57-
override predicate isPrivate() { function_member(this, _, _, _, true, _, _, _, _) }
71+
override Type getDeclaringType() { none() }
72+
}
5873

59-
override predicate isPublic() { function_member(this, _, _, _, _, true, _, _, _) }
74+
private predicate isMethod(Member m, ScriptBlock body) {
75+
function_member(m, body, _, _, _, _, _, _, _)
76+
}
6077

61-
override predicate isStatic() { function_member(this, _, _, _, _, _, true, _, _) }
78+
/**
79+
* A method definition. That is, a function defined inside a class definition.
80+
*/
81+
class Method extends FunctionBase {
82+
Method() { isMethod(_, super.getBody()) }
6283

63-
predicate isConstructor() { function_member(this, _, true, _, _, _, _, _, _) }
84+
/** Gets the member corresponding to this function definition. */
85+
Member getMember() { isMethod(result, super.getBody()) }
6486

65-
override Parameter getFunctionParameter(int i) { result.isFunctionParameter(this, i) }
87+
/** Holds if this method is a constructor. */
88+
predicate isConstructor() { function_member(this.getMember(), _, true, _, _, _, _, _, _) }
6689

67-
TypeConstraint getTypeConstraint() { function_member_return_type(this, result) }
90+
final override Type getDeclaringType() { result = this.getMember().getDeclaringType() }
6891
}
6992

70-
class Constructor extends MemberFunction {
93+
/** A constructor definition. */
94+
class Constructor extends Method {
7195
Constructor() { this.isConstructor() }
7296
}
7397

74-
final class Function = AbstractFunction;
98+
final class Function = FunctionBase;

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

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,30 @@ class InvokeMemberExpr extends @invoke_member_expression, MemberExprBase {
1818
override predicate isStatic() { this.getQualifier() instanceof TypeNameExpr }
1919
}
2020

21+
/**
22+
* A call to a constructor. For example:
23+
*
24+
* ```powershell
25+
* [System.IO.FileInfo]::new("C:\\file.txt")
26+
* ```
27+
*/
2128
class ConstructorCall extends InvokeMemberExpr {
22-
ConstructorCall() { this.isStatic() and this.getName() = "new" }
23-
24-
Type getConstructedType() { result = this.getQualifier().(TypeNameExpr).getType() }
29+
TypeNameExpr typename;
30+
31+
ConstructorCall() {
32+
this.isStatic() and typename = this.getQualifier() and this.getName() = "new"
33+
}
34+
35+
/**
36+
* Gets the type being constructed by this constructor call.
37+
*
38+
* Note that the type may not exist in the database.
39+
*
40+
* Use `getConstructedTypeName` to get the name of the type (which will
41+
* always exist in the database).
42+
*/
43+
Type getConstructedType() { result = typename.getType() }
44+
45+
/** Gets the name of the type being constructed by this constructor call. */
46+
string getConstructedTypeName() { result = typename.getName() }
2547
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import powershell
2+
3+
abstract private class AbstractObjectCreation extends Call {
4+
/**
5+
* The type of the object being constructed.
6+
* Note that the type may not exist in the database.
7+
*
8+
* Use `getConstructedTypeName` to get the name of the type (which will
9+
* always exist in the database).
10+
*/
11+
abstract Type getConstructedType();
12+
13+
/** The name of the type of the object being constructed. */
14+
abstract string getConstructedTypeName();
15+
}
16+
17+
/**
18+
* An object creation from a call to a constructor. For example:
19+
* ```powershell
20+
* [System.IO.FileInfo]::new("C:\\file.txt")
21+
* ```
22+
*/
23+
class NewObjectCreation extends AbstractObjectCreation instanceof ConstructorCall {
24+
final override Type getConstructedType() { result = ConstructorCall.super.getConstructedType() }
25+
26+
final override string getConstructedTypeName() {
27+
result = ConstructorCall.super.getConstructedTypeName()
28+
}
29+
}
30+
31+
/**
32+
* An object creation from a call to `New-Object`. For example:
33+
* ```powershell
34+
* New-Object -TypeName System.IO.FileInfo -ArgumentList "C:\\file.txt"
35+
* ```
36+
*/
37+
class DotNetObjectCreation extends AbstractObjectCreation instanceof Cmd {
38+
DotNetObjectCreation() { this.getCommandName() = "New-Object" }
39+
40+
final override Type getConstructedType() { none() }
41+
42+
final override string getConstructedTypeName() {
43+
// Either it's the named argument `TypeName`
44+
result = Cmd.super.getNamedArgument("TypeName").(StringConstExpr).getValue().getValue()
45+
or
46+
// Or it's the first positional argument if that's the named argument
47+
not Cmd.super.hasNamedArgument("TypeName") and
48+
exists(StringConstExpr arg | arg = Cmd.super.getPositionalArgument(0) |
49+
result = arg.getValue().getValue() and
50+
not arg = Cmd.super.getNamedArgument(["ArgumentList", "Property"])
51+
)
52+
}
53+
}
54+
55+
final class ObjectCreation = AbstractObjectCreation;

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

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,15 @@ class Type extends @type_definition, Stmt {
1111

1212
Member getAMember() { result = this.getMember(_) }
1313

14-
MemberFunction getMemberFunction(string name) {
15-
result = this.getAMember() and
14+
Method getMethod(string name) {
15+
result.getMember() = this.getAMember() and
1616
result.hasName(name)
1717
}
1818

19-
MemberFunction getAMemberFunction() { result = this.getMemberFunction(_) }
19+
Constructor getAConstructor() {
20+
result = this.getAMethod() and
21+
result.getName() = this.getName()
22+
}
23+
24+
Method getAMethod() { result = this.getMethod(_) }
2025
}

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ private import internal.Internal as Internal
44

55
private predicate isFunctionParameterImpl(Internal::Parameter p, Function f, int i) {
66
function_definition_parameter(f, i, p)
7-
or
8-
function_member_parameter(f, i, p)
97
}
108

119
private predicate hasParameterBlockImpl(Internal::Parameter p, ParamBlock block, int i) {

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

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,8 @@ abstract private class NonExprChildMapping extends ChildMapping {
128128
abstract private class AbstractCallCfgNode extends AstCfgNode {
129129
override string getAPrimaryQlClass() { result = "CfgCall" }
130130

131+
final predicate hasName(string name) { this.getName() = name }
132+
131133
abstract string getName();
132134

133135
ExprCfgNode getQualifier() { none() }
@@ -145,6 +147,18 @@ abstract private class AbstractCallCfgNode extends AstCfgNode {
145147

146148
final class CallCfgNode = AbstractCallCfgNode;
147149

150+
class ObjectCreationCfgNode extends CallCfgNode {
151+
ObjectCreation objectCreation;
152+
153+
ObjectCreationCfgNode() { this.getAstNode() = objectCreation }
154+
155+
ObjectCreation getObjectCreation() { result = objectCreation }
156+
157+
Type getConstructedType() { result = objectCreation.getConstructedType() }
158+
159+
string getConstructedTypeName() { result = objectCreation.getConstructedTypeName() }
160+
}
161+
148162
/** Provides classes for control-flow nodes that wrap AST expressions. */
149163
module ExprNodes {
150164
private class VarAccessChildMapping extends ExprChildMapping, VarAccess {
@@ -226,12 +240,12 @@ module ExprNodes {
226240

227241
final override ExprCfgNode getAnArgument() { e.hasCfgChild(e.getAnArgument(), this, result) }
228242

229-
final override string getName() { none() }
243+
final override string getName() { result = e.getName() }
230244

231245
final override ExprCfgNode getCommand() { none() }
232246
}
233247

234-
/** A control-flow node that wraps an `ConstructorCall` expression. */
248+
/** A control-flow node that wraps an `ConstructorCall` expression. */
235249
class ConstructorCallCfgNode extends InvokeMemberCfgNode {
236250
ConstructorCallCfgNode() { super.getExpr() instanceof ConstructorCall }
237251

@@ -247,6 +261,23 @@ module ExprNodes {
247261
InvokeMemberCfgNode getInvokeMember() { this = result.getQualifier() }
248262
}
249263

264+
class TypeNameChildMapping extends ExprChildMapping, TypeNameExpr {
265+
override predicate relevantChild(Ast n) { none() }
266+
}
267+
268+
/** A control-flow node that wraps a `TypeName` expression. */
269+
class TypeNameCfgNode extends ExprCfgNode {
270+
override string getAPrimaryQlClass() { result = "TypeNameCfgNode" }
271+
272+
override TypeNameChildMapping e;
273+
274+
override TypeNameExpr getExpr() { result = super.getExpr() }
275+
276+
Type getType() { result = this.getExpr().getType() }
277+
278+
string getTypeName() { result = this.getExpr().getName() }
279+
}
280+
250281
class ConditionalChildMapping extends ExprChildMapping, ConditionalExpr {
251282
override predicate relevantChild(Ast n) { n = this.getCondition() or n = this.getABranch() }
252283
}
@@ -350,4 +381,19 @@ module StmtNodes {
350381
/** Gets the RHS of this assignment. */
351382
final StmtCfgNode getRightHandSide() { s.hasCfgChild(s.getRightHandSide(), this, result) }
352383
}
384+
385+
class CmdExprChildMapping extends NonExprChildMapping, CmdExpr {
386+
override predicate relevantChild(Ast n) { n = this.getExpr() }
387+
}
388+
389+
/** A control-flow node that wraps a `CmdExpr` expression. */
390+
class CmdExprCfgNode extends StmtCfgNode {
391+
override string getAPrimaryQlClass() { result = "CmdExprCfgNode" }
392+
393+
override CmdExprChildMapping s;
394+
395+
override CmdExpr getStmt() { result = super.getStmt() }
396+
397+
final ExprCfgNode getExpr() { s.hasCfgChild(s.getExpr(), this, result) }
398+
}
353399
}

0 commit comments

Comments
 (0)