Skip to content

Commit e2446d7

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

File tree

9 files changed

+285
-17
lines changed

9 files changed

+285
-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: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
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,no_run
14+
/// #[unsafe(no_mangle)]
15+
/// pub fn strlen() {} // clash with the libc `strlen` function
16+
/// // care must be taken when implementing this function
17+
/// ```
18+
///
19+
/// {{produces}}
20+
///
21+
/// ### Explanation
22+
///
23+
/// Up-most care is required when overriding those fundamental functions assumed and
24+
/// used by the standard library. They must follow the C specification, not use any
25+
/// standard-library facility or undefined behavior may occur.
26+
///
27+
/// The symbols currently checked are respectively:
28+
/// - from `core`[^1]: `memcpy`, `memmove`, `memset`, `memcmp`, `bcmp`, `strlen`
29+
/// - from `std`: `read`, `write`, `open`, `close`
30+
///
31+
/// [^1]: https://doc.rust-lang.org/core/index.html#how-to-use-the-core-library
32+
pub CLASHING_FUNCTION_NAMES_WITH_FUNDAMENTAL_FUNCTIONS,
33+
Warn,
34+
"using a function name that clashes with fundamental function names"
35+
}
36+
37+
declare_lint_pass!(FundamentalFunctions => [CLASHING_FUNCTION_NAMES_WITH_FUNDAMENTAL_FUNCTIONS]);
38+
39+
static CORE_FUNDAMENTAL_FUNCTION_NAMES: &[&str] =
40+
&["memcpy", "memmove", "memset", "memcmp", "bcmp", "strlen"];
41+
42+
static STD_FUNDAMENTAL_FUNCTION_NAMES: &[&str] = &["open", "read", "write", "close"];
43+
44+
impl<'tcx> LateLintPass<'tcx> for FundamentalFunctions {
45+
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
46+
let hir::ItemKind::Fn { sig: _, ident: _, generics: _, body: _, has_body: true } =
47+
item.kind
48+
else {
49+
return;
50+
};
51+
52+
let Some(symbol_name) = rustc_symbol_mangling::symbol_name_without_mangling(
53+
cx.tcx,
54+
rustc_middle::ty::InstanceKind::Item(item.owner_id.to_def_id()),
55+
) else {
56+
return;
57+
};
58+
59+
if CORE_FUNDAMENTAL_FUNCTION_NAMES.contains(&&*symbol_name)
60+
|| STD_FUNDAMENTAL_FUNCTION_NAMES.contains(&&*symbol_name)
61+
{
62+
cx.emit_span_lint(
63+
CLASHING_FUNCTION_NAMES_WITH_FUNDAMENTAL_FUNCTIONS,
64+
item.span,
65+
ClashingFunctionNamesWithFundamentalFunctions { symbol_name },
66+
);
67+
}
68+
}
69+
}

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() {}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
warning: this function symbol name `memcpy` clashes with the fundamental functions expected with `core` and `std`
2+
--> $DIR/clashing-fn-names-with-fundamental-functions.rs:9:1
3+
|
4+
LL | / pub extern "C" fn memcpy(
5+
LL | | dest: *mut c_void,
6+
LL | | src: *const c_void,
7+
LL | | n: i64,
8+
LL | | ) -> *mut c_void { std::ptr::null_mut() }
9+
| |_________________________________________^
10+
|
11+
= note: extra care must be taken when exposing a function with those symbol names, they must match exactly (ABI, function arguments, function return type, behavior, ...)
12+
= note: see <https://doc.rust-lang.org/core/index.html#how-to-use-the-core-library> for the more details
13+
= help: either allow this lint or remove any `#[unsafe(no_mangle)]` or `#[unsafe(export_name = "memcpy")]` if present
14+
= note: `#[warn(clashing_function_names_with_fundamental_functions)]` on by default
15+
16+
warning: this function symbol name `memmove` clashes with the fundamental functions expected with `core` and `std`
17+
--> $DIR/clashing-fn-names-with-fundamental-functions.rs:17:1
18+
|
19+
LL | pub fn memmove() {}
20+
| ^^^^^^^^^^^^^^^^^^^
21+
|
22+
= note: extra care must be taken when exposing a function with those symbol names, they must match exactly (ABI, function arguments, function return type, behavior, ...)
23+
= note: see <https://doc.rust-lang.org/core/index.html#how-to-use-the-core-library> for the more details
24+
= help: either allow this lint or remove any `#[unsafe(no_mangle)]` or `#[unsafe(export_name = "memmove")]` if present
25+
26+
warning: this function symbol name `memset` clashes with the fundamental functions expected with `core` and `std`
27+
--> $DIR/clashing-fn-names-with-fundamental-functions.rs:21:1
28+
|
29+
LL | pub fn memset() {}
30+
| ^^^^^^^^^^^^^^^^^^
31+
|
32+
= note: extra care must be taken when exposing a function with those symbol names, they must match exactly (ABI, function arguments, function return type, behavior, ...)
33+
= note: see <https://doc.rust-lang.org/core/index.html#how-to-use-the-core-library> for the more details
34+
= help: either allow this lint or remove any `#[unsafe(no_mangle)]` or `#[unsafe(export_name = "memset")]` if present
35+
36+
warning: this function symbol name `memcmp` clashes with the fundamental functions expected with `core` and `std`
37+
--> $DIR/clashing-fn-names-with-fundamental-functions.rs:25:1
38+
|
39+
LL | pub fn memcmp() {}
40+
| ^^^^^^^^^^^^^^^^^^
41+
|
42+
= note: extra care must be taken when exposing a function with those symbol names, they must match exactly (ABI, function arguments, function return type, behavior, ...)
43+
= note: see <https://doc.rust-lang.org/core/index.html#how-to-use-the-core-library> for the more details
44+
= help: either allow this lint or remove any `#[unsafe(no_mangle)]` or `#[unsafe(export_name = "memcmp")]` if present
45+
46+
warning: this function symbol name `bcmp` clashes with the fundamental functions expected with `core` and `std`
47+
--> $DIR/clashing-fn-names-with-fundamental-functions.rs:29:1
48+
|
49+
LL | pub fn bcmp_() {}
50+
| ^^^^^^^^^^^^^^^^^
51+
|
52+
= note: extra care must be taken when exposing a function with those symbol names, they must match exactly (ABI, function arguments, function return type, behavior, ...)
53+
= note: see <https://doc.rust-lang.org/core/index.html#how-to-use-the-core-library> for the more details
54+
= help: either allow this lint or remove any `#[unsafe(no_mangle)]` or `#[unsafe(export_name = "bcmp")]` if present
55+
56+
warning: this function symbol name `strlen` clashes with the fundamental functions expected with `core` and `std`
57+
--> $DIR/clashing-fn-names-with-fundamental-functions.rs:33:1
58+
|
59+
LL | pub fn strlen() {}
60+
| ^^^^^^^^^^^^^^^^^^
61+
|
62+
= note: extra care must be taken when exposing a function with those symbol names, they must match exactly (ABI, function arguments, function return type, behavior, ...)
63+
= note: see <https://doc.rust-lang.org/core/index.html#how-to-use-the-core-library> for the more details
64+
= help: either allow this lint or remove any `#[unsafe(no_mangle)]` or `#[unsafe(export_name = "strlen")]` if present
65+
66+
warning: this function symbol name `open` clashes with the fundamental functions expected with `core` and `std`
67+
--> $DIR/clashing-fn-names-with-fundamental-functions.rs:39:1
68+
|
69+
LL | pub fn open() {}
70+
| ^^^^^^^^^^^^^^^^
71+
|
72+
= note: extra care must be taken when exposing a function with those symbol names, they must match exactly (ABI, function arguments, function return type, behavior, ...)
73+
= note: see <https://doc.rust-lang.org/core/index.html#how-to-use-the-core-library> for the more details
74+
= help: either allow this lint or remove any `#[unsafe(no_mangle)]` or `#[unsafe(export_name = "open")]` if present
75+
76+
warning: this function symbol name `read` clashes with the fundamental functions expected with `core` and `std`
77+
--> $DIR/clashing-fn-names-with-fundamental-functions.rs:43:1
78+
|
79+
LL | pub async fn read1() {}
80+
| ^^^^^^^^^^^^^^^^^^^^^^^
81+
|
82+
= note: extra care must be taken when exposing a function with those symbol names, they must match exactly (ABI, function arguments, function return type, behavior, ...)
83+
= note: see <https://doc.rust-lang.org/core/index.html#how-to-use-the-core-library> for the more details
84+
= help: either allow this lint or remove any `#[unsafe(no_mangle)]` or `#[unsafe(export_name = "read")]` if present
85+
86+
warning: this function symbol name `write` clashes with the fundamental functions expected with `core` and `std`
87+
--> $DIR/clashing-fn-names-with-fundamental-functions.rs:47:1
88+
|
89+
LL | pub fn write1() {}
90+
| ^^^^^^^^^^^^^^^^^^
91+
|
92+
= note: extra care must be taken when exposing a function with those symbol names, they must match exactly (ABI, function arguments, function return type, behavior, ...)
93+
= note: see <https://doc.rust-lang.org/core/index.html#how-to-use-the-core-library> for the more details
94+
= help: either allow this lint or remove any `#[unsafe(no_mangle)]` or `#[unsafe(export_name = "write")]` if present
95+
96+
warning: this function symbol name `close` clashes with the fundamental functions expected with `core` and `std`
97+
--> $DIR/clashing-fn-names-with-fundamental-functions.rs:51:1
98+
|
99+
LL | pub fn close_() {}
100+
| ^^^^^^^^^^^^^^^^^^
101+
|
102+
= note: extra care must be taken when exposing a function with those symbol names, they must match exactly (ABI, function arguments, function return type, behavior, ...)
103+
= note: see <https://doc.rust-lang.org/core/index.html#how-to-use-the-core-library> for the more details
104+
= help: either allow this lint or remove any `#[unsafe(no_mangle)]` or `#[unsafe(export_name = "close")]` if present
105+
106+
warning: 10 warnings emitted
107+

0 commit comments

Comments
 (0)