Skip to content

Commit c105896

Browse files
committed
lint ImproperCTypes: clean up exported static variables
1 parent 9dedb0c commit c105896

File tree

3 files changed

+113
-64
lines changed

3 files changed

+113
-64
lines changed

compiler/rustc_lint/src/types/improper_ctypes.rs

Lines changed: 77 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use std::iter;
44
use std::ops::ControlFlow;
55

66
use rustc_abi::VariantIdx;
7+
use rustc_ast::Mutability;
78
use rustc_data_structures::fx::FxHashSet;
89
use rustc_errors::DiagMessage;
910
use rustc_hir::def::CtorKind;
@@ -422,33 +423,43 @@ fn get_type_sizedness<'tcx, 'a>(cx: &'a LateContext<'tcx>, ty: Ty<'tcx>) -> Type
422423

423424
#[allow(non_snake_case)]
424425
mod CTypesVisitorStateFlags {
425-
pub(super) const NO_FLAGS: u8 = 0b00000;
426+
pub(super) const NO_FLAGS: u8 = 0b000000;
426427
/// for use in (externally-linked) static variables
427-
pub(super) const STATIC: u8 = 0b00001;
428+
pub(super) const STATIC: u8 = 0b000001;
428429
/// for use in functions in general
429-
pub(super) const FUNC: u8 = 0b00010;
430+
pub(super) const FUNC: u8 = 0b000010;
430431
/// for variables in function returns (implicitly: not for static variables)
431-
pub(super) const FN_RETURN: u8 = 0b00100;
432-
/// for variables in functions which are defined in rust (implicitly: not for static variables)
433-
pub(super) const FN_DEFINED: u8 = 0b01000;
434-
/// for time where we are only defining the type of something
432+
pub(super) const FN_RETURN: u8 = 0b000100;
433+
/// for variables in functions/variables which are defined in rust
434+
pub(super) const DEFINED: u8 = 0b001000;
435+
/// for times where we are only defining the type of something
435436
/// (struct/enum/union definitions, FnPtrs)
436-
pub(super) const THEORETICAL: u8 = 0b10000;
437+
pub(super) const THEORETICAL: u8 = 0b010000;
438+
/// if we are looking at an interface where the value can be set by the non-rust side
439+
/// (important for e.g. nonzero assumptions)
440+
pub(super) const FOREIGN_VALUES: u8 = 0b100000;
437441
}
438442

439443
#[repr(u8)]
440444
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
441445
enum CTypesVisitorState {
442446
None = CTypesVisitorStateFlags::NO_FLAGS,
443447
// uses bitflags from CTypesVisitorStateFlags
444-
StaticTy = CTypesVisitorStateFlags::STATIC,
445-
ExportedStaticTy = CTypesVisitorStateFlags::STATIC | CTypesVisitorStateFlags::FN_DEFINED,
446-
ArgumentTyInDefinition = CTypesVisitorStateFlags::FUNC | CTypesVisitorStateFlags::FN_DEFINED,
448+
StaticTy = CTypesVisitorStateFlags::STATIC | CTypesVisitorStateFlags::FOREIGN_VALUES,
449+
ExportedStaticTy = CTypesVisitorStateFlags::STATIC | CTypesVisitorStateFlags::DEFINED,
450+
ExportedStaticMutTy = CTypesVisitorStateFlags::STATIC
451+
| CTypesVisitorStateFlags::DEFINED
452+
| CTypesVisitorStateFlags::FOREIGN_VALUES,
453+
ArgumentTyInDefinition = CTypesVisitorStateFlags::FUNC
454+
| CTypesVisitorStateFlags::DEFINED
455+
| CTypesVisitorStateFlags::FOREIGN_VALUES,
447456
ReturnTyInDefinition = CTypesVisitorStateFlags::FUNC
448457
| CTypesVisitorStateFlags::FN_RETURN
449-
| CTypesVisitorStateFlags::FN_DEFINED,
458+
| CTypesVisitorStateFlags::DEFINED,
450459
ArgumentTyInDeclaration = CTypesVisitorStateFlags::FUNC,
451-
ReturnTyInDeclaration = CTypesVisitorStateFlags::FUNC | CTypesVisitorStateFlags::FN_RETURN,
460+
ReturnTyInDeclaration = CTypesVisitorStateFlags::FUNC
461+
| CTypesVisitorStateFlags::FN_RETURN
462+
| CTypesVisitorStateFlags::FOREIGN_VALUES,
452463
ArgumentTyInFnPtr = CTypesVisitorStateFlags::FUNC | CTypesVisitorStateFlags::THEORETICAL,
453464
ReturnTyInFnPtr = CTypesVisitorStateFlags::FUNC
454465
| CTypesVisitorStateFlags::THEORETICAL
@@ -489,58 +500,35 @@ impl CTypesVisitorState {
489500
/// to be treated as an opaque type on the other side of the FFI boundary
490501
fn is_in_defined_function(self) -> bool {
491502
use CTypesVisitorStateFlags::*;
492-
let ret = ((self as u8) & FN_DEFINED) != 0;
493-
#[cfg(debug_assertions)]
494-
if ret {
495-
assert!(self.is_in_function());
496-
}
497-
ret
498-
}
499-
/// whether we the type is used (directly or not) in a function pointer type
500-
fn is_in_fn_ptr(self) -> bool {
501-
use CTypesVisitorStateFlags::*;
502-
((self as u8) & THEORETICAL) != 0 && self.is_in_function()
503+
((self as u8) & DEFINED) != 0 && self.is_in_function()
503504
}
504505

505506
/// whether we can expect type parameters and co in a given type
506507
fn can_expect_ty_params(self) -> bool {
507508
use CTypesVisitorStateFlags::*;
508-
// rust-defined functions, as well as FnPtrs and ADT definitions
509-
if ((self as u8) & THEORETICAL) != 0 {
510-
true
511-
} else {
512-
((self as u8) & FN_DEFINED) != 0 && ((self as u8) & STATIC) == 0
513-
}
509+
// rust-defined functions, as well as FnPtrs
510+
((self as u8) & THEORETICAL) != 0 || self.is_in_defined_function()
514511
}
515512

516513
/// whether the value for that type might come from the non-rust side of a FFI boundary
517514
/// this is particularly useful for non-raw pointers, since rust assume they are non-null
518515
fn value_may_be_unchecked(self) -> bool {
519-
if self.is_in_static() {
520-
// FIXME: this is evidently untrue for non-mut static variables
521-
// (assuming the cross-FFI code respects this)
522-
true
523-
} else if self.is_in_defined_function() {
524-
// function definitions are assumed to be maybe-not-rust-caller, rust-callee
525-
!self.is_in_function_return()
526-
} else if self.is_in_fn_ptr() {
527-
// 4 cases for function pointers:
528-
// - rust caller, rust callee: everything comes from rust
529-
// - non-rust-caller, non-rust callee: declaring invariants that are not valid
530-
// is suboptimal, but ultimately not our problem
531-
// - non-rust-caller, rust callee: there will be a function declaration somewhere,
532-
// let's assume it will raise the appropriate warning in our stead
533-
// - rust caller, non-rust callee: it's possible that the function is a callback,
534-
// not something from a pre-declared API.
535-
// so, in theory, we need to care about the function return being possibly non-rust-controlled.
536-
// sadly, we need to ignore this because making pointers out of rust-defined functions
537-
// would force to systematically cast or overwrap their return types...
538-
// FIXME: is there anything better we can do here?
539-
false
540-
} else {
541-
// function declarations are assumed to be rust-caller, non-rust-callee
542-
self.is_in_function_return()
543-
}
516+
// function definitions are assumed to be maybe-not-rust-caller, rust-callee
517+
// function declarations are assumed to be rust-caller, non-rust-callee
518+
// 4 cases for function pointers:
519+
// - rust caller, rust callee: everything comes from rust
520+
// - non-rust-caller, non-rust callee: declaring invariants that are not valid
521+
// is suboptimal, but ultimately not our problem
522+
// - non-rust-caller, rust callee: there will be a function declaration somewhere,
523+
// let's assume it will raise the appropriate warning in our stead
524+
// - rust caller, non-rust callee: it's possible that the function is a callback,
525+
// not something from a pre-declared API.
526+
// so, in theory, we need to care about the function return being possibly non-rust-controlled.
527+
// sadly, we need to ignore this because making pointers out of rust-defined functions
528+
// would force to systematically cast or overwrap their return types...
529+
// FIXME: is there anything better we can do here?
530+
use CTypesVisitorStateFlags::*;
531+
((self as u8) & FOREIGN_VALUES) != 0
544532
}
545533
}
546534

@@ -1654,10 +1642,21 @@ impl ImproperCTypesLint {
16541642
}
16551643

16561644
/// Check that a `#[no_mangle]`/`#[export_name = _]` static variable is of a ffi-safe type
1657-
fn check_exported_static<'tcx>(&self, cx: &LateContext<'tcx>, id: hir::OwnerId, span: Span) {
1645+
fn check_exported_static<'tcx>(
1646+
&self,
1647+
cx: &LateContext<'tcx>,
1648+
id: hir::OwnerId,
1649+
span: Span,
1650+
is_mut: bool,
1651+
) {
16581652
let ty = cx.tcx.type_of(id).instantiate_identity();
16591653
let visitor = ImproperCTypesVisitor::new(cx);
1660-
let ffi_res = visitor.check_for_type(CTypesVisitorState::ExportedStaticTy, ty);
1654+
let state = if is_mut {
1655+
CTypesVisitorState::ExportedStaticMutTy
1656+
} else {
1657+
CTypesVisitorState::ExportedStaticTy
1658+
};
1659+
let ffi_res = visitor.check_for_type(state, ty);
16611660
self.process_ffi_result(cx, span, ffi_res, CItemKind::ExportedStatic);
16621661
}
16631662

@@ -1844,11 +1843,27 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesLint {
18441843
cx.tcx.type_of(item.owner_id).instantiate_identity(),
18451844
);
18461845

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-
{
1851-
self.check_exported_static(cx, item.owner_id, ty.span);
1846+
// FIXME: cx.tcx.has_attr no worky
1847+
// if let hir::ItemKind::Static(is_mut, _, _, _) = item.kind
1848+
// && (cx.tcx.has_attr(item.owner_id, sym::no_mangle)
1849+
// || cx.tcx.has_attr(item.owner_id, sym::export_name))
1850+
if let hir::ItemKind::Static(is_mut, _, _, _) = item.kind {
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+
let is_mut = match is_mut {
1862+
Mutability::Not => false,
1863+
Mutability::Mut => true,
1864+
};
1865+
self.check_exported_static(cx, item.owner_id, ty.span, is_mut);
1866+
}
18521867
}
18531868
}
18541869
// See `check_fn` for declarations, `check_foreign_items` for definitions in extern blocks

tests/ui/lint/improper_ctypes/ctypes.rs

Lines changed: 11 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,16 @@ 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+
//~^ ERROR: uses type `&u32`
150+
141151
#[cfg(not(target_arch = "wasm32"))]
142152
extern "C" {
143153
pub fn good1(size: *const c_int);

tests/ui/lint/improper_ctypes/ctypes.stderr

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,5 +208,29 @@ 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/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/ctypes.rs:8:36
221+
|
222+
LL | #![deny(improper_c_fn_definitions, improper_c_var_definitions)]
223+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
224+
225+
error: foreign-code-reachable static uses type `&u32`, which is not FFI-safe
226+
--> $DIR/ctypes.rs:148:33
227+
|
228+
LL | static mut EXPORTED_STATIC_MUT: &u32 = &DEFAULT_U32;
229+
| ^^^^ not FFI-safe
230+
|
231+
= help: consider using a raw pointer, or wrapping `&u32` in an `Option<_>`
232+
= note: boxes, references, and function pointers are assumed to be valid (non-null, non-dangling, aligned) pointers,
233+
which cannot be garanteed if their values are produced by non-rust code
234+
235+
error: aborting due to 22 previous errors
212236

0 commit comments

Comments
 (0)