Skip to content

Commit f4b9440

Browse files
committed
ImproperCTypes: also check 'exported' static variables
Added the missing case for FFI-exposed pieces of code: static variables with the `no_mangle` or `export_name` annotations. This adds a new lint, which is managed by the rest of the ImproperCTypes architecture.
1 parent 9a747eb commit f4b9440

File tree

5 files changed

+101
-12
lines changed

5 files changed

+101
-12
lines changed

compiler/rustc_lint/src/types/improper_ctypes.rs

Lines changed: 72 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -57,27 +57,32 @@ declare_lint! {
5757

5858
declare_lint! {
5959
/// The `improper_ctypes_definitions` lint detects incorrect use of
60-
/// [`extern` function] definitions.
61-
/// (In other words, functions to be used by foreign code.)
60+
/// [`extern` function] definitions and [`no_mangle`] / [`export_name`] static variable definitions.
61+
/// (In other words, functions and global variables to be used by foreign code.)
6262
///
6363
/// [`extern` function]: https://doc.rust-lang.org/reference/items/functions.html#extern-function-qualifier
64+
/// [`no_mangle`]: https://doc.rust-lang.org/stable/reference/abi.html#the-no_mangle-attribute
65+
/// [`export_name`]: https://doc.rust-lang.org/stable/reference/abi.html#the-export_name-attribute
6466
///
6567
/// ### Example
6668
///
6769
/// ```rust
6870
/// # #![allow(unused)]
6971
/// pub extern "C" fn str_type(p: &str) { }
72+
/// # #[used]
73+
/// # #[unsafe(no_mangle)]
74+
/// static PLUGIN_ABI_MIN_VERSION: &'static str = "0.0.5";
7075
/// ```
7176
///
7277
/// {{produces}}
7378
///
7479
/// ### Explanation
7580
///
76-
/// There are many parameter and return types that may be specified in an
77-
/// `extern` function that are not compatible with the given ABI. This
78-
/// lint is an alert that these types should not be used. The lint usually
79-
/// should provide a description of the issue, along with possibly a hint
80-
/// on how to resolve it.
81+
/// There are many types that may be specified at interfaces exposed to foreign code,
82+
/// but are not follow the rules to ensure proper ABI compatibility.
83+
/// This lint is issued when a mistake is detected.
84+
/// The lint usually should provide a description of the issue,
85+
/// along with possibly a hint on how to resolve it.
8186
pub(crate) IMPROPER_CTYPES_DEFINITIONS,
8287
Warn,
8388
"proper use of libc types in foreign item definitions"
@@ -282,6 +287,9 @@ enum CItemKind {
282287
ExportedFunction,
283288
/// `extern "C"` function pointers -> also IMPROPER_CTYPES,
284289
Callback,
290+
/// `no_mangle`/`export_name` static variables, assumed to be used from across an FFI boundary,
291+
/// -> also IMPROPER_CTYPES_DEFINITIONS
292+
ExportedStatic,
285293
}
286294

287295
/// Annotates whether we are in the context of a function's argument types or return type.
@@ -694,6 +702,8 @@ struct VisitorState {
694702
impl RootUseFlags {
695703
// The values that can be set.
696704
const STATIC_TY: Self = Self::STATIC;
705+
const EXPORTED_STATIC_TY: Self =
706+
Self::from_bits(Self::STATIC.bits() | Self::DEFINED.bits()).unwrap();
697707
const ARGUMENT_TY_IN_DEFINITION: Self =
698708
Self::from_bits(Self::FUNC.bits() | Self::DEFINED.bits()).unwrap();
699709
const RETURN_TY_IN_DEFINITION: Self =
@@ -734,6 +744,9 @@ impl VisitorState {
734744
(CItemKind::ExportedFunction, FnPos::Arg) => RootUseFlags::ARGUMENT_TY_IN_DEFINITION,
735745
(CItemKind::ImportedExtern, FnPos::Arg) => RootUseFlags::ARGUMENT_TY_IN_DECLARATION,
736746
(CItemKind::Callback, FnPos::Arg) => RootUseFlags::ARGUMENT_TY_IN_FNPTR,
747+
(CItemKind::ExportedStatic, _) => bug!(
748+
"VisitorState::entry_point_from_fnmode() should not be used for static variables!"
749+
),
737750
};
738751
Self::entry_point(p_flags)
739752
}
@@ -743,6 +756,11 @@ impl VisitorState {
743756
Self::entry_point(RootUseFlags::STATIC_TY)
744757
}
745758

759+
/// Get the proper visitor state for a locally-defined static variable's type
760+
fn static_var_def() -> Self {
761+
Self::entry_point(RootUseFlags::EXPORTED_STATIC_TY)
762+
}
763+
746764
/// Whether the type is used as the type of a static variable.
747765
fn is_direct_in_static(&self) -> bool {
748766
let ret = self.root_use_flags.contains(RootUseFlags::STATIC);
@@ -1632,6 +1650,15 @@ impl<'tcx> ImproperCTypesLint {
16321650
self.process_ffi_result(cx, span, ffi_res, CItemKind::ImportedExtern);
16331651
}
16341652

1653+
/// Check that a `#[no_mangle]`/`#[export_name = _]` static variable is of a ffi-safe type.
1654+
fn check_exported_static(&self, cx: &LateContext<'tcx>, id: hir::HirId, span: Span) {
1655+
let ty = cx.tcx.type_of(id.owner).instantiate_identity();
1656+
let mod_id = cx.tcx.parent_module(id).to_def_id();
1657+
let mut visitor = ImproperCTypesVisitor::new(cx, mod_id);
1658+
let ffi_res = visitor.check_type(VisitorState::static_var_def(), ty);
1659+
self.process_ffi_result(cx, span, ffi_res, CItemKind::ExportedStatic);
1660+
}
1661+
16351662
/// Check if a function's argument types and result type are "ffi-safe".
16361663
fn check_foreign_fn(
16371664
&mut self,
@@ -1738,10 +1765,13 @@ impl<'tcx> ImproperCTypesLint {
17381765
// Internally, we treat this differently, but at the end of the day
17391766
// their linting needs to be enabled/disabled alongside that of "FFI-imported" items.
17401767
CItemKind::Callback => IMPROPER_CTYPES,
1768+
// Same thing with static variables, which are "FFI-exported"
1769+
CItemKind::ExportedStatic => IMPROPER_CTYPES_DEFINITIONS,
17411770
};
17421771
let desc = match fn_mode {
17431772
CItemKind::ImportedExtern => "`extern` block",
17441773
CItemKind::ExportedFunction => "`extern` fn",
1774+
CItemKind::ExportedStatic => "foreign-code-reachable static",
17451775
CItemKind::Callback => "`extern` callback",
17461776
};
17471777
for reason in reasons.iter_mut() {
@@ -1785,7 +1815,14 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesLint {
17851815
let mir_sig = cx.tcx.fn_sig(it.owner_id.def_id).instantiate_identity();
17861816
let mod_id = cx.tcx.parent_module_from_def_id(it.owner_id.def_id).to_def_id();
17871817
if !abi.is_rustic_abi() {
1788-
self.check_foreign_fn(cx, CItemKind::ImportedExtern, mir_sig, sig.decl, mod_id, 0);
1818+
self.check_foreign_fn(
1819+
cx,
1820+
CItemKind::ImportedExtern,
1821+
mir_sig,
1822+
sig.decl,
1823+
mod_id,
1824+
0,
1825+
);
17891826
}
17901827
}
17911828
hir::ForeignItemKind::Static(ty, _, _) if !abi.is_rustic_abi() => {
@@ -1805,6 +1842,25 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesLint {
18051842
ty,
18061843
cx.tcx.type_of(item.owner_id).instantiate_identity(),
18071844
);
1845+
1846+
// FIXME: cx.tcx.has_attr no worky
1847+
// if matches!(item.kind, hir::ItemKind::Static(..))
1848+
// && (cx.tcx.has_attr(item.owner_id, sym::no_mangle)
1849+
// || cx.tcx.has_attr(item.owner_id, sym::export_name))
1850+
if matches!(item.kind, hir::ItemKind::Static(..)) {
1851+
let is_exported_static = cx.tcx.get_all_attrs(item.owner_id).iter().any(|x| {
1852+
matches!(
1853+
x,
1854+
hir::Attribute::Parsed(
1855+
hir::attrs::AttributeKind::NoMangle(_)
1856+
| hir::attrs::AttributeKind::ExportName { .. }
1857+
)
1858+
)
1859+
});
1860+
if is_exported_static {
1861+
self.check_exported_static(cx, item.hir_id(), ty.span);
1862+
}
1863+
}
18081864
}
18091865
// See `check_fn` for declarations, `check_foreign_items` for definitions in extern blocks
18101866
hir::ItemKind::Fn { .. } => {}
@@ -1887,7 +1943,14 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesLint {
18871943
if !sig.header.abi.is_rustic_abi() {
18881944
let mir_sig = cx.tcx.fn_sig(local_id).instantiate_identity();
18891945
let mod_id = cx.tcx.parent_module_from_def_id(local_id).to_def_id();
1890-
self.check_foreign_fn(cx, CItemKind::ExportedFunction, mir_sig, sig.decl, mod_id, 0);
1946+
self.check_foreign_fn(
1947+
cx,
1948+
CItemKind::ExportedFunction,
1949+
mir_sig,
1950+
sig.decl,
1951+
mod_id,
1952+
0,
1953+
);
18911954
}
18921955
}
18931956
hir::TraitItemKind::Type(_, ty_maybe) => {

src/tools/miri/tests/fail/function_calls/exported_symbol_wrong_type.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#[no_mangle]
2+
#[allow(improper_c_var_definitions)]
23
static FOO: () = ();
34

45
fn main() {

tests/ui/lint/improper-ctypes/lint-ctypes.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
#![feature(pattern_type_macro)]
55

66
#![allow(private_interfaces)]
7-
#![deny(improper_ctypes)]
7+
#![deny(improper_ctypes, improper_ctypes_definitions)]
88

99
use std::cell::UnsafeCell;
1010
use std::marker::PhantomData;
@@ -138,6 +138,15 @@ extern "C" {
138138
pub fn good19(_: &String);
139139
}
140140

141+
static DEFAULT_U32: u32 = 42;
142+
#[no_mangle]
143+
static EXPORTED_STATIC: &u32 = &DEFAULT_U32;
144+
#[no_mangle]
145+
static EXPORTED_STATIC_BAD: &'static str = "is this reaching you, plugin?";
146+
//~^ ERROR: uses type `&str`
147+
#[export_name="EXPORTED_STATIC_MUT_BUT_RENAMED"]
148+
static mut EXPORTED_STATIC_MUT: &u32 = &DEFAULT_U32;
149+
141150
#[cfg(not(target_arch = "wasm32"))]
142151
extern "C" {
143152
pub fn good1(size: *const c_int);

tests/ui/lint/improper-ctypes/lint-ctypes.stderr

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ LL | pub fn slice_type(p: &[u32]);
99
note: the lint level is defined here
1010
--> $DIR/lint-ctypes.rs:7:9
1111
|
12-
LL | #![deny(improper_ctypes)]
12+
LL | #![deny(improper_ctypes, improper_ctypes_definitions)]
1313
| ^^^^^^^^^^^^^^^
1414

1515
error: `extern` block uses type `&str`, which is not FFI-safe
@@ -215,5 +215,19 @@ LL | pub fn no_niche_b(b: Option<UnsafeCell<&i32>>);
215215
= help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
216216
= note: enum has no representation hint
217217

218-
error: aborting due to 21 previous errors
218+
error: foreign-code-reachable static uses type `&str`, which is not FFI-safe
219+
--> $DIR/lint-ctypes.rs:145:29
220+
|
221+
LL | static EXPORTED_STATIC_BAD: &'static str = "is this reaching you, plugin?";
222+
| ^^^^^^^^^^^^ not FFI-safe
223+
|
224+
= help: consider using `*const u8` and a length instead
225+
= note: this reference to an unsized type contains metadata, which makes it incompatible with a C pointer
226+
note: the lint level is defined here
227+
--> $DIR/lint-ctypes.rs:7:26
228+
|
229+
LL | #![deny(improper_ctypes, improper_ctypes_definitions)]
230+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
231+
232+
error: aborting due to 22 previous errors
219233

tests/ui/statics/nested-allocations-dont-inherit-codegen-attrs.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
//@ build-pass
22

3+
#![allow(improper_c_var_definitions)]
4+
35
// Make sure that the nested static allocation for `FOO` doesn't inherit `no_mangle`.
46
#[no_mangle]
57
pub static mut FOO: &mut [i32] = &mut [42];

0 commit comments

Comments
 (0)