Skip to content

Commit ea86322

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 e59850a commit ea86322

File tree

7 files changed

+102
-3
lines changed

7 files changed

+102
-3
lines changed

compiler/rustc_lint/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,7 @@ fn register_builtins(store: &mut LintStore) {
339339
"improper_c_boundaries",
340340
IMPROPER_C_CALLBACKS,
341341
IMPROPER_C_FN_DEFINITIONS,
342+
IMPROPER_C_VAR_DEFINITIONS,
342343
IMPROPER_CTYPES
343344
);
344345

compiler/rustc_lint/src/types.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ use {rustc_ast as ast, rustc_hir as hir};
1212

1313
mod improper_ctypes; // these filed do the implementation for ImproperCTypesDefinitions,ImproperCTypesDeclarations
1414
pub(crate) use improper_ctypes::{
15-
IMPROPER_C_CALLBACKS, IMPROPER_C_FN_DEFINITIONS, IMPROPER_CTYPES, ImproperCTypesLint,
15+
IMPROPER_C_CALLBACKS, IMPROPER_C_FN_DEFINITIONS, IMPROPER_C_VAR_DEFINITIONS, IMPROPER_CTYPES,
16+
ImproperCTypesLint,
1617
};
1718

1819
use crate::lints::{

compiler/rustc_lint/src/types/improper_ctypes.rs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,37 @@ declare_lint! {
7979
"proper use of libc types in foreign item definitions"
8080
}
8181

82+
declare_lint! {
83+
/// The `improper_c_var_definitions` lint detects incorrect use of
84+
/// [`no_mangle`] and [`export_name`] static variable definitions.
85+
/// (In other words, static variables accessible by name by foreign code.)
86+
///
87+
/// [`no_mangle`]: https://doc.rust-lang.org/stable/reference/abi.html#the-no_mangle-attribute
88+
/// [`export_name`]: https://doc.rust-lang.org/stable/reference/abi.html#the-export_name-attribute
89+
///
90+
/// ### Example
91+
///
92+
/// ```rust
93+
/// # #[unsafe(no_mangle)]
94+
/// # #[used]
95+
/// static mut PLUGIN_ABI_MIN_VERSION: &'static str = "0.0.5";
96+
/// ```
97+
///
98+
/// {{produces}}
99+
///
100+
/// ### Explanation
101+
///
102+
/// The compiler has several checks to verify that types used in
103+
/// static variables exposed to foreign code are safe and follow
104+
/// certain rules to ensure proper compatibility with the foreign interfaces.
105+
/// This lint is issued when it detects a probable mistake in a definition.
106+
/// The lint usually should provide a description of the issue,
107+
/// along with possibly a hint on how to resolve it.
108+
pub(crate) IMPROPER_C_VAR_DEFINITIONS,
109+
Warn,
110+
"proper use of libc types in foreign-reachable static variable definitions"
111+
}
112+
82113
declare_lint! {
83114
/// The `improper_c_callbacks` lint detects incorrect use of
84115
/// [`extern` function] pointers.
@@ -165,6 +196,7 @@ declare_lint! {
165196
declare_lint_pass!(ImproperCTypesLint => [
166197
IMPROPER_CTYPES,
167198
IMPROPER_C_FN_DEFINITIONS,
199+
IMPROPER_C_VAR_DEFINITIONS,
168200
IMPROPER_C_CALLBACKS,
169201
USES_POWER_ALIGNMENT,
170202
]);
@@ -316,6 +348,8 @@ enum CItemKind {
316348
ImportedExtern,
317349
/// `extern "C"` function definitions, to be used elsewhere -> IMPROPER_C_FN_DEFINITIONS,
318350
ExportedFunction,
351+
/// `no_mangle`/`export_name` static variables, assumed to be used from across an FFI boundary,
352+
ExportedStatic,
319353
/// `extern "C"` function pointers -> IMPROPER_C_CALLBACKS,
320354
Callback,
321355
}
@@ -691,6 +725,8 @@ struct VisitorState {
691725
impl PersistentStateFlags {
692726
// The values that can be set.
693727
const STATIC_TY: Self = Self::STATIC;
728+
const EXPORTED_STATIC_TY: Self =
729+
Self::from_bits(Self::STATIC.bits() | Self::DEFINED.bits()).unwrap();
694730
const ARGUMENT_TY_IN_DEFINITION: Self =
695731
Self::from_bits(Self::FUNC.bits() | Self::DEFINED.bits()).unwrap();
696732
const RETURN_TY_IN_DEFINITION: Self =
@@ -792,6 +828,12 @@ impl VisitorState {
792828
Self::entry_point(PersistentStateFlags::STATIC_TY)
793829
}
794830

831+
/// Get the proper visitor state for a locally-defined static variable's type
832+
#[inline]
833+
fn static_var_def() -> Self {
834+
Self::entry_point(PersistentStateFlags::EXPORTED_STATIC_TY)
835+
}
836+
795837
/// Whether the type is used as the type of a static variable.
796838
fn is_direct_in_static(&self) -> bool {
797839
let ret = self.persistent_flags.contains(PersistentStateFlags::STATIC);
@@ -1716,6 +1758,14 @@ impl<'tcx> ImproperCTypesLint {
17161758
self.process_ffi_result(cx, span, ffi_res, CItemKind::ImportedExtern);
17171759
}
17181760

1761+
/// Check that a `#[no_mangle]`/`#[export_name = _]` static variable is of a ffi-safe type.
1762+
fn check_exported_static(&self, cx: &LateContext<'tcx>, id: hir::OwnerId, span: Span) {
1763+
let ty = cx.tcx.type_of(id).instantiate_identity();
1764+
let mut visitor = ImproperCTypesVisitor::new(cx);
1765+
let ffi_res = visitor.check_type(VisitorState::static_var_def(), ty);
1766+
self.process_ffi_result(cx, span, ffi_res, CItemKind::ExportedStatic);
1767+
}
1768+
17191769
/// Check if a function's argument types and result type are "ffi-safe".
17201770
fn check_foreign_fn(
17211771
&mut self,
@@ -1816,11 +1866,13 @@ impl<'tcx> ImproperCTypesLint {
18161866
let lint = match fn_mode {
18171867
CItemKind::ImportedExtern => IMPROPER_CTYPES,
18181868
CItemKind::ExportedFunction => IMPROPER_C_FN_DEFINITIONS,
1869+
CItemKind::ExportedStatic => IMPROPER_C_VAR_DEFINITIONS,
18191870
CItemKind::Callback => IMPROPER_C_CALLBACKS,
18201871
};
18211872
let desc = match fn_mode {
18221873
CItemKind::ImportedExtern => "`extern` block",
18231874
CItemKind::ExportedFunction => "`extern` fn",
1875+
CItemKind::ExportedStatic => "foreign-code-reachable static",
18241876
CItemKind::Callback => "`extern` callback",
18251877
};
18261878
for reason in reasons.iter_mut() {
@@ -1888,6 +1940,25 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesLint {
18881940
ty,
18891941
cx.tcx.type_of(item.owner_id).instantiate_identity(),
18901942
);
1943+
1944+
// FIXME: cx.tcx.has_attr no worky
1945+
// if matches!(item.kind, hir::ItemKind::Static(..))
1946+
// && (cx.tcx.has_attr(item.owner_id, sym::no_mangle)
1947+
// || cx.tcx.has_attr(item.owner_id, sym::export_name))
1948+
if matches!(item.kind, hir::ItemKind::Static(..)) {
1949+
let is_exported_static = cx.tcx.get_all_attrs(item.owner_id).iter().any(|x| {
1950+
matches!(
1951+
x,
1952+
hir::Attribute::Parsed(
1953+
hir::attrs::AttributeKind::NoMangle(_)
1954+
| hir::attrs::AttributeKind::ExportName { .. }
1955+
)
1956+
)
1957+
});
1958+
if is_exported_static {
1959+
self.check_exported_static(cx, item.owner_id, ty.span);
1960+
}
1961+
}
18911962
}
18921963
// See `check_fn` for declarations, `check_foreign_items` for definitions in extern blocks
18931964
hir::ItemKind::Fn { .. } => {}

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
@@ -5,7 +5,7 @@
55

66
#![allow(private_interfaces)]
77
#![deny(improper_ctypes, improper_c_callbacks)]
8-
#![deny(improper_c_fn_definitions)]
8+
#![deny(improper_c_fn_definitions, improper_c_var_definitions)]
99

1010
use std::cell::UnsafeCell;
1111
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: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,5 +208,19 @@ LL | pub fn no_niche_b(b: Option<UnsafeCell<&i32>>);
208208
= help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
209209
= note: enum has no representation hint
210210

211-
error: aborting due to 20 previous errors
211+
error: foreign-code-reachable static uses type `&str`, which is not FFI-safe
212+
--> $DIR/lint-ctypes.rs:145:29
213+
|
214+
LL | static EXPORTED_STATIC_BAD: &'static str = "is this reaching you, plugin?";
215+
| ^^^^^^^^^^^^ not FFI-safe
216+
|
217+
= help: consider using `*const u8` and a length instead
218+
= note: this reference to an unsized type contains metadata, which makes it incompatible with a C pointer
219+
note: the lint level is defined here
220+
--> $DIR/lint-ctypes.rs:8:36
221+
|
222+
LL | #![deny(improper_c_fn_definitions, improper_c_var_definitions)]
223+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
224+
225+
error: aborting due to 21 previous errors
212226

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)