diff --git a/Cargo.lock b/Cargo.lock index 2dc6e8ff10320..e527318166308 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4042,6 +4042,7 @@ dependencies = [ "rustc_parse_format", "rustc_session", "rustc_span", + "rustc_symbol_mangling", "rustc_target", "rustc_trait_selection", "smallvec", diff --git a/compiler/rustc_lint/Cargo.toml b/compiler/rustc_lint/Cargo.toml index 3a50aac50cb3e..7c86d2eea9620 100644 --- a/compiler/rustc_lint/Cargo.toml +++ b/compiler/rustc_lint/Cargo.toml @@ -22,6 +22,7 @@ rustc_middle = { path = "../rustc_middle" } rustc_parse_format = { path = "../rustc_parse_format" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } +rustc_symbol_mangling = { path = "../rustc_symbol_mangling" } rustc_target = { path = "../rustc_target" } rustc_trait_selection = { path = "../rustc_trait_selection" } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 417e5a97069c3..508df77639e3e 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -195,6 +195,11 @@ lint_cfg_attr_no_attributes = lint_check_name_unknown_tool = unknown lint tool: `{$tool_name}` +lint_redefining_runtime_symbols = redefinition of the runtime `{$symbol_name}` symbol used by the standard library + .match_exactly = extra care must be taken when redefining those symbols, they must match exactly (ABI, function arguments, function return type, behavior, ...) + .learn_more = see for the more details + .help = either allow this lint or remove any `#[unsafe(no_mangle)]` or `#[unsafe(export_name = "{$symbol_name}")]` + lint_closure_returning_async_block = closure returning async block can be made into an async closure .label = this async block can be removed, and the closure can be turned into an async closure .suggestion = turn this into an async closure diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 9bb53fea54a18..e2a5fe0524e34 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -70,6 +70,7 @@ mod pass_by_value; mod passes; mod precedence; mod ptr_nulls; +mod redefining_runtime_symbols; mod redundant_semicolon; mod reference_casting; mod shadowed_into_iter; @@ -110,6 +111,7 @@ use opaque_hidden_inferred_bound::*; use pass_by_value::*; use precedence::*; use ptr_nulls::*; +use redefining_runtime_symbols::*; use redundant_semicolon::*; use reference_casting::*; use rustc_hir::def_id::LocalModDefId; @@ -240,6 +242,7 @@ late_lint_methods!( AsyncClosureUsage: AsyncClosureUsage, AsyncFnInTrait: AsyncFnInTrait, NonLocalDefinitions: NonLocalDefinitions::default(), + RedefiningRuntimeSymbols: RedefiningRuntimeSymbols, ImplTraitOvercaptures: ImplTraitOvercaptures, IfLetRescope: IfLetRescope::default(), StaticMutRefs: StaticMutRefs, diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 56d65ed08f9e3..3889f2d2e3cce 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -705,6 +705,16 @@ pub(crate) enum UseLetUnderscoreIgnoreSuggestion { }, } +// redefining_runtime_symbols.rs +#[derive(LintDiagnostic)] +#[diag(lint_redefining_runtime_symbols)] +#[note(lint_match_exactly)] +#[note(lint_learn_more)] +#[help] +pub(crate) struct RedefiningRuntimeSymbolsDiag { + pub symbol_name: String, +} + // drop_forget_useless.rs #[derive(LintDiagnostic)] #[diag(lint_dropping_references)] diff --git a/compiler/rustc_lint/src/redefining_runtime_symbols.rs b/compiler/rustc_lint/src/redefining_runtime_symbols.rs new file mode 100644 index 0000000000000..450ab0981b16b --- /dev/null +++ b/compiler/rustc_lint/src/redefining_runtime_symbols.rs @@ -0,0 +1,72 @@ +use rustc_hir as hir; +use rustc_session::{declare_lint, declare_lint_pass}; + +use crate::lints::RedefiningRuntimeSymbolsDiag; +use crate::{LateContext, LateLintPass, LintContext}; + +declare_lint! { + /// The `redefining_runtime_symbols` lint checks for items whose symbol name redefines + /// a runtime symbols expected by `core` and/or `std`. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(redefining_runtime_symbols)] + /// + /// #[unsafe(no_mangle)] + /// pub fn strlen() {} // redefines the libc `strlen` function + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Up-most care is required when redefining runtime symbols assumed and + /// used by the standard library. They must follow the C specification, not use any + /// standard-library facility or undefined behavior may occur. + /// + /// The symbols currently checked are respectively: + /// - from `core`[^1]: `memcpy`, `memmove`, `memset`, `memcmp`, `bcmp`, `strlen` + /// - from `std`: `open`/`open64`, `read`, `write`, `close` + /// + /// [^1]: https://doc.rust-lang.org/core/index.html#how-to-use-the-core-library + pub REDEFINING_RUNTIME_SYMBOLS, + Warn, + "redefining a symbol used by the standard library" +} + +declare_lint_pass!(RedefiningRuntimeSymbols => [REDEFINING_RUNTIME_SYMBOLS]); + +static CORE_RUNTIME_SYMBOLS: &[&str] = &["memcpy", "memmove", "memset", "memcmp", "bcmp", "strlen"]; + +static STD_RUNTIME_SYMBOLS: &[&str] = &["open", "open64", "read", "write", "close"]; + +impl<'tcx> LateLintPass<'tcx> for RedefiningRuntimeSymbols { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { + // Bail-out if the item is not a function/method or static. + match item.kind { + hir::ItemKind::Fn { sig: _, ident: _, generics: _, body: _, has_body: true } + | hir::ItemKind::Static(..) => {} + _ => return, + } + + // Compute the symbol name of our item (without mangling, as our mangling cannot ever + // conflict with runtime symbols). + let Some(symbol_name) = rustc_symbol_mangling::symbol_name_from_attrs( + cx.tcx, + rustc_middle::ty::InstanceKind::Item(item.owner_id.to_def_id()), + ) else { + return; + }; + + if CORE_RUNTIME_SYMBOLS.contains(&&*symbol_name) + || STD_RUNTIME_SYMBOLS.contains(&&*symbol_name) + { + cx.emit_span_lint( + REDEFINING_RUNTIME_SYMBOLS, + item.span, + RedefiningRuntimeSymbolsDiag { symbol_name }, + ); + } + } +} diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs index d97ee95652530..76e3d29317263 100644 --- a/compiler/rustc_symbol_mangling/src/lib.rs +++ b/compiler/rustc_symbol_mangling/src/lib.rs @@ -100,7 +100,7 @@ use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; use rustc_middle::mir::mono::{InstantiationMode, MonoItem}; use rustc_middle::query::Providers; -use rustc_middle::ty::{self, Instance, TyCtxt}; +use rustc_middle::ty::{self, Instance, InstanceKind, TyCtxt}; use rustc_session::config::SymbolManglingVersion; use tracing::debug; @@ -158,29 +158,22 @@ pub fn typeid_for_trait_ref<'tcx>( v0::mangle_typeid_for_trait_ref(tcx, trait_ref) } -/// Computes the symbol name for the given instance. This function will call -/// `compute_instantiating_crate` if it needs to factor the instantiating crate -/// into the symbol name. -fn compute_symbol_name<'tcx>( +pub fn symbol_name_from_attrs<'tcx>( tcx: TyCtxt<'tcx>, - instance: Instance<'tcx>, - compute_instantiating_crate: impl FnOnce() -> CrateNum, -) -> String { - let def_id = instance.def_id(); - let args = instance.args; - - debug!("symbol_name(def_id={:?}, args={:?})", def_id, args); + instance_kind: InstanceKind<'tcx>, +) -> Option { + let def_id = instance_kind.def_id(); if let Some(def_id) = def_id.as_local() { if tcx.proc_macro_decls_static(()) == Some(def_id) { let stable_crate_id = tcx.stable_crate_id(LOCAL_CRATE); - return tcx.sess.generate_proc_macro_decls_symbol(stable_crate_id); + return Some(tcx.sess.generate_proc_macro_decls_symbol(stable_crate_id)); } } // FIXME(eddyb) Precompute a custom symbol name based on attributes. let attrs = if tcx.def_kind(def_id).has_codegen_attrs() { - &tcx.codegen_instance_attrs(instance.def) + &tcx.codegen_instance_attrs(instance_kind) } else { CodegenFnAttrs::EMPTY }; @@ -206,7 +199,7 @@ fn compute_symbol_name<'tcx>( // legacy symbol mangling scheme. let name = if let Some(name) = attrs.symbol_name { name } else { tcx.item_name(def_id) }; - return v0::mangle_internal_symbol(tcx, name.as_str()); + return Some(v0::mangle_internal_symbol(tcx, name.as_str())); } let wasm_import_module_exception_force_mangling = { @@ -234,15 +227,35 @@ fn compute_symbol_name<'tcx>( if !wasm_import_module_exception_force_mangling { if let Some(name) = attrs.symbol_name { // Use provided name - return name.to_string(); + return Some(name.to_string()); } if attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE) { // Don't mangle - return tcx.item_name(def_id).to_string(); + return Some(tcx.item_name(def_id).to_string()); } } + None +} + +/// Computes the symbol name for the given instance. This function will call +/// `compute_instantiating_crate` if it needs to factor the instantiating crate +/// into the symbol name. +fn compute_symbol_name<'tcx>( + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, + compute_instantiating_crate: impl FnOnce() -> CrateNum, +) -> String { + let def_id = instance.def_id(); + let args = instance.args; + + debug!("symbol_name(def_id={:?}, args={:?})", def_id, args); + + if let Some(symbol) = symbol_name_from_attrs(tcx, instance.def) { + return symbol; + } + // If we're dealing with an instance of a function that's inlined from // another crate but we're marking it as globally shared to our // compilation (aka we're not making an internal copy in each of our diff --git a/tests/ui/lint/redefining-runtime-symbols.rs b/tests/ui/lint/redefining-runtime-symbols.rs new file mode 100644 index 0000000000000..25f433fc77c00 --- /dev/null +++ b/tests/ui/lint/redefining-runtime-symbols.rs @@ -0,0 +1,63 @@ +//@ check-pass +//@ edition: 2021 + +use std::ffi::c_void; + +// From core + +#[no_mangle] +pub extern "C" fn memcpy( + dest: *mut c_void, + src: *const c_void, + n: i64, +) -> *mut c_void { std::ptr::null_mut() } +//~^^^^^ WARN redefinition of the runtime `memcpy` symbol + +#[no_mangle] +pub fn memmove() {} +//~^ WARN redefinition of the runtime `memmove` symbol + +#[no_mangle] +pub fn memset() {} +//~^ WARN redefinition of the runtime `memset` symbol + +#[no_mangle] +pub fn memcmp() {} +//~^ WARN redefinition of the runtime `memcmp` symbol + +#[export_name = "bcmp"] +pub fn bcmp_() {} +//~^ WARN redefinition of the runtime `bcmp` symbol + +#[no_mangle] +pub static strlen: () = (); +//~^ WARN redefinition of the runtime `strlen` symbol + +// From std + +#[no_mangle] +pub fn open() {} +//~^ WARN redefinition of the runtime `open` symbol + +#[no_mangle] +pub fn open64() {} +//~^ WARN redefinition of the runtime `open64` symbol + +#[export_name = "read"] +pub async fn read1() {} +//~^ WARN redefinition of the runtime `read` symbol + +#[export_name = "write"] +pub fn write1() {} +//~^ WARN redefinition of the runtime `write` symbol + +#[export_name = "close"] +pub fn close_() {} +//~^ WARN redefinition of the runtime `close` symbol + +extern "C" { + // No warning, not a body. + pub fn close(a: i32) -> i32; +} + +fn main() {} diff --git a/tests/ui/lint/redefining-runtime-symbols.stderr b/tests/ui/lint/redefining-runtime-symbols.stderr new file mode 100644 index 0000000000000..ae4314c98a0b2 --- /dev/null +++ b/tests/ui/lint/redefining-runtime-symbols.stderr @@ -0,0 +1,117 @@ +warning: redefinition of the runtime `memcpy` symbol used by the standard library + --> $DIR/redefining-runtime-symbols.rs:9:1 + | +LL | / pub extern "C" fn memcpy( +LL | | dest: *mut c_void, +LL | | src: *const c_void, +LL | | n: i64, +LL | | ) -> *mut c_void { std::ptr::null_mut() } + | |_________________________________________^ + | + = note: extra care must be taken when redefining those symbols, they must match exactly (ABI, function arguments, function return type, behavior, ...) + = note: see for the more details + = help: either allow this lint or remove any `#[unsafe(no_mangle)]` or `#[unsafe(export_name = "memcpy")]` + = note: `#[warn(redefining_runtime_symbols)]` on by default + +warning: redefinition of the runtime `memmove` symbol used by the standard library + --> $DIR/redefining-runtime-symbols.rs:17:1 + | +LL | pub fn memmove() {} + | ^^^^^^^^^^^^^^^^^^^ + | + = note: extra care must be taken when redefining those symbols, they must match exactly (ABI, function arguments, function return type, behavior, ...) + = note: see for the more details + = help: either allow this lint or remove any `#[unsafe(no_mangle)]` or `#[unsafe(export_name = "memmove")]` + +warning: redefinition of the runtime `memset` symbol used by the standard library + --> $DIR/redefining-runtime-symbols.rs:21:1 + | +LL | pub fn memset() {} + | ^^^^^^^^^^^^^^^^^^ + | + = note: extra care must be taken when redefining those symbols, they must match exactly (ABI, function arguments, function return type, behavior, ...) + = note: see for the more details + = help: either allow this lint or remove any `#[unsafe(no_mangle)]` or `#[unsafe(export_name = "memset")]` + +warning: redefinition of the runtime `memcmp` symbol used by the standard library + --> $DIR/redefining-runtime-symbols.rs:25:1 + | +LL | pub fn memcmp() {} + | ^^^^^^^^^^^^^^^^^^ + | + = note: extra care must be taken when redefining those symbols, they must match exactly (ABI, function arguments, function return type, behavior, ...) + = note: see for the more details + = help: either allow this lint or remove any `#[unsafe(no_mangle)]` or `#[unsafe(export_name = "memcmp")]` + +warning: redefinition of the runtime `bcmp` symbol used by the standard library + --> $DIR/redefining-runtime-symbols.rs:29:1 + | +LL | pub fn bcmp_() {} + | ^^^^^^^^^^^^^^^^^ + | + = note: extra care must be taken when redefining those symbols, they must match exactly (ABI, function arguments, function return type, behavior, ...) + = note: see for the more details + = help: either allow this lint or remove any `#[unsafe(no_mangle)]` or `#[unsafe(export_name = "bcmp")]` + +warning: redefinition of the runtime `strlen` symbol used by the standard library + --> $DIR/redefining-runtime-symbols.rs:33:1 + | +LL | pub static strlen: () = (); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: extra care must be taken when redefining those symbols, they must match exactly (ABI, function arguments, function return type, behavior, ...) + = note: see for the more details + = help: either allow this lint or remove any `#[unsafe(no_mangle)]` or `#[unsafe(export_name = "strlen")]` + +warning: redefinition of the runtime `open` symbol used by the standard library + --> $DIR/redefining-runtime-symbols.rs:39:1 + | +LL | pub fn open() {} + | ^^^^^^^^^^^^^^^^ + | + = note: extra care must be taken when redefining those symbols, they must match exactly (ABI, function arguments, function return type, behavior, ...) + = note: see for the more details + = help: either allow this lint or remove any `#[unsafe(no_mangle)]` or `#[unsafe(export_name = "open")]` + +warning: redefinition of the runtime `open64` symbol used by the standard library + --> $DIR/redefining-runtime-symbols.rs:43:1 + | +LL | pub fn open64() {} + | ^^^^^^^^^^^^^^^^^^ + | + = note: extra care must be taken when redefining those symbols, they must match exactly (ABI, function arguments, function return type, behavior, ...) + = note: see for the more details + = help: either allow this lint or remove any `#[unsafe(no_mangle)]` or `#[unsafe(export_name = "open64")]` + +warning: redefinition of the runtime `read` symbol used by the standard library + --> $DIR/redefining-runtime-symbols.rs:47:1 + | +LL | pub async fn read1() {} + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: extra care must be taken when redefining those symbols, they must match exactly (ABI, function arguments, function return type, behavior, ...) + = note: see for the more details + = help: either allow this lint or remove any `#[unsafe(no_mangle)]` or `#[unsafe(export_name = "read")]` + +warning: redefinition of the runtime `write` symbol used by the standard library + --> $DIR/redefining-runtime-symbols.rs:51:1 + | +LL | pub fn write1() {} + | ^^^^^^^^^^^^^^^^^^ + | + = note: extra care must be taken when redefining those symbols, they must match exactly (ABI, function arguments, function return type, behavior, ...) + = note: see for the more details + = help: either allow this lint or remove any `#[unsafe(no_mangle)]` or `#[unsafe(export_name = "write")]` + +warning: redefinition of the runtime `close` symbol used by the standard library + --> $DIR/redefining-runtime-symbols.rs:55:1 + | +LL | pub fn close_() {} + | ^^^^^^^^^^^^^^^^^^ + | + = note: extra care must be taken when redefining those symbols, they must match exactly (ABI, function arguments, function return type, behavior, ...) + = note: see for the more details + = help: either allow this lint or remove any `#[unsafe(no_mangle)]` or `#[unsafe(export_name = "close")]` + +warning: 11 warnings emitted +