Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rust ref pattern #18754

Merged
merged 8 commits into from
Feb 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -273,6 +273,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 @@ -584,11 +593,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 @@ -1266,6 +1287,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 @@ -1560,6 +1589,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