Merge remote-tracking branch 'upstream/main' into access-invalid-pointer-fp

This commit is contained in:
Geoffrey White
2025-11-21 17:39:56 +00:00
583 changed files with 32416 additions and 12567 deletions

View File

@@ -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
View File

@@ -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

View File

@@ -0,0 +1,4 @@
---
dependencies: {}
compiled: false
lockVersion: 1.0.0

View File

@@ -0,0 +1,7 @@
name: codeql/rust-examples
groups:
- rust
- examples
dependencies:
codeql/rust-all: ${workspace}
warnOnImplicitThis: true

View 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."

View 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()

View 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"

View File

@@ -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
}

View File

@@ -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 |

View File

@@ -15,3 +15,5 @@ mod macros {
}
pub mod a_module;
pub extern crate self as extern_crate_alias;

View File

@@ -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 |

View File

@@ -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 |

View File

@@ -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

View 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.

View File

@@ -1,4 +1,5 @@
---
category: minorAnalysis
---
## 0.1.20
### Minor Analysis Improvements
* Added models for cookie methods in the `poem` crate.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.1.19
lastReleaseVersion: 0.1.20

View File

@@ -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);
}
}
}

View File

@@ -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.

View File

@@ -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
)

View File

@@ -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
}

View File

@@ -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>

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)
)

View File

@@ -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() =

View File

@@ -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" }
}

View File

@@ -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() {

View File

@@ -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() }
}
}

View File

@@ -105,7 +105,7 @@ module Impl {
not isAttributeMacroExpansionSourceLocation(macro, n.getLocation())
)
or
isFromMacroExpansion(getImmediatelyEnclosingMacroInvocation(n))
isFromMacroExpansion(pragma[only_bind_into](getImmediatelyEnclosingMacroInvocation(n)))
}
cached

View File

@@ -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" }
}

View File

@@ -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 = "(...)"
}
}
}

View File

@@ -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 }

View File

@@ -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"]
)
}

View File

@@ -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.

View File

@@ -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()
}
}

View File

@@ -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
}

View File

@@ -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()
)
}

View File

@@ -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

View File

@@ -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)
)
}
}

View File

@@ -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() + "]" }

View File

@@ -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()
}
}

View File

@@ -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 }
}
}

View File

@@ -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() }
}
/**

View File

@@ -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
)
}

View File

@@ -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)
)
}
}

View File

@@ -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

View File

@@ -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)
)
}

View File

@@ -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
}

View File

@@ -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
)
}
}

View File

@@ -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 }
}
/**

View File

@@ -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()
)
}

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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

View 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.

View File

@@ -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.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.1.19
lastReleaseVersion: 0.1.20

View File

@@ -1,5 +1,5 @@
name: codeql/rust-queries
version: 0.1.20-dev
version: 0.1.21-dev
groups:
- rust
- queries

View File

@@ -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) {

View File

@@ -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() }

View File

@@ -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()

View File

@@ -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()

View File

@@ -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() }

View File

@@ -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 |

View File

@@ -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() {

View File

@@ -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(...) |

View File

@@ -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 |

View File

@@ -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] |

View File

@@ -1,2 +0,0 @@
multipleCallTargets
| main.rs:389:14:389:30 | ... .lt(...) |

View File

@@ -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) {

View File

@@ -1,2 +0,0 @@
multipleCallTargets
| test.rs:31:22:31:72 | ... .read_to_string(...) |

View File

@@ -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 |

View File

@@ -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"

View File

@@ -0,0 +1,2 @@
multipleCallTargets
| test.rs:288:7:288:36 | ... .as_str() |

View File

@@ -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()
}
}

View File

@@ -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(...) |

View File

@@ -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 {

View File

@@ -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;

View File

@@ -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]

View File

@@ -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
(

View File

@@ -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) }

View File

@@ -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(...) |

View File

@@ -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 |

View File

@@ -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(...) |

View File

@@ -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 |

View File

@@ -1,2 +0,0 @@
multipleCallTargets
| request_forgery_tests.rs:30:36:30:52 | user_url.as_str() |

View File

@@ -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,
);