Skip to content

Commit b6dfbc3

Browse files
committed
PS: Instantiate most of the shared type-tracking library.
1 parent 9049407 commit b6dfbc3

File tree

4 files changed

+320
-0
lines changed

4 files changed

+320
-0
lines changed

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

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,8 @@ module VariableCapture {
107107
/** A collection of cached types and predicates to be evaluated in the same stage. */
108108
cached
109109
private module Cached {
110+
private import semmle.code.powershell.typetracking.internal.TypeTrackingImpl
111+
110112
cached
111113
newtype TNode =
112114
TExprNode(CfgNodes::ExprCfgNode n) or
@@ -152,6 +154,66 @@ private module Cached {
152154
SsaFlow::localFlowStep(_, nodeFrom, nodeTo, _)
153155
}
154156

157+
/**
158+
* This is the local flow predicate that is used in type tracking.
159+
*/
160+
cached
161+
predicate localFlowStepTypeTracker(Node nodeFrom, Node nodeTo) {
162+
LocalFlow::localFlowStepCommon(nodeFrom, nodeTo)
163+
or
164+
SsaFlow::localFlowStep(_, nodeFrom, nodeTo, _)
165+
}
166+
167+
/** Holds if `n` wraps an SSA definition without ingoing flow. */
168+
private predicate entrySsaDefinition(SsaDefinitionExtNode n) {
169+
n.getDefinitionExt() =
170+
any(SsaImpl::WriteDefinition def | not def.(Ssa::WriteDefinition).assigns(_))
171+
}
172+
173+
pragma[nomagic]
174+
private predicate reachedFromExprOrEntrySsaDef(Node n) {
175+
localFlowStepTypeTracker(any(Node n0 |
176+
n0 instanceof ExprNode
177+
or
178+
entrySsaDefinition(n0)
179+
), n)
180+
or
181+
exists(Node mid |
182+
reachedFromExprOrEntrySsaDef(mid) and
183+
localFlowStepTypeTracker(mid, n)
184+
)
185+
}
186+
187+
private predicate isStoreTargetNode(Node n) {
188+
TypeTrackingInput::storeStep(_, n, _)
189+
or
190+
TypeTrackingInput::loadStoreStep(_, n, _, _)
191+
or
192+
TypeTrackingInput::withContentStepImpl(_, n, _)
193+
or
194+
TypeTrackingInput::withoutContentStepImpl(_, n, _)
195+
}
196+
197+
cached
198+
predicate isLocalSourceNode(Node n) {
199+
n instanceof ParameterNode
200+
or
201+
// Expressions that can't be reached from another entry definition or expression
202+
n instanceof ExprNode and
203+
not reachedFromExprOrEntrySsaDef(n)
204+
or
205+
// Ensure all entry SSA definitions are local sources, except those that correspond
206+
// to parameters (which are themselves local sources)
207+
entrySsaDefinition(n) and
208+
not exists(SsaImpl::ParameterExt p |
209+
p.isInitializedBy(n.(SsaDefinitionExtNode).getDefinitionExt())
210+
)
211+
or
212+
isStoreTargetNode(n)
213+
or
214+
TypeTrackingInput::loadStep(_, n, _)
215+
}
216+
155217
cached
156218
newtype TContentSet = TSingletonContent(Content c)
157219

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,13 @@ class ParameterNode extends Node {
7676
final Parameter getParameter() { result = getParameter(this) }
7777
}
7878

79+
/**
80+
* A data-flow node that is a source of local flow.
81+
*/
82+
class LocalSourceNode extends Node {
83+
LocalSourceNode() { isLocalSourceNode(this) }
84+
}
85+
7986
/**
8087
* A node associated with an object after an operation that might have
8188
* changed its state.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/**
2+
* Provides classes and predicates for simple data-flow reachability suitable
3+
* for tracking types.
4+
*/
5+
6+
private import semmle.code.powershell.typetracking.internal.TypeTrackingImpl as Impl
7+
import Impl::Shared::TypeTracking<Impl::TypeTrackingInput>
Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
import codeql.typetracking.TypeTracking as Shared
2+
import codeql.typetracking.internal.TypeTrackingImpl as SharedImpl
3+
private import powershell
4+
private import semmle.code.powershell.controlflow.Cfg as Cfg
5+
private import Cfg::CfgNodes
6+
private import codeql.typetracking.internal.SummaryTypeTracker as SummaryTypeTracker
7+
private import semmle.code.powershell.dataflow.DataFlow
8+
private import semmle.code.powershell.dataflow.internal.DataFlowImplCommon as DataFlowImplCommon
9+
private import semmle.code.powershell.dataflow.internal.DataFlowPublic as DataFlowPublic
10+
private import semmle.code.powershell.dataflow.internal.DataFlowPrivate as DataFlowPrivate
11+
private import semmle.code.powershell.dataflow.internal.DataFlowDispatch as DataFlowDispatch
12+
private import codeql.util.Unit
13+
14+
pragma[noinline]
15+
private predicate argumentPositionMatch(
16+
DataFlowDispatch::DataFlowCall call, DataFlowPrivate::ArgumentNode arg,
17+
DataFlowDispatch::ParameterPosition ppos
18+
) {
19+
exists(DataFlowDispatch::ArgumentPosition apos |
20+
arg.argumentOf(call, apos) and
21+
DataFlowDispatch::parameterMatch(ppos, apos)
22+
)
23+
}
24+
25+
pragma[noinline]
26+
private predicate viableParam(
27+
DataFlowDispatch::DataFlowCall call, DataFlowPrivate::ParameterNodeImpl p,
28+
DataFlowDispatch::ParameterPosition ppos
29+
) {
30+
exists(DataFlowDispatch::DataFlowCallable callable |
31+
DataFlowDispatch::getTarget(call) = callable.asCfgScope()
32+
|
33+
p.isParameterOf(callable, ppos)
34+
)
35+
}
36+
37+
/** Holds if there is flow from `arg` to `p` via the call `call`. */
38+
pragma[nomagic]
39+
predicate callStep(
40+
DataFlowDispatch::DataFlowCall call, DataFlow::Node arg, DataFlowPrivate::ParameterNodeImpl p
41+
) {
42+
exists(DataFlowDispatch::ParameterPosition pos |
43+
argumentPositionMatch(call, arg, pos) and
44+
viableParam(call, p, pos)
45+
)
46+
}
47+
48+
private module SummaryTypeTrackerInput implements SummaryTypeTracker::Input {
49+
class Node = DataFlow::Node;
50+
51+
class Content = DataFlowPublic::ContentSet;
52+
53+
class ContentFilter = TypeTrackingInput::ContentFilter;
54+
55+
ContentFilter getFilterFromWithoutContentStep(Content content) {
56+
none() // TODO
57+
}
58+
59+
ContentFilter getFilterFromWithContentStep(Content content) {
60+
none() // TODO
61+
}
62+
63+
// Summaries and their stacks
64+
class SummaryComponent extends Unit {
65+
SummaryComponent() { none() }
66+
}
67+
68+
class SummaryComponentStack extends Unit {
69+
SummaryComponent head() { none() }
70+
}
71+
72+
SummaryComponentStack singleton(SummaryComponent component) { none() }
73+
74+
SummaryComponentStack push(SummaryComponent head, SummaryComponentStack tail) { none() }
75+
76+
SummaryComponent return() { none() }
77+
78+
SummaryComponent content(Content contents) { none() }
79+
80+
SummaryComponent withoutContent(Content contents) { none() }
81+
82+
SummaryComponent withContent(Content contents) { none() }
83+
84+
class SummarizedCallable extends Unit {
85+
SummarizedCallable() { none() }
86+
87+
predicate propagatesFlow(
88+
SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
89+
) {
90+
none()
91+
}
92+
}
93+
94+
Node argumentOf(Node call, SummaryComponent arg, boolean isPostUpdate) { none() }
95+
96+
Node parameterOf(Node callable, SummaryComponent param) { none() }
97+
98+
Node returnOf(Node callable, SummaryComponent return) { none() }
99+
100+
Node callTo(SummarizedCallable callable) { none() }
101+
}
102+
103+
private module TypeTrackerSummaryFlow = SummaryTypeTracker::SummaryFlow<SummaryTypeTrackerInput>;
104+
105+
private newtype TContentFilter = MkElementFilter()
106+
107+
module TypeTrackingInput implements Shared::TypeTrackingInput {
108+
class Node = DataFlowPublic::Node;
109+
110+
class LocalSourceNode = DataFlowPublic::LocalSourceNode;
111+
112+
class Content = DataFlowPublic::ContentSet;
113+
114+
/**
115+
* A label to use for `WithContent` and `WithoutContent` steps, restricting
116+
* which `ContentSet` may pass through.
117+
*/
118+
class ContentFilter extends TContentFilter {
119+
/** Gets a string representation of this content filter. */
120+
string toString() { this = MkElementFilter() and result = "elements" }
121+
122+
/** Gets the content of a type-tracker that matches this filter. */
123+
Content getAMatchingContent() {
124+
none() // TODO
125+
}
126+
}
127+
128+
/**
129+
* Holds if a value stored with `storeContents` can be read back with `loadContents`.
130+
*/
131+
pragma[inline]
132+
predicate compatibleContents(Content storeContents, Content loadContents) {
133+
storeContents.getAStoreContent() = loadContents.getAReadContent()
134+
}
135+
136+
/** Holds if there is a simple local flow step from `nodeFrom` to `nodeTo` */
137+
predicate simpleLocalSmallStep = DataFlowPrivate::localFlowStepTypeTracker/2;
138+
139+
/** Holds if there is a level step from `nodeFrom` to `nodeTo`, which does not depend on the call graph. */
140+
pragma[nomagic]
141+
predicate levelStepNoCall(Node nodeFrom, LocalSourceNode nodeTo) {
142+
TypeTrackerSummaryFlow::levelStepNoCall(nodeFrom, nodeTo)
143+
}
144+
145+
/** Holds if there is a level step from `nodeFrom` to `nodeTo`, which may depend on the call graph. */
146+
pragma[nomagic]
147+
predicate levelStepCall(Node nodeFrom, LocalSourceNode nodeTo) { none() }
148+
149+
/**
150+
* Holds if `nodeFrom` steps to `nodeTo` by being passed as a parameter in a call.
151+
*
152+
* Flow into summarized library methods is not included, as that will lead to negative
153+
* recursion (or, at best, terrible performance), since identifying calls to library
154+
* methods is done using API graphs (which uses type tracking).
155+
*/
156+
predicate callStep(Node nodeFrom, LocalSourceNode nodeTo) { callStep(_, nodeFrom, nodeTo) }
157+
158+
/**
159+
* Holds if `nodeFrom` steps to `nodeTo` by being returned from a call.
160+
*/
161+
predicate returnStep(Node nodeFrom, LocalSourceNode nodeTo) {
162+
exists(CallCfgNode call |
163+
nodeFrom instanceof DataFlowPrivate::ReturnNode and
164+
nodeFrom.(DataFlowPrivate::NodeImpl).getCfgScope() =
165+
DataFlowDispatch::getTarget(DataFlowDispatch::TNormalCall(call)) and
166+
nodeTo.asExpr().getAstNode() = call.getAstNode()
167+
)
168+
}
169+
170+
/**
171+
* Holds if `nodeFrom` is being written to the `contents` of the object
172+
* in `nodeTo`.
173+
*
174+
* Note that the choice of `nodeTo` does not have to make sense
175+
* "chronologically". All we care about is whether the `contents` of
176+
* `nodeTo` can have a specific type, and the assumption is that if a specific
177+
* type appears here, then any access of that particular content can yield
178+
* something of that particular type.
179+
*/
180+
predicate storeStep(Node nodeFrom, Node nodeTo, Content contents) {
181+
DataFlowPrivate::storeStep(nodeFrom, contents, nodeTo)
182+
or
183+
TypeTrackerSummaryFlow::basicStoreStep(nodeFrom, nodeTo, contents)
184+
}
185+
186+
/**
187+
* Holds if `nodeTo` is the result of accessing the `content` content of `nodeFrom`.
188+
*/
189+
predicate loadStep(Node nodeFrom, LocalSourceNode nodeTo, Content contents) {
190+
DataFlowPrivate::readStep(nodeFrom, contents, nodeTo)
191+
or
192+
TypeTrackerSummaryFlow::basicLoadStep(nodeFrom, nodeTo, contents)
193+
}
194+
195+
/**
196+
* Holds if the `loadContent` of `nodeFrom` is stored in the `storeContent` of `nodeTo`.
197+
*/
198+
predicate loadStoreStep(Node nodeFrom, Node nodeTo, Content loadContent, Content storeContent) {
199+
TypeTrackerSummaryFlow::basicLoadStoreStep(nodeFrom, nodeTo, loadContent, storeContent)
200+
}
201+
202+
/**
203+
* Same as `withContentStep`, but `nodeTo` has type `Node` instead of `LocalSourceNode`,
204+
* which allows for it by used in the definition of `LocalSourceNode`.
205+
*/
206+
additional predicate withContentStepImpl(Node nodeFrom, Node nodeTo, ContentFilter filter) {
207+
TypeTrackerSummaryFlow::basicWithContentStep(nodeFrom, nodeTo, filter)
208+
}
209+
210+
/**
211+
* Holds if type-tracking should step from `nodeFrom` to `nodeTo` if inside a
212+
* content matched by `filter`.
213+
*/
214+
predicate withContentStep(Node nodeFrom, LocalSourceNode nodeTo, ContentFilter filter) {
215+
withContentStepImpl(nodeFrom, nodeTo, filter)
216+
}
217+
218+
/**
219+
* Same as `withoutContentStep`, but `nodeTo` has type `Node` instead of `LocalSourceNode`,
220+
* which allows for it by used in the definition of `LocalSourceNode`.
221+
*/
222+
additional predicate withoutContentStepImpl(Node nodeFrom, Node nodeTo, ContentFilter filter) {
223+
TypeTrackerSummaryFlow::basicWithoutContentStep(nodeFrom, nodeTo, filter)
224+
}
225+
226+
/**
227+
* Holds if type-tracking should step from `nodeFrom` to `nodeTo` but block
228+
* flow of contents matched by `filter` through here.
229+
*/
230+
predicate withoutContentStep(Node nodeFrom, LocalSourceNode nodeTo, ContentFilter filter) {
231+
withoutContentStepImpl(nodeFrom, nodeTo, filter)
232+
}
233+
234+
/**
235+
* Holds if data can flow from `node1` to `node2` in a way that discards call contexts.
236+
*/
237+
predicate jumpStep(Node nodeFrom, LocalSourceNode nodeTo) {
238+
DataFlowPrivate::jumpStep(nodeFrom, nodeTo)
239+
}
240+
241+
predicate hasFeatureBacktrackStoreTarget() { none() }
242+
}
243+
244+
import SharedImpl::TypeTracking<TypeTrackingInput>

0 commit comments

Comments
 (0)