From 2241a074a12fb7fb5475a6be1947c1d184535d3a Mon Sep 17 00:00:00 2001 From: Simon Friis Vindum Date: Mon, 14 Sep 2026 14:22:07 +0200 Subject: [PATCH] Rust: Remove pre-1.94 format macro fallback Drop the synthetic format-family macro expansion path and its dedicated legacy-toolchain tests now that extraction uses a pinned compatible Rust toolchain. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- rust/extractor/src/translate.rs | 1 - rust/extractor/src/translate/base.rs | 65 ------- rust/extractor/src/translate/format_args.rs | 163 ------------------ rust/ql/test/.gitignore | 4 - .../format-macros-legacy/Cargo.lock | 7 - .../format-macros-legacy/FormatArgs.expected | 12 -- .../format-macros-legacy/FormatArgs.ql | 8 - .../LogInjection.expected | 28 --- .../format-macros-legacy/LogInjection.qlref | 4 - .../inline-taint-flow.expected | 124 ------------- .../format-macros-legacy/inline-taint-flow.ql | 12 -- .../format-macros-legacy/main.rs | 74 -------- .../format-macros-legacy/rust-toolchain.toml | 14 -- rust/ql/test/setup.sh | 4 - 14 files changed, 520 deletions(-) delete mode 100644 rust/extractor/src/translate/format_args.rs delete mode 100644 rust/ql/test/library-tests/format-macros-legacy/Cargo.lock delete mode 100644 rust/ql/test/library-tests/format-macros-legacy/FormatArgs.expected delete mode 100644 rust/ql/test/library-tests/format-macros-legacy/FormatArgs.ql delete mode 100644 rust/ql/test/library-tests/format-macros-legacy/LogInjection.expected delete mode 100644 rust/ql/test/library-tests/format-macros-legacy/LogInjection.qlref delete mode 100644 rust/ql/test/library-tests/format-macros-legacy/inline-taint-flow.expected delete mode 100644 rust/ql/test/library-tests/format-macros-legacy/inline-taint-flow.ql delete mode 100644 rust/ql/test/library-tests/format-macros-legacy/main.rs delete mode 100644 rust/ql/test/library-tests/format-macros-legacy/rust-toolchain.toml diff --git a/rust/extractor/src/translate.rs b/rust/extractor/src/translate.rs index 586d46ee27e2..1e9f3775d40e 100644 --- a/rust/extractor/src/translate.rs +++ b/rust/extractor/src/translate.rs @@ -1,5 +1,4 @@ mod base; -mod format_args; mod generated; mod mappings; diff --git a/rust/extractor/src/translate/base.rs b/rust/extractor/src/translate/base.rs index d25ca31e6de3..05f838cfbc3b 100644 --- a/rust/extractor/src/translate/base.rs +++ b/rust/extractor/src/translate/base.rs @@ -1,4 +1,3 @@ -use super::format_args; use super::mappings::Emission; use crate::generated::{self}; use crate::rust_analyzer::FileSemanticInformation; @@ -461,9 +460,6 @@ impl<'db> Translator<'db> { )); } } else if self.semantics.is_some() { - if self.reconstruct_format_args_expansion(mcall, label) { - return; - } // let's not spam warnings if we don't have semantics, we already emitted one let range = self.text_range_for_node(mcall); self.emit_parse_error( @@ -785,67 +781,6 @@ impl<'db> Translator<'db> { result } - /// Reconstructs and emits the expansion of a format-family macro (`format!`, - /// `println!`, `write!`, `panic!`, ...). - /// - /// On `rustc <1.94` sysroots these macros no longer resolve, so `expand_macro_call` - /// returns `None` and we get a bare unexpanded `MacroCall` with no flow through it. - /// We rebuild the token tree of the real expansion ourselves (see - /// [`super::format_args`]), parse it, and register the result as the macro - /// expansion. Locations of the synthesized nodes are routed through the expansion - /// span map via `builtin_derive_span_map`. - /// - /// Returns `true` when the macro was recognized and an expansion was emitted. - fn reconstruct_format_args_expansion( - &mut self, - mcall: &ast::MacroCall, - label: Label, - ) -> bool { - self.try_reconstruct_format_args_expansion(mcall, label) - .is_some() - } - - /// Attempts to reconstruct and emit a format-family macro expansion. - fn try_reconstruct_format_args_expansion( - &mut self, - mcall: &ast::MacroCall, - label: Label, - ) -> Option<()> { - let name = mcall.path()?.segment()?.name_ref()?.text().to_string(); - let wrap = format_args::Wrap::for_macro(&name)?; - let tt_node = mcall.token_tree()?; - let semantics = self.semantics?; - let db = semantics.db; - let file_id = semantics.hir_file_for(mcall.syntax()); - let span_map = file_id.span_map(db); - let call_site = span_map.span_for_range(mcall.syntax().text_range()); - let input = syntax_node_to_token_tree( - tt_node.syntax(), - span_map, - call_site, - DocCommentDesugarMode::ProcMacro, - ); - let output = format_args::reconstruct(wrap, &input, call_site)?; - - let edition = self.file_id.map(|f| f.edition(db))?; - let (parsed, output_span_map) = - token_tree_to_syntax_node(&output, TopEntryPoint::Expr, &mut |_| edition); - let root = parsed.syntax_node(); - let expr = ast::Expr::cast(root.clone()) - .or_else(|| root.descendants().find_map(ast::Expr::cast))?; - // Sanity check: the parsed expression must contain the reconstructed - // `FormatArgsExpr` (either directly, or wrapped in the callee above). - expr.syntax() - .descendants() - .find_map(ast::FormatArgsExpr::cast)?; - let previous = self.builtin_derive_span_map.replace(output_span_map); - let emitted = self.emit_expr(&expr); - self.builtin_derive_span_map = previous; - let value = emitted?; - generated::MacroCall::emit_macro_call_expansion(label, value.into(), &mut self.trap.writer); - Some(()) - } - pub(crate) fn emit_derive_expansion( &mut self, node: &(impl Into + Clone), diff --git a/rust/extractor/src/translate/format_args.rs b/rust/extractor/src/translate/format_args.rs deleted file mode 100644 index c6ac1b9b8d0d..000000000000 --- a/rust/extractor/src/translate/format_args.rs +++ /dev/null @@ -1,163 +0,0 @@ -//! Reconstruction of the `format_args!` expansion of the format-family macros. -//! -//! On `rustc <1.94` sysroots the format-family macros (`format!`, `println!`, -//! `write!`, `panic!`, ...) no longer resolve, so `expand_macro_call` returns `None` -//! and we get a bare unexpanded `MacroCall`. The syntactic lowering of these macros -//! is a pure, sysroot-independent transform, so we rebuild the same token tree the -//! real (>=1.94) expansion produces and parse it ourselves, giving pre-1.94 -//! toolchains the same AST as newer ones. -//! -//! This module owns the pure token-tree construction; [`super::base::Translator`] -//! handles parsing the result and emitting it as the macro expansion. - -use ra_ap_hir_expand::intern::Symbol; -use ra_ap_hir_expand::tt; -use ra_ap_span::Span; - -/// How a format-family macro wraps its `format_args!`. We rebuild the same shape the -/// real (>=1.94) expansion has, so older toolchains get the same AST. -#[derive(Clone, Copy, PartialEq, Eq)] -pub(crate) enum Wrap { - /// `format_args!` and friends are themselves the `FormatArgsExpr`. - Bare, - /// Wrapped in a call to the given absolute path, e.g. `std::fmt::format(..)`. - Call(&'static [&'static str]), - /// `write!`/`writeln!`: `.write_fmt(format_args!(..))`. - WriteMethod, -} - -impl Wrap { - /// Classifies a macro by its name, or returns `None` if it is not a - /// format-family macro we reconstruct. - /// - /// `format_args_nl!`'s trailing newline is intentionally dropped: it is not - /// relevant to flow or to the sinks keyed on the callee, so all variants map to - /// the same `format_args` reconstruction. - pub(crate) fn for_macro(name: &str) -> Option { - Some(match name { - "format_args" | "const_format_args" | "format_args_nl" => Wrap::Bare, - "format" => Wrap::Call(&["std", "fmt", "format"]), - "print" | "println" => Wrap::Call(&["std", "io", "_print"]), - "eprint" | "eprintln" => Wrap::Call(&["std", "io", "_eprint"]), - "panic" => Wrap::Call(&["core", "panicking", "panic_fmt"]), - "write" | "writeln" => Wrap::WriteMethod, - _ => return None, - }) - } -} - -/// Builds the reconstructed expansion token tree for `wrap` from the macro `input`. -/// -/// Returns `None` if the input does not match the expected shape (currently only when -/// a `write!` argument list has no writer/format-args separating comma). `call_site` -/// is used to span the synthesized tokens; the format arguments keep their own spans. -pub(crate) fn reconstruct( - wrap: Wrap, - input: &tt::TopSubtree, - call_site: Span, -) -> Option { - let emitter = Emitter { - call_site, - format_args_parens: input.view().top_subtree().delimiter, - }; - - let mut builder = tt::TopSubtreeBuilder::new(tt::Delimiter::invisible_spanned(call_site)); - match wrap { - Wrap::Bare => emitter.push_format_args(&mut builder, input.view().token_trees()), - Wrap::Call(path) => { - emitter.push_path(&mut builder, path); - emitter.push_parenthesized_format_args(&mut builder, input.view().token_trees()); - } - Wrap::WriteMethod => { - let (writer, content) = split_arguments(input)?; - builder.extend_with_tt(writer); - builder.push(emitter.punct('.', tt::Spacing::Alone)); - builder.push(emitter.ident("write_fmt")); - emitter.push_parenthesized_format_args(&mut builder, content); - } - } - Some(builder.build()) -} - -/// Splits the macro argument list into the leading writer (for `write!`/`writeln!`, -/// everything up to the first top-level comma) and the format arguments. -fn split_arguments<'a>( - input: &'a tt::TopSubtree, -) -> Option<(tt::TokenTreesView<'a>, tt::TokenTreesView<'a>)> { - let mut iter = input.view().iter(); - let start = iter.savepoint(); - let mut found_comma = false; - while let Some(element) = iter.peek() { - if let tt::TtElement::Leaf(tt::Leaf::Punct(punct)) = element - && punct.char == ',' - { - found_comma = true; - break; - } - iter.next(); - } - if !found_comma { - return None; - } - let writer = iter.from_savepoint(start); - iter.next(); // consume the comma - Some((writer, iter.remaining())) -} - -/// Emits the synthesized tokens, tagging them with the macro call site span. -struct Emitter { - call_site: Span, - /// The delimiter of the macro input, reused for the `format_args(..)` parentheses - /// so those spans point back at the original argument list. - format_args_parens: tt::Delimiter, -} - -impl Emitter { - fn ident(&self, sym: &str) -> tt::Leaf { - tt::Leaf::Ident(tt::Ident { - sym: Symbol::intern(sym), - span: self.call_site, - is_raw: tt::IdentIsRaw::No, - }) - } - - fn punct(&self, char: char, spacing: tt::Spacing) -> tt::Leaf { - tt::Leaf::Punct(tt::Punct { - char, - spacing, - span: self.call_site, - }) - } - - /// Pushes `builtin # format_args ( )`, the token form the parser turns - /// into a `FormatArgsExpr`. Its argument leaves keep their real source spans. - fn push_format_args(&self, builder: &mut tt::TopSubtreeBuilder, content: tt::TokenTreesView) { - builder.push(self.ident("builtin")); - builder.push(self.punct('#', tt::Spacing::Alone)); - builder.push(self.ident("format_args")); - builder.open(tt::DelimiterKind::Parenthesis, self.format_args_parens.open); - builder.extend_with_tt(content); - builder.close(self.format_args_parens.close); - } - - /// Pushes `:: seg :: seg ...`, an absolute path. - fn push_path(&self, builder: &mut tt::TopSubtreeBuilder, path: &[&str]) { - for segment in path { - builder.push(self.punct(':', tt::Spacing::Joint)); - builder.push(self.punct(':', tt::Spacing::Alone)); - builder.push(self.ident(segment)); - } - } - - /// Pushes `( builtin#format_args() )`, the argument list of the wrapping - /// call or method. - fn push_parenthesized_format_args( - &self, - builder: &mut tt::TopSubtreeBuilder, - content: tt::TokenTreesView, - ) { - builder.open(tt::DelimiterKind::Parenthesis, self.call_site); - self.push_format_args(builder, content); - builder.close(self.call_site); - } -} diff --git a/rust/ql/test/.gitignore b/rust/ql/test/.gitignore index eb43358aa293..9ea6b0797852 100644 --- a/rust/ql/test/.gitignore +++ b/rust/ql/test/.gitignore @@ -3,10 +3,6 @@ target/ # these are all generated, see `rust/extractor/src/qltest.rs` for details Cargo.toml /*/**/rust-toolchain.toml -# but this one is committed on purpose: it pins a pre-1.94 toolchain to exercise -# the extractor's `FormatArgsExpr` reconstruction fallback. -!/library-tests/format-macros-legacy/rust-toolchain.toml lib.rs .proc_macro/ .lib/ - diff --git a/rust/ql/test/library-tests/format-macros-legacy/Cargo.lock b/rust/ql/test/library-tests/format-macros-legacy/Cargo.lock deleted file mode 100644 index b9856cfaf77d..000000000000 --- a/rust/ql/test/library-tests/format-macros-legacy/Cargo.lock +++ /dev/null @@ -1,7 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "test" -version = "0.0.1" diff --git a/rust/ql/test/library-tests/format-macros-legacy/FormatArgs.expected b/rust/ql/test/library-tests/format-macros-legacy/FormatArgs.expected deleted file mode 100644 index d8373ec3f9e3..000000000000 --- a/rust/ql/test/library-tests/format-macros-legacy/FormatArgs.expected +++ /dev/null @@ -1,12 +0,0 @@ -| main.rs:13:18:13:24 | FormatArgsExpr | 1 | -| main.rs:16:18:16:22 | FormatArgsExpr | 0 | -| main.rs:19:21:19:35 | FormatArgsExpr | 1 | -| main.rs:27:25:27:33 | FormatArgsExpr | 1 | -| main.rs:38:40:38:46 | FormatArgsExpr | 0 | -| main.rs:43:52:43:58 | FormatArgsExpr | 0 | -| main.rs:49:26:49:32 | FormatArgsExpr | 1 | -| main.rs:54:28:54:32 | FormatArgsExpr | 0 | -| main.rs:64:14:64:26 | FormatArgsExpr | 1 | -| main.rs:65:15:65:25 | FormatArgsExpr | 0 | -| main.rs:66:12:66:24 | FormatArgsExpr | 1 | -| main.rs:67:13:67:23 | FormatArgsExpr | 0 | diff --git a/rust/ql/test/library-tests/format-macros-legacy/FormatArgs.ql b/rust/ql/test/library-tests/format-macros-legacy/FormatArgs.ql deleted file mode 100644 index 77d7ab895bda..000000000000 --- a/rust/ql/test/library-tests/format-macros-legacy/FormatArgs.ql +++ /dev/null @@ -1,8 +0,0 @@ -// Reconstructed `FormatArgsExpr` nodes for the format-family macros. On the -// pinned pre-1.94 toolchain these come entirely from the extractor's -// reconstruction path, so the presence of one node per macro invocation -// (including `write!`/`writeln!`) confirms it fires across the family. -import rust - -from FormatArgsExpr f -select f, f.getNumberOfArgs() diff --git a/rust/ql/test/library-tests/format-macros-legacy/LogInjection.expected b/rust/ql/test/library-tests/format-macros-legacy/LogInjection.expected deleted file mode 100644 index b294b89e83fb..000000000000 --- a/rust/ql/test/library-tests/format-macros-legacy/LogInjection.expected +++ /dev/null @@ -1,28 +0,0 @@ -#select -| main.rs:64:14:64:26 | MacroExpr | main.rs:62:19:62:45 | ...::var(...) | main.rs:64:14:64:26 | MacroExpr | Log entry depends on a $@. | main.rs:62:19:62:45 | ...::var(...) | user-provided value | -| main.rs:65:15:65:25 | MacroExpr | main.rs:62:19:62:45 | ...::var(...) | main.rs:65:15:65:25 | MacroExpr | Log entry depends on a $@. | main.rs:62:19:62:45 | ...::var(...) | user-provided value | -| main.rs:66:12:66:24 | MacroExpr | main.rs:62:19:62:45 | ...::var(...) | main.rs:66:12:66:24 | MacroExpr | Log entry depends on a $@. | main.rs:62:19:62:45 | ...::var(...) | user-provided value | -| main.rs:67:13:67:23 | MacroExpr | main.rs:62:19:62:45 | ...::var(...) | main.rs:67:13:67:23 | MacroExpr | Log entry depends on a $@. | main.rs:62:19:62:45 | ...::var(...) | user-provided value | -edges -| main.rs:62:9:62:15 | tainted | main.rs:64:14:64:26 | MacroExpr | provenance | Sink:MaD:2 | -| main.rs:62:9:62:15 | tainted | main.rs:65:15:65:25 | MacroExpr | provenance | Sink:MaD:1 | -| main.rs:62:9:62:15 | tainted | main.rs:66:12:66:24 | MacroExpr | provenance | Sink:MaD:2 | -| main.rs:62:9:62:15 | tainted | main.rs:67:13:67:23 | MacroExpr | provenance | Sink:MaD:1 | -| main.rs:62:19:62:45 | ...::var(...) | main.rs:62:19:62:45 | ...::var(...) [Ok] | provenance | Src:MaD:3 | -| main.rs:62:19:62:45 | ...::var(...) [Ok] | main.rs:62:19:62:65 | ... .unwrap_or_default() | provenance | MaD:4 | -| main.rs:62:19:62:65 | ... .unwrap_or_default() | main.rs:62:9:62:15 | tainted | provenance | | -models -| 1 | Sink: std::io::stdio::_eprint; Argument[0]; log-injection | -| 2 | Sink: std::io::stdio::_print; Argument[0]; log-injection | -| 3 | Source: std::env::var; ReturnValue.Field[core::result::Result::Ok(0)]; environment | -| 4 | Summary: ::unwrap_or_default; Argument[self].Field[core::result::Result::Ok(0)]; ReturnValue; value | -nodes -| main.rs:62:9:62:15 | tainted | semmle.label | tainted | -| main.rs:62:19:62:45 | ...::var(...) | semmle.label | ...::var(...) | -| main.rs:62:19:62:45 | ...::var(...) [Ok] | semmle.label | ...::var(...) [Ok] | -| main.rs:62:19:62:65 | ... .unwrap_or_default() | semmle.label | ... .unwrap_or_default() | -| main.rs:64:14:64:26 | MacroExpr | semmle.label | MacroExpr | -| main.rs:65:15:65:25 | MacroExpr | semmle.label | MacroExpr | -| main.rs:66:12:66:24 | MacroExpr | semmle.label | MacroExpr | -| main.rs:67:13:67:23 | MacroExpr | semmle.label | MacroExpr | -subpaths diff --git a/rust/ql/test/library-tests/format-macros-legacy/LogInjection.qlref b/rust/ql/test/library-tests/format-macros-legacy/LogInjection.qlref deleted file mode 100644 index 3949abc78143..000000000000 --- a/rust/ql/test/library-tests/format-macros-legacy/LogInjection.qlref +++ /dev/null @@ -1,4 +0,0 @@ -query: queries/security/CWE-117/LogInjection.ql -postprocess: - - utils/test/PrettyPrintModels.ql - - utils/test/InlineExpectationsTestQuery.ql diff --git a/rust/ql/test/library-tests/format-macros-legacy/inline-taint-flow.expected b/rust/ql/test/library-tests/format-macros-legacy/inline-taint-flow.expected deleted file mode 100644 index 97ef77e17760..000000000000 --- a/rust/ql/test/library-tests/format-macros-legacy/inline-taint-flow.expected +++ /dev/null @@ -1,124 +0,0 @@ -models -| 1 | Summary: ::as_str; Argument[self].Reference; ReturnValue.Reference; taint | -| 2 | Summary: ::write_fmt; Argument[0]; Argument[self].Reference; taint | -| 3 | Summary: ::write_str; Argument[0].Reference; Argument[self].Reference; taint | -| 4 | Summary: alloc::fmt::format; Argument[0]; ReturnValue; taint | -| 5 | Summary: core::fmt::write; Argument[1]; Argument[0].Reference; taint | -| 6 | Summary: core::hint::must_use; Argument[0]; ReturnValue; value | -edges -| main.rs:12:9:12:9 | a | main.rs:13:18:13:24 | MacroExpr | provenance | | -| main.rs:12:13:12:21 | source(...) | main.rs:12:9:12:9 | a | provenance | | -| main.rs:13:18:13:24 | ...::format(...) | main.rs:13:18:13:24 | { ... } | provenance | | -| main.rs:13:18:13:24 | ...::must_use(...) | main.rs:13:10:13:25 | MacroExpr | provenance | | -| main.rs:13:18:13:24 | MacroExpr | main.rs:13:18:13:24 | ...::format(...) | provenance | MaD:4 | -| main.rs:13:18:13:24 | { ... } | main.rs:13:18:13:24 | ...::must_use(...) | provenance | MaD:6 | -| main.rs:15:9:15:9 | b | main.rs:16:18:16:22 | MacroExpr | provenance | | -| main.rs:15:13:15:21 | source(...) | main.rs:15:9:15:9 | b | provenance | | -| main.rs:16:18:16:22 | ...::format(...) | main.rs:16:18:16:22 | { ... } | provenance | | -| main.rs:16:18:16:22 | ...::must_use(...) | main.rs:16:10:16:23 | MacroExpr | provenance | | -| main.rs:16:18:16:22 | MacroExpr | main.rs:16:18:16:22 | ...::format(...) | provenance | MaD:4 | -| main.rs:16:18:16:22 | { ... } | main.rs:16:18:16:22 | ...::must_use(...) | provenance | MaD:6 | -| main.rs:18:9:18:9 | c | main.rs:19:21:19:35 | MacroExpr | provenance | | -| main.rs:18:13:18:21 | source(...) | main.rs:18:9:18:9 | c | provenance | | -| main.rs:19:9:19:9 | s | main.rs:20:10:20:10 | s | provenance | | -| main.rs:19:21:19:35 | ...::format(...) | main.rs:19:21:19:35 | { ... } | provenance | | -| main.rs:19:21:19:35 | ...::must_use(...) | main.rs:19:9:19:9 | s | provenance | | -| main.rs:19:21:19:35 | MacroExpr | main.rs:19:21:19:35 | ...::format(...) | provenance | MaD:4 | -| main.rs:19:21:19:35 | { ... } | main.rs:19:21:19:35 | ...::must_use(...) | provenance | MaD:6 | -| main.rs:26:9:26:9 | a | main.rs:27:9:27:9 | b | provenance | | -| main.rs:26:13:26:21 | source(...) | main.rs:26:9:26:9 | a | provenance | | -| main.rs:27:9:27:9 | b | main.rs:28:30:28:30 | b | provenance | | -| main.rs:28:9:28:9 | c | main.rs:29:10:29:10 | c | provenance | | -| main.rs:28:13:28:31 | ...::format(...) | main.rs:28:9:28:9 | c | provenance | | -| main.rs:28:30:28:30 | b | main.rs:28:13:28:31 | ...::format(...) | provenance | MaD:4 | -| main.rs:32:9:32:9 | d | main.rs:33:28:33:28 | d | provenance | | -| main.rs:32:13:32:21 | source(...) | main.rs:32:9:32:9 | d | provenance | | -| main.rs:33:13:33:16 | [post] buf1 | main.rs:34:10:34:13 | buf1 | provenance | | -| main.rs:33:28:33:28 | d | main.rs:33:28:33:37 | d.as_str() [&ref] | provenance | MaD:1 | -| main.rs:33:28:33:37 | d.as_str() [&ref] | main.rs:33:13:33:16 | [post] buf1 | provenance | MaD:3 | -| main.rs:37:9:37:9 | e | main.rs:38:28:38:46 | MacroExpr | provenance | | -| main.rs:37:13:37:21 | source(...) | main.rs:37:9:37:9 | e | provenance | | -| main.rs:38:13:38:16 | [post] buf2 | main.rs:39:10:39:13 | buf2 | provenance | | -| main.rs:38:28:38:46 | MacroExpr | main.rs:38:13:38:16 | [post] buf2 | provenance | MaD:2 | -| main.rs:42:9:42:9 | f | main.rs:43:40:43:58 | MacroExpr | provenance | | -| main.rs:42:13:42:21 | source(...) | main.rs:42:9:42:9 | f | provenance | | -| main.rs:43:29:43:37 | [post] &mut buf3 [&ref] | main.rs:43:34:43:37 | [post] buf3 | provenance | | -| main.rs:43:34:43:37 | [post] buf3 | main.rs:44:10:44:13 | buf3 | provenance | | -| main.rs:43:40:43:58 | MacroExpr | main.rs:43:29:43:37 | [post] &mut buf3 [&ref] | provenance | MaD:5 | -| main.rs:48:9:48:9 | g | main.rs:49:26:49:32 | MacroExpr | provenance | | -| main.rs:48:13:48:21 | source(...) | main.rs:48:9:48:9 | g | provenance | | -| main.rs:49:20:49:23 | [post] buf4 | main.rs:50:10:50:13 | buf4 | provenance | | -| main.rs:49:26:49:32 | MacroExpr | main.rs:49:20:49:23 | [post] buf4 | provenance | MaD:2 | -| main.rs:53:9:53:9 | h | main.rs:54:28:54:32 | MacroExpr | provenance | | -| main.rs:53:13:53:21 | source(...) | main.rs:53:9:53:9 | h | provenance | | -| main.rs:54:22:54:25 | [post] buf5 | main.rs:55:10:55:13 | buf5 | provenance | | -| main.rs:54:28:54:32 | MacroExpr | main.rs:54:22:54:25 | [post] buf5 | provenance | MaD:2 | -nodes -| main.rs:12:9:12:9 | a | semmle.label | a | -| main.rs:12:13:12:21 | source(...) | semmle.label | source(...) | -| main.rs:13:10:13:25 | MacroExpr | semmle.label | MacroExpr | -| main.rs:13:18:13:24 | ...::format(...) | semmle.label | ...::format(...) | -| main.rs:13:18:13:24 | ...::must_use(...) | semmle.label | ...::must_use(...) | -| main.rs:13:18:13:24 | MacroExpr | semmle.label | MacroExpr | -| main.rs:13:18:13:24 | { ... } | semmle.label | { ... } | -| main.rs:15:9:15:9 | b | semmle.label | b | -| main.rs:15:13:15:21 | source(...) | semmle.label | source(...) | -| main.rs:16:10:16:23 | MacroExpr | semmle.label | MacroExpr | -| main.rs:16:18:16:22 | ...::format(...) | semmle.label | ...::format(...) | -| main.rs:16:18:16:22 | ...::must_use(...) | semmle.label | ...::must_use(...) | -| main.rs:16:18:16:22 | MacroExpr | semmle.label | MacroExpr | -| main.rs:16:18:16:22 | { ... } | semmle.label | { ... } | -| main.rs:18:9:18:9 | c | semmle.label | c | -| main.rs:18:13:18:21 | source(...) | semmle.label | source(...) | -| main.rs:19:9:19:9 | s | semmle.label | s | -| main.rs:19:21:19:35 | ...::format(...) | semmle.label | ...::format(...) | -| main.rs:19:21:19:35 | ...::must_use(...) | semmle.label | ...::must_use(...) | -| main.rs:19:21:19:35 | MacroExpr | semmle.label | MacroExpr | -| main.rs:19:21:19:35 | { ... } | semmle.label | { ... } | -| main.rs:20:10:20:10 | s | semmle.label | s | -| main.rs:26:9:26:9 | a | semmle.label | a | -| main.rs:26:13:26:21 | source(...) | semmle.label | source(...) | -| main.rs:27:9:27:9 | b | semmle.label | b | -| main.rs:28:9:28:9 | c | semmle.label | c | -| main.rs:28:13:28:31 | ...::format(...) | semmle.label | ...::format(...) | -| main.rs:28:30:28:30 | b | semmle.label | b | -| main.rs:29:10:29:10 | c | semmle.label | c | -| main.rs:32:9:32:9 | d | semmle.label | d | -| main.rs:32:13:32:21 | source(...) | semmle.label | source(...) | -| main.rs:33:13:33:16 | [post] buf1 | semmle.label | [post] buf1 | -| main.rs:33:28:33:28 | d | semmle.label | d | -| main.rs:33:28:33:37 | d.as_str() [&ref] | semmle.label | d.as_str() [&ref] | -| main.rs:34:10:34:13 | buf1 | semmle.label | buf1 | -| main.rs:37:9:37:9 | e | semmle.label | e | -| main.rs:37:13:37:21 | source(...) | semmle.label | source(...) | -| main.rs:38:13:38:16 | [post] buf2 | semmle.label | [post] buf2 | -| main.rs:38:28:38:46 | MacroExpr | semmle.label | MacroExpr | -| main.rs:39:10:39:13 | buf2 | semmle.label | buf2 | -| main.rs:42:9:42:9 | f | semmle.label | f | -| main.rs:42:13:42:21 | source(...) | semmle.label | source(...) | -| main.rs:43:29:43:37 | [post] &mut buf3 [&ref] | semmle.label | [post] &mut buf3 [&ref] | -| main.rs:43:34:43:37 | [post] buf3 | semmle.label | [post] buf3 | -| main.rs:43:40:43:58 | MacroExpr | semmle.label | MacroExpr | -| main.rs:44:10:44:13 | buf3 | semmle.label | buf3 | -| main.rs:48:9:48:9 | g | semmle.label | g | -| main.rs:48:13:48:21 | source(...) | semmle.label | source(...) | -| main.rs:49:20:49:23 | [post] buf4 | semmle.label | [post] buf4 | -| main.rs:49:26:49:32 | MacroExpr | semmle.label | MacroExpr | -| main.rs:50:10:50:13 | buf4 | semmle.label | buf4 | -| main.rs:53:9:53:9 | h | semmle.label | h | -| main.rs:53:13:53:21 | source(...) | semmle.label | source(...) | -| main.rs:54:22:54:25 | [post] buf5 | semmle.label | [post] buf5 | -| main.rs:54:28:54:32 | MacroExpr | semmle.label | MacroExpr | -| main.rs:55:10:55:13 | buf5 | semmle.label | buf5 | -subpaths -testFailures -#select -| main.rs:13:10:13:25 | MacroExpr | main.rs:12:13:12:21 | source(...) | main.rs:13:10:13:25 | MacroExpr | $@ | main.rs:12:13:12:21 | source(...) | source(...) | -| main.rs:16:10:16:23 | MacroExpr | main.rs:15:13:15:21 | source(...) | main.rs:16:10:16:23 | MacroExpr | $@ | main.rs:15:13:15:21 | source(...) | source(...) | -| main.rs:20:10:20:10 | s | main.rs:18:13:18:21 | source(...) | main.rs:20:10:20:10 | s | $@ | main.rs:18:13:18:21 | source(...) | source(...) | -| main.rs:29:10:29:10 | c | main.rs:26:13:26:21 | source(...) | main.rs:29:10:29:10 | c | $@ | main.rs:26:13:26:21 | source(...) | source(...) | -| main.rs:34:10:34:13 | buf1 | main.rs:32:13:32:21 | source(...) | main.rs:34:10:34:13 | buf1 | $@ | main.rs:32:13:32:21 | source(...) | source(...) | -| main.rs:39:10:39:13 | buf2 | main.rs:37:13:37:21 | source(...) | main.rs:39:10:39:13 | buf2 | $@ | main.rs:37:13:37:21 | source(...) | source(...) | -| main.rs:44:10:44:13 | buf3 | main.rs:42:13:42:21 | source(...) | main.rs:44:10:44:13 | buf3 | $@ | main.rs:42:13:42:21 | source(...) | source(...) | -| main.rs:50:10:50:13 | buf4 | main.rs:48:13:48:21 | source(...) | main.rs:50:10:50:13 | buf4 | $@ | main.rs:48:13:48:21 | source(...) | source(...) | -| main.rs:55:10:55:13 | buf5 | main.rs:53:13:53:21 | source(...) | main.rs:55:10:55:13 | buf5 | $@ | main.rs:53:13:53:21 | source(...) | source(...) | diff --git a/rust/ql/test/library-tests/format-macros-legacy/inline-taint-flow.ql b/rust/ql/test/library-tests/format-macros-legacy/inline-taint-flow.ql deleted file mode 100644 index 5dcb7ee70a9d..000000000000 --- a/rust/ql/test/library-tests/format-macros-legacy/inline-taint-flow.ql +++ /dev/null @@ -1,12 +0,0 @@ -/** - * @kind path-problem - */ - -import rust -import utils.test.InlineFlowTest -import DefaultFlowTest -import TaintFlow::PathGraph - -from TaintFlow::PathNode source, TaintFlow::PathNode sink -where TaintFlow::flowPath(source, sink) -select sink, source, sink, "$@", source, source.toString() diff --git a/rust/ql/test/library-tests/format-macros-legacy/main.rs b/rust/ql/test/library-tests/format-macros-legacy/main.rs deleted file mode 100644 index 641391211618..000000000000 --- a/rust/ql/test/library-tests/format-macros-legacy/main.rs +++ /dev/null @@ -1,74 +0,0 @@ -// Verifies that dataflow through the format-family macros is recovered on -// pre-1.94 toolchains, where rust-analyzer no longer expands them and the -// extractor reconstructs the `FormatArgsExpr` (see `rust-toolchain.toml`). - -fn source(i: i64) -> String { - i.to_string() -} - -fn sink(_s: String) {} - -pub fn format_flow() { - let a = source(1); - sink(format!("{}", a)); // $ hasTaintFlow=1 - - let b = source(2); - sink(format!("{b}")); // $ hasTaintFlow=2 - - let c = source(3); - let s = format!("x={c} y={}", c); - sink(s); // $ hasTaintFlow=3 -} - -pub fn exercises_reconstruction() { - // these exercise reconstruction of the rest of the family, including the - // writer-argument handling of `write!`/`writeln!`. - let a = source(4); - let b = format_args!("{}", a); - let c = std::fmt::format(b); - sink(c); // $ hasTaintFlow=4 - - let mut buf1 = String::new(); - let d = source(5); - let _ = buf1.write_str(d.as_str()); - sink(buf1); // $ hasTaintFlow=5 - - let mut buf2 = String::new(); - let e = source(6); - let _ = buf2.write_fmt(format_args!("{e}")); - sink(buf2); // $ hasTaintFlow=6 - - let mut buf3 = String::new(); - let f = source(7); - let _ = std::fmt::write(&mut buf3, format_args!("{f}")); - sink(buf3); // $ hasTaintFlow=7 - - use std::fmt::Write; - let mut buf4 = String::new(); - let g = source(8); - let _ = write!(buf4, "{}", g); - sink(buf4); // $ hasTaintFlow=8 - - let mut buf5 = String::new(); - let h = source(9); - let _ = writeln!(buf5, "{h}"); - sink(buf5); // $ hasTaintFlow=9 -} - -// The log-injection sinks (`println!`/`eprintln!`/`panic!`) are reconstructed -// into their real callees (`_print`/`_eprint`/`panic_fmt`), so the manual sink -// models keep firing on <1.94 exactly as they do on native expansions. -pub fn log_injection_sinks() { - let tainted = std::env::var("USER_INPUT").unwrap_or_default(); // $ Source=environment - - println!("{}", tainted); // $ Alert[rust/log-injection]=environment - eprintln!("{tainted}"); // $ Alert[rust/log-injection]=environment - print!("{}", tainted); // $ Alert[rust/log-injection]=environment - eprint!("{tainted}"); // $ Alert[rust/log-injection]=environment -} - -fn main() { - format_flow(); - exercises_reconstruction(); - log_injection_sinks(); -} diff --git a/rust/ql/test/library-tests/format-macros-legacy/rust-toolchain.toml b/rust/ql/test/library-tests/format-macros-legacy/rust-toolchain.toml deleted file mode 100644 index da4efb0b5b53..000000000000 --- a/rust/ql/test/library-tests/format-macros-legacy/rust-toolchain.toml +++ /dev/null @@ -1,14 +0,0 @@ -# Pinned to a pre-1.94 toolchain on purpose. -# -# rust-analyzer 0.0.347 only expands the builtin `format_args!` machinery against -# a std that carries the new lowering (roughly >= 1.94). On older toolchains the -# format-family macros (`format!`, `println!`, `write!`, ...) fail to expand, so -# the extractor reconstructs the `FormatArgsExpr` itself. This test exercises that -# reconstruction path, which the default test toolchain never hits. -# -# Any toolchain named here must also be pre-installed in `../setup.sh`, otherwise -# the parallel QL tests race on `rustup` auto-install. -[toolchain] -channel = "1.93" -profile = "minimal" -components = [ "rust-src" ] diff --git a/rust/ql/test/setup.sh b/rust/ql/test/setup.sh index 24fa06a70e44..f3cf992e0567 100755 --- a/rust/ql/test/setup.sh +++ b/rust/ql/test/setup.sh @@ -14,9 +14,5 @@ rustup toolchain install 1.97.0 --profile minimal --component rust-src pushd ../../extractor/src/nightly-toolchain rustup install popd -# pre-1.94 toolchain exercising the extractor's `FormatArgsExpr` reconstruction -pushd library-tests/format-macros-legacy -rustup install -popd # this needs to be last to set the default toolchain rustup install