Merge pull request #18010 from paldepind/rust-df-viable-callable

Rust: Include method calls in DataFlowCall and implement simple call target resolution
This commit is contained in:
Simon Friis Vindum 2024-11-19 11:10:34 +01:00 коммит произвёл GitHub
Родитель ef9f3835e5 86672b7e8c
Коммит 2307df41ed
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
5 изменённых файлов: 160 добавлений и 28 удалений

Просмотреть файл

@ -31,8 +31,19 @@ class ExprCfgNode extends AstCfgNode {
}
/** A CFG node that corresponds to a call in the AST. */
class CallCfgNode extends ExprCfgNode {
class CallExprCfgNode extends ExprCfgNode {
override CallExpr node;
/** Gets the underlying `CallExpr`. */
CallExpr getCallExpr() { result = node }
}
/** A CFG node that corresponds to a call in the AST. */
class MethodCallExprCfgNode extends ExprCfgNode {
override MethodCallExpr node;
/** Gets the underlying `MethodCallExpr`. */
MethodCallExpr getMethodCallExpr() { result = node }
}
final class ExitCfgNode = ExitNode;

Просмотреть файл

@ -42,35 +42,23 @@ final class DataFlowCallable extends TDataFlowCallable {
Location getLocation() { result = this.asCfgScope().getLocation() }
}
abstract class DataFlowCall extends TDataFlowCall {
/** Gets the enclosing callable. */
abstract DataFlowCallable getEnclosingCallable();
/** Gets the underlying source code call, if any. */
abstract CallCfgNode asCall();
/** Gets a textual representation of this call. */
abstract string toString();
/** Gets the location of this call. */
abstract Location getLocation();
}
final class NormalCall extends DataFlowCall, TNormalCall {
private CallCfgNode c;
NormalCall() { this = TNormalCall(c) }
final class DataFlowCall extends TDataFlowCall {
/** Gets the underlying call in the CFG, if any. */
override CallCfgNode asCall() { result = c }
CallExprCfgNode asCallExprCfgNode() { this = TNormalCall(result) }
override DataFlowCallable getEnclosingCallable() {
result = TCfgScope(c.getExpr().getEnclosingCfgScope())
MethodCallExprCfgNode asMethodCallExprCfgNode() { this = TMethodCall(result) }
ExprCfgNode asExprCfgNode() {
result = this.asCallExprCfgNode() or result = this.asMethodCallExprCfgNode()
}
override string toString() { result = c.toString() }
DataFlowCallable getEnclosingCallable() {
result = TCfgScope(this.asExprCfgNode().getExpr().getEnclosingCfgScope())
}
override Location getLocation() { result = c.getLocation() }
string toString() { result = this.asExprCfgNode().toString() }
Location getLocation() { result = this.asExprCfgNode().getLocation() }
}
module Node {
@ -204,7 +192,7 @@ module Node {
ExprOutNode() { this.asExpr() instanceof CallExpr }
/** Gets the underlying call CFG node that includes this out node. */
override DataFlowCall getCall() { result.(NormalCall).asCall() = this.getCfgNode() }
override DataFlowCall getCall() { result.asExprCfgNode() = this.getCfgNode() }
}
/**
@ -331,7 +319,15 @@ module RustDataFlow implements InputSig<Location> {
final class ReturnKind = ReturnKindAlias;
/** Gets a viable implementation of the target of the given `Call`. */
DataFlowCallable viableCallable(DataFlowCall c) { none() }
DataFlowCallable viableCallable(DataFlowCall c) {
exists(Function f, string name | result.asCfgScope() = f and name = f.getName().toString() |
if f.getParamList().hasSelfParam()
then name = c.asMethodCallExprCfgNode().getMethodCallExpr().getNameRef().getText()
else
name =
c.asCallExprCfgNode().getCallExpr().getExpr().(PathExpr).getPath().getPart().toString()
)
}
/**
* Gets a node that can read the value returned from `call` with return kind
@ -488,7 +484,9 @@ private module Cached {
TSsaNode(SsaImpl::DataFlowIntegration::SsaNode node)
cached
newtype TDataFlowCall = TNormalCall(CallCfgNode c)
newtype TDataFlowCall =
TNormalCall(CallExprCfgNode c) or
TMethodCall(MethodCallExprCfgNode c)
cached
newtype TOptionalContentSet =

Просмотреть файл

@ -0,0 +1,94 @@
fn source(i: i64) -> i64 {
1000 + i
}
fn sink(s: i64) {
println!("{}", s);
}
// -----------------------------------------------------------------------------
// Data flow in, out, and through functions.
fn get_data(n: i64) -> i64 {
source(n)
}
fn data_out_of_call() {
let a = get_data(7);
sink(a); // $ hasValueFlow=n
}
fn data_in(n: i64) {
sink(n + 7); // $ hasValueFlow
}
fn data_in_to_call() {
let a = source(3);
data_in(a);
}
fn pass_through(i: i64) -> i64 {
i
}
fn data_through_call() {
let a = source(1);
let b = pass_through(a);
sink(b); // $ hasValueFlow=1
}
// -----------------------------------------------------------------------------
// Data flow in, out, and through method.
struct MyFlag {
flag: bool,
}
impl MyFlag {
fn data_in(&self, n: i64) {
sink(n); // $ hasValueFlow=1
}
fn get_data(&self) -> i64 {
if self.flag {
0
} else {
source(2)
}
}
fn data_through(&self, n: i64) -> i64 {
if self.flag {
0
} else {
n
}
}
}
fn data_out_of_method() {
let mn = MyFlag { flag: true };
let a = mn.get_data();
sink(a);
}
fn data_in_to_method_call() {
let mn = MyFlag { flag: true };
let a = source(1);
mn.data_in(a)
}
fn data_through_method() {
let mn = MyFlag { flag: true };
let a = source(4);
mn.data_through(a);
sink(a); // $ hasValueFlow=4
}
fn main() {
data_out_of_call();
data_in_to_call();
data_through_call();
data_out_of_method();
data_in_to_method_call();
data_through_method();
}

Просмотреть файл

@ -0,0 +1,24 @@
| main.rs:13:5:13:13 | CallExpr | main.rs:1:1:3:1 | source |
| main.rs:17:13:17:23 | CallExpr | main.rs:12:1:14:1 | get_data |
| main.rs:18:5:18:11 | CallExpr | main.rs:5:1:7:1 | sink |
| main.rs:22:5:22:15 | CallExpr | main.rs:5:1:7:1 | sink |
| main.rs:26:13:26:21 | CallExpr | main.rs:1:1:3:1 | source |
| main.rs:27:5:27:14 | CallExpr | main.rs:21:1:23:1 | data_in |
| main.rs:35:13:35:21 | CallExpr | main.rs:1:1:3:1 | source |
| main.rs:36:13:36:27 | CallExpr | main.rs:30:1:32:1 | pass_through |
| main.rs:37:5:37:11 | CallExpr | main.rs:5:1:7:1 | sink |
| main.rs:49:9:49:15 | CallExpr | main.rs:5:1:7:1 | sink |
| main.rs:55:13:55:21 | CallExpr | main.rs:1:1:3:1 | source |
| main.rs:69:13:69:25 | ... .get_data(...) | main.rs:51:5:57:5 | get_data |
| main.rs:70:5:70:11 | CallExpr | main.rs:5:1:7:1 | sink |
| main.rs:75:13:75:21 | CallExpr | main.rs:1:1:3:1 | source |
| main.rs:76:5:76:17 | ... .data_in(...) | main.rs:48:5:50:5 | data_in |
| main.rs:81:13:81:21 | CallExpr | main.rs:1:1:3:1 | source |
| main.rs:82:5:82:22 | ... .data_through(...) | main.rs:58:5:64:5 | data_through |
| main.rs:83:5:83:11 | CallExpr | main.rs:5:1:7:1 | sink |
| main.rs:87:5:87:22 | CallExpr | main.rs:16:1:19:1 | data_out_of_call |
| main.rs:88:5:88:21 | CallExpr | main.rs:25:1:28:1 | data_in_to_call |
| main.rs:89:5:89:23 | CallExpr | main.rs:34:1:38:1 | data_through_call |
| main.rs:91:5:91:24 | CallExpr | main.rs:67:1:71:1 | data_out_of_method |
| main.rs:92:5:92:28 | CallExpr | main.rs:73:1:77:1 | data_in_to_method_call |
| main.rs:93:5:93:25 | CallExpr | main.rs:79:1:84:1 | data_through_method |

Просмотреть файл

@ -0,0 +1,5 @@
import codeql.rust.dataflow.internal.DataFlowImpl
query predicate viableCallable(DataFlowCall call, DataFlowCallable callee) {
RustDataFlow::viableCallable(call) = callee
}