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: Avoid location based variable analysis #18462

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
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
8 changes: 4 additions & 4 deletions rust/ql/.generated.list

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

27 changes: 27 additions & 0 deletions rust/ql/lib/codeql/rust/AstConsistency.qll
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,27 @@ query predicate multipleParents(Element child, string childClass, Element parent
parentClass = parent.getPrimaryQlClasses()
}

/** Holds if `parent` has multiple children at the same index. */
query predicate multipleChildren(Element parent, int index, Element child1, Element child2) {
child1 = getChildAndAccessor(parent, index, _) and
child2 = getChildAndAccessor(parent, index, _) and
child1 != child2
}

/**
* Holds if `child` has multiple positions amongst the `accessor` children
* of `parent`.
*
* Children are allowed to have multiple positions for _different_ accessors,
* for example in an array repeat expression `[1; 10]`, `1` has positions for
* both `getRepeatOperand()` and `getExpr()`.
*/
query predicate multiplePositions(Element parent, int pos1, int pos2, string accessor, Element child) {
child = getChildAndAccessor(parent, pos1, accessor) and
child = getChildAndAccessor(parent, pos2, accessor) and
pos1 != pos2
}

/**
* Gets counts of abstract syntax tree inconsistencies of each type.
*/
Expand All @@ -71,4 +92,10 @@ int getAstInconsistencyCounts(string type) {
or
type = "Multiple parents" and
result = count(Element e | multipleParents(e) | e)
or
type = "Multiple children" and
result = count(Element e | multipleChildren(_, _, e, _) | e)
or
type = "Multiple positions" and
result = count(Element e | multiplePositions(_, _, _, _, e) | e)
}
2 changes: 1 addition & 1 deletion rust/ql/lib/codeql/rust/controlflow/CfgNodes.qll
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ final class FormatArgsExprCfgNode extends Nodes::FormatArgsExprCfgNode {
/** Gets a format argument of the `i`th format of this format arguments expression (0-based). */
FormatTemplateVariableAccessCfgNode getFormatTemplateVariableAccess(int i) {
exists(FormatTemplateVariableAccess v |
v.getArgument() = node.getFormat(i).getArgument() and
v.getArgument() = node.getFormat(i).getArgument(_) and
result.getFormatTemplateVariableAccess() = v and
any(ChildMapping mapping).hasCfgChild(node, v, this, result)
)
Expand Down
10 changes: 10 additions & 0 deletions rust/ql/lib/codeql/rust/elements/internal/FormatArgumentImpl.qll
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,16 @@ module Impl {

override Format getParent() { result = Synth::TFormat(parent, index, _, _) }

/** Gets the position of this argument. */
int getPosition() {
this =
rank[result + 1](FormatArgument f, int offs |
f = Synth::TFormatArgument(parent, index, _, _, _, offs)
|
f order by offs
)
}

override FormatTemplateVariableAccess getVariable() { result.getArgument() = this }
}

Expand Down
4 changes: 3 additions & 1 deletion rust/ql/lib/codeql/rust/elements/internal/FormatImpl.qll
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ module Impl {

override int getIndex() { result = index }

override FormatArgument getArgument() { result.getParent() = this }
override FormatArgument getArgument(int i) {
result.getParent() = this and i = result.getPosition()
}

/**
* Gets the name or position reference of this format, if any. For example `name` and `0` in:
Expand Down
119 changes: 90 additions & 29 deletions rust/ql/lib/codeql/rust/elements/internal/VariableImpl.qll
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ module Impl {

/**
* A path expression that may access a local variable. These are paths that
* only consists of a simple name (i.e., without generic arguments,
* only consist of a simple name (i.e., without generic arguments,
* qualifiers, etc.).
*/
private class VariableAccessCand extends PathExprBase {
Expand Down Expand Up @@ -231,51 +231,117 @@ module Impl {
)
}

private Element getImmediateChildMin(Element e, int index) {
// A child may have multiple positions for different accessors,
// so always use the first
result = getImmediateChildAndAccessor(e, index, _) and
index = min(int i | result = getImmediateChildAndAccessor(e, i, _) | i)
}

private Element getImmediateChild(Element e, int index) {
result = rank[index + 1](Element res, int i | res = getImmediateChildMin(e, i) | res order by i)
}

private Element getImmediateLastChild(Element e) {
exists(int last |
result = getImmediateChild(e, last) and
not exists(getImmediateChild(e, last + 1))
)
}

pragma[nomagic]
private int getPreOrderNumbering(VariableScope scope, Element n) {
n = scope and
result = 0
or
exists(Element parent |
not parent instanceof VariableScope
or
parent = scope
|
// first child of a previously numbered node
result = getPreOrderNumbering(scope, parent) + 1 and
n = getImmediateChild(parent, 0)
or
// non-first child of a previously numbered node
exists(Element child, int i |
result = getLastPreOrderNumbering(scope, child) + 1 and
child = getImmediateChild(parent, i) and
n = getImmediateChild(parent, i + 1)
)
)
}

pragma[nomagic]
private int getLastPreOrderNumbering(VariableScope scope, Element n) {
exists(Element leaf |
result = getPreOrderNumbering(scope, leaf) and
leaf != scope and
(
not exists(getImmediateChild(leaf, _))
or
leaf instanceof VariableScope
) and
(
n = leaf
or
getImmediateLastChild(n) = leaf and
not n instanceof VariableScope
)
)
or
exists(Element mid |
mid = getImmediateLastChild(n) and
result = getLastPreOrderNumbering(scope, mid) and
not mid instanceof VariableScope and
not n instanceof VariableScope
)
}

/**
* Holds if `v` is named `name` and is declared inside variable scope
* `scope`, and `v` is bound starting from `(line, column)`.
* `scope`. The pre-order numbering of the binding site of `v`, amongst
* all nodes nester under `scope`, is `ord`.
*/
private predicate variableDeclInScope(
Variable v, VariableScope scope, string name, int line, int column
) {
private predicate variableDeclInScope(Variable v, VariableScope scope, string name, int ord) {
name = v.getName() and
(
parameterDeclInScope(v, scope) and
scope.getLocation().hasLocationFileInfo(_, line, column, _, _)
ord = getPreOrderNumbering(scope, scope)
or
exists(Pat pat | pat = getAVariablePatAncestor(v) |
scope =
any(MatchArmScope arm |
arm.getPat() = pat and
arm.getLocation().hasLocationFileInfo(_, line, column, _, _)
ord = getPreOrderNumbering(scope, arm)
)
or
exists(LetStmt let |
let.getPat() = pat and
scope = getEnclosingScope(let) and
// for `let` statements, variables are bound _after_ the statement, i.e.
// not in the RHS
let.getLocation().hasLocationFileInfo(_, _, _, line, column)
ord = getLastPreOrderNumbering(scope, let) + 1
)
or
exists(IfExpr ie, LetExpr let |
let.getPat() = pat and
ie.getCondition() = let and
scope = ie.getThen() and
scope.getLocation().hasLocationFileInfo(_, line, column, _, _)
ord = getPreOrderNumbering(scope, scope)
)
or
exists(ForExpr fe |
fe.getPat() = pat and
scope = fe.getLoopBody() and
scope.getLocation().hasLocationFileInfo(_, line, column, _, _)
ord = getPreOrderNumbering(scope, scope)
)
or
exists(WhileExpr we, LetExpr let |
let.getPat() = pat and
we.getCondition() = let and
scope = we.getLoopBody() and
scope.getLocation().hasLocationFileInfo(_, line, column, _, _)
ord = getPreOrderNumbering(scope, scope)
)
)
)
Expand All @@ -290,21 +356,22 @@ module Impl {
* to reach `scope` from `cand`.
*/
private predicate variableAccessCandInScope(
VariableAccessCand cand, VariableScope scope, string name, int nestLevel, int startline,
int startcolumn, int endline, int endcolumn
VariableAccessCand cand, VariableScope scope, string name, int nestLevel, int ord
) {
name = cand.getName() and
scope = [cand.(VariableScope), getEnclosingScope(cand)] and
cand.getLocation().hasLocationFileInfo(_, startline, startcolumn, endline, endcolumn) and
ord = getPreOrderNumbering(scope, cand) and
// cand.getLocation().hasLocationFileInfo(_, startline, startcolumn, endline, endcolumn) and
nestLevel = 0
or
exists(VariableScope inner |
variableAccessCandInScope(cand, inner, name, nestLevel - 1, _, _, _, _) and
variableAccessCandInScope(cand, inner, name, nestLevel - 1, _) and
scope = getEnclosingScope(inner) and
// Use the location of the inner scope as the location of the access, instead of the
// actual access location. This allows us to collapse multiple accesses in inner
// scopes to a single entity
inner.getLocation().hasLocationFileInfo(_, startline, startcolumn, endline, endcolumn)
// inner.getLocation().hasLocationFileInfo(_, startline, startcolumn, endline, endcolumn)
ord = getPreOrderNumbering(scope, inner)
)
}

Expand Down Expand Up @@ -375,15 +442,10 @@ module Impl {
}

pragma[nomagic]
predicate rankBy(
string name, VariableScope scope, int startline, int startcolumn, int endline, int endcolumn
) {
variableDeclInScope(this.asVariable(), scope, name, startline, startcolumn) and
endline = -1 and
endcolumn = -1
predicate rankBy(string name, VariableScope scope, int ord) {
variableDeclInScope(this.asVariable(), scope, name, ord)
or
variableAccessCandInScope(this.asVariableAccessCand(), scope, name, _, startline, startcolumn,
endline, endcolumn)
variableAccessCandInScope(this.asVariableAccessCand(), scope, name, _, ord)
}
}

Expand All @@ -396,11 +458,10 @@ module Impl {

int getRank(VariableScope scope, string name, VariableOrAccessCand v) {
v =
rank[result](VariableOrAccessCand v0, int startline, int startcolumn, int endline,
int endcolumn |
v0.rankBy(name, scope, startline, startcolumn, endline, endcolumn)
rank[result](VariableOrAccessCand v0, int ord |
v0.rankBy(name, scope, ord)
|
v0 order by startline, startcolumn, endline, endcolumn
v0 order by ord
)
}
}
Expand Down Expand Up @@ -440,7 +501,7 @@ module Impl {
exists(int rnk |
variableReachesRank(scope, name, v, rnk) and
rnk = rankVariableOrAccess(scope, name, TVariableOrAccessCandVariableAccessCand(cand)) and
variableAccessCandInScope(cand, scope, name, nestLevel, _, _, _, _)
variableAccessCandInScope(cand, scope, name, nestLevel, _)
)
}

Expand Down
13 changes: 9 additions & 4 deletions rust/ql/lib/codeql/rust/elements/internal/generated/Format.qll

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

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

Loading
Loading