Skip to content

Commit 8951eae

Browse files
author
Max Schaefer
committed
JavaScript: Improve caching of getACallee and related predicates.
1 parent 627583f commit 8951eae

File tree

3 files changed

+74
-47
lines changed

3 files changed

+74
-47
lines changed

javascript/ql/src/LanguageFeatures/InconsistentNew.ql

Lines changed: 7 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -28,41 +28,20 @@ predicate guardsAgainstMissingNew(Function f) {
2828
)
2929
}
3030

31-
/**
32-
* Holds if `callee` is a function that may be invoked at callsite `cs`,
33-
* where `imprecision` is a heuristic measure of how likely it is that `callee`
34-
* is only suggested as a potential callee due to imprecise analysis of global
35-
* variables and is not, in fact, a viable callee at all.
36-
*/
37-
predicate calls(DataFlow::InvokeNode cs, Function callee, int imprecision) {
38-
callee = cs.getACallee() and
39-
(
40-
// if global flow was used to derive the callee, we may be imprecise
41-
if cs.isIndefinite("global")
42-
then
43-
// callees within the same file are probably genuine
44-
callee.getFile() = cs.getFile() and imprecision = 0
45-
or
46-
// calls to global functions declared in an externs file are fairly
47-
// safe as well
48-
callee.inExternsFile() and imprecision = 1
49-
or
50-
// otherwise we make worst-case assumptions
51-
imprecision = 2
52-
else
53-
// no global flow, so no imprecision
54-
imprecision = 0
55-
)
56-
}
57-
5831
/**
5932
* Gets a function that may be invoked at `cs`, preferring callees that
6033
* are less likely to be derived due to analysis imprecision and excluding
6134
* whitelisted call sites and callees. Additionally, `isNew` is bound to
6235
* `true` if `cs` is a `new` expression, and to `false` otherwise.
6336
*/
6437
Function getALikelyCallee(DataFlow::InvokeNode cs, boolean isNew) {
65-
calls(cs, result, min(int p | calls(cs, _, p))) and
38+
result = min(Function callee, int imprecision |
39+
callee = cs.getACallee(imprecision)
40+
|
41+
callee
42+
order by
43+
imprecision
44+
) and
6645
not cs.isUncertain() and
6746
not whitelistedCall(cs) and
6847
not whitelistedCallee(result) and

javascript/ql/src/semmle/javascript/dataflow/Nodes.qll

Lines changed: 66 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -81,21 +81,20 @@ class InvokeNode extends DataFlow::DefaultSourceNode {
8181
}
8282

8383
/** Gets an abstract value representing possible callees of this call site. */
84-
cached
85-
AbstractValue getACalleeValue() { result = impl.getCalleeNode().analyze().getAValue() }
86-
87-
/** Gets a potential callee based on dataflow analysis results. */
88-
private Function getACalleeFromDataflow() {
89-
result = getACalleeValue().(AbstractCallable).getFunction()
90-
}
84+
AbstractValue getACalleeValue() { result = InvokeNode::getACalleeValue(this) }
9185

9286
/** Gets a potential callee of this call site. */
93-
Function getACallee() {
94-
result = getACalleeFromDataflow()
95-
or
96-
not exists(getACalleeFromDataflow()) and
97-
result = impl.(DataFlow::Impl::ExplicitInvokeNode).asExpr().(InvokeExpr).getResolvedCallee()
98-
}
87+
Function getACallee() { result = InvokeNode::getACallee(this) }
88+
89+
/**
90+
* Gets a callee of this call site where `imprecision` is a heuristic measure of how
91+
* likely it is that `callee` is only suggested as a potential callee due to
92+
* imprecise analysis of global variables and is not, in fact, a viable callee at all.
93+
*
94+
* Callees with imprecision zero, in particular, have either been derived without
95+
* considering global variables, or are calls to a global variable within the same file.
96+
*/
97+
Function getACallee(int imprecision) { result = InvokeNode::getACallee(this, imprecision) }
9998

10099
/**
101100
* Holds if the approximation of possible callees for this call site is
@@ -136,6 +135,60 @@ class InvokeNode extends DataFlow::DefaultSourceNode {
136135
predicate isUncertain() { isImprecise() or isIncomplete() }
137136
}
138137

138+
/** Auxiliary module used to cache a few related predicates together. */
139+
cached
140+
private module InvokeNode {
141+
/** Gets an abstract value representing possible callees of `invk`. */
142+
cached
143+
AbstractValue getACalleeValue(InvokeNode invk) {
144+
result = invk.getCalleeNode().analyze().getAValue()
145+
}
146+
147+
/** Gets a potential callee of `invk` based on dataflow analysis results. */
148+
private Function getACalleeFromDataflow(InvokeNode invk) {
149+
result = getACalleeValue(invk).(AbstractCallable).getFunction()
150+
}
151+
152+
/** Gets a potential callee of `invk`. */
153+
cached
154+
Function getACallee(InvokeNode invk) {
155+
result = getACalleeFromDataflow(invk)
156+
or
157+
not exists(getACalleeFromDataflow(invk)) and
158+
result = invk.(DataFlow::Impl::ExplicitInvokeNode).asExpr().(InvokeExpr).getResolvedCallee()
159+
}
160+
161+
/**
162+
* Gets a callee of `invk` where `imprecision` is a heuristic measure of how
163+
* likely it is that `callee` is only suggested as a potential callee due to
164+
* imprecise analysis of global variables and is not, in fact, a viable callee at all.
165+
*
166+
* Callees with imprecision zero, in particular, have either been derived without
167+
* considering global variables, or are calls to a global variable within the same file.
168+
*/
169+
cached
170+
Function getACallee(InvokeNode invk, int imprecision) {
171+
result = getACallee(invk) and
172+
(
173+
// if global flow was used to derive the callee, we may be imprecise
174+
if invk.isIndefinite("global")
175+
then
176+
// callees within the same file are probably genuine
177+
result.getFile() = invk.getFile() and imprecision = 0
178+
or
179+
// calls to global functions declared in an externs file are fairly
180+
// safe as well
181+
result.inExternsFile() and imprecision = 1
182+
or
183+
// otherwise we make worst-case assumptions
184+
imprecision = 2
185+
else
186+
// no global flow, so no imprecision
187+
imprecision = 0
188+
)
189+
}
190+
}
191+
139192
/** A data flow node corresponding to a function call without `new`. */
140193
class CallNode extends InvokeNode {
141194
override DataFlow::Impl::CallNodeDef impl;

javascript/ql/src/semmle/javascript/dataflow/internal/FlowSteps.qll

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,7 @@ predicate shouldTrackProperties(AbstractValue obj) {
2020
/**
2121
* Holds if `invk` may invoke `f`.
2222
*/
23-
predicate calls(DataFlow::InvokeNode invk, Function f) {
24-
if invk.isIndefinite("global")
25-
then (
26-
f = invk.getACallee() and f.getFile() = invk.getFile()
27-
) else f = invk.getACallee()
28-
}
23+
predicate calls(DataFlow::InvokeNode invk, Function f) { f = invk.getACallee(0) }
2924

3025
/**
3126
* Holds if `invk` may invoke `f` indirectly through the given `callback` argument.

0 commit comments

Comments
 (0)