Skip to content

Commit b5950b0

Browse files
authored
Merge pull request #92 from microsoft/powershell-dataflow-skeleton
PS: Add dataflow skeleton
2 parents 198ece9 + faf774f commit b5950b0

File tree

12 files changed

+890
-0
lines changed

12 files changed

+890
-0
lines changed

powershell/ql/lib/qlpack.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,6 @@ library: true
99
upgrades: upgrades
1010
dependencies:
1111
codeql/controlflow: ${workspace}
12+
codeql/dataflow: ${workspace}
13+
codeql/util: ${workspace}
1214
warnOnImplicitThis: true

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,5 +48,28 @@ class ExprCfgNode extends AstCfgNode {
4848
Expr getExpr() { result = e }
4949
}
5050

51+
/** A control-flow node that wraps an AST statement. */
52+
class StmtCfgNode extends AstCfgNode {
53+
override string getAPrimaryQlClass() { result = "StmtCfgNode" }
54+
55+
Stmt s;
56+
57+
StmtCfgNode() { s = this.getAstNode() }
58+
59+
/** Gets the underlying expression. */
60+
Stmt getStmt() { result = s }
61+
}
62+
5163
/** Provides classes for control-flow nodes that wrap AST expressions. */
5264
module ExprNodes { }
65+
66+
module StmtNodes {
67+
/** A control-flow node that wraps a `Cmd` AST expression. */
68+
class CallCfgNode extends StmtCfgNode {
69+
override string getAPrimaryQlClass() { result = "CallCfgNode" }
70+
71+
override Cmd s;
72+
73+
override Cmd getStmt() { result = super.getStmt() }
74+
}
75+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/**
2+
* Provides classes for performing local (intra-procedural) and
3+
* global (inter-procedural) data flow analyses.
4+
*/
5+
6+
import powershell
7+
8+
module DataFlow {
9+
private import internal.DataFlowImplSpecific
10+
private import codeql.dataflow.DataFlow
11+
import DataFlowMake<Location, PowershellDataFlow>
12+
import internal.DataFlowImpl
13+
}
Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
private import powershell
2+
private import semmle.code.powershell.Cfg
3+
private import DataFlowPrivate
4+
private import codeql.util.Boolean
5+
private import codeql.util.Unit
6+
7+
newtype TReturnKind = TNormalReturnKind()
8+
9+
/**
10+
* Gets a node that can read the value returned from `call` with return kind
11+
* `kind`.
12+
*/
13+
OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) { call = result.getCall(kind) }
14+
15+
/**
16+
* A return kind. A return kind describes how a value can be returned
17+
* from a callable.
18+
*/
19+
abstract class ReturnKind extends TReturnKind {
20+
/** Gets a textual representation of this position. */
21+
abstract string toString();
22+
}
23+
24+
/**
25+
* A value returned from a callable using a `return` statement or an expression
26+
* body, that is, a "normal" return.
27+
*/
28+
class NormalReturnKind extends ReturnKind, TNormalReturnKind {
29+
override string toString() { result = "return" }
30+
}
31+
32+
/** A callable defined in library code, identified by a unique string. */
33+
abstract class LibraryCallable extends string {
34+
bindingset[this]
35+
LibraryCallable() { any() }
36+
37+
/** Gets a call to this library callable. */
38+
Cmd getACall() { none() }
39+
}
40+
41+
/**
42+
* A callable. This includes callables from source code, as well as callables
43+
* defined in library code.
44+
*/
45+
class DataFlowCallable extends TDataFlowCallable {
46+
/**
47+
* Gets the underlying CFG scope, if any.
48+
*
49+
* This is usually a `Callable`, but can also be a `Toplevel` file.
50+
*/
51+
CfgScope asCfgScope() { this = TCfgScope(result) }
52+
53+
/** Gets the underlying library callable, if any. */
54+
LibraryCallable asLibraryCallable() { this = TLibraryCallable(result) }
55+
56+
/** Gets a textual representation of this callable. */
57+
string toString() { result = [this.asCfgScope().toString(), this.asLibraryCallable()] }
58+
59+
/** Gets the location of this callable. */
60+
Location getLocation() {
61+
result = this.asCfgScope().getLocation()
62+
or
63+
this instanceof TLibraryCallable and
64+
result instanceof EmptyLocation
65+
}
66+
67+
/** Gets a best-effort total ordering. */
68+
int totalorder() { none() }
69+
}
70+
71+
/**
72+
* A call. This includes calls from source code, as well as call(back)s
73+
* inside library callables with a flow summary.
74+
*/
75+
abstract class DataFlowCall extends TDataFlowCall {
76+
/** Gets the enclosing callable. */
77+
abstract DataFlowCallable getEnclosingCallable();
78+
79+
/** Gets the underlying source code call, if any. */
80+
abstract CfgNodes::StmtNodes::CallCfgNode asCall();
81+
82+
/** Gets a textual representation of this call. */
83+
abstract string toString();
84+
85+
/** Gets the location of this call. */
86+
abstract Location getLocation();
87+
88+
DataFlowCallable getARuntimeTarget() { none() }
89+
90+
ArgumentNode getAnArgumentNode() { none() }
91+
92+
/** Gets a best-effort total ordering. */
93+
int totalorder() { none() }
94+
95+
/**
96+
* Holds if this element is at the specified location.
97+
* The location spans column `startcolumn` of line `startline` to
98+
* column `endcolumn` of line `endline` in file `filepath`.
99+
* For more information, see
100+
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries).
101+
*/
102+
predicate hasLocationInfo(
103+
string filepath, int startline, int startcolumn, int endline, int endcolumn
104+
) {
105+
this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
106+
}
107+
}
108+
109+
class NormalCall extends DataFlowCall, TNormalCall {
110+
private CfgNodes::StmtNodes::CallCfgNode c;
111+
112+
NormalCall() { this = TNormalCall(c) }
113+
114+
override CfgNodes::StmtNodes::CallCfgNode asCall() { result = c }
115+
116+
override DataFlowCallable getEnclosingCallable() { result = TCfgScope(c.getScope()) }
117+
118+
override string toString() { result = c.toString() }
119+
120+
override Location getLocation() { result = c.getLocation() }
121+
}
122+
123+
/** A call for which we want to compute call targets. */
124+
private class RelevantCall extends CfgNodes::StmtNodes::CallCfgNode { }
125+
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())
131+
}
132+
133+
/**
134+
* A unit class for adding additional call steps.
135+
*
136+
* Extend this class to add additional call steps to the data flow graph.
137+
*/
138+
class AdditionalCallTarget extends Unit {
139+
/**
140+
* Gets a viable target for `call`.
141+
*/
142+
abstract DataFlowCallable viableTarget(CfgNodes::StmtNodes::CallCfgNode call);
143+
}
144+
145+
/** Holds if `call` may resolve to the returned summarized library method. */
146+
DataFlowCallable viableLibraryCallable(DataFlowCall call) {
147+
exists(LibraryCallable callable |
148+
result = TLibraryCallable(callable) and
149+
call.asCall().getStmt() = callable.getACall()
150+
)
151+
}
152+
153+
cached
154+
private module Cached {
155+
cached
156+
newtype TDataFlowCallable =
157+
TCfgScope(CfgScope scope) or
158+
TLibraryCallable(LibraryCallable callable)
159+
160+
cached
161+
newtype TDataFlowCall = TNormalCall(CfgNodes::StmtNodes::CallCfgNode c)
162+
163+
/** Gets a viable run-time target for the call `call`. */
164+
cached
165+
DataFlowCallable viableCallable(DataFlowCall call) {
166+
result = viableSourceCallable(call)
167+
or
168+
result = viableLibraryCallable(call)
169+
}
170+
171+
cached
172+
newtype TArgumentPosition =
173+
TPositionalArgumentPosition(int pos) { exists(Cmd c | exists(c.getArgument(pos))) }
174+
175+
cached
176+
newtype TParameterPosition = TPositionalParameterPosition(int pos) { none() /* TODO */ }
177+
}
178+
179+
import Cached
180+
181+
/** A parameter position. */
182+
class ParameterPosition extends TParameterPosition {
183+
/** Holds if this position represents a positional parameter at position `pos`. */
184+
predicate isPositional(int pos) { this = TPositionalParameterPosition(pos) }
185+
186+
/** Gets a textual representation of this position. */
187+
string toString() { exists(int pos | this.isPositional(pos) and result = "position " + pos) }
188+
}
189+
190+
/** An argument position. */
191+
class ArgumentPosition extends TArgumentPosition {
192+
/** Holds if this position represents a positional argument at position `pos`. */
193+
predicate isPositional(int pos) { this = TPositionalArgumentPosition(pos) }
194+
195+
/** Gets a textual representation of this position. */
196+
string toString() { exists(int pos | this.isPositional(pos) and result = "position " + pos) }
197+
}
198+
199+
/** Holds if arguments at position `apos` match parameters at position `ppos`. */
200+
pragma[nomagic]
201+
predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) {
202+
exists(int pos | ppos.isPositional(pos) and apos.isPositional(pos))
203+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
private import DataFlowImplCommon
2+
private import DataFlowImplSpecific::Private
3+
import DataFlowImplSpecific::Public
4+
import DataFlowImplCommonPublic
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
private import powershell
2+
private import DataFlowImplSpecific
3+
private import codeql.dataflow.internal.DataFlowImplCommon
4+
import MakeImplCommon<Location, PowershellDataFlow>
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/**
2+
* Provides Powershell-specific definitions for use in the data flow library.
3+
*/
4+
5+
private import powershell
6+
private import codeql.dataflow.DataFlow
7+
8+
module Private {
9+
import DataFlowPrivate
10+
import DataFlowDispatch
11+
}
12+
13+
module Public {
14+
import DataFlowPublic
15+
}
16+
17+
module PowershellDataFlow implements InputSig<Location> {
18+
import Private
19+
import Public
20+
21+
class ParameterNode = Private::ParameterNodeImpl;
22+
23+
Node exprNode(DataFlowExpr e) { result = Public::exprNode(e) }
24+
}

0 commit comments

Comments
 (0)