Skip to content

Commit 592ed8a

Browse files
committed
remove ordinary return flow from generator functions
1 parent 74db25d commit 592ed8a

File tree

8 files changed

+38
-6
lines changed

8 files changed

+38
-6
lines changed

javascript/ql/src/semmle/javascript/Functions.qll

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,9 @@ class Function extends @function, Parameterized, TypeParameterized, StmtContaine
204204
/** Holds if this function is an asynchronous function. */
205205
predicate isAsync() { isAsync(this) }
206206

207+
/** Holds if this function is not asynchronous and also not a generator. */
208+
predicate isOrdinary() { not isAsync() and not isGenerator() }
209+
207210
/** Gets the enclosing function or toplevel of this function. */
208211
override StmtContainer getEnclosingContainer() { result = getEnclosingStmt().getContainer() }
209212

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1031,7 +1031,7 @@ private predicate storeStep(
10311031
summary = PathSummary::level()
10321032
or
10331033
exists(Function f, DataFlow::Node mid, DataFlow::Node invk |
1034-
not f.isAsync() and invk = succ
1034+
f.isOrdinary() and invk = succ
10351035
or
10361036
// store in an immediately awaited function call
10371037
f.isAsync() and
@@ -1156,7 +1156,7 @@ private predicate loadStep(
11561156
summary = PathSummary::level()
11571157
or
11581158
exists(Function f, DataFlow::Node read, DataFlow::Node invk |
1159-
not f.isAsync() and invk = succ
1159+
f.isOrdinary() and invk = succ
11601160
or
11611161
// load from an immediately awaited function call
11621162
f.isAsync() and

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1495,7 +1495,7 @@ module DataFlow {
14951495
)
14961496
or
14971497
// from returned expr to the FunctionReturnNode.
1498-
exists(Function f | not f.isAsync() |
1498+
exists(Function f | f.isOrdinary() |
14991499
DataFlow::functionReturnNode(succ, f) and pred = valueNode(f.getAReturnedExpr())
15001500
)
15011501
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ private module NodeTracking {
243243
basicStoreStep(pred, succ, prop) and
244244
summary = PathSummary::level()
245245
or
246-
exists(Function f, DataFlow::Node mid | not f.isAsync() |
246+
exists(Function f, DataFlow::Node mid | f.isOrdinary() |
247247
// `f` stores its parameter `pred` in property `prop` of a value that flows back to the caller,
248248
// and `succ` is an invocation of `f`
249249
reachableFromInput(f, succ, pred, mid, summary) and
@@ -266,7 +266,7 @@ private module NodeTracking {
266266
basicLoadStep(pred, succ, prop) and
267267
summary = PathSummary::level()
268268
or
269-
exists(Function f, DataFlow::SourceNode parm | not f.isAsync() |
269+
exists(Function f, DataFlow::SourceNode parm | f.isOrdinary() |
270270
argumentPassing(succ, pred, f, parm) and
271271
reachesReturn(f, parm.getAPropertyRead(prop), summary)
272272
)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ predicate localExceptionStep(DataFlow::Node pred, DataFlow::Node succ) {
7171
*/
7272
predicate localExceptionStepWithAsyncFlag(DataFlow::Node pred, DataFlow::Node succ, boolean async) {
7373
exists(DataFlow::Node target | target = getThrowTarget(pred) |
74-
async = false and
74+
async = false and // this also covers generators - as the behavior of exceptions is close enough to the behavior of ordinary functions when it comes to exceptions (assuming that the iterator does not cross function boundaries).
7575
succ = target and
7676
not succ = any(DataFlow::FunctionNode f | f.getFunction().isAsync()).getExceptionalReturn()
7777
or

javascript/ql/test/library-tests/Generators/DataFlow.expected

Whitespace-only changes.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import javascript
2+
import testUtilities.ConsistencyChecking
3+
4+
class GeneratorFlowConfig extends DataFlow::Configuration {
5+
GeneratorFlowConfig() { this = "GeneratorFlowConfig" }
6+
7+
override predicate isSource(DataFlow::Node source) { source.asExpr().getStringValue() = "source" }
8+
9+
override predicate isSink(DataFlow::Node sink) {
10+
sink = any(DataFlow::CallNode call | call.getCalleeName() = "sink").getAnArgument()
11+
}
12+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
(function () {
2+
var source = "source";
3+
sink(source); // NOT OK
4+
5+
function *gen1() {
6+
yield source;
7+
}
8+
for (const x of gen1()) {
9+
sink(x); // NOT OK - but not found yet [INCONSISTENCY]
10+
}
11+
12+
function *gen2() {
13+
yield "safe";
14+
return source;
15+
}
16+
sink(gen2()); // OK
17+
});

0 commit comments

Comments
 (0)