Skip to content

Commit

Permalink
Merge pull request #18754 from paldepind/rust-ref-pattern
Browse files Browse the repository at this point in the history
Rust ref pattern
  • Loading branch information
paldepind authored Feb 19, 2025
2 parents ebd6fd4 + faef735 commit ae7e15d
Show file tree
Hide file tree
Showing 22 changed files with 867 additions and 296 deletions.
2 changes: 1 addition & 1 deletion rust/ql/.generated.list

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -648,8 +648,12 @@ module PatternTrees {

abstract class PostOrderPatTree extends StandardPatTree, StandardPostOrderTree { }

class IdentPatTree extends PostOrderPatTree, IdentPat {
override Pat getPat(int i) { i = 0 and result = this.getPat() }
class IdentPatTree extends PostOrderTree, IdentPat {
override predicate first(AstNode node) {
first(this.getPat(), node)
or
not this.hasPat() and node = this.getName()
}

override predicate last(AstNode node, Completion c) {
super.last(node, c)
Expand All @@ -658,8 +662,16 @@ module PatternTrees {
}

override predicate succ(AstNode pred, AstNode succ, Completion c) {
super.succ(pred, succ, c) and c.(MatchCompletion).succeeded()
// Edge from successful subpattern to name
last(this.getPat(), pred, c) and
first(this.getName(), succ) and
c.(MatchCompletion).succeeded()
or
// Edge from name to the identifier pattern itself
last(this.getName(), pred, c) and succ = this and completionIsNormal(c)
}

override predicate propagatesAbnormal(AstNode child) { child = this.getPat() }
}

class BoxPatTree extends PreOrderPatTree, BoxPat {
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion rust/ql/lib/codeql/rust/dataflow/Ssa.qll
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ module Ssa {
)
or
exists(LetStmtCfgNode ls |
ls.getPat() = write and
ls.getPat().(IdentPatCfgNode).getName() = write and
ls.getInitializer() = value
)
}
Expand Down
30 changes: 30 additions & 0 deletions rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,15 @@ module Node {
override PatCfgNode asPat() { result = n }
}

/** A data flow node that corresponds to a name node in the CFG. */
final class NameNode extends AstCfgFlowNode, TNameNode {
override NameCfgNode n;

NameNode() { this = TNameNode(n) }

NameCfgNode asName() { result = n }
}

/**
* The value of a parameter at function entry, viewed as a node in a data
* flow graph.
Expand Down Expand Up @@ -603,11 +612,23 @@ module LocalFlow {
predicate localFlowStepCommon(Node nodeFrom, Node nodeTo) {
nodeFrom.getCfgNode() = getALastEvalNode(nodeTo.getCfgNode())
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()
)
or
exists(IdentPatCfgNode p |
not p.isRef() and
nodeFrom.getCfgNode() = p and
nodeTo.getCfgNode() = p.getName()
)
or
exists(SelfParamCfgNode self |
nodeFrom.getCfgNode() = self and
nodeTo.getCfgNode() = self.getName()
)
or
// An edge from a pattern/expression to its corresponding SSA definition.
nodeFrom.(Node::AstCfgFlowNode).getCfgNode() =
nodeTo.(Node::SsaNode).getDefinitionExt().(Ssa::WriteDefinition).getControlFlowNode()
Expand Down Expand Up @@ -1285,6 +1306,14 @@ module RustDataFlow implements InputSig<Location> {
node2.asExpr().(ArrayListExprCfgNode).getAnExpr()
]
or
// Store from a `ref` identifier pattern into the contained name.
exists(IdentPatCfgNode p |
c instanceof ReferenceContent and
p.isRef() and
node1.asPat() = p and
node2.(Node::NameNode).asName() = p.getName()
)
or
fieldAssignment(node1, node2.(PostUpdateNode).getPreUpdateNode(), c)
or
referenceAssignment(node1, node2.(PostUpdateNode).getPreUpdateNode(), c)
Expand Down Expand Up @@ -1579,6 +1608,7 @@ private module Cached {
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
Expand Down
20 changes: 10 additions & 10 deletions rust/ql/lib/codeql/rust/dataflow/internal/SsaImpl.qll
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,25 @@ private import Cfg
private import codeql.rust.controlflow.internal.ControlFlowGraphImpl as ControlFlowGraphImpl
private import codeql.ssa.Ssa as SsaImplCommon

/** Holds if `v` is introduced like `let v : i64;`. */
private predicate isUnitializedLet(IdentPat pat, Variable v) {
pat = v.getPat() and
/**
* Holds if `name` occurs in the left-hand side of an uninitialized let
* statement such as in `let name : i64;`.
*/
private predicate isInUninitializedLet(Name name) {
exists(LetStmt let |
let = v.getLetStmt() and
let.getPat().(IdentPat).getName() = name and
not let.hasInitializer()
)
}

/** Holds if `write` writes to variable `v`. */
predicate variableWrite(AstNode write, Variable v) {
exists(IdentPat pat |
pat = write and
pat = v.getPat() and
not isUnitializedLet(pat, v)
exists(Name name |
name = write and
name = v.getName() and
not isInUninitializedLet(name)
)
or
exists(SelfParam self | self = write and self = v.getSelfParam())
or
exists(VariableAccess access |
access = write and
access.getVariable() = v
Expand Down
Loading

0 comments on commit ae7e15d

Please sign in to comment.