Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4042,6 +4042,7 @@ dependencies = [
"rustc_parse_format",
"rustc_session",
"rustc_span",
"rustc_symbol_mangling",
"rustc_target",
"rustc_trait_selection",
"smallvec",
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_lint/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"] }
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_lint/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -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 <https://doc.rust-lang.org/core/index.html#how-to-use-the-core-library> 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
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_lint/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -240,6 +242,7 @@ late_lint_methods!(
AsyncClosureUsage: AsyncClosureUsage,
AsyncFnInTrait: AsyncFnInTrait,
NonLocalDefinitions: NonLocalDefinitions::default(),
RedefiningRuntimeSymbols: RedefiningRuntimeSymbols,
ImplTraitOvercaptures: ImplTraitOvercaptures,
IfLetRescope: IfLetRescope::default(),
StaticMutRefs: StaticMutRefs,
Expand Down
10 changes: 10 additions & 0 deletions compiler/rustc_lint/src/lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
72 changes: 72 additions & 0 deletions compiler/rustc_lint/src/redefining_runtime_symbols.rs
Original file line number Diff line number Diff line change
@@ -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`: `read`, `write`, `open`, `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", "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_without_mangling(
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 },
);
}
}
}
47 changes: 30 additions & 17 deletions compiler/rustc_symbol_mangling/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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_without_mangling<'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<String> {
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
};
Expand All @@ -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 = {
Expand Down Expand Up @@ -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_without_mangling(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
Expand Down
59 changes: 59 additions & 0 deletions tests/ui/lint/redefining-runtime-symbols.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
//@ 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

#[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() {}
Loading
Loading