Rust: Use Call in data flow

This commit is contained in:
Simon Friis Vindum
2025-06-06 14:18:28 +02:00
parent 5642445e1d
commit fecd445e78
6 changed files with 51 additions and 46 deletions

View File

@@ -8,6 +8,7 @@ private import codeql.util.Boolean
private import codeql.dataflow.DataFlow
private import codeql.dataflow.internal.DataFlowImpl
private import rust
private import codeql.rust.elements.Call
private import SsaImpl as SsaImpl
private import codeql.rust.controlflow.internal.Scope as Scope
private import codeql.rust.internal.PathResolution
@@ -55,11 +56,7 @@ final class DataFlowCallable extends TDataFlowCallable {
final class DataFlowCall extends TDataFlowCall {
/** Gets the underlying call in the CFG, if any. */
CallExprCfgNode asCallExprCfgNode() { result = this.asCallBaseExprCfgNode() }
MethodCallExprCfgNode asMethodCallExprCfgNode() { result = this.asCallBaseExprCfgNode() }
CallExprBaseCfgNode asCallBaseExprCfgNode() { this = TCall(result) }
CallCfgNode asCallCfgNode() { this = TCall(result) }
predicate isSummaryCall(
FlowSummaryImpl::Public::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNode receiver
@@ -68,7 +65,7 @@ final class DataFlowCall extends TDataFlowCall {
}
DataFlowCallable getEnclosingCallable() {
result = TCfgScope(this.asCallBaseExprCfgNode().getExpr().getEnclosingCfgScope())
result = TCfgScope(this.asCallCfgNode().getExpr().getEnclosingCfgScope())
or
exists(FlowSummaryImpl::Public::SummarizedCallable c |
this.isSummaryCall(c, _) and
@@ -77,7 +74,7 @@ final class DataFlowCall extends TDataFlowCall {
}
string toString() {
result = this.asCallBaseExprCfgNode().toString()
result = this.asCallCfgNode().toString()
or
exists(
FlowSummaryImpl::Public::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNode receiver
@@ -87,7 +84,7 @@ final class DataFlowCall extends TDataFlowCall {
)
}
Location getLocation() { result = this.asCallBaseExprCfgNode().getLocation() }
Location getLocation() { result = this.asCallCfgNode().getLocation() }
}
/**
@@ -135,38 +132,23 @@ final class ParameterPosition extends TParameterPosition {
*/
final class ArgumentPosition extends ParameterPosition {
/** Gets the argument of `call` at this position, if any. */
Expr getArgument(CallExprBase call) {
result = call.getArgList().getArg(this.getPosition())
Expr getArgument(Call call) {
result = call.getArgument(this.getPosition())
or
this.isSelf() and
result = call.(MethodCallExpr).getReceiver()
result = call.getReceiver() and this.isSelf()
}
}
/** Holds if `call` invokes a qualified path that resolves to a method. */
private predicate callToMethod(CallExpr call) {
exists(Path path |
path = call.getFunction().(PathExpr).getPath() and
path.hasQualifier() and
resolvePath(path).(Function).getParamList().hasSelfParam()
)
}
/**
* Holds if `arg` is an argument of `call` at the position `pos`.
*
* Note that this does not hold for the receiever expression of a method call
* as the synthetic `ReceiverNode` is the argument for the `self` parameter.
*/
predicate isArgumentForCall(ExprCfgNode arg, CallExprBaseCfgNode call, ParameterPosition pos) {
if callToMethod(call.(CallExprCfgNode).getCallExpr())
then
// The first argument is for the `self` parameter
arg = call.getArgument(0) and pos.isSelf()
or
// Succeeding arguments are shifted left
arg = call.getArgument(pos.getPosition() + 1)
else arg = call.getArgument(pos.getPosition())
predicate isArgumentForCall(ExprCfgNode arg, CallCfgNode call, ParameterPosition pos) {
call.getArgument(pos.getPosition()) = arg
or
call.getReceiver() = arg and pos.isSelf() and not call.getCall().receiverImplicitlyBorrowed()
}
/** Provides logic related to SSA. */
@@ -419,9 +401,9 @@ module RustDataFlow implements InputSig<Location> {
/** Gets a viable implementation of the target of the given `Call`. */
DataFlowCallable viableCallable(DataFlowCall call) {
result.asCfgScope() = call.asCallBaseExprCfgNode().getCallExprBase().getStaticTarget()
result.asCfgScope() = call.asCallCfgNode().getCall().getStaticTarget()
or
result.asLibraryCallable().getACall() = call.asCallBaseExprCfgNode().getCallExprBase()
result.asLibraryCallable().getACall() = call.asCallCfgNode().getCall()
}
/**
@@ -812,7 +794,7 @@ module RustDataFlow implements InputSig<Location> {
*/
predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) {
(
receiver.asExpr() = call.asCallExprCfgNode().getFunction() and
receiver.asExpr() = call.asCallCfgNode().(CallExprCfgNode).getFunction() and
// All calls to complex expressions and local variable accesses are lambda call.
exists(Expr f | f = receiver.asExpr().getExpr() |
f instanceof PathExpr implies f = any(Variable v).getAnAccess()
@@ -976,7 +958,7 @@ private module Cached {
cached
newtype TDataFlowCall =
TCall(CallExprBaseCfgNode c) { Stages::DataFlowStage::ref() } or
TCall(CallCfgNode c) { Stages::DataFlowStage::ref() } or
TSummaryCall(
FlowSummaryImpl::Public::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNode receiver
) {

View File

@@ -153,7 +153,7 @@ private import Make<Location, RustDataFlow, Input> as Impl
private module StepsInput implements Impl::Private::StepsInputSig {
DataFlowCall getACall(Public::SummarizedCallable sc) {
result.asCallBaseExprCfgNode().getCallExprBase() = sc.(LibraryCallable).getACall()
result.asCallCfgNode().getCall() = sc.(LibraryCallable).getACall()
}
RustDataFlow::Node getSourceNode(Input::SourceBase source, Impl::Private::SummaryComponent sc) {

View File

@@ -224,13 +224,13 @@ abstract class ArgumentNode extends Node {
}
final class ExprArgumentNode extends ArgumentNode, ExprNode {
private CallExprBaseCfgNode call_;
private CallCfgNode call_;
private RustDataFlow::ArgumentPosition pos_;
ExprArgumentNode() { isArgumentForCall(n, call_, pos_) }
override predicate isArgumentOf(DataFlowCall call, RustDataFlow::ArgumentPosition pos) {
call.asCallBaseExprCfgNode() = call_ and pos = pos_
call.asCallCfgNode() = call_ and pos = pos_
}
}
@@ -239,7 +239,7 @@ final class ExprArgumentNode extends ArgumentNode, ExprNode {
* has taken place.
*/
final class ReceiverNode extends ArgumentNode, TReceiverNode {
private MethodCallExprCfgNode n;
private CallCfgNode n;
ReceiverNode() { this = TReceiverNode(n, false) }
@@ -248,7 +248,7 @@ final class ReceiverNode extends ArgumentNode, TReceiverNode {
MethodCallExprCfgNode getMethodCall() { result = n }
override predicate isArgumentOf(DataFlowCall call, RustDataFlow::ArgumentPosition pos) {
call.asMethodCallExprCfgNode() = n and pos = TSelfParameterPosition()
call.asCallCfgNode() = n and pos = TSelfParameterPosition()
}
override CfgScope getCfgScope() { result = n.getAstNode().getEnclosingCfgScope() }
@@ -281,7 +281,7 @@ final class ClosureArgumentNode extends ArgumentNode, ExprNode {
ClosureArgumentNode() { lambdaCallExpr(call_, _, this.asExpr()) }
override predicate isArgumentOf(DataFlowCall call, RustDataFlow::ArgumentPosition pos) {
call.asCallExprCfgNode() = call_ and
call.asCallCfgNode() = call_ and
pos.isClosureSelf()
}
}
@@ -330,11 +330,11 @@ abstract class OutNode extends Node {
}
final private class ExprOutNode extends ExprNode, OutNode {
ExprOutNode() { this.asExpr() instanceof CallExprBaseCfgNode }
ExprOutNode() { this.asExpr() instanceof CallCfgNode }
/** Gets the underlying call CFG node that includes this out node. */
override DataFlowCall getCall(ReturnKind kind) {
result.asCallBaseExprCfgNode() = this.getCfgNode() and
result.asCallCfgNode() = this.getCfgNode() and
kind = TNormalReturnKind()
}
}
@@ -404,7 +404,7 @@ final class ExprPostUpdateNode extends PostUpdateNode, TExprPostUpdateNode {
}
final class ReceiverPostUpdateNode extends PostUpdateNode, TReceiverNode {
private MethodCallExprCfgNode n;
private CallCfgNode n;
ReceiverPostUpdateNode() { this = TReceiverNode(n, true) }
@@ -467,11 +467,12 @@ newtype TNode =
any(FieldExprCfgNode access).getContainer(), //
any(TryExprCfgNode try).getExpr(), //
any(PrefixExprCfgNode pe | pe.getOperatorName() = "*").getExpr(), //
any(AwaitExprCfgNode a).getExpr(), any(MethodCallExprCfgNode mc).getReceiver(), //
any(AwaitExprCfgNode a).getExpr(), //
any(MethodCallExprCfgNode mc).getReceiver(), //
getPostUpdateReverseStep(any(PostUpdateNode n).getPreUpdateNode().asExpr(), _)
]
} or
TReceiverNode(MethodCallExprCfgNode mc, Boolean isPost) or
TReceiverNode(CallCfgNode mc, Boolean isPost) { mc.getCall().receiverImplicitlyBorrowed() } or
TSsaNode(SsaImpl::DataFlowIntegration::SsaNode node) or
TFlowSummaryNode(FlowSummaryImpl::Private::SummaryNode sn) or
TClosureSelfReferenceNode(CfgScope c) { lambdaCreationExpr(c, _) } or

View File

@@ -85,6 +85,14 @@ edges
| main.rs:175:9:175:35 | MyInt {...} [MyInt] | main.rs:173:42:176:5 | { ... } [MyInt] | provenance | |
| main.rs:175:24:175:27 | self [MyInt] | main.rs:175:24:175:33 | self.value | provenance | |
| main.rs:175:24:175:33 | self.value | main.rs:175:9:175:35 | MyInt {...} [MyInt] | provenance | |
| main.rs:195:9:195:9 | a [MyInt] | main.rs:197:13:197:13 | a [MyInt] | provenance | |
| main.rs:195:13:195:38 | MyInt {...} [MyInt] | main.rs:195:9:195:9 | a [MyInt] | provenance | |
| main.rs:195:28:195:36 | source(...) | main.rs:195:13:195:38 | MyInt {...} [MyInt] | provenance | |
| main.rs:197:9:197:9 | c [MyInt] | main.rs:198:10:198:10 | c [MyInt] | provenance | |
| main.rs:197:13:197:13 | a [MyInt] | main.rs:173:12:173:15 | SelfParam [MyInt] | provenance | |
| main.rs:197:13:197:13 | a [MyInt] | main.rs:197:13:197:17 | ... + ... [MyInt] | provenance | |
| main.rs:197:13:197:17 | ... + ... [MyInt] | main.rs:197:9:197:9 | c [MyInt] | provenance | |
| main.rs:198:10:198:10 | c [MyInt] | main.rs:198:10:198:16 | c.value | provenance | |
| main.rs:205:9:205:9 | a [MyInt] | main.rs:173:12:173:15 | SelfParam [MyInt] | provenance | |
| main.rs:205:9:205:9 | a [MyInt] | main.rs:207:13:207:20 | a.add(...) [MyInt] | provenance | |
| main.rs:205:13:205:38 | MyInt {...} [MyInt] | main.rs:205:9:205:9 | a [MyInt] | provenance | |
@@ -213,6 +221,14 @@ nodes
| main.rs:175:9:175:35 | MyInt {...} [MyInt] | semmle.label | MyInt {...} [MyInt] |
| main.rs:175:24:175:27 | self [MyInt] | semmle.label | self [MyInt] |
| main.rs:175:24:175:33 | self.value | semmle.label | self.value |
| main.rs:195:9:195:9 | a [MyInt] | semmle.label | a [MyInt] |
| main.rs:195:13:195:38 | MyInt {...} [MyInt] | semmle.label | MyInt {...} [MyInt] |
| main.rs:195:28:195:36 | source(...) | semmle.label | source(...) |
| main.rs:197:9:197:9 | c [MyInt] | semmle.label | c [MyInt] |
| main.rs:197:13:197:13 | a [MyInt] | semmle.label | a [MyInt] |
| main.rs:197:13:197:17 | ... + ... [MyInt] | semmle.label | ... + ... [MyInt] |
| main.rs:198:10:198:10 | c [MyInt] | semmle.label | c [MyInt] |
| main.rs:198:10:198:16 | c.value | semmle.label | c.value |
| main.rs:205:9:205:9 | a [MyInt] | semmle.label | a [MyInt] |
| main.rs:205:13:205:38 | MyInt {...} [MyInt] | semmle.label | MyInt {...} [MyInt] |
| main.rs:205:28:205:36 | source(...) | semmle.label | source(...) |
@@ -262,6 +278,7 @@ subpaths
| main.rs:134:29:134:29 | a | main.rs:110:27:110:32 | ...: i64 | main.rs:110:42:116:5 | { ... } | main.rs:134:13:134:30 | mn.data_through(...) |
| main.rs:147:38:147:38 | a | main.rs:110:27:110:32 | ...: i64 | main.rs:110:42:116:5 | { ... } | main.rs:147:13:147:39 | ...::data_through(...) |
| main.rs:165:24:165:33 | source(...) | main.rs:159:12:159:17 | ...: i64 | main.rs:159:28:161:5 | { ... } [MyInt] | main.rs:165:13:165:34 | ...::new(...) [MyInt] |
| main.rs:197:13:197:13 | a [MyInt] | main.rs:173:12:173:15 | SelfParam [MyInt] | main.rs:173:42:176:5 | { ... } [MyInt] | main.rs:197:13:197:17 | ... + ... [MyInt] |
| main.rs:205:9:205:9 | a [MyInt] | main.rs:173:12:173:15 | SelfParam [MyInt] | main.rs:173:42:176:5 | { ... } [MyInt] | main.rs:207:13:207:20 | a.add(...) [MyInt] |
| main.rs:254:49:254:49 | a [MyInt] | main.rs:242:18:242:21 | SelfParam [MyInt] | main.rs:242:48:244:5 | { ... } [MyInt] | main.rs:254:30:254:53 | ...::take_self(...) [MyInt] |
| main.rs:259:54:259:54 | b [MyInt] | main.rs:246:26:246:37 | ...: MyInt [MyInt] | main.rs:246:49:248:5 | { ... } [MyInt] | main.rs:259:30:259:55 | ...::take_second(...) [MyInt] |
@@ -280,6 +297,7 @@ testFailures
| main.rs:135:10:135:10 | b | main.rs:133:13:133:21 | source(...) | main.rs:135:10:135:10 | b | $@ | main.rs:133:13:133:21 | source(...) | source(...) |
| main.rs:148:10:148:10 | b | main.rs:146:13:146:22 | source(...) | main.rs:148:10:148:10 | b | $@ | main.rs:146:13:146:22 | source(...) | source(...) |
| main.rs:167:10:167:10 | m | main.rs:165:24:165:33 | source(...) | main.rs:167:10:167:10 | m | $@ | main.rs:165:24:165:33 | source(...) | source(...) |
| main.rs:198:10:198:16 | c.value | main.rs:195:28:195:36 | source(...) | main.rs:198:10:198:16 | c.value | $@ | main.rs:195:28:195:36 | source(...) | source(...) |
| main.rs:208:10:208:16 | d.value | main.rs:205:28:205:36 | source(...) | main.rs:208:10:208:16 | d.value | $@ | main.rs:205:28:205:36 | source(...) | source(...) |
| main.rs:255:10:255:10 | c | main.rs:252:28:252:36 | source(...) | main.rs:255:10:255:10 | c | $@ | main.rs:252:28:252:36 | source(...) | source(...) |
| main.rs:260:10:260:10 | c | main.rs:258:28:258:37 | source(...) | main.rs:260:10:260:10 | c | $@ | main.rs:258:28:258:37 | source(...) | source(...) |

View File

@@ -195,7 +195,7 @@ fn test_operator_overloading() {
let a = MyInt { value: source(5) };
let b = MyInt { value: 2 };
let c = a + b;
sink(c.value); // $ MISSING: hasValueFlow=5
sink(c.value); // $ hasValueFlow=5
let a = MyInt { value: 2 };
let b = MyInt { value: source(6) };

View File

@@ -41,8 +41,10 @@
| main.rs:165:24:165:33 | source(...) | main.rs:1:1:3:1 | fn source |
| main.rs:167:5:167:11 | sink(...) | main.rs:5:1:7:1 | fn sink |
| main.rs:195:28:195:36 | source(...) | main.rs:1:1:3:1 | fn source |
| main.rs:197:13:197:17 | ... + ... | main.rs:173:5:176:5 | fn add |
| main.rs:198:5:198:17 | sink(...) | main.rs:5:1:7:1 | fn sink |
| main.rs:201:28:201:36 | source(...) | main.rs:1:1:3:1 | fn source |
| main.rs:202:13:202:17 | ... + ... | main.rs:173:5:176:5 | fn add |
| main.rs:203:5:203:17 | sink(...) | main.rs:5:1:7:1 | fn sink |
| main.rs:205:28:205:36 | source(...) | main.rs:1:1:3:1 | fn source |
| main.rs:207:13:207:20 | a.add(...) | main.rs:173:5:176:5 | fn add |
@@ -50,10 +52,12 @@
| main.rs:212:28:212:37 | source(...) | main.rs:1:1:3:1 | fn source |
| main.rs:215:5:215:17 | sink(...) | main.rs:5:1:7:1 | fn sink |
| main.rs:218:28:218:37 | source(...) | main.rs:1:1:3:1 | fn source |
| main.rs:219:5:219:10 | ... *= ... | main.rs:180:5:182:5 | fn mul_assign |
| main.rs:220:5:220:17 | sink(...) | main.rs:5:1:7:1 | fn sink |
| main.rs:223:28:223:37 | source(...) | main.rs:1:1:3:1 | fn source |
| main.rs:226:5:226:11 | sink(...) | main.rs:5:1:7:1 | fn sink |
| main.rs:228:28:228:37 | source(...) | main.rs:1:1:3:1 | fn source |
| main.rs:229:13:229:14 | * ... | main.rs:188:5:190:5 | fn deref |
| main.rs:230:5:230:11 | sink(...) | main.rs:5:1:7:1 | fn sink |
| main.rs:252:28:252:36 | source(...) | main.rs:1:1:3:1 | fn source |
| main.rs:254:30:254:53 | ...::take_self(...) | main.rs:242:5:244:5 | fn take_self |