1use rustc_hir::def_id::LocalDefId;
2use rustc_hir::{self as hir, CanonicalSymbol, FnSig, ForeignItemKind};
3use rustc_infer::infer::DefineOpaqueTypes;
4use rustc_middle::ty::{self, Instance, PolyFnSig, Ty};
5use rustc_session::{declare_lint, declare_lint_pass};
6use rustc_span::{Span, Symbol};
7use rustc_trait_selection::infer::TyCtxtInferExt;
8
9use crate::lints::RedefiningRuntimeSymbolsDiag;
10use crate::{LateContext, LateLintPass, LintContext};
11
12#[doc =
r" The `invalid_runtime_symbol_definitions` lint checks the signature of items whose"]
#[doc =
r" symbol name is a runtime symbol expected by `core` or `std` differs significantly from the"]
#[doc =
r" expected signature (like mismatch ABI, mismatch C variadics, mismatch argument count,"]
#[doc = r" missing return type, ...)."]
#[doc = r""]
#[doc = r" ### Example"]
#[doc = r""]
#[doc = r" ```rust,compile_fail"]
#[doc = r" #[unsafe(no_mangle)]"]
#[doc = r" pub fn strlen() {} // invalid definition of the `strlen` function"]
#[doc = r" ```"]
#[doc = r""]
#[doc = r" {{produces}}"]
#[doc = r""]
#[doc = r" ### Explanation"]
#[doc = r""]
#[doc =
r" Up-most care is required when defining runtime symbols assumed and"]
#[doc =
r" used by the standard library. They must follow the C specification, not use any"]
#[doc = r" standard-library facility or undefined behavior may occur."]
#[doc = r""]
#[doc =
r" The symbols currently checked are `memcpy`, `memmove`, `memset`, `memcmp`,"]
#[doc =
r" `bcmp`, `strlen`, as well as the following POSIX symbols: `open`, `read`, `write`"]
#[doc = r" `close`, `malloc`, `realloc`, `free` and `exit`."]
#[doc = r""]
#[doc =
r" [^1]: https://doc.rust-lang.org/core/index.html#how-to-use-the-core-library"]
pub static INVALID_RUNTIME_SYMBOL_DEFINITIONS: &::rustc_lint_defs::Lint =
&::rustc_lint_defs::Lint {
name: "INVALID_RUNTIME_SYMBOL_DEFINITIONS",
default_level: ::rustc_lint_defs::Deny,
desc: "invalid definition of a symbol used by the standard library",
is_externally_loaded: false,
..::rustc_lint_defs::Lint::default_fields_for_macro()
};declare_lint! {
13 pub INVALID_RUNTIME_SYMBOL_DEFINITIONS,
39 Deny,
40 "invalid definition of a symbol used by the standard library"
41}
42
43#[doc =
r" The `suspicious_runtime_symbol_definitions` lint checks the signature of items whose"]
#[doc = r" symbol name is a runtime symbol expected by `core` or `std`."]
#[doc = r""]
#[doc = r" ### Example"]
#[doc = r""]
#[doc = r" ```rust,no_run,standalone_crate"]
#[doc = r" #[unsafe(no_mangle)]"]
#[doc = r#" pub extern "C" fn strlen(ptr: *mut f32) -> usize { 0 }"#]
#[doc = r" // suspicious definition of the `strlen` function"]
#[doc = r" // `ptr` should be `*const std::ffi::c_char`"]
#[doc = r" ```"]
#[doc = r""]
#[doc = r" {{produces}}"]
#[doc = r""]
#[doc = r" ### Explanation"]
#[doc = r""]
#[doc =
r" Up-most care is required when defining runtime symbols assumed and"]
#[doc =
r" used by the standard library. They must follow the C specification, not use any"]
#[doc = r" standard-library facility or undefined behavior may occur."]
#[doc = r""]
#[doc =
r" The symbols currently checked are `memcpy`, `memmove`, `memset`, `memcmp`,"]
#[doc =
r" `bcmp`, `strlen`, as well as the following POSIX symbols: `open`, `read`, `write`"]
#[doc = r" `close`, `malloc`, `realloc`, `free` and `exit`."]
#[doc = r""]
#[doc =
r" [^1]: https://doc.rust-lang.org/core/index.html#how-to-use-the-core-library"]
pub static SUSPICIOUS_RUNTIME_SYMBOL_DEFINITIONS: &::rustc_lint_defs::Lint =
&::rustc_lint_defs::Lint {
name: "SUSPICIOUS_RUNTIME_SYMBOL_DEFINITIONS",
default_level: ::rustc_lint_defs::Warn,
desc: "suspicious definition of a symbol used by the standard library",
is_externally_loaded: false,
..::rustc_lint_defs::Lint::default_fields_for_macro()
};declare_lint! {
44 pub SUSPICIOUS_RUNTIME_SYMBOL_DEFINITIONS,
70 Warn,
71 "suspicious definition of a symbol used by the standard library"
72}
73
74pub struct RuntimeSymbols;
#[automatically_derived]
impl ::core::marker::Copy for RuntimeSymbols { }
#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for RuntimeSymbols { }
#[automatically_derived]
impl ::core::clone::Clone for RuntimeSymbols {
#[inline]
fn clone(&self) -> RuntimeSymbols { *self }
}
impl ::rustc_lint_defs::LintPass for RuntimeSymbols {
fn name(&self) -> &'static str { "RuntimeSymbols" }
fn get_lints(&self) -> ::rustc_lint_defs::LintVec {
::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[INVALID_RUNTIME_SYMBOL_DEFINITIONS,
SUSPICIOUS_RUNTIME_SYMBOL_DEFINITIONS]))
}
}
impl RuntimeSymbols {
#[allow(unused)]
pub fn lint_vec() -> ::rustc_lint_defs::LintVec {
::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[INVALID_RUNTIME_SYMBOL_DEFINITIONS,
SUSPICIOUS_RUNTIME_SYMBOL_DEFINITIONS]))
}
}declare_lint_pass!(RuntimeSymbols => [INVALID_RUNTIME_SYMBOL_DEFINITIONS, SUSPICIOUS_RUNTIME_SYMBOL_DEFINITIONS]);
75
76impl<'tcx> LateLintPass<'tcx> for RuntimeSymbols {
77 fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
78 match item.kind {
80 hir::ItemKind::Fn { sig, ident: _, generics, body: _, has_body: _ } => {
81 if !generics.params.is_empty() {
84 return;
85 }
86
87 let Some(symbol_name) = rustc_symbol_mangling::symbol_name_from_attrs(
90 cx.tcx,
91 rustc_middle::ty::InstanceKind::Item(item.owner_id.to_def_id()),
92 ) else {
93 return;
94 };
95
96 check_fn(cx, &symbol_name, sig, item.owner_id.def_id);
97 }
98 hir::ItemKind::Static(..) => {
99 let Some(symbol_name) = rustc_symbol_mangling::symbol_name_from_attrs(
102 cx.tcx,
103 rustc_middle::ty::InstanceKind::Item(item.owner_id.to_def_id()),
104 ) else {
105 return;
106 };
107
108 let def_id = item.owner_id.def_id;
109
110 check_static(cx, &symbol_name, def_id, item.span);
111 }
112 hir::ItemKind::ForeignMod { abi: _, items } => {
113 for item in items {
114 let item = cx.tcx.hir_foreign_item(*item);
115
116 let did = item.owner_id.def_id;
117 let instance = Instance::new_raw(
118 did.to_def_id(),
119 ty::List::identity_for_item(cx.tcx, did),
120 );
121 let symbol_name = cx.tcx.symbol_name(instance);
122
123 match item.kind {
124 ForeignItemKind::Fn(fn_sig, _idents, _generics) => {
125 check_fn(cx, &symbol_name.name, fn_sig, did);
126 }
127 ForeignItemKind::Static(..) => {
128 if cx.tcx.codegen_fn_attrs(did).import_linkage.is_some() {
130 check_static(cx, &symbol_name.name, did, item.span);
131 }
132 }
133 ForeignItemKind::Type => return,
134 }
135 }
136 }
137 _ => return,
138 }
139 }
140}
141
142fn check_fn(cx: &LateContext<'_>, symbol_name: &str, sig: FnSig<'_>, did: LocalDefId) {
143 let s = Symbol::intern(symbol_name);
144 let Some(CanonicalSymbol { symbol: _, def_id: expected_def_id }) =
145 cx.tcx.all_canonical_symbols(()).iter().find(|cs| cs.symbol == s)
146 else {
147 return;
149 };
150
151 let lang_sig = cx.tcx.normalize_erasing_regions(
153 cx.typing_env(),
154 cx.tcx.fn_sig(expected_def_id).instantiate_identity(),
155 );
156 let user_sig = cx
157 .tcx
158 .normalize_erasing_regions(cx.typing_env(), cx.tcx.fn_sig(did).instantiate_identity());
159
160 check(cx, symbol_name, did, sig.span, lang_sig, user_sig);
161}
162
163fn check_static<'tcx>(cx: &LateContext<'tcx>, symbol_name: &str, did: LocalDefId, sp: Span) {
164 let s = Symbol::intern(symbol_name);
165 let Some(CanonicalSymbol { symbol: _, def_id: expected_def_id }) =
166 cx.tcx.all_canonical_symbols(()).iter().find(|cs| cs.symbol == s)
167 else {
168 return;
170 };
171
172 let lang_sig = cx.tcx.normalize_erasing_regions(
174 cx.typing_env(),
175 cx.tcx.fn_sig(expected_def_id).instantiate_identity(),
176 );
177
178 let outer_user_sig = cx.tcx.type_of(did).instantiate_identity().skip_norm_wip();
180
181 let user_sig: Ty<'_> = match outer_user_sig.kind() {
183 ty::Adt(def, args) if Some(def.did()) == cx.tcx.lang_items().option_type() => {
184 args.type_at(0)
185 }
186 _ => outer_user_sig,
187 };
188
189 let user_sig = if let ty::FnPtr(sig_tys, hdr) = user_sig.kind() {
190 sig_tys.with(*hdr)
191 } else {
192 let lang_sig = Ty::new_fn_ptr(cx.tcx, lang_sig);
195 cx.emit_span_lint(
196 INVALID_RUNTIME_SYMBOL_DEFINITIONS,
197 sp,
198 RedefiningRuntimeSymbolsDiag::Invalid {
199 symbol_name: symbol_name.to_string(),
200 found_fn_sig: user_sig,
201 expected_fn_sig: lang_sig,
202 },
203 );
204 return;
205 };
206
207 check(cx, symbol_name, did, sp, lang_sig, user_sig);
209}
210
211fn check<'tcx>(
212 cx: &LateContext<'tcx>,
213 symbol_name: &str,
214 did: LocalDefId,
215 sp: Span,
216 lang_sig: PolyFnSig<'tcx>,
217 user_sig: PolyFnSig<'tcx>,
218) {
219 let infcx = cx.tcx.infer_ctxt().build(cx.typing_mode());
221 let cause = rustc_middle::traits::ObligationCause::misc(sp, did);
222 let result = infcx.at(&cause, cx.param_env).eq(DefineOpaqueTypes::No, lang_sig, user_sig);
223
224 if result.is_err() {
226 let expected = Ty::new_fn_ptr(cx.tcx, lang_sig);
228 let actual = Ty::new_fn_ptr(cx.tcx, user_sig);
229
230 if lang_sig.abi() != user_sig.abi()
231 || lang_sig.c_variadic() != user_sig.c_variadic()
232 || lang_sig.inputs().skip_binder().len() != user_sig.inputs().skip_binder().len()
233 || (!lang_sig.output().skip_binder().is_unit()
234 && user_sig.output().skip_binder().is_unit())
235 {
236 cx.emit_span_lint(
237 INVALID_RUNTIME_SYMBOL_DEFINITIONS,
238 sp,
239 RedefiningRuntimeSymbolsDiag::Invalid {
240 symbol_name: symbol_name.to_string(),
241 found_fn_sig: actual,
242 expected_fn_sig: expected,
243 },
244 );
245 } else {
246 cx.emit_span_lint(
247 SUSPICIOUS_RUNTIME_SYMBOL_DEFINITIONS,
248 sp,
249 RedefiningRuntimeSymbolsDiag::Suspicious {
250 symbol_name: symbol_name.to_string(),
251 found_fn_sig: actual,
252 expected_fn_sig: expected,
253 },
254 );
255 };
256 }
257}