Skip to content

Commit 24de5ec

Browse files
committed
Add lint warn about clashing function names with fundamental functions
1 parent 9642c0e commit 24de5ec

File tree

9 files changed

+287
-17
lines changed

9 files changed

+287
-17
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4042,6 +4042,7 @@ dependencies = [
40424042
"rustc_parse_format",
40434043
"rustc_session",
40444044
"rustc_span",
4045+
"rustc_symbol_mangling",
40454046
"rustc_target",
40464047
"rustc_trait_selection",
40474048
"smallvec",

compiler/rustc_lint/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ rustc_middle = { path = "../rustc_middle" }
2222
rustc_parse_format = { path = "../rustc_parse_format" }
2323
rustc_session = { path = "../rustc_session" }
2424
rustc_span = { path = "../rustc_span" }
25+
rustc_symbol_mangling = { path = "../rustc_symbol_mangling" }
2526
rustc_target = { path = "../rustc_target" }
2627
rustc_trait_selection = { path = "../rustc_trait_selection" }
2728
smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }

compiler/rustc_lint/messages.ftl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,11 @@ lint_cfg_attr_no_attributes =
195195
196196
lint_check_name_unknown_tool = unknown lint tool: `{$tool_name}`
197197
198+
lint_clashing_function_names_with_fundamental_functions = this function symbol name `{$symbol_name}` clashes with the fundamental functions expected with `core` and `std`
199+
.match_exactly = extra care must be taken when exposing a function with those symbol names, they must match exactly (ABI, function arguments, function return type, behavior, ...)
200+
.learn_more = see <https://doc.rust-lang.org/core/index.html#how-to-use-the-core-library> for the more details
201+
.help = either allow this lint or remove any `#[unsafe(no_mangle)]` or `#[unsafe(export_name = "{$symbol_name}")]` if present
202+
198203
lint_closure_returning_async_block = closure returning async block can be made into an async closure
199204
.label = this async block can be removed, and the closure can be turned into an async closure
200205
.suggestion = turn this into an async closure
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
use rustc_hir as hir;
2+
use rustc_session::{declare_lint, declare_lint_pass};
3+
4+
use crate::lints::ClashingFunctionNamesWithFundamentalFunctions;
5+
use crate::{LateContext, LateLintPass, LintContext};
6+
7+
declare_lint! {
8+
/// The `clashing_function_names_with_fundamental_functions` lint checks for function
9+
/// name whose name clash with a fundamental functions expected by `core` and `std`.
10+
///
11+
/// ### Example
12+
///
13+
/// ```rust,compile_fail
14+
/// #![deny(clashing_function_names_with_fundamental_functions)]
15+
///
16+
/// #[unsafe(no_mangle)]
17+
/// pub fn strlen() {} // clash with the libc `strlen` function
18+
/// // care must be taken when implementing this function
19+
/// ```
20+
///
21+
/// {{produces}}
22+
///
23+
/// ### Explanation
24+
///
25+
/// Up-most care is required when overriding those fundamental functions assumed and
26+
/// used by the standard library. They must follow the C specification, not use any
27+
/// standard-library facility or undefined behavior may occur.
28+
///
29+
/// The symbols currently checked are respectively:
30+
/// - from `core`[^1]: `memcpy`, `memmove`, `memset`, `memcmp`, `bcmp`, `strlen`
31+
/// - from `std`: `read`, `write`, `open`, `close`
32+
///
33+
/// [^1]: https://doc.rust-lang.org/core/index.html#how-to-use-the-core-library
34+
pub CLASHING_FUNCTION_NAMES_WITH_FUNDAMENTAL_FUNCTIONS,
35+
Warn,
36+
"using a function name that clashes with fundamental function names"
37+
}
38+
39+
declare_lint_pass!(FundamentalFunctions => [CLASHING_FUNCTION_NAMES_WITH_FUNDAMENTAL_FUNCTIONS]);
40+
41+
static CORE_FUNDAMENTAL_FUNCTION_NAMES: &[&str] =
42+
&["memcpy", "memmove", "memset", "memcmp", "bcmp", "strlen"];
43+
44+
static STD_FUNDAMENTAL_FUNCTION_NAMES: &[&str] = &["open", "read", "write", "close"];
45+
46+
impl<'tcx> LateLintPass<'tcx> for FundamentalFunctions {
47+
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
48+
let hir::ItemKind::Fn { sig: _, ident: _, generics: _, body: _, has_body: true } =
49+
item.kind
50+
else {
51+
return;
52+
};
53+
54+
let Some(symbol_name) = rustc_symbol_mangling::symbol_name_without_mangling(
55+
cx.tcx,
56+
rustc_middle::ty::InstanceKind::Item(item.owner_id.to_def_id()),
57+
) else {
58+
return;
59+
};
60+
61+
if CORE_FUNDAMENTAL_FUNCTION_NAMES.contains(&&*symbol_name)
62+
|| STD_FUNDAMENTAL_FUNCTION_NAMES.contains(&&*symbol_name)
63+
{
64+
cx.emit_span_lint(
65+
CLASHING_FUNCTION_NAMES_WITH_FUNDAMENTAL_FUNCTIONS,
66+
item.span,
67+
ClashingFunctionNamesWithFundamentalFunctions { symbol_name },
68+
);
69+
}
70+
}
71+
}

compiler/rustc_lint/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ mod errors;
4848
mod expect;
4949
mod for_loops_over_fallibles;
5050
mod foreign_modules;
51+
mod fundamental_functions;
5152
mod if_let_rescope;
5253
mod impl_trait_overcaptures;
5354
mod internal;
@@ -92,6 +93,7 @@ use deref_into_dyn_supertrait::*;
9293
use drop_forget_useless::*;
9394
use enum_intrinsics_non_enums::EnumIntrinsicsNonEnums;
9495
use for_loops_over_fallibles::*;
96+
use fundamental_functions::*;
9597
use if_let_rescope::IfLetRescope;
9698
use impl_trait_overcaptures::ImplTraitOvercaptures;
9799
use internal::*;
@@ -240,6 +242,7 @@ late_lint_methods!(
240242
AsyncClosureUsage: AsyncClosureUsage,
241243
AsyncFnInTrait: AsyncFnInTrait,
242244
NonLocalDefinitions: NonLocalDefinitions::default(),
245+
FundamentalFunctions: FundamentalFunctions,
243246
ImplTraitOvercaptures: ImplTraitOvercaptures,
244247
IfLetRescope: IfLetRescope::default(),
245248
StaticMutRefs: StaticMutRefs,

compiler/rustc_lint/src/lints.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -705,6 +705,16 @@ pub(crate) enum UseLetUnderscoreIgnoreSuggestion {
705705
},
706706
}
707707

708+
// fundamental_functions.rs
709+
#[derive(LintDiagnostic)]
710+
#[diag(lint_clashing_function_names_with_fundamental_functions)]
711+
#[note(lint_match_exactly)]
712+
#[note(lint_learn_more)]
713+
#[help]
714+
pub(crate) struct ClashingFunctionNamesWithFundamentalFunctions {
715+
pub symbol_name: String,
716+
}
717+
708718
// drop_forget_useless.rs
709719
#[derive(LintDiagnostic)]
710720
#[diag(lint_dropping_references)]

compiler/rustc_symbol_mangling/src/lib.rs

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
100100
use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
101101
use rustc_middle::mir::mono::{InstantiationMode, MonoItem};
102102
use rustc_middle::query::Providers;
103-
use rustc_middle::ty::{self, Instance, TyCtxt};
103+
use rustc_middle::ty::{self, Instance, InstanceKind, TyCtxt};
104104
use rustc_session::config::SymbolManglingVersion;
105105
use tracing::debug;
106106

@@ -158,29 +158,22 @@ pub fn typeid_for_trait_ref<'tcx>(
158158
v0::mangle_typeid_for_trait_ref(tcx, trait_ref)
159159
}
160160

161-
/// Computes the symbol name for the given instance. This function will call
162-
/// `compute_instantiating_crate` if it needs to factor the instantiating crate
163-
/// into the symbol name.
164-
fn compute_symbol_name<'tcx>(
161+
pub fn symbol_name_without_mangling<'tcx>(
165162
tcx: TyCtxt<'tcx>,
166-
instance: Instance<'tcx>,
167-
compute_instantiating_crate: impl FnOnce() -> CrateNum,
168-
) -> String {
169-
let def_id = instance.def_id();
170-
let args = instance.args;
171-
172-
debug!("symbol_name(def_id={:?}, args={:?})", def_id, args);
163+
instance_kind: InstanceKind<'tcx>,
164+
) -> Option<String> {
165+
let def_id = instance_kind.def_id();
173166

174167
if let Some(def_id) = def_id.as_local() {
175168
if tcx.proc_macro_decls_static(()) == Some(def_id) {
176169
let stable_crate_id = tcx.stable_crate_id(LOCAL_CRATE);
177-
return tcx.sess.generate_proc_macro_decls_symbol(stable_crate_id);
170+
return Some(tcx.sess.generate_proc_macro_decls_symbol(stable_crate_id));
178171
}
179172
}
180173

181174
// FIXME(eddyb) Precompute a custom symbol name based on attributes.
182175
let attrs = if tcx.def_kind(def_id).has_codegen_attrs() {
183-
&tcx.codegen_instance_attrs(instance.def)
176+
&tcx.codegen_instance_attrs(instance_kind)
184177
} else {
185178
CodegenFnAttrs::EMPTY
186179
};
@@ -206,7 +199,7 @@ fn compute_symbol_name<'tcx>(
206199
// legacy symbol mangling scheme.
207200
let name = if let Some(name) = attrs.symbol_name { name } else { tcx.item_name(def_id) };
208201

209-
return v0::mangle_internal_symbol(tcx, name.as_str());
202+
return Some(v0::mangle_internal_symbol(tcx, name.as_str()));
210203
}
211204

212205
let wasm_import_module_exception_force_mangling = {
@@ -234,15 +227,35 @@ fn compute_symbol_name<'tcx>(
234227
if !wasm_import_module_exception_force_mangling {
235228
if let Some(name) = attrs.symbol_name {
236229
// Use provided name
237-
return name.to_string();
230+
return Some(name.to_string());
238231
}
239232

240233
if attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE) {
241234
// Don't mangle
242-
return tcx.item_name(def_id).to_string();
235+
return Some(tcx.item_name(def_id).to_string());
243236
}
244237
}
245238

239+
None
240+
}
241+
242+
/// Computes the symbol name for the given instance. This function will call
243+
/// `compute_instantiating_crate` if it needs to factor the instantiating crate
244+
/// into the symbol name.
245+
fn compute_symbol_name<'tcx>(
246+
tcx: TyCtxt<'tcx>,
247+
instance: Instance<'tcx>,
248+
compute_instantiating_crate: impl FnOnce() -> CrateNum,
249+
) -> String {
250+
let def_id = instance.def_id();
251+
let args = instance.args;
252+
253+
debug!("symbol_name(def_id={:?}, args={:?})", def_id, args);
254+
255+
if let Some(symbol) = symbol_name_without_mangling(tcx, instance.def) {
256+
return symbol;
257+
}
258+
246259
// If we're dealing with an instance of a function that's inlined from
247260
// another crate but we're marking it as globally shared to our
248261
// compilation (aka we're not making an internal copy in each of our
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
//@ check-pass
2+
//@ edition: 2021
3+
4+
use std::ffi::c_void;
5+
6+
// From core
7+
8+
#[no_mangle]
9+
pub extern "C" fn memcpy(
10+
dest: *mut c_void,
11+
src: *const c_void,
12+
n: i64,
13+
) -> *mut c_void { std::ptr::null_mut() }
14+
//~^^^^^ WARN `memcpy` clashes
15+
16+
#[no_mangle]
17+
pub fn memmove() {}
18+
//~^ WARN `memmove` clashes
19+
20+
#[no_mangle]
21+
pub fn memset() {}
22+
//~^ WARN `memset` clashes
23+
24+
#[no_mangle]
25+
pub fn memcmp() {}
26+
//~^ WARN `memcmp` clashes
27+
28+
#[export_name = "bcmp"]
29+
pub fn bcmp_() {}
30+
//~^ WARN `bcmp` clashes
31+
32+
#[no_mangle]
33+
pub fn strlen() {}
34+
//~^ WARN `strlen` clashes
35+
36+
// From std
37+
38+
#[no_mangle]
39+
pub fn open() {}
40+
//~^ WARN `open` clashes
41+
42+
#[export_name = "read"]
43+
pub async fn read1() {}
44+
//~^ WARN `read` clashes
45+
46+
#[export_name = "write"]
47+
pub fn write1() {}
48+
//~^ WARN `write` clashes
49+
50+
#[export_name = "close"]
51+
pub fn close_() {}
52+
//~^ WARN `close` clashes
53+
54+
extern "C" {
55+
// No warning, not a body.
56+
pub fn close(a: i32) -> i32;
57+
}
58+
59+
fn main() {}

0 commit comments

Comments
 (0)