Skip to content

Commit 27f2d41

Browse files
authored
Merge pull request github#6652 from asgerf/js/type-tracking-through-callback
Approved by erik-krogh
2 parents 649c2ce + db1de18 commit 27f2d41

File tree

7 files changed

+41
-4
lines changed

7 files changed

+41
-4
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -947,7 +947,7 @@ private predicate exploratoryFlowStep(
947947
isAdditionalLoadStoreStep(pred, succ, _, _, cfg) or
948948
// the following three disjuncts taken together over-approximate flow through
949949
// higher-order calls
950-
callback(pred, succ) or
950+
exploratoryCallbackStep(pred, succ) or
951951
succ = pred.(DataFlow::FunctionNode).getAParameter() or
952952
exploratoryBoundInvokeStep(pred, succ)
953953
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ private module NodeTracking {
154154
or
155155
basicLoadStep(mid, nd, _)
156156
or
157-
callback(mid, nd)
157+
exploratoryCallbackStep(mid, nd)
158158
or
159159
nd = mid.(DataFlow::FunctionNode).getAParameter()
160160
)

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

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -434,7 +434,7 @@ private module CachedSteps {
434434
* invocation.
435435
*/
436436
cached
437-
predicate callback(DataFlow::Node arg, DataFlow::SourceNode cb) {
437+
predicate exploratoryCallbackStep(DataFlow::Node arg, DataFlow::SourceNode cb) {
438438
Stages::TypeTracking::ref() and
439439
exists(DataFlow::InvokeNode invk, DataFlow::ParameterNode cbParm, DataFlow::Node cbArg |
440440
arg = invk.getAnArgument() and
@@ -444,12 +444,24 @@ private module CachedSteps {
444444
)
445445
or
446446
exists(DataFlow::ParameterNode cbParm, DataFlow::Node cbArg |
447-
callback(arg, cbParm) and
447+
exploratoryCallbackStep(arg, cbParm) and
448448
callStep(cbArg, cbParm) and
449449
cb.flowsTo(cbArg)
450450
)
451451
}
452452

453+
/** Gets a function that flows to `parameter` via one or more parameter-passing steps. */
454+
cached
455+
DataFlow::FunctionNode getACallbackSource(DataFlow::ParameterNode parameter) {
456+
Stages::TypeTracking::ref() and
457+
callStep(result.getALocalUse(), parameter)
458+
or
459+
exists(DataFlow::ParameterNode mid |
460+
callStep(mid.getALocalUse(), parameter) and
461+
result = getACallbackSource(mid)
462+
)
463+
}
464+
453465
/**
454466
* Holds if `f` may return `base`, which has a write of property `prop` with right-hand side `rhs`.
455467
*/

javascript/ql/lib/semmle/javascript/dataflow/internal/StepSummary.qll

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,13 @@ private module Cached {
156156
succ = fun.getAnInvocation()
157157
)
158158
)
159+
or
160+
// Add 'return' steps from callback arguments to callback parameters
161+
exists(DataFlow::ParameterNode parameter, int i |
162+
pred = parameter.getAnInvocation().getArgument(i) and
163+
succ = getACallbackSource(parameter).getParameter(i) and
164+
summary = ReturnStep()
165+
)
159166
}
160167
}
161168

javascript/ql/test/library-tests/TypeTracking/ClassStyle.expected

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ test_Connection
3838
| tst.js:112:10:112:14 | obj.x |
3939
| tst.js:114:1:114:28 | getX({ ... on() }) |
4040
| tst.js:114:11:114:25 | getConnection() |
41+
| tst.js:118:12:118:26 | getConnection() |
42+
| tst.js:120:21:120:24 | conn |
43+
| tst.js:126:22:126:25 | conn |
4144
| tst_conflict.js:6:38:6:77 | api.cha ... ction() |
4245
test_DataCallback
4346
| client.js:3:28:3:34 | x => {} |

javascript/ql/test/library-tests/TypeTracking/PredicateStyle.expected

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ connection
4040
| type tracker without call steps | tst.js:108:8:108:22 | getConnection() |
4141
| type tracker without call steps | tst.js:114:1:114:28 | getX({ ... on() }) |
4242
| type tracker without call steps | tst.js:114:11:114:25 | getConnection() |
43+
| type tracker without call steps | tst.js:118:12:118:26 | getConnection() |
44+
| type tracker without call steps | tst.js:120:21:120:24 | conn |
45+
| type tracker without call steps | tst.js:126:22:126:25 | conn |
4346
| type tracker without call steps | tst_conflict.js:6:38:6:77 | api.cha ... ction() |
4447
| type tracker without call steps with property MyApplication.namespace.connection | file://:0:0:0:0 | global access path |
4548
| type tracker without call steps with property conflict | tst.js:63:3:63:25 | MyAppli ... mespace |

javascript/ql/test/library-tests/TypeTracking/tst.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,3 +113,15 @@ function getX(obj) {
113113
}
114114
getX({ x: getConnection() });
115115
getX({ x: somethingElse() });
116+
117+
function makeConnectionAsync(callback) {
118+
callback(getConnection());
119+
}
120+
makeConnectionAsync(conn => {});
121+
makeConnectionAsync(); // suppress single-call precision gains
122+
123+
function makeConnectionAsync2(callback) {
124+
makeConnectionAsync(callback);
125+
}
126+
makeConnectionAsync2(conn => {});
127+
makeConnectionAsync2(); // suppress single-call precision gains

0 commit comments

Comments
 (0)