Skip to content

Commit 465be47

Browse files
author
Max Schaefer
committed
JavaScript: Only follow level flow steps when summarising functions.
It is not only wasteful to consider paths with unmatched calls/returns, but also wrong; see test case in next commit.
1 parent 455dbcc commit 465be47

File tree

3 files changed

+14
-6
lines changed

3 files changed

+14
-6
lines changed

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -557,7 +557,8 @@ private predicate callInputStep(
557557
/**
558558
* Holds if `input`, which is either an argument to `f` at `invk` or a definition
559559
* that is captured by `f`, may flow to `nd` under configuration `cfg` (possibly through
560-
* callees) along a path summarized by `summary`.
560+
* callees, but not containing any unmatched calls or returns) along a path summarized by
561+
* `summary`.
561562
*
562563
* Note that the summary does not take the initial step from argument to parameter
563564
* into account.
@@ -577,7 +578,7 @@ private predicate reachableFromInput(
577578
}
578579

579580
/**
580-
* Holds if there is a step from `pred` to `succ` under `cfg` that can be appended
581+
* Holds if there is a level step from `pred` to `succ` under `cfg` that can be appended
581582
* to a path represented by `oldSummary` yielding a path represented by `newSummary`.
582583
*/
583584
pragma[noinline]
@@ -587,6 +588,7 @@ private predicate appendStep(
587588
) {
588589
exists(PathSummary stepSummary |
589590
flowStep(pred, cfg, succ, stepSummary) and
591+
stepSummary.isLevel() and
590592
newSummary = oldSummary.append(stepSummary)
591593
)
592594
}

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,8 @@ private module NodeTracking {
131131

132132
/**
133133
* Holds if `input`, which is either an argument to `f` at `invk` or a definition
134-
* that is captured by `f`, may flow to `nd` (possibly through callees) along
135-
* a path summarized by `summary`.
134+
* that is captured by `f`, may flow to `nd` (possibly through callees, but not containing
135+
* any unmatched calls or returns) along a path summarized by `summary`.
136136
*/
137137
private predicate reachableFromInput(
138138
Function f, DataFlow::Node invk, DataFlow::Node input, DataFlow::Node nd, PathSummary summary
@@ -143,6 +143,7 @@ private module NodeTracking {
143143
exists(DataFlow::Node mid, PathSummary oldSummary, PathSummary newSummary |
144144
reachableFromInput(f, invk, input, mid, oldSummary) and
145145
flowStep(mid, nd, newSummary) and
146+
newSummary.isLevel() and
146147
summary = oldSummary.append(newSummary)
147148
)
148149
}

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -340,12 +340,17 @@ class PathSummary extends TPathSummary {
340340

341341
PathSummary() { this = MkPathSummary(hasReturn, hasCall, start, end) }
342342

343-
/** Indicates whether the path represented by this summary contains any return steps. */
343+
/** Indicates whether the path represented by this summary contains any unmatched return steps. */
344344
boolean hasReturn() { result = hasReturn }
345345

346-
/** Indicates whether the path represented by this summary contains any call steps. */
346+
/** Indicates whether the path represented by this summary contains any unmatched call steps. */
347347
boolean hasCall() { result = hasCall }
348348

349+
/** Holds if the path represented by this summary contains no unmatched call or return steps. */
350+
predicate isLevel() {
351+
hasReturn = false and hasCall = false
352+
}
353+
349354
/** Gets the flow label describing the value at the start of this flow path. */
350355
FlowLabel getStartLabel() { result = start }
351356

0 commit comments

Comments
 (0)