1use rustc_hir::def_id::{DefId, LocalDefId};
2use rustc_hir::{self as hir, FnSig, ForeignItemKind, LanguageItems};
3use rustc_infer::infer::DefineOpaqueTypes;
4use rustc_middle::ty::{self, Instance, Ty};
5use rustc_session::{declare_lint, declare_lint_pass};
6use rustc_span::Span;
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` 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` and `strlen`."]
#[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,
38 Deny,
39 "invalid definition of a symbol used by the standard library"
40}
41
42#[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`."]
#[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` and `strlen`."]
#[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! {
43 pub SUSPICIOUS_RUNTIME_SYMBOL_DEFINITIONS,
68 Warn,
69 "suspicious definition of a symbol used by the standard library"
70}
71
72pub 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]);
73
74static EXPECTED_SYMBOLS: &[ExpectedSymbol] = &[
75 ExpectedSymbol { symbol: "memcpy", lang: LanguageItems::memcpy_fn },
76 ExpectedSymbol { symbol: "memmove", lang: LanguageItems::memmove_fn },
77 ExpectedSymbol { symbol: "memset", lang: LanguageItems::memset_fn },
78 ExpectedSymbol { symbol: "memcmp", lang: LanguageItems::memcmp_fn },
79 ExpectedSymbol { symbol: "bcmp", lang: LanguageItems::bcmp_fn },
80 ExpectedSymbol { symbol: "strlen", lang: LanguageItems::strlen_fn },
81];
82
83#[derive(#[automatically_derived]
impl ::core::marker::Copy for ExpectedSymbol { }Copy, #[automatically_derived]
impl ::core::clone::Clone for ExpectedSymbol {
#[inline]
fn clone(&self) -> ExpectedSymbol {
let _: ::core::clone::AssertParamIsClone<&'static str>;
let _:
::core::clone::AssertParamIsClone<fn(&LanguageItems)
-> Option<DefId>>;
*self
}
}Clone, #[automatically_derived]
impl ::core::fmt::Debug for ExpectedSymbol {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field2_finish(f,
"ExpectedSymbol", "symbol", &self.symbol, "lang", &&self.lang)
}
}Debug)]
84struct ExpectedSymbol {
85 symbol: &'static str,
86 lang: fn(&LanguageItems) -> Option<DefId>,
87}
88
89impl<'tcx> LateLintPass<'tcx> for RuntimeSymbols {
90 fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
91 match item.kind {
93 hir::ItemKind::Fn { sig, ident: _, generics, body: _, has_body: _ } => {
94 if !generics.params.is_empty() {
97 return;
98 }
99
100 let Some(symbol_name) = rustc_symbol_mangling::symbol_name_from_attrs(
103 cx.tcx,
104 rustc_middle::ty::InstanceKind::Item(item.owner_id.to_def_id()),
105 ) else {
106 return;
107 };
108
109 check_fn(cx, &symbol_name, sig, item.owner_id.def_id);
110 }
111 hir::ItemKind::Static(..) => {
112 let Some(symbol_name) = rustc_symbol_mangling::symbol_name_from_attrs(
115 cx.tcx,
116 rustc_middle::ty::InstanceKind::Item(item.owner_id.to_def_id()),
117 ) else {
118 return;
119 };
120
121 let def_id = item.owner_id.def_id;
122
123 check_static(cx, &symbol_name, def_id, item.span);
124 }
125 hir::ItemKind::ForeignMod { abi: _, items } => {
126 for item in items {
127 let item = cx.tcx.hir_foreign_item(*item);
128
129 let did = item.owner_id.def_id;
130 let instance = Instance::new_raw(
131 did.to_def_id(),
132 ty::List::identity_for_item(cx.tcx, did),
133 );
134 let symbol_name = cx.tcx.symbol_name(instance);
135
136 match item.kind {
137 ForeignItemKind::Fn(fn_sig, _idents, _generics) => {
138 check_fn(cx, &symbol_name.name, fn_sig, did);
139 }
140 ForeignItemKind::Static(..) => {
141 check_static(cx, &symbol_name.name, did, item.span);
142 }
143 ForeignItemKind::Type => return,
144 }
145 }
146 }
147 _ => return,
148 }
149 }
150}
151
152fn check_fn(cx: &LateContext<'_>, symbol_name: &str, sig: FnSig<'_>, did: LocalDefId) {
153 let Some(expected_symbol) = EXPECTED_SYMBOLS.iter().find(|es| es.symbol == symbol_name) else {
154 return;
156 };
157
158 let Some(expected_def_id) = (expected_symbol.lang)(&cx.tcx.lang_items()) else {
159 return;
161 };
162
163 let lang_sig = cx.tcx.normalize_erasing_regions(
165 cx.typing_env(),
166 cx.tcx.fn_sig(expected_def_id).instantiate_identity(),
167 );
168 let user_sig = cx
169 .tcx
170 .normalize_erasing_regions(cx.typing_env(), cx.tcx.fn_sig(did).instantiate_identity());
171
172 let infcx = cx.tcx.infer_ctxt().build(cx.typing_mode());
174 let cause = rustc_middle::traits::ObligationCause::misc(sig.span, did);
175 let result = infcx.at(&cause, cx.param_env).eq(DefineOpaqueTypes::No, lang_sig, user_sig);
176
177 if let Err(_terr) = result {
179 let expected = Ty::new_fn_ptr(cx.tcx, lang_sig);
181 let actual = Ty::new_fn_ptr(cx.tcx, user_sig);
182
183 if lang_sig.abi() != user_sig.abi()
184 || lang_sig.c_variadic() != user_sig.c_variadic()
185 || lang_sig.inputs().skip_binder().len() != user_sig.inputs().skip_binder().len()
186 || (!lang_sig.output().skip_binder().is_unit()
187 && user_sig.output().skip_binder().is_unit())
188 {
189 cx.emit_span_lint(
190 INVALID_RUNTIME_SYMBOL_DEFINITIONS,
191 sig.span,
192 RedefiningRuntimeSymbolsDiag::FnDefInvalid {
193 symbol_name: symbol_name.to_string(),
194 found_fn_sig: actual,
195 expected_fn_sig: expected,
196 },
197 );
198 } else {
199 cx.emit_span_lint(
200 SUSPICIOUS_RUNTIME_SYMBOL_DEFINITIONS,
201 sig.span,
202 RedefiningRuntimeSymbolsDiag::FnDefSuspicious {
203 symbol_name: symbol_name.to_string(),
204 found_fn_sig: actual,
205 expected_fn_sig: expected,
206 },
207 );
208 };
209 }
210}
211
212fn check_static<'tcx>(cx: &LateContext<'tcx>, symbol_name: &str, did: LocalDefId, sp: Span) {
213 let Some(expected_symbol) = EXPECTED_SYMBOLS.iter().find(|es| es.symbol == symbol_name) else {
214 return;
216 };
217
218 let Some(expected_def_id) = (expected_symbol.lang)(&cx.tcx.lang_items()) else {
219 return;
221 };
222
223 let static_ty = cx.tcx.type_of(did).instantiate_identity().skip_norm_wip();
225
226 let inner_static_ty: Ty<'_> = match static_ty.kind() {
228 ty::Adt(def, args) if Some(def.did()) == cx.tcx.lang_items().option_type() => {
229 args.type_at(0)
230 }
231 _ => static_ty,
232 };
233
234 let lang_sig = cx.tcx.normalize_erasing_regions(
236 cx.typing_env(),
237 cx.tcx.fn_sig(expected_def_id).instantiate_identity(),
238 );
239
240 let expected = Ty::new_fn_ptr(cx.tcx, lang_sig);
241
242 if expected != inner_static_ty {
244 cx.emit_span_lint(
245 INVALID_RUNTIME_SYMBOL_DEFINITIONS,
246 sp,
247 RedefiningRuntimeSymbolsDiag::Static {
248 static_ty,
249 symbol_name: symbol_name.to_string(),
250 expected_fn_sig: expected,
251 },
252 );
253 }
254}