Skip to content

Commit 6f89834

Browse files
Rust: Share the returned-value check with callback return sinks
1 parent af7dd28 commit 6f89834

4 files changed

Lines changed: 27 additions & 10 deletions

File tree

‎rust/ql/lib/codeql/rust/dataflow/internal/FlowSummaryImpl.qll‎

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,6 @@ private Callable getCallable(Expr expr) {
144144
}
145145

146146
module Input2 implements Impl::Private::InputSig2 {
147-
private import codeql.rust.controlflow.CfgNodes
148-
149147
class SourceSinkReportingElement extends AstNode {
150148
DataFlowCallable getEnclosingCallable() { result.asCfgScope() = this.getEnclosingCfgScope() }
151149

@@ -158,7 +156,7 @@ module Input2 implements Impl::Private::InputSig2 {
158156
exists(Callable c |
159157
sc = Impl::Private::SummaryComponent::return(_) and
160158
c = getCallable(this) and
161-
result.getACfgNode().getASuccessor() instanceof AnnotatedExitCfgNode and
159+
isReturnedValue(result) and
162160
result.getEnclosingCfgScope() = c
163161
)
164162
}

‎rust/ql/lib/codeql/rust/dataflow/internal/Node.qll‎

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -472,14 +472,17 @@ abstract class ReturnNode extends Node {
472472
abstract ReturnKind getKind();
473473
}
474474

475+
/** Holds if the value of `n` is returned from its enclosing callable. */
476+
predicate isReturnedValue(AstNode n) {
477+
exists(SuccessorType t | n.getACfgNode().getASuccessor(t) instanceof AnnotatedExitCfgNode |
478+
// The early return of `x?` returns the `Err` or `None` held by `x`, which is
479+
// a different value from the one `x?` evaluates to
480+
not (n instanceof TryExpr and t instanceof ReturnSuccessor)
481+
)
482+
}
483+
475484
final class ExprReturnNode extends ExprNode, ReturnNode {
476-
ExprReturnNode() {
477-
exists(SuccessorType t | n.getACfgNode().getASuccessor(t) instanceof AnnotatedExitCfgNode |
478-
// The early return of `x?` returns the `Err` or `None` held by `x`, which is
479-
// a different value from the one `x?` evaluates to
480-
not (n instanceof TryExpr and t instanceof ReturnSuccessor)
481-
)
482-
}
485+
ExprReturnNode() { isReturnedValue(n) }
483486

484487
override ReturnKind getKind() { result = TNormalReturnKind() }
485488
}

‎rust/ql/test/library-tests/dataflow/models/main.rs‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -581,3 +581,18 @@ async fn main() {
581581
test_neutrals();
582582
let dummy = Some(0); // ensure that the the `lang:core` crate is extracted
583583
}
584+
585+
mod try_in_sink_callback {
586+
use super::source;
587+
588+
// has a sink model
589+
fn pass_sink_option(f: impl FnOnce(()) -> Option<i64>) {}
590+
591+
fn test_try_in_sink_callback() {
592+
let a = |_a| {
593+
let _s = Some(source(100))?;
594+
Some(0)
595+
};
596+
pass_sink_option(a);
597+
}
598+
}

‎rust/ql/test/library-tests/dataflow/models/models.ext.yml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ extensions:
2626
- ["<main::MyFieldEnum>::sink", "Argument[self].Field[main::MyFieldEnum::D::field_d]", "test-sink", "manual"]
2727
- ["main::sink_out_of_function::pass_sink", "Argument[0].ReturnValue", "test-sink", "manual"]
2828
- ["main::sink_out_of_function::pass_sink_nested", "Argument[0].ReturnValue.Field[main::MyFieldEnum::E::field_e].Field[core::option::Option::Some(0)]", "test-sink", "manual"]
29+
- ["main::try_in_sink_callback::pass_sink_option", "Argument[0].ReturnValue", "test-sink", "manual"]
2930
- ["main::external_file::generated_sink", "Argument[0]", "test-sink", "dfc-generated"]
3031
- ["main::external_file::neutral_generated_sink", "Argument[0]", "test-sink", "dfc-generated"]
3132
- ["main::external_file::neutral_manual_sink", "Argument[0]", "test-sink", "manual"]

0 commit comments

Comments
 (0)