From 0aee2e6fb26b073e61f48697cad34f671f0b83c3 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Wed, 15 Jan 2025 11:03:36 +0100 Subject: [PATCH 1/5] Rust: Implement path resolution in QL --- rust/ql/lib/codeql/rust/AstConsistency.qll | 11 + .../elements/internal/CallExprBaseImpl.qll | 7 +- .../rust/elements/internal/PathResolution.qll | 464 ++++++++++++++++++ .../CONSISTENCY/AstConsistency.expected | 3 + rust/ql/test/library-tests/modules/main.rs | 201 ++++++++ .../library-tests/modules/modules.expected | 137 ++++++ rust/ql/test/library-tests/modules/modules.ql | 49 ++ rust/ql/test/library-tests/modules/my.rs | 12 + .../test/library-tests/modules/my/nested.rs | 22 + rust/ql/test/library-tests/modules/my2/mod.rs | 6 + .../test/library-tests/modules/my2/nested2.rs | 11 + .../diagnostics/AstConsistencyCounts.expected | 1 + .../CWE-696/BadCTorInitialization.expected | 89 ++-- .../test/query-tests/security/CWE-696/test.rs | 5 +- 14 files changed, 972 insertions(+), 46 deletions(-) create mode 100644 rust/ql/lib/codeql/rust/elements/internal/PathResolution.qll create mode 100644 rust/ql/test/library-tests/modules/CONSISTENCY/AstConsistency.expected create mode 100644 rust/ql/test/library-tests/modules/main.rs create mode 100644 rust/ql/test/library-tests/modules/modules.expected create mode 100644 rust/ql/test/library-tests/modules/modules.ql create mode 100644 rust/ql/test/library-tests/modules/my.rs create mode 100644 rust/ql/test/library-tests/modules/my/nested.rs create mode 100644 rust/ql/test/library-tests/modules/my2/mod.rs create mode 100644 rust/ql/test/library-tests/modules/my2/nested2.rs diff --git a/rust/ql/lib/codeql/rust/AstConsistency.qll b/rust/ql/lib/codeql/rust/AstConsistency.qll index 0c9359d6569c..91fefd0d8088 100644 --- a/rust/ql/lib/codeql/rust/AstConsistency.qll +++ b/rust/ql/lib/codeql/rust/AstConsistency.qll @@ -73,6 +73,14 @@ query predicate multiplePositions(Element parent, int pos1, int pos2, string acc pos1 != pos2 } +private import codeql.rust.elements.internal.PathResolution + +/** Holds if `p` may resolve to multiple items. */ +query predicate multiplePathResolutions(Path p, Item i) { + i = resolvePath(p) and + strictcount(resolvePath(p)) > 1 +} + /** * Gets counts of abstract syntax tree inconsistencies of each type. */ @@ -98,4 +106,7 @@ int getAstInconsistencyCounts(string type) { or type = "Multiple positions" and result = count(Element e | multiplePositions(_, _, _, _, e) | e) + or + type = "Multiple path resolutions" and + result = count(Path p | multiplePathResolutions(p, _) | p) } diff --git a/rust/ql/lib/codeql/rust/elements/internal/CallExprBaseImpl.qll b/rust/ql/lib/codeql/rust/elements/internal/CallExprBaseImpl.qll index f67c8f289257..e93abe999d26 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/CallExprBaseImpl.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/CallExprBaseImpl.qll @@ -16,6 +16,7 @@ module Impl { private import codeql.rust.elements.internal.MethodCallExprImpl::Impl private import codeql.rust.elements.internal.CallExprImpl::Impl private import codeql.rust.elements.internal.PathExprImpl::Impl + private import codeql.rust.elements.internal.PathResolution pragma[nomagic] Resolvable getCallResolvable(CallExprBase call) { @@ -33,6 +34,10 @@ module Impl { * Gets the target callable of this call, if a unique such target can * be statically resolved. */ - Callable getStaticTarget() { getCallResolvable(this).resolvesAsItem(result) } + Callable getStaticTarget() { + getCallResolvable(this).resolvesAsItem(result) + or + result = resolvePath(this.(CallExpr).getFunction().(PathExpr).getPath()) + } } } diff --git a/rust/ql/lib/codeql/rust/elements/internal/PathResolution.qll b/rust/ql/lib/codeql/rust/elements/internal/PathResolution.qll new file mode 100644 index 000000000000..682a681dec95 --- /dev/null +++ b/rust/ql/lib/codeql/rust/elements/internal/PathResolution.qll @@ -0,0 +1,464 @@ +/** Provides functionality for resolving paths. */ + +private import rust +private import codeql.rust.elements.internal.generated.ParentChild + +/** + * An item that may be referred to by a path, and which is a node in + * the _item graph_. + * + * The item graph is a labeled directed graph, where an edge + * `item1 --name--> item2` means that `item2` is available inside the + * scope of `item1` under the name `name`. For example, if we have + * + * ```rust + * mod m1 { + * mod m2 { } + * } + * ``` + * + * then there is an edge `m1 --m2--> m1::m2`. + * + * Source files are also considered nodes in the item graph, and for + * each source file `f` there is an edge `f --name--> item` when `f` + * declares `item` with the name `name`. + * + * For imports like + * + * ```rust + * mod m1 { + * mod m2; + * use m2::foo; + * } + * ``` + * + * we first generate an edge `m1::m2 --name--> f::item`, where `item` is + * any item (named `name`) inside the imported source file `f`. Using this + * edge, `m2::foo` can resolve to `f::foo`, which results in the edge + * `m1::use m2 --foo--> f::foo`. Lastly, all edges out of `use` nodes are + * lifted to predecessors in the graph, so we get an edge `m1 --foo--> f::foo`. + * + * + * References: + * - https://doc.rust-lang.org/reference/items/modules.html + * - https://doc.rust-lang.org/reference/names/scopes.html + * - https://doc.rust-lang.org/reference/paths.html + * - https://doc.rust-lang.org/reference/visibility-and-privacy.html + */ +abstract class ItemNode extends AstNode { + /** Gets the (original) name of this item. */ + abstract string getName(); + + /** Gets the visibility of this item, if any. */ + abstract Visibility getVisibility(); + + /** Holds if this item is declared as `pub`. */ + bindingset[this] + pragma[inline_late] + predicate isPublic() { exists(this.getVisibility()) } + + /** Gets an element that has this item as immediately enlcosing item. */ + pragma[nomagic] + Element getADescendant() { + getImmediateParent(result) = this + or + exists(Element mid | + mid = this.getADescendant() and + getImmediateParent(result) = mid and + not mid instanceof ItemNode + ) + } + + /** Gets the immediately enclosing item of this item, if any. */ + pragma[nomagic] + ItemNode getImmediateParent() { this = result.getADescendant() } + + /** Gets the immediately enclosing module (or source file) of this item. */ + pragma[nomagic] + ModuleLikeNode getImmediateParentModule() { this = result.getAnItemInScope() } + + /** Gets a successor named `name` of this item, if any. */ + pragma[nomagic] + ItemNode getASuccessor(string name) { + sourceFileEdge(this, name, result) + or + this = result.getImmediateParent() and + name = result.getName() + or + fileImportEdge(this, name, result) + or + useImportEdge(this, name, result) + or + // items made available through `use` are available to nodes that contain the `use` + exists(UseItemNode use | + use = this.getASuccessor(_) and + result = use.getASuccessor(name) + ) + or + // items made available through macro calls are available to nodes that contain the macro call + exists(MacroCallItemNode call | + call = this.getASuccessor(_) and + result = call.getASuccessor(name) + ) + or + name = "super" and + if this instanceof Module + then result = this.getImmediateParentModule() + else result = this.getImmediateParentModule().getImmediateParentModule() + or + name = "self" and + if this instanceof Module then result = this else result = this.getImmediateParentModule() + or + name = "Self" and + this = result.(ImplOrTraitItemNode).getAnItemInSelfScope() + or + name = "crate" and + result.(SourceFileItemNode).getFile() = this.getFile() + } +} + +/** A module or a source file. */ +abstract private class ModuleLikeNode extends ItemNode { + /** Gets an item that may refer directly to items defined in this module. */ + pragma[nomagic] + ItemNode getAnItemInScope() { + result.getImmediateParent() = this + or + exists(ItemNode mid | + mid = this.getAnItemInScope() and + result.getImmediateParent() = mid and + not mid instanceof ModuleLikeNode + ) + } +} + +private class SourceFileItemNode extends ModuleLikeNode, SourceFile { + override string getName() { result = "(source file)" } + + override Visibility getVisibility() { none() } +} + +private class ConstItemNode extends ItemNode instanceof Const { + override string getName() { result = Const.super.getName().getText() } + + override Visibility getVisibility() { result = Const.super.getVisibility() } +} + +private class EnumItemNode extends ItemNode instanceof Enum { + override string getName() { result = Enum.super.getName().getText() } + + override Visibility getVisibility() { result = Enum.super.getVisibility() } +} + +private class VariantItemNode extends ItemNode instanceof Variant { + override string getName() { result = Variant.super.getName().getText() } + + override Visibility getVisibility() { result = Variant.super.getVisibility() } +} + +private class FunctionItemNode extends ItemNode instanceof Function { + override string getName() { result = Function.super.getName().getText() } + + override Visibility getVisibility() { result = Function.super.getVisibility() } +} + +abstract private class ImplOrTraitItemNode extends ItemNode { + /** Gets an item that may refer to this node using `Self`. */ + pragma[nomagic] + ItemNode getAnItemInSelfScope() { + result.getImmediateParent() = this + or + exists(ItemNode mid | + mid = this.getAnItemInSelfScope() and + result.getImmediateParent() = mid and + not mid instanceof ImplOrTraitItemNode + ) + } +} + +private class ImplItemNode extends ImplOrTraitItemNode instanceof Impl { + override string getName() { result = "(impl)" } + + override Visibility getVisibility() { none() } +} + +private class MacroCallItemNode extends ItemNode instanceof MacroCall { + override string getName() { result = "(macro call)" } + + override Visibility getVisibility() { none() } +} + +private class ModuleItemNode extends ModuleLikeNode instanceof Module { + override string getName() { result = Module.super.getName().getText() } + + override Visibility getVisibility() { result = Module.super.getVisibility() } +} + +private class StructItemNode extends ItemNode instanceof Struct { + override string getName() { result = Struct.super.getName().getText() } + + override Visibility getVisibility() { result = Struct.super.getVisibility() } +} + +private class TraitItemNode extends ImplOrTraitItemNode instanceof Trait { + override string getName() { result = Trait.super.getName().getText() } + + override Visibility getVisibility() { result = Trait.super.getVisibility() } +} + +private class UnionItemNode extends ItemNode instanceof Union { + override string getName() { result = Union.super.getName().getText() } + + override Visibility getVisibility() { result = Union.super.getVisibility() } +} + +private class UseItemNode extends ItemNode instanceof Use { + override string getName() { result = "(use)" } + + override Visibility getVisibility() { none() } +} + +private class BlockExprItemNode extends ItemNode instanceof BlockExpr { + override string getName() { result = "(block expr)" } + + override Visibility getVisibility() { none() } +} + +private predicate sourceFileEdge(SourceFile f, string name, ItemNode item) { + item = f.getAnItem() and + name = item.getName() +} + +/** Holds if `f` is available as `mod name;` inside `folder`. */ +private predicate fileModule(SourceFile f, string name, Folder folder) { + exists(File file | file = f.getFile() | + file.getBaseName() = name + ".rs" and + folder = file.getParentContainer() + or + exists(Folder encl | + file.getBaseName() = "mod.rs" and + encl = file.getParentContainer() and + name = encl.getBaseName() and + folder = encl.getParentContainer() + ) + ) +} + +/** + * Holds if `m` is a `mod name;` module declaration happening in a file named + * `fileName.rs`, inside the folder `parent`. + */ +private predicate modImport(Module m, string fileName, string name, Folder parent) { + exists(File f | + f = m.getFile() and + not m.hasItemList() and + // TODO: handle + // ``` + // #[path = "foo.rs"] + // mod bar; + // ``` + not m.getAnAttr().getMeta().getPath().getPart().getNameRef().getText() = "path" and + name = m.getName().getText() and + parent = f.getParentContainer() and + fileName = f.getStem() + ) +} + +/** Holds if `m` is a `mod name;` item importing file `f`. */ +private predicate fileImport(Module m, SourceFile f) { + exists(string fileName, string name, Folder parent | modImport(m, fileName, name, parent) | + // sibling import + fileModule(f, name, parent) + or + // child import + fileModule(f, name, parent.getFolder(fileName)) + ) +} + +/** + * Holds if `mod` is a `mod name;` item targeting a file resulting in `item` being + * in scope under the name `name`. + */ +private predicate fileImportEdge(Module mod, string name, ItemNode item) { + item.isPublic() and + exists(SourceFile f | + fileImport(mod, f) and + sourceFileEdge(f, name, item) + ) +} + +pragma[nomagic] +private predicate useTreeIsGlobImport(UseTree use) { + // TODO: the extractor should provide this information + use.getLocation() != use.getPath().getLocation() and + not use.hasUseTreeList() and + not use.hasRename() +} + +private predicate useTreeDeclares(UseTree tree, string name) { + not useTreeIsGlobImport(tree) and + not exists(tree.getUseTreeList()) and + ( + name = tree.getRename().getName().getText() and + name != "_" + or + not tree.hasRename() and + name = tree.getPath().getPart().getNameRef().getText() + ) + or + exists(UseTree mid | + useTreeDeclares(mid, name) and + mid = tree.getUseTreeList().getAUseTree() + ) +} + +/** + * Holds if `item` explicitly declares a sub item named `name`. This includes + * items declared by `use` statements, except for glob imports. + */ +pragma[nomagic] +private predicate declares(ItemNode item, string name) { + exists(ItemNode child | child.getImmediateParent() = item | + child.getName() = name + or + useTreeDeclares(child.(Use).getUseTree(), name) + ) + or + exists(MacroCallItemNode call | + declares(call, name) and + call.getImmediateParent() = item + ) +} + +private class RelevantPath extends Path { + RelevantPath() { not this = any(VariableAccess va).(PathExpr).getPath() } + + pragma[nomagic] + predicate isRoot(string name) { + not exists(this.getQualifier()) and + not this = any(UseTreeList list).getAUseTree().getPath() and + name = this.getPart().getNameRef().getText() + } +} + +/** + * Holds if the root path `root` references an item named `name`, and `name` + * may be looked up inside enclosing item `encl`. + */ +pragma[nomagic] +private predicate rootPathLookup(RelevantPath root, string name, ItemNode encl) { + exists(ItemNode encl0 | + // lookup in the immediately enclosing item + root.isRoot(name) and + encl0.getADescendant() = root + or + // lookup in an outer scope, but only if the item is not declared in inner scope + exists(ItemNode mid | + rootPathLookup(root, name, mid) and + not declares(mid, name) + | + // nested modules do not have unqualified access to items from outer modules, + // except for items declared at top-level in the source file + if mid instanceof Module + then encl0.(SourceFileItemNode) = mid.getImmediateParent+() + else encl0 = mid.getImmediateParent() + ) + | + // functions in `impl` blocks need to use explicit `Self::` to access other + // functions in the `impl` block + if encl0 instanceof ImplOrTraitItemNode then encl = encl0.getImmediateParent() else encl = encl0 + ) +} + +/** Gets the item that `path` resolves to, if any. */ +cached +ItemNode resolvePath(RelevantPath path) { + exists(ItemNode encl, string name | + rootPathLookup(path, name, encl) and + result = encl.getASuccessor(name) + ) + or + exists(ItemNode q, string name | + q = resolvePathQualifier(path, name) and + result = q.getASuccessor(name) + ) + or + result = resolveUseTreeListItem(_, _, path) +} + +pragma[nomagic] +private ItemNode resolvePathQualifier(RelevantPath path, string name) { + result = resolvePath(path.getQualifier()) and + name = path.getPart().getNameRef().getText() +} + +private predicate isUseTreeSubPath(UseTree tree, RelevantPath path) { + path = tree.getPath() + or + exists(RelevantPath mid | + isUseTreeSubPath(tree, mid) and + path = mid.getQualifier() + ) +} + +pragma[nomagic] +private predicate isUseTreeSubPathUnqualified(UseTree tree, RelevantPath path, string name) { + isUseTreeSubPath(tree, path) and + not exists(path.getQualifier()) and + name = path.getPart().getNameRef().getText() +} + +pragma[nomagic] +private ItemNode resolveUseTreeListItem(Use use, UseTree tree, RelevantPath path) { + exists(UseTree midTree, ItemNode mid, string name | + mid = resolveUseTreeListItem(use, midTree) and + tree = midTree.getUseTreeList().getAUseTree() and + isUseTreeSubPathUnqualified(tree, path, pragma[only_bind_into](name)) and + result = mid.getASuccessor(pragma[only_bind_into](name)) + ) + or + exists(ItemNode q, string name | + q = resolveUseTreeListItemQualifier(use, tree, path, name) and + result = q.getASuccessor(name) + ) +} + +pragma[nomagic] +private ItemNode resolveUseTreeListItemQualifier( + Use use, UseTree tree, RelevantPath path, string name +) { + result = resolveUseTreeListItem(use, tree, path.getQualifier()) and + name = path.getPart().getNameRef().getText() +} + +pragma[nomagic] +private ItemNode resolveUseTreeListItem(Use use, UseTree tree) { + tree = use.getUseTree() and + result = resolvePath(tree.getPath()) + or + result = resolveUseTreeListItem(use, tree, tree.getPath()) +} + +/** Holds if `use` imports `item` as `name`. */ +pragma[nomagic] +private predicate useImportEdge(Use use, string name, ItemNode item) { + exists(UseTree tree, ItemNode used | + used = resolveUseTreeListItem(use, tree) and + not exists(tree.getUseTreeList()) and + if useTreeIsGlobImport(tree) + then + exists(ItemNode encl | + encl.getADescendant() = use and + item = used.getASuccessor(name) and + // glob imports can be shadowed + not declares(encl, name) + ) + else item = used + | + not tree.hasRename() and + name = item.getName() + or + name = tree.getRename().getName().getText() and + name != "_" + ) +} diff --git a/rust/ql/test/library-tests/modules/CONSISTENCY/AstConsistency.expected b/rust/ql/test/library-tests/modules/CONSISTENCY/AstConsistency.expected new file mode 100644 index 000000000000..9d4e175192b4 --- /dev/null +++ b/rust/ql/test/library-tests/modules/CONSISTENCY/AstConsistency.expected @@ -0,0 +1,3 @@ +multiplePathResolutions +| main.rs:118:9:118:9 | f | main.rs:104:5:106:5 | fn f | +| main.rs:118:9:118:9 | f | main.rs:110:5:112:5 | fn f | diff --git a/rust/ql/test/library-tests/modules/main.rs b/rust/ql/test/library-tests/modules/main.rs new file mode 100644 index 000000000000..b372030ec1b5 --- /dev/null +++ b/rust/ql/test/library-tests/modules/main.rs @@ -0,0 +1,201 @@ +mod my; // I1 + +use my::*; // $ item=I1 + +use my::nested::nested1::nested2::*; // $ item=I3 + +mod my2; // I14 + +use my2::*; // $ item=I14 + +use my2::nested2::nested3::nested4::{f, g}; // $ item=I11 item=I12 item=I13 + +mod m1 { + fn f() { + println!("main.rs::m1::f"); + } // I16 + + pub mod m2 { + fn f() { + println!("main.rs::m1::m2::f"); + } // I18 + + pub fn g() { + println!("main.rs::m1::m2::g"); + f(); // $ item=I18 + super::f(); // $ item=I16 + } // I19 + + pub mod m3 { + use super::f; // $ item=I18 + pub fn h() { + println!("main.rs::m1::m2::m3::h"); + f(); // $ item=I18 + } // I21 + } // I20 + } // I17 +} // I15 + +mod m4 { + use super::m1::m2::g; // $ item=I19 + + pub fn i() { + println!("main.rs::m4::i"); + g(); // $ item=I19 + } // I23 +} // I22 + +struct Foo {} // I24 + +fn h() { + println!("main.rs::h"); + + struct Foo {} // I26 + + fn f() { + use m1::m2::g; // $ item=I19 + g(); // $ item=I19 + + struct Foo {} // I28 + println!("main.rs::h::f"); + let _ = Foo {}; // $ item=I28 + } // I27 + + let _ = Foo {}; // $ item=I26 + + f(); // $ item=I27 + + self::i(); // $ item=I29 +} // I25 + +fn i() { + println!("main.rs::i"); + + let _ = Foo {}; // $ item=I24 + + { + struct Foo { + x: i32, + } // I30 + + let _ = Foo { x: 0 }; // $ item=I30 + } +} // I29 + +use my2::nested2 as my2_nested2_alias; // $ item=I8 + +use my2_nested2_alias::nested3::{nested4::f as f_alias, nested4::g as g_alias, nested4::*}; // $ item=I10 item=I12 item=I13 item=I11 + +macro_rules! fn_in_macro { + ($e:expr) => { + fn f_defined_in_macro() { + $e + } + }; +} + +fn j() { + println!("main.rs::j"); + fn_in_macro!(println!("main.rs::j::f")); + f_defined_in_macro(); // $ item=f_defined_in_macro +} // I31 + +mod m5 { + pub fn f() { + println!("main.rs::m5::f"); + } // I33 +} // I32 + +mod m6 { + fn f() { + println!("main.rs::m6::f"); + } // I35 + + pub fn g() { + println!("main.rs::m6::g"); + // this import shadows the definition `I35`, which we don't currently handle + use super::m5::*; // $ item=I32 + f(); // $ item=I33 $ SPURIOUS: item=I35 + } // I36 +} // I34 + +mod m7 { + pub enum MyEnum { + A(i32), // I42 + B { x: i32 }, // I43 + C, // I44 + } // I41 + + #[rustfmt::skip] + pub fn f() -> MyEnum // $ item=I41 + { + println!("main.rs::m7::f"); + let _ = MyEnum::A(0); // $ item=I42 + let _ = MyEnum::B { x: 0 }; // $ item=I43 + MyEnum::C // $ item=I44 + } // I45 +} // I40 + +mod m8 { + trait MyTrait { + fn f(&self); // I48 + + fn g(&self) { + println!("main.rs::m8::MyTrait::g"); + f(); // $ item=I51 + Self::f(self); // $ item=I48 + } // I49 + } // I47 + + struct MyStruct {} // I50 + + fn f() { + println!("main.rs::m8::f"); + } // I51 + + #[rustfmt::skip] + impl MyTrait for MyStruct { // $ item=I47 item=I50 + fn f(&self) { + println!("main.rs::m8::::f"); + f(); // $ item=I51 + Self::g(self); // $ item=I54 + } // I53 + + fn g(&self) { + println!("main.rs::m8::::g"); + } // I54 + } // I52 + + #[rustfmt::skip] + pub fn g() { + let x = MyStruct {}; // $ item=I50 + MyTrait::f(&x); // $ item=I48 + // $ MISSING: item=52 + ::f(&x); // $ MISSING: item=I53 + let x = MyStruct {}; // $ item=I50 + x.f(); // $ MISSING: item=I53 + let x = MyStruct {}; // $ item=I50 + x.g(); // $ MISSING: item=I54 + } // I55 +} // I46 + +fn main() { + my::nested::nested1::nested2::f(); // $ item=I4 + my::f(); // $ item=I38 + nested2::nested3::nested4::f(); // $ item=I12 + f(); // $ item=I12 + g(); // $ item=I13 + crate::h(); // $ item=I25 + m1::m2::g(); // $ item=I19 + m1::m2::m3::h(); // $ item=I21 + m4::i(); // $ item=I23 + h(); // $ item=I25 + f_alias(); // $ item=I12 + g_alias(); // $ item=I13 + j(); // $ item=I31 + m6::g(); // $ item=I36 + m7::f(); // $ item=I45 + m8::g(); // $ item=I55 +} diff --git a/rust/ql/test/library-tests/modules/modules.expected b/rust/ql/test/library-tests/modules/modules.expected new file mode 100644 index 000000000000..26f76962c702 --- /dev/null +++ b/rust/ql/test/library-tests/modules/modules.expected @@ -0,0 +1,137 @@ +testFailures +mod +| lib.rs:1:1:1:7 | mod my | +| main.rs:1:1:1:7 | mod my | +| main.rs:7:1:7:8 | mod my2 | +| main.rs:13:1:37:1 | mod m1 | +| main.rs:18:5:36:5 | mod m2 | +| main.rs:29:9:35:9 | mod m3 | +| main.rs:39:1:46:1 | mod m4 | +| main.rs:103:1:107:1 | mod m5 | +| main.rs:109:1:120:1 | mod m6 | +| main.rs:122:1:137:1 | mod m7 | +| main.rs:139:1:182:1 | mod m8 | +| my2/mod.rs:1:1:1:16 | mod nested2 | +| my2/nested2.rs:1:1:11:1 | mod nested3 | +| my2/nested2.rs:2:5:10:5 | mod nested4 | +| my.rs:1:1:1:15 | mod nested | +| my/nested.rs:1:1:17:1 | mod nested1 | +| my/nested.rs:2:5:11:5 | mod nested2 | +resolvePath +| main.rs:3:5:3:6 | my | main.rs:1:1:1:7 | mod my | +| main.rs:5:5:5:6 | my | main.rs:1:1:1:7 | mod my | +| main.rs:5:5:5:14 | ...::nested | my.rs:1:1:1:15 | mod nested | +| main.rs:5:5:5:23 | ...::nested1 | my/nested.rs:1:1:17:1 | mod nested1 | +| main.rs:5:5:5:32 | ...::nested2 | my/nested.rs:2:5:11:5 | mod nested2 | +| main.rs:9:5:9:7 | my2 | main.rs:7:1:7:8 | mod my2 | +| main.rs:11:5:11:7 | my2 | main.rs:7:1:7:8 | mod my2 | +| main.rs:11:5:11:16 | ...::nested2 | my2/mod.rs:1:1:1:16 | mod nested2 | +| main.rs:11:5:11:25 | ...::nested3 | my2/nested2.rs:1:1:11:1 | mod nested3 | +| main.rs:11:5:11:34 | ...::nested4 | my2/nested2.rs:2:5:10:5 | mod nested4 | +| main.rs:11:38:11:38 | f | my2/nested2.rs:3:9:5:9 | fn f | +| main.rs:11:41:11:41 | g | my2/nested2.rs:7:9:9:9 | fn g | +| main.rs:25:13:25:13 | f | main.rs:19:9:21:9 | fn f | +| main.rs:26:13:26:17 | super | main.rs:13:1:37:1 | mod m1 | +| main.rs:26:13:26:20 | ...::f | main.rs:14:5:16:5 | fn f | +| main.rs:30:17:30:21 | super | main.rs:18:5:36:5 | mod m2 | +| main.rs:30:17:30:24 | ...::f | main.rs:19:9:21:9 | fn f | +| main.rs:33:17:33:17 | f | main.rs:19:9:21:9 | fn f | +| main.rs:40:9:40:13 | super | main.rs:1:1:201:2 | SourceFile | +| main.rs:40:9:40:17 | ...::m1 | main.rs:13:1:37:1 | mod m1 | +| main.rs:40:9:40:21 | ...::m2 | main.rs:18:5:36:5 | mod m2 | +| main.rs:40:9:40:24 | ...::g | main.rs:23:9:27:9 | fn g | +| main.rs:44:9:44:9 | g | main.rs:23:9:27:9 | fn g | +| main.rs:56:13:56:14 | m1 | main.rs:13:1:37:1 | mod m1 | +| main.rs:56:13:56:18 | ...::m2 | main.rs:18:5:36:5 | mod m2 | +| main.rs:56:13:56:21 | ...::g | main.rs:23:9:27:9 | fn g | +| main.rs:57:9:57:9 | g | main.rs:23:9:27:9 | fn g | +| main.rs:61:17:61:19 | Foo | main.rs:59:9:59:21 | struct Foo | +| main.rs:64:13:64:15 | Foo | main.rs:53:5:53:17 | struct Foo | +| main.rs:66:5:66:5 | f | main.rs:55:5:62:5 | fn f | +| main.rs:68:5:68:8 | self | main.rs:1:1:201:2 | SourceFile | +| main.rs:68:5:68:11 | ...::i | main.rs:71:1:83:1 | fn i | +| main.rs:74:13:74:15 | Foo | main.rs:48:1:48:13 | struct Foo | +| main.rs:81:17:81:19 | Foo | main.rs:77:9:79:9 | struct Foo | +| main.rs:85:5:85:7 | my2 | main.rs:7:1:7:8 | mod my2 | +| main.rs:85:5:85:16 | ...::nested2 | my2/mod.rs:1:1:1:16 | mod nested2 | +| main.rs:87:5:87:21 | my2_nested2_alias | my2/mod.rs:1:1:1:16 | mod nested2 | +| main.rs:87:5:87:30 | ...::nested3 | my2/nested2.rs:1:1:11:1 | mod nested3 | +| main.rs:87:34:87:40 | nested4 | my2/nested2.rs:2:5:10:5 | mod nested4 | +| main.rs:87:34:87:43 | ...::f | my2/nested2.rs:3:9:5:9 | fn f | +| main.rs:87:57:87:63 | nested4 | my2/nested2.rs:2:5:10:5 | mod nested4 | +| main.rs:87:57:87:66 | ...::g | my2/nested2.rs:7:9:9:9 | fn g | +| main.rs:87:80:87:86 | nested4 | my2/nested2.rs:2:5:10:5 | mod nested4 | +| main.rs:100:5:100:22 | f_defined_in_macro | main.rs:99:18:99:42 | fn f_defined_in_macro | +| main.rs:117:13:117:17 | super | main.rs:1:1:201:2 | SourceFile | +| main.rs:117:13:117:21 | ...::m5 | main.rs:103:1:107:1 | mod m5 | +| main.rs:118:9:118:9 | f | main.rs:104:5:106:5 | fn f | +| main.rs:118:9:118:9 | f | main.rs:110:5:112:5 | fn f | +| main.rs:130:19:130:24 | MyEnum | main.rs:123:5:127:5 | enum MyEnum | +| main.rs:133:17:133:22 | MyEnum | main.rs:123:5:127:5 | enum MyEnum | +| main.rs:133:17:133:25 | ...::A | main.rs:124:9:124:14 | A | +| main.rs:134:17:134:22 | MyEnum | main.rs:123:5:127:5 | enum MyEnum | +| main.rs:134:17:134:25 | ...::B | main.rs:124:23:125:20 | B | +| main.rs:135:9:135:14 | MyEnum | main.rs:123:5:127:5 | enum MyEnum | +| main.rs:135:9:135:17 | ...::C | main.rs:125:23:126:9 | C | +| main.rs:145:13:145:13 | f | main.rs:152:5:154:5 | fn f | +| main.rs:146:13:146:16 | Self | main.rs:140:5:148:5 | trait MyTrait | +| main.rs:146:13:146:19 | ...::f | main.rs:141:9:141:20 | fn f | +| main.rs:157:10:157:16 | MyTrait | main.rs:140:5:148:5 | trait MyTrait | +| main.rs:157:22:157:29 | MyStruct | main.rs:150:5:150:22 | struct MyStruct | +| main.rs:160:13:160:13 | f | main.rs:152:5:154:5 | fn f | +| main.rs:161:13:161:16 | Self | main.rs:156:5:167:5 | impl MyTrait for MyStruct { ... } | +| main.rs:161:13:161:19 | ...::g | main.rs:164:9:166:9 | fn g | +| main.rs:171:17:171:24 | MyStruct | main.rs:150:5:150:22 | struct MyStruct | +| main.rs:172:9:172:15 | MyTrait | main.rs:140:5:148:5 | trait MyTrait | +| main.rs:172:9:172:18 | ...::f | main.rs:141:9:141:20 | fn f | +| main.rs:173:10:173:17 | MyStruct | main.rs:150:5:150:22 | struct MyStruct | +| main.rs:173:10:173:17 | MyStruct | main.rs:150:5:150:22 | struct MyStruct | +| main.rs:177:17:177:24 | MyStruct | main.rs:150:5:150:22 | struct MyStruct | +| main.rs:179:17:179:24 | MyStruct | main.rs:150:5:150:22 | struct MyStruct | +| main.rs:185:5:185:6 | my | main.rs:1:1:1:7 | mod my | +| main.rs:185:5:185:14 | ...::nested | my.rs:1:1:1:15 | mod nested | +| main.rs:185:5:185:23 | ...::nested1 | my/nested.rs:1:1:17:1 | mod nested1 | +| main.rs:185:5:185:32 | ...::nested2 | my/nested.rs:2:5:11:5 | mod nested2 | +| main.rs:185:5:185:35 | ...::f | my/nested.rs:3:9:5:9 | fn f | +| main.rs:186:5:186:6 | my | main.rs:1:1:1:7 | mod my | +| main.rs:186:5:186:9 | ...::f | my.rs:5:1:7:1 | fn f | +| main.rs:187:5:187:11 | nested2 | my2/mod.rs:1:1:1:16 | mod nested2 | +| main.rs:187:5:187:20 | ...::nested3 | my2/nested2.rs:1:1:11:1 | mod nested3 | +| main.rs:187:5:187:29 | ...::nested4 | my2/nested2.rs:2:5:10:5 | mod nested4 | +| main.rs:187:5:187:32 | ...::f | my2/nested2.rs:3:9:5:9 | fn f | +| main.rs:188:5:188:5 | f | my2/nested2.rs:3:9:5:9 | fn f | +| main.rs:189:5:189:5 | g | my2/nested2.rs:7:9:9:9 | fn g | +| main.rs:190:5:190:9 | crate | main.rs:1:1:201:2 | SourceFile | +| main.rs:190:5:190:12 | ...::h | main.rs:50:1:69:1 | fn h | +| main.rs:191:5:191:6 | m1 | main.rs:13:1:37:1 | mod m1 | +| main.rs:191:5:191:10 | ...::m2 | main.rs:18:5:36:5 | mod m2 | +| main.rs:191:5:191:13 | ...::g | main.rs:23:9:27:9 | fn g | +| main.rs:192:5:192:6 | m1 | main.rs:13:1:37:1 | mod m1 | +| main.rs:192:5:192:10 | ...::m2 | main.rs:18:5:36:5 | mod m2 | +| main.rs:192:5:192:14 | ...::m3 | main.rs:29:9:35:9 | mod m3 | +| main.rs:192:5:192:17 | ...::h | main.rs:30:27:34:13 | fn h | +| main.rs:193:5:193:6 | m4 | main.rs:39:1:46:1 | mod m4 | +| main.rs:193:5:193:9 | ...::i | main.rs:42:5:45:5 | fn i | +| main.rs:194:5:194:5 | h | main.rs:50:1:69:1 | fn h | +| main.rs:195:5:195:11 | f_alias | my2/nested2.rs:3:9:5:9 | fn f | +| main.rs:196:5:196:11 | g_alias | my2/nested2.rs:7:9:9:9 | fn g | +| main.rs:197:5:197:5 | j | main.rs:97:1:101:1 | fn j | +| main.rs:198:5:198:6 | m6 | main.rs:109:1:120:1 | mod m6 | +| main.rs:198:5:198:9 | ...::g | main.rs:114:5:119:5 | fn g | +| main.rs:199:5:199:6 | m7 | main.rs:122:1:137:1 | mod m7 | +| main.rs:199:5:199:9 | ...::f | main.rs:129:5:136:5 | fn f | +| main.rs:200:5:200:6 | m8 | main.rs:139:1:182:1 | mod m8 | +| main.rs:200:5:200:9 | ...::g | main.rs:169:5:181:5 | fn g | +| my2/mod.rs:5:5:5:11 | nested2 | my2/mod.rs:1:1:1:16 | mod nested2 | +| my2/mod.rs:5:5:5:20 | ...::nested3 | my2/nested2.rs:1:1:11:1 | mod nested3 | +| my2/mod.rs:5:5:5:29 | ...::nested4 | my2/nested2.rs:2:5:10:5 | mod nested4 | +| my2/mod.rs:5:5:5:32 | ...::f | my2/nested2.rs:3:9:5:9 | fn f | +| my.rs:3:5:3:10 | nested | my.rs:1:1:1:15 | mod nested | +| my.rs:3:5:3:13 | ...::g | my/nested.rs:19:1:22:1 | fn g | +| my.rs:11:5:11:5 | g | my/nested.rs:19:1:22:1 | fn g | +| my/nested.rs:9:13:9:13 | f | my/nested.rs:3:9:5:9 | fn f | +| my/nested.rs:15:9:15:15 | nested2 | my/nested.rs:2:5:11:5 | mod nested2 | +| my/nested.rs:15:9:15:18 | ...::f | my/nested.rs:3:9:5:9 | fn f | +| my/nested.rs:21:5:21:11 | nested1 | my/nested.rs:1:1:17:1 | mod nested1 | +| my/nested.rs:21:5:21:20 | ...::nested2 | my/nested.rs:2:5:11:5 | mod nested2 | +| my/nested.rs:21:5:21:23 | ...::f | my/nested.rs:3:9:5:9 | fn f | diff --git a/rust/ql/test/library-tests/modules/modules.ql b/rust/ql/test/library-tests/modules/modules.ql new file mode 100644 index 000000000000..df3828a4e123 --- /dev/null +++ b/rust/ql/test/library-tests/modules/modules.ql @@ -0,0 +1,49 @@ +import rust +import codeql.rust.elements.internal.PathResolution +import utils.test.InlineExpectationsTest + +query predicate mod(Module m) { any() } + +query predicate resolvePath(Path p, ItemNode i) { i = resolvePath(p) } + +module ResolveTest implements TestSig { + string getARelevantTag() { result = "item" } + + private predicate itemAt(ItemNode i, string filepath, int line, boolean inMacro) { + i.getLocation().hasLocationInfo(filepath, _, _, line, _) and + if i.isInMacroExpansion() then inMacro = true else inMacro = false + } + + private predicate commmentAt(string text, string filepath, int line) { + exists(Comment c | + c.getLocation().hasLocationInfo(filepath, line, _, _, _) and + c.getCommentText() = text + ) + } + + private predicate item(ItemNode i, string value) { + exists(string filepath, int line, boolean inMacro | itemAt(i, filepath, line, inMacro) | + commmentAt(value, filepath, line) and + inMacro = false + or + ( + not commmentAt(_, filepath, line) + or + inMacro = true + ) and + value = i.getName() + ) + } + + predicate hasActualResult(Location location, string element, string tag, string value) { + exists(Path p | + not p = any(Path parent).getQualifier() and + location = p.getLocation() and + element = p.toString() and + item(resolvePath(p), value) and + tag = "item" + ) + } +} + +import MakeTest diff --git a/rust/ql/test/library-tests/modules/my.rs b/rust/ql/test/library-tests/modules/my.rs new file mode 100644 index 000000000000..fd9511a21181 --- /dev/null +++ b/rust/ql/test/library-tests/modules/my.rs @@ -0,0 +1,12 @@ +pub mod nested; // I37 + +use nested::g; // $ item=I7 + +pub fn f() { + println!("my.rs::f"); +} // I38 + +pub fn h() { + println!("my.rs::h"); + g(); // $ item=I7 +} // I39 diff --git a/rust/ql/test/library-tests/modules/my/nested.rs b/rust/ql/test/library-tests/modules/my/nested.rs new file mode 100644 index 000000000000..639ed241ae3e --- /dev/null +++ b/rust/ql/test/library-tests/modules/my/nested.rs @@ -0,0 +1,22 @@ +pub mod nested1 { + pub mod nested2 { + pub fn f() { + println!("nested.rs:nested1::nested2::f"); + } // I4 + + fn g() { + println!("nested.rs:nested1::nested2::g"); + f(); // $ item=I4 + } // I5 + } // I3 + + fn g() { + println!("nested.rs:nested1::g"); + nested2::f(); // $ item=I4 + } // I6 +} // I1 + +pub fn g() { + println!("nested.rs::g"); + nested1::nested2::f(); // $ item=I4 +} // I7 diff --git a/rust/ql/test/library-tests/modules/my2/mod.rs b/rust/ql/test/library-tests/modules/my2/mod.rs new file mode 100644 index 000000000000..fcd82bf02c88 --- /dev/null +++ b/rust/ql/test/library-tests/modules/my2/mod.rs @@ -0,0 +1,6 @@ +pub mod nested2; // I8 + +fn g() { + println!("mod.rs::g"); + nested2::nested3::nested4::f(); // $ item=I12 +} // I9 diff --git a/rust/ql/test/library-tests/modules/my2/nested2.rs b/rust/ql/test/library-tests/modules/my2/nested2.rs new file mode 100644 index 000000000000..dec8b8a2377e --- /dev/null +++ b/rust/ql/test/library-tests/modules/my2/nested2.rs @@ -0,0 +1,11 @@ +pub mod nested3 { + pub mod nested4 { + pub fn f() { + println!("nested2.rs::nested3::nested4::f"); + } // I12 + + pub fn g() { + println!("nested2.rs::nested3::nested4::g"); + } // I13 + } // I11 +} // I10 diff --git a/rust/ql/test/query-tests/diagnostics/AstConsistencyCounts.expected b/rust/ql/test/query-tests/diagnostics/AstConsistencyCounts.expected index 7f8d388fdc50..1a79d92303dc 100644 --- a/rust/ql/test/query-tests/diagnostics/AstConsistencyCounts.expected +++ b/rust/ql/test/query-tests/diagnostics/AstConsistencyCounts.expected @@ -1,6 +1,7 @@ | Multiple children | 0 | | Multiple locations | 0 | | Multiple parents | 0 | +| Multiple path resolutions | 0 | | Multiple positions | 0 | | Multiple primary QL classes | 0 | | Multiple toStrings | 0 | diff --git a/rust/ql/test/query-tests/security/CWE-696/BadCTorInitialization.expected b/rust/ql/test/query-tests/security/CWE-696/BadCTorInitialization.expected index 311e1828f535..9d8fc2524718 100644 --- a/rust/ql/test/query-tests/security/CWE-696/BadCTorInitialization.expected +++ b/rust/ql/test/query-tests/security/CWE-696/BadCTorInitialization.expected @@ -1,44 +1,49 @@ #select -| test.rs:31:9:31:25 | ...::stdout(...) | test.rs:29:1:29:13 | Attr | test.rs:31:9:31:25 | ...::stdout(...) | Call to ...::stdout(...) in a function with the ctor attribute. | -| test.rs:36:9:36:25 | ...::stdout(...) | test.rs:34:1:34:13 | Attr | test.rs:36:9:36:25 | ...::stdout(...) | Call to ...::stdout(...) in a function with the dtor attribute. | -| test.rs:43:9:43:25 | ...::stdout(...) | test.rs:40:1:40:13 | Attr | test.rs:43:9:43:25 | ...::stdout(...) | Call to ...::stdout(...) in a function with the dtor attribute. | -| test.rs:53:9:53:16 | stdout(...) | test.rs:51:1:51:7 | Attr | test.rs:53:9:53:16 | stdout(...) | Call to stdout(...) in a function with the ctor attribute. | -| test.rs:58:9:58:16 | stderr(...) | test.rs:56:1:56:7 | Attr | test.rs:58:9:58:16 | stderr(...) | Call to stderr(...) in a function with the ctor attribute. | -| test.rs:63:14:63:28 | ...::_print(...) | test.rs:61:1:61:7 | Attr | test.rs:63:14:63:28 | ...::_print(...) | Call to ...::_print(...) in a function with the ctor attribute. | -| test.rs:69:9:69:24 | ...::stdin(...) | test.rs:66:1:66:7 | Attr | test.rs:69:9:69:24 | ...::stdin(...) | Call to ...::stdin(...) in a function with the ctor attribute. | -| test.rs:90:5:90:35 | ...::sleep(...) | test.rs:88:1:88:7 | Attr | test.rs:90:5:90:35 | ...::sleep(...) | Call to ...::sleep(...) in a function with the ctor attribute. | -| test.rs:97:5:97:23 | ...::exit(...) | test.rs:95:1:95:7 | Attr | test.rs:97:5:97:23 | ...::exit(...) | Call to ...::exit(...) in a function with the ctor attribute. | -| test.rs:126:9:126:16 | stderr(...) | test.rs:129:1:129:7 | Attr | test.rs:126:9:126:16 | stderr(...) | Call to stderr(...) in a function with the ctor attribute. | -| test.rs:126:9:126:16 | stderr(...) | test.rs:145:1:145:7 | Attr | test.rs:126:9:126:16 | stderr(...) | Call to stderr(...) in a function with the ctor attribute. | -| test.rs:126:9:126:44 | ... .write_all(...) | test.rs:129:1:129:7 | Attr | test.rs:126:9:126:44 | ... .write_all(...) | Call to ... .write_all(...) in a function with the ctor attribute. | -| test.rs:126:9:126:44 | ... .write_all(...) | test.rs:145:1:145:7 | Attr | test.rs:126:9:126:44 | ... .write_all(...) | Call to ... .write_all(...) in a function with the ctor attribute. | -| test.rs:171:5:171:15 | ...::stdout(...) | test.rs:169:1:169:7 | Attr | test.rs:171:5:171:15 | ...::stdout(...) | Call to ...::stdout(...) in a function with the ctor attribute. | +| test.rs:30:9:30:25 | ...::stdout(...) | test.rs:28:1:28:13 | Attr | test.rs:30:9:30:25 | ...::stdout(...) | Call to ...::stdout(...) in a function with the ctor attribute. | +| test.rs:35:9:35:25 | ...::stdout(...) | test.rs:33:1:33:13 | Attr | test.rs:35:9:35:25 | ...::stdout(...) | Call to ...::stdout(...) in a function with the dtor attribute. | +| test.rs:42:9:42:25 | ...::stdout(...) | test.rs:39:1:39:13 | Attr | test.rs:42:9:42:25 | ...::stdout(...) | Call to ...::stdout(...) in a function with the dtor attribute. | +| test.rs:52:9:52:16 | stdout(...) | test.rs:50:1:50:7 | Attr | test.rs:52:9:52:16 | stdout(...) | Call to stdout(...) in a function with the ctor attribute. | +| test.rs:57:9:57:16 | stderr(...) | test.rs:55:1:55:7 | Attr | test.rs:57:9:57:16 | stderr(...) | Call to stderr(...) in a function with the ctor attribute. | +| test.rs:62:14:62:28 | ...::_print(...) | test.rs:60:1:60:7 | Attr | test.rs:62:14:62:28 | ...::_print(...) | Call to ...::_print(...) in a function with the ctor attribute. | +| test.rs:68:9:68:24 | ...::stdin(...) | test.rs:65:1:65:7 | Attr | test.rs:68:9:68:24 | ...::stdin(...) | Call to ...::stdin(...) in a function with the ctor attribute. | +| test.rs:89:5:89:35 | ...::sleep(...) | test.rs:87:1:87:7 | Attr | test.rs:89:5:89:35 | ...::sleep(...) | Call to ...::sleep(...) in a function with the ctor attribute. | +| test.rs:96:5:96:23 | ...::exit(...) | test.rs:94:1:94:7 | Attr | test.rs:96:5:96:23 | ...::exit(...) | Call to ...::exit(...) in a function with the ctor attribute. | +| test.rs:125:9:125:16 | stderr(...) | test.rs:128:1:128:7 | Attr | test.rs:125:9:125:16 | stderr(...) | Call to stderr(...) in a function with the ctor attribute. | +| test.rs:125:9:125:16 | stderr(...) | test.rs:144:1:144:7 | Attr | test.rs:125:9:125:16 | stderr(...) | Call to stderr(...) in a function with the ctor attribute. | +| test.rs:125:9:125:16 | stderr(...) | test.rs:150:1:150:7 | Attr | test.rs:125:9:125:16 | stderr(...) | Call to stderr(...) in a function with the ctor attribute. | +| test.rs:125:9:125:44 | ... .write_all(...) | test.rs:128:1:128:7 | Attr | test.rs:125:9:125:44 | ... .write_all(...) | Call to ... .write_all(...) in a function with the ctor attribute. | +| test.rs:125:9:125:44 | ... .write_all(...) | test.rs:144:1:144:7 | Attr | test.rs:125:9:125:44 | ... .write_all(...) | Call to ... .write_all(...) in a function with the ctor attribute. | +| test.rs:125:9:125:44 | ... .write_all(...) | test.rs:150:1:150:7 | Attr | test.rs:125:9:125:44 | ... .write_all(...) | Call to ... .write_all(...) in a function with the ctor attribute. | +| test.rs:170:5:170:15 | ...::stdout(...) | test.rs:168:1:168:7 | Attr | test.rs:170:5:170:15 | ...::stdout(...) | Call to ...::stdout(...) in a function with the ctor attribute. | edges -| test.rs:29:1:29:13 | Attr | test.rs:29:1:32:1 | fn bad1_1 | -| test.rs:29:1:32:1 | fn bad1_1 | test.rs:31:9:31:25 | ...::stdout(...) | -| test.rs:34:1:34:13 | Attr | test.rs:34:1:37:1 | fn bad1_2 | -| test.rs:34:1:37:1 | fn bad1_2 | test.rs:36:9:36:25 | ...::stdout(...) | -| test.rs:39:1:44:1 | fn bad1_3 | test.rs:43:9:43:25 | ...::stdout(...) | -| test.rs:40:1:40:13 | Attr | test.rs:39:1:44:1 | fn bad1_3 | -| test.rs:51:1:51:7 | Attr | test.rs:51:1:54:1 | fn bad2_1 | -| test.rs:51:1:54:1 | fn bad2_1 | test.rs:53:9:53:16 | stdout(...) | -| test.rs:56:1:56:7 | Attr | test.rs:56:1:59:1 | fn bad2_2 | -| test.rs:56:1:59:1 | fn bad2_2 | test.rs:58:9:58:16 | stderr(...) | -| test.rs:61:1:61:7 | Attr | test.rs:61:1:64:1 | fn bad2_3 | -| test.rs:61:1:64:1 | fn bad2_3 | test.rs:63:14:63:28 | ...::_print(...) | -| test.rs:66:1:66:7 | Attr | test.rs:66:1:70:1 | fn bad2_4 | -| test.rs:66:1:70:1 | fn bad2_4 | test.rs:69:9:69:24 | ...::stdin(...) | -| test.rs:88:1:88:7 | Attr | test.rs:88:1:91:1 | fn bad2_7 | -| test.rs:88:1:91:1 | fn bad2_7 | test.rs:90:5:90:35 | ...::sleep(...) | -| test.rs:95:1:95:7 | Attr | test.rs:95:1:98:1 | fn bad2_8 | -| test.rs:95:1:98:1 | fn bad2_8 | test.rs:97:5:97:23 | ...::exit(...) | -| test.rs:125:1:127:1 | fn call_target3_1 | test.rs:126:9:126:16 | stderr(...) | -| test.rs:125:1:127:1 | fn call_target3_1 | test.rs:126:9:126:44 | ... .write_all(...) | -| test.rs:129:1:129:7 | Attr | test.rs:129:1:132:1 | fn bad3_1 | -| test.rs:129:1:132:1 | fn bad3_1 | test.rs:131:5:131:20 | call_target3_1(...) | -| test.rs:131:5:131:20 | call_target3_1(...) | test.rs:125:1:127:1 | fn call_target3_1 | -| test.rs:145:1:145:7 | Attr | test.rs:145:1:149:1 | fn bad3_3 | -| test.rs:145:1:149:1 | fn bad3_3 | test.rs:147:5:147:20 | call_target3_1(...) | -| test.rs:147:5:147:20 | call_target3_1(...) | test.rs:125:1:127:1 | fn call_target3_1 | -| test.rs:169:1:169:7 | Attr | test.rs:169:1:172:1 | fn bad4_1 | -| test.rs:169:1:172:1 | fn bad4_1 | test.rs:171:5:171:15 | ...::stdout(...) | +| test.rs:28:1:28:13 | Attr | test.rs:28:1:31:1 | fn bad1_1 | +| test.rs:28:1:31:1 | fn bad1_1 | test.rs:30:9:30:25 | ...::stdout(...) | +| test.rs:33:1:33:13 | Attr | test.rs:33:1:36:1 | fn bad1_2 | +| test.rs:33:1:36:1 | fn bad1_2 | test.rs:35:9:35:25 | ...::stdout(...) | +| test.rs:38:1:43:1 | fn bad1_3 | test.rs:42:9:42:25 | ...::stdout(...) | +| test.rs:39:1:39:13 | Attr | test.rs:38:1:43:1 | fn bad1_3 | +| test.rs:50:1:50:7 | Attr | test.rs:50:1:53:1 | fn bad2_1 | +| test.rs:50:1:53:1 | fn bad2_1 | test.rs:52:9:52:16 | stdout(...) | +| test.rs:55:1:55:7 | Attr | test.rs:55:1:58:1 | fn bad2_2 | +| test.rs:55:1:58:1 | fn bad2_2 | test.rs:57:9:57:16 | stderr(...) | +| test.rs:60:1:60:7 | Attr | test.rs:60:1:63:1 | fn bad2_3 | +| test.rs:60:1:63:1 | fn bad2_3 | test.rs:62:14:62:28 | ...::_print(...) | +| test.rs:65:1:65:7 | Attr | test.rs:65:1:69:1 | fn bad2_4 | +| test.rs:65:1:69:1 | fn bad2_4 | test.rs:68:9:68:24 | ...::stdin(...) | +| test.rs:87:1:87:7 | Attr | test.rs:87:1:90:1 | fn bad2_7 | +| test.rs:87:1:90:1 | fn bad2_7 | test.rs:89:5:89:35 | ...::sleep(...) | +| test.rs:94:1:94:7 | Attr | test.rs:94:1:97:1 | fn bad2_8 | +| test.rs:94:1:97:1 | fn bad2_8 | test.rs:96:5:96:23 | ...::exit(...) | +| test.rs:124:1:126:1 | fn call_target3_1 | test.rs:125:9:125:16 | stderr(...) | +| test.rs:124:1:126:1 | fn call_target3_1 | test.rs:125:9:125:44 | ... .write_all(...) | +| test.rs:128:1:128:7 | Attr | test.rs:128:1:131:1 | fn bad3_1 | +| test.rs:128:1:131:1 | fn bad3_1 | test.rs:130:5:130:20 | call_target3_1(...) | +| test.rs:130:5:130:20 | call_target3_1(...) | test.rs:124:1:126:1 | fn call_target3_1 | +| test.rs:144:1:144:7 | Attr | test.rs:144:1:148:1 | fn bad3_3 | +| test.rs:144:1:148:1 | fn bad3_3 | test.rs:146:5:146:20 | call_target3_1(...) | +| test.rs:146:5:146:20 | call_target3_1(...) | test.rs:124:1:126:1 | fn call_target3_1 | +| test.rs:150:1:150:7 | Attr | test.rs:150:1:153:1 | fn bad3_4 | +| test.rs:150:1:153:1 | fn bad3_4 | test.rs:152:5:152:12 | bad3_3(...) | +| test.rs:152:5:152:12 | bad3_3(...) | test.rs:144:1:148:1 | fn bad3_3 | +| test.rs:168:1:168:7 | Attr | test.rs:168:1:171:1 | fn bad4_1 | +| test.rs:168:1:171:1 | fn bad4_1 | test.rs:170:5:170:15 | ...::stdout(...) | diff --git a/rust/ql/test/query-tests/security/CWE-696/test.rs b/rust/ql/test/query-tests/security/CWE-696/test.rs index 5cd7f451f2cb..b23c06aa6a69 100644 --- a/rust/ql/test/query-tests/security/CWE-696/test.rs +++ b/rust/ql/test/query-tests/security/CWE-696/test.rs @@ -1,4 +1,3 @@ - // --- attribute variants --- use std::io::Write; @@ -123,7 +122,7 @@ unsafe fn harmless2_11() { // --- transitive cases --- fn call_target3_1() { - _ = stderr().write_all(b"Hello, world!"); // $ Alert=source3_1 Alert=source3_3 MISSING: Alert=source3_4 + _ = stderr().write_all(b"Hello, world!"); // $ Alert=source3_1 Alert=source3_3 Alert=source3_4 } #[ctor] // $ Source=source3_1 @@ -148,7 +147,7 @@ fn bad3_3() { call_target3_2(); } -#[ctor] // $ MISSING: Source=source3_4 +#[ctor] // $ Source=source3_4 fn bad3_4() { bad3_3(); } From 1f6d39f520aca04a837f75df123ed9043520641f Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Thu, 30 Jan 2025 11:46:18 +0100 Subject: [PATCH 2/5] Rust: Rename `modules` test to `path-resolution` --- .../CONSISTENCY/AstConsistency.expected | 0 rust/ql/test/library-tests/{modules => path-resolution}/main.rs | 0 rust/ql/test/library-tests/{modules => path-resolution}/my.rs | 0 .../test/library-tests/{modules => path-resolution}/my/nested.rs | 0 .../ql/test/library-tests/{modules => path-resolution}/my2/mod.rs | 0 .../library-tests/{modules => path-resolution}/my2/nested2.rs | 0 .../modules.expected => path-resolution/path-resolution.expected} | 0 .../{modules/modules.ql => path-resolution/path-resolution.ql} | 0 8 files changed, 0 insertions(+), 0 deletions(-) rename rust/ql/test/library-tests/{modules => path-resolution}/CONSISTENCY/AstConsistency.expected (100%) rename rust/ql/test/library-tests/{modules => path-resolution}/main.rs (100%) rename rust/ql/test/library-tests/{modules => path-resolution}/my.rs (100%) rename rust/ql/test/library-tests/{modules => path-resolution}/my/nested.rs (100%) rename rust/ql/test/library-tests/{modules => path-resolution}/my2/mod.rs (100%) rename rust/ql/test/library-tests/{modules => path-resolution}/my2/nested2.rs (100%) rename rust/ql/test/library-tests/{modules/modules.expected => path-resolution/path-resolution.expected} (100%) rename rust/ql/test/library-tests/{modules/modules.ql => path-resolution/path-resolution.ql} (100%) diff --git a/rust/ql/test/library-tests/modules/CONSISTENCY/AstConsistency.expected b/rust/ql/test/library-tests/path-resolution/CONSISTENCY/AstConsistency.expected similarity index 100% rename from rust/ql/test/library-tests/modules/CONSISTENCY/AstConsistency.expected rename to rust/ql/test/library-tests/path-resolution/CONSISTENCY/AstConsistency.expected diff --git a/rust/ql/test/library-tests/modules/main.rs b/rust/ql/test/library-tests/path-resolution/main.rs similarity index 100% rename from rust/ql/test/library-tests/modules/main.rs rename to rust/ql/test/library-tests/path-resolution/main.rs diff --git a/rust/ql/test/library-tests/modules/my.rs b/rust/ql/test/library-tests/path-resolution/my.rs similarity index 100% rename from rust/ql/test/library-tests/modules/my.rs rename to rust/ql/test/library-tests/path-resolution/my.rs diff --git a/rust/ql/test/library-tests/modules/my/nested.rs b/rust/ql/test/library-tests/path-resolution/my/nested.rs similarity index 100% rename from rust/ql/test/library-tests/modules/my/nested.rs rename to rust/ql/test/library-tests/path-resolution/my/nested.rs diff --git a/rust/ql/test/library-tests/modules/my2/mod.rs b/rust/ql/test/library-tests/path-resolution/my2/mod.rs similarity index 100% rename from rust/ql/test/library-tests/modules/my2/mod.rs rename to rust/ql/test/library-tests/path-resolution/my2/mod.rs diff --git a/rust/ql/test/library-tests/modules/my2/nested2.rs b/rust/ql/test/library-tests/path-resolution/my2/nested2.rs similarity index 100% rename from rust/ql/test/library-tests/modules/my2/nested2.rs rename to rust/ql/test/library-tests/path-resolution/my2/nested2.rs diff --git a/rust/ql/test/library-tests/modules/modules.expected b/rust/ql/test/library-tests/path-resolution/path-resolution.expected similarity index 100% rename from rust/ql/test/library-tests/modules/modules.expected rename to rust/ql/test/library-tests/path-resolution/path-resolution.expected diff --git a/rust/ql/test/library-tests/modules/modules.ql b/rust/ql/test/library-tests/path-resolution/path-resolution.ql similarity index 100% rename from rust/ql/test/library-tests/modules/modules.ql rename to rust/ql/test/library-tests/path-resolution/path-resolution.ql From 8eb5792f3bb75f6bbf8bfd66a3dc7dae88b79730 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Thu, 30 Jan 2025 13:17:10 +0100 Subject: [PATCH 3/5] Address review comments --- rust/ql/lib/codeql/rust/AstConsistency.qll | 4 +- .../rust/elements/internal/PathImpl.qll | 6 ++ .../rust/elements/internal/PathResolution.qll | 50 +++++++----- .../rust/frameworks/rustcrypto/RustCrypto.qll | 5 +- .../WeakSensitiveDataHashingExtensions.qll | 3 +- .../security/CWE-696/BadCtorInitialization.ql | 2 +- .../library-tests/path-resolution/main.rs | 11 +++ .../path-resolution/path-resolution.expected | 81 ++++++++++--------- .../path-resolution/path-resolution.ql | 9 +-- .../test/library-tests/variables/variables.ql | 9 +-- 10 files changed, 100 insertions(+), 80 deletions(-) diff --git a/rust/ql/lib/codeql/rust/AstConsistency.qll b/rust/ql/lib/codeql/rust/AstConsistency.qll index 91fefd0d8088..3ff90e010138 100644 --- a/rust/ql/lib/codeql/rust/AstConsistency.qll +++ b/rust/ql/lib/codeql/rust/AstConsistency.qll @@ -75,8 +75,8 @@ query predicate multiplePositions(Element parent, int pos1, int pos2, string acc private import codeql.rust.elements.internal.PathResolution -/** Holds if `p` may resolve to multiple items. */ -query predicate multiplePathResolutions(Path p, Item i) { +/** Holds if `p` may resolve to multiple items including `i`. */ +query predicate multiplePathResolutions(Path p, ItemNode i) { i = resolvePath(p) and strictcount(resolvePath(p)) > 1 } diff --git a/rust/ql/lib/codeql/rust/elements/internal/PathImpl.qll b/rust/ql/lib/codeql/rust/elements/internal/PathImpl.qll index 34cd16c144b4..e73c36913f05 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/PathImpl.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/PathImpl.qll @@ -27,5 +27,11 @@ module Impl { then result = "...::" + this.getPart().toAbbreviatedString() else result = this.getPart().toAbbreviatedString() } + + /** + * Gets the text of this path, if it exists. + */ + pragma[nomagic] + string getText() { result = this.getPart().getNameRef().getText() } } } diff --git a/rust/ql/lib/codeql/rust/elements/internal/PathResolution.qll b/rust/ql/lib/codeql/rust/elements/internal/PathResolution.qll index 682a681dec95..739bbc315583 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/PathResolution.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/PathResolution.qll @@ -57,7 +57,7 @@ abstract class ItemNode extends AstNode { pragma[inline_late] predicate isPublic() { exists(this.getVisibility()) } - /** Gets an element that has this item as immediately enlcosing item. */ + /** Gets an element that has this item as immediately enclosing item. */ pragma[nomagic] Element getADescendant() { getImmediateParent(result) = this @@ -77,9 +77,8 @@ abstract class ItemNode extends AstNode { pragma[nomagic] ModuleLikeNode getImmediateParentModule() { this = result.getAnItemInScope() } - /** Gets a successor named `name` of this item, if any. */ pragma[nomagic] - ItemNode getASuccessor(string name) { + private ItemNode getASuccessorRec(string name) { sourceFileEdge(this, name, result) or this = result.getImmediateParent() and @@ -91,15 +90,21 @@ abstract class ItemNode extends AstNode { or // items made available through `use` are available to nodes that contain the `use` exists(UseItemNode use | - use = this.getASuccessor(_) and - result = use.getASuccessor(name) + use = this.getASuccessorRec(_) and + result = use.(ItemNode).getASuccessorRec(name) ) or // items made available through macro calls are available to nodes that contain the macro call exists(MacroCallItemNode call | - call = this.getASuccessor(_) and - result = call.getASuccessor(name) + call = this.getASuccessorRec(_) and + result = call.(ItemNode).getASuccessorRec(name) ) + } + + /** Gets a successor named `name` of this item, if any. */ + pragma[nomagic] + ItemNode getASuccessor(string name) { + result = this.getASuccessorRec(name) or name = "super" and if this instanceof Module @@ -107,7 +112,8 @@ abstract class ItemNode extends AstNode { else result = this.getImmediateParentModule().getImmediateParentModule() or name = "self" and - if this instanceof Module then result = this else result = this.getImmediateParentModule() + not this instanceof Module and + result = this.getImmediateParentModule() or name = "Self" and this = result.(ImplOrTraitItemNode).getAnItemInSelfScope() @@ -224,6 +230,7 @@ private class BlockExprItemNode extends ItemNode instanceof BlockExpr { override Visibility getVisibility() { none() } } +/** Holds if `item` has the name `name` and is a top-level item inside `f`. */ private predicate sourceFileEdge(SourceFile f, string name, ItemNode item) { item = f.getAnItem() and name = item.getName() @@ -257,7 +264,7 @@ private predicate modImport(Module m, string fileName, string name, Folder paren // #[path = "foo.rs"] // mod bar; // ``` - not m.getAnAttr().getMeta().getPath().getPart().getNameRef().getText() = "path" and + not m.getAnAttr().getMeta().getPath().getText() = "path" and name = m.getName().getText() and parent = f.getParentContainer() and fileName = f.getStem() @@ -303,7 +310,7 @@ private predicate useTreeDeclares(UseTree tree, string name) { name != "_" or not tree.hasRename() and - name = tree.getPath().getPart().getNameRef().getText() + name = tree.getPath().getText() ) or exists(UseTree mid | @@ -330,31 +337,32 @@ private predicate declares(ItemNode item, string name) { ) } +/** A path that does not access a local variable. */ private class RelevantPath extends Path { RelevantPath() { not this = any(VariableAccess va).(PathExpr).getPath() } pragma[nomagic] - predicate isRoot(string name) { + predicate isUnqualified(string name) { not exists(this.getQualifier()) and not this = any(UseTreeList list).getAUseTree().getPath() and - name = this.getPart().getNameRef().getText() + name = this.getText() } } /** - * Holds if the root path `root` references an item named `name`, and `name` + * Holds if the unqualified path `p` references an item named `name`, and `name` * may be looked up inside enclosing item `encl`. */ pragma[nomagic] -private predicate rootPathLookup(RelevantPath root, string name, ItemNode encl) { +private predicate unqualifiedPathLookup(RelevantPath p, string name, ItemNode encl) { exists(ItemNode encl0 | // lookup in the immediately enclosing item - root.isRoot(name) and - encl0.getADescendant() = root + p.isUnqualified(name) and + encl0.getADescendant() = p or // lookup in an outer scope, but only if the item is not declared in inner scope exists(ItemNode mid | - rootPathLookup(root, name, mid) and + unqualifiedPathLookup(p, name, mid) and not declares(mid, name) | // nested modules do not have unqualified access to items from outer modules, @@ -374,7 +382,7 @@ private predicate rootPathLookup(RelevantPath root, string name, ItemNode encl) cached ItemNode resolvePath(RelevantPath path) { exists(ItemNode encl, string name | - rootPathLookup(path, name, encl) and + unqualifiedPathLookup(path, name, encl) and result = encl.getASuccessor(name) ) or @@ -389,7 +397,7 @@ ItemNode resolvePath(RelevantPath path) { pragma[nomagic] private ItemNode resolvePathQualifier(RelevantPath path, string name) { result = resolvePath(path.getQualifier()) and - name = path.getPart().getNameRef().getText() + name = path.getText() } private predicate isUseTreeSubPath(UseTree tree, RelevantPath path) { @@ -405,7 +413,7 @@ pragma[nomagic] private predicate isUseTreeSubPathUnqualified(UseTree tree, RelevantPath path, string name) { isUseTreeSubPath(tree, path) and not exists(path.getQualifier()) and - name = path.getPart().getNameRef().getText() + name = path.getText() } pragma[nomagic] @@ -428,7 +436,7 @@ private ItemNode resolveUseTreeListItemQualifier( Use use, UseTree tree, RelevantPath path, string name ) { result = resolveUseTreeListItem(use, tree, path.getQualifier()) and - name = path.getPart().getNameRef().getText() + name = path.getText() } pragma[nomagic] diff --git a/rust/ql/lib/codeql/rust/frameworks/rustcrypto/RustCrypto.qll b/rust/ql/lib/codeql/rust/frameworks/rustcrypto/RustCrypto.qll index 9dd40004766a..faeccd6a17e4 100644 --- a/rust/ql/lib/codeql/rust/frameworks/rustcrypto/RustCrypto.qll +++ b/rust/ql/lib/codeql/rust/frameworks/rustcrypto/RustCrypto.qll @@ -25,10 +25,9 @@ class StreamCipherInit extends Cryptography::CryptographicOperation::Range { exists(PathExpr p, string rawAlgorithmName | this.asExpr().getExpr().(CallExpr).getFunction() = p and p.getResolvedCrateOrigin().matches("%/RustCrypto%") and - p.getPath().getPart().getNameRef().getText() = - ["new", "new_from_slice", "new_from_slices", "new_with_eff_key_len"] and + p.getPath().getText() = ["new", "new_from_slice", "new_from_slices", "new_with_eff_key_len"] and ( - rawAlgorithmName = p.getPath().getQualifier().getPart().getNameRef().getText() or + rawAlgorithmName = p.getPath().getQualifier().(Path).getText() or // todo: remove infix cast when codegenerator has been fixed rawAlgorithmName = p.getPath() .getQualifier() diff --git a/rust/ql/lib/codeql/rust/security/WeakSensitiveDataHashingExtensions.qll b/rust/ql/lib/codeql/rust/security/WeakSensitiveDataHashingExtensions.qll index 61caa4dd4758..3f245bcb6b54 100644 --- a/rust/ql/lib/codeql/rust/security/WeakSensitiveDataHashingExtensions.qll +++ b/rust/ql/lib/codeql/rust/security/WeakSensitiveDataHashingExtensions.qll @@ -186,8 +186,7 @@ class ModeledHashOperation extends Cryptography::CryptographicOperation::Range { sinkNode(input, "hasher-input") and call = input.(Node::FlowSummaryNode).getSinkElement().getCall() and call = this.asExpr().getExpr() and - algorithmName = - call.getFunction().(PathExpr).getPath().getQualifier().getPart().getNameRef().getText() + algorithmName = call.getFunction().(PathExpr).getPath().getQualifier().(Path).getText() // todo: remove infix cast when codegenerator has been fixed ) } diff --git a/rust/ql/src/queries/security/CWE-696/BadCtorInitialization.ql b/rust/ql/src/queries/security/CWE-696/BadCtorInitialization.ql index 221b8e4eb090..80364a9de06a 100644 --- a/rust/ql/src/queries/security/CWE-696/BadCtorInitialization.ql +++ b/rust/ql/src/queries/security/CWE-696/BadCtorInitialization.ql @@ -20,7 +20,7 @@ class CtorAttr extends Attr { string whichAttr; CtorAttr() { - whichAttr = this.getMeta().getPath().getPart().getNameRef().getText() and + whichAttr = this.getMeta().getPath().getText() and whichAttr = ["ctor", "dtor"] } diff --git a/rust/ql/test/library-tests/path-resolution/main.rs b/rust/ql/test/library-tests/path-resolution/main.rs index b372030ec1b5..467e340e3b6e 100644 --- a/rust/ql/test/library-tests/path-resolution/main.rs +++ b/rust/ql/test/library-tests/path-resolution/main.rs @@ -181,6 +181,16 @@ mod m8 { } // I55 } // I46 +mod m9 { + pub struct MyStruct {} // I56 + + #[rustfmt::skip] + pub fn f() -> self::MyStruct { // $ item=I56 + println!("main.rs::m9::f"); + self::MyStruct {} // $ item=I56 + } // I57 +} + fn main() { my::nested::nested1::nested2::f(); // $ item=I4 my::f(); // $ item=I38 @@ -198,4 +208,5 @@ fn main() { m6::g(); // $ item=I36 m7::f(); // $ item=I45 m8::g(); // $ item=I55 + m9::f(); // $ item=I57 } diff --git a/rust/ql/test/library-tests/path-resolution/path-resolution.expected b/rust/ql/test/library-tests/path-resolution/path-resolution.expected index 26f76962c702..fba9b81db683 100644 --- a/rust/ql/test/library-tests/path-resolution/path-resolution.expected +++ b/rust/ql/test/library-tests/path-resolution/path-resolution.expected @@ -11,6 +11,7 @@ mod | main.rs:109:1:120:1 | mod m6 | | main.rs:122:1:137:1 | mod m7 | | main.rs:139:1:182:1 | mod m8 | +| main.rs:184:1:192:1 | mod m9 | | my2/mod.rs:1:1:1:16 | mod nested2 | | my2/nested2.rs:1:1:11:1 | mod nested3 | | my2/nested2.rs:2:5:10:5 | mod nested4 | @@ -36,7 +37,7 @@ resolvePath | main.rs:30:17:30:21 | super | main.rs:18:5:36:5 | mod m2 | | main.rs:30:17:30:24 | ...::f | main.rs:19:9:21:9 | fn f | | main.rs:33:17:33:17 | f | main.rs:19:9:21:9 | fn f | -| main.rs:40:9:40:13 | super | main.rs:1:1:201:2 | SourceFile | +| main.rs:40:9:40:13 | super | main.rs:1:1:212:2 | SourceFile | | main.rs:40:9:40:17 | ...::m1 | main.rs:13:1:37:1 | mod m1 | | main.rs:40:9:40:21 | ...::m2 | main.rs:18:5:36:5 | mod m2 | | main.rs:40:9:40:24 | ...::g | main.rs:23:9:27:9 | fn g | @@ -48,7 +49,7 @@ resolvePath | main.rs:61:17:61:19 | Foo | main.rs:59:9:59:21 | struct Foo | | main.rs:64:13:64:15 | Foo | main.rs:53:5:53:17 | struct Foo | | main.rs:66:5:66:5 | f | main.rs:55:5:62:5 | fn f | -| main.rs:68:5:68:8 | self | main.rs:1:1:201:2 | SourceFile | +| main.rs:68:5:68:8 | self | main.rs:1:1:212:2 | SourceFile | | main.rs:68:5:68:11 | ...::i | main.rs:71:1:83:1 | fn i | | main.rs:74:13:74:15 | Foo | main.rs:48:1:48:13 | struct Foo | | main.rs:81:17:81:19 | Foo | main.rs:77:9:79:9 | struct Foo | @@ -62,7 +63,7 @@ resolvePath | main.rs:87:57:87:66 | ...::g | my2/nested2.rs:7:9:9:9 | fn g | | main.rs:87:80:87:86 | nested4 | my2/nested2.rs:2:5:10:5 | mod nested4 | | main.rs:100:5:100:22 | f_defined_in_macro | main.rs:99:18:99:42 | fn f_defined_in_macro | -| main.rs:117:13:117:17 | super | main.rs:1:1:201:2 | SourceFile | +| main.rs:117:13:117:17 | super | main.rs:1:1:212:2 | SourceFile | | main.rs:117:13:117:21 | ...::m5 | main.rs:103:1:107:1 | mod m5 | | main.rs:118:9:118:9 | f | main.rs:104:5:106:5 | fn f | | main.rs:118:9:118:9 | f | main.rs:110:5:112:5 | fn f | @@ -88,40 +89,46 @@ resolvePath | main.rs:173:10:173:17 | MyStruct | main.rs:150:5:150:22 | struct MyStruct | | main.rs:177:17:177:24 | MyStruct | main.rs:150:5:150:22 | struct MyStruct | | main.rs:179:17:179:24 | MyStruct | main.rs:150:5:150:22 | struct MyStruct | -| main.rs:185:5:185:6 | my | main.rs:1:1:1:7 | mod my | -| main.rs:185:5:185:14 | ...::nested | my.rs:1:1:1:15 | mod nested | -| main.rs:185:5:185:23 | ...::nested1 | my/nested.rs:1:1:17:1 | mod nested1 | -| main.rs:185:5:185:32 | ...::nested2 | my/nested.rs:2:5:11:5 | mod nested2 | -| main.rs:185:5:185:35 | ...::f | my/nested.rs:3:9:5:9 | fn f | -| main.rs:186:5:186:6 | my | main.rs:1:1:1:7 | mod my | -| main.rs:186:5:186:9 | ...::f | my.rs:5:1:7:1 | fn f | -| main.rs:187:5:187:11 | nested2 | my2/mod.rs:1:1:1:16 | mod nested2 | -| main.rs:187:5:187:20 | ...::nested3 | my2/nested2.rs:1:1:11:1 | mod nested3 | -| main.rs:187:5:187:29 | ...::nested4 | my2/nested2.rs:2:5:10:5 | mod nested4 | -| main.rs:187:5:187:32 | ...::f | my2/nested2.rs:3:9:5:9 | fn f | -| main.rs:188:5:188:5 | f | my2/nested2.rs:3:9:5:9 | fn f | -| main.rs:189:5:189:5 | g | my2/nested2.rs:7:9:9:9 | fn g | -| main.rs:190:5:190:9 | crate | main.rs:1:1:201:2 | SourceFile | -| main.rs:190:5:190:12 | ...::h | main.rs:50:1:69:1 | fn h | -| main.rs:191:5:191:6 | m1 | main.rs:13:1:37:1 | mod m1 | -| main.rs:191:5:191:10 | ...::m2 | main.rs:18:5:36:5 | mod m2 | -| main.rs:191:5:191:13 | ...::g | main.rs:23:9:27:9 | fn g | -| main.rs:192:5:192:6 | m1 | main.rs:13:1:37:1 | mod m1 | -| main.rs:192:5:192:10 | ...::m2 | main.rs:18:5:36:5 | mod m2 | -| main.rs:192:5:192:14 | ...::m3 | main.rs:29:9:35:9 | mod m3 | -| main.rs:192:5:192:17 | ...::h | main.rs:30:27:34:13 | fn h | -| main.rs:193:5:193:6 | m4 | main.rs:39:1:46:1 | mod m4 | -| main.rs:193:5:193:9 | ...::i | main.rs:42:5:45:5 | fn i | -| main.rs:194:5:194:5 | h | main.rs:50:1:69:1 | fn h | -| main.rs:195:5:195:11 | f_alias | my2/nested2.rs:3:9:5:9 | fn f | -| main.rs:196:5:196:11 | g_alias | my2/nested2.rs:7:9:9:9 | fn g | -| main.rs:197:5:197:5 | j | main.rs:97:1:101:1 | fn j | -| main.rs:198:5:198:6 | m6 | main.rs:109:1:120:1 | mod m6 | -| main.rs:198:5:198:9 | ...::g | main.rs:114:5:119:5 | fn g | -| main.rs:199:5:199:6 | m7 | main.rs:122:1:137:1 | mod m7 | -| main.rs:199:5:199:9 | ...::f | main.rs:129:5:136:5 | fn f | -| main.rs:200:5:200:6 | m8 | main.rs:139:1:182:1 | mod m8 | -| main.rs:200:5:200:9 | ...::g | main.rs:169:5:181:5 | fn g | +| main.rs:188:19:188:22 | self | main.rs:184:1:192:1 | mod m9 | +| main.rs:188:19:188:32 | ...::MyStruct | main.rs:185:5:185:26 | struct MyStruct | +| main.rs:190:9:190:12 | self | main.rs:184:1:192:1 | mod m9 | +| main.rs:190:9:190:22 | ...::MyStruct | main.rs:185:5:185:26 | struct MyStruct | +| main.rs:195:5:195:6 | my | main.rs:1:1:1:7 | mod my | +| main.rs:195:5:195:14 | ...::nested | my.rs:1:1:1:15 | mod nested | +| main.rs:195:5:195:23 | ...::nested1 | my/nested.rs:1:1:17:1 | mod nested1 | +| main.rs:195:5:195:32 | ...::nested2 | my/nested.rs:2:5:11:5 | mod nested2 | +| main.rs:195:5:195:35 | ...::f | my/nested.rs:3:9:5:9 | fn f | +| main.rs:196:5:196:6 | my | main.rs:1:1:1:7 | mod my | +| main.rs:196:5:196:9 | ...::f | my.rs:5:1:7:1 | fn f | +| main.rs:197:5:197:11 | nested2 | my2/mod.rs:1:1:1:16 | mod nested2 | +| main.rs:197:5:197:20 | ...::nested3 | my2/nested2.rs:1:1:11:1 | mod nested3 | +| main.rs:197:5:197:29 | ...::nested4 | my2/nested2.rs:2:5:10:5 | mod nested4 | +| main.rs:197:5:197:32 | ...::f | my2/nested2.rs:3:9:5:9 | fn f | +| main.rs:198:5:198:5 | f | my2/nested2.rs:3:9:5:9 | fn f | +| main.rs:199:5:199:5 | g | my2/nested2.rs:7:9:9:9 | fn g | +| main.rs:200:5:200:9 | crate | main.rs:1:1:212:2 | SourceFile | +| main.rs:200:5:200:12 | ...::h | main.rs:50:1:69:1 | fn h | +| main.rs:201:5:201:6 | m1 | main.rs:13:1:37:1 | mod m1 | +| main.rs:201:5:201:10 | ...::m2 | main.rs:18:5:36:5 | mod m2 | +| main.rs:201:5:201:13 | ...::g | main.rs:23:9:27:9 | fn g | +| main.rs:202:5:202:6 | m1 | main.rs:13:1:37:1 | mod m1 | +| main.rs:202:5:202:10 | ...::m2 | main.rs:18:5:36:5 | mod m2 | +| main.rs:202:5:202:14 | ...::m3 | main.rs:29:9:35:9 | mod m3 | +| main.rs:202:5:202:17 | ...::h | main.rs:30:27:34:13 | fn h | +| main.rs:203:5:203:6 | m4 | main.rs:39:1:46:1 | mod m4 | +| main.rs:203:5:203:9 | ...::i | main.rs:42:5:45:5 | fn i | +| main.rs:204:5:204:5 | h | main.rs:50:1:69:1 | fn h | +| main.rs:205:5:205:11 | f_alias | my2/nested2.rs:3:9:5:9 | fn f | +| main.rs:206:5:206:11 | g_alias | my2/nested2.rs:7:9:9:9 | fn g | +| main.rs:207:5:207:5 | j | main.rs:97:1:101:1 | fn j | +| main.rs:208:5:208:6 | m6 | main.rs:109:1:120:1 | mod m6 | +| main.rs:208:5:208:9 | ...::g | main.rs:114:5:119:5 | fn g | +| main.rs:209:5:209:6 | m7 | main.rs:122:1:137:1 | mod m7 | +| main.rs:209:5:209:9 | ...::f | main.rs:129:5:136:5 | fn f | +| main.rs:210:5:210:6 | m8 | main.rs:139:1:182:1 | mod m8 | +| main.rs:210:5:210:9 | ...::g | main.rs:169:5:181:5 | fn g | +| main.rs:211:5:211:6 | m9 | main.rs:184:1:192:1 | mod m9 | +| main.rs:211:5:211:9 | ...::f | main.rs:187:5:191:5 | fn f | | my2/mod.rs:5:5:5:11 | nested2 | my2/mod.rs:1:1:1:16 | mod nested2 | | my2/mod.rs:5:5:5:20 | ...::nested3 | my2/nested2.rs:1:1:11:1 | mod nested3 | | my2/mod.rs:5:5:5:29 | ...::nested4 | my2/nested2.rs:2:5:10:5 | mod nested4 | diff --git a/rust/ql/test/library-tests/path-resolution/path-resolution.ql b/rust/ql/test/library-tests/path-resolution/path-resolution.ql index df3828a4e123..bce15517b570 100644 --- a/rust/ql/test/library-tests/path-resolution/path-resolution.ql +++ b/rust/ql/test/library-tests/path-resolution/path-resolution.ql @@ -23,14 +23,9 @@ module ResolveTest implements TestSig { private predicate item(ItemNode i, string value) { exists(string filepath, int line, boolean inMacro | itemAt(i, filepath, line, inMacro) | - commmentAt(value, filepath, line) and - inMacro = false + commmentAt(value, filepath, line) and inMacro = false or - ( - not commmentAt(_, filepath, line) - or - inMacro = true - ) and + not (commmentAt(_, filepath, line) and inMacro = false) and value = i.getName() ) } diff --git a/rust/ql/test/library-tests/variables/variables.ql b/rust/ql/test/library-tests/variables/variables.ql index 2ec66e1196ec..9d643a36f38b 100644 --- a/rust/ql/test/library-tests/variables/variables.ql +++ b/rust/ql/test/library-tests/variables/variables.ql @@ -37,14 +37,9 @@ module VariableAccessTest implements TestSig { private predicate decl(Variable v, string value) { exists(string filepath, int line, boolean inMacro | declAt(v, filepath, line, inMacro) | - commmentAt(value, filepath, line) and - inMacro = false + commmentAt(value, filepath, line) and inMacro = false or - ( - not commmentAt(_, filepath, line) - or - inMacro = true - ) and + not (commmentAt(_, filepath, line) and inMacro = false) and value = v.getName() ) } From 9d06f809021f9a30ab0c0475ea508d4cc1a98792 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Fri, 31 Jan 2025 09:48:48 +0100 Subject: [PATCH 4/5] Rust: Elaborate QL doc on `PathResolution.qll` --- rust/ql/lib/codeql/rust/elements/internal/PathResolution.qll | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rust/ql/lib/codeql/rust/elements/internal/PathResolution.qll b/rust/ql/lib/codeql/rust/elements/internal/PathResolution.qll index 739bbc315583..c9e971e5d04f 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/PathResolution.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/PathResolution.qll @@ -1,4 +1,6 @@ -/** Provides functionality for resolving paths. */ +/** + * Provides functionality for resolving paths, using the predicate `resolvePath`. + */ private import rust private import codeql.rust.elements.internal.generated.ParentChild From 1cb524f76f8bc33c195709974854d85cd2dd7002 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Fri, 31 Jan 2025 10:10:26 +0100 Subject: [PATCH 5/5] Rust: Remove `useTreeIsGlobImport` workaround --- .../codeql/rust/elements/internal/PathResolution.qll | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/rust/ql/lib/codeql/rust/elements/internal/PathResolution.qll b/rust/ql/lib/codeql/rust/elements/internal/PathResolution.qll index c9e971e5d04f..78d6c9f4a4f5 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/PathResolution.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/PathResolution.qll @@ -296,16 +296,8 @@ private predicate fileImportEdge(Module mod, string name, ItemNode item) { ) } -pragma[nomagic] -private predicate useTreeIsGlobImport(UseTree use) { - // TODO: the extractor should provide this information - use.getLocation() != use.getPath().getLocation() and - not use.hasUseTreeList() and - not use.hasRename() -} - private predicate useTreeDeclares(UseTree tree, string name) { - not useTreeIsGlobImport(tree) and + not tree.isGlob() and not exists(tree.getUseTreeList()) and ( name = tree.getRename().getName().getText() and @@ -455,7 +447,7 @@ private predicate useImportEdge(Use use, string name, ItemNode item) { exists(UseTree tree, ItemNode used | used = resolveUseTreeListItem(use, tree) and not exists(tree.getUseTreeList()) and - if useTreeIsGlobImport(tree) + if tree.isGlob() then exists(ItemNode encl | encl.getADescendant() = use and