mirror of
https://github.com/github/codeql.git
synced 2025-12-16 16:53:25 +01:00
Merge remote-tracking branch 'upstream/main' into access-invalid-pointer-fp
This commit is contained in:
1
rust/ql/.generated.list
generated
1
rust/ql/.generated.list
generated
@@ -235,7 +235,6 @@ lib/codeql/rust/elements/internal/AwaitExprConstructor.qll 44ff1653e73d5b9f6885c
|
||||
lib/codeql/rust/elements/internal/BecomeExprConstructor.qll ba073aaa256cb8827a0307c3128d50f62b11aac0b1f324e48c95f30351a9b942 3a787ded505c3158fa4f4923f66e8ecdcb7b5f86f27f64c5412dc32dca031f18
|
||||
lib/codeql/rust/elements/internal/BinaryExprConstructor.qll 7f9b17757f78b9fb7c46e21d2040a77fa50083bef4911c8464991c3d1ad91d87 a59390cd8e896c0bfbdc9ba0674e06d980ffcefa710fbc9886be52ed427e9717
|
||||
lib/codeql/rust/elements/internal/BlockExprConstructor.qll 438337c807645e98a01440f3f4610d68b0567ba15c8f51dc43bf5a30c9af3696 48ce7a546910c884619762349b8ada9836284f8008298fdb0070a38f7ddf25a0
|
||||
lib/codeql/rust/elements/internal/BlockExprImpl.qll 36ac09e4a6eeeec22919b62b1d004bdb5bb2527e67932c308aec383a770768d6 3b4b2a2014f6fe075c63a2d633b297566b548ef2e4343cadf067a9edbcadc876
|
||||
lib/codeql/rust/elements/internal/BoxPatConstructor.qll 153f110ba25fd6c889092bfd16f73bb610fa60d6e0c8965d5f44d2446fcd48a2 9324cf0d8aa29945551bf8ab64801d598f57aab8cd4e19bcd4e9ef8a4a4e06eb
|
||||
lib/codeql/rust/elements/internal/BreakExprConstructor.qll 356be043c28e0b34fdf925a119c945632ee883c6f5ebb9a27003c6a8d250afd9 bb77e66b04bb9489340e7506931559b94285c6904b6f9d2f83b214cba4f3cfd5
|
||||
lib/codeql/rust/elements/internal/CallExprConstructor.qll 742b38e862e2cf82fd1ecc4d4fc5b4782a9c7c07f031452b2bae7aa59d5aa13a cad6e0a8be21d91b20ac2ec16cab9c30eae810b452c0f1992ed87d5c7f4144dc
|
||||
|
||||
1
rust/ql/.gitattributes
generated
vendored
1
rust/ql/.gitattributes
generated
vendored
@@ -237,7 +237,6 @@
|
||||
/lib/codeql/rust/elements/internal/BecomeExprConstructor.qll linguist-generated
|
||||
/lib/codeql/rust/elements/internal/BinaryExprConstructor.qll linguist-generated
|
||||
/lib/codeql/rust/elements/internal/BlockExprConstructor.qll linguist-generated
|
||||
/lib/codeql/rust/elements/internal/BlockExprImpl.qll linguist-generated
|
||||
/lib/codeql/rust/elements/internal/BoxPatConstructor.qll linguist-generated
|
||||
/lib/codeql/rust/elements/internal/BreakExprConstructor.qll linguist-generated
|
||||
/lib/codeql/rust/elements/internal/CallExprConstructor.qll linguist-generated
|
||||
|
||||
4
rust/ql/examples/qlpack.lock.yml
Normal file
4
rust/ql/examples/qlpack.lock.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
dependencies: {}
|
||||
compiled: false
|
||||
lockVersion: 1.0.0
|
||||
7
rust/ql/examples/qlpack.yml
Normal file
7
rust/ql/examples/qlpack.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
name: codeql/rust-examples
|
||||
groups:
|
||||
- rust
|
||||
- examples
|
||||
dependencies:
|
||||
codeql/rust-all: ${workspace}
|
||||
warnOnImplicitThis: true
|
||||
18
rust/ql/examples/snippets/empty_if.ql
Normal file
18
rust/ql/examples/snippets/empty_if.ql
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* @name Empty 'if' expression
|
||||
* @description Finds 'if' expressions where the "then" branch is empty and no
|
||||
* "else" branch exists.
|
||||
* @id rust/examples/empty-if
|
||||
* @tags example
|
||||
*/
|
||||
|
||||
import rust
|
||||
|
||||
// find 'if' expressions...
|
||||
from IfExpr ifExpr
|
||||
where
|
||||
// where the 'then' branch is empty
|
||||
ifExpr.getThen().getStmtList().getNumberOfStmtOrExpr() = 0 and
|
||||
// and no 'else' branch exists
|
||||
not ifExpr.hasElse()
|
||||
select ifExpr, "This 'if' expression is redundant."
|
||||
48
rust/ql/examples/snippets/simple_constant_password.ql
Normal file
48
rust/ql/examples/snippets/simple_constant_password.ql
Normal file
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* @name Constant password
|
||||
* @description Finds places where a string literal is used in a function call
|
||||
* argument that looks like a password.
|
||||
* @id rust/examples/simple-constant-password
|
||||
* @tags example
|
||||
*/
|
||||
|
||||
import rust
|
||||
import codeql.rust.dataflow.DataFlow
|
||||
import codeql.rust.dataflow.TaintTracking
|
||||
|
||||
/**
|
||||
* A data flow configuration for tracking flow from a string literal to a function
|
||||
* call argument that looks like a password. For example:
|
||||
* ```
|
||||
* fn set_password(password: &str) { ... }
|
||||
*
|
||||
* ...
|
||||
*
|
||||
* let pwd = "123456"; // source
|
||||
* set_password(pwd); // sink (argument 0)
|
||||
* ```
|
||||
*/
|
||||
module ConstantPasswordConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node node) {
|
||||
// `node` is a string literal
|
||||
node.asExpr() instanceof StringLiteralExpr
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node node) {
|
||||
// `node` is an argument whose corresponding parameter name matches the pattern "pass%"
|
||||
exists(CallExpr call, Function target, int argIndex, Variable v |
|
||||
call.getStaticTarget() = target and
|
||||
v.getParameter() = target.getParam(argIndex) and
|
||||
v.getText().matches("pass%") and
|
||||
call.getArg(argIndex) = node.asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// instantiate the data flow configuration as a global taint tracking module
|
||||
module ConstantPasswordFlow = TaintTracking::Global<ConstantPasswordConfig>;
|
||||
|
||||
// report flows from sources to sinks
|
||||
from DataFlow::Node sourceNode, DataFlow::Node sinkNode
|
||||
where ConstantPasswordFlow::flow(sourceNode, sinkNode)
|
||||
select sinkNode, "The value $@ is used as a constant password.", sourceNode, sourceNode.toString()
|
||||
39
rust/ql/examples/snippets/simple_sql_injection.ql
Normal file
39
rust/ql/examples/snippets/simple_sql_injection.ql
Normal file
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* @name Database query built from user-controlled sources
|
||||
* @description Finds places where a value from a remote or local user input
|
||||
* is used as the first argument of a call to `sqlx_core::query::query`.
|
||||
* @id rust/examples/simple-sql-injection
|
||||
* @tags example
|
||||
*/
|
||||
|
||||
import rust
|
||||
import codeql.rust.dataflow.DataFlow
|
||||
import codeql.rust.dataflow.TaintTracking
|
||||
import codeql.rust.Concepts
|
||||
|
||||
/**
|
||||
* A data flow configuration for tracking flow from a user input (threat model
|
||||
* source) to the first argument of a call to `sqlx_core::query::query`.
|
||||
*/
|
||||
module SqlInjectionConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node node) {
|
||||
// `node` is a user input (threat model source)
|
||||
node instanceof ActiveThreatModelSource
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node node) {
|
||||
// `node` is the first argument of a call to `sqlx_core::query::query`
|
||||
exists(CallExpr call |
|
||||
call.getStaticTarget().getCanonicalPath() = "sqlx_core::query::query" and
|
||||
call.getArg(0) = node.asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// instantiate the data flow configuration as a global taint tracking module
|
||||
module SqlInjectionFlow = TaintTracking::Global<SqlInjectionConfig>;
|
||||
|
||||
// report flows from sources to sinks
|
||||
from DataFlow::Node sourceNode, DataFlow::Node sinkNode
|
||||
where SqlInjectionFlow::flow(sourceNode, sinkNode)
|
||||
select sinkNode, "This query depends on a $@.", sourceNode, "user-provided value"
|
||||
@@ -7,4 +7,5 @@ mod a_module;
|
||||
fn main() {
|
||||
my_macro2!(); // $ item=my_macro2
|
||||
hello(); // $ item=HELLO
|
||||
lib::extern_crate_alias::a_module::hello(); // $ item=HELLO
|
||||
}
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
| exe/src/main.rs:7:1:10:1 | fn main |
|
||||
| exe/src/main.rs:7:1:11:1 | fn main |
|
||||
| lib/src/a_module/mod.rs:1:1:4:1 | fn hello |
|
||||
|
||||
@@ -15,3 +15,5 @@ mod macros {
|
||||
}
|
||||
|
||||
pub mod a_module;
|
||||
|
||||
pub extern crate self as extern_crate_alias;
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
| Inconsistencies - Path resolution | 0 |
|
||||
| Inconsistencies - SSA | 0 |
|
||||
| Inconsistencies - data flow | 0 |
|
||||
| Lines of code extracted | 21 |
|
||||
| Lines of user code extracted | 21 |
|
||||
| Lines of code extracted | 23 |
|
||||
| Lines of user code extracted | 23 |
|
||||
| Macro calls - resolved | 10 |
|
||||
| Macro calls - total | 10 |
|
||||
| Macro calls - unresolved | 0 |
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
| Inconsistencies - Path resolution | 0 |
|
||||
| Inconsistencies - SSA | 0 |
|
||||
| Inconsistencies - data flow | 0 |
|
||||
| Lines of code extracted | 21 |
|
||||
| Lines of user code extracted | 21 |
|
||||
| Lines of code extracted | 23 |
|
||||
| Lines of user code extracted | 23 |
|
||||
| Macro calls - resolved | 10 |
|
||||
| Macro calls - total | 10 |
|
||||
| Macro calls - unresolved | 0 |
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
## 0.1.20
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Added models for cookie methods in the `poem` crate.
|
||||
|
||||
## 0.1.19
|
||||
|
||||
### Major Analysis Improvements
|
||||
|
||||
4
rust/ql/lib/change-notes/2025-11-19-dataflow-ast.md
Normal file
4
rust/ql/lib/change-notes/2025-11-19-dataflow-ast.md
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: breaking
|
||||
---
|
||||
* The type `DataFlow::Node` is now based directly on the AST instead of the CFG, which means that predicates like `asExpr()` return AST nodes instead of CFG nodes.
|
||||
@@ -1,4 +1,5 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
## 0.1.20
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Added models for cookie methods in the `poem` crate.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.1.19
|
||||
lastReleaseVersion: 0.1.20
|
||||
|
||||
@@ -4,14 +4,13 @@
|
||||
* provide concrete subclasses.
|
||||
*/
|
||||
|
||||
private import rust
|
||||
private import codeql.rust.dataflow.DataFlow
|
||||
private import codeql.rust.dataflow.internal.DataFlowImpl
|
||||
private import codeql.Locations
|
||||
private import codeql.threatmodels.ThreatModels
|
||||
private import codeql.rust.Frameworks
|
||||
private import codeql.rust.dataflow.FlowSource
|
||||
private import codeql.rust.controlflow.ControlFlowGraph as Cfg
|
||||
private import codeql.rust.controlflow.CfgNodes as CfgNodes
|
||||
private import codeql.concepts.ConceptsShared
|
||||
|
||||
private module ConceptsShared = ConceptsMake<Location, RustDataFlow>;
|
||||
@@ -345,16 +344,16 @@ module Path {
|
||||
SafeAccessCheck() { this = DataFlow::BarrierGuard<safeAccessCheck/3>::getABarrierNode() }
|
||||
}
|
||||
|
||||
private predicate safeAccessCheck(CfgNodes::AstCfgNode g, Cfg::CfgNode node, boolean branch) {
|
||||
g.(SafeAccessCheck::Range).checks(node, branch)
|
||||
private predicate safeAccessCheck(AstNode g, Expr e, boolean branch) {
|
||||
g.(SafeAccessCheck::Range).checks(e, branch)
|
||||
}
|
||||
|
||||
/** Provides a class for modeling new path safety checks. */
|
||||
module SafeAccessCheck {
|
||||
/** A data-flow node that checks that a path is safe to access in some way, for example by having a controlled prefix. */
|
||||
abstract class Range extends CfgNodes::AstCfgNode {
|
||||
/** Holds if this guard validates `node` upon evaluating to `branch`. */
|
||||
abstract predicate checks(Cfg::CfgNode node, boolean branch);
|
||||
abstract class Range extends AstNode {
|
||||
/** Holds if this guard validates `e` upon evaluating to `branch`. */
|
||||
abstract predicate checks(Expr e, boolean branch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,8 +8,6 @@ private import codeql.dataflow.DataFlow
|
||||
private import internal.DataFlowImpl as DataFlowImpl
|
||||
private import internal.Node as Node
|
||||
private import internal.Content as Content
|
||||
private import codeql.rust.controlflow.ControlFlowGraph as Cfg
|
||||
private import codeql.rust.controlflow.CfgNodes as CfgNodes
|
||||
|
||||
/**
|
||||
* Provides classes for performing local (intra-procedural) and global
|
||||
@@ -68,7 +66,7 @@ module DataFlow {
|
||||
* For example, the guard `g` might be a call `isSafe(x)` and the expression `e`
|
||||
* the argument `x`.
|
||||
*/
|
||||
signature predicate guardChecksSig(CfgNodes::AstCfgNode g, Cfg::CfgNode e, boolean branch);
|
||||
signature predicate guardChecksSig(AstNode g, Expr e, boolean branch);
|
||||
|
||||
/**
|
||||
* Provides a set of barrier nodes for a guard that validates an expression.
|
||||
|
||||
@@ -9,7 +9,6 @@ module Ssa {
|
||||
private import rust
|
||||
private import codeql.rust.controlflow.BasicBlocks
|
||||
private import codeql.rust.controlflow.ControlFlowGraph
|
||||
private import codeql.rust.controlflow.CfgNodes
|
||||
private import codeql.rust.controlflow.internal.ControlFlowGraphImpl as CfgImpl
|
||||
private import internal.SsaImpl as SsaImpl
|
||||
|
||||
@@ -51,7 +50,7 @@ module Ssa {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
final CfgNode getARead() { result = SsaImpl::getARead(this) }
|
||||
final Expr getARead() { result = SsaImpl::getARead(this) }
|
||||
|
||||
/**
|
||||
* Gets a first control flow node that reads the value of this SSA definition.
|
||||
@@ -80,7 +79,7 @@ module Ssa {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
final CfgNode getAFirstRead() { SsaImpl::firstRead(this, result) }
|
||||
final Expr getAFirstRead() { SsaImpl::firstRead(this, result) }
|
||||
|
||||
/**
|
||||
* Holds if `read1` and `read2` are adjacent reads of this SSA definition.
|
||||
@@ -108,7 +107,7 @@ module Ssa {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
final predicate hasAdjacentReads(CfgNode read1, CfgNode read2) {
|
||||
final predicate hasAdjacentReads(Expr read1, Expr read2) {
|
||||
SsaImpl::adjacentReadPair(this, read1, read2)
|
||||
}
|
||||
|
||||
@@ -168,28 +167,28 @@ module Ssa {
|
||||
* ```
|
||||
*/
|
||||
class WriteDefinition extends Definition, SsaImpl::WriteDefinition {
|
||||
private CfgNode write;
|
||||
private AstNode write;
|
||||
|
||||
WriteDefinition() {
|
||||
exists(BasicBlock bb, int i, Variable v, CfgNode n |
|
||||
exists(BasicBlock bb, int i, Variable v, AstNode n |
|
||||
this.definesAt(v, bb, i) and
|
||||
SsaImpl::variableWriteActual(bb, i, v, n)
|
||||
SsaImpl::variableWriteActual(bb, i, v, n.getACfgNode())
|
||||
|
|
||||
write.(VariableAccessCfgNode).getAccess().getVariable() = v and
|
||||
write.(VariableAccess).getVariable() = v and
|
||||
(
|
||||
write = n.(AssignmentExprCfgNode).getAWriteAccess()
|
||||
write = n.(AssignmentExpr).getAWriteAccess()
|
||||
or
|
||||
write = n.(CompoundAssignmentExprCfgNode).getLhs()
|
||||
write = n.(CompoundAssignmentExpr).getLhs()
|
||||
)
|
||||
or
|
||||
not n instanceof AssignmentExprCfgNode and
|
||||
not n instanceof CompoundAssignmentExprCfgNode and
|
||||
not n instanceof AssignmentExpr and
|
||||
not n instanceof CompoundAssignmentExpr and
|
||||
write = n
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the underlying write access. */
|
||||
final CfgNode getWriteAccess() { result = write }
|
||||
final AstNode getWriteAccess() { result = write }
|
||||
|
||||
/**
|
||||
* Holds if this SSA definition assigns `value` to the underlying
|
||||
@@ -199,19 +198,19 @@ module Ssa {
|
||||
* `let` statement, `let x = value`. Note that patterns on the lhs. are
|
||||
* currently not supported.
|
||||
*/
|
||||
predicate assigns(ExprCfgNode value) {
|
||||
exists(AssignmentExprCfgNode ae |
|
||||
predicate assigns(Expr value) {
|
||||
exists(AssignmentExpr ae |
|
||||
ae.getLhs() = write and
|
||||
ae.getRhs() = value
|
||||
)
|
||||
or
|
||||
exists(IdentPatCfgNode pat | pat.getName() = write |
|
||||
exists(LetStmtCfgNode ls |
|
||||
exists(IdentPat pat | pat.getName() = write |
|
||||
exists(LetStmt ls |
|
||||
pat = ls.getPat() and
|
||||
ls.getInitializer() = value
|
||||
)
|
||||
or
|
||||
exists(LetExprCfgNode le |
|
||||
exists(LetExpr le |
|
||||
pat = le.getPat() and
|
||||
le.getScrutinee() = value
|
||||
)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
private import rust
|
||||
private import codeql.rust.controlflow.CfgNodes
|
||||
private import codeql.rust.frameworks.stdlib.Builtins
|
||||
private import DataFlowImpl
|
||||
|
||||
/**
|
||||
@@ -21,14 +21,18 @@ abstract class Content extends TContent {
|
||||
abstract class FieldContent extends Content {
|
||||
/** Gets an access to this field. */
|
||||
pragma[nomagic]
|
||||
abstract FieldExprCfgNode getAnAccess();
|
||||
abstract FieldExpr getAnAccess();
|
||||
}
|
||||
|
||||
/** A tuple field belonging to either a variant or a struct. */
|
||||
class TupleFieldContent extends FieldContent, TTupleFieldContent {
|
||||
private TupleField field;
|
||||
|
||||
TupleFieldContent() { this = TTupleFieldContent(field) }
|
||||
TupleFieldContent() {
|
||||
this = TTupleFieldContent(field) and
|
||||
// tuples are handled using the special `TupleContent` type
|
||||
not field = any(TupleType tt).getATupleField()
|
||||
}
|
||||
|
||||
/** Holds if this field belongs to an enum variant. */
|
||||
predicate isVariantField(Variant v, int pos) { field.isVariantField(v, pos) }
|
||||
@@ -36,7 +40,7 @@ class TupleFieldContent extends FieldContent, TTupleFieldContent {
|
||||
/** Holds if this field belongs to a struct. */
|
||||
predicate isStructField(Struct s, int pos) { field.isStructField(s, pos) }
|
||||
|
||||
override FieldExprCfgNode getAnAccess() { field = result.getFieldExpr().getTupleField() }
|
||||
override FieldExpr getAnAccess() { field = result.getTupleField() }
|
||||
|
||||
final override string toString() {
|
||||
exists(Variant v, int pos, string vname |
|
||||
@@ -69,7 +73,7 @@ class StructFieldContent extends FieldContent, TStructFieldContent {
|
||||
/** Holds if this field belongs to a struct. */
|
||||
predicate isStructField(Struct s, string name) { field.isStructField(s, name) }
|
||||
|
||||
override FieldExprCfgNode getAnAccess() { field = result.getFieldExpr().getStructField() }
|
||||
override FieldExpr getAnAccess() { field = result.getStructField() }
|
||||
|
||||
final override string toString() {
|
||||
exists(Variant v, string name, string vname |
|
||||
@@ -148,7 +152,7 @@ final class TuplePositionContent extends FieldContent, TTuplePositionContent {
|
||||
/** Gets the index of this tuple position. */
|
||||
int getPosition() { result = pos }
|
||||
|
||||
override FieldExprCfgNode getAnAccess() {
|
||||
override FieldExpr getAnAccess() {
|
||||
// TODO: limit to tuple types
|
||||
result.getIdentifier().getText().toInt() = pos
|
||||
}
|
||||
|
||||
@@ -29,17 +29,6 @@ private module Input implements InputSig<Location, RustDataFlow> {
|
||||
}
|
||||
|
||||
predicate missingLocationExclude(RustDataFlow::Node n) { not exists(n.asExpr().getLocation()) }
|
||||
|
||||
predicate multipleArgumentCallExclude(Node::ArgumentNode arg, DataFlowCall call) {
|
||||
// An argument such as `x` in `if !x { ... }` has two successors (and hence
|
||||
// two calls); one for each Boolean outcome of `x`.
|
||||
exists(CfgNodes::ExprCfgNode n |
|
||||
arg.isArgumentOf(call, _) and
|
||||
n = call.asCallCfgNode() and
|
||||
arg.asExpr().getASuccessor(any(ConditionalSuccessor c)).getASuccessor*() = n and
|
||||
n.getASplit() instanceof ConditionalCompletionSplitting::ConditionalCompletionSplit
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import MakeConsistency<Location, RustDataFlow, RustTaintTracking, Input>
|
||||
|
||||
@@ -14,7 +14,6 @@ private import codeql.rust.controlflow.internal.Scope as Scope
|
||||
private import codeql.rust.internal.PathResolution
|
||||
private import codeql.rust.internal.TypeInference as TypeInference
|
||||
private import codeql.rust.controlflow.ControlFlowGraph
|
||||
private import codeql.rust.controlflow.CfgNodes
|
||||
private import codeql.rust.dataflow.Ssa
|
||||
private import codeql.rust.dataflow.FlowSummary
|
||||
private import Node
|
||||
@@ -60,7 +59,7 @@ final class DataFlowCallable extends TDataFlowCallable {
|
||||
|
||||
final class DataFlowCall extends TDataFlowCall {
|
||||
/** Gets the underlying call in the CFG, if any. */
|
||||
CallCfgNode asCallCfgNode() { this = TCall(result) }
|
||||
Call asCall() { this = TCall(result) }
|
||||
|
||||
predicate isSummaryCall(
|
||||
FlowSummaryImpl::Public::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNode receiver
|
||||
@@ -69,13 +68,13 @@ final class DataFlowCall extends TDataFlowCall {
|
||||
}
|
||||
|
||||
DataFlowCallable getEnclosingCallable() {
|
||||
result.asCfgScope() = this.asCallCfgNode().getExpr().getEnclosingCfgScope()
|
||||
result.asCfgScope() = this.asCall().getEnclosingCfgScope()
|
||||
or
|
||||
this.isSummaryCall(result.asSummarizedCallable(), _)
|
||||
}
|
||||
|
||||
string toString() {
|
||||
result = this.asCallCfgNode().toString()
|
||||
result = this.asCall().toString()
|
||||
or
|
||||
exists(
|
||||
FlowSummaryImpl::Public::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNode receiver
|
||||
@@ -85,7 +84,7 @@ final class DataFlowCall extends TDataFlowCall {
|
||||
)
|
||||
}
|
||||
|
||||
Location getLocation() { result = this.asCallCfgNode().getLocation() }
|
||||
Location getLocation() { result = this.asCall().getLocation() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -146,13 +145,13 @@ final class ArgumentPosition extends ParameterPosition {
|
||||
* 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, CallCfgNode call, ParameterPosition pos) {
|
||||
predicate isArgumentForCall(Expr arg, Call call, ParameterPosition pos) {
|
||||
// TODO: Handle index expressions as calls in data flow.
|
||||
not call.getCall() instanceof IndexExpr and
|
||||
not call instanceof IndexExpr and
|
||||
(
|
||||
call.getPositionalArgument(pos.getPosition()) = arg
|
||||
or
|
||||
call.getReceiver() = arg and pos.isSelf() and not call.getCall().receiverImplicitlyBorrowed()
|
||||
call.getReceiver() = arg and pos.isSelf() and not call.receiverImplicitlyBorrowed()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -181,19 +180,24 @@ module SsaFlow {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a node that may execute last in `n`, and which, when it executes last,
|
||||
* will be the value of `n`.
|
||||
* Gets a node that may execute last in `e`, and which, when it executes last,
|
||||
* will be the value of `e`.
|
||||
*/
|
||||
private ExprCfgNode getALastEvalNode(ExprCfgNode e) {
|
||||
e = any(IfExprCfgNode n | result = [n.getThen(), n.getElse()]) or
|
||||
result = e.(LoopExprCfgNode).getLoopBody() or
|
||||
result = e.(ReturnExprCfgNode).getExpr() or
|
||||
result = e.(BreakExprCfgNode).getExpr() or
|
||||
result = e.(BlockExprCfgNode).getTailExpr() or
|
||||
result = e.(MacroBlockExprCfgNode).getTailExpr() or
|
||||
result = e.(MatchExprCfgNode).getArmExpr(_) or
|
||||
result = e.(MacroExprCfgNode).getMacroCall().(MacroCallCfgNode).getExpandedNode() or
|
||||
result.(BreakExprCfgNode).getTarget() = e
|
||||
private Expr getALastEvalNode(Expr e) {
|
||||
e = any(IfExpr n | result = [n.getThen(), n.getElse()]) or
|
||||
result = e.(LoopExpr).getLoopBody() or
|
||||
result = e.(ReturnExpr).getExpr() or
|
||||
result = e.(BreakExpr).getExpr() or
|
||||
e =
|
||||
any(BlockExpr be |
|
||||
not be.isAsync() and
|
||||
result = be.getTailExpr()
|
||||
) or
|
||||
result = e.(MacroBlockExpr).getTailExpr() or
|
||||
result = e.(MatchExpr).getAnArm().getExpr() or
|
||||
result = e.(MacroExpr).getMacroCall().getMacroCallExpansion() or
|
||||
result.(BreakExpr).getTarget() = e or
|
||||
result = e.(ParenExpr).getExpr()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -211,11 +215,11 @@ private ExprCfgNode getALastEvalNode(ExprCfgNode e) {
|
||||
* we add a reverse flow step from `[post] { foo(); &mut a}` to `[post] &mut a`,
|
||||
* in order for the side-effect of `set_data` to reach `&mut a`.
|
||||
*/
|
||||
ExprCfgNode getPostUpdateReverseStep(ExprCfgNode e, boolean preservesValue) {
|
||||
Expr getPostUpdateReverseStep(Expr e, boolean preservesValue) {
|
||||
result = getALastEvalNode(e) and
|
||||
preservesValue = true
|
||||
or
|
||||
result = e.(CastExprCfgNode).getExpr() and
|
||||
result = e.(CastExpr).getExpr() and
|
||||
preservesValue = false
|
||||
}
|
||||
|
||||
@@ -236,48 +240,48 @@ module LocalFlow {
|
||||
|
||||
pragma[nomagic]
|
||||
predicate localFlowStepCommon(Node nodeFrom, Node nodeTo) {
|
||||
nodeFrom.getCfgNode() = getALastEvalNode(nodeTo.getCfgNode())
|
||||
nodeFrom.asExpr() = getALastEvalNode(nodeTo.asExpr())
|
||||
or
|
||||
// An edge from the right-hand side of a let statement to the left-hand side.
|
||||
exists(LetStmtCfgNode s |
|
||||
nodeFrom.getCfgNode() = s.getInitializer() and
|
||||
nodeTo.getCfgNode() = s.getPat()
|
||||
exists(LetStmt s |
|
||||
nodeFrom.asExpr() = s.getInitializer() and
|
||||
nodeTo.asPat() = s.getPat()
|
||||
)
|
||||
or
|
||||
// An edge from the right-hand side of a let expression to the left-hand side.
|
||||
exists(LetExprCfgNode e |
|
||||
nodeFrom.getCfgNode() = e.getScrutinee() and
|
||||
nodeTo.getCfgNode() = e.getPat()
|
||||
exists(LetExpr e |
|
||||
nodeFrom.asExpr() = e.getScrutinee() and
|
||||
nodeTo.asPat() = e.getPat()
|
||||
)
|
||||
or
|
||||
exists(IdentPatCfgNode p |
|
||||
exists(IdentPat p |
|
||||
not p.isRef() and
|
||||
nodeFrom.getCfgNode() = p and
|
||||
nodeTo.getCfgNode() = p.getName()
|
||||
nodeFrom.asPat() = p and
|
||||
nodeTo.(NameNode).getName() = p.getName()
|
||||
)
|
||||
or
|
||||
exists(SelfParamCfgNode self |
|
||||
nodeFrom.getCfgNode() = self and
|
||||
nodeTo.getCfgNode() = self.getName()
|
||||
exists(SelfParam self |
|
||||
nodeFrom.asParameter() = self and
|
||||
nodeTo.(NameNode).getName() = self.getName()
|
||||
)
|
||||
or
|
||||
// An edge from a pattern/expression to its corresponding SSA definition.
|
||||
nodeFrom.(AstCfgFlowNode).getCfgNode() =
|
||||
nodeFrom.(AstNodeNode).getAstNode() =
|
||||
nodeTo.(SsaNode).asDefinition().(Ssa::WriteDefinition).getWriteAccess()
|
||||
or
|
||||
nodeFrom.(SourceParameterNode).getParameter().(ParamCfgNode).getPat() = nodeTo.asPat()
|
||||
nodeFrom.(SourceParameterNode).getParameter().(Param).getPat() = nodeTo.asPat()
|
||||
or
|
||||
exists(AssignmentExprCfgNode a |
|
||||
a.getRhs() = nodeFrom.getCfgNode() and
|
||||
a.getLhs() = nodeTo.getCfgNode()
|
||||
exists(AssignmentExpr a |
|
||||
a.getRhs() = nodeFrom.asExpr() and
|
||||
a.getLhs() = nodeTo.asExpr()
|
||||
)
|
||||
or
|
||||
exists(MatchExprCfgNode match |
|
||||
exists(MatchExpr match |
|
||||
nodeFrom.asExpr() = match.getScrutinee() and
|
||||
nodeTo.asPat() = match.getArmPat(_)
|
||||
nodeTo.asPat() = match.getAnArm().getPat()
|
||||
)
|
||||
or
|
||||
nodeFrom.asPat().(OrPatCfgNode).getAPat() = nodeTo.asPat()
|
||||
nodeFrom.asPat().(OrPat).getAPat() = nodeTo.asPat()
|
||||
or
|
||||
// Simple value step from receiver expression to receiver node, in case
|
||||
// there is no implicit deref or borrow operation.
|
||||
@@ -305,12 +309,10 @@ predicate lambdaCreationExpr(Expr creation) {
|
||||
* Holds if `call` is a lambda call of kind `kind` where `receiver` is the
|
||||
* invoked expression.
|
||||
*/
|
||||
predicate lambdaCallExpr(CallExprCfgNode call, LambdaCallKind kind, ExprCfgNode receiver) {
|
||||
predicate lambdaCallExpr(CallExpr call, LambdaCallKind kind, Expr receiver) {
|
||||
receiver = call.getFunction() and
|
||||
// All calls to complex expressions and local variable accesses are lambda call.
|
||||
exists(Expr f | f = receiver.getExpr() |
|
||||
f instanceof PathExpr implies f = any(Variable v).getAnAccess()
|
||||
) and
|
||||
(receiver instanceof PathExpr implies receiver = any(Variable v).getAnAccess()) and
|
||||
exists(kind)
|
||||
}
|
||||
|
||||
@@ -379,19 +381,27 @@ module RustDataFlow implements InputSig<Location> {
|
||||
node instanceof CaptureNode or
|
||||
node instanceof ClosureParameterNode or
|
||||
node instanceof ReceiverNode or
|
||||
node instanceof ReceiverPostUpdateNode
|
||||
node.asExpr() instanceof ParenExpr or
|
||||
nodeIsHidden(node.(PostUpdateNode).getPreUpdateNode())
|
||||
}
|
||||
|
||||
private Expr stripParens(Expr e) {
|
||||
not e instanceof ParenExpr and
|
||||
result = e
|
||||
or
|
||||
result = stripParens(e.(ParenExpr).getExpr())
|
||||
}
|
||||
|
||||
predicate neverSkipInPathGraph(Node node) {
|
||||
node.(Node::Node).getCfgNode() = any(LetStmtCfgNode s).getPat()
|
||||
node.(Node::Node).asPat() = any(LetStmt s).getPat()
|
||||
or
|
||||
node.(Node::Node).getCfgNode() = any(LetExprCfgNode e).getPat()
|
||||
node.(Node::Node).asPat() = any(LetExpr e).getPat()
|
||||
or
|
||||
node.(Node::Node).getCfgNode() = any(AssignmentExprCfgNode a).getLhs()
|
||||
node.(Node::Node).asExpr() = stripParens(any(AssignmentExpr a).getLhs())
|
||||
or
|
||||
exists(MatchExprCfgNode match |
|
||||
node.asExpr() = match.getScrutinee() or
|
||||
node.asExpr() = match.getArmPat(_)
|
||||
exists(MatchExpr match |
|
||||
node.asExpr() = stripParens(match.getScrutinee()) or
|
||||
node.asPat() = match.getAnArm().getPat()
|
||||
)
|
||||
or
|
||||
FlowSummaryImpl::Private::Steps::sourceLocalStep(_, node, _)
|
||||
@@ -399,7 +409,7 @@ module RustDataFlow implements InputSig<Location> {
|
||||
FlowSummaryImpl::Private::Steps::sinkLocalStep(node, _, _)
|
||||
}
|
||||
|
||||
class DataFlowExpr = ExprCfgNode;
|
||||
class DataFlowExpr = Expr;
|
||||
|
||||
/** Gets the node corresponding to `e`. */
|
||||
Node exprNode(DataFlowExpr e) { result.asExpr() = e }
|
||||
@@ -412,7 +422,7 @@ module RustDataFlow implements InputSig<Location> {
|
||||
|
||||
/** Gets a viable implementation of the target of the given `Call`. */
|
||||
DataFlowCallable viableCallable(DataFlowCall call) {
|
||||
exists(Call c | c = call.asCallCfgNode().getCall() |
|
||||
exists(Call c | c = call.asCall() |
|
||||
result.asCfgScope() = c.getARuntimeTarget()
|
||||
or
|
||||
exists(SummarizedCallable sc, Function staticTarget |
|
||||
@@ -511,79 +521,79 @@ module RustDataFlow implements InputSig<Location> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate implicitDerefToReceiver(Node node1, ReceiverNode node2, ReferenceContent c) {
|
||||
TypeInference::receiverHasImplicitDeref(node1.asExpr().getExpr()) and
|
||||
TypeInference::receiverHasImplicitDeref(node1.asExpr()) and
|
||||
node1.asExpr() = node2.getReceiver() and
|
||||
exists(c)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate implicitBorrowToReceiver(Node node1, ReceiverNode node2, ReferenceContent c) {
|
||||
TypeInference::receiverHasImplicitBorrow(node1.asExpr().getExpr()) and
|
||||
TypeInference::receiverHasImplicitBorrow(node1.asExpr()) and
|
||||
node1.asExpr() = node2.getReceiver() and
|
||||
exists(c)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate referenceExprToExpr(Node node1, Node node2, ReferenceContent c) {
|
||||
node1.asExpr() = node2.asExpr().(RefExprCfgNode).getExpr() and
|
||||
node1.asExpr() = node2.asExpr().(RefExpr).getExpr() and
|
||||
exists(c)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
additional predicate readContentStep(Node node1, Content c, Node node2) {
|
||||
exists(TupleStructPatCfgNode pat, int pos |
|
||||
exists(TupleStructPat pat, int pos |
|
||||
pat = node1.asPat() and
|
||||
node2.asPat() = pat.getField(pos) and
|
||||
c = TTupleFieldContent(pat.getTupleStructPat().getTupleField(pos))
|
||||
c = TTupleFieldContent(pat.getTupleField(pos))
|
||||
)
|
||||
or
|
||||
exists(TuplePatCfgNode pat, int pos |
|
||||
exists(TuplePat pat, int pos |
|
||||
pos = c.(TuplePositionContent).getPosition() and
|
||||
node1.asPat() = pat and
|
||||
node2.asPat() = pat.getField(pos)
|
||||
)
|
||||
or
|
||||
exists(StructPatCfgNode pat, string field |
|
||||
exists(StructPat pat, string field |
|
||||
pat = node1.asPat() and
|
||||
c = TStructFieldContent(pat.getStructPat().getStructField(field)) and
|
||||
node2.asPat() = pat.getFieldPat(field)
|
||||
c = TStructFieldContent(pat.getStructField(field)) and
|
||||
node2.asPat() = pat.getPatField(field).getPat()
|
||||
)
|
||||
or
|
||||
c instanceof ReferenceContent and
|
||||
node1.asPat().(RefPatCfgNode).getPat() = node2.asPat()
|
||||
node1.asPat().(RefPat).getPat() = node2.asPat()
|
||||
or
|
||||
exists(FieldExprCfgNode access |
|
||||
exists(FieldExpr access |
|
||||
node1.asExpr() = access.getContainer() and
|
||||
node2.asExpr() = access and
|
||||
access = c.(FieldContent).getAnAccess()
|
||||
)
|
||||
or
|
||||
exists(IndexExprCfgNode arr |
|
||||
exists(IndexExpr arr |
|
||||
c instanceof ElementContent and
|
||||
node1.asExpr() = arr.getBase() and
|
||||
node2.asExpr() = arr
|
||||
)
|
||||
or
|
||||
exists(ForExprCfgNode for |
|
||||
exists(ForExpr for |
|
||||
c instanceof ElementContent and
|
||||
node1.asExpr() = for.getIterable() and
|
||||
node2.asPat() = for.getPat()
|
||||
)
|
||||
or
|
||||
exists(SlicePatCfgNode pat |
|
||||
exists(SlicePat pat |
|
||||
c instanceof ElementContent and
|
||||
node1.asPat() = pat and
|
||||
node2.asPat() = pat.getAPat()
|
||||
)
|
||||
or
|
||||
exists(TryExprCfgNode try |
|
||||
exists(TryExpr try |
|
||||
node1.asExpr() = try.getExpr() and
|
||||
node2.asExpr() = try and
|
||||
c.(TupleFieldContent)
|
||||
.isVariantField([any(OptionEnum o).getSome(), any(ResultEnum r).getOk()], 0)
|
||||
)
|
||||
or
|
||||
exists(PrefixExprCfgNode deref |
|
||||
exists(PrefixExpr deref |
|
||||
c instanceof ReferenceContent and
|
||||
deref.getOperatorName() = "*" and
|
||||
node1.asExpr() = deref.getExpr() and
|
||||
@@ -597,7 +607,7 @@ module RustDataFlow implements InputSig<Location> {
|
||||
c instanceof FunctionCallReturnContent
|
||||
)
|
||||
or
|
||||
exists(AwaitExprCfgNode await |
|
||||
exists(AwaitExpr await |
|
||||
c instanceof FutureContent and
|
||||
node1.asExpr() = await.getExpr() and
|
||||
node2.asExpr() = await
|
||||
@@ -644,7 +654,7 @@ module RustDataFlow implements InputSig<Location> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fieldAssignment(Node node1, Node node2, FieldContent c) {
|
||||
exists(AssignmentExprCfgNode assignment, FieldExprCfgNode access |
|
||||
exists(AssignmentExpr assignment, FieldExpr access |
|
||||
assignment.getLhs() = access and
|
||||
node1.asExpr() = assignment.getRhs() and
|
||||
node2.asExpr() = access.getContainer() and
|
||||
@@ -654,7 +664,7 @@ module RustDataFlow implements InputSig<Location> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate referenceAssignment(Node node1, Node node2, ReferenceContent c) {
|
||||
exists(AssignmentExprCfgNode assignment, PrefixExprCfgNode deref |
|
||||
exists(AssignmentExpr assignment, PrefixExpr deref |
|
||||
assignment.getLhs() = deref and
|
||||
deref.getOperatorName() = "*" and
|
||||
node1.asExpr() = assignment.getRhs() and
|
||||
@@ -665,19 +675,19 @@ module RustDataFlow implements InputSig<Location> {
|
||||
|
||||
pragma[nomagic]
|
||||
additional predicate storeContentStep(Node node1, Content c, Node node2) {
|
||||
exists(CallExprCfgNode call, int pos |
|
||||
node1.asExpr() = call.getArgument(pragma[only_bind_into](pos)) and
|
||||
exists(CallExpr call, int pos |
|
||||
node1.asExpr() = call.getArg(pragma[only_bind_into](pos)) and
|
||||
node2.asExpr() = call and
|
||||
c = TTupleFieldContent(call.getCallExpr().getTupleField(pragma[only_bind_into](pos)))
|
||||
c = TTupleFieldContent(call.getTupleField(pragma[only_bind_into](pos)))
|
||||
)
|
||||
or
|
||||
exists(StructExprCfgNode re, string field |
|
||||
c = TStructFieldContent(re.getStructExpr().getStructField(field)) and
|
||||
node1.asExpr() = re.getFieldExpr(field) and
|
||||
exists(StructExpr re, string field |
|
||||
c = TStructFieldContent(re.getStructField(field)) and
|
||||
node1.asExpr() = re.getFieldExpr(field).getExpr() and
|
||||
node2.asExpr() = re
|
||||
)
|
||||
or
|
||||
exists(TupleExprCfgNode tuple |
|
||||
exists(TupleExpr tuple |
|
||||
node1.asExpr() = tuple.getField(c.(TuplePositionContent).getPosition()) and
|
||||
node2.asExpr() = tuple
|
||||
)
|
||||
@@ -685,23 +695,23 @@ module RustDataFlow implements InputSig<Location> {
|
||||
c instanceof ElementContent and
|
||||
node1.asExpr() =
|
||||
[
|
||||
node2.asExpr().(ArrayRepeatExprCfgNode).getRepeatOperand(),
|
||||
node2.asExpr().(ArrayListExprCfgNode).getAnExpr()
|
||||
node2.asExpr().(ArrayRepeatExpr).getRepeatOperand(),
|
||||
node2.asExpr().(ArrayListExpr).getAnExpr()
|
||||
]
|
||||
or
|
||||
// Store from a `ref` identifier pattern into the contained name.
|
||||
exists(IdentPatCfgNode p |
|
||||
exists(IdentPat p |
|
||||
c instanceof ReferenceContent and
|
||||
p.isRef() and
|
||||
node1.asPat() = p and
|
||||
node2.(NameNode).asName() = p.getName()
|
||||
node2.(NameNode).getName() = p.getName()
|
||||
)
|
||||
or
|
||||
fieldAssignment(node1, node2.(PostUpdateNode).getPreUpdateNode(), c)
|
||||
or
|
||||
referenceAssignment(node1, node2.(PostUpdateNode).getPreUpdateNode(), c)
|
||||
or
|
||||
exists(AssignmentExprCfgNode assignment, IndexExprCfgNode index |
|
||||
exists(AssignmentExpr assignment, IndexExpr index |
|
||||
c instanceof ElementContent and
|
||||
assignment.getLhs() = index and
|
||||
node1.asExpr() = assignment.getRhs() and
|
||||
@@ -808,7 +818,7 @@ module RustDataFlow implements InputSig<Location> {
|
||||
/** Holds if `creation` is an expression that creates a lambda of kind `kind` for `c`. */
|
||||
predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c) {
|
||||
exists(kind) and
|
||||
exists(Expr e | e = creation.asExpr().getExpr() |
|
||||
exists(Expr e | e = creation.asExpr() |
|
||||
lambdaCreationExpr(e) and e = c.asCfgScope()
|
||||
or
|
||||
// A path expression, that resolves to a function, evaluates to a function
|
||||
@@ -825,9 +835,9 @@ module RustDataFlow implements InputSig<Location> {
|
||||
*/
|
||||
predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) {
|
||||
(
|
||||
receiver.asExpr() = call.asCallCfgNode().(CallExprCfgNode).getFunction() and
|
||||
receiver.asExpr() = call.asCall().(CallExpr).getFunction() and
|
||||
// All calls to complex expressions and local variable accesses are lambda call.
|
||||
exists(Expr f | f = receiver.asExpr().getExpr() |
|
||||
exists(Expr f | f = receiver.asExpr() |
|
||||
f instanceof PathExpr implies f = any(Variable v).getAnAccess()
|
||||
)
|
||||
or
|
||||
@@ -854,7 +864,7 @@ module VariableCapture {
|
||||
private import codeql.dataflow.VariableCapture as SharedVariableCapture
|
||||
private import codeql.rust.controlflow.BasicBlocks as BasicBlocks
|
||||
|
||||
private predicate closureFlowStep(ExprCfgNode e1, ExprCfgNode e2) {
|
||||
private predicate closureFlowStep(Expr e1, Expr e2) {
|
||||
Stages::DataFlowStage::ref() and
|
||||
e1 = getALastEvalNode(e2)
|
||||
or
|
||||
@@ -883,48 +893,48 @@ module VariableCapture {
|
||||
|
||||
CapturedParameter() { p = this.getParameter() }
|
||||
|
||||
SourceParameterNode getParameterNode() { result.getParameter().getParamBase() = p }
|
||||
SourceParameterNode getParameterNode() { result.getParameter() = p }
|
||||
}
|
||||
|
||||
class Expr extends CfgNode {
|
||||
predicate hasCfgNode(BasicBlocks::BasicBlock bb, int i) { this = bb.getNode(i) }
|
||||
class Expr extends AstNode {
|
||||
predicate hasCfgNode(BasicBlocks::BasicBlock bb, int i) { this = bb.getNode(i).getAstNode() }
|
||||
}
|
||||
|
||||
class VariableWrite extends Expr {
|
||||
ExprCfgNode source;
|
||||
Expr source;
|
||||
CapturedVariable v;
|
||||
|
||||
VariableWrite() {
|
||||
exists(AssignmentExprCfgNode assign, Variable::VariableWriteAccess write |
|
||||
exists(AssignmentExpr assign, Variable::VariableWriteAccess write |
|
||||
this = assign and
|
||||
v = write.getVariable() and
|
||||
assign.getLhs().getExpr() = write and
|
||||
assign.getLhs() = write and
|
||||
assign.getRhs() = source
|
||||
)
|
||||
or
|
||||
exists(LetStmtCfgNode ls |
|
||||
this = ls and
|
||||
v.getPat() = ls.getPat().getPat() and
|
||||
ls.getInitializer() = source
|
||||
)
|
||||
this =
|
||||
any(LetStmt ls |
|
||||
v.getPat() = ls.getPat() and
|
||||
ls.getInitializer() = source
|
||||
)
|
||||
or
|
||||
exists(LetExprCfgNode le |
|
||||
this = le and
|
||||
v.getPat() = le.getPat().getPat() and
|
||||
le.getScrutinee() = source
|
||||
)
|
||||
this =
|
||||
any(LetExpr le |
|
||||
v.getPat() = le.getPat() and
|
||||
le.getScrutinee() = source
|
||||
)
|
||||
}
|
||||
|
||||
CapturedVariable getVariable() { result = v }
|
||||
|
||||
ExprCfgNode getSource() { result = source }
|
||||
Expr getSource() { result = source }
|
||||
}
|
||||
|
||||
class VariableRead extends Expr instanceof ExprCfgNode {
|
||||
class VariableRead extends Expr {
|
||||
CapturedVariable v;
|
||||
|
||||
VariableRead() {
|
||||
exists(VariableAccess read | this.getExpr() = read and v = read.getVariable() |
|
||||
exists(VariableAccess read | this = read and v = read.getVariable() |
|
||||
read instanceof VariableReadAccess
|
||||
or
|
||||
read = any(RefExpr re).getExpr()
|
||||
@@ -934,10 +944,10 @@ module VariableCapture {
|
||||
CapturedVariable getVariable() { result = v }
|
||||
}
|
||||
|
||||
class ClosureExpr extends Expr instanceof ExprCfgNode {
|
||||
ClosureExpr() { lambdaCreationExpr(super.getExpr()) }
|
||||
class ClosureExpr extends Expr {
|
||||
ClosureExpr() { lambdaCreationExpr(this) }
|
||||
|
||||
predicate hasBody(Callable body) { body = super.getExpr() }
|
||||
predicate hasBody(Callable body) { body = this }
|
||||
|
||||
predicate hasAliasedAccess(Expr f) { closureFlowStep+(this, f) and not closureFlowStep(f, _) }
|
||||
}
|
||||
@@ -991,10 +1001,11 @@ private module Cached {
|
||||
|
||||
cached
|
||||
newtype TDataFlowCall =
|
||||
TCall(CallCfgNode c) {
|
||||
TCall(Call call) {
|
||||
Stages::DataFlowStage::ref() and
|
||||
call.hasEnclosingCfgScope() and
|
||||
// TODO: Handle index expressions as calls in data flow.
|
||||
not c.getCall() instanceof IndexExpr
|
||||
not call instanceof IndexExpr
|
||||
} or
|
||||
TSummaryCall(
|
||||
FlowSummaryImpl::Public::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNode receiver
|
||||
|
||||
@@ -9,7 +9,6 @@ private import codeql.rust.dataflow.internal.DataFlowImpl
|
||||
private import codeql.rust.internal.PathResolution
|
||||
private import codeql.rust.dataflow.FlowSummary
|
||||
private import codeql.rust.dataflow.Ssa
|
||||
private import codeql.rust.controlflow.CfgNodes
|
||||
private import Content
|
||||
|
||||
module Input implements InputSig<Location, RustDataFlow> {
|
||||
@@ -132,9 +131,7 @@ module Input implements InputSig<Location, RustDataFlow> {
|
||||
private import Make<Location, RustDataFlow, Input> as Impl
|
||||
|
||||
private module StepsInput implements Impl::Private::StepsInputSig {
|
||||
DataFlowCall getACall(Public::SummarizedCallable sc) {
|
||||
result.asCallCfgNode().getCall().getStaticTarget() = sc
|
||||
}
|
||||
DataFlowCall getACall(Public::SummarizedCallable sc) { result.asCall().getStaticTarget() = sc }
|
||||
|
||||
/** Gets the argument of `source` described by `sc`, if any. */
|
||||
private Expr getSourceNodeArgument(Input::SourceBase source, Impl::Private::SummaryComponent sc) {
|
||||
@@ -151,10 +148,9 @@ private module StepsInput implements Impl::Private::StepsInputSig {
|
||||
result = expr.(ClosureExpr)
|
||||
or
|
||||
// The expression is an SSA read of an assignment of a closure
|
||||
exists(Ssa::Definition def, ExprCfgNode value |
|
||||
def.getARead().getAstNode() = expr and
|
||||
def.getAnUltimateDefinition().(Ssa::WriteDefinition).assigns(value) and
|
||||
result = value.getExpr().(ClosureExpr)
|
||||
exists(Ssa::Definition def |
|
||||
def.getARead() = expr and
|
||||
def.getAnUltimateDefinition().(Ssa::WriteDefinition).assigns(result.(ClosureExpr))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -164,7 +160,7 @@ private module StepsInput implements Impl::Private::StepsInputSig {
|
||||
|
||||
RustDataFlow::Node getSourceNode(Input::SourceBase source, Impl::Private::SummaryComponentStack s) {
|
||||
s.head() = Impl::Private::SummaryComponent::return(_) and
|
||||
result.asExpr().getExpr() = source.getCall()
|
||||
result.asExpr() = source.getCall()
|
||||
or
|
||||
exists(ArgumentPosition pos, Expr arg |
|
||||
s.head() = Impl::Private::SummaryComponent::parameter(pos) and
|
||||
@@ -172,13 +168,13 @@ private module StepsInput implements Impl::Private::StepsInputSig {
|
||||
result.asParameter() = getCallable(arg).getParam(pos.getPosition())
|
||||
)
|
||||
or
|
||||
result.(RustDataFlow::PostUpdateNode).getPreUpdateNode().asExpr().getExpr() =
|
||||
result.(RustDataFlow::PostUpdateNode).getPreUpdateNode().asExpr() =
|
||||
getSourceNodeArgument(source, s.headOfSingleton())
|
||||
}
|
||||
|
||||
RustDataFlow::Node getSinkNode(Input::SinkBase sink, Impl::Private::SummaryComponent sc) {
|
||||
exists(CallExprBase call, Expr arg, ArgumentPosition pos |
|
||||
result.asExpr().getExpr() = arg and
|
||||
result.asExpr() = arg and
|
||||
sc = Impl::Private::SummaryComponent::argument(pos) and
|
||||
call = sink.getCall() and
|
||||
arg = pos.getArgument(call)
|
||||
|
||||
@@ -35,17 +35,17 @@ class NodePublic extends TNode {
|
||||
/**
|
||||
* Gets the expression that corresponds to this node, if any.
|
||||
*/
|
||||
final ExprCfgNode asExpr() { this = TExprNode(result) }
|
||||
final Expr asExpr() { this = TExprNode(result) }
|
||||
|
||||
/**
|
||||
* Gets the parameter that corresponds to this node, if any.
|
||||
*/
|
||||
ParamBase asParameter() { result = this.(SourceParameterNode).getParameter().getParamBase() }
|
||||
ParamBase asParameter() { result = this.(SourceParameterNode).getParameter() }
|
||||
|
||||
/**
|
||||
* Gets the pattern that corresponds to this node, if any.
|
||||
*/
|
||||
final PatCfgNode asPat() { this = TPatNode(result) }
|
||||
final Pat asPat() { this = TPatNode(result) }
|
||||
}
|
||||
|
||||
abstract class Node extends NodePublic {
|
||||
@@ -56,9 +56,9 @@ abstract class Node extends NodePublic {
|
||||
abstract CfgScope getCfgScope();
|
||||
|
||||
/**
|
||||
* Gets the control flow node that corresponds to this data flow node.
|
||||
* Gets the AST node that corresponds to this data flow node, if any.
|
||||
*/
|
||||
CfgNode getCfgNode() { none() }
|
||||
AstNode getAstNode() { none() }
|
||||
}
|
||||
|
||||
/** A data flow node used to model flow summaries. */
|
||||
@@ -118,17 +118,17 @@ class FlowSummaryNode extends Node, TFlowSummaryNode {
|
||||
}
|
||||
}
|
||||
|
||||
/** A data flow node that corresponds directly to a CFG node for an AST node. */
|
||||
abstract class AstCfgFlowNode extends Node {
|
||||
AstCfgNode n;
|
||||
/** A data flow node that corresponds directly to an AST node. */
|
||||
abstract class AstNodeNode extends Node {
|
||||
AstNode n;
|
||||
|
||||
final override CfgNode getCfgNode() { result = n }
|
||||
final override AstNode getAstNode() { result = n }
|
||||
|
||||
final override CfgScope getCfgScope() { result = n.getAstNode().getEnclosingCfgScope() }
|
||||
final override CfgScope getCfgScope() { result = n.getEnclosingCfgScope() }
|
||||
|
||||
final override Location getLocation() { result = n.getAstNode().getLocation() }
|
||||
final override Location getLocation() { result = n.getLocation() }
|
||||
|
||||
final override string toString() { result = n.getAstNode().toString() }
|
||||
final override string toString() { result = n.toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -139,25 +139,25 @@ abstract class AstCfgFlowNode extends Node {
|
||||
* to multiple `ExprNode`s, just like it may correspond to multiple
|
||||
* `ControlFlow::Node`s.
|
||||
*/
|
||||
class ExprNode extends AstCfgFlowNode, TExprNode {
|
||||
override ExprCfgNode n;
|
||||
class ExprNode extends AstNodeNode, TExprNode {
|
||||
override Expr n;
|
||||
|
||||
ExprNode() { this = TExprNode(n) }
|
||||
}
|
||||
|
||||
final class PatNode extends AstCfgFlowNode, TPatNode {
|
||||
override PatCfgNode n;
|
||||
final class PatNode extends AstNodeNode, TPatNode {
|
||||
override Pat n;
|
||||
|
||||
PatNode() { this = TPatNode(n) }
|
||||
}
|
||||
|
||||
/** A data flow node that corresponds to a name node in the CFG. */
|
||||
final class NameNode extends AstCfgFlowNode, TNameNode {
|
||||
override NameCfgNode n;
|
||||
final class NameNode extends AstNodeNode, TNameNode {
|
||||
override Name n;
|
||||
|
||||
NameNode() { this = TNameNode(n) }
|
||||
|
||||
NameCfgNode asName() { result = n }
|
||||
Name getName() { result = n }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -169,20 +169,20 @@ abstract class ParameterNode extends Node {
|
||||
abstract predicate isParameterOf(DataFlowCallable c, ParameterPosition pos);
|
||||
}
|
||||
|
||||
final class SourceParameterNode extends AstCfgFlowNode, ParameterNode, TSourceParameterNode {
|
||||
override ParamBaseCfgNode n;
|
||||
final class SourceParameterNode extends AstNodeNode, ParameterNode, TSourceParameterNode {
|
||||
override ParamBase n;
|
||||
|
||||
SourceParameterNode() { this = TSourceParameterNode(n) }
|
||||
|
||||
override predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) {
|
||||
n.getAstNode() = pos.getParameterIn(c.asCfgScope().(Callable).getParamList())
|
||||
n = pos.getParameterIn(c.asCfgScope().(Callable).getParamList())
|
||||
}
|
||||
|
||||
/** Get the parameter position of this parameter. */
|
||||
ParameterPosition getPosition() { this.isParameterOf(_, result) }
|
||||
|
||||
/** Gets the parameter in the CFG that this node corresponds to. */
|
||||
ParamBaseCfgNode getParameter() { result = n }
|
||||
ParamBase getParameter() { result = n }
|
||||
}
|
||||
|
||||
/** A parameter for a library callable with a flow summary. */
|
||||
@@ -223,13 +223,13 @@ abstract class ArgumentNode extends Node {
|
||||
}
|
||||
|
||||
final class ExprArgumentNode extends ArgumentNode, ExprNode {
|
||||
private CallCfgNode call_;
|
||||
private Call call_;
|
||||
private RustDataFlow::ArgumentPosition pos_;
|
||||
|
||||
ExprArgumentNode() { isArgumentForCall(n, call_, pos_) }
|
||||
|
||||
override predicate isArgumentOf(DataFlowCall call, RustDataFlow::ArgumentPosition pos) {
|
||||
call.asCallCfgNode() = call_ and pos = pos_
|
||||
call.asCall() = call_ and pos = pos_
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,19 +238,19 @@ final class ExprArgumentNode extends ArgumentNode, ExprNode {
|
||||
* has taken place.
|
||||
*/
|
||||
final class ReceiverNode extends ArgumentNode, TReceiverNode {
|
||||
private CallCfgNode n;
|
||||
private Call n;
|
||||
|
||||
ReceiverNode() { this = TReceiverNode(n, false) }
|
||||
|
||||
ExprCfgNode getReceiver() { result = n.getReceiver() }
|
||||
Expr getReceiver() { result = n.getReceiver() }
|
||||
|
||||
MethodCallExprCfgNode getMethodCall() { result = n }
|
||||
MethodCallExpr getMethodCall() { result = n }
|
||||
|
||||
override predicate isArgumentOf(DataFlowCall call, RustDataFlow::ArgumentPosition pos) {
|
||||
call.asCallCfgNode() = n and pos = TSelfParameterPosition()
|
||||
call.asCall() = n and pos = TSelfParameterPosition()
|
||||
}
|
||||
|
||||
override CfgScope getCfgScope() { result = n.getAstNode().getEnclosingCfgScope() }
|
||||
override CfgScope getCfgScope() { result = n.getEnclosingCfgScope() }
|
||||
|
||||
override Location getLocation() { result = this.getReceiver().getLocation() }
|
||||
|
||||
@@ -275,12 +275,12 @@ final class SummaryArgumentNode extends FlowSummaryNode, ArgumentNode {
|
||||
* passed into the closure body at an invocation.
|
||||
*/
|
||||
final class ClosureArgumentNode extends ArgumentNode, ExprNode {
|
||||
private CallExprCfgNode call_;
|
||||
private CallExpr call_;
|
||||
|
||||
ClosureArgumentNode() { lambdaCallExpr(call_, _, this.asExpr()) }
|
||||
|
||||
override predicate isArgumentOf(DataFlowCall call, RustDataFlow::ArgumentPosition pos) {
|
||||
call.asCallCfgNode() = call_ and
|
||||
call.asCall() = call_ and
|
||||
pos.isClosureSelf()
|
||||
}
|
||||
}
|
||||
@@ -309,7 +309,7 @@ abstract class ReturnNode extends Node {
|
||||
}
|
||||
|
||||
final class ExprReturnNode extends ExprNode, ReturnNode {
|
||||
ExprReturnNode() { this.getCfgNode().getASuccessor() instanceof AnnotatedExitCfgNode }
|
||||
ExprReturnNode() { n.getACfgNode().getASuccessor() instanceof AnnotatedExitCfgNode }
|
||||
|
||||
override ReturnKind getKind() { result = TNormalReturnKind() }
|
||||
}
|
||||
@@ -329,11 +329,11 @@ abstract class OutNode extends Node {
|
||||
}
|
||||
|
||||
final private class ExprOutNode extends ExprNode, OutNode {
|
||||
ExprOutNode() { this.asExpr() instanceof CallCfgNode }
|
||||
ExprOutNode() { this.asExpr() instanceof Call }
|
||||
|
||||
/** Gets the underlying call CFG node that includes this out node. */
|
||||
override DataFlowCall getCall(ReturnKind kind) {
|
||||
result.asCallCfgNode() = this.getCfgNode() and
|
||||
result.asCall() = n and
|
||||
kind = TNormalReturnKind()
|
||||
}
|
||||
}
|
||||
@@ -391,27 +391,27 @@ abstract class PostUpdateNode extends PostUpdateNodePublic, Node {
|
||||
}
|
||||
|
||||
final class ExprPostUpdateNode extends PostUpdateNode, TExprPostUpdateNode {
|
||||
private ExprCfgNode n;
|
||||
private Expr e;
|
||||
|
||||
ExprPostUpdateNode() { this = TExprPostUpdateNode(n) }
|
||||
ExprPostUpdateNode() { this = TExprPostUpdateNode(e) }
|
||||
|
||||
override Node getPreUpdateNode() { result = TExprNode(n) }
|
||||
override Node getPreUpdateNode() { result = TExprNode(e) }
|
||||
|
||||
override CfgScope getCfgScope() { result = n.getScope() }
|
||||
override CfgScope getCfgScope() { result = e.getEnclosingCfgScope() }
|
||||
|
||||
override Location getLocation() { result = n.getLocation() }
|
||||
override Location getLocation() { result = e.getLocation() }
|
||||
}
|
||||
|
||||
final class ReceiverPostUpdateNode extends PostUpdateNode, TReceiverNode {
|
||||
private CallCfgNode n;
|
||||
private Call call;
|
||||
|
||||
ReceiverPostUpdateNode() { this = TReceiverNode(n, true) }
|
||||
ReceiverPostUpdateNode() { this = TReceiverNode(call, true) }
|
||||
|
||||
override Node getPreUpdateNode() { result = TReceiverNode(n, false) }
|
||||
override Node getPreUpdateNode() { result = TReceiverNode(call, false) }
|
||||
|
||||
override CfgScope getCfgScope() { result = n.getAstNode().getEnclosingCfgScope() }
|
||||
override CfgScope getCfgScope() { result = call.getEnclosingCfgScope() }
|
||||
|
||||
override Location getLocation() { result = n.getReceiver().getLocation() }
|
||||
override Location getLocation() { result = call.getReceiver().getLocation() }
|
||||
}
|
||||
|
||||
final class SummaryPostUpdateNode extends FlowSummaryNode, PostUpdateNode {
|
||||
@@ -445,38 +445,46 @@ final class CastNode extends ExprNode {
|
||||
|
||||
cached
|
||||
newtype TNode =
|
||||
TExprNode(ExprCfgNode n) { Stages::DataFlowStage::ref() } or
|
||||
TSourceParameterNode(ParamBaseCfgNode p) or
|
||||
TPatNode(PatCfgNode p) or
|
||||
TNameNode(NameCfgNode n) { n.getName() = any(Variable v).getName() } or
|
||||
TExprPostUpdateNode(ExprCfgNode e) {
|
||||
isArgumentForCall(e, _, _)
|
||||
or
|
||||
lambdaCallExpr(_, _, e)
|
||||
or
|
||||
lambdaCreationExpr(e.getExpr())
|
||||
or
|
||||
// Whenever `&mut e` has a post-update node we also create one for `e`.
|
||||
// E.g., for `e` in `f(..., &mut e, ...)` or `*(&mut e) = ...`.
|
||||
e = any(RefExprCfgNode ref | ref.isMut() and exists(TExprPostUpdateNode(ref))).getExpr()
|
||||
or
|
||||
e =
|
||||
[
|
||||
any(IndexExprCfgNode i).getBase(), //
|
||||
any(FieldExprCfgNode access).getContainer(), //
|
||||
any(TryExprCfgNode try).getExpr(), //
|
||||
any(PrefixExprCfgNode pe | pe.getOperatorName() = "*").getExpr(), //
|
||||
any(AwaitExprCfgNode a).getExpr(), //
|
||||
any(MethodCallExprCfgNode mc).getReceiver(), //
|
||||
getPostUpdateReverseStep(any(PostUpdateNode n).getPreUpdateNode().asExpr(), _)
|
||||
]
|
||||
TExprNode(Expr e) { e.hasEnclosingCfgScope() and Stages::DataFlowStage::ref() } or
|
||||
TSourceParameterNode(ParamBase p) { p.hasEnclosingCfgScope() } or
|
||||
TPatNode(Pat p) { p.hasEnclosingCfgScope() } or
|
||||
TNameNode(Name n) { n = any(Variable v).getName() and n.hasEnclosingCfgScope() } or
|
||||
TExprPostUpdateNode(Expr e) {
|
||||
e.hasEnclosingCfgScope() and
|
||||
(
|
||||
isArgumentForCall(e, _, _)
|
||||
or
|
||||
lambdaCallExpr(_, _, e)
|
||||
or
|
||||
lambdaCreationExpr(e)
|
||||
or
|
||||
// Whenever `&mut e` has a post-update node we also create one for `e`.
|
||||
// E.g., for `e` in `f(..., &mut e, ...)` or `*(&mut e) = ...`.
|
||||
e = any(RefExpr ref | ref.isMut() and exists(TExprPostUpdateNode(ref))).getExpr()
|
||||
or
|
||||
e =
|
||||
[
|
||||
any(IndexExpr i).getBase(), //
|
||||
any(FieldExpr access).getContainer(), //
|
||||
any(TryExpr try).getExpr(), //
|
||||
any(PrefixExpr pe | pe.getOperatorName() = "*").getExpr(), //
|
||||
any(AwaitExpr a).getExpr(), //
|
||||
any(MethodCallExpr mc).getReceiver(), //
|
||||
getPostUpdateReverseStep(any(PostUpdateNode n).getPreUpdateNode().asExpr(), _)
|
||||
]
|
||||
)
|
||||
} or
|
||||
TReceiverNode(CallCfgNode mc, Boolean isPost) {
|
||||
mc.getCall().receiverImplicitlyBorrowed() and
|
||||
TReceiverNode(Call call, Boolean isPost) {
|
||||
call.hasEnclosingCfgScope() and
|
||||
call.receiverImplicitlyBorrowed() and
|
||||
// TODO: Handle index expressions as calls in data flow.
|
||||
not mc.getCall() instanceof IndexExpr
|
||||
not call instanceof IndexExpr
|
||||
} or
|
||||
TSsaNode(SsaImpl::DataFlowIntegration::SsaNode node) or
|
||||
TFlowSummaryNode(FlowSummaryImpl::Private::SummaryNode sn) or
|
||||
TFlowSummaryNode(FlowSummaryImpl::Private::SummaryNode sn) {
|
||||
forall(AstNode n | n = sn.getSinkElement() or n = sn.getSourceElement() |
|
||||
n.hasEnclosingCfgScope()
|
||||
)
|
||||
} or
|
||||
TClosureSelfReferenceNode(CfgScope c) { lambdaCreationExpr(c) } or
|
||||
TCaptureNode(VariableCapture::Flow::SynthesizedCaptureNode cn)
|
||||
|
||||
@@ -2,7 +2,6 @@ private import rust
|
||||
private import codeql.rust.controlflow.BasicBlocks as BasicBlocks
|
||||
private import BasicBlocks
|
||||
private import codeql.rust.controlflow.ControlFlowGraph as Cfg
|
||||
private import codeql.rust.controlflow.CfgNodes as CfgNodes
|
||||
private import Cfg
|
||||
private import codeql.rust.controlflow.internal.ControlFlowGraphImpl as ControlFlowGraphImpl
|
||||
private import codeql.ssa.Ssa as SsaImplCommon
|
||||
@@ -229,11 +228,11 @@ private module Cached {
|
||||
}
|
||||
|
||||
cached
|
||||
CfgNode getARead(Definition def) {
|
||||
Expr getARead(Definition def) {
|
||||
exists(Variable v, BasicBlock bb, int i |
|
||||
Impl::ssaDefReachesRead(v, def, bb, i) and
|
||||
variableReadCertain(bb, i, v.getAnAccess(), v) and
|
||||
result = bb.getNode(i)
|
||||
result = bb.getNode(i).getAstNode()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -247,8 +246,10 @@ private module Cached {
|
||||
* without passing through any other non-pseudo read.
|
||||
*/
|
||||
cached
|
||||
predicate firstRead(Definition def, CfgNode read) {
|
||||
exists(BasicBlock bb, int i | Impl::firstUse(def, bb, i, true) and read = bb.getNode(i))
|
||||
predicate firstRead(Definition def, Expr read) {
|
||||
exists(BasicBlock bb, int i |
|
||||
Impl::firstUse(def, bb, i, true) and read = bb.getNode(i).getAstNode()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -257,12 +258,12 @@ private module Cached {
|
||||
* passing through another non-pseudo read.
|
||||
*/
|
||||
cached
|
||||
predicate adjacentReadPair(Definition def, CfgNode read1, CfgNode read2) {
|
||||
predicate adjacentReadPair(Definition def, Expr read1, Expr read2) {
|
||||
exists(BasicBlock bb1, int i1, BasicBlock bb2, int i2, Variable v |
|
||||
Impl::ssaDefReachesRead(v, def, bb1, i1) and
|
||||
Impl::adjacentUseUse(bb1, i1, bb2, i2, v, true) and
|
||||
read1 = bb1.getNode(i1) and
|
||||
read2 = bb2.getNode(i2)
|
||||
read1 = bb1.getNode(i1).getAstNode() and
|
||||
read2 = bb2.getNode(i2).getAstNode()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -287,7 +288,7 @@ private module Cached {
|
||||
DataFlowIntegrationImpl::localMustFlowStep(v, nodeFrom, nodeTo)
|
||||
}
|
||||
|
||||
signature predicate guardChecksSig(CfgNodes::AstCfgNode g, Cfg::CfgNode e, boolean branch);
|
||||
signature predicate guardChecksSig(AstNode g, Expr e, boolean branch);
|
||||
|
||||
cached // nothing is actually cached
|
||||
module BarrierGuard<guardChecksSig/3 guardChecks> {
|
||||
@@ -310,47 +311,49 @@ private module Cached {
|
||||
import Cached
|
||||
private import codeql.rust.dataflow.Ssa
|
||||
|
||||
private class ExprAlias = Expr;
|
||||
|
||||
private module DataFlowIntegrationInput implements Impl::DataFlowIntegrationInputSig {
|
||||
private import codeql.rust.dataflow.internal.DataFlowImpl as DataFlowImpl
|
||||
private import codeql.util.Boolean
|
||||
|
||||
class Expr extends CfgNodes::AstCfgNode {
|
||||
predicate hasCfgNode(BasicBlock bb, int i) { this = bb.getNode(i) }
|
||||
class Expr extends ExprAlias {
|
||||
predicate hasCfgNode(BasicBlock bb, int i) { this.getACfgNode() = bb.getNode(i) }
|
||||
}
|
||||
|
||||
Expr getARead(Definition def) { result = Cached::getARead(def) }
|
||||
|
||||
predicate ssaDefHasSource(WriteDefinition def) { none() } // handled in `DataFlowImpl.qll` instead
|
||||
|
||||
private predicate isArg(CfgNodes::CallExprBaseCfgNode call, CfgNodes::ExprCfgNode e) {
|
||||
call.getArgument(_) = e
|
||||
private predicate isArg(CallExprBase call, Expr e) {
|
||||
call.getAnArg() = e
|
||||
or
|
||||
call.(CfgNodes::MethodCallExprCfgNode).getReceiver() = e
|
||||
call.(MethodCallExpr).getReceiver() = e
|
||||
or
|
||||
exists(CfgNodes::ExprCfgNode mid |
|
||||
exists(Expr mid |
|
||||
isArg(call, mid) and
|
||||
e = DataFlowImpl::getPostUpdateReverseStep(mid, _)
|
||||
)
|
||||
}
|
||||
|
||||
predicate allowFlowIntoUncertainDef(UncertainWriteDefinition def) {
|
||||
exists(CfgNodes::CallExprBaseCfgNode call, Variable v, BasicBlock bb, int i |
|
||||
exists(Variable v, BasicBlock bb, int i |
|
||||
def.definesAt(v, bb, i) and
|
||||
mutablyBorrows(bb.getNode(i).getAstNode(), v) and
|
||||
isArg(call, bb.getNode(i))
|
||||
isArg(_, bb.getNode(i).getAstNode())
|
||||
)
|
||||
}
|
||||
|
||||
class GuardValue = Boolean;
|
||||
|
||||
class Guard extends CfgNodes::AstCfgNode {
|
||||
class Guard extends AstNode {
|
||||
/**
|
||||
* Holds if the evaluation of this guard to `branch` corresponds to the edge
|
||||
* from `bb1` to `bb2`.
|
||||
*/
|
||||
predicate hasValueBranchEdge(BasicBlock bb1, BasicBlock bb2, GuardValue branch) {
|
||||
exists(Cfg::ConditionalSuccessor s |
|
||||
this = bb1.getANode() and
|
||||
this = bb1.getANode().getAstNode() and
|
||||
bb2 = bb1.getASuccessor(s) and
|
||||
s.getValue() = branch
|
||||
)
|
||||
@@ -369,7 +372,7 @@ private module DataFlowIntegrationInput implements Impl::DataFlowIntegrationInpu
|
||||
/** Holds if the guard `guard` controls block `bb` upon evaluating to `branch`. */
|
||||
predicate guardDirectlyControlsBlock(Guard guard, BasicBlock bb, GuardValue branch) {
|
||||
exists(ConditionBasicBlock conditionBlock, ConditionalSuccessor s |
|
||||
guard = conditionBlock.getLastNode() and
|
||||
guard = conditionBlock.getLastNode().getAstNode() and
|
||||
s.getValue() = branch and
|
||||
conditionBlock.edgeDominates(bb, s)
|
||||
)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
private import rust
|
||||
private import codeql.dataflow.TaintTracking
|
||||
private import codeql.rust.controlflow.CfgNodes
|
||||
private import codeql.rust.dataflow.DataFlow
|
||||
private import codeql.rust.dataflow.FlowSummary
|
||||
private import DataFlowImpl
|
||||
@@ -21,22 +20,22 @@ module RustTaintTracking implements InputSig<Location, RustDataFlow> {
|
||||
Stages::DataFlowStage::ref() and
|
||||
model = "" and
|
||||
(
|
||||
exists(BinaryExprCfgNode binary |
|
||||
exists(BinaryExpr binary |
|
||||
binary.getOperatorName() = ["+", "-", "*", "/", "%", "&", "|", "^", "<<", ">>"] and
|
||||
pred.asExpr() = [binary.getLhs(), binary.getRhs()] and
|
||||
succ.asExpr() = binary
|
||||
)
|
||||
or
|
||||
exists(PrefixExprCfgNode prefix |
|
||||
exists(PrefixExpr prefix |
|
||||
prefix.getOperatorName() = ["-", "!"] and
|
||||
pred.asExpr() = prefix.getExpr() and
|
||||
succ.asExpr() = prefix
|
||||
)
|
||||
or
|
||||
pred.asExpr() = succ.asExpr().(CastExprCfgNode).getExpr()
|
||||
pred.asExpr() = succ.asExpr().(CastExpr).getExpr()
|
||||
or
|
||||
exists(IndexExprCfgNode index |
|
||||
index.getIndex() instanceof RangeExprCfgNode and
|
||||
exists(IndexExpr index |
|
||||
index.getIndex() instanceof RangeExpr and
|
||||
pred.asExpr() = index.getBase() and
|
||||
succ.asExpr() = index
|
||||
)
|
||||
@@ -52,8 +51,16 @@ module RustTaintTracking implements InputSig<Location, RustDataFlow> {
|
||||
cs.getContent() instanceof ReferenceContent
|
||||
)
|
||||
or
|
||||
exists(FormatArgsExprCfgNode format | succ.asExpr() = format |
|
||||
pred.asExpr() = [format.getArgumentExpr(_), format.getFormatTemplateVariableAccess(_)]
|
||||
exists(FormatArgsExpr format | succ.asExpr() = format |
|
||||
pred.asExpr() = format.getAnArg().getExpr()
|
||||
or
|
||||
pred.asExpr() =
|
||||
any(FormatTemplateVariableAccess v |
|
||||
exists(Format f |
|
||||
f = format.getAFormat() and
|
||||
v.getArgument() = [f.getArgumentRef(), f.getWidthArgument(), f.getPrecisionArgument()]
|
||||
)
|
||||
)
|
||||
)
|
||||
or
|
||||
succ.(Node::PostUpdateNode).getPreUpdateNode().asExpr() =
|
||||
|
||||
@@ -25,6 +25,11 @@ final class AssignmentOperation = AssignmentOperationImpl;
|
||||
final class AssignmentExpr extends AssignmentOperationImpl {
|
||||
AssignmentExpr() { this.getOperatorName() = "=" }
|
||||
|
||||
/**
|
||||
* Gets a write access that occurs in the left-hand side of this assignment expression.
|
||||
*/
|
||||
VariableWriteAccess getAWriteAccess() { this = result.getAssignmentExpr() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "AssignmentExpr" }
|
||||
}
|
||||
|
||||
|
||||
@@ -59,6 +59,9 @@ module Impl {
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if this node is inside a CFG scope. */
|
||||
predicate hasEnclosingCfgScope() { exists(this.getEnclosingCfgScope()) }
|
||||
|
||||
/** Gets the block that encloses this node, if any. */
|
||||
cached
|
||||
BlockExpr getEnclosingBlock() {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// generated by codegen, remove this comment if you wish to edit this file
|
||||
/**
|
||||
* This module provides a hand-modifiable wrapper around the generated class `BlockExpr`.
|
||||
*
|
||||
@@ -12,6 +11,7 @@ private import codeql.rust.elements.internal.generated.BlockExpr
|
||||
* be referenced directly.
|
||||
*/
|
||||
module Impl {
|
||||
// the following QLdoc is generated: if you need to edit it, do it in the schema file
|
||||
/**
|
||||
* A block expression. For example:
|
||||
* ```rust
|
||||
@@ -26,5 +26,10 @@ module Impl {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class BlockExpr extends Generated::BlockExpr { }
|
||||
class BlockExpr extends Generated::BlockExpr {
|
||||
/**
|
||||
* Gets the tail expression of this block, if it exists.
|
||||
*/
|
||||
Expr getTailExpr() { result = this.getStmtList().getTailExpr() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ module Impl {
|
||||
not isAttributeMacroExpansionSourceLocation(macro, n.getLocation())
|
||||
)
|
||||
or
|
||||
isFromMacroExpansion(getImmediatelyEnclosingMacroInvocation(n))
|
||||
isFromMacroExpansion(pragma[only_bind_into](getImmediatelyEnclosingMacroInvocation(n)))
|
||||
}
|
||||
|
||||
cached
|
||||
|
||||
@@ -68,7 +68,7 @@ module Impl {
|
||||
* [1]: https://doc.rust-lang.org/reference/tokens.html#string-literals
|
||||
*/
|
||||
class StringLiteralExpr extends LiteralExpr {
|
||||
StringLiteralExpr() { this.getTextValue().regexpMatch("r?#*\".*\"#*") }
|
||||
StringLiteralExpr() { this.getTextValue().charAt(0) = ["\"", "r"] }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "StringLiteralExpr" }
|
||||
}
|
||||
|
||||
@@ -21,6 +21,24 @@ module Impl {
|
||||
* ```
|
||||
*/
|
||||
class ParenExpr extends Generated::ParenExpr {
|
||||
override string toStringImpl() { result = "(" + this.getExpr().toAbbreviatedString() + ")" }
|
||||
override string toStringImpl() {
|
||||
result = "(" + this.getExpr().toAbbreviatedString() + ")"
|
||||
or
|
||||
// In macro expansions such as
|
||||
//
|
||||
// ```rust
|
||||
// [
|
||||
// "a",
|
||||
// "b",
|
||||
// #[cfg(target_os = "macos")]
|
||||
// "c",
|
||||
// ]
|
||||
// ```
|
||||
//
|
||||
// the last array element will give rise to an empty `ParenExpr` when not
|
||||
// compiling for macos.
|
||||
not exists(this.getExpr().toAbbreviatedString()) and
|
||||
result = "(...)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,10 +32,16 @@ module Impl {
|
||||
result.getName().getText() = name
|
||||
}
|
||||
|
||||
/** Gets a record field, if any. */
|
||||
StructField getAStructField() { result = this.getStructField(_) }
|
||||
|
||||
/** Gets the `i`th tuple field, if any. */
|
||||
pragma[nomagic]
|
||||
TupleField getTupleField(int i) { result = this.getFieldList().(TupleFieldList).getField(i) }
|
||||
|
||||
/** Gets a tuple field, if any. */
|
||||
TupleField getATupleField() { result = this.getTupleField(_) }
|
||||
|
||||
/** Holds if this struct uses tuple fields. */
|
||||
pragma[nomagic]
|
||||
predicate isTuple() { this.getFieldList() instanceof TupleFieldList }
|
||||
|
||||
@@ -11,7 +11,7 @@ private import codeql.rust.Concepts
|
||||
private class PoemHandlerParam extends RemoteSource::Range {
|
||||
PoemHandlerParam() {
|
||||
exists(TupleStructPat param |
|
||||
this.asPat().getPat() = param.getAField() and
|
||||
this.asPat() = param.getAField() and
|
||||
param.getStruct().getCanonicalPath() = ["poem::web::query::Query", "poem::web::path::Path"]
|
||||
)
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ class StreamCipherInit extends Cryptography::CryptographicOperation::Range {
|
||||
// a call to `cipher::KeyInit::new`, `cipher::KeyInit::new_from_slice`,
|
||||
// `cipher::KeyIvInit::new`, `cipher::KeyIvInit::new_from_slices`, `rc2::Rc2::new_with_eff_key_len` or similar.
|
||||
exists(CallExprBase ce, string rawAlgorithmName |
|
||||
ce = this.asExpr().getExpr() and
|
||||
ce = this.asExpr() and
|
||||
ce.getStaticTarget().(Function).getName().getText() =
|
||||
["new", "new_from_slice", "new_with_eff_key_len", "new_from_slices"] and
|
||||
// extract the algorithm name from the type of `ce` or its receiver.
|
||||
|
||||
@@ -136,3 +136,36 @@ class F32 extends FloatingPointTypeImpl {
|
||||
class F64 extends FloatingPointTypeImpl {
|
||||
F64() { this.getName() = "f64" }
|
||||
}
|
||||
|
||||
/** The builtin slice type `[T]`. */
|
||||
class SliceType extends BuiltinType {
|
||||
SliceType() { this.getName() = "Slice" }
|
||||
}
|
||||
|
||||
/** The builtin array type `[T; N]`. */
|
||||
class ArrayType extends BuiltinType {
|
||||
ArrayType() { this.getName() = "Array" }
|
||||
}
|
||||
|
||||
/** The builtin reference type `&T` or `&mut T`. */
|
||||
class RefType extends BuiltinType {
|
||||
RefType() { this.getName() = "Ref" }
|
||||
}
|
||||
|
||||
/** The builtin pointer type `*const T` or `*mut T`. */
|
||||
class PtrType extends BuiltinType {
|
||||
PtrType() { this.getName() = "Ptr" }
|
||||
}
|
||||
|
||||
/** A builtin tuple type `(T1, T2, ...)`. */
|
||||
class TupleType extends BuiltinType {
|
||||
TupleType() { this.getName().matches("Tuple%") }
|
||||
|
||||
/** Gets the arity of this tuple type. */
|
||||
int getArity() {
|
||||
not this.hasGenericParamList() and
|
||||
result = 0
|
||||
or
|
||||
result = this.getGenericParamList().getNumberOfGenericParams()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,20 +4,16 @@
|
||||
|
||||
private import rust
|
||||
private import codeql.rust.Concepts
|
||||
private import codeql.rust.controlflow.ControlFlowGraph as Cfg
|
||||
private import codeql.rust.controlflow.CfgNodes as CfgNodes
|
||||
private import codeql.rust.dataflow.DataFlow
|
||||
private import codeql.rust.internal.PathResolution
|
||||
|
||||
/**
|
||||
* A call to the `starts_with` method on a `Path`.
|
||||
*/
|
||||
private class StartswithCall extends Path::SafeAccessCheck::Range, CfgNodes::MethodCallExprCfgNode {
|
||||
StartswithCall() {
|
||||
this.getMethodCallExpr().getStaticTarget().getCanonicalPath() = "<std::path::Path>::starts_with"
|
||||
}
|
||||
private class StartswithCall extends Path::SafeAccessCheck::Range, MethodCallExpr {
|
||||
StartswithCall() { this.getStaticTarget().getCanonicalPath() = "<std::path::Path>::starts_with" }
|
||||
|
||||
override predicate checks(Cfg::CfgNode e, boolean branch) {
|
||||
override predicate checks(Expr e, boolean branch) {
|
||||
e = this.getReceiver() and
|
||||
branch = true
|
||||
}
|
||||
|
||||
@@ -259,8 +259,7 @@ abstract class ItemNode extends Locatable {
|
||||
kind.isInternal() and
|
||||
useOpt.isNone()
|
||||
or
|
||||
externCrateEdge(this, name, result) and
|
||||
kind.isInternal() and
|
||||
externCrateEdge(this, name, kind, result) and
|
||||
useOpt.isNone()
|
||||
or
|
||||
macroExportEdge(this, name, result) and
|
||||
@@ -276,7 +275,7 @@ abstract class ItemNode extends Locatable {
|
||||
result = use_.getASuccessor(name, kind, _)
|
||||
)
|
||||
or
|
||||
exists(ExternCrateItemNode ec | result = ec.(ItemNode).getASuccessor(name, kind, useOpt) |
|
||||
exists(ExternCrateItemNode ec | result = ec.getASuccessor(name, kind, useOpt) |
|
||||
ec = this.getASuccessor(_, _, _)
|
||||
or
|
||||
// if the extern crate appears in the crate root, then the crate name is also added
|
||||
@@ -527,7 +526,7 @@ class ExternCrateItemNode extends ItemNode instanceof ExternCrate {
|
||||
|
||||
override Namespace getNamespace() { none() }
|
||||
|
||||
override Visibility getVisibility() { none() }
|
||||
override Visibility getVisibility() { result = ExternCrate.super.getVisibility() }
|
||||
|
||||
override Attr getAnAttr() { result = ExternCrate.super.getAnAttr() }
|
||||
|
||||
@@ -713,12 +712,34 @@ abstract class ImplOrTraitItemNode extends ItemNode {
|
||||
predicate hasAssocItem(string name) { name = this.getAnAssocItem().getName() }
|
||||
}
|
||||
|
||||
private TypeItemNode resolveBuiltin(TypeRepr tr) {
|
||||
tr instanceof SliceTypeRepr and
|
||||
result instanceof Builtins::SliceType
|
||||
or
|
||||
tr instanceof ArrayTypeRepr and
|
||||
result instanceof Builtins::ArrayType
|
||||
or
|
||||
tr instanceof RefTypeRepr and
|
||||
result instanceof Builtins::RefType
|
||||
or
|
||||
tr instanceof PtrTypeRepr and
|
||||
result instanceof Builtins::PtrType
|
||||
or
|
||||
result.(Builtins::TupleType).getArity() = tr.(TupleTypeRepr).getNumberOfFields()
|
||||
}
|
||||
|
||||
final class ImplItemNode extends ImplOrTraitItemNode instanceof Impl {
|
||||
Path getSelfPath() { result = super.getSelfTy().(PathTypeRepr).getPath() }
|
||||
|
||||
Path getTraitPath() { result = super.getTrait().(PathTypeRepr).getPath() }
|
||||
|
||||
TypeItemNode resolveSelfTy() { result = resolvePath(this.getSelfPath()) }
|
||||
TypeItemNode resolveSelfTyBuiltin() { result = resolveBuiltin(this.(Impl).getSelfTy()) }
|
||||
|
||||
TypeItemNode resolveSelfTy() {
|
||||
result = resolvePath(this.getSelfPath())
|
||||
or
|
||||
result = this.resolveSelfTyBuiltin()
|
||||
}
|
||||
|
||||
TraitItemNode resolveTraitTy() { result = resolvePath(this.getTraitPath()) }
|
||||
|
||||
@@ -893,7 +914,11 @@ private class ModuleItemNode extends ModuleLikeNode instanceof Module {
|
||||
}
|
||||
|
||||
private class ImplItemNodeImpl extends ImplItemNode {
|
||||
TypeItemNode resolveSelfTyCand() { result = resolvePathCand(this.getSelfPath()) }
|
||||
TypeItemNode resolveSelfTyCand() {
|
||||
result = resolvePathCand(this.getSelfPath())
|
||||
or
|
||||
result = this.resolveSelfTyBuiltin()
|
||||
}
|
||||
|
||||
TraitItemNode resolveTraitTyCand() { result = resolvePathCand(this.getTraitPath()) }
|
||||
}
|
||||
@@ -1764,6 +1789,10 @@ private ItemNode resolvePathCand0(RelevantPath path, Namespace ns) {
|
||||
or
|
||||
result = resolveUseTreeListItem(_, _, path, _) and
|
||||
ns = result.getNamespace()
|
||||
or
|
||||
result = resolveBuiltin(path.getSegment().getTypeRepr()) and
|
||||
not path.getSegment().hasTraitTypeRepr() and
|
||||
ns.isType()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -2077,8 +2106,11 @@ private predicate useImportEdge(Use use, string name, ItemNode item, SuccessorKi
|
||||
|
||||
/** Holds if `ec` imports `crate` as `name`. */
|
||||
pragma[nomagic]
|
||||
private predicate externCrateEdge(ExternCrateItemNode ec, string name, CrateItemNode crate) {
|
||||
private predicate externCrateEdge(
|
||||
ExternCrateItemNode ec, string name, SuccessorKind kind, CrateItemNode crate
|
||||
) {
|
||||
name = ec.getName() and
|
||||
(if ec.isPublic() then kind.isBoth() else kind.isInternal()) and
|
||||
exists(SourceFile f, string s |
|
||||
ec.getFile() = f.getFile() and
|
||||
s = ec.(ExternCrate).getIdentifier().getText()
|
||||
@@ -2141,7 +2173,8 @@ pragma[nomagic]
|
||||
private predicate builtin(string name, ItemNode i) {
|
||||
exists(BuiltinSourceFile builtins |
|
||||
builtins.getFile().getBaseName() = "types.rs" and
|
||||
i = builtins.getASuccessor(name)
|
||||
i = builtins.getASuccessor(name) and
|
||||
i.isPublic()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ private import codeql.rust.internal.CachedStages
|
||||
private import codeql.rust.elements.internal.generated.Raw
|
||||
private import codeql.rust.elements.internal.generated.Synth
|
||||
private import codeql.rust.frameworks.stdlib.Stdlib
|
||||
private import codeql.rust.frameworks.stdlib.Builtins as Builtins
|
||||
|
||||
/**
|
||||
* Holds if a dyn trait type should have a type parameter associated with `n`. A
|
||||
@@ -31,38 +32,21 @@ private predicate dynTraitTypeParameter(Trait trait, AstNode n) {
|
||||
|
||||
cached
|
||||
newtype TType =
|
||||
TTuple(int arity) {
|
||||
arity =
|
||||
[
|
||||
any(TupleTypeRepr t).getNumberOfFields(),
|
||||
any(TupleExpr e).getNumberOfFields(),
|
||||
any(TuplePat p).getNumberOfFields()
|
||||
] and
|
||||
Stages::TypeInferenceStage::ref()
|
||||
} or
|
||||
TStruct(Struct s) or
|
||||
TStruct(Struct s) { Stages::TypeInferenceStage::ref() } or
|
||||
TEnum(Enum e) or
|
||||
TTrait(Trait t) or
|
||||
TUnion(Union u) or
|
||||
TArrayType() or // todo: add size?
|
||||
TRefType() or // todo: add mut?
|
||||
TImplTraitType(ImplTraitTypeRepr impl) or
|
||||
TDynTraitType(Trait t) { t = any(DynTraitTypeRepr dt).getTrait() } or
|
||||
TSliceType() or
|
||||
TNeverType() or
|
||||
TPtrType() or
|
||||
TTupleTypeParameter(int arity, int i) { exists(TTuple(arity)) and i in [0 .. arity - 1] } or
|
||||
TUnknownType() or
|
||||
TTypeParamTypeParameter(TypeParam t) or
|
||||
TAssociatedTypeTypeParameter(TypeAlias t) { any(TraitItemNode trait).getAnAssocItem() = t } or
|
||||
TArrayTypeParameter() or
|
||||
TDynTraitTypeParameter(AstNode n) { dynTraitTypeParameter(_, n) } or
|
||||
TImplTraitTypeParameter(ImplTraitTypeRepr implTrait, TypeParam tp) {
|
||||
implTraitTypeParam(implTrait, _, tp)
|
||||
} or
|
||||
TRefTypeParameter() or
|
||||
TSelfTypeParameter(Trait t) or
|
||||
TSliceTypeParameter() or
|
||||
TPtrTypeParameter()
|
||||
TSelfTypeParameter(Trait t)
|
||||
|
||||
private predicate implTraitTypeParam(ImplTraitTypeRepr implTrait, int i, TypeParam tp) {
|
||||
implTrait.isInReturnPos() and
|
||||
@@ -105,26 +89,25 @@ abstract class Type extends TType {
|
||||
}
|
||||
|
||||
/** A tuple type `(T, ...)`. */
|
||||
class TupleType extends Type, TTuple {
|
||||
class TupleType extends StructType {
|
||||
private int arity;
|
||||
|
||||
TupleType() { this = TTuple(arity) }
|
||||
|
||||
override TypeParameter getPositionalTypeParameter(int i) {
|
||||
result = TTupleTypeParameter(arity, i)
|
||||
}
|
||||
TupleType() { arity = this.getStruct().(Builtins::TupleType).getArity() }
|
||||
|
||||
/** Gets the arity of this tuple type. */
|
||||
int getArity() { result = arity }
|
||||
|
||||
override string toString() { result = "(T_" + arity + ")" }
|
||||
}
|
||||
|
||||
override Location getLocation() { result instanceof EmptyLocation }
|
||||
pragma[nomagic]
|
||||
TypeParamTypeParameter getTupleTypeParameter(int arity, int i) {
|
||||
result = any(TupleType t | t.getArity() = arity).getPositionalTypeParameter(i)
|
||||
}
|
||||
|
||||
/** The unit type `()`. */
|
||||
class UnitType extends TupleType {
|
||||
UnitType() { this = TTuple(0) }
|
||||
UnitType() { this.getArity() = 0 }
|
||||
|
||||
override string toString() { result = "()" }
|
||||
}
|
||||
@@ -225,20 +208,17 @@ class UnionType extends Type, TUnion {
|
||||
/**
|
||||
* An array type.
|
||||
*
|
||||
* Array types like `[i64; 5]` are modeled as normal generic types
|
||||
* with a single type argument.
|
||||
* Array types like `[i64; 5]` are modeled as normal generic types.
|
||||
*/
|
||||
class ArrayType extends Type, TArrayType {
|
||||
ArrayType() { this = TArrayType() }
|
||||
class ArrayType extends StructType {
|
||||
ArrayType() { this.getStruct() instanceof Builtins::ArrayType }
|
||||
|
||||
override TypeParameter getPositionalTypeParameter(int i) {
|
||||
result = TArrayTypeParameter() and
|
||||
i = 0
|
||||
}
|
||||
override string toString() { result = "[;]" }
|
||||
}
|
||||
|
||||
override string toString() { result = "[]" }
|
||||
|
||||
override Location getLocation() { result instanceof EmptyLocation }
|
||||
pragma[nomagic]
|
||||
TypeParamTypeParameter getArrayTypeParameter() {
|
||||
result = any(ArrayType t).getPositionalTypeParameter(0)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -247,17 +227,15 @@ class ArrayType extends Type, TArrayType {
|
||||
* Reference types like `& i64` are modeled as normal generic types
|
||||
* with a single type argument.
|
||||
*/
|
||||
class RefType extends Type, TRefType {
|
||||
RefType() { this = TRefType() }
|
||||
|
||||
override TypeParameter getPositionalTypeParameter(int i) {
|
||||
result = TRefTypeParameter() and
|
||||
i = 0
|
||||
}
|
||||
class RefType extends StructType {
|
||||
RefType() { this.getStruct() instanceof Builtins::RefType }
|
||||
|
||||
override string toString() { result = "&" }
|
||||
}
|
||||
|
||||
override Location getLocation() { result instanceof EmptyLocation }
|
||||
pragma[nomagic]
|
||||
TypeParamTypeParameter getRefTypeParameter() {
|
||||
result = any(RefType t).getPositionalTypeParameter(0)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -339,17 +317,15 @@ class ImplTraitReturnType extends ImplTraitType {
|
||||
* Slice types like `[i64]` are modeled as normal generic types
|
||||
* with a single type argument.
|
||||
*/
|
||||
class SliceType extends Type, TSliceType {
|
||||
SliceType() { this = TSliceType() }
|
||||
|
||||
override TypeParameter getPositionalTypeParameter(int i) {
|
||||
result = TSliceTypeParameter() and
|
||||
i = 0
|
||||
}
|
||||
class SliceType extends StructType {
|
||||
SliceType() { this.getStruct() instanceof Builtins::SliceType }
|
||||
|
||||
override string toString() { result = "[]" }
|
||||
}
|
||||
|
||||
override Location getLocation() { result instanceof EmptyLocation }
|
||||
pragma[nomagic]
|
||||
TypeParamTypeParameter getSliceTypeParameter() {
|
||||
result = any(SliceType t).getPositionalTypeParameter(0)
|
||||
}
|
||||
|
||||
class NeverType extends Type, TNeverType {
|
||||
@@ -360,17 +336,49 @@ class NeverType extends Type, TNeverType {
|
||||
override Location getLocation() { result instanceof EmptyLocation }
|
||||
}
|
||||
|
||||
class PtrType extends Type, TPtrType {
|
||||
override TypeParameter getPositionalTypeParameter(int i) {
|
||||
i = 0 and
|
||||
result = TPtrTypeParameter()
|
||||
}
|
||||
class PtrType extends StructType {
|
||||
PtrType() { this.getStruct() instanceof Builtins::PtrType }
|
||||
|
||||
override string toString() { result = "*" }
|
||||
|
||||
override Location getLocation() { result instanceof EmptyLocation }
|
||||
}
|
||||
|
||||
/**
|
||||
* A special pseudo type used to indicate that the actual type may have to be
|
||||
* inferred by propagating type information back into call arguments.
|
||||
*
|
||||
* For example, in
|
||||
*
|
||||
* ```rust
|
||||
* let x = Default::default();
|
||||
* foo(x);
|
||||
* ```
|
||||
*
|
||||
* `Default::default()` is assigned this type, which allows us to infer the actual
|
||||
* type from the type of `foo`'s first parameter.
|
||||
*
|
||||
* Unknown types are not restricted to root types, for example in a call like
|
||||
* `Vec::new()` we assign this type at the type path corresponding to the type
|
||||
* parameter of `Vec`.
|
||||
*
|
||||
* Unknown types are used to restrict when type information is allowed to flow
|
||||
* into call arguments (including method call receivers), in order to avoid
|
||||
* combinatorial explosions.
|
||||
*/
|
||||
class UnknownType extends Type, TUnknownType {
|
||||
override TypeParameter getPositionalTypeParameter(int i) { none() }
|
||||
|
||||
override string toString() { result = "(context typed)" }
|
||||
|
||||
override Location getLocation() { result instanceof EmptyLocation }
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
TypeParamTypeParameter getPtrTypeParameter() {
|
||||
result = any(PtrType t).getPositionalTypeParameter(0)
|
||||
}
|
||||
|
||||
/** A type parameter. */
|
||||
abstract class TypeParameter extends Type {
|
||||
override TypeParameter getPositionalTypeParameter(int i) { none() }
|
||||
@@ -430,37 +438,6 @@ class AssociatedTypeTypeParameter extends TypeParameter, TAssociatedTypeTypePara
|
||||
override Location getLocation() { result = typeAlias.getLocation() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A tuple type parameter. For instance the `T` in `(T, U)`.
|
||||
*
|
||||
* Since tuples are structural their type parameters can be represented as their
|
||||
* positional index. The type inference library requires that type parameters
|
||||
* belong to a single type, so we also include the arity of the tuple type.
|
||||
*/
|
||||
class TupleTypeParameter extends TypeParameter, TTupleTypeParameter {
|
||||
private int arity;
|
||||
private int index;
|
||||
|
||||
TupleTypeParameter() { this = TTupleTypeParameter(arity, index) }
|
||||
|
||||
override string toString() { result = index.toString() + "(" + arity + ")" }
|
||||
|
||||
override Location getLocation() { result instanceof EmptyLocation }
|
||||
|
||||
/** Gets the index of this tuple type parameter. */
|
||||
int getIndex() { result = index }
|
||||
|
||||
/** Gets the tuple type that corresponds to this tuple type parameter. */
|
||||
TupleType getTupleType() { result = TTuple(arity) }
|
||||
}
|
||||
|
||||
/** An implicit array type parameter. */
|
||||
class ArrayTypeParameter extends TypeParameter, TArrayTypeParameter {
|
||||
override string toString() { result = "[T;...]" }
|
||||
|
||||
override Location getLocation() { result instanceof EmptyLocation }
|
||||
}
|
||||
|
||||
class DynTraitTypeParameter extends TypeParameter, TDynTraitTypeParameter {
|
||||
private AstNode n;
|
||||
|
||||
@@ -508,26 +485,6 @@ class ImplTraitTypeParameter extends TypeParameter, TImplTraitTypeParameter {
|
||||
override Location getLocation() { result = typeParam.getLocation() }
|
||||
}
|
||||
|
||||
/** An implicit reference type parameter. */
|
||||
class RefTypeParameter extends TypeParameter, TRefTypeParameter {
|
||||
override string toString() { result = "&T" }
|
||||
|
||||
override Location getLocation() { result instanceof EmptyLocation }
|
||||
}
|
||||
|
||||
/** An implicit slice type parameter. */
|
||||
class SliceTypeParameter extends TypeParameter, TSliceTypeParameter {
|
||||
override string toString() { result = "[T]" }
|
||||
|
||||
override Location getLocation() { result instanceof EmptyLocation }
|
||||
}
|
||||
|
||||
class PtrTypeParameter extends TypeParameter, TPtrTypeParameter {
|
||||
override string toString() { result = "*T" }
|
||||
|
||||
override Location getLocation() { result instanceof EmptyLocation }
|
||||
}
|
||||
|
||||
/**
|
||||
* The implicit `Self` type parameter of a trait, that refers to the
|
||||
* implementing type of the trait.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -20,11 +20,11 @@ abstract class TypeMention extends AstNode {
|
||||
class TupleTypeReprMention extends TypeMention instanceof TupleTypeRepr {
|
||||
override Type resolveTypeAt(TypePath path) {
|
||||
path.isEmpty() and
|
||||
result = TTuple(super.getNumberOfFields())
|
||||
result.(TupleType).getArity() = super.getNumberOfFields()
|
||||
or
|
||||
exists(TypePath suffix, int i |
|
||||
result = super.getField(i).(TypeMention).resolveTypeAt(suffix) and
|
||||
path = TypePath::cons(TTupleTypeParameter(super.getNumberOfFields(), i), suffix)
|
||||
path = TypePath::cons(getTupleTypeParameter(super.getNumberOfFields(), i), suffix)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -32,11 +32,11 @@ class TupleTypeReprMention extends TypeMention instanceof TupleTypeRepr {
|
||||
class ParenthesizedArgListMention extends TypeMention instanceof ParenthesizedArgList {
|
||||
override Type resolveTypeAt(TypePath path) {
|
||||
path.isEmpty() and
|
||||
result = TTuple(super.getNumberOfTypeArgs())
|
||||
result.(TupleType).getArity() = super.getNumberOfTypeArgs()
|
||||
or
|
||||
exists(TypePath suffix, int index |
|
||||
result = super.getTypeArg(index).getTypeRepr().(TypeMention).resolveTypeAt(suffix) and
|
||||
path = TypePath::cons(TTupleTypeParameter(super.getNumberOfTypeArgs(), index), suffix)
|
||||
path = TypePath::cons(getTupleTypeParameter(super.getNumberOfTypeArgs(), index), suffix)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -44,11 +44,11 @@ class ParenthesizedArgListMention extends TypeMention instanceof ParenthesizedAr
|
||||
class ArrayTypeReprMention extends TypeMention instanceof ArrayTypeRepr {
|
||||
override Type resolveTypeAt(TypePath path) {
|
||||
path.isEmpty() and
|
||||
result = TArrayType()
|
||||
result instanceof ArrayType
|
||||
or
|
||||
exists(TypePath suffix |
|
||||
result = super.getElementTypeRepr().(TypeMention).resolveTypeAt(suffix) and
|
||||
path = TypePath::cons(TArrayTypeParameter(), suffix)
|
||||
path = TypePath::cons(getArrayTypeParameter(), suffix)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -56,11 +56,11 @@ class ArrayTypeReprMention extends TypeMention instanceof ArrayTypeRepr {
|
||||
class RefTypeReprMention extends TypeMention instanceof RefTypeRepr {
|
||||
override Type resolveTypeAt(TypePath path) {
|
||||
path.isEmpty() and
|
||||
result = TRefType()
|
||||
result instanceof RefType
|
||||
or
|
||||
exists(TypePath suffix |
|
||||
result = super.getTypeRepr().(TypeMention).resolveTypeAt(suffix) and
|
||||
path = TypePath::cons(TRefTypeParameter(), suffix)
|
||||
path = TypePath::cons(getRefTypeParameter(), suffix)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -68,11 +68,11 @@ class RefTypeReprMention extends TypeMention instanceof RefTypeRepr {
|
||||
class SliceTypeReprMention extends TypeMention instanceof SliceTypeRepr {
|
||||
override Type resolveTypeAt(TypePath path) {
|
||||
path.isEmpty() and
|
||||
result = TSliceType()
|
||||
result instanceof SliceType
|
||||
or
|
||||
exists(TypePath suffix |
|
||||
result = super.getTypeRepr().(TypeMention).resolveTypeAt(suffix) and
|
||||
path = TypePath::cons(TSliceTypeParameter(), suffix)
|
||||
path = TypePath::cons(getSliceTypeParameter(), suffix)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -108,6 +108,20 @@ class AliasPathTypeMention extends PathTypeMention {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `i`th type argument of `p`.
|
||||
*
|
||||
* Takes into account that variants can have type arguments applied to both the
|
||||
* enum and the variant itself, e.g. `Option::<i32>::Some` is valid in addition
|
||||
* to `Option::Some::<i32>`.
|
||||
*/
|
||||
TypeMention getPathTypeArgument(Path p, int i) {
|
||||
result = p.getSegment().getGenericArgList().getTypeArg(i)
|
||||
or
|
||||
resolvePath(p) instanceof Variant and
|
||||
result = p.getQualifier().getSegment().getGenericArgList().getTypeArg(i)
|
||||
}
|
||||
|
||||
class NonAliasPathTypeMention extends PathTypeMention {
|
||||
TypeItemNode resolved;
|
||||
|
||||
@@ -143,18 +157,6 @@ class NonAliasPathTypeMention extends PathTypeMention {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the positional type argument at index `i` that occurs in this path, if
|
||||
* any.
|
||||
*/
|
||||
private TypeMention getPathPositionalTypeArgument(int i) {
|
||||
result = this.getSegment().getGenericArgList().getTypeArg(i)
|
||||
or
|
||||
// `Option::<i32>::Some` is valid in addition to `Option::Some::<i32>`
|
||||
resolvePath(this) instanceof Variant and
|
||||
result = this.getQualifier().getSegment().getGenericArgList().getTypeArg(i)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type mention that instantiates the implicit `Self` type parameter
|
||||
* for this path, if it occurs in the position of a trait bound.
|
||||
@@ -173,7 +175,7 @@ class NonAliasPathTypeMention extends PathTypeMention {
|
||||
private Type getDefaultPositionalTypeArgument(int i, TypePath path) {
|
||||
// If a type argument is not given in the path, then we use the default for
|
||||
// the type parameter if one exists for the type.
|
||||
not exists(this.getPathPositionalTypeArgument(i)) and
|
||||
not exists(getPathTypeArgument(this, i)) and
|
||||
// Defaults only apply to type mentions in type annotations
|
||||
this = any(PathTypeRepr ptp).getPath().getQualifier*() and
|
||||
exists(Type ty, TypePath prefix |
|
||||
@@ -191,7 +193,7 @@ class NonAliasPathTypeMention extends PathTypeMention {
|
||||
}
|
||||
|
||||
private Type getPositionalTypeArgument(int i, TypePath path) {
|
||||
result = this.getPathPositionalTypeArgument(i).resolveTypeAt(path)
|
||||
result = getPathTypeArgument(this, i).resolveTypeAt(path)
|
||||
or
|
||||
result = this.getDefaultPositionalTypeArgument(i, path)
|
||||
}
|
||||
@@ -207,6 +209,11 @@ class NonAliasPathTypeMention extends PathTypeMention {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private TypeAlias getResolvedAlias(string name) {
|
||||
result = resolved.(TraitItemNode).getAssocItem(name)
|
||||
}
|
||||
|
||||
/** Gets the type mention in this path for the type parameter `tp`, if any. */
|
||||
pragma[nomagic]
|
||||
private TypeMention getTypeMentionForTypeParameter(TypeParameter tp) {
|
||||
@@ -228,16 +235,11 @@ class NonAliasPathTypeMention extends PathTypeMention {
|
||||
// }
|
||||
// ```
|
||||
// the rhs. of the type alias is a type argument to the trait.
|
||||
exists(ImplItemNode impl, AssociatedTypeTypeParameter param, TypeAlias alias, string name |
|
||||
exists(ImplItemNode impl, TypeAlias alias, string name |
|
||||
this = impl.getTraitPath() and
|
||||
param.getTrait() = resolved and
|
||||
name = param.getTypeAlias().getName().getText() and
|
||||
alias = impl.getASuccessor(pragma[only_bind_into](name)) and
|
||||
result = alias.getTypeRepr() and
|
||||
tp =
|
||||
TAssociatedTypeTypeParameter(resolved
|
||||
.(TraitItemNode)
|
||||
.getAssocItem(pragma[only_bind_into](name)))
|
||||
tp = TAssociatedTypeTypeParameter(this.getResolvedAlias(pragma[only_bind_into](name)))
|
||||
)
|
||||
or
|
||||
// Handle the special syntactic sugar for function traits. For now we only
|
||||
@@ -289,6 +291,9 @@ class NonAliasPathTypeMention extends PathTypeMention {
|
||||
result = this.getSelfTraitBoundArg().resolveTypeAt(suffix) and
|
||||
typePath = TypePath::cons(TSelfTypeParameter(resolved), suffix)
|
||||
)
|
||||
or
|
||||
not this.getSegment().hasTraitTypeRepr() and
|
||||
result = this.getSegment().getTypeRepr().(TypeMention).resolveTypeAt(typePath)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -424,11 +429,11 @@ class ShorthandSelfParameterMention extends TypeMention instanceof SelfParam {
|
||||
then
|
||||
// `fn f(&self, ...)`
|
||||
typePath.isEmpty() and
|
||||
result = TRefType()
|
||||
result instanceof RefType
|
||||
or
|
||||
exists(TypePath suffix |
|
||||
result = this.resolveSelfType(suffix) and
|
||||
typePath = TypePath::cons(TRefTypeParameter(), suffix)
|
||||
typePath = TypePath::cons(getRefTypeParameter(), suffix)
|
||||
)
|
||||
else
|
||||
// `fn f(self, ...)`
|
||||
@@ -539,11 +544,11 @@ class NeverTypeReprMention extends TypeMention, NeverTypeRepr {
|
||||
class PtrTypeReprMention extends TypeMention instanceof PtrTypeRepr {
|
||||
override Type resolveTypeAt(TypePath path) {
|
||||
path.isEmpty() and
|
||||
result = TPtrType()
|
||||
result instanceof PtrType
|
||||
or
|
||||
exists(TypePath suffix |
|
||||
result = super.getTypeRepr().(TypeMention).resolveTypeAt(suffix) and
|
||||
path = TypePath::cons(TPtrTypeParameter(), suffix)
|
||||
path = TypePath::cons(getPtrTypeParameter(), suffix)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,7 +92,8 @@ module SatisfiesBlanketConstraint<
|
||||
|
||||
Type getTypeAt(TypePath path) {
|
||||
result = at.getTypeAt(blanketPath.appendInverse(path)) and
|
||||
not result = TNeverType()
|
||||
not result = TNeverType() and
|
||||
not result = TUnknownType()
|
||||
}
|
||||
|
||||
string toString() { result = at.toString() + " [blanket at " + blanketPath.toString() + "]" }
|
||||
|
||||
@@ -72,16 +72,24 @@ module FunctionPositionMatchingInput {
|
||||
}
|
||||
|
||||
private newtype TAssocFunctionType =
|
||||
/** An associated function `f` that should be specialized for `i` at `pos`. */
|
||||
MkAssocFunctionType(Function f, ImplOrTraitItemNode i, FunctionPosition pos) {
|
||||
f = i.getASuccessor(_) and exists(pos.getTypeMention(f))
|
||||
/** An associated function `f` in `parent` should be specialized for `i` at `pos`. */
|
||||
MkAssocFunctionType(
|
||||
ImplOrTraitItemNode parent, Function f, ImplOrTraitItemNode i, FunctionPosition pos
|
||||
) {
|
||||
parent.getAnAssocItem() = f and
|
||||
i.getASuccessor(_) = f and
|
||||
// When `f` is not directly in `i`, the `parent` should be satisfiable
|
||||
// through `i`. This ensures that `parent` is either a supertrait of `i` or
|
||||
// `i` in an `impl` block implementing `parent`.
|
||||
(parent = i or BaseTypes::rootTypesSatisfaction(_, TTrait(parent), i, _, _)) and
|
||||
exists(pos.getTypeMention(f))
|
||||
}
|
||||
|
||||
bindingset[condition, constraint, tp]
|
||||
bindingset[abs, constraint, tp]
|
||||
private Type getTraitConstraintTypeAt(
|
||||
TypeMention condition, TypeMention constraint, TypeParameter tp, TypePath path
|
||||
TypeAbstraction abs, TypeMention constraint, TypeParameter tp, TypePath path
|
||||
) {
|
||||
BaseTypes::conditionSatisfiesConstraintTypeAt(_, condition, constraint,
|
||||
BaseTypes::conditionSatisfiesConstraintTypeAt(abs, _, constraint,
|
||||
TypePath::singleton(tp).appendInverse(path), result)
|
||||
}
|
||||
|
||||
@@ -91,28 +99,19 @@ private Type getTraitConstraintTypeAt(
|
||||
*/
|
||||
pragma[nomagic]
|
||||
Type getAssocFunctionTypeAt(Function f, ImplOrTraitItemNode i, FunctionPosition pos, TypePath path) {
|
||||
exists(MkAssocFunctionType(f, i, pos)) and
|
||||
(
|
||||
exists(ImplOrTraitItemNode parent | exists(MkAssocFunctionType(parent, f, i, pos)) |
|
||||
// No specialization needed when the function is directly in the trait or
|
||||
// impl block or the declared type is not a type parameter
|
||||
(i.getAnAssocItem() = f or not result instanceof TypeParameter) and
|
||||
(parent = i or not result instanceof TypeParameter) and
|
||||
result = pos.getTypeMention(f).resolveTypeAt(path)
|
||||
or
|
||||
not i.getAnAssocItem() = f and
|
||||
exists(TypePath prefix, TypePath suffix, TypeParameter tp |
|
||||
exists(TypePath prefix, TypePath suffix, TypeParameter tp, TypeMention constraint |
|
||||
BaseTypes::rootTypesSatisfaction(_, TTrait(parent), i, _, constraint) and
|
||||
path = prefix.append(suffix) and
|
||||
tp = pos.getTypeMention(f).resolveTypeAt(prefix)
|
||||
|
|
||||
tp = pos.getTypeMention(f).resolveTypeAt(prefix) and
|
||||
if tp = TSelfTypeParameter(_)
|
||||
then result = resolveImplOrTraitType(i, suffix)
|
||||
else
|
||||
exists(TraitItemNode trait, TypeMention condition, TypeMention constraint |
|
||||
trait.getAnAssocItem() = f and
|
||||
BaseTypes::rootTypesSatisfaction(_, TTrait(trait), _, condition, constraint) and
|
||||
result = getTraitConstraintTypeAt(condition, constraint, tp, suffix)
|
||||
|
|
||||
condition = i.(Trait) or condition = i.(Impl).getSelfTy()
|
||||
)
|
||||
else result = getTraitConstraintTypeAt(i, constraint, tp, suffix)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -125,32 +124,34 @@ Type getAssocFunctionTypeAt(Function f, ImplOrTraitItemNode i, FunctionPosition
|
||||
*
|
||||
* ```rust
|
||||
* trait T1 {
|
||||
* fn m1(self); // self1
|
||||
* fn m1(self); // T1::m1
|
||||
*
|
||||
* fn m2(self) { ... } // self2
|
||||
* fn m2(self) { ... } // T1::m2
|
||||
* }
|
||||
*
|
||||
* trait T2 : T1 {
|
||||
* fn m3(self); // self3
|
||||
* fn m3(self); // T2::m3
|
||||
* }
|
||||
*
|
||||
* impl T1 for X {
|
||||
* fn m1(self) { ... } // X::m1
|
||||
* }
|
||||
*
|
||||
* impl T2 for X {
|
||||
* fn m1(self) { ... } // self4
|
||||
*
|
||||
* fn m3(self) { ... } // self5
|
||||
* fn m3(self) { ... } // X::m3
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* param | `impl` or trait | type
|
||||
* ------- | --------------- | ----
|
||||
* `self1` | `trait T1` | `T1`
|
||||
* `self1` | `trait T2` | `T2`
|
||||
* `self2` | `trait T1` | `T1`
|
||||
* `self2` | `trait T2` | `T2`
|
||||
* `self2` | `impl T2 for X` | `X`
|
||||
* `self3` | `trait T2` | `T2`
|
||||
* `self4` | `impl T2 for X` | `X`
|
||||
* `self5` | `impl T2 for X` | `X`
|
||||
* f | `impl` or trait | pos | type
|
||||
* -------- | --------------- | ------ | ----
|
||||
* `T1::m1` | `trait T1` | `self` | `T1`
|
||||
* `T1::m1` | `trait T2` | `self` | `T2`
|
||||
* `T1::m2` | `trait T1` | `self` | `T1`
|
||||
* `T1::m2` | `trait T2` | `self` | `T2`
|
||||
* `T1::m2` | `impl T1 for X` | `self` | `X`
|
||||
* `T2::m3` | `trait T2` | `self` | `T2`
|
||||
* `X::m1` | `impl T1 for X` | `self` | `X`
|
||||
* `X::m3` | `impl T2 for X` | `self` | `X`
|
||||
*/
|
||||
class AssocFunctionType extends MkAssocFunctionType {
|
||||
/**
|
||||
@@ -158,7 +159,7 @@ class AssocFunctionType extends MkAssocFunctionType {
|
||||
* when viewed as a member of the `impl` or trait item `i`.
|
||||
*/
|
||||
predicate appliesTo(Function f, ImplOrTraitItemNode i, FunctionPosition pos) {
|
||||
this = MkAssocFunctionType(f, i, pos)
|
||||
this = MkAssocFunctionType(_, f, i, pos)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -229,7 +230,8 @@ module ArgIsInstantiationOf<
|
||||
private class ArgSubst extends ArgFinal {
|
||||
Type getTypeAt(TypePath path) {
|
||||
result = substituteLookupTraits(super.getTypeAt(path)) and
|
||||
not result = TNeverType()
|
||||
not result = TNeverType() and
|
||||
not result = TUnknownType()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,14 @@ module AccessAfterLifetime {
|
||||
*/
|
||||
abstract class Barrier extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* Holds if the value pointed to by `source` accesses a variable `target` with scope `scope`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate sourceValueScope(Source source, Variable target, BlockExpr scope) {
|
||||
valueScope(source.getTarget(), target, scope)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the pair `(source, sink)`, that represents a flow from a
|
||||
* pointer or reference to a dereference, has its dereference outside the
|
||||
@@ -47,8 +55,8 @@ module AccessAfterLifetime {
|
||||
bindingset[source, sink]
|
||||
predicate dereferenceAfterLifetime(Source source, Sink sink, Variable target) {
|
||||
exists(BlockExpr valueScope, BlockExpr accessScope |
|
||||
valueScope(source.getTarget(), target, valueScope) and
|
||||
accessScope = sink.asExpr().getExpr().getEnclosingBlock() and
|
||||
sourceValueScope(source, target, valueScope) and
|
||||
accessScope = sink.asExpr().getEnclosingBlock() and
|
||||
not mayEncloseOnStack(valueScope, accessScope)
|
||||
)
|
||||
}
|
||||
@@ -104,7 +112,7 @@ module AccessAfterLifetime {
|
||||
private class RefExprSource extends Source {
|
||||
Expr targetValue;
|
||||
|
||||
RefExprSource() { this.asExpr().getExpr().(RefExpr).getExpr() = targetValue }
|
||||
RefExprSource() { this.asExpr().(RefExpr).getExpr() = targetValue }
|
||||
|
||||
override Expr getTarget() { result = targetValue }
|
||||
}
|
||||
@@ -114,6 +122,6 @@ module AccessAfterLifetime {
|
||||
* variables through closures properly.
|
||||
*/
|
||||
private class ClosureBarrier extends Barrier {
|
||||
ClosureBarrier() { this.asExpr().getExpr().getEnclosingCallable() instanceof ClosureExpr }
|
||||
ClosureBarrier() { this.asExpr().getEnclosingCallable() instanceof ClosureExpr }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ module AccessInvalidPointer {
|
||||
* A pointer access using the unary `*` operator.
|
||||
*/
|
||||
private class DereferenceSink extends Sink {
|
||||
DereferenceSink() { any(DerefExpr p).getExpr() = this.asExpr().getExpr() }
|
||||
DereferenceSink() { any(DerefExpr p).getExpr() = this.asExpr() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,9 +7,9 @@ import rust
|
||||
private import codeql.rust.dataflow.DataFlow
|
||||
private import codeql.rust.internal.TypeInference as TypeInference
|
||||
private import codeql.rust.internal.Type
|
||||
private import codeql.rust.frameworks.stdlib.Builtins
|
||||
private import codeql.rust.controlflow.ControlFlowGraph as Cfg
|
||||
private import codeql.rust.controlflow.CfgNodes as CfgNodes
|
||||
private import codeql.rust.frameworks.stdlib.Builtins as Builtins
|
||||
|
||||
/**
|
||||
* A node whose type is a numeric or boolean type, which may be an appropriate
|
||||
@@ -18,11 +18,11 @@ private import codeql.rust.controlflow.CfgNodes as CfgNodes
|
||||
class NumericTypeBarrier extends DataFlow::Node {
|
||||
NumericTypeBarrier() {
|
||||
exists(StructType t, Struct s |
|
||||
t = TypeInference::inferType(this.asExpr().getExpr()) and
|
||||
t = TypeInference::inferType(this.asExpr()) and
|
||||
s = t.getStruct()
|
||||
|
|
||||
s instanceof NumericType or
|
||||
s instanceof Bool
|
||||
s instanceof Builtins::NumericType or
|
||||
s instanceof Builtins::Bool
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -34,11 +34,11 @@ class NumericTypeBarrier extends DataFlow::Node {
|
||||
class IntegralOrBooleanTypeBarrier extends DataFlow::Node {
|
||||
IntegralOrBooleanTypeBarrier() {
|
||||
exists(StructType t, Struct s |
|
||||
t = TypeInference::inferType(this.asExpr().getExpr()) and
|
||||
t = TypeInference::inferType(this.asExpr()) and
|
||||
s = t.getStruct()
|
||||
|
|
||||
s instanceof IntegralType or
|
||||
s instanceof Bool
|
||||
s instanceof Builtins::IntegralType or
|
||||
s instanceof Builtins::Bool
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -48,11 +48,11 @@ class IntegralOrBooleanTypeBarrier extends DataFlow::Node {
|
||||
* sub-expression `node` is not null. For example when `ptr.is_null()` is
|
||||
* `false`, we have that `ptr` is not null.
|
||||
*/
|
||||
private predicate notNullCheck(CfgNodes::AstCfgNode g, Cfg::CfgNode node, boolean branch) {
|
||||
private predicate notNullCheck(AstNode g, Expr e, boolean branch) {
|
||||
exists(MethodCallExpr call |
|
||||
call.getStaticTarget().getName().getText() = "is_null" and
|
||||
g = call.getACfgNode() and
|
||||
node = call.getReceiver().getACfgNode() and
|
||||
g = call and
|
||||
e = call.getReceiver() and
|
||||
branch = false
|
||||
)
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ module HardcodedCryptographicValue {
|
||||
* A literal, considered as a flow source.
|
||||
*/
|
||||
private class LiteralSource extends Source {
|
||||
LiteralSource() { this.asExpr().getExpr() instanceof LiteralExpr }
|
||||
LiteralSource() { this.asExpr() instanceof LiteralExpr }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -75,8 +75,8 @@ module HardcodedCryptographicValue {
|
||||
*/
|
||||
private class ArrayListSource extends Source {
|
||||
ArrayListSource() {
|
||||
this.asExpr().getExpr().(ArrayListExpr).getExpr(_) instanceof LiteralExpr or
|
||||
this.asExpr().getExpr().(ArrayRepeatExpr).getRepeatOperand() instanceof LiteralExpr
|
||||
this.asExpr().(ArrayListExpr).getExpr(_) instanceof LiteralExpr or
|
||||
this.asExpr().(ArrayRepeatExpr).getRepeatOperand() instanceof LiteralExpr
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ module HardcodedCryptographicValue {
|
||||
exists(CallExprBase ce |
|
||||
ce.getStaticTarget().(Addressable).getCanonicalPath() =
|
||||
["getrandom::fill", "getrandom::getrandom"] and
|
||||
this.asExpr().getExpr().getParentNode*() = ce.getArgList().getArg(0)
|
||||
this.asExpr().getParentNode*() = ce.getArgList().getArg(0)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,13 +85,13 @@ module InsecureCookie {
|
||||
cookieOptionalBarrier(summaryNode, attrib, arg) and
|
||||
// find a call and arg referenced by this optional barrier
|
||||
ce.getStaticTarget() = summaryNode.getSummarizedCallable() and
|
||||
ce.getArg(arg) = argNode.asExpr().getExpr() and
|
||||
ce.getArg(arg) = argNode.asExpr() and
|
||||
// check if the argument is always `true`
|
||||
(
|
||||
if
|
||||
forex(DataFlow::Node argSourceNode, BooleanLiteralExpr argSourceValue |
|
||||
DataFlow::localFlow(argSourceNode, argNode) and
|
||||
argSourceValue = argSourceNode.asExpr().getExpr()
|
||||
argSourceValue = argSourceNode.asExpr()
|
||||
|
|
||||
argSourceValue.getTextValue() = "true"
|
||||
)
|
||||
@@ -101,7 +101,7 @@ module InsecureCookie {
|
||||
// and find the node where this happens (we can't just use the flow summary node, since its
|
||||
// shared across all calls to the modeled function, we need a node specific to this call)
|
||||
(
|
||||
node.asExpr().getExpr() = ce.(MethodCallExpr).getReceiver() // e.g. `a` in `a.set_secure(true)`
|
||||
node.asExpr() = ce.(MethodCallExpr).getReceiver() // e.g. `a` in `a.set_secure(true)`
|
||||
or
|
||||
exists(BasicBlock bb, int i |
|
||||
// associated SSA node
|
||||
|
||||
@@ -29,7 +29,7 @@ private class SensitiveDataCall extends SensitiveData {
|
||||
|
||||
SensitiveDataCall() {
|
||||
exists(CallExprBase call, string name |
|
||||
call = this.asExpr().getExpr() and
|
||||
call = this.asExpr() and
|
||||
name =
|
||||
[
|
||||
call.getStaticTarget().(Function).getName().getText(),
|
||||
@@ -50,7 +50,6 @@ private class SensitiveVariableAccess extends SensitiveData {
|
||||
|
||||
SensitiveVariableAccess() {
|
||||
HeuristicNames::nameIndicatesSensitiveData(this.asExpr()
|
||||
.getExpr()
|
||||
.(VariableAccess)
|
||||
.getVariable()
|
||||
.(Variable)
|
||||
@@ -69,7 +68,7 @@ private class SensitiveFieldAccess extends SensitiveData {
|
||||
SensitiveDataClassification classification;
|
||||
|
||||
SensitiveFieldAccess() {
|
||||
exists(FieldExpr fe | fieldExprParentField*(fe) = this.asExpr().getExpr() |
|
||||
exists(FieldExpr fe | fieldExprParentField*(fe) = this.asExpr() |
|
||||
HeuristicNames::nameIndicatesSensitiveData(fe.getIdentifier().getText(), classification)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ private import codeql.rust.dataflow.TaintTracking
|
||||
private import codeql.rust.Concepts
|
||||
private import codeql.rust.dataflow.internal.DataFlowImpl
|
||||
private import codeql.rust.controlflow.ControlFlowGraph as Cfg
|
||||
private import codeql.rust.controlflow.CfgNodes as CfgNodes
|
||||
|
||||
/**
|
||||
* Provides default sources, sinks and barriers for detecting path injection
|
||||
@@ -50,16 +49,16 @@ module TaintedPath {
|
||||
}
|
||||
}
|
||||
|
||||
private predicate sanitizerGuard(CfgNodes::AstCfgNode g, Cfg::CfgNode node, boolean branch) {
|
||||
g.(SanitizerGuard::Range).checks(node, branch)
|
||||
private predicate sanitizerGuard(AstNode g, Expr e, boolean branch) {
|
||||
g.(SanitizerGuard::Range).checks(e, branch)
|
||||
}
|
||||
|
||||
/** Provides a class for modeling new path safety checks. */
|
||||
module SanitizerGuard {
|
||||
/** A data-flow node that checks that a path is safe to access. */
|
||||
abstract class Range extends CfgNodes::AstCfgNode {
|
||||
/** Holds if this guard validates `node` upon evaluating to `branch`. */
|
||||
abstract predicate checks(Cfg::CfgNode node, boolean branch);
|
||||
abstract class Range extends AstNode {
|
||||
/** Holds if this guard validates `e` upon evaluating to `branch`. */
|
||||
abstract predicate checks(Expr e, boolean branch);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,15 +66,14 @@ module SanitizerGuard {
|
||||
* A check of the form `!strings.Contains(nd, "..")`, considered as a sanitizer guard for
|
||||
* path traversal.
|
||||
*/
|
||||
private class DotDotCheck extends SanitizerGuard::Range, CfgNodes::MethodCallExprCfgNode {
|
||||
private class DotDotCheck extends SanitizerGuard::Range, MethodCallExpr {
|
||||
DotDotCheck() {
|
||||
this.getAstNode().(CallExprBase).getStaticTarget().(Addressable).getCanonicalPath() =
|
||||
this.getStaticTarget().(Addressable).getCanonicalPath() =
|
||||
["<alloc::string::String>::contains", "<core::str>::contains"] and
|
||||
this.getArgument(0).getAstNode().(LiteralExpr).getTextValue() =
|
||||
["\"..\"", "\"../\"", "\"..\\\""]
|
||||
this.getArg(0).(LiteralExpr).getTextValue() = ["\"..\"", "\"../\"", "\"..\\\""]
|
||||
}
|
||||
|
||||
override predicate checks(Cfg::CfgNode e, boolean branch) {
|
||||
override predicate checks(Expr e, boolean branch) {
|
||||
e = this.getReceiver() and
|
||||
branch = false
|
||||
}
|
||||
|
||||
@@ -7,8 +7,6 @@ import rust
|
||||
private import codeql.rust.Concepts
|
||||
private import codeql.rust.dataflow.DataFlow
|
||||
private import codeql.rust.dataflow.FlowSink
|
||||
private import codeql.rust.controlflow.ControlFlowGraph as Cfg
|
||||
private import codeql.rust.controlflow.CfgNodes as CfgNodes
|
||||
|
||||
/**
|
||||
* Provides default sources, sinks and barriers for detecting uncontrolled
|
||||
@@ -45,23 +43,24 @@ module UncontrolledAllocationSize {
|
||||
|
||||
/**
|
||||
* Holds if comparison `g` having result `branch` indicates an upper bound for the sub-expression
|
||||
* `node`. For example when the comparison `x < 10` is true, we have an upper bound for `x`.
|
||||
* `e`. For example when the comparison `x < 10` is true, we have an upper bound for `x`.
|
||||
*/
|
||||
private predicate isUpperBoundCheck(CfgNodes::AstCfgNode g, Cfg::CfgNode node, boolean branch) {
|
||||
exists(BinaryExpr cmp | g = cmp.getACfgNode() |
|
||||
node = cmp.(RelationalOperation).getLesserOperand().getACfgNode() and
|
||||
branch = true
|
||||
or
|
||||
node = cmp.(RelationalOperation).getGreaterOperand().getACfgNode() and
|
||||
branch = false
|
||||
or
|
||||
cmp instanceof EqualsOperation and
|
||||
[cmp.getLhs(), cmp.getRhs()].getACfgNode() = node and
|
||||
branch = true
|
||||
or
|
||||
cmp instanceof NotEqualsOperation and
|
||||
[cmp.getLhs(), cmp.getRhs()].getACfgNode() = node and
|
||||
branch = false
|
||||
)
|
||||
private predicate isUpperBoundCheck(AstNode g, Expr e, boolean branch) {
|
||||
g =
|
||||
any(BinaryExpr cmp |
|
||||
e = cmp.(RelationalOperation).getLesserOperand() and
|
||||
branch = true
|
||||
or
|
||||
e = cmp.(RelationalOperation).getGreaterOperand() and
|
||||
branch = false
|
||||
or
|
||||
cmp instanceof EqualsOperation and
|
||||
[cmp.getLhs(), cmp.getRhs()] = e and
|
||||
branch = true
|
||||
or
|
||||
cmp instanceof NotEqualsOperation and
|
||||
[cmp.getLhs(), cmp.getRhs()] = e and
|
||||
branch = false
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ module UseOfHttp {
|
||||
* An HTTP string literal as a source.
|
||||
*/
|
||||
private class HttpStringLiteralAsSource extends Source {
|
||||
HttpStringLiteralAsSource() { this.asExpr().getExpr() instanceof HttpStringLiteral }
|
||||
HttpStringLiteralAsSource() { this.asExpr() instanceof HttpStringLiteral }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -189,7 +189,7 @@ class ModeledHashOperation extends Cryptography::CryptographicOperation::Range {
|
||||
exists(CallExpr call |
|
||||
sinkNode(input, "hasher-input") and
|
||||
call = input.(Node::FlowSummaryNode).getSinkElement().getCall() and
|
||||
call = this.asExpr().getExpr() and
|
||||
call = this.asExpr() and
|
||||
algorithmName = call.getFunction().(PathExpr).getPath().getQualifier().getText()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
private import codeql.util.Unit
|
||||
private import rust
|
||||
private import codeql.rust.dataflow.DataFlow
|
||||
private import codeql.rust.controlflow.CfgNodes
|
||||
private import codeql.rust.dataflow.FlowSink
|
||||
private import codeql.rust.Concepts
|
||||
private import codeql.rust.security.Barriers as Barriers
|
||||
@@ -57,8 +56,8 @@ module RegexInjection {
|
||||
exists(CallExprBase call, Addressable a |
|
||||
call.getStaticTarget() = a and
|
||||
a.getCanonicalPath() = "<regex::regex::string::Regex>::new" and
|
||||
this.asExpr().getExpr() = call.getArg(0) and
|
||||
not this.asExpr() instanceof LiteralExprCfgNode
|
||||
this.asExpr() = call.getArg(0) and
|
||||
not this.asExpr() instanceof LiteralExpr
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -78,7 +77,6 @@ module RegexInjection {
|
||||
// A barrier is any call to a function named `escape`, in particular this
|
||||
// makes calls to `regex::escape` a barrier.
|
||||
this.asExpr()
|
||||
.getExpr()
|
||||
.(CallExpr)
|
||||
.getFunction()
|
||||
.(PathExpr)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/rust-all
|
||||
version: 0.1.20-dev
|
||||
version: 0.1.21-dev
|
||||
groups: rust
|
||||
extractor: rust
|
||||
dbscheme: rust.dbscheme
|
||||
|
||||
@@ -18,20 +18,20 @@ private import internal.InlineExpectationsTestImpl as InlineExpectationsTestImpl
|
||||
* representation of the path has `name` as a prefix.
|
||||
*/
|
||||
bindingset[name]
|
||||
private predicate callTargetName(CallExprCfgNode call, string name) {
|
||||
call.getFunction().(PathExprCfgNode).toString().matches(name + "%")
|
||||
private predicate callTargetName(CallExpr call, string name) {
|
||||
call.getFunction().(PathExpr).toString().matches(name + "%")
|
||||
}
|
||||
|
||||
private module FlowTestImpl implements InputSig<Location, RustDataFlow> {
|
||||
predicate defaultSource(DataFlow::Node source) { callTargetName(source.asExpr(), "source") }
|
||||
|
||||
predicate defaultSink(DataFlow::Node sink) {
|
||||
any(CallExprCfgNode call | callTargetName(call, "sink")).getArgument(_) = sink.asExpr()
|
||||
any(CallExpr call | callTargetName(call, "sink")).getAnArg() = sink.asExpr()
|
||||
}
|
||||
|
||||
private string getSourceArgString(DataFlow::Node src) {
|
||||
defaultSource(src) and
|
||||
result = src.asExpr().(CallExprCfgNode).getArgument(0).toString()
|
||||
result = src.asExpr().(CallExpr).getArg(0).toString()
|
||||
or
|
||||
sourceNode(src, _) and
|
||||
result = src.(Node::FlowSummaryNode).getSourceElement().getCall().getArg(0).toString() and
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
## 0.1.20
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Taint flow barriers have been added to the `rust/regex-injection`, `rust/sql-injection` and `rust/log-injection`, reducing the frequency of false positive results for these queries.
|
||||
|
||||
## 0.1.19
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
4
rust/ql/src/change-notes/2025-11-07-example-queries.md
Normal file
4
rust/ql/src/change-notes/2025-11-07-example-queries.md
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: newQuery
|
||||
---
|
||||
* Added three example queries (`rust/examples/empty-if`, `rust/examples/simple-sql-injection` and `rust/examples/simple-constant-password`) to help developers learn to write CodeQL queries for Rust.
|
||||
@@ -1,4 +1,5 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
## 0.1.20
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Taint flow barriers have been added to the `rust/regex-injection`, `rust/sql-injection` and `rust/log-injection`, reducing the frequency of false positive results for these queries.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.1.19
|
||||
lastReleaseVersion: 0.1.20
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/rust-queries
|
||||
version: 0.1.20-dev
|
||||
version: 0.1.21-dev
|
||||
groups:
|
||||
- rust
|
||||
- queries
|
||||
|
||||
@@ -37,7 +37,7 @@ module CleartextLoggingConfig implements DataFlow::ConfigSig {
|
||||
|
||||
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
// flow from `a` to `&a`
|
||||
node2.asExpr().getExpr().(RefExpr).getExpr() = node1.asExpr().getExpr()
|
||||
node2.asExpr().(RefExpr).getExpr() = node1.asExpr()
|
||||
}
|
||||
|
||||
predicate allowImplicitRead(DataFlow::Node node, DataFlow::ContentSet c) {
|
||||
|
||||
@@ -36,7 +36,7 @@ module CleartextStorageDatabaseConfig implements DataFlow::ConfigSig {
|
||||
|
||||
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
// flow from `a` to `&a`
|
||||
node2.asExpr().getExpr().(RefExpr).getExpr() = node1.asExpr().getExpr()
|
||||
node2.asExpr().(RefExpr).getExpr() = node1.asExpr()
|
||||
}
|
||||
|
||||
predicate observeDiffInformedIncrementalMode() { any() }
|
||||
|
||||
@@ -23,40 +23,37 @@ import AccessAfterLifetimeFlow::PathGraph
|
||||
* lifetime has ended.
|
||||
*/
|
||||
module AccessAfterLifetimeConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node node) { node instanceof AccessAfterLifetime::Source }
|
||||
predicate isSource(DataFlow::Node node) {
|
||||
node instanceof AccessAfterLifetime::Source and
|
||||
// exclude cases with sources in macros, since these results are difficult to interpret
|
||||
not node.asExpr().isFromMacroExpansion()
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node node) { node instanceof AccessAfterLifetime::Sink }
|
||||
predicate isSink(DataFlow::Node node) {
|
||||
node instanceof AccessAfterLifetime::Sink and
|
||||
// exclude cases with sinks in macros, since these results are difficult to interpret
|
||||
not node.asExpr().isFromMacroExpansion() and
|
||||
// include only results inside `unsafe` blocks, as other results tend to be false positives
|
||||
(
|
||||
node.asExpr().getEnclosingBlock*().isUnsafe() or
|
||||
node.asExpr().getEnclosingCallable().(Function).isUnsafe()
|
||||
)
|
||||
}
|
||||
|
||||
predicate isBarrier(DataFlow::Node barrier) { barrier instanceof AccessAfterLifetime::Barrier }
|
||||
|
||||
predicate observeDiffInformedIncrementalMode() { any() }
|
||||
|
||||
Location getASelectedSourceLocation(DataFlow::Node source) {
|
||||
exists(Variable target, DataFlow::Node sink |
|
||||
exists(Variable target |
|
||||
AccessAfterLifetime::sourceValueScope(source, target, _) and
|
||||
result = [target.getLocation(), source.getLocation()]
|
||||
|
|
||||
isSink(sink) and
|
||||
narrowDereferenceAfterLifetime(source, sink, target)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module AccessAfterLifetimeFlow = TaintTracking::Global<AccessAfterLifetimeConfig>;
|
||||
|
||||
pragma[inline]
|
||||
predicate narrowDereferenceAfterLifetime(DataFlow::Node source, DataFlow::Node sink, Variable target) {
|
||||
// check that the dereference is outside the lifetime of the target
|
||||
AccessAfterLifetime::dereferenceAfterLifetime(source, sink, target) and
|
||||
// include only results inside `unsafe` blocks, as other results tend to be false positives
|
||||
(
|
||||
sink.asExpr().getExpr().getEnclosingBlock*().isUnsafe() or
|
||||
sink.asExpr().getExpr().getEnclosingCallable().(Function).isUnsafe()
|
||||
) and
|
||||
// exclude cases with sources / sinks in macros, since these results are difficult to interpret
|
||||
not source.asExpr().getExpr().isFromMacroExpansion() and
|
||||
not sink.asExpr().getExpr().isFromMacroExpansion()
|
||||
}
|
||||
|
||||
from
|
||||
AccessAfterLifetimeFlow::PathNode sourceNode, AccessAfterLifetimeFlow::PathNode sinkNode,
|
||||
Variable target
|
||||
@@ -64,6 +61,6 @@ where
|
||||
// flow from a pointer or reference to the dereference
|
||||
AccessAfterLifetimeFlow::flowPath(sourceNode, sinkNode) and
|
||||
// check that the dereference is outside the lifetime of the target
|
||||
narrowDereferenceAfterLifetime(sourceNode.getNode(), sinkNode.getNode(), target)
|
||||
AccessAfterLifetime::dereferenceAfterLifetime(sourceNode.getNode(), sinkNode.getNode(), target)
|
||||
select sinkNode.getNode(), sourceNode, sinkNode,
|
||||
"Access of a pointer to $@ after its lifetime has ended.", target, target.toString()
|
||||
|
||||
@@ -21,7 +21,7 @@ where
|
||||
not write.isFromMacroExpansion() and
|
||||
not isAllowableUnused(v) and
|
||||
// SSA definitions are only created for live writes
|
||||
not write = any(Ssa::WriteDefinition def).getWriteAccess().getAstNode() and
|
||||
not write = any(Ssa::WriteDefinition def).getWriteAccess() and
|
||||
// avoid overlap with the unused variable query
|
||||
not isUnused(v)
|
||||
select write, "Variable $@ is assigned a value that is never used.", v, v.getText()
|
||||
|
||||
@@ -120,7 +120,7 @@ private module SummaryModelGeneratorInput implements SummaryModelGeneratorInputS
|
||||
}
|
||||
|
||||
QualifiedCallable getAsExprEnclosingCallable(NodeExtended node) {
|
||||
result.getFunction() = node.asExpr().getScope()
|
||||
result.getFunction() = node.asExpr().getEnclosingCfgScope()
|
||||
}
|
||||
|
||||
Parameter asParameter(NodeExtended node) { result = node.asParameter() }
|
||||
|
||||
@@ -16,30 +16,32 @@ stringLiteral
|
||||
| literal.rs:21:5:21:8 | r"R" |
|
||||
| literal.rs:22:5:22:11 | "\\\\x52" |
|
||||
| literal.rs:23:5:23:11 | r"\\x52" |
|
||||
| literal.rs:25:5:29:5 | "\n A normal string literal\n... |
|
||||
| literal.rs:31:5:34:6 | r#"\n A raw string literal\n ... |
|
||||
integerLiteral
|
||||
| literal.rs:28:5:28:7 | 123 | |
|
||||
| literal.rs:29:5:29:10 | 123i32 | i32 |
|
||||
| literal.rs:30:5:30:10 | 123u32 | u32 |
|
||||
| literal.rs:31:5:31:11 | 123_u32 | u32 |
|
||||
| literal.rs:33:5:33:8 | 0xff | |
|
||||
| literal.rs:34:5:34:11 | 0xff_u8 | u8 |
|
||||
| literal.rs:35:5:35:12 | 0x01_f32 | |
|
||||
| literal.rs:36:5:36:11 | 0x01_e3 | |
|
||||
| literal.rs:38:5:38:8 | 0o70 | |
|
||||
| literal.rs:39:5:39:12 | 0o70_i16 | i16 |
|
||||
| literal.rs:41:5:41:25 | 0b1111_1111_1001_0000 | |
|
||||
| literal.rs:42:5:42:28 | 0b1111_1111_1001_0000i64 | i64 |
|
||||
| literal.rs:43:5:43:15 | 0b________1 | |
|
||||
| literal.rs:45:5:45:10 | 0usize | usize |
|
||||
| literal.rs:48:5:49:10 | 128_i8 | i8 |
|
||||
| literal.rs:50:5:51:10 | 256_u8 | u8 |
|
||||
| literal.rs:39:5:39:7 | 123 | |
|
||||
| literal.rs:40:5:40:10 | 123i32 | i32 |
|
||||
| literal.rs:41:5:41:10 | 123u32 | u32 |
|
||||
| literal.rs:42:5:42:11 | 123_u32 | u32 |
|
||||
| literal.rs:44:5:44:8 | 0xff | |
|
||||
| literal.rs:45:5:45:11 | 0xff_u8 | u8 |
|
||||
| literal.rs:46:5:46:12 | 0x01_f32 | |
|
||||
| literal.rs:47:5:47:11 | 0x01_e3 | |
|
||||
| literal.rs:49:5:49:8 | 0o70 | |
|
||||
| literal.rs:50:5:50:12 | 0o70_i16 | i16 |
|
||||
| literal.rs:52:5:52:25 | 0b1111_1111_1001_0000 | |
|
||||
| literal.rs:53:5:53:28 | 0b1111_1111_1001_0000i64 | i64 |
|
||||
| literal.rs:54:5:54:15 | 0b________1 | |
|
||||
| literal.rs:56:5:56:10 | 0usize | usize |
|
||||
| literal.rs:59:5:60:10 | 128_i8 | i8 |
|
||||
| literal.rs:61:5:62:10 | 256_u8 | u8 |
|
||||
floatLiteral
|
||||
| literal.rs:56:5:56:8 | 5f32 | f32 |
|
||||
| literal.rs:58:5:58:12 | 123.0f64 | f64 |
|
||||
| literal.rs:59:5:59:10 | 0.1f64 | f64 |
|
||||
| literal.rs:60:5:60:10 | 0.1f32 | f32 |
|
||||
| literal.rs:61:5:61:14 | 12E+99_f64 | f64 |
|
||||
| literal.rs:62:18:62:19 | 2. | |
|
||||
| literal.rs:67:5:67:8 | 5f32 | f32 |
|
||||
| literal.rs:69:5:69:12 | 123.0f64 | f64 |
|
||||
| literal.rs:70:5:70:10 | 0.1f64 | f64 |
|
||||
| literal.rs:71:5:71:10 | 0.1f32 | f32 |
|
||||
| literal.rs:72:5:72:14 | 12E+99_f64 | f64 |
|
||||
| literal.rs:73:18:73:19 | 2. | |
|
||||
booleanLiteral
|
||||
| literal.rs:66:5:66:8 | true |
|
||||
| literal.rs:67:5:67:9 | false |
|
||||
| literal.rs:77:5:77:8 | true |
|
||||
| literal.rs:78:5:78:9 | false |
|
||||
|
||||
@@ -21,6 +21,17 @@ fn string_literals() {
|
||||
r"R"; // R
|
||||
"\\x52";
|
||||
r"\x52"; // \x52
|
||||
|
||||
"
|
||||
A normal string literal
|
||||
across many
|
||||
lines
|
||||
";
|
||||
|
||||
r#"
|
||||
A raw string literal
|
||||
across multiple lines
|
||||
"#;
|
||||
}
|
||||
|
||||
fn integer_literals() {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
models
|
||||
edges
|
||||
| main.rs:9:13:9:19 | ...: ... | main.rs:10:11:10:11 | s | provenance | |
|
||||
| main.rs:10:11:10:11 | s | main.rs:9:30:14:1 | { ... } | provenance | |
|
||||
| main.rs:10:11:10:11 | s | main.rs:12:9:12:9 | s | provenance | |
|
||||
| main.rs:12:9:12:9 | s | main.rs:9:30:14:1 | { ... } | provenance | |
|
||||
| main.rs:21:9:21:9 | s | main.rs:22:10:22:10 | s | provenance | |
|
||||
| main.rs:21:13:21:21 | source(...) | main.rs:21:9:21:9 | s | provenance | |
|
||||
| main.rs:26:9:26:9 | s | main.rs:27:22:27:22 | s | provenance | |
|
||||
@@ -16,6 +17,7 @@ nodes
|
||||
| main.rs:9:13:9:19 | ...: ... | semmle.label | ...: ... |
|
||||
| main.rs:9:30:14:1 | { ... } | semmle.label | { ... } |
|
||||
| main.rs:10:11:10:11 | s | semmle.label | s |
|
||||
| main.rs:12:9:12:9 | s | semmle.label | s |
|
||||
| main.rs:17:10:17:18 | source(...) | semmle.label | source(...) |
|
||||
| main.rs:21:9:21:9 | s | semmle.label | s |
|
||||
| main.rs:21:13:21:21 | source(...) | semmle.label | source(...) |
|
||||
|
||||
@@ -82,12 +82,14 @@ localStep
|
||||
| main.rs:57:9:57:9 | a | main.rs:57:9:57:9 | [SSA] a |
|
||||
| main.rs:57:9:57:9 | a | main.rs:57:9:57:9 | a |
|
||||
| main.rs:57:13:59:5 | loop { ... } | main.rs:57:9:57:9 | a |
|
||||
| main.rs:57:18:59:5 | { ... } | main.rs:57:13:59:5 | loop { ... } |
|
||||
| main.rs:58:9:58:15 | break 1 | main.rs:57:13:59:5 | loop { ... } |
|
||||
| main.rs:58:15:58:15 | 1 | main.rs:58:9:58:15 | break 1 |
|
||||
| main.rs:61:9:61:9 | [SSA] b | main.rs:64:10:64:10 | b |
|
||||
| main.rs:61:9:61:9 | b | main.rs:61:9:61:9 | [SSA] b |
|
||||
| main.rs:61:9:61:9 | b | main.rs:61:9:61:9 | b |
|
||||
| main.rs:61:13:63:5 | loop { ... } | main.rs:61:9:61:9 | b |
|
||||
| main.rs:61:18:63:5 | { ... } | main.rs:61:13:63:5 | loop { ... } |
|
||||
| main.rs:62:9:62:23 | break ... | main.rs:61:13:63:5 | loop { ... } |
|
||||
| main.rs:62:15:62:23 | source(...) | main.rs:62:9:62:23 | break ... |
|
||||
| main.rs:68:9:68:13 | mut i | main.rs:68:13:68:13 | i |
|
||||
@@ -131,6 +133,7 @@ localStep
|
||||
| main.rs:92:9:92:9 | a | main.rs:92:9:92:9 | [SSA] a |
|
||||
| main.rs:92:9:92:9 | a | main.rs:92:9:92:9 | a |
|
||||
| main.rs:92:13:97:5 | 'block: { ... } | main.rs:92:9:92:9 | a |
|
||||
| main.rs:93:14:95:9 | { ... } | main.rs:93:9:95:9 | if b {...} |
|
||||
| main.rs:94:13:94:26 | break 'block 1 | main.rs:92:13:97:5 | 'block: { ... } |
|
||||
| main.rs:94:26:94:26 | 1 | main.rs:94:13:94:26 | break 'block 1 |
|
||||
| main.rs:96:9:96:9 | 2 | main.rs:92:13:97:5 | 'block: { ... } |
|
||||
@@ -143,6 +146,7 @@ localStep
|
||||
| main.rs:102:9:102:9 | a | main.rs:102:9:102:9 | [SSA] a |
|
||||
| main.rs:102:9:102:9 | a | main.rs:102:9:102:9 | a |
|
||||
| main.rs:102:13:107:5 | 'block: { ... } | main.rs:102:9:102:9 | a |
|
||||
| main.rs:103:14:105:9 | { ... } | main.rs:103:9:105:9 | if b {...} |
|
||||
| main.rs:104:13:104:26 | break 'block 1 | main.rs:102:13:107:5 | 'block: { ... } |
|
||||
| main.rs:104:26:104:26 | 1 | main.rs:104:13:104:26 | break 'block 1 |
|
||||
| main.rs:106:9:106:22 | break 'block 2 | main.rs:102:13:107:5 | 'block: { ... } |
|
||||
@@ -713,6 +717,7 @@ localStep
|
||||
| main.rs:480:16:480:19 | name | main.rs:480:16:480:19 | [SSA] name |
|
||||
| main.rs:480:16:480:19 | name | main.rs:480:16:480:19 | name |
|
||||
| main.rs:481:9:485:9 | if cond {...} | main.rs:480:31:486:5 | { ... } |
|
||||
| main.rs:481:17:485:9 | { ... } | main.rs:481:9:485:9 | if cond {...} |
|
||||
| main.rs:482:17:482:17 | [SSA] n | main.rs:483:18:483:18 | n |
|
||||
| main.rs:482:17:482:17 | n | main.rs:482:17:482:17 | [SSA] n |
|
||||
| main.rs:482:17:482:17 | n | main.rs:482:17:482:17 | n |
|
||||
|
||||
@@ -163,8 +163,9 @@ edges
|
||||
| main.rs:352:11:352:12 | s1 [A] | main.rs:356:11:356:12 | s1 [A] | provenance | |
|
||||
| main.rs:353:9:353:25 | ...::A(...) [A] | main.rs:353:24:353:24 | n | provenance | |
|
||||
| main.rs:353:24:353:24 | n | main.rs:353:35:353:35 | n | provenance | |
|
||||
| main.rs:356:11:356:12 | s1 [A] | main.rs:357:9:357:25 | ...::A(...) [A] | provenance | |
|
||||
| main.rs:356:11:356:12 | s1 [A] | main.rs:357:9:357:45 | ... \| ... [A] | provenance | |
|
||||
| main.rs:357:9:357:25 | ...::A(...) [A] | main.rs:357:24:357:24 | n | provenance | |
|
||||
| main.rs:357:9:357:45 | ... \| ... [A] | main.rs:357:9:357:25 | ...::A(...) [A] | provenance | |
|
||||
| main.rs:357:24:357:24 | n | main.rs:357:55:357:55 | n | provenance | |
|
||||
| main.rs:368:9:368:10 | s1 [A] | main.rs:370:11:370:12 | s1 [A] | provenance | |
|
||||
| main.rs:368:14:368:26 | A(...) [A] | main.rs:368:9:368:10 | s1 [A] | provenance | |
|
||||
@@ -173,8 +174,9 @@ edges
|
||||
| main.rs:370:11:370:12 | s1 [A] | main.rs:374:11:374:12 | s1 [A] | provenance | |
|
||||
| main.rs:371:9:371:12 | A(...) [A] | main.rs:371:11:371:11 | n | provenance | |
|
||||
| main.rs:371:11:371:11 | n | main.rs:371:22:371:22 | n | provenance | |
|
||||
| main.rs:374:11:374:12 | s1 [A] | main.rs:375:9:375:12 | A(...) [A] | provenance | |
|
||||
| main.rs:374:11:374:12 | s1 [A] | main.rs:375:9:375:19 | ... \| ... [A] | provenance | |
|
||||
| main.rs:375:9:375:12 | A(...) [A] | main.rs:375:11:375:11 | n | provenance | |
|
||||
| main.rs:375:9:375:19 | ... \| ... [A] | main.rs:375:9:375:12 | A(...) [A] | provenance | |
|
||||
| main.rs:375:11:375:11 | n | main.rs:375:29:375:29 | n | provenance | |
|
||||
| main.rs:389:9:389:10 | s1 [C] | main.rs:393:11:393:12 | s1 [C] | provenance | |
|
||||
| main.rs:389:14:391:5 | ...::C {...} [C] | main.rs:389:9:389:10 | s1 [C] | provenance | |
|
||||
@@ -183,8 +185,9 @@ edges
|
||||
| main.rs:393:11:393:12 | s1 [C] | main.rs:397:11:397:12 | s1 [C] | provenance | |
|
||||
| main.rs:394:9:394:38 | ...::C {...} [C] | main.rs:394:36:394:36 | n | provenance | |
|
||||
| main.rs:394:36:394:36 | n | main.rs:394:48:394:48 | n | provenance | |
|
||||
| main.rs:397:11:397:12 | s1 [C] | main.rs:398:9:398:38 | ...::C {...} [C] | provenance | |
|
||||
| main.rs:397:11:397:12 | s1 [C] | main.rs:398:9:398:71 | ... \| ... [C] | provenance | |
|
||||
| main.rs:398:9:398:38 | ...::C {...} [C] | main.rs:398:36:398:36 | n | provenance | |
|
||||
| main.rs:398:9:398:71 | ... \| ... [C] | main.rs:398:9:398:38 | ...::C {...} [C] | provenance | |
|
||||
| main.rs:398:36:398:36 | n | main.rs:398:81:398:81 | n | provenance | |
|
||||
| main.rs:409:9:409:10 | s1 [C] | main.rs:413:11:413:12 | s1 [C] | provenance | |
|
||||
| main.rs:409:14:411:5 | C {...} [C] | main.rs:409:9:409:10 | s1 [C] | provenance | |
|
||||
@@ -193,8 +196,9 @@ edges
|
||||
| main.rs:413:11:413:12 | s1 [C] | main.rs:417:11:417:12 | s1 [C] | provenance | |
|
||||
| main.rs:414:9:414:24 | C {...} [C] | main.rs:414:22:414:22 | n | provenance | |
|
||||
| main.rs:414:22:414:22 | n | main.rs:414:34:414:34 | n | provenance | |
|
||||
| main.rs:417:11:417:12 | s1 [C] | main.rs:418:9:418:24 | C {...} [C] | provenance | |
|
||||
| main.rs:417:11:417:12 | s1 [C] | main.rs:418:9:418:43 | ... \| ... [C] | provenance | |
|
||||
| main.rs:418:9:418:24 | C {...} [C] | main.rs:418:22:418:22 | n | provenance | |
|
||||
| main.rs:418:9:418:43 | ... \| ... [C] | main.rs:418:9:418:24 | C {...} [C] | provenance | |
|
||||
| main.rs:418:22:418:22 | n | main.rs:418:53:418:53 | n | provenance | |
|
||||
| main.rs:430:9:430:12 | arr1 [element] | main.rs:431:14:431:17 | arr1 [element] | provenance | |
|
||||
| main.rs:430:16:430:33 | [...] [element] | main.rs:430:9:430:12 | arr1 [element] | provenance | |
|
||||
@@ -443,6 +447,7 @@ nodes
|
||||
| main.rs:353:35:353:35 | n | semmle.label | n |
|
||||
| main.rs:356:11:356:12 | s1 [A] | semmle.label | s1 [A] |
|
||||
| main.rs:357:9:357:25 | ...::A(...) [A] | semmle.label | ...::A(...) [A] |
|
||||
| main.rs:357:9:357:45 | ... \| ... [A] | semmle.label | ... \| ... [A] |
|
||||
| main.rs:357:24:357:24 | n | semmle.label | n |
|
||||
| main.rs:357:55:357:55 | n | semmle.label | n |
|
||||
| main.rs:368:9:368:10 | s1 [A] | semmle.label | s1 [A] |
|
||||
@@ -454,6 +459,7 @@ nodes
|
||||
| main.rs:371:22:371:22 | n | semmle.label | n |
|
||||
| main.rs:374:11:374:12 | s1 [A] | semmle.label | s1 [A] |
|
||||
| main.rs:375:9:375:12 | A(...) [A] | semmle.label | A(...) [A] |
|
||||
| main.rs:375:9:375:19 | ... \| ... [A] | semmle.label | ... \| ... [A] |
|
||||
| main.rs:375:11:375:11 | n | semmle.label | n |
|
||||
| main.rs:375:29:375:29 | n | semmle.label | n |
|
||||
| main.rs:389:9:389:10 | s1 [C] | semmle.label | s1 [C] |
|
||||
@@ -465,6 +471,7 @@ nodes
|
||||
| main.rs:394:48:394:48 | n | semmle.label | n |
|
||||
| main.rs:397:11:397:12 | s1 [C] | semmle.label | s1 [C] |
|
||||
| main.rs:398:9:398:38 | ...::C {...} [C] | semmle.label | ...::C {...} [C] |
|
||||
| main.rs:398:9:398:71 | ... \| ... [C] | semmle.label | ... \| ... [C] |
|
||||
| main.rs:398:36:398:36 | n | semmle.label | n |
|
||||
| main.rs:398:81:398:81 | n | semmle.label | n |
|
||||
| main.rs:409:9:409:10 | s1 [C] | semmle.label | s1 [C] |
|
||||
@@ -476,6 +483,7 @@ nodes
|
||||
| main.rs:414:34:414:34 | n | semmle.label | n |
|
||||
| main.rs:417:11:417:12 | s1 [C] | semmle.label | s1 [C] |
|
||||
| main.rs:418:9:418:24 | C {...} [C] | semmle.label | C {...} [C] |
|
||||
| main.rs:418:9:418:43 | ... \| ... [C] | semmle.label | ... \| ... [C] |
|
||||
| main.rs:418:22:418:22 | n | semmle.label | n |
|
||||
| main.rs:418:53:418:53 | n | semmle.label | n |
|
||||
| main.rs:430:9:430:12 | arr1 [element] | semmle.label | arr1 [element] |
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
multipleCallTargets
|
||||
| main.rs:389:14:389:30 | ... .lt(...) |
|
||||
@@ -16,7 +16,7 @@ module MyFlowConfig implements DataFlow::ConfigSig {
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
any(CallExpr call |
|
||||
call.getFunction().(PathExpr).getPath().getSegment().getIdentifier().getText() = "sink"
|
||||
).getArgList().getAnArg() = sink.asExpr().getExpr()
|
||||
).getArgList().getAnArg() = sink.asExpr()
|
||||
}
|
||||
|
||||
predicate allowImplicitRead(DataFlow::Node node, DataFlow::ContentSet c) {
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
multipleCallTargets
|
||||
| test.rs:31:22:31:72 | ... .read_to_string(...) |
|
||||
@@ -1,3 +1,20 @@
|
||||
| struct Array | |
|
||||
| struct Ptr | |
|
||||
| struct Ref | |
|
||||
| struct Slice | |
|
||||
| struct Tuple0 | |
|
||||
| struct Tuple1 | |
|
||||
| struct Tuple2 | |
|
||||
| struct Tuple3 | |
|
||||
| struct Tuple4 | |
|
||||
| struct Tuple5 | |
|
||||
| struct Tuple6 | |
|
||||
| struct Tuple7 | |
|
||||
| struct Tuple8 | |
|
||||
| struct Tuple9 | |
|
||||
| struct Tuple10 | |
|
||||
| struct Tuple11 | |
|
||||
| struct Tuple12 | |
|
||||
| struct bool | |
|
||||
| struct char | |
|
||||
| struct f32 | FloatingPointType, NumericType |
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import rust
|
||||
import codeql.rust.frameworks.stdlib.Builtins
|
||||
import codeql.rust.internal.Type
|
||||
|
||||
string describe(BuiltinType t) {
|
||||
t instanceof NumericType and result = "NumericType"
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
multipleCallTargets
|
||||
| test.rs:288:7:288:36 | ... .as_str() |
|
||||
@@ -13,7 +13,7 @@ module SensitiveDataConfig implements DataFlow::ConfigSig {
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
any(CallExpr call |
|
||||
call.getFunction().(PathExpr).getPath().getSegment().getIdentifier().getText() = "sink"
|
||||
).getArgList().getAnArg() = sink.asExpr().getExpr()
|
||||
).getArgList().getAnArg() = sink.asExpr()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,13 +5,10 @@ multipleCallTargets
|
||||
| dereference.rs:184:17:184:30 | ... .foo() |
|
||||
| dereference.rs:186:17:186:25 | S.bar(...) |
|
||||
| dereference.rs:187:17:187:29 | S.bar(...) |
|
||||
| main.rs:589:9:589:14 | S4.m() |
|
||||
| main.rs:590:9:590:18 | ...::m(...) |
|
||||
| main.rs:591:9:591:20 | ... .m() |
|
||||
| main.rs:592:9:592:24 | ...::m(...) |
|
||||
| main.rs:2524:13:2524:31 | ...::from(...) |
|
||||
| main.rs:2525:13:2525:31 | ...::from(...) |
|
||||
| main.rs:2526:13:2526:31 | ...::from(...) |
|
||||
| main.rs:2532:13:2532:31 | ...::from(...) |
|
||||
| main.rs:2533:13:2533:31 | ...::from(...) |
|
||||
| main.rs:2534:13:2534:31 | ...::from(...) |
|
||||
| main.rs:2634:13:2634:31 | ...::from(...) |
|
||||
| main.rs:2635:13:2635:31 | ...::from(...) |
|
||||
| main.rs:2636:13:2636:31 | ...::from(...) |
|
||||
| main.rs:2642:13:2642:31 | ...::from(...) |
|
||||
| main.rs:2643:13:2643:31 | ...::from(...) |
|
||||
| main.rs:2644:13:2644:31 | ...::from(...) |
|
||||
|
||||
@@ -38,7 +38,7 @@ impl<T> S<T> {
|
||||
fn explicit_monomorphic_dereference() {
|
||||
// Dereference with method call
|
||||
let a1 = MyIntPointer { value: 34i64 };
|
||||
let _b1 = a1.deref(); // $ target=MyIntPointer::deref type=_b1:&T.i64
|
||||
let _b1 = a1.deref(); // $ target=MyIntPointer::deref type=_b1:TRef.i64
|
||||
|
||||
// Dereference with overloaded dereference operator
|
||||
let a2 = MyIntPointer { value: 34i64 };
|
||||
@@ -52,7 +52,7 @@ fn explicit_monomorphic_dereference() {
|
||||
fn explicit_polymorphic_dereference() {
|
||||
// Explicit dereference with type parameter
|
||||
let c1 = MySmartPointer { value: 'a' };
|
||||
let _d1 = c1.deref(); // $ target=MySmartPointer::deref type=_d1:&T.char
|
||||
let _d1 = c1.deref(); // $ target=MySmartPointer::deref type=_d1:TRef.char
|
||||
|
||||
// Explicit dereference with type parameter
|
||||
let c2 = MySmartPointer { value: 'a' };
|
||||
@@ -66,7 +66,7 @@ fn explicit_polymorphic_dereference() {
|
||||
fn explicit_ref_dereference() {
|
||||
// Explicit dereference with type parameter
|
||||
let e1 = &'a';
|
||||
let _f1 = e1.deref(); // $ target=deref type=_f1:&T.char
|
||||
let _f1 = e1.deref(); // $ target=deref type=_f1:TRef.char
|
||||
|
||||
// Explicit dereference with type parameter
|
||||
let e2 = &'a';
|
||||
@@ -80,7 +80,7 @@ fn explicit_ref_dereference() {
|
||||
fn explicit_box_dereference() {
|
||||
// Explicit dereference with type parameter
|
||||
let g1: Box<char> = Box::new('a'); // $ target=new
|
||||
let _h1 = g1.deref(); // $ target=deref type=_h1:&T.char
|
||||
let _h1 = g1.deref(); // $ target=deref type=_h1:TRef.char
|
||||
|
||||
// Explicit dereference with type parameter
|
||||
let g2: Box<char> = Box::new('a'); // $ target=new
|
||||
@@ -101,7 +101,7 @@ fn implicit_dereference() {
|
||||
let _y = x.is_positive(); // $ MISSING: target=is_positive type=_y:bool
|
||||
|
||||
let z = MySmartPointer { value: S(0i64) };
|
||||
let z_ = z.foo(); // $ MISSING: target=foo type=z_:&T.i64
|
||||
let z_ = z.foo(); // $ MISSING: target=foo type=z_:TRef.i64
|
||||
}
|
||||
|
||||
mod implicit_deref_coercion_cycle {
|
||||
|
||||
@@ -586,10 +586,10 @@ mod impl_overlap {
|
||||
println!("{:?}", w.m(x)); // $ target=S3<T>::m
|
||||
println!("{:?}", S3::m(&w, x)); // $ target=S3<T>::m
|
||||
|
||||
S4.m(); // $ target=<S4_as_MyTrait1>::m $ SPURIOUS: target=MyTrait1::m
|
||||
S4.m(); // $ target=<S4_as_MyTrait1>::m
|
||||
S4::m(&S4); // $ target=<S4_as_MyTrait1>::m $ SPURIOUS: target=MyTrait1::m
|
||||
S5(0i32).m(); // $ target=<S5<i32>_as_MyTrait1>::m $ SPURIOUS: target=MyTrait1::m
|
||||
S5::m(&S5(0i32)); // $ target=<S5<i32>_as_MyTrait1>::m $ SPURIOUS: target=MyTrait1::m
|
||||
S5(0i32).m(); // $ target=<S5<i32>_as_MyTrait1>::m
|
||||
S5::m(&S5(0i32)); // $ target=<S5<i32>_as_MyTrait1>::m
|
||||
S5(true).m(); // $ target=MyTrait1::m
|
||||
S5::m(&S5(true)); // $ target=MyTrait1::m
|
||||
}
|
||||
@@ -758,6 +758,26 @@ mod function_trait_bounds {
|
||||
fn assoc(x: Self) -> A;
|
||||
}
|
||||
|
||||
impl<T: Default> MyTrait<T> for S2 {
|
||||
fn m1(self) -> T {
|
||||
Default::default() // $ target=default
|
||||
}
|
||||
|
||||
fn assoc(x: Self) -> T {
|
||||
Default::default() // $ target=default
|
||||
}
|
||||
}
|
||||
|
||||
impl MyTrait<i32> for S1 {
|
||||
fn m1(self) -> i32 {
|
||||
0
|
||||
}
|
||||
|
||||
fn assoc(x: Self) -> i32 {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
// Type parameter with bound occurs in the root of a parameter type.
|
||||
|
||||
fn call_trait_m1<T1, T2: MyTrait<T1> + Copy>(x: T2) -> T1 {
|
||||
@@ -863,6 +883,8 @@ mod function_trait_bounds {
|
||||
println!("{:?}", b);
|
||||
let b = call_trait_thing_m1_3(y3); // $ type=b:S2 target=call_trait_thing_m1_3
|
||||
println!("{:?}", b);
|
||||
let x = S1::m2(S1); // $ target=m2 $ type=x:i32
|
||||
let y: i32 = S2::m2(S2); // $ target=m2
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1520,7 +1542,7 @@ mod method_call_type_conversion {
|
||||
let x7 = S(&S2);
|
||||
// Non-implicit dereference with nested borrow in order to test that the
|
||||
// implicit dereference handling doesn't affect nested borrows.
|
||||
let t = x7.m1(); // $ target=m1 type=t:& type=t:&T.S2
|
||||
let t = x7.m1(); // $ target=m1 type=t:& type=t:TRef.S2
|
||||
println!("{:?}", x7);
|
||||
|
||||
let x9: String = "Hello".to_string(); // $ certainType=x9:String target=to_string
|
||||
@@ -1576,11 +1598,18 @@ mod implicit_self_borrow {
|
||||
fn foo(&self) -> &Self {
|
||||
self
|
||||
}
|
||||
|
||||
fn bar(&self, x: &Self) -> &Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub fn f() {
|
||||
let x = MyStruct(S);
|
||||
x.foo(); // $ target=foo
|
||||
let x = MyStruct(S);
|
||||
// `&&x` below is Deref coerced to `&x` (see https://doc.rust-lang.org/std/ops/trait.Deref.html#deref-coercion)
|
||||
x.bar(&&x); // $ target=bar
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1703,10 +1732,91 @@ mod builtins {
|
||||
let z = x + y; // $ type=z:i32 target=add
|
||||
let z = x.abs(); // $ target=abs $ type=z:i32
|
||||
let c = 'c'; // $ certainType=c:char
|
||||
let hello = "Hello"; // $ certainType=hello:&T.str
|
||||
let hello = "Hello"; // $ certainType=hello:TRef.str
|
||||
let f = 123.0f64; // $ certainType=f:f64
|
||||
let t = true; // $ certainType=t:bool
|
||||
let f = false; // $ certainType=f:bool
|
||||
|
||||
trait MyTrait<T> {
|
||||
fn my_method(&self) -> &T;
|
||||
|
||||
fn my_func() -> T;
|
||||
}
|
||||
|
||||
impl<T: Default, const N: usize> MyTrait<T> for [T; N] {
|
||||
fn my_method(&self) -> &T {
|
||||
self.get(0).unwrap() // $ MISSING: target=get target=unwrap
|
||||
}
|
||||
|
||||
fn my_func() -> T {
|
||||
T::default() // $ target=default
|
||||
}
|
||||
}
|
||||
|
||||
let x = [1, 2, 3].my_method(); // $ target=my_method type=x:TRef.i32
|
||||
let x = <[_; 3]>::my_method(&[1, 2, 3]); // $ target=my_method type=x:TRef.i32
|
||||
let x = <[i32; 3]>::my_func(); // $ target=my_func type=x:i32
|
||||
|
||||
impl<T: Default> MyTrait<T> for [T] {
|
||||
fn my_method(&self) -> &T {
|
||||
self.get(0).unwrap() // $ target=get target=unwrap
|
||||
}
|
||||
|
||||
fn my_func() -> T {
|
||||
T::default() // $ target=default
|
||||
}
|
||||
}
|
||||
|
||||
let s: &[i32] = &[1, 2, 3];
|
||||
let x = s.my_method(); // $ target=my_method type=x:TRef.i32
|
||||
let x = <[_]>::my_method(s); // $ target=my_method type=x:TRef.i32
|
||||
let x = <[i32]>::my_func(); // $ target=my_func type=x:i32
|
||||
|
||||
impl<T: Default> MyTrait<T> for (T, i32) {
|
||||
fn my_method(&self) -> &T {
|
||||
&self.0 // $ fieldof=Tuple2
|
||||
}
|
||||
|
||||
fn my_func() -> T {
|
||||
T::default() // $ target=default
|
||||
}
|
||||
}
|
||||
|
||||
let p = (42, 7);
|
||||
let x = p.my_method(); // $ target=my_method type=x:TRef.i32
|
||||
let x = <(_, _)>::my_method(&p); // $ target=my_method type=x:TRef.i32
|
||||
let x = <(i32, i32)>::my_func(); // $ target=my_func type=x:i32
|
||||
|
||||
impl<T: Default> MyTrait<T> for &T {
|
||||
fn my_method(&self) -> &T {
|
||||
*self // $ target=deref
|
||||
}
|
||||
|
||||
fn my_func() -> T {
|
||||
T::default() // $ target=default
|
||||
}
|
||||
}
|
||||
|
||||
let r = &42;
|
||||
let x = r.my_method(); // $ target=my_method type=x:TRef.i32
|
||||
let x = <&_>::my_method(&r); // $ target=my_method type=x:TRef.i32
|
||||
let x = <&i32>::my_func(); // $ target=my_func type=x:i32
|
||||
|
||||
impl<T: Default> MyTrait<T> for *mut T {
|
||||
fn my_method(&self) -> &T {
|
||||
unsafe { &**self } // $ target=deref target=deref
|
||||
}
|
||||
|
||||
fn my_func() -> T {
|
||||
T::default() // $ target=default
|
||||
}
|
||||
}
|
||||
|
||||
let mut v = 42;
|
||||
let p: *mut i32 = &mut v;
|
||||
let x = unsafe { p.my_method() }; // $ target=my_method type=x:TRef.i32
|
||||
let x = unsafe { <*mut _>::my_method(&p) }; // $ target=my_method type=x:TRef.i32
|
||||
let x = <*mut i32>::my_func(); // $ target=my_func type=x:i32
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2234,7 +2344,7 @@ mod impl_trait {
|
||||
|
||||
// For this function the `impl` type does not appear in the root of the return type
|
||||
let f = get_a_my_trait3(S1).unwrap().get_a(); // $ target=get_a_my_trait3 target=unwrap target=MyTrait::get_a type=f:S1
|
||||
let g = get_a_my_trait4(S1).0.get_a(); // $ target=get_a_my_trait4 target=MyTrait::get_a type=g:S1
|
||||
let g = get_a_my_trait4(S1).0.get_a(); // $ target=get_a_my_trait4 target=MyTrait::get_a type=g:S1 fieldof=Tuple2
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2502,24 +2612,24 @@ mod loops {
|
||||
for i in [1, 2, 3].map(|x| x + 1) {} // $ target=map MISSING: type=i:i32
|
||||
for i in [1, 2, 3].into_iter() {} // $ target=into_iter type=i:i32
|
||||
|
||||
let vals1 = [1u8, 2, 3]; // $ type=vals1:[T;...].u8
|
||||
let vals1 = [1u8, 2, 3]; // $ type=vals1:TArray.u8
|
||||
for u in vals1 {} // $ type=u:u8
|
||||
|
||||
let vals2 = [1u16; 3]; // $ type=vals2:[T;...].u16
|
||||
let vals2 = [1u16; 3]; // $ type=vals2:TArray.u16
|
||||
for u in vals2 {} // $ type=u:u16
|
||||
|
||||
let vals3: [u32; 3] = [1, 2, 3]; // $ certainType=vals3:[T;...].u32
|
||||
let vals3: [u32; 3] = [1, 2, 3]; // $ certainType=vals3:TArray.u32
|
||||
for u in vals3 {} // $ type=u:u32
|
||||
|
||||
let vals4: [u64; 3] = [1; 3]; // $ certainType=vals4:[T;...].u64
|
||||
let vals4: [u64; 3] = [1; 3]; // $ certainType=vals4:TArray.u64
|
||||
for u in vals4 {} // $ type=u:u64
|
||||
|
||||
let mut strings1 = ["foo", "bar", "baz"]; // $ type=strings1:[T;...].&T.str
|
||||
for s in &strings1 {} // $ type=s:&T.&T.str
|
||||
for s in &mut strings1 {} // $ type=s:&T.&T.str
|
||||
for s in strings1 {} // $ type=s:&T.str
|
||||
let mut strings1 = ["foo", "bar", "baz"]; // $ type=strings1:TArray.TRef.str
|
||||
for s in &strings1 {} // $ type=s:TRef.TRef.str
|
||||
for s in &mut strings1 {} // $ type=s:TRef.TRef.str
|
||||
for s in strings1 {} // $ type=s:TRef.str
|
||||
|
||||
let strings2 = // $ type=strings2:[T;...].String
|
||||
let strings2 = // $ type=strings2:TArray.String
|
||||
[
|
||||
String::from("foo"), // $ target=from
|
||||
String::from("bar"), // $ target=from
|
||||
@@ -2527,15 +2637,15 @@ mod loops {
|
||||
];
|
||||
for s in strings2 {} // $ type=s:String
|
||||
|
||||
let strings3 = // $ type=strings3:&T.[T;...].String
|
||||
let strings3 = // $ type=strings3:TRef.TArray.String
|
||||
&[
|
||||
String::from("foo"), // $ target=from
|
||||
String::from("bar"), // $ target=from
|
||||
String::from("baz"), // $ target=from
|
||||
];
|
||||
for s in strings3 {} // $ type=s:&T.String
|
||||
for s in strings3 {} // $ type=s:TRef.String
|
||||
|
||||
let callables = [MyCallable::new(), MyCallable::new(), MyCallable::new()]; // $ target=new $ type=callables:[T;...].MyCallable
|
||||
let callables = [MyCallable::new(), MyCallable::new(), MyCallable::new()]; // $ target=new $ type=callables:TArray.MyCallable
|
||||
for c // $ type=c:MyCallable
|
||||
in callables
|
||||
{
|
||||
@@ -2549,7 +2659,7 @@ mod loops {
|
||||
let range = 0..10; // $ certainType=range:Range type=range:Idx.i32
|
||||
for i in range {} // $ type=i:i32
|
||||
let range_full = ..; // $ certainType=range_full:RangeFull
|
||||
for i in &[1i64, 2i64, 3i64][range_full] {} // $ target=index MISSING: type=i:&T.i64
|
||||
for i in &[1i64, 2i64, 3i64][range_full] {} // $ target=index MISSING: type=i:TRef.i64
|
||||
|
||||
let range1 = // $ certainType=range1:Range type=range1:Idx.u16
|
||||
std::ops::Range {
|
||||
@@ -2560,7 +2670,7 @@ mod loops {
|
||||
|
||||
// for loops with containers
|
||||
|
||||
let vals3 = vec![1, 2, 3]; // $ MISSING: type=vals3:Vec type=vals3:T.i32
|
||||
let vals3 = vec![1, 2, 3]; // $ type=vals3:Vec $ MISSING: type=vals3:T.i32
|
||||
for i in vals3 {} // $ MISSING: type=i:i32
|
||||
|
||||
let vals4a: Vec<u16> = [1u16, 2, 3].to_vec(); // $ certainType=vals4a:Vec certainType=vals4a:T.u16
|
||||
@@ -2572,27 +2682,27 @@ mod loops {
|
||||
let vals5 = Vec::from([1u32, 2, 3]); // $ certainType=vals5:Vec target=from type=vals5:T.u32
|
||||
for u in vals5 {} // $ type=u:u32
|
||||
|
||||
let vals6: Vec<&u64> = [1u64, 2, 3].iter().collect(); // $ certainType=vals6:Vec certainType=vals6:T.&T.u64
|
||||
for u in vals6 {} // $ type=u:&T.u64
|
||||
let vals6: Vec<&u64> = [1u64, 2, 3].iter().collect(); // $ certainType=vals6:Vec certainType=vals6:T.TRef.u64
|
||||
for u in vals6 {} // $ type=u:TRef.u64
|
||||
|
||||
let mut vals7 = Vec::new(); // $ target=new certainType=vals7:Vec type=vals7:T.u8
|
||||
vals7.push(1u8); // $ target=push
|
||||
for u in vals7 {} // $ type=u:u8
|
||||
|
||||
let matrix1 = vec![vec![1, 2], vec![3, 4]]; // $ MISSING: type=matrix1:Vec type=matrix1:T.Vec type=matrix1:T.T.i32
|
||||
let matrix1 = vec![vec![1, 2], vec![3, 4]]; // $ type=matrix1:Vec $ MISSING: type=matrix1:T.Vec type=matrix1:T.T.i32
|
||||
#[rustfmt::skip]
|
||||
let _ = for row in matrix1 { // $ MISSING: type=row:Vec type=row:T.i32
|
||||
for cell in row { // $ MISSING: type=cell:i32
|
||||
}
|
||||
};
|
||||
|
||||
let mut map1 = std::collections::HashMap::new(); // $ target=new type=map1:K.i32 type=map1:V.Box $ MISSING: type=map1:Hashmap type1=map1:V.T.&T.str
|
||||
let mut map1 = std::collections::HashMap::new(); // $ target=new type=map1:K.i32 type=map1:V.Box $ MISSING: type=map1:Hashmap type1=map1:V.T.TRef.str
|
||||
map1.insert(1, Box::new("one")); // $ target=insert target=new
|
||||
map1.insert(2, Box::new("two")); // $ target=insert target=new
|
||||
for key in map1.keys() {} // $ target=keys type=key:&T.i32
|
||||
for value in map1.values() {} // $ target=values type=value:&T.Box type=value:&T.T.&T.str
|
||||
for (key, value) in map1.iter() {} // $ target=iter type=key:&T.i32 type=value:&T.Box type=value:&T.T.&T.str
|
||||
for (key, value) in &map1 {} // $ type=key:&T.i32 type=value:&T.Box type=value:&T.T.&T.str
|
||||
for key in map1.keys() {} // $ target=keys type=key:TRef.i32
|
||||
for value in map1.values() {} // $ target=values type=value:TRef.Box type=value:TRef.T.TRef.str
|
||||
for (key, value) in map1.iter() {} // $ target=iter type=key:TRef.i32 type=value:TRef.Box type=value:TRef.T.TRef.str
|
||||
for (key, value) in &map1 {} // $ type=key:TRef.i32 type=value:TRef.Box type=value:TRef.T.TRef.str
|
||||
|
||||
// while loops
|
||||
|
||||
@@ -2680,8 +2790,8 @@ mod tuples {
|
||||
let (mut e, f) = S1::get_pair(); // $ target=get_pair type=e:S1 type=f:S1
|
||||
let (mut g, mut h) = S1::get_pair(); // $ target=get_pair type=g:S1 type=h:S1
|
||||
|
||||
a.0.foo(); // $ target=foo
|
||||
b.1.foo(); // $ target=foo
|
||||
a.0.foo(); // $ target=foo fieldof=Tuple2
|
||||
b.1.foo(); // $ target=foo fieldof=Tuple2
|
||||
c.foo(); // $ target=foo
|
||||
d.foo(); // $ target=foo
|
||||
e.foo(); // $ target=foo
|
||||
@@ -2694,19 +2804,19 @@ mod tuples {
|
||||
// `a` and `b` to be inferred.
|
||||
let a = Default::default(); // $ target=default type=a:i64
|
||||
let b = Default::default(); // $ target=default type=b:bool
|
||||
let pair = (a, b); // $ type=pair:0(2).i64 type=pair:1(2).bool
|
||||
let i: i64 = pair.0;
|
||||
let j: bool = pair.1;
|
||||
let pair = (a, b); // $ type=pair:T0.i64 type=pair:T1.bool
|
||||
let i: i64 = pair.0; // $ fieldof=Tuple2
|
||||
let j: bool = pair.1; // $ fieldof=Tuple2
|
||||
|
||||
let pair = [1, 1].into(); // $ type=pair:(T_2) type=pair:0(2).i32 type=pair:1(2).i32 MISSING: target=into
|
||||
let pair = [1, 1].into(); // $ type=pair:(T_2) type=pair:T0.i32 type=pair:T1.i32 MISSING: target=into
|
||||
match pair {
|
||||
(0, 0) => print!("unexpected"),
|
||||
_ => print!("expected"),
|
||||
}
|
||||
let x = pair.0; // $ type=x:i32
|
||||
let x = pair.0; // $ type=x:i32 fieldof=Tuple2
|
||||
|
||||
let y = &S1::get_pair(); // $ target=get_pair
|
||||
y.0.foo(); // $ target=foo
|
||||
y.0.foo(); // $ target=foo fieldof=Tuple2
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2875,6 +2985,57 @@ mod block_types {
|
||||
}
|
||||
}
|
||||
|
||||
mod context_typed {
|
||||
pub fn f() {
|
||||
let x = None; // $ type=x:T.i32
|
||||
let x: Option<i32> = x;
|
||||
let x = Option::<i32>::None; // $ type=x:T.i32
|
||||
let x = Option::None::<i32>; // $ type=x:T.i32
|
||||
|
||||
fn pin_option<T>(opt: Option<T>, x: T) {}
|
||||
|
||||
let x = None; // $ type=x:T.i32
|
||||
pin_option(x, 0); // $ target=pin_option
|
||||
|
||||
enum MyEither<T1, T2> {
|
||||
A { left: T1 },
|
||||
B { right: T2 },
|
||||
}
|
||||
|
||||
let x = MyEither::A { left: 0 }; // $ type=x:T1.i32 type=x:T2.String
|
||||
let x: MyEither<i32, String> = x;
|
||||
let x = MyEither::<_, String>::A { left: 0 }; // $ type=x:T1.i32 certainType=x:T2.String
|
||||
#[rustfmt::skip]
|
||||
let x = MyEither::B::<i32, _> { // $ certainType=x:T1.i32 type=x:T2.String
|
||||
right: String::new(), // $ target=new
|
||||
};
|
||||
|
||||
fn pin_my_either<T>(e: MyEither<T, String>, x: T) {}
|
||||
|
||||
#[rustfmt::skip]
|
||||
let x = MyEither::B { // $ type=x:T1.i32 type=x:T2.String
|
||||
right: String::new(), // $ target=new
|
||||
};
|
||||
pin_my_either(x, 0); // $ target=pin_my_either
|
||||
|
||||
let x = Result::Ok(0); // $ type=x:E.String
|
||||
let x: Result<i32, String> = x;
|
||||
let x = Result::<i32, String>::Ok(0); // $ type=x:E.String
|
||||
let x = Result::Ok::<i32, String>(0); // $ type=x:E.String
|
||||
|
||||
fn pin_result<T, E>(res: Result<T, E>, x: E) {}
|
||||
|
||||
let x = Result::Ok(0); // $ type=x:T.i32 type=x:E.bool
|
||||
pin_result(x, false); // $ target=pin_result
|
||||
|
||||
let mut x = Vec::new(); // $ type=x:T.i32 target=new
|
||||
x.push(0); // $ target=push
|
||||
|
||||
let y = Default::default(); // $ type=y:i32 target=default
|
||||
x.push(y); // $ target=push
|
||||
}
|
||||
}
|
||||
|
||||
mod blanket_impl;
|
||||
mod closure;
|
||||
mod dereference;
|
||||
|
||||
@@ -37,18 +37,18 @@ pub fn f() -> Option<()> {
|
||||
|
||||
let value3 = 42;
|
||||
if let ref mesg = value3 {
|
||||
let mesg = mesg; // $ type=mesg:&T.i32
|
||||
let mesg = mesg; // $ type=mesg:TRef.i32
|
||||
println!("{mesg}");
|
||||
}
|
||||
|
||||
let value4 = Some(42);
|
||||
if let Some(ref mesg) = value4 {
|
||||
let mesg = mesg; // $ type=mesg:&T.i32
|
||||
let mesg = mesg; // $ type=mesg:TRef.i32
|
||||
println!("{mesg}");
|
||||
}
|
||||
|
||||
let ref value5 = 42;
|
||||
let x = value5; // $ type=x:&T.i32
|
||||
let x = value5; // $ type=x:TRef.i32
|
||||
|
||||
let my_record_struct = MyRecordStruct {
|
||||
value1: 42,
|
||||
@@ -102,7 +102,7 @@ pub fn f() -> Option<()> {
|
||||
) => {
|
||||
let a = value1; // $ type=a:bool
|
||||
let b = x; // $ type=b:i32
|
||||
let c = y; // $ type=c:&T.str
|
||||
let c = y; // $ type=c:TRef.str
|
||||
();
|
||||
}
|
||||
_ => (),
|
||||
@@ -197,7 +197,7 @@ pub fn literal_patterns() {
|
||||
let string_val = "hello";
|
||||
match string_val {
|
||||
"hello" => {
|
||||
let hello_match = string_val; // $ certainType=hello_match:&T.str
|
||||
let hello_match = string_val; // $ certainType=hello_match:TRef.str
|
||||
println!("String literal: {}", hello_match);
|
||||
}
|
||||
_ => {}
|
||||
@@ -230,7 +230,7 @@ pub fn identifier_patterns() {
|
||||
// IdentPat with ref
|
||||
match &value {
|
||||
ref x => {
|
||||
let ref_bound = x; // $ type=ref_bound:&T.&T.i32
|
||||
let ref_bound = x; // $ type=ref_bound:TRef.TRef.i32
|
||||
println!("Reference identifier: {:?}", ref_bound);
|
||||
}
|
||||
}
|
||||
@@ -269,7 +269,7 @@ pub fn identifier_patterns() {
|
||||
let mut ref_mut_val = 5i32;
|
||||
match &mut ref_mut_val {
|
||||
ref mut x => {
|
||||
let ref_mut_bound = x; // $ type=ref_mut_bound:&T.&T.i32
|
||||
let ref_mut_bound = x; // $ type=ref_mut_bound:TRef.TRef.i32
|
||||
**ref_mut_bound += 1; // $ target=deref target=add_assign
|
||||
println!("Ref mut pattern");
|
||||
}
|
||||
@@ -341,14 +341,14 @@ pub fn reference_patterns() {
|
||||
|
||||
match &mut mutable_value {
|
||||
&mut ref x => {
|
||||
let mut_ref_bound = x; // $ type=mut_ref_bound:&T.i32
|
||||
let mut_ref_bound = x; // $ type=mut_ref_bound:TRef.i32
|
||||
println!("Mutable ref pattern: {}", mut_ref_bound);
|
||||
}
|
||||
}
|
||||
|
||||
match &value {
|
||||
ref x => {
|
||||
let ref_pattern = x; // $ type=ref_pattern:&T.&T.i32
|
||||
let ref_pattern = x; // $ type=ref_pattern:TRef.TRef.i32
|
||||
println!("Reference pattern: {}", ref_pattern);
|
||||
}
|
||||
}
|
||||
@@ -525,7 +525,7 @@ pub fn slice_patterns() {
|
||||
// SlicePat - Slice patterns
|
||||
match slice {
|
||||
[] => {
|
||||
let empty_slice = slice; // $ certainType=empty_slice:&T.[T].i32
|
||||
let empty_slice = slice; // $ certainType=empty_slice:TRef.TSlice.i32
|
||||
println!("Empty slice: {:?}", empty_slice);
|
||||
}
|
||||
[x] => {
|
||||
@@ -540,7 +540,7 @@ pub fn slice_patterns() {
|
||||
[first, middle @ .., last] => {
|
||||
let slice_start = *first; // $ MISSING: type=slice_start:i32
|
||||
let slice_end = *last; // $ MISSING: type=slice_end:i32
|
||||
let slice_middle = middle; // $ MISSING: type=slice_middle:&T.[T].i32
|
||||
let slice_middle = middle; // $ MISSING: type=slice_middle:TRef.TSlice.i32
|
||||
println!(
|
||||
"First: {}, last: {}, middle len: {}",
|
||||
slice_start,
|
||||
@@ -717,7 +717,7 @@ pub fn complex_nested_patterns() {
|
||||
}
|
||||
// Catch-all with identifier pattern
|
||||
other => {
|
||||
let other_complex = other; // $ type=other_complex:0(2).Point type=other_complex:1(2).MyOption
|
||||
let other_complex = other; // $ type=other_complex:T0.Point type=other_complex:T1.MyOption
|
||||
println!("Other complex data: {:?}", other_complex);
|
||||
}
|
||||
}
|
||||
@@ -750,7 +750,7 @@ pub fn patterns_in_let_statements() {
|
||||
// Let with reference pattern
|
||||
let value = 42i32;
|
||||
let ref ref_val = value;
|
||||
let let_ref = ref_val; // $ certainType=let_ref:&T.i32
|
||||
let let_ref = ref_val; // $ certainType=let_ref:TRef.i32
|
||||
|
||||
// Let with mutable pattern
|
||||
let mut mut_val = 10i32;
|
||||
@@ -779,13 +779,13 @@ pub fn patterns_in_function_parameters() {
|
||||
|
||||
// Call the functions to use them
|
||||
let point = Point { x: 5, y: 10 };
|
||||
let extracted = extract_point(point); // $ target=extract_point certainType=extracted:0(2).i32 certainType=extracted:1(2).i32
|
||||
let extracted = extract_point(point); // $ target=extract_point certainType=extracted:T0.i32 certainType=extracted:T1.i32
|
||||
|
||||
let color = Color(200, 100, 50);
|
||||
let red = extract_color(color); // $ target=extract_color certainType=red:u8
|
||||
|
||||
let tuple = (42i32, 3.14f64, true);
|
||||
let tuple_extracted = extract_tuple(tuple); // $ target=extract_tuple certainType=tuple_extracted:0(2).i32 certainType=tuple_extracted:1(2).bool
|
||||
let tuple_extracted = extract_tuple(tuple); // $ target=extract_tuple certainType=tuple_extracted:T0.i32 certainType=tuple_extracted:T1.bool
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,10 +1,12 @@
|
||||
import rust
|
||||
import utils.test.InlineExpectationsTest
|
||||
import codeql.rust.internal.Type
|
||||
import codeql.rust.internal.TypeInference as TypeInference
|
||||
import TypeInference
|
||||
|
||||
query predicate inferType(AstNode n, TypePath path, Type t) {
|
||||
t = TypeInference::inferType(n, path) and
|
||||
t != TUnknownType() and
|
||||
n.fromSource() and
|
||||
not n.isFromMacroExpansion() and
|
||||
not n instanceof IdentPat and // avoid overlap in the output with the underlying `Name` node
|
||||
@@ -58,9 +60,10 @@ module TypeTest implements TestSig {
|
||||
exists(AstNode n, TypePath path, Type t |
|
||||
t = TypeInference::inferType(n, path) and
|
||||
(
|
||||
if t = TypeInference::CertainTypeInference::inferCertainType(n, path)
|
||||
then tag = "certainType"
|
||||
else tag = "type"
|
||||
tag = "type"
|
||||
or
|
||||
t = TypeInference::CertainTypeInference::inferCertainType(n, path) and
|
||||
tag = "certainType"
|
||||
) and
|
||||
location = n.getLocation() and
|
||||
(
|
||||
|
||||
@@ -10,17 +10,17 @@ query predicate definition(Ssa::Definition def, Variable v) {
|
||||
toBeTested(v.getEnclosingCfgScope()) and def.getSourceVariable() = v
|
||||
}
|
||||
|
||||
query predicate read(Ssa::Definition def, Variable v, CfgNode read) {
|
||||
query predicate read(Ssa::Definition def, Variable v, Expr read) {
|
||||
toBeTested(v.getEnclosingCfgScope()) and def.getSourceVariable() = v and read = def.getARead()
|
||||
}
|
||||
|
||||
query predicate firstRead(Ssa::Definition def, Variable v, CfgNode read) {
|
||||
query predicate firstRead(Ssa::Definition def, Variable v, Expr read) {
|
||||
toBeTested(v.getEnclosingCfgScope()) and
|
||||
def.getSourceVariable() = v and
|
||||
read = def.getAFirstRead()
|
||||
}
|
||||
|
||||
query predicate adjacentReads(Ssa::Definition def, Variable v, CfgNode read1, CfgNode read2) {
|
||||
query predicate adjacentReads(Ssa::Definition def, Variable v, Expr read1, Expr read2) {
|
||||
toBeTested(v.getEnclosingCfgScope()) and
|
||||
def.getSourceVariable() = v and
|
||||
def.hasAdjacentReads(read1, read2)
|
||||
@@ -54,4 +54,4 @@ query predicate ultimateDef(Ssa::Definition def, Definition ult) {
|
||||
ult != def
|
||||
}
|
||||
|
||||
query predicate assigns(Ssa::WriteDefinition def, CfgNode value) { def.assigns(value) }
|
||||
query predicate assigns(Ssa::WriteDefinition def, Expr value) { def.assigns(value) }
|
||||
|
||||
@@ -3,29 +3,10 @@ multipleCallTargets
|
||||
| mysql.rs:16:26:16:85 | ...::from(...) |
|
||||
| mysql.rs:18:13:18:66 | ...::from(...) |
|
||||
| mysql.rs:19:30:19:83 | ...::from(...) |
|
||||
| mysql.rs:46:45:46:66 | remote_string.as_str() |
|
||||
| mysql.rs:47:71:47:92 | remote_string.as_str() |
|
||||
| mysql.rs:48:46:48:67 | remote_string.as_str() |
|
||||
| mysql.rs:49:33:49:54 | remote_string.as_str() |
|
||||
| mysql.rs:50:46:50:67 | remote_string.as_str() |
|
||||
| mysql.rs:52:37:52:58 | remote_string.as_str() |
|
||||
| mysql.rs:56:14:56:35 | remote_string.as_str() |
|
||||
| mysql.rs:62:14:62:35 | remote_string.as_str() |
|
||||
| mysql.rs:66:40:66:61 | remote_string.as_str() |
|
||||
| mysql.rs:67:39:67:60 | remote_string.as_str() |
|
||||
| mysql.rs:70:14:70:35 | remote_string.as_str() |
|
||||
| mysql.rs:100:24:100:39 | ...::from(...) |
|
||||
| mysql.rs:101:26:101:85 | ...::from(...) |
|
||||
| mysql.rs:103:13:103:66 | ...::from(...) |
|
||||
| mysql.rs:104:30:104:83 | ...::from(...) |
|
||||
| mysql.rs:126:45:126:66 | remote_string.as_str() |
|
||||
| mysql.rs:128:38:128:59 | remote_string.as_str() |
|
||||
| mysql.rs:130:33:130:54 | remote_string.as_str() |
|
||||
| mysql.rs:131:54:131:75 | remote_string.as_str() |
|
||||
| mysql.rs:135:18:135:39 | remote_string.as_str() |
|
||||
| mysql.rs:140:40:140:61 | remote_string.as_str() |
|
||||
| mysql.rs:142:62:142:83 | remote_string.as_str() |
|
||||
| mysql.rs:145:31:145:52 | remote_string.as_str() |
|
||||
| sqlx.rs:46:24:46:44 | ...::from(...) |
|
||||
| sqlx.rs:47:56:47:76 | ...::from(...) |
|
||||
| sqlx.rs:48:97:48:117 | ...::from(...) |
|
||||
@@ -33,8 +14,6 @@ multipleCallTargets
|
||||
| sqlx.rs:51:24:51:77 | ...::from(...) |
|
||||
| sqlx.rs:55:26:55:79 | ...::from(...) |
|
||||
| sqlx.rs:61:28:61:81 | ...::from(...) |
|
||||
| sqlx.rs:69:30:69:52 | unsafe_query_2.as_str() |
|
||||
| sqlx.rs:80:29:80:51 | unsafe_query_2.as_str() |
|
||||
| sqlx.rs:99:24:99:44 | ...::from(...) |
|
||||
| sqlx.rs:100:97:100:117 | ...::from(...) |
|
||||
| sqlx.rs:101:24:101:77 | ...::from(...) |
|
||||
|
||||
@@ -36,7 +36,7 @@ edges
|
||||
| mysql.rs:12:13:12:29 | mut remote_string | mysql.rs:18:71:18:83 | remote_string | provenance | |
|
||||
| mysql.rs:12:33:12:54 | ...::get | mysql.rs:12:33:12:77 | ...::get(...) [Ok] | provenance | Src:MaD:23 |
|
||||
| mysql.rs:12:33:12:77 | ...::get(...) [Ok] | mysql.rs:12:33:13:21 | ... .unwrap() | provenance | MaD:31 |
|
||||
| mysql.rs:12:33:13:21 | ... .unwrap() | mysql.rs:12:33:14:19 | ... .text() [Ok] | provenance | MaD:34 |
|
||||
| mysql.rs:12:33:13:21 | ... .unwrap() | mysql.rs:12:33:14:19 | ... .text() [Ok] | provenance | MaD:33 |
|
||||
| mysql.rs:12:33:14:19 | ... .text() [Ok] | mysql.rs:12:33:15:40 | ... .unwrap_or(...) | provenance | MaD:32 |
|
||||
| mysql.rs:12:33:15:40 | ... .unwrap_or(...) | mysql.rs:12:13:12:29 | mut remote_string | provenance | |
|
||||
| mysql.rs:17:13:17:24 | unsafe_query | mysql.rs:25:38:25:49 | unsafe_query | provenance | |
|
||||
@@ -113,7 +113,7 @@ edges
|
||||
| mysql.rs:97:13:97:29 | mut remote_string | mysql.rs:103:71:103:83 | remote_string | provenance | |
|
||||
| mysql.rs:97:33:97:54 | ...::get | mysql.rs:97:33:97:77 | ...::get(...) [Ok] | provenance | Src:MaD:23 |
|
||||
| mysql.rs:97:33:97:77 | ...::get(...) [Ok] | mysql.rs:97:33:98:21 | ... .unwrap() | provenance | MaD:31 |
|
||||
| mysql.rs:97:33:98:21 | ... .unwrap() | mysql.rs:97:33:99:19 | ... .text() [Ok] | provenance | MaD:34 |
|
||||
| mysql.rs:97:33:98:21 | ... .unwrap() | mysql.rs:97:33:99:19 | ... .text() [Ok] | provenance | MaD:33 |
|
||||
| mysql.rs:97:33:99:19 | ... .text() [Ok] | mysql.rs:97:33:100:40 | ... .unwrap_or(...) | provenance | MaD:32 |
|
||||
| mysql.rs:97:33:100:40 | ... .unwrap_or(...) | mysql.rs:97:13:97:29 | mut remote_string | provenance | |
|
||||
| mysql.rs:102:13:102:24 | unsafe_query | mysql.rs:110:38:110:49 | unsafe_query | provenance | |
|
||||
@@ -177,15 +177,13 @@ edges
|
||||
| sqlx.rs:48:9:48:21 | remote_string | sqlx.rs:59:17:59:72 | MacroExpr | provenance | |
|
||||
| sqlx.rs:48:25:48:46 | ...::get | sqlx.rs:48:25:48:69 | ...::get(...) [Ok] | provenance | Src:MaD:23 |
|
||||
| sqlx.rs:48:25:48:69 | ...::get(...) [Ok] | sqlx.rs:48:25:48:78 | ... .unwrap() | provenance | MaD:31 |
|
||||
| sqlx.rs:48:25:48:78 | ... .unwrap() | sqlx.rs:48:25:48:85 | ... .text() [Ok] | provenance | MaD:34 |
|
||||
| sqlx.rs:48:25:48:78 | ... .unwrap() | sqlx.rs:48:25:48:85 | ... .text() [Ok] | provenance | MaD:33 |
|
||||
| sqlx.rs:48:25:48:85 | ... .text() [Ok] | sqlx.rs:48:25:48:118 | ... .unwrap_or(...) | provenance | MaD:32 |
|
||||
| sqlx.rs:48:25:48:118 | ... .unwrap_or(...) | sqlx.rs:48:9:48:21 | remote_string | provenance | |
|
||||
| sqlx.rs:53:9:53:22 | unsafe_query_1 [&ref] | sqlx.rs:78:25:78:47 | unsafe_query_1.as_str() [&ref] | provenance | MaD:29 |
|
||||
| sqlx.rs:53:26:53:36 | &arg_string [&ref] | sqlx.rs:53:9:53:22 | unsafe_query_1 [&ref] | provenance | |
|
||||
| sqlx.rs:53:27:53:36 | arg_string | sqlx.rs:53:26:53:36 | &arg_string [&ref] | provenance | |
|
||||
| sqlx.rs:54:9:54:22 | unsafe_query_2 [&ref] | sqlx.rs:80:29:80:51 | unsafe_query_2.as_str() [&ref] | provenance | MaD:29 |
|
||||
| sqlx.rs:54:9:54:22 | unsafe_query_2 [&ref] | sqlx.rs:80:29:80:51 | unsafe_query_2.as_str() [&ref] | provenance | MaD:33 |
|
||||
| sqlx.rs:54:9:54:22 | unsafe_query_2 [&ref] | sqlx.rs:80:29:80:51 | unsafe_query_2.as_str() [&ref] | provenance | MaD:33 |
|
||||
| sqlx.rs:54:26:54:39 | &remote_string [&ref] | sqlx.rs:54:9:54:22 | unsafe_query_2 [&ref] | provenance | |
|
||||
| sqlx.rs:54:27:54:39 | remote_string | sqlx.rs:54:26:54:39 | &remote_string [&ref] | provenance | |
|
||||
| sqlx.rs:55:9:55:22 | unsafe_query_3 | sqlx.rs:81:29:81:42 | unsafe_query_3 | provenance | |
|
||||
@@ -200,8 +198,8 @@ edges
|
||||
| sqlx.rs:56:9:56:22 | unsafe_query_4 | sqlx.rs:82:29:82:51 | unsafe_query_4.as_str() | provenance | MaD:29 |
|
||||
| sqlx.rs:59:17:59:72 | ...::format(...) | sqlx.rs:59:17:59:72 | { ... } | provenance | |
|
||||
| sqlx.rs:59:17:59:72 | ...::must_use(...) | sqlx.rs:56:9:56:22 | unsafe_query_4 | provenance | |
|
||||
| sqlx.rs:59:17:59:72 | MacroExpr | sqlx.rs:59:17:59:72 | ...::format(...) | provenance | MaD:35 |
|
||||
| sqlx.rs:59:17:59:72 | { ... } | sqlx.rs:59:17:59:72 | ...::must_use(...) | provenance | MaD:36 |
|
||||
| sqlx.rs:59:17:59:72 | MacroExpr | sqlx.rs:59:17:59:72 | ...::format(...) | provenance | MaD:34 |
|
||||
| sqlx.rs:59:17:59:72 | { ... } | sqlx.rs:59:17:59:72 | ...::must_use(...) | provenance | MaD:35 |
|
||||
| sqlx.rs:78:25:78:47 | unsafe_query_1.as_str() [&ref] | sqlx.rs:78:13:78:23 | ...::query | provenance | MaD:20 Sink:MaD:20 |
|
||||
| sqlx.rs:80:29:80:51 | unsafe_query_2.as_str() [&ref] | sqlx.rs:80:17:80:27 | ...::query | provenance | MaD:20 Sink:MaD:20 |
|
||||
| sqlx.rs:81:29:81:42 | unsafe_query_3 | sqlx.rs:81:29:81:51 | unsafe_query_3.as_str() [&ref] | provenance | MaD:29 |
|
||||
@@ -213,7 +211,7 @@ edges
|
||||
| sqlx.rs:100:9:100:21 | remote_string | sqlx.rs:102:84:102:96 | remote_string | provenance | |
|
||||
| sqlx.rs:100:25:100:46 | ...::get | sqlx.rs:100:25:100:69 | ...::get(...) [Ok] | provenance | Src:MaD:23 |
|
||||
| sqlx.rs:100:25:100:69 | ...::get(...) [Ok] | sqlx.rs:100:25:100:78 | ... .unwrap() | provenance | MaD:31 |
|
||||
| sqlx.rs:100:25:100:78 | ... .unwrap() | sqlx.rs:100:25:100:85 | ... .text() [Ok] | provenance | MaD:34 |
|
||||
| sqlx.rs:100:25:100:78 | ... .unwrap() | sqlx.rs:100:25:100:85 | ... .text() [Ok] | provenance | MaD:33 |
|
||||
| sqlx.rs:100:25:100:85 | ... .text() [Ok] | sqlx.rs:100:25:100:118 | ... .unwrap_or(...) | provenance | MaD:32 |
|
||||
| sqlx.rs:100:25:100:118 | ... .unwrap_or(...) | sqlx.rs:100:9:100:21 | remote_string | provenance | |
|
||||
| sqlx.rs:102:9:102:22 | unsafe_query_1 | sqlx.rs:113:31:113:44 | unsafe_query_1 | provenance | |
|
||||
@@ -255,7 +253,7 @@ edges
|
||||
| sqlx.rs:173:9:173:21 | remote_string | sqlx.rs:175:84:175:96 | remote_string | provenance | |
|
||||
| sqlx.rs:173:25:173:46 | ...::get | sqlx.rs:173:25:173:69 | ...::get(...) [Ok] | provenance | Src:MaD:23 |
|
||||
| sqlx.rs:173:25:173:69 | ...::get(...) [Ok] | sqlx.rs:173:25:173:78 | ... .unwrap() | provenance | MaD:31 |
|
||||
| sqlx.rs:173:25:173:78 | ... .unwrap() | sqlx.rs:173:25:173:85 | ... .text() [Ok] | provenance | MaD:34 |
|
||||
| sqlx.rs:173:25:173:78 | ... .unwrap() | sqlx.rs:173:25:173:85 | ... .text() [Ok] | provenance | MaD:33 |
|
||||
| sqlx.rs:173:25:173:85 | ... .text() [Ok] | sqlx.rs:173:25:173:118 | ... .unwrap_or(...) | provenance | MaD:32 |
|
||||
| sqlx.rs:173:25:173:118 | ... .unwrap_or(...) | sqlx.rs:173:9:173:21 | remote_string | provenance | |
|
||||
| sqlx.rs:175:9:175:22 | unsafe_query_1 | sqlx.rs:188:29:188:42 | unsafe_query_1 | provenance | |
|
||||
@@ -302,10 +300,9 @@ models
|
||||
| 30 | Summary: <core::option::Option>::unwrap_or; Argument[self].Field[core::option::Option::Some(0)]; ReturnValue; value |
|
||||
| 31 | Summary: <core::result::Result>::unwrap; Argument[self].Field[core::result::Result::Ok(0)]; ReturnValue; value |
|
||||
| 32 | Summary: <core::result::Result>::unwrap_or; Argument[self].Field[core::result::Result::Ok(0)]; ReturnValue; value |
|
||||
| 33 | Summary: <core::str>::as_str; Argument[self]; ReturnValue; value |
|
||||
| 34 | Summary: <reqwest::blocking::response::Response>::text; Argument[self]; ReturnValue.Field[core::result::Result::Ok(0)]; taint |
|
||||
| 35 | Summary: alloc::fmt::format; Argument[0]; ReturnValue; taint |
|
||||
| 36 | Summary: core::hint::must_use; Argument[0]; ReturnValue; value |
|
||||
| 33 | Summary: <reqwest::blocking::response::Response>::text; Argument[self]; ReturnValue.Field[core::result::Result::Ok(0)]; taint |
|
||||
| 34 | Summary: alloc::fmt::format; Argument[0]; ReturnValue; taint |
|
||||
| 35 | Summary: core::hint::must_use; Argument[0]; ReturnValue; value |
|
||||
nodes
|
||||
| mysql.rs:12:13:12:29 | mut remote_string | semmle.label | mut remote_string |
|
||||
| mysql.rs:12:33:12:54 | ...::get | semmle.label | ...::get |
|
||||
|
||||
@@ -13,7 +13,6 @@ multipleCallTargets
|
||||
| test_storage.rs:73:25:73:67 | ...::from(...) |
|
||||
| test_storage.rs:75:25:75:65 | ...::from(...) |
|
||||
| test_storage.rs:76:25:76:65 | ...::from(...) |
|
||||
| test_storage.rs:77:14:77:24 | s1.as_str() |
|
||||
| test_storage.rs:78:25:78:65 | ...::from(...) |
|
||||
| test_storage.rs:79:25:79:65 | ...::from(...) |
|
||||
| test_storage.rs:80:25:80:70 | ...::from(...) |
|
||||
|
||||
@@ -186,28 +186,6 @@ edges
|
||||
| lifetime.rs:655:11:655:25 | &raw const str2 | lifetime.rs:655:4:655:7 | ref1 | provenance | |
|
||||
| lifetime.rs:655:11:655:25 | &raw const str2 [&ref] | lifetime.rs:655:4:655:7 | ref1 [&ref] | provenance | |
|
||||
| lifetime.rs:655:22:655:25 | str2 | lifetime.rs:655:11:655:25 | &raw const str2 [&ref] | provenance | |
|
||||
| lifetime.rs:680:7:680:8 | r1 | lifetime.rs:692:13:692:14 | r1 | provenance | |
|
||||
| lifetime.rs:682:4:682:12 | &... | lifetime.rs:680:7:680:8 | r1 | provenance | |
|
||||
| lifetime.rs:684:7:684:14 | TuplePat [tuple.0] | lifetime.rs:684:8:684:9 | r2 | provenance | |
|
||||
| lifetime.rs:684:7:684:14 | TuplePat [tuple.1] | lifetime.rs:684:12:684:13 | r3 | provenance | |
|
||||
| lifetime.rs:684:8:684:9 | r2 | lifetime.rs:693:13:693:14 | r2 | provenance | |
|
||||
| lifetime.rs:684:12:684:13 | r3 | lifetime.rs:694:13:694:14 | r3 | provenance | |
|
||||
| lifetime.rs:686:4:687:16 | TupleExpr [tuple.0] | lifetime.rs:684:7:684:14 | TuplePat [tuple.0] | provenance | |
|
||||
| lifetime.rs:686:4:687:16 | TupleExpr [tuple.1] | lifetime.rs:684:7:684:14 | TuplePat [tuple.1] | provenance | |
|
||||
| lifetime.rs:686:5:686:13 | &... | lifetime.rs:686:4:687:16 | TupleExpr [tuple.0] | provenance | |
|
||||
| lifetime.rs:687:5:687:15 | &... | lifetime.rs:686:4:687:16 | TupleExpr [tuple.1] | provenance | |
|
||||
| lifetime.rs:717:35:723:2 | { ... } | lifetime.rs:730:11:730:25 | e1.test_match() | provenance | |
|
||||
| lifetime.rs:718:7:718:8 | r1 | lifetime.rs:717:35:723:2 | { ... } | provenance | |
|
||||
| lifetime.rs:719:26:719:34 | &... | lifetime.rs:718:7:718:8 | r1 | provenance | |
|
||||
| lifetime.rs:730:6:730:7 | r1 | lifetime.rs:734:12:734:13 | r1 | provenance | |
|
||||
| lifetime.rs:730:11:730:25 | e1.test_match() | lifetime.rs:730:6:730:7 | r1 | provenance | |
|
||||
| lifetime.rs:766:2:766:11 | &val | lifetime.rs:766:2:766:11 | ptr | provenance | |
|
||||
| lifetime.rs:766:2:766:11 | ptr | lifetime.rs:766:2:766:11 | ptr | provenance | |
|
||||
| lifetime.rs:767:2:767:11 | &val | lifetime.rs:767:2:767:11 | ptr | provenance | |
|
||||
| lifetime.rs:767:2:767:11 | ptr | lifetime.rs:767:2:767:11 | ptr | provenance | |
|
||||
| lifetime.rs:769:6:769:8 | ptr | lifetime.rs:771:12:771:14 | ptr | provenance | |
|
||||
| lifetime.rs:769:12:769:21 | &val | lifetime.rs:769:12:769:21 | ptr | provenance | |
|
||||
| lifetime.rs:769:12:769:21 | ptr | lifetime.rs:769:6:769:8 | ptr | provenance | |
|
||||
| lifetime.rs:781:2:781:19 | return ... | lifetime.rs:785:11:785:41 | get_local_for_unsafe_function(...) | provenance | |
|
||||
| lifetime.rs:781:9:781:19 | &my_local10 | lifetime.rs:781:2:781:19 | return ... | provenance | |
|
||||
| lifetime.rs:785:6:785:7 | p1 | lifetime.rs:789:12:789:13 | p1 | provenance | |
|
||||
@@ -421,35 +399,6 @@ nodes
|
||||
| lifetime.rs:655:22:655:25 | str2 | semmle.label | str2 |
|
||||
| lifetime.rs:659:15:659:18 | ref1 | semmle.label | ref1 |
|
||||
| lifetime.rs:667:14:667:17 | ref1 | semmle.label | ref1 |
|
||||
| lifetime.rs:680:7:680:8 | r1 | semmle.label | r1 |
|
||||
| lifetime.rs:682:4:682:12 | &... | semmle.label | &... |
|
||||
| lifetime.rs:684:7:684:14 | TuplePat [tuple.0] | semmle.label | TuplePat [tuple.0] |
|
||||
| lifetime.rs:684:7:684:14 | TuplePat [tuple.1] | semmle.label | TuplePat [tuple.1] |
|
||||
| lifetime.rs:684:8:684:9 | r2 | semmle.label | r2 |
|
||||
| lifetime.rs:684:12:684:13 | r3 | semmle.label | r3 |
|
||||
| lifetime.rs:686:4:687:16 | TupleExpr [tuple.0] | semmle.label | TupleExpr [tuple.0] |
|
||||
| lifetime.rs:686:4:687:16 | TupleExpr [tuple.1] | semmle.label | TupleExpr [tuple.1] |
|
||||
| lifetime.rs:686:5:686:13 | &... | semmle.label | &... |
|
||||
| lifetime.rs:687:5:687:15 | &... | semmle.label | &... |
|
||||
| lifetime.rs:692:13:692:14 | r1 | semmle.label | r1 |
|
||||
| lifetime.rs:693:13:693:14 | r2 | semmle.label | r2 |
|
||||
| lifetime.rs:694:13:694:14 | r3 | semmle.label | r3 |
|
||||
| lifetime.rs:717:35:723:2 | { ... } | semmle.label | { ... } |
|
||||
| lifetime.rs:718:7:718:8 | r1 | semmle.label | r1 |
|
||||
| lifetime.rs:719:26:719:34 | &... | semmle.label | &... |
|
||||
| lifetime.rs:730:6:730:7 | r1 | semmle.label | r1 |
|
||||
| lifetime.rs:730:11:730:25 | e1.test_match() | semmle.label | e1.test_match() |
|
||||
| lifetime.rs:734:12:734:13 | r1 | semmle.label | r1 |
|
||||
| lifetime.rs:766:2:766:11 | &val | semmle.label | &val |
|
||||
| lifetime.rs:766:2:766:11 | ptr | semmle.label | ptr |
|
||||
| lifetime.rs:766:2:766:11 | ptr | semmle.label | ptr |
|
||||
| lifetime.rs:767:2:767:11 | &val | semmle.label | &val |
|
||||
| lifetime.rs:767:2:767:11 | ptr | semmle.label | ptr |
|
||||
| lifetime.rs:767:2:767:11 | ptr | semmle.label | ptr |
|
||||
| lifetime.rs:769:6:769:8 | ptr | semmle.label | ptr |
|
||||
| lifetime.rs:769:12:769:21 | &val | semmle.label | &val |
|
||||
| lifetime.rs:769:12:769:21 | ptr | semmle.label | ptr |
|
||||
| lifetime.rs:771:12:771:14 | ptr | semmle.label | ptr |
|
||||
| lifetime.rs:781:2:781:19 | return ... | semmle.label | return ... |
|
||||
| lifetime.rs:781:9:781:19 | &my_local10 | semmle.label | &my_local10 |
|
||||
| lifetime.rs:785:6:785:7 | p1 | semmle.label | p1 |
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
multipleCallTargets
|
||||
| request_forgery_tests.rs:30:36:30:52 | user_url.as_str() |
|
||||
@@ -23,3 +23,48 @@ pub struct isize;
|
||||
// floating-point types
|
||||
pub struct f32;
|
||||
pub struct f64;
|
||||
|
||||
struct Slice<TSlice>;
|
||||
struct Array<TArray, const N: usize>;
|
||||
struct Ref<TRef>; // todo: add mut variant
|
||||
struct Ptr<TPtr>; // todo: add mut variant
|
||||
|
||||
// tuples
|
||||
struct Tuple0;
|
||||
struct Tuple1<T0>(T0);
|
||||
struct Tuple2<T0, T1>(T0, T1);
|
||||
struct Tuple3<T0, T1, T2>(T0, T1, T2);
|
||||
struct Tuple4<T0, T1, T2, T3>(T0, T1, T2, T3);
|
||||
struct Tuple5<T0, T1, T2, T3, T4>(T0, T1, T2, T3, T4);
|
||||
struct Tuple6<T0, T1, T2, T3, T4, T5>(T0, T1, T2, T3, T4, T5);
|
||||
struct Tuple7<T0, T1, T2, T3, T4, T5, T6>(T0, T1, T2, T3, T4, T5, T6);
|
||||
struct Tuple8<T0, T1, T2, T3, T4, T5, T6, T7>(T0, T1, T2, T3, T4, T5, T6, T7);
|
||||
struct Tuple9<T0, T1, T2, T3, T4, T5, T6, T7, T8>(T0, T1, T2, T3, T4, T5, T6, T7, T8);
|
||||
struct Tuple10<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9>(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9);
|
||||
struct Tuple11<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(
|
||||
T0,
|
||||
T1,
|
||||
T2,
|
||||
T3,
|
||||
T4,
|
||||
T5,
|
||||
T6,
|
||||
T7,
|
||||
T8,
|
||||
T9,
|
||||
T10,
|
||||
);
|
||||
struct Tuple12<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>(
|
||||
T0,
|
||||
T1,
|
||||
T2,
|
||||
T3,
|
||||
T4,
|
||||
T5,
|
||||
T6,
|
||||
T7,
|
||||
T8,
|
||||
T9,
|
||||
T10,
|
||||
T11,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user