Skip to content

Commit a9861e1

Browse files
committed
PS: Add a library that calculated escaping values much more efficiently using the forward/reverse pruning technique.
1 parent f85767f commit a9861e1

File tree

2 files changed

+199
-84
lines changed

2 files changed

+199
-84
lines changed
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
private import semmle.code.powershell.controlflow.CfgNodes
2+
3+
/**
4+
* The input module which defines the set of sources for which to calculate
5+
* "escaping expressions".
6+
*/
7+
signature module InputSig {
8+
/**
9+
* Holds if `source` is a relevant AST element that we want to compute
10+
* which expressions are returned from.
11+
*/
12+
predicate isSource(AstCfgNode source);
13+
}
14+
15+
/** The output signature from the "escape analysis". */
16+
signature module OutputSig<InputSig Input> {
17+
/** Gets an expression that escapes from `source` */
18+
ExprCfgNode getAReturn(AstCfgNode source);
19+
20+
/**
21+
* Gets the `i`'th expression that escapes from `source`, if an ordering can
22+
* be determined statically.
23+
*/
24+
ExprCfgNode getReturn(AstCfgNode source, int i);
25+
26+
/** Holds multiple value may escape from `source`. */
27+
predicate mayReturnMultipleValues(AstCfgNode source);
28+
29+
/**
30+
* Holds if each value escaping from `source` is guarenteed to only escape
31+
* once. In particular, if `count(getAReturn(source)) = 1` and this predicate
32+
* holds, then only one value can escape from `source`.
33+
*
34+
* If `count(getAReturn(source)) > 1` and this predicate holds,
35+
* it means that a sequence of values may escape from `source`.
36+
*/
37+
predicate eachValueIsReturnedOnce(AstCfgNode source);
38+
}
39+
40+
module Make<InputSig Input> implements OutputSig<Input> {
41+
private import Input
42+
43+
private predicate step0(AstCfgNode pred, AstCfgNode succ) {
44+
exists(NamedBlockCfgNode nb |
45+
pred = nb and
46+
succ = nb.getAStmt()
47+
)
48+
or
49+
exists(StmtNodes::StmtBlockCfgNode sb |
50+
pred = sb and
51+
succ = sb.getAStmt()
52+
)
53+
or
54+
exists(StmtNodes::ExprStmtCfgNode es |
55+
pred = es and
56+
succ = es.getExpr()
57+
)
58+
or
59+
exists(StmtNodes::ReturnStmtCfgNode es |
60+
pred = es and
61+
succ = es.getPipeline()
62+
)
63+
or
64+
exists(ExprNodes::ArrayLiteralCfgNode al |
65+
pred = al and
66+
succ = al.getAnExpr()
67+
)
68+
or
69+
exists(StmtNodes::LoopStmtCfgNode loop |
70+
pred = loop and
71+
succ = loop.getBody()
72+
)
73+
or
74+
exists(ExprNodes::IfCfgNode if_ |
75+
pred = if_ and
76+
succ = if_.getABranch()
77+
)
78+
or
79+
exists(StmtNodes::SwitchStmtCfgNode switch |
80+
pred = switch and
81+
succ = switch.getACase()
82+
)
83+
or
84+
exists(CatchClauseCfgNode catch |
85+
pred = catch and
86+
succ = catch.getBody()
87+
)
88+
or
89+
exists(StmtNodes::TryStmtCfgNode try |
90+
pred = try and
91+
succ = [try.getBody(), try.getFinally()]
92+
)
93+
}
94+
95+
private predicate fwd(AstCfgNode n) {
96+
isSource(n)
97+
or
98+
exists(AstCfgNode pred |
99+
fwd(pred) and
100+
step0(pred, n)
101+
)
102+
}
103+
104+
private predicate isSink(AstCfgNode sink) {
105+
fwd(sink) and
106+
(
107+
sink instanceof ExprCfgNode and
108+
// If is not really an expression
109+
not sink instanceof ExprNodes::IfCfgNode and
110+
// When `a, b, c` is returned it is flattened to returning a, and b, and c.
111+
not sink instanceof ExprNodes::ArrayLiteralCfgNode
112+
)
113+
}
114+
115+
private predicate rev(AstCfgNode n) {
116+
fwd(n) and
117+
(
118+
isSink(n)
119+
or
120+
exists(AstCfgNode succ |
121+
rev(succ) and
122+
step0(n, succ)
123+
)
124+
)
125+
}
126+
127+
private predicate step(AstCfgNode n1, AstCfgNode n2) {
128+
rev(n1) and
129+
rev(n2) and
130+
step0(n1, n2)
131+
}
132+
133+
private predicate stepPlus(AstCfgNode n1, AstCfgNode n2) =
134+
doublyBoundedFastTC(step/2, isSource/1, isSink/1)(n1, n2)
135+
136+
/** Gets a value that may be returned from `source`. */
137+
private ExprCfgNode getAReturn0(AstCfgNode source) {
138+
isSource(source) and
139+
isSink(result) and
140+
stepPlus(source, result)
141+
}
142+
143+
private predicate inScopeOfSource(AstCfgNode n, AstCfgNode source) {
144+
isSource(source) and
145+
n.getAstNode().getParent*() = source.getAstNode()
146+
}
147+
148+
private predicate getASuccessor(AstCfgNode pred, AstCfgNode succ) {
149+
exists(AstCfgNode source |
150+
inScopeOfSource(pred, source) and
151+
pred.getASuccessor() = succ and
152+
inScopeOfSource(succ, source)
153+
)
154+
}
155+
156+
/** Holds if `e` may be returned multiple times from `source`. */
157+
private predicate mayBeReturnedMoreThanOnce(ExprCfgNode e, AstCfgNode source) {
158+
e = getAReturn0(source) and getASuccessor+(e, e)
159+
}
160+
161+
predicate eachValueIsReturnedOnce(AstCfgNode source) {
162+
isSource(source) and
163+
not mayBeReturnedMoreThanOnce(_, source)
164+
}
165+
166+
private predicate isSourceForSingularReturn(AstCfgNode source) {
167+
isSource(source) and
168+
eachValueIsReturnedOnce(source)
169+
}
170+
171+
private predicate hasReturnOrderImpl0(int dist, ExprCfgNode e, AstCfgNode source) =
172+
shortestDistances(isSourceForSingularReturn/1, getASuccessor/2)(source, e, dist)
173+
174+
private predicate hasReturnOrderImpl(int dist, ExprCfgNode e) {
175+
hasReturnOrderImpl0(dist, e, _) and
176+
e = getAReturn0(_)
177+
}
178+
179+
private predicate hasReturnOrder(int i, ExprCfgNode e) {
180+
e = rank[i + 1](ExprCfgNode e0, int i0 | hasReturnOrderImpl(i0, e0) | e0 order by i0)
181+
}
182+
183+
ExprCfgNode getReturn(AstCfgNode source, int i) {
184+
result = getAReturn0(source) and
185+
eachValueIsReturnedOnce(source) and
186+
hasReturnOrder(i, result)
187+
}
188+
189+
ExprCfgNode getAReturn(AstCfgNode source) { result = getAReturn0(source) }
190+
191+
/**
192+
* Holds if `source` may return multiple values, and `n` is one of the values.
193+
*/
194+
predicate mayReturnMultipleValues(AstCfgNode source) {
195+
strictcount(getAReturn0(source)) > 1
196+
or
197+
mayBeReturnedMoreThanOnce(_, source)
198+
}
199+
}

powershell/ql/lib/semmle/code/powershell/internal/AstEscape.qll

Lines changed: 0 additions & 84 deletions
This file was deleted.

0 commit comments

Comments
 (0)