From edb7aaabab1d5166b207c6a548d8e2e9c6a68e6a Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Fri, 4 Apr 2025 10:50:54 +0200 Subject: [PATCH 1/3] Rust: Add path attribute test --- rust/ql/test/library-tests/path-resolution/my2/mod.rs | 5 +++++ rust/ql/test/library-tests/path-resolution/my2/renamed.rs | 1 + .../library-tests/path-resolution/path-resolution.expected | 6 ++++-- 3 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 rust/ql/test/library-tests/path-resolution/my2/renamed.rs diff --git a/rust/ql/test/library-tests/path-resolution/my2/mod.rs b/rust/ql/test/library-tests/path-resolution/my2/mod.rs index 64291a53af53..27969db97c0e 100644 --- a/rust/ql/test/library-tests/path-resolution/my2/mod.rs +++ b/rust/ql/test/library-tests/path-resolution/my2/mod.rs @@ -10,3 +10,8 @@ pub use nested2::nested5::*; // $ item=I114 pub use nested2::nested7::nested8::{self}; // $ item=I118 pub mod my3; + +#[path = "renamed.rs"] +mod mymod; + +use mymod::f; // $ MISSING: item=I1001 diff --git a/rust/ql/test/library-tests/path-resolution/my2/renamed.rs b/rust/ql/test/library-tests/path-resolution/my2/renamed.rs new file mode 100644 index 000000000000..fa37691c1366 --- /dev/null +++ b/rust/ql/test/library-tests/path-resolution/my2/renamed.rs @@ -0,0 +1 @@ +pub fn f() {} // I1001 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 1d480f2f5ad9..85a56c3384c4 100644 --- a/rust/ql/test/library-tests/path-resolution/path-resolution.expected +++ b/rust/ql/test/library-tests/path-resolution/path-resolution.expected @@ -30,6 +30,7 @@ mod | main.rs:523:1:548:1 | mod m23 | | my2/mod.rs:1:1:1:16 | mod nested2 | | my2/mod.rs:12:1:12:12 | mod my3 | +| my2/mod.rs:14:1:15:10 | mod mymod | | my2/nested2.rs:1:1:11:1 | mod nested3 | | my2/nested2.rs:2:5:10:5 | mod nested4 | | my2/nested2.rs:13:1:19:1 | mod nested5 | @@ -306,12 +307,13 @@ resolvePath | my2/mod.rs:10:9:10:24 | ...::nested7 | my2/nested2.rs:21:1:27:1 | mod nested7 | | my2/mod.rs:10:9:10:33 | ...::nested8 | my2/nested2.rs:22:5:26:5 | mod nested8 | | my2/mod.rs:10:37:10:40 | self | my2/nested2.rs:22:5:26:5 | mod nested8 | +| my2/mod.rs:17:5:17:9 | mymod | my2/mod.rs:14:1:15:10 | mod mymod | | my2/my3/mod.rs:3:5:3:5 | g | my2/mod.rs:3:1:6:1 | fn g | | my2/my3/mod.rs:4:5:4:5 | h | main.rs:50:1:69:1 | fn h | -| my2/my3/mod.rs:7:5:7:9 | super | my2/mod.rs:1:1:12:13 | SourceFile | +| my2/my3/mod.rs:7:5:7:9 | super | my2/mod.rs:1:1:17:39 | SourceFile | | my2/my3/mod.rs:7:5:7:16 | ...::super | main.rs:1:1:578:2 | SourceFile | | my2/my3/mod.rs:7:5:7:19 | ...::h | main.rs:50:1:69:1 | fn h | -| my2/my3/mod.rs:8:5:8:9 | super | my2/mod.rs:1:1:12:13 | SourceFile | +| my2/my3/mod.rs:8:5:8:9 | super | my2/mod.rs:1:1:17:39 | SourceFile | | my2/my3/mod.rs:8:5:8:12 | ...::g | my2/mod.rs:3:1:6:1 | fn g | | 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 | From 13f4a6afa6426b9b2e708cb23901333c686704f6 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Fri, 4 Apr 2025 10:51:35 +0200 Subject: [PATCH 2/3] Rust: Handle path attributes in path resolution --- rust/ql/lib/codeql/files/FileSystem.qll | 2 + .../codeql/rust/internal/PathResolution.qll | 27 ++++++++--- .../library-tests/path-resolution/my2/mod.rs | 2 +- .../path-resolution/path-resolution.expected | 5 +- shared/util/codeql/util/FileSystem.qll | 47 +++++++++++++++++++ 5 files changed, 74 insertions(+), 9 deletions(-) diff --git a/rust/ql/lib/codeql/files/FileSystem.qll b/rust/ql/lib/codeql/files/FileSystem.qll index 175f50c7c9ea..5a60d28418eb 100644 --- a/rust/ql/lib/codeql/files/FileSystem.qll +++ b/rust/ql/lib/codeql/files/FileSystem.qll @@ -34,6 +34,8 @@ class Container = Impl::Container; class Folder = Impl::Folder; +module Folder = Impl::Folder; + /** A file. */ class File extends Container, Impl::File { /** Holds if this file was extracted from ordinary source code. */ diff --git a/rust/ql/lib/codeql/rust/internal/PathResolution.qll b/rust/ql/lib/codeql/rust/internal/PathResolution.qll index 91d7e87704c6..ad64e50d009f 100644 --- a/rust/ql/lib/codeql/rust/internal/PathResolution.qll +++ b/rust/ql/lib/codeql/rust/internal/PathResolution.qll @@ -655,6 +655,11 @@ private predicate fileModule(SourceFile f, string name, Folder folder) { ) } +private Meta getPathAttrMeta(Module m) { + result = m.getAnAttr().getMeta() and + result.getPath().getText() = "path" +} + /** * Holds if `m` is a `mod name;` module declaration, where the corresponding * module file needs to be looked up in `lookup` or one of its descandants. @@ -663,12 +668,7 @@ private predicate modImport0(Module m, string name, Folder lookup) { exists(File f, Folder parent, string fileName | f = m.getFile() and not m.hasItemList() and - // TODO: handle - // ``` - // #[path = "foo.rs"] - // mod bar; - // ``` - not m.getAnAttr().getMeta().getPath().getText() = "path" and + not exists(getPathAttrMeta(m)) and name = m.getName().getText() and parent = f.getParentContainer() and fileName = f.getStem() @@ -717,6 +717,16 @@ private predicate modImportNestedLookup(Module m, ModuleItemNode ancestor, Folde ) } +private predicate pathAttrImport(Folder f, Module m, string relativePath) { + exists(Meta meta | + f = m.getFile().getParentContainer() and + meta = getPathAttrMeta(m) and + relativePath = meta.getExpr().(LiteralExpr).getTextValue().regexpCapture("\"(.+)\"", 1) + ) +} + +private predicate append(Folder f, string relativePath) { pathAttrImport(f, _, relativePath) } + /** Holds if `m` is a `mod name;` item importing file `f`. */ private predicate fileImport(Module m, SourceFile f) { exists(string name, Folder parent | @@ -730,6 +740,11 @@ private predicate fileImport(Module m, SourceFile f) { // `m` is inside a nested module modImportNestedLookup(m, m, parent) ) + or + exists(Folder folder, string relativePath | + pathAttrImport(folder, m, relativePath) and + f.getFile() = Folder::Append::append(folder, relativePath) + ) } /** diff --git a/rust/ql/test/library-tests/path-resolution/my2/mod.rs b/rust/ql/test/library-tests/path-resolution/my2/mod.rs index 27969db97c0e..43c1a05e91fe 100644 --- a/rust/ql/test/library-tests/path-resolution/my2/mod.rs +++ b/rust/ql/test/library-tests/path-resolution/my2/mod.rs @@ -14,4 +14,4 @@ pub mod my3; #[path = "renamed.rs"] mod mymod; -use mymod::f; // $ MISSING: item=I1001 +use mymod::f; // $ item=I1001 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 85a56c3384c4..7fbbca66c39c 100644 --- a/rust/ql/test/library-tests/path-resolution/path-resolution.expected +++ b/rust/ql/test/library-tests/path-resolution/path-resolution.expected @@ -308,12 +308,13 @@ resolvePath | my2/mod.rs:10:9:10:33 | ...::nested8 | my2/nested2.rs:22:5:26:5 | mod nested8 | | my2/mod.rs:10:37:10:40 | self | my2/nested2.rs:22:5:26:5 | mod nested8 | | my2/mod.rs:17:5:17:9 | mymod | my2/mod.rs:14:1:15:10 | mod mymod | +| my2/mod.rs:17:5:17:12 | ...::f | my2/renamed.rs:1:1:1:13 | fn f | | my2/my3/mod.rs:3:5:3:5 | g | my2/mod.rs:3:1:6:1 | fn g | | my2/my3/mod.rs:4:5:4:5 | h | main.rs:50:1:69:1 | fn h | -| my2/my3/mod.rs:7:5:7:9 | super | my2/mod.rs:1:1:17:39 | SourceFile | +| my2/my3/mod.rs:7:5:7:9 | super | my2/mod.rs:1:1:17:30 | SourceFile | | my2/my3/mod.rs:7:5:7:16 | ...::super | main.rs:1:1:578:2 | SourceFile | | my2/my3/mod.rs:7:5:7:19 | ...::h | main.rs:50:1:69:1 | fn h | -| my2/my3/mod.rs:8:5:8:9 | super | my2/mod.rs:1:1:17:39 | SourceFile | +| my2/my3/mod.rs:8:5:8:9 | super | my2/mod.rs:1:1:17:30 | SourceFile | | my2/my3/mod.rs:8:5:8:12 | ...::g | my2/mod.rs:3:1:6:1 | fn g | | 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 | diff --git a/shared/util/codeql/util/FileSystem.qll b/shared/util/codeql/util/FileSystem.qll index a9eb21279b63..261139dcf41b 100644 --- a/shared/util/codeql/util/FileSystem.qll +++ b/shared/util/codeql/util/FileSystem.qll @@ -218,6 +218,53 @@ module Make { /** Gets the URL of this file. */ override string getURL() { result = "file://" + this.getAbsolutePath() + ":0:0:0:0" } } + + /** Provides logic related to `Folder`s. */ + module Folder { + /** Holds if `relativePath` needs to be appended to `f`. */ + signature predicate appendSig(Folder f, string relativePath); + + /** Provides the `append` predicate for appending a relative path onto a folder. */ + module Append { + pragma[nomagic] + private string getComponent(string relativePath, int i) { + app(_, relativePath) and + result = relativePath.replaceAll("\\", "/").regexpFind("[^/]+", i, _) + } + + pragma[nomagic] + private Container appendStep(Folder f, string relativePath, int i) { + i = -1 and + app(f, relativePath) and + result = f + or + exists(Container mid, string comp | + mid = appendStep(f, relativePath, i - 1) and + comp = getComponent(relativePath, i) and + if comp = ".." + then result = mid.getParentContainer() + else + if comp = "." + then result = mid + else ( + result = mid.getAChildContainer() and + result.getBaseName() = comp + ) + ) + } + + /** + * Gets the file or folder obtained by appending `relativePath` onto `f`. + */ + pragma[nomagic] + Container append(Folder f, string relativePath) { + exists(int components | + components = (-1).maximum(max(int comp | exists(getComponent(relativePath, comp)) | comp)) and + result = appendStep(f, relativePath, components) + ) + } + } + } } /** A file. */ From 52401aaa735cc623e293e195cb391cdc477eaa5a Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Wed, 9 Apr 2025 17:19:25 +0200 Subject: [PATCH 3/3] Address review comments --- .../codeql/rust/internal/PathResolution.qll | 9 ++++++-- shared/util/codeql/util/FileSystem.qll | 21 ++++++++++++------- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/rust/ql/lib/codeql/rust/internal/PathResolution.qll b/rust/ql/lib/codeql/rust/internal/PathResolution.qll index ad64e50d009f..9a73d8d035c7 100644 --- a/rust/ql/lib/codeql/rust/internal/PathResolution.qll +++ b/rust/ql/lib/codeql/rust/internal/PathResolution.qll @@ -655,6 +655,11 @@ private predicate fileModule(SourceFile f, string name, Folder folder) { ) } +/** + * Gets the `Meta` of the module `m`'s [path attribute][1]. + * + * [1]: https://doc.rust-lang.org/reference/items/modules.html#r-items.mod.outlined.path + */ private Meta getPathAttrMeta(Module m) { result = m.getAnAttr().getMeta() and result.getPath().getText() = "path" @@ -725,7 +730,7 @@ private predicate pathAttrImport(Folder f, Module m, string relativePath) { ) } -private predicate append(Folder f, string relativePath) { pathAttrImport(f, _, relativePath) } +private predicate shouldAppend(Folder f, string relativePath) { pathAttrImport(f, _, relativePath) } /** Holds if `m` is a `mod name;` item importing file `f`. */ private predicate fileImport(Module m, SourceFile f) { @@ -743,7 +748,7 @@ private predicate fileImport(Module m, SourceFile f) { or exists(Folder folder, string relativePath | pathAttrImport(folder, m, relativePath) and - f.getFile() = Folder::Append::append(folder, relativePath) + f.getFile() = Folder::Append::append(folder, relativePath) ) } diff --git a/shared/util/codeql/util/FileSystem.qll b/shared/util/codeql/util/FileSystem.qll index 261139dcf41b..ea58db929c51 100644 --- a/shared/util/codeql/util/FileSystem.qll +++ b/shared/util/codeql/util/FileSystem.qll @@ -222,20 +222,27 @@ module Make { /** Provides logic related to `Folder`s. */ module Folder { /** Holds if `relativePath` needs to be appended to `f`. */ - signature predicate appendSig(Folder f, string relativePath); + signature predicate shouldAppendSig(Folder f, string relativePath); /** Provides the `append` predicate for appending a relative path onto a folder. */ - module Append { + module Append { pragma[nomagic] private string getComponent(string relativePath, int i) { - app(_, relativePath) and + shouldAppend(_, relativePath) and result = relativePath.replaceAll("\\", "/").regexpFind("[^/]+", i, _) } + private int getNumberOfComponents(string relativePath) { + result = strictcount(int i | exists(getComponent(relativePath, i)) | i) + or + relativePath = "" and + result = 0 + } + pragma[nomagic] private Container appendStep(Folder f, string relativePath, int i) { i = -1 and - app(f, relativePath) and + shouldAppend(f, relativePath) and result = f or exists(Container mid, string comp | @@ -258,9 +265,9 @@ module Make { */ pragma[nomagic] Container append(Folder f, string relativePath) { - exists(int components | - components = (-1).maximum(max(int comp | exists(getComponent(relativePath, comp)) | comp)) and - result = appendStep(f, relativePath, components) + exists(int last | + last = getNumberOfComponents(relativePath) - 1 and + result = appendStep(f, relativePath, last) ) } }