Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 15f0e85

Browse files
committed
JS: Restructure call graph computation
1 parent c5f29e0 commit 15f0e85

3 files changed

Lines changed: 98 additions & 107 deletions

File tree

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

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,14 +117,58 @@ module DataFlow {
117117
int getIntValue() { result = asExpr().getIntValue() }
118118

119119
/** Gets a function value that may reach this node. */
120-
FunctionNode getAFunctionValue() {
121-
result.getAstNode() = analyze().getAValue().(AbstractCallable).getFunction()
120+
final FunctionNode getAFunctionValue() {
121+
result = getAFunctionValue(_)
122+
}
123+
124+
/**
125+
* Gets a function value that may reach this node.
126+
*
127+
* This predicate may be overridden to customize the call graph.
128+
*
129+
* Note that any additional values added by overiding predicates will not
130+
* be propagated along data flow edges.
131+
*
132+
* `imprecision` is a heuristic measure of how likely it is that `callee`
133+
* is only suggested as a potential callee due to
134+
* imprecise analysis of global variables and is not, in fact, a viable callee at all.
135+
*/
136+
cached
137+
FunctionNode getAFunctionValue(int imprecision) {
138+
exists(Function fun |
139+
result = fun.flow() and
140+
fun = analyze().getAValue().(AbstractCallable).getFunction()
141+
|
142+
if analyze().getAValue().isIndefinite("global") then
143+
fun.getFile() = getFile() and imprecision = 0
144+
or
145+
fun.inExternsFile() and imprecision = 1
146+
or
147+
imprecision = 2
148+
else
149+
imprecision = 0
150+
)
122151
or
152+
imprecision = 0 and
123153
exists(string name |
124154
GlobalAccessPath::isAssignedInUniqueFile(name) and
125155
GlobalAccessPath::fromRhs(result) = name and
126156
GlobalAccessPath::fromReference(this) = name
127157
)
158+
or
159+
imprecision = 0 and
160+
exists(DataFlow::ClassNode cls |
161+
exists(string name |
162+
cls.getAnInstanceMemberAccess(name).flowsTo(this) and
163+
result = cls.getInstanceMethod(name)
164+
or
165+
cls.getAStaticMemberAccess(name).flowsTo(this) and
166+
result = cls.getStaticMethod(name)
167+
)
168+
or
169+
cls.getAClassReference().flowsTo(this) and
170+
result = cls.getConstructor()
171+
)
128172
}
129173

130174
/**

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

Lines changed: 50 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -104,15 +104,15 @@ class InvokeNode extends DataFlow::SourceNode {
104104
}
105105

106106
/** Gets an abstract value representing possible callees of this call site. */
107-
AbstractValue getACalleeValue() { result = InvokeNode::getACalleeValue(this) }
107+
final AbstractValue getACalleeValue() { result = getCalleeNode().analyze().getAValue() }
108108

109109
/**
110110
* Gets a potential callee of this call site.
111111
*
112112
* To alter the call graph as seen by the interprocedural data flow libraries, override
113113
* the `getACallee(int imprecision)` predicate instead.
114114
*/
115-
Function getACallee() { result = InvokeNode::getACallee(this) }
115+
final Function getACallee() { result = getACallee(_) }
116116

117117
/**
118118
* Gets a callee of this call site where `imprecision` is a heuristic measure of how
@@ -125,7 +125,20 @@ class InvokeNode extends DataFlow::SourceNode {
125125
* This predicate can be overridden to alter the call graph used by the interprocedural
126126
* data flow libraries.
127127
*/
128-
Function getACallee(int imprecision) { result = InvokeNode::getACallee(this, imprecision) }
128+
cached
129+
Function getACallee(int imprecision) {
130+
result.flow() = getCalleeNode().getAFunctionValue(imprecision)
131+
or
132+
imprecision = 0 and
133+
exists(InvokeExpr expr | expr = this.(DataFlow::Impl::ExplicitInvokeNode).asExpr() |
134+
result = expr.getResolvedCallee()
135+
or
136+
exists(DataFlow::ClassNode cls |
137+
expr.(SuperCall).getBinder() = cls.getAnInstanceMethodOrConstructor().getFunction() and
138+
result = cls.getADirectSuperClass().getConstructor().getFunction()
139+
)
140+
)
141+
}
129142

130143
/**
131144
* Holds if the approximation of possible callees for this call site is
@@ -173,60 +186,6 @@ class InvokeNode extends DataFlow::SourceNode {
173186
}
174187
}
175188

176-
/** Auxiliary module used to cache a few related predicates together. */
177-
cached
178-
private module InvokeNode {
179-
/** Gets an abstract value representing possible callees of `invk`. */
180-
cached
181-
AbstractValue getACalleeValue(InvokeNode invk) {
182-
result = invk.getCalleeNode().analyze().getAValue()
183-
}
184-
185-
/** Gets a potential callee of `invk` based on dataflow analysis results. */
186-
private Function getACalleeFromDataflow(InvokeNode invk) {
187-
result = getACalleeValue(invk).(AbstractCallable).getFunction()
188-
}
189-
190-
/** Gets a potential callee of `invk`. */
191-
cached
192-
Function getACallee(InvokeNode invk) {
193-
result = getACalleeFromDataflow(invk)
194-
or
195-
not exists(getACalleeFromDataflow(invk)) and
196-
result = invk.(DataFlow::Impl::ExplicitInvokeNode).asExpr().(InvokeExpr).getResolvedCallee()
197-
}
198-
199-
/**
200-
* Gets a callee of `invk` where `imprecision` is a heuristic measure of how
201-
* likely it is that `callee` is only suggested as a potential callee due to
202-
* imprecise analysis of global variables and is not, in fact, a viable callee at all.
203-
*
204-
* Callees with imprecision zero, in particular, have either been derived without
205-
* considering global variables, or are calls to a global variable within the same file.
206-
*/
207-
cached
208-
Function getACallee(InvokeNode invk, int imprecision) {
209-
result = getACallee(invk) and
210-
(
211-
// if global flow was used to derive the callee, we may be imprecise
212-
if invk.isIndefinite("global")
213-
then
214-
// callees within the same file are probably genuine
215-
result.getFile() = invk.getFile() and imprecision = 0
216-
or
217-
// calls to global functions declared in an externs file are fairly
218-
// safe as well
219-
result.inExternsFile() and imprecision = 1
220-
or
221-
// otherwise we make worst-case assumptions
222-
imprecision = 2
223-
else
224-
// no global flow, so no imprecision
225-
imprecision = 0
226-
)
227-
}
228-
}
229-
230189
/** A data flow node corresponding to a function call without `new`. */
231190
class CallNode extends InvokeNode {
232191
override DataFlow::Impl::CallNodeDef impl;
@@ -657,6 +616,8 @@ class ClassNode extends DataFlow::SourceNode {
657616

658617
/**
659618
* Gets a direct super class of this class.
619+
*
620+
* This predicate can be overridden to customize the class hierarchy.
660621
*/
661622
ClassNode getADirectSuperClass() { result.getAClassReference().flowsTo(getASuperClassNode()) }
662623

@@ -686,8 +647,10 @@ class ClassNode extends DataFlow::SourceNode {
686647

687648
/**
688649
* Gets a dataflow node that refers to this class object.
650+
*
651+
* This predicate can be overridden to customize the tracking of class objects.
689652
*/
690-
private DataFlow::SourceNode getAClassReference(DataFlow::TypeTracker t) {
653+
DataFlow::SourceNode getAClassReference(DataFlow::TypeTracker t) {
691654
t.start() and
692655
result.(AnalyzedNode).getAValue() = getAbstractClassValue()
693656
or
@@ -698,14 +661,16 @@ class ClassNode extends DataFlow::SourceNode {
698661
* Gets a dataflow node that refers to this class object.
699662
*/
700663
cached
701-
DataFlow::SourceNode getAClassReference() {
664+
final DataFlow::SourceNode getAClassReference() {
702665
result = getAClassReference(DataFlow::TypeTracker::end())
703666
}
704667

705668
/**
706669
* Gets a dataflow node that refers to an instance of this class.
670+
*
671+
* This predicate can be overridden to customize the tracking of class instances.
707672
*/
708-
private DataFlow::SourceNode getAnInstanceReference(DataFlow::TypeTracker t) {
673+
DataFlow::SourceNode getAnInstanceReference(DataFlow::TypeTracker t) {
709674
result = getAClassReference(t.continue()).getAnInstantiation()
710675
or
711676
t.start() and
@@ -740,10 +705,34 @@ class ClassNode extends DataFlow::SourceNode {
740705
* Gets a dataflow node that refers to an instance of this class.
741706
*/
742707
cached
743-
DataFlow::SourceNode getAnInstanceReference() {
708+
final DataFlow::SourceNode getAnInstanceReference() {
744709
result = getAnInstanceReference(DataFlow::TypeTracker::end())
745710
}
746711

712+
/**
713+
* Gets a property read that accesses the property `name` on an instance of this class.
714+
*
715+
* Concretely, this holds when the base is an instance of this class or a subclass thereof.
716+
*
717+
* This predicate may be overridden to customize the class hierarchy analysis.
718+
*/
719+
DataFlow::PropRead getAnInstanceMemberAccess(string name) {
720+
result = getAnInstanceReference().getAPropertyRead(name)
721+
or
722+
exists(DataFlow::ClassNode subclass |
723+
result = subclass.getAnInstanceMemberAccess(name) and
724+
not exists(subclass.getAnInstanceMember(name)) and
725+
subclass = getADirectSubClass()
726+
)
727+
}
728+
729+
/**
730+
* Gets an access to a static member of this class.
731+
*/
732+
DataFlow::PropRead getAStaticMemberAccess(string name) {
733+
result = getAClassReference().getAPropertyRead(name)
734+
}
735+
747736
/**
748737
* Holds if this class is exposed in the global scope through the given qualified name.
749738
*/

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

Lines changed: 2 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -97,54 +97,11 @@ private module CachedSteps {
9797
f = cap.getContainer()
9898
}
9999

100-
/**
101-
* Holds if the method invoked by `invoke` resolved to a member named `name` in `cls`
102-
* or one of its super classes.
103-
*/
104-
cached
105-
predicate callResolvesToMember(DataFlow::InvokeNode invoke, DataFlow::ClassNode cls, string name) {
106-
invoke = cls.getAnInstanceReference().getAMethodCall(name)
107-
or
108-
exists(DataFlow::ClassNode subclass |
109-
callResolvesToMember(invoke, subclass, name) and
110-
not exists(subclass.getAnInstanceMember(name)) and
111-
cls = subclass.getADirectSuperClass()
112-
)
113-
}
114-
115100
/**
116101
* Holds if `invk` may invoke `f`.
117102
*/
118103
cached
119-
predicate calls(DataFlow::InvokeNode invk, Function f) {
120-
f = invk.getACallee(0)
121-
or
122-
exists(DataFlow::ClassNode cls |
123-
// Call to class member
124-
exists(string name |
125-
callResolvesToMember(invk, cls, name) and
126-
f = cls.getInstanceMethod(name).getFunction()
127-
or
128-
invk = cls.getAClassReference().getAMethodCall(name) and
129-
f = cls.getStaticMethod(name).getFunction()
130-
)
131-
or
132-
// Call to constructor
133-
invk = cls.getAClassReference().getAnInvocation() and
134-
f = cls.getConstructor().getFunction()
135-
or
136-
// Super call to constructor
137-
invk.asExpr().(SuperCall).getBinder() = cls.getConstructor().getFunction() and
138-
f = cls.getADirectSuperClass().getConstructor().getFunction()
139-
)
140-
or
141-
// Call from `foo.bar.baz()` to `foo.bar.baz = function()`
142-
exists(string name |
143-
GlobalAccessPath::isAssignedInUniqueFile(name) and
144-
GlobalAccessPath::fromRhs(f.flow()) = name and
145-
GlobalAccessPath::fromReference(invk.getCalleeNode()) = name
146-
)
147-
}
104+
predicate calls(DataFlow::InvokeNode invk, Function f) { f = invk.getACallee(0) }
148105

149106
/**
150107
* Holds if `invk` may invoke `f` indirectly through the given `callback` argument.
@@ -525,3 +482,4 @@ module PathSummary {
525482
*/
526483
PathSummary return() { exists(FlowLabel lbl | result = MkPathSummary(true, false, lbl, lbl)) }
527484
}
485+

0 commit comments

Comments
 (0)