Skip to content
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
1 change: 0 additions & 1 deletion rust/ql/.generated.list

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

1 change: 0 additions & 1 deletion rust/ql/.gitattributes

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 @@ -99,6 +99,8 @@ class FormatTemplateVariableAccessTree extends LeafTree, FormatTemplateVariableA
class ItemTree extends LeafTree, Item {
ItemTree() {
not this instanceof MacroCall and
not this instanceof Const and
not this instanceof Static and
this = any(StmtList s).getAStatement()
}
}
Expand Down
11 changes: 11 additions & 0 deletions rust/ql/lib/codeql/rust/controlflow/internal/Scope.qll
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,14 @@ final class CallableScope extends CfgScopeImpl, Callable {
/** Holds if `scope` is exited when `last` finishes with completion `c`. */
override predicate scopeLast(AstNode last, Completion c) { last(this.getBody(), last, c) }
}

/**
* A special scope used to represent the context in which `const`s and
* `static`s are initialized. We do not actually compute a CFG for such
* scopes.
*/
final class SourceFileScope extends CfgScopeImpl, SourceFile {
override predicate scopeFirst(AstNode first) { none() }

override predicate scopeLast(AstNode last, Completion c) { none() }
}
16 changes: 15 additions & 1 deletion rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll
Original file line number Diff line number Diff line change
Expand Up @@ -653,8 +653,22 @@ module RustDataFlowGen<RustDataFlowInputSig Input> implements InputSig<Location>
*/
predicate jumpStep(Node node1, Node node2) {
FlowSummaryImpl::Private::Steps::summaryJumpStep(node1.(FlowSummaryNode).getSummaryNode(),
node2.(FlowSummaryNode).getSummaryNode()) or
node2.(FlowSummaryNode).getSummaryNode())
or
FlowSummaryImpl::Private::Steps::sourceJumpStep(node1.(FlowSummaryNode).getSummaryNode(), node2)
or
exists(Const c |
node1.asExpr() = c.getBody() and
node2.asExpr() = c.getAnAccess()
)
or
exists(StaticNode sn, Static s | s = sn.getStatic() |
node1 = sn and
node2.asExpr() = s.getAnAccess().(StaticReadAccess)
or
node1.asExpr() = [s.getBody(), s.getAnAccess().(StaticWriteAccess)] and
node2 = sn
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can see here (and in one of the new tests) that we assume all writes to a static could (via the two dataflow steps) reach all reads. This seems like a reasonable approximation, and I think it's what we do in other languages that have similar features.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it is similar to what we do in other languages.

)
}

pragma[nomagic]
Expand Down
14 changes: 13 additions & 1 deletion rust/ql/lib/codeql/rust/dataflow/internal/Node.qll
Original file line number Diff line number Diff line change
Expand Up @@ -704,6 +704,17 @@ final class CastNode extends ExprNode {
CastNode() { none() }
}

/**
* A node in the data flow graph that corresponds to a `static`.
*/
class StaticNode extends AstNodeNode, TStaticNode {
override Static n;

StaticNode() { this = TStaticNode(n) }

Static getStatic() { result = n }
}

cached
newtype TNode =
TExprNode(Expr e) { e.hasEnclosingCfgScope() and Stages::DataFlowStage::ref() } or
Expand Down Expand Up @@ -755,4 +766,5 @@ newtype TNode =
)
} or
TClosureSelfReferenceNode(CfgScope c) { lambdaCreationExpr(c) } or
TCaptureNode(VariableCapture::Flow::SynthesizedCaptureNode cn)
TCaptureNode(VariableCapture::Flow::SynthesizedCaptureNode cn) or
TStaticNode(Static s)
11 changes: 11 additions & 0 deletions rust/ql/lib/codeql/rust/elements/StaticAccess.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/**
* This module provides the public class `StaticAccess`.
*/

private import internal.StaticImpl

final class StaticAccess = Impl::StaticAccess;

final class StaticWriteAccess = Impl::StaticWriteAccess;

final class StaticReadAccess = Impl::StaticReadAccess;
16 changes: 15 additions & 1 deletion rust/ql/lib/codeql/rust/elements/internal/AstNodeImpl.qll
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,29 @@ module Impl {
)
}

pragma[nomagic]
private predicate isConstOrStatic(File f) {
f = this.getFile() and
(
this instanceof Const
or
this instanceof Static
)
}

/** Gets the CFG scope that encloses this node, if any. */
cached
CfgScope getEnclosingCfgScope() {
exists(AstNode p | p = this.getParentNode() |
result = p
result = p and
not result instanceof SourceFile
or
not p instanceof CfgScope and
not this.isConstOrStatic(_) and
result = p.getEnclosingCfgScope()
)
or
this.isConstOrStatic(result.(SourceFile).getFile())
}

/** Holds if this node is inside a CFG scope. */
Expand Down
7 changes: 6 additions & 1 deletion rust/ql/lib/codeql/rust/elements/internal/ConstImpl.qll
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,12 @@ module Impl {
* const X: i32 = 42;
* ```
*/
class Const extends Generated::Const { }
class Const extends Generated::Const {
/** Gets an access to this constant item. */
ConstAccess getAnAccess() { this = result.getConst() }

override string toStringImpl() { result = "const " + this.getName().getText() }
}

/**
* A constant access.
Expand Down
46 changes: 44 additions & 2 deletions rust/ql/lib/codeql/rust/elements/internal/StaticImpl.qll
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
// generated by codegen, remove this comment if you wish to edit this file
/**
* This module provides a hand-modifiable wrapper around the generated class `Static`.
*
* INTERNAL: Do not use.
*/

private import codeql.rust.elements.internal.generated.Static
private import codeql.rust.elements.internal.AstNodeImpl::Impl as AstNodeImpl
private import codeql.rust.elements.internal.PathExprImpl::Impl as PathExprImpl
private import codeql.rust.elements.internal.VariableImpl::Impl as VariableImpl
private import codeql.rust.internal.PathResolution

/**
* INTERNAL: This module contains the customizable definition of `Static` and should not
* be referenced directly.
*/
module Impl {
// the following QLdoc is generated: if you need to edit it, do it in the schema file
/**
* A static item declaration.
*
Expand All @@ -20,5 +24,43 @@ module Impl {
* static X: i32 = 42;
* ```
*/
class Static extends Generated::Static { }
class Static extends Generated::Static {
/** Gets an access to this static item. */
StaticAccess getAnAccess() { this = result.getStatic() }

override string toStringImpl() { result = "static " + this.getName().getText() }
}

/**
* A static access.
*
* For example:
* ```rust
* static X: i32 = 42;
*
* fn main() {
* println!("{}", X);
* }
* ```
*/
class StaticAccess extends AstNodeImpl::AstNode, PathExprImpl::PathExpr {
private Static s;

StaticAccess() { s = resolvePath(this.getPath()) }

/** Gets the static being accessed. */
Static getStatic() { result = s }

override string getAPrimaryQlClass() { result = "StaticAccess" }
}

/** A static write access. */
class StaticWriteAccess extends StaticAccess {
StaticWriteAccess() { VariableImpl::assignmentOperationDescendant(_, this) }
}

/** A static read access. */
class StaticReadAccess extends StaticAccess {
StaticReadAccess() { not this instanceof StaticWriteAccess }
}
}
32 changes: 32 additions & 0 deletions rust/ql/lib/codeql/rust/internal/PathResolution.qll
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,38 @@ private class ConstItemNode extends AssocItemNode instanceof Const {
override TypeParam getTypeParam(int i) { none() }
}

private class StaticItemNode extends ItemNode instanceof Static {
override string getName() { result = Static.super.getName().getText() }

override Namespace getNamespace() { result.isValue() }

override Visibility getVisibility() { result = Static.super.getVisibility() }

override Attr getAnAttr() { result = Static.super.getAnAttr() }

override TypeParam getTypeParam(int i) { none() }

override predicate hasCanonicalPath(Crate c) { this.hasCanonicalPathPrefix(c) }

bindingset[c]
private string getCanonicalPathPart(Crate c, int i) {
i = 0 and
result = this.getCanonicalPathPrefix(c)
or
i = 1 and
result = "::"
or
i = 2 and
result = this.getName()
}

language[monotonicAggregates]
override string getCanonicalPath(Crate c) {
this.hasCanonicalPath(c) and
result = strictconcat(int i | i in [0 .. 2] | this.getCanonicalPathPart(c, i) order by i)
}
}

private class TypeItemTypeItemNode extends NamedItemNode, TypeItemNode instanceof TypeItem {
override string getName() { result = TypeItem.super.getName().getText() }

Expand Down
1 change: 1 addition & 0 deletions rust/ql/lib/rust.qll
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import codeql.rust.elements.AssignmentOperation
import codeql.rust.elements.BitwiseOperation
import codeql.rust.elements.ComparisonOperation
import codeql.rust.elements.ConstAccess
import codeql.rust.elements.StaticAccess
import codeql.rust.elements.DerefExpr
import codeql.rust.elements.LiteralExprExt
import codeql.rust.elements.LogicalOperation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ private module ResolveTest implements TestSig {
i.getLocation().hasLocationInfo(filepath, _, _, line, _)
}

private predicate commmentAt(string text, string filepath, int line) {
private predicate commentAt(string text, string filepath, int line) {
exists(Comment c |
c.getLocation().hasLocationInfo(filepath, line, _, _, _) and
c.getCommentText().trim() = text and
Expand All @@ -28,9 +28,9 @@ private module ResolveTest implements TestSig {
if i instanceof SourceFile
then value = i.getFile().getBaseName()
else (
commmentAt(value, filepath, line)
commentAt(value, filepath, line)
or
not commmentAt(_, filepath, line) and
not commentAt(_, filepath, line) and
value = i.getName()
)
)
Expand Down
9 changes: 9 additions & 0 deletions rust/ql/test/TestUtils.qll
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,12 @@ class CrateElement extends Element {
class Builtin extends AstNode {
Builtin() { this.getFile().getAbsolutePath().matches("%/builtins/%.rs") }
}

predicate commentAt(string text, string filepath, int line) {
exists(Comment c |
c.getLocation().hasLocationInfo(filepath, line, _, _, _) and
c.getCommentText().trim() = text and
c.fromSource() and
not text.matches("$%")
)
}
8 changes: 4 additions & 4 deletions rust/ql/test/extractor-tests/generated/Const/Const.expected
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
instances
| gen_const.rs:4:5:7:22 | Const | isConst: | yes | isDefault: | no | hasImplementation: | yes |
| gen_const.rs:4:5:7:22 | const X | isConst: | yes | isDefault: | no | hasImplementation: | yes |
getAttributeMacroExpansion
getAttr
getBody
| gen_const.rs:4:5:7:22 | Const | gen_const.rs:7:20:7:21 | 42 |
| gen_const.rs:4:5:7:22 | const X | gen_const.rs:7:20:7:21 | 42 |
getGenericParamList
getName
| gen_const.rs:4:5:7:22 | Const | gen_const.rs:7:11:7:11 | X |
| gen_const.rs:4:5:7:22 | const X | gen_const.rs:7:11:7:11 | X |
getTypeRepr
| gen_const.rs:4:5:7:22 | Const | gen_const.rs:7:14:7:16 | i32 |
| gen_const.rs:4:5:7:22 | const X | gen_const.rs:7:14:7:16 | i32 |
getVisibility
getWhereClause
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ instances
getAttr
getExternItem
| gen_extern_item_list.rs:7:16:10:5 | ExternItemList | 0 | gen_extern_item_list.rs:8:9:8:17 | fn foo |
| gen_extern_item_list.rs:7:16:10:5 | ExternItemList | 1 | gen_extern_item_list.rs:9:9:9:24 | Static |
| gen_extern_item_list.rs:7:16:10:5 | ExternItemList | 1 | gen_extern_item_list.rs:9:9:9:24 | static BAR |
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
instances
| gen_static.rs:4:5:7:23 | Static | isMut: | no | isStatic: | yes | isUnsafe: | no |
| gen_static.rs:4:5:7:23 | static X | isMut: | no | isStatic: | yes | isUnsafe: | no |
getAttributeMacroExpansion
getAttr
getBody
| gen_static.rs:4:5:7:23 | Static | gen_static.rs:7:21:7:22 | 42 |
| gen_static.rs:4:5:7:23 | static X | gen_static.rs:7:21:7:22 | 42 |
getName
| gen_static.rs:4:5:7:23 | Static | gen_static.rs:7:12:7:12 | X |
| gen_static.rs:4:5:7:23 | static X | gen_static.rs:7:12:7:12 | X |
getTypeRepr
| gen_static.rs:4:5:7:23 | Static | gen_static.rs:7:15:7:17 | i32 |
| gen_static.rs:4:5:7:23 | static X | gen_static.rs:7:15:7:17 | i32 |
getVisibility
Original file line number Diff line number Diff line change
Expand Up @@ -957,7 +957,7 @@ macro_expansion.rs:
# 94| getName(): [Name] MyTrait
# 98| getItem(20): [Union] union MyDeriveUnion
# 99| getDeriveMacroExpansion(0): [MacroItems] MacroItems
# 99| getItem(0): [Const] Const
# 99| getItem(0): [Const] const CONST_MyDeriveUnion
# 98| getBody(): [IntegerLiteralExpr] 42
# 99| getName(): [Name] CONST_MyDeriveUnion
# 98| getTypeRepr(): [PathTypeRepr] u32
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ derive_macros
| macro_expansion.rs:83:1:86:1 | struct MyDerive | 0 | 0 | macro_expansion.rs:84:8:85:9 | impl ...::Debug for MyDerive::<...> { ... } |
| macro_expansion.rs:88:1:92:1 | enum MyDeriveEnum | 0 | 0 | macro_expansion.rs:89:6:91:12 | impl ...::PartialEq for MyDeriveEnum::<...> { ... } |
| macro_expansion.rs:88:1:92:1 | enum MyDeriveEnum | 1 | 0 | macro_expansion.rs:89:6:89:17 | impl ...::Eq for MyDeriveEnum::<...> { ... } |
| macro_expansion.rs:98:1:102:1 | union MyDeriveUnion | 0 | 0 | macro_expansion.rs:99:7:99:19 | Const |
| macro_expansion.rs:98:1:102:1 | union MyDeriveUnion | 0 | 0 | macro_expansion.rs:99:7:99:19 | const CONST_MyDeriveUnion |
| macro_expansion.rs:98:1:102:1 | union MyDeriveUnion | 0 | 1 | macro_expansion.rs:99:7:99:19 | impl MyTrait for MyDeriveUnion { ... } |
macro_calls
| macro_expansion.rs:5:9:5:35 | concat!... | macro_expansion.rs:5:17:5:34 | "Hello world!" |
Expand Down
15 changes: 9 additions & 6 deletions rust/ql/test/library-tests/const_access/const_access.expected
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
testFailures
constAccess
| main.rs:17:13:17:24 | GLOBAL_CONST | main.rs:1:1:1:29 | Const |
| main.rs:19:13:19:24 | STRING_CONST | main.rs:2:1:2:35 | Const |
| main.rs:21:13:21:33 | ...::ASSOC_CONST | main.rs:9:5:9:33 | Const |
| main.rs:23:13:23:35 | ...::MODULE_CONST | main.rs:13:5:13:38 | Const |
| main.rs:25:8:25:19 | GLOBAL_CONST | main.rs:1:1:1:29 | Const |
| main.rs:29:16:29:36 | ...::ASSOC_CONST | main.rs:9:5:9:33 | Const |
| main.rs:17:13:17:24 | GLOBAL_CONST | main.rs:1:1:1:29 | const GLOBAL_CONST |
| main.rs:19:13:19:24 | STRING_CONST | main.rs:2:1:2:35 | const STRING_CONST |
| main.rs:21:13:21:33 | ...::ASSOC_CONST | main.rs:9:5:9:33 | const ASSOC_CONST |
| main.rs:23:13:23:35 | ...::MODULE_CONST | main.rs:13:5:13:38 | const MODULE_CONST |
| main.rs:26:16:26:27 | GLOBAL_CONST | main.rs:1:1:1:29 | const GLOBAL_CONST |
| main.rs:30:16:30:36 | ...::ASSOC_CONST | main.rs:9:5:9:33 | const ASSOC_CONST |
| main.rs:33:20:33:31 | GLOBAL_CONST | main.rs:1:1:1:29 | const GLOBAL_CONST |
| main.rs:39:17:39:28 | STRING_CONST | main.rs:38:9:38:43 | const STRING_CONST |
| main.rs:43:21:43:32 | STRING_CONST | main.rs:42:13:42:48 | const STRING_CONST |
14 changes: 12 additions & 2 deletions rust/ql/test/library-tests/const_access/const_access.ql
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,25 @@ import TestUtils
query predicate constAccess(ConstAccess ca, Const c) { toBeTested(ca) and c = ca.getConst() }

module ConstAccessTest implements TestSig {
private predicate constAt(Const c, string filepath, int line) {
c.getLocation().hasLocationInfo(filepath, _, _, line, _)
}

string getARelevantTag() { result = "const_access" }

predicate hasActualResult(Location location, string element, string tag, string value) {
exists(ConstAccess ca |
exists(ConstAccess ca, Const c, string filepath, int line |
toBeTested(ca) and
location = ca.getLocation() and
element = ca.toString() and
tag = "const_access" and
value = ca.getConst().getName().getText()
c = ca.getConst() and
constAt(c, filepath, line)
|
commentAt(value, filepath, line)
or
not commentAt(_, filepath, line) and
value = c.getName().getText()
)
}
}
Expand Down
Loading
Loading