Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ final class DataFlowCall extends TDataFlowCall {
/** Gets the underlying call, if any. */
Call asCall() { this = TCall(result) }

/** Gets the underlying await expression, if any. */
AwaitExpr asAwaitExpr() { this = TAwaitExpr(result) }

predicate isImplicitDerefCall(Expr e, DerefChain derefChain, int i, Function target) {
this = TImplicitDerefCall(e, derefChain, i, target)
}
Expand All @@ -73,15 +76,16 @@ final class DataFlowCall extends TDataFlowCall {
}

DataFlowCallable getEnclosingCallable() {
result.asCfgScope() = this.asCall().getEnclosingCfgScope()
result.asCfgScope() =
[this.asCall().getEnclosingCfgScope(), this.asAwaitExpr().getEnclosingCfgScope()]
or
result.asCfgScope() = any(Expr e | this.isImplicitDerefCall(e, _, _, _)).getEnclosingCfgScope()
or
this.isSummaryCall(result.asSummarizedCallable(), _)
}

string toString() {
result = this.asCall().toString()
result = [this.asCall().toString(), this.asAwaitExpr().toString()]
or
exists(Expr e, DerefChain derefChain, int i |
this.isImplicitDerefCall(e, derefChain, i, _) and
Expand All @@ -97,7 +101,7 @@ final class DataFlowCall extends TDataFlowCall {
}

Location getLocation() {
result = this.asCall().getLocation()
result = [this.asCall().getLocation(), this.asAwaitExpr().getLocation()]
or
result = any(Expr e | this.isImplicitDerefCall(e, _, _, _)).getLocation()
}
Expand Down Expand Up @@ -1004,7 +1008,11 @@ module RustDataFlowGen<RustDataFlowInputSig Input> implements InputSig<Location>
*/
predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) {
(
receiver.asExpr() = call.asCall().(CallExprImpl::DynamicCallExpr).getFunction()
receiver.asExpr() =
[
call.asCall().(CallExprImpl::DynamicCallExpr).getFunction(),
call.asAwaitExpr().getExpr()
]
or
call.isSummaryCall(_, receiver.(FlowSummaryNode).getSummaryNode())
) and
Expand Down Expand Up @@ -1176,6 +1184,7 @@ private module Cached {
Stages::DataFlowStage::ref() and
call.hasEnclosingCfgScope()
} or
TAwaitExpr(AwaitExpr await) { await.hasEnclosingCfgScope() } or
TImplicitDerefCall(Expr e, DerefChain derefChain, int i, Function target) {
TypeInference::implicitDerefChainBorrow(e, derefChain, _) and
target = derefChain.getElement(i).getDerefFunction() and
Expand Down
19 changes: 19 additions & 0 deletions rust/ql/lib/codeql/rust/dataflow/internal/Node.qll
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,20 @@ final class ClosureArgumentNode extends ArgumentNode, ExprNode {
}
}

/**
* A data flow node that represents the run-time representation of an async
* block passed into its body when awaited.
*/
final class AsyncBlockArgumentNode extends ArgumentNode, ExprNode {
private AwaitExpr await;

AsyncBlockArgumentNode() { this.asExpr() = await.getExpr() }

override predicate isArgumentOf(DataFlowCall call, RustDataFlow::ArgumentPosition pos) {
call.asAwaitExpr() = await and pos.isClosureSelf()
}
}

/** An SSA node. */
class SsaNode extends Node, TSsaNode {
SsaImpl::DataFlowIntegration::SsaNode node;
Expand Down Expand Up @@ -485,12 +499,17 @@ final private class ExprOutNode extends ExprNode, OutNode {
not call instanceof DerefExpr and // Handled by `DerefOutNode`
not call instanceof IndexExpr // Handled by `IndexOutNode`
)
or
this.asExpr() instanceof AwaitExpr
}

/** Gets the underlying call node that includes this out node. */
override DataFlowCall getCall(ReturnKind kind) {
result.asCall() = n and
kind = TNormalReturnKind()
or
result.asAwaitExpr() = n and
kind = TNormalReturnKind()
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,9 @@ edges
| main.rs:342:9:342:9 | a | main.rs:343:10:343:10 | a | provenance | |
| main.rs:342:13:342:26 | async_source(...) | main.rs:342:9:342:9 | a | provenance | |
| main.rs:346:13:346:13 | c | main.rs:347:14:347:14 | c | provenance | |
| main.rs:346:13:346:13 | c | main.rs:348:9:348:9 | c | provenance | |
| main.rs:346:17:346:25 | source(...) | main.rs:346:13:346:13 | c | provenance | |
| main.rs:348:9:348:9 | c | main.rs:350:10:350:16 | await b | provenance | |
| main.rs:354:9:354:9 | a | main.rs:355:10:355:10 | a | provenance | |
| main.rs:354:13:354:55 | ...::block_on(...) | main.rs:354:9:354:9 | a | provenance | |
| main.rs:354:41:354:54 | async_source(...) | main.rs:354:13:354:55 | ...::block_on(...) | provenance | MaD:3 |
Expand Down Expand Up @@ -443,6 +445,8 @@ nodes
| main.rs:346:13:346:13 | c | semmle.label | c |
| main.rs:346:17:346:25 | source(...) | semmle.label | source(...) |
| main.rs:347:14:347:14 | c | semmle.label | c |
| main.rs:348:9:348:9 | c | semmle.label | c |
| main.rs:350:10:350:16 | await b | semmle.label | await b |
| main.rs:354:9:354:9 | a | semmle.label | a |
| main.rs:354:13:354:55 | ...::block_on(...) | semmle.label | ...::block_on(...) |
| main.rs:354:41:354:54 | async_source(...) | semmle.label | async_source(...) |
Expand Down Expand Up @@ -539,6 +543,7 @@ testFailures
| main.rs:337:10:337:10 | a | main.rs:336:13:336:21 | source(...) | main.rs:337:10:337:10 | a | $@ | main.rs:336:13:336:21 | source(...) | source(...) |
| main.rs:343:10:343:10 | a | main.rs:336:13:336:21 | source(...) | main.rs:343:10:343:10 | a | $@ | main.rs:336:13:336:21 | source(...) | source(...) |
| main.rs:347:14:347:14 | c | main.rs:346:17:346:25 | source(...) | main.rs:347:14:347:14 | c | $@ | main.rs:346:17:346:25 | source(...) | source(...) |
| main.rs:350:10:350:16 | await b | main.rs:346:17:346:25 | source(...) | main.rs:350:10:350:16 | await b | $@ | main.rs:346:17:346:25 | source(...) | source(...) |
| main.rs:355:10:355:10 | a | main.rs:336:13:336:21 | source(...) | main.rs:355:10:355:10 | a | $@ | main.rs:336:13:336:21 | source(...) | source(...) |
| main.rs:404:14:404:15 | n1 | main.rs:379:13:379:21 | source(...) | main.rs:404:14:404:15 | n1 | $@ | main.rs:379:13:379:21 | source(...) | source(...) |
| main.rs:408:14:408:15 | n2 | main.rs:379:13:379:21 | source(...) | main.rs:408:14:408:15 | n2 | $@ | main.rs:379:13:379:21 | source(...) | source(...) |
Expand Down
2 changes: 1 addition & 1 deletion rust/ql/test/library-tests/dataflow/global/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ async fn test_async_await_async_part() {
sink(c); // $ hasValueFlow=2
c
};
sink(b.await); // $ MISSING: hasValueFlow=2
sink(b.await); // $ hasValueFlow=2
}

fn test_async_await() {
Expand Down
144 changes: 144 additions & 0 deletions rust/ql/test/library-tests/dataflow/lambdas/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,14 @@ edges
| main.rs:111:26:111:36 | \|...\| ... : ... => .. [captured a] | main.rs:111:35:111:35 | a | provenance | heuristic-callback |
| main.rs:112:26:112:36 | \|...\| ... : ... => .. [captured a] | main.rs:112:35:112:35 | a | provenance | heuristic-callback |
| main.rs:113:26:113:36 | \|...\| ... : ... => .. [captured a] | main.rs:113:35:113:35 | a | provenance | heuristic-callback |
| main.rs:120:30:120:40 | source(...) | main.rs:121:14:121:25 | await future | provenance | |
| main.rs:125:20:125:30 | source(...) | main.rs:126:9:128:9 | { ... } : ... => .. [captured data] | provenance | |
| main.rs:126:9:128:9 | { ... } : ... => .. [captured data] | main.rs:127:18:127:21 | data | provenance | |
| main.rs:133:20:133:30 | source(...) | main.rs:135:14:135:19 | future [captured data] | provenance | |
| main.rs:135:14:135:19 | future [captured data] | main.rs:134:30:134:33 | data | provenance | |
| main.rs:135:14:135:19 | future [captured data] | main.rs:135:14:135:25 | await future | provenance | |
| main.rs:141:9:143:9 | [post] { ... } : ... => .. [captured captured] | main.rs:145:14:145:21 | captured | provenance | |
| main.rs:142:24:142:34 | source(...) | main.rs:141:9:143:9 | [post] { ... } : ... => .. [captured captured] | provenance | |
nodes
| main.rs:10:20:10:52 | if cond {...} else {...} | semmle.label | if cond {...} else {...} |
| main.rs:10:30:10:39 | source(...) | semmle.label | source(...) |
Expand Down Expand Up @@ -138,9 +146,22 @@ nodes
| main.rs:112:35:112:35 | a | semmle.label | a |
| main.rs:113:26:113:36 | \|...\| ... : ... => .. [captured a] | semmle.label | \|...\| ... : ... => .. [captured a] |
| main.rs:113:35:113:35 | a | semmle.label | a |
| main.rs:120:30:120:40 | source(...) | semmle.label | source(...) |
| main.rs:121:14:121:25 | await future | semmle.label | await future |
| main.rs:125:20:125:30 | source(...) | semmle.label | source(...) |
| main.rs:126:9:128:9 | { ... } : ... => .. [captured data] | semmle.label | { ... } : ... => .. [captured data] |
| main.rs:127:18:127:21 | data | semmle.label | data |
| main.rs:133:20:133:30 | source(...) | semmle.label | source(...) |
| main.rs:134:30:134:33 | data | semmle.label | data |
| main.rs:135:14:135:19 | future [captured data] | semmle.label | future [captured data] |
| main.rs:135:14:135:25 | await future | semmle.label | await future |
| main.rs:141:9:143:9 | [post] { ... } : ... => .. [captured captured] | semmle.label | [post] { ... } : ... => .. [captured captured] |
| main.rs:142:24:142:34 | source(...) | semmle.label | source(...) |
| main.rs:145:14:145:21 | captured | semmle.label | captured |
subpaths
| main.rs:29:21:29:21 | a | main.rs:27:20:27:23 | ... | main.rs:27:26:27:52 | if cond {...} else {...} | main.rs:29:13:29:22 | f(...) |
| main.rs:77:21:77:21 | a | main.rs:66:24:66:32 | ...: i64 | main.rs:66:42:72:1 | { ... } | main.rs:77:13:77:22 | f(...) |
| main.rs:135:14:135:19 | future [captured data] | main.rs:134:30:134:33 | data | main.rs:134:30:134:33 | data | main.rs:135:14:135:25 | await future |
testFailures
#select
| main.rs:11:10:11:16 | f(...) | main.rs:10:30:10:39 | source(...) | main.rs:11:10:11:16 | f(...) | $@ | main.rs:10:30:10:39 | source(...) | source(...) |
Expand All @@ -161,3 +182,7 @@ testFailures
| main.rs:111:35:111:35 | a | main.rs:109:13:109:22 | source(...) | main.rs:111:35:111:35 | a | $@ | main.rs:109:13:109:22 | source(...) | source(...) |
| main.rs:112:35:112:35 | a | main.rs:109:13:109:22 | source(...) | main.rs:112:35:112:35 | a | $@ | main.rs:109:13:109:22 | source(...) | source(...) |
| main.rs:113:35:113:35 | a | main.rs:109:13:109:22 | source(...) | main.rs:113:35:113:35 | a | $@ | main.rs:109:13:109:22 | source(...) | source(...) |
| main.rs:121:14:121:25 | await future | main.rs:120:30:120:40 | source(...) | main.rs:121:14:121:25 | await future | $@ | main.rs:120:30:120:40 | source(...) | source(...) |
| main.rs:127:18:127:21 | data | main.rs:125:20:125:30 | source(...) | main.rs:127:18:127:21 | data | $@ | main.rs:125:20:125:30 | source(...) | source(...) |
| main.rs:135:14:135:25 | await future | main.rs:133:20:133:30 | source(...) | main.rs:135:14:135:25 | await future | $@ | main.rs:133:20:133:30 | source(...) | source(...) |
| main.rs:145:14:145:21 | captured | main.rs:142:24:142:34 | source(...) | main.rs:145:14:145:21 | captured | $@ | main.rs:142:24:142:34 | source(...) | source(...) |
56 changes: 56 additions & 0 deletions rust/ql/test/library-tests/dataflow/lambdas/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,61 @@ fn test_external_call() {
may_invoke_callback4(|x| sink(a)); // $ hasValueFlow=81
}

mod async_blocks {
use super::{sink, source};

async fn async_block_flow_out() {
let future = async { source(206) };
sink(future.await); // $ hasValueFlow=206
}

async fn async_block_flow_in() {
let data = source(207);
async {
sink(data); // $ hasValueFlow=207
}
.await;
}

async fn async_block_flow_through() {
let data = source(208);
let future = async { data };
sink(future.await); // $ hasValueFlow=208
}

async fn async_block_captured_variable() {
let mut captured = 1;
sink(captured);
async {
captured = source(209);
}
.await;
sink(captured); // $ hasValueFlow=209
}

async fn boxed_async_block_captured_variable() {
let mut captured = 1;
sink(captured);
Box::pin(async {
captured = source(210);
})
.await;
sink(captured); // $ MISSING: hasValueFlow=210
}

async fn test_async_blocks() {
async_block_flow_out().await;
async_block_flow_in().await;
async_block_flow_through().await;
async_block_captured_variable().await;
boxed_async_block_captured_variable().await;
}

pub fn main() {
futures::executor::block_on(test_async_blocks());
}
}

fn main() {
closure_flow_out();
closure_flow_in();
Expand All @@ -123,4 +178,5 @@ fn main() {
function_flows_through();
test_apply();
test_apply_wrap();
async_blocks::main();
}
Loading
Loading