Rust: Base DataFlow::Node on AST instead of CFG

This commit is contained in:
Tom Hvitved
2025-11-19 11:55:22 +01:00
parent 0c43f2c4f0
commit 489fff9572
42 changed files with 403 additions and 366 deletions

View File

@@ -25,7 +25,7 @@ import codeql.rust.dataflow.TaintTracking
module ConstantPasswordConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node node) {
// `node` is a string literal
node.asExpr().getExpr() instanceof StringLiteralExpr
node.asExpr() instanceof StringLiteralExpr
}
predicate isSink(DataFlow::Node node) {
@@ -34,7 +34,7 @@ module ConstantPasswordConfig implements DataFlow::ConfigSig {
call.getStaticTarget() = target and
v.getParameter() = target.getParam(argIndex) and
v.getText().matches("pass%") and
call.getArg(argIndex) = node.asExpr().getExpr()
call.getArg(argIndex) = node.asExpr()
)
}
}

View File

@@ -25,7 +25,7 @@ module SqlInjectionConfig implements DataFlow::ConfigSig {
// `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().getExpr()
call.getArg(0) = node.asExpr()
)
}
}

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,6 @@
*/
private import rust
private import codeql.rust.controlflow.CfgNodes
private import codeql.rust.frameworks.stdlib.Builtins
private import DataFlowImpl
@@ -22,7 +21,7 @@ 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. */
@@ -41,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 |
@@ -74,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 |
@@ -153,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. */
@@ -119,16 +119,16 @@ 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;
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`.
*
@@ -26,5 +25,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

@@ -21,6 +21,11 @@ module Impl {
* ```
*/
class ParenExpr extends Generated::ParenExpr {
override string toStringImpl() { result = "(" + this.getExpr().toAbbreviatedString() + ")" }
override string toStringImpl() {
result = "(" + this.getExpr().toAbbreviatedString() + ")"
or
not exists(this.getExpr().toAbbreviatedString()) and
result = "(...)"
}
}
}

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

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

@@ -39,6 +39,14 @@ module AccessAfterLifetime {
*/
abstract class Barrier extends DataFlow::Node { }
/**
* Holds if the value `source` points to 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

@@ -50,7 +50,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

@@ -16,7 +16,7 @@ private import codeql.rust.frameworks.stdlib.Builtins as Builtins
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 Builtins::NumericType or
@@ -32,7 +32,7 @@ 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 Builtins::IntegralType or

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

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

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

@@ -26,17 +26,17 @@ module AccessAfterLifetimeConfig implements DataFlow::ConfigSig {
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().getExpr().isFromMacroExpansion()
not node.asExpr().isFromMacroExpansion()
}
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().getExpr().isFromMacroExpansion() and
not node.asExpr().isFromMacroExpansion() and
// include only results inside `unsafe` blocks, as other results tend to be false positives
(
node.asExpr().getExpr().getEnclosingBlock*().isUnsafe() or
node.asExpr().getExpr().getEnclosingCallable().(Function).isUnsafe()
node.asExpr().getEnclosingBlock*().isUnsafe() or
node.asExpr().getEnclosingCallable().(Function).isUnsafe()
)
}
@@ -45,11 +45,9 @@ module AccessAfterLifetimeConfig implements DataFlow::ConfigSig {
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
AccessAfterLifetime::dereferenceAfterLifetime(source, sink, target)
)
}
}

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

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

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

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

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