Skip to content

Commit 948d21c

Browse files
committed
JS: Propagate exceptions from summarized callables by default
1 parent dcdb2e5 commit 948d21c

File tree

3 files changed

+43
-2
lines changed

3 files changed

+43
-2
lines changed

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,9 @@ private module Cached {
100100
// So it doesn't cause negative recursion but it might look a bit surprising.
101101
FlowSummaryPrivate::Steps::summaryStoreStep(sn, MkAwaited(), _)
102102
} or
103+
TFlowSummaryDefaultExceptionalReturn(FlowSummaryImpl::Public::SummarizedCallable callable) {
104+
not DataFlowPrivate::mentionsExceptionalReturn(callable)
105+
} or
103106
TSynthCaptureNode(VariableCapture::VariableCaptureOutput::SynthesizedCaptureNode node) or
104107
TGenericSynthesizedNode(AstNode node, string tag, DataFlowPrivate::DataFlowCallable container) {
105108
any(AdditionalFlowInternal flow).needsSynthesizedNode(node, tag, container)

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

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,33 @@ class FlowSummaryIntermediateAwaitStoreNode extends DataFlow::Node,
112112
}
113113
}
114114

115+
predicate mentionsExceptionalReturn(FlowSummaryImpl::Public::SummarizedCallable callable) {
116+
exists(FlowSummaryImpl::Private::SummaryNode node | node.getSummarizedCallable() = callable |
117+
FlowSummaryImpl::Private::summaryReturnNode(node, MkExceptionalReturnKind())
118+
or
119+
FlowSummaryImpl::Private::summaryOutNode(_, node, MkExceptionalReturnKind())
120+
)
121+
}
122+
123+
/**
124+
* Exceptional return node in a summarized callable whose summary does not mention `ReturnValue[exception]`.
125+
*
126+
* By default, every call inside such a callable will forward their exceptional return to the caller's
127+
* exceptional return, i.e. exceptions are not caught.
128+
*/
129+
class FlowSummaryDefaultExceptionalReturn extends DataFlow::Node,
130+
TFlowSummaryDefaultExceptionalReturn
131+
{
132+
private FlowSummaryImpl::Public::SummarizedCallable callable;
133+
134+
FlowSummaryDefaultExceptionalReturn() { this = TFlowSummaryDefaultExceptionalReturn(callable) }
135+
136+
FlowSummaryImpl::Public::SummarizedCallable getSummarizedCallable() { result = callable }
137+
138+
cached
139+
override string toString() { result = "[default exceptional return] " + callable }
140+
}
141+
115142
class CaptureNode extends DataFlow::Node, TSynthCaptureNode {
116143
/** Gets the underlying node from the variable-capture library. */
117144
VariableCaptureOutput::SynthesizedCaptureNode getNode() {
@@ -296,6 +323,9 @@ private predicate returnNodeImpl(DataFlow::Node node, ReturnKind kind) {
296323
)
297324
or
298325
FlowSummaryImpl::Private::summaryReturnNode(node.(FlowSummaryNode).getSummaryNode(), kind)
326+
or
327+
node instanceof FlowSummaryDefaultExceptionalReturn and
328+
kind = MkExceptionalReturnKind()
299329
}
300330

301331
private DataFlow::Node getAnOutNodeImpl(DataFlowCall call, ReturnKind kind) {
@@ -311,6 +341,10 @@ private DataFlow::Node getAnOutNodeImpl(DataFlowCall call, ReturnKind kind) {
311341
or
312342
FlowSummaryImpl::Private::summaryOutNode(call.(SummaryCall).getReceiver(),
313343
result.(FlowSummaryNode).getSummaryNode(), kind)
344+
or
345+
kind = MkExceptionalReturnKind() and
346+
result.(FlowSummaryDefaultExceptionalReturn).getSummarizedCallable() =
347+
call.(SummaryCall).getSummarizedCallable()
314348
}
315349

316350
class ReturnNode extends DataFlow::Node {
@@ -505,6 +539,8 @@ DataFlowCallable nodeGetEnclosingCallable(Node node) {
505539
or
506540
result.asLibraryCallable() = node.(FlowSummaryIntermediateAwaitStoreNode).getSummarizedCallable()
507541
or
542+
result.asLibraryCallable() = node.(FlowSummaryDefaultExceptionalReturn).getSummarizedCallable()
543+
or
508544
node = TGenericSynthesizedNode(_, _, result)
509545
}
510546

@@ -865,6 +901,8 @@ class SummaryCall extends DataFlowCall, MkSummaryCall {
865901

866902
/** Gets the receiver node. */
867903
FlowSummaryImpl::Private::SummaryNode getReceiver() { result = receiver }
904+
905+
FlowSummaryImpl::Public::SummarizedCallable getSummarizedCallable() { result = enclosingCallable }
868906
}
869907

870908
/**

javascript/ql/test/library-tests/TripleDot/exceptions.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ function e1() {
1313
throw source('e1.3'); // Same as e1.2 but without callback parameters
1414
});
1515
} catch (err) {
16-
sink(err); // $ hasValueFlow=e1.2 hasValueFlow=e1.3 MISSING: hasValueFlow=e1.1
16+
sink(err); // $ hasValueFlow=e1.2 hasValueFlow=e1.3 hasValueFlow=e1.1
1717
}
1818
}
1919

@@ -58,7 +58,7 @@ function e4() {
5858
try {
5959
thrower([source("e4.1")]);
6060
} catch (e) {
61-
sink(e); // $ MISSING: hasValueFlow=e4.1
61+
sink(e); // $ hasValueFlow=e4.1
6262
}
6363
try {
6464
thrower(["safe"]);

0 commit comments

Comments
 (0)