Skip to content
Open
Show file tree
Hide file tree
Changes from all 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`: `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"];
Comment on lines +28 to +42
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the significance of documenting a split between core and std? It looks like they get checked the same.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not very important; it's mainly a way to distinguish between explicitly documented symbols (those in core) and those that are more implicit (those in std).

Regarding the checking, it could be asked to not check the std runtime symbols in #![no_std], which I don't think we should do, #![no_std] crates may still end-up in std-full crates.


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 },
);
}
}
}
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_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<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_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
Expand Down
63 changes: 63 additions & 0 deletions tests/ui/lint/redefining-runtime-symbols.rs
Original file line number Diff line number Diff line change
@@ -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() {}
Loading
Loading