Skip to content

Commit a01a594

Browse files
committed
lint ImproperCTypes: clean up exported static variables
1 parent 4906ee4 commit a01a594

File tree

3 files changed

+92
-69
lines changed

3 files changed

+92
-69
lines changed

compiler/rustc_lint/src/types/improper_ctypes.rs

Lines changed: 56 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::iter;
33
use std::ops::ControlFlow;
44

55
use rustc_abi::{Integer, IntegerType, VariantIdx};
6+
use rustc_ast::Mutability;
67
use rustc_data_structures::fx::FxHashSet;
78
use rustc_errors::DiagMessage;
89
use rustc_hir::def::CtorKind;
@@ -422,49 +423,50 @@ 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
455466
| CTypesVisitorStateFlags::FN_RETURN,
456467
}
457468

458469
impl CTypesVisitorState {
459-
/// whether the type is used in a static variable
460-
fn is_in_static(self) -> bool {
461-
use CTypesVisitorStateFlags::*;
462-
let ret = ((self as u8) & STATIC) != 0;
463-
if ret {
464-
assert!(((self as u8) & FUNC) == 0);
465-
}
466-
ret
467-
}
468470
/// whether the type is used in a function
469471
fn is_in_function(self) -> bool {
470472
use CTypesVisitorStateFlags::*;
@@ -489,58 +491,35 @@ impl CTypesVisitorState {
489491
/// to be treated as an opaque type on the other side of the FFI boundary
490492
fn is_in_defined_function(self) -> bool {
491493
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()
494+
((self as u8) & DEFINED) != 0 && self.is_in_function()
503495
}
504496

505497
/// whether we can expect type parameters and co in a given type
506498
fn can_expect_ty_params(self) -> bool {
507499
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-
}
500+
// rust-defined functions, as well as FnPtrs
501+
((self as u8) & THEORETICAL) != 0 || self.is_in_defined_function()
514502
}
515503

516504
/// whether the value for that type might come from the non-rust side of a FFI boundary
517505
/// this is particularly useful for non-raw pointers, since rust assume they are non-null
518506
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-
}
507+
// function definitions are assumed to be maybe-not-rust-caller, rust-callee
508+
// function declarations are assumed to be rust-caller, non-rust-callee
509+
// 4 cases for function pointers:
510+
// - rust caller, rust callee: everything comes from rust
511+
// - non-rust-caller, non-rust callee: declaring invariants that are not valid
512+
// is suboptimal, but ultimately not our problem
513+
// - non-rust-caller, rust callee: there will be a function declaration somewhere,
514+
// let's assume it will raise the appropriate warning in our stead
515+
// - rust caller, non-rust callee: it's possible that the function is a callback,
516+
// not something from a pre-declared API.
517+
// so, in theory, we need to care about the function return being possibly non-rust-controlled.
518+
// sadly, we need to ignore this because making pointers out of rust-defined functions
519+
// would force to systematically cast or overwrap their return types...
520+
// FIXME: is there anything better we can do here?
521+
use CTypesVisitorStateFlags::*;
522+
((self as u8) & FOREIGN_VALUES) != 0
544523
}
545524
}
546525

@@ -1620,10 +1599,16 @@ impl ImproperCTypesLint {
16201599
cx: &LateContext<'tcx>,
16211600
id: hir::OwnerId,
16221601
span: Span,
1602+
is_mut: bool,
16231603
) {
16241604
let ty = cx.tcx.type_of(id).instantiate_identity();
16251605
let mut visitor = ImproperCTypesVisitor::new(cx);
1626-
let ffi_res = visitor.check_for_type(CTypesVisitorState::ExportedStaticTy, ty);
1606+
let state = if is_mut {
1607+
CTypesVisitorState::ExportedStaticMutTy
1608+
} else {
1609+
CTypesVisitorState::ExportedStaticTy
1610+
};
1611+
let ffi_res = visitor.check_for_type(state, ty);
16271612
self.process_ffi_result(cx, span, ffi_res, CItemKind::ExportedStatic);
16281613
}
16291614

@@ -1810,11 +1795,15 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesLint {
18101795
cx.tcx.type_of(item.owner_id).instantiate_identity(),
18111796
);
18121797

1813-
if matches!(item.kind, hir::ItemKind::Static(..))
1798+
if let hir::ItemKind::Static(_, _, is_mut, _) = item.kind
18141799
&& (cx.tcx.has_attr(item.owner_id, sym::no_mangle)
18151800
|| cx.tcx.has_attr(item.owner_id, sym::export_name))
18161801
{
1817-
self.check_exported_static(cx, item.owner_id, ty.span);
1802+
let is_mut = match is_mut {
1803+
Mutability::Not => false,
1804+
Mutability::Mut => true,
1805+
};
1806+
self.check_exported_static(cx, item.owner_id, ty.span, is_mut);
18181807
}
18191808
}
18201809
// 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;
@@ -140,6 +140,16 @@ extern "C" {
140140
pub fn good19(_: &String);
141141
}
142142

143+
static DEFAULT_U32: u32 = 42;
144+
#[no_mangle]
145+
static EXPORTED_STATIC: &u32 = &DEFAULT_U32;
146+
#[no_mangle]
147+
static EXPORTED_STATIC_BAD: &'static str = "is this reaching you, plugin?";
148+
//~^ ERROR: uses type `&str`
149+
#[export_name="EXPORTED_STATIC_MUT_BUT_RENAMED"]
150+
static mut EXPORTED_STATIC_MUT: &u32 = &DEFAULT_U32;
151+
//~^ ERROR: uses type `&u32`
152+
143153
#[cfg(not(target_arch = "wasm32"))]
144154
extern "C" {
145155
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
@@ -254,5 +254,29 @@ LL | pub static static_u128_array_type: [u128; 16];
254254
|
255255
= note: 128-bit integers don't currently have a known stable ABI
256256

257-
error: aborting due to 25 previous errors
257+
error: foreign-code-reachable static uses type `&str`, which is not FFI-safe
258+
--> $DIR/ctypes.rs:147:29
259+
|
260+
LL | static EXPORTED_STATIC_BAD: &'static str = "is this reaching you, plugin?";
261+
| ^^^^^^^^^^^^ not FFI-safe
262+
|
263+
= help: consider using `*const u8` and a length instead
264+
= note: this reference to an unsized type contains metadata, which makes it incompatible with a C pointer
265+
note: the lint level is defined here
266+
--> $DIR/ctypes.rs:8:36
267+
|
268+
LL | #![deny(improper_c_fn_definitions, improper_c_var_definitions)]
269+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
270+
271+
error: foreign-code-reachable static uses type `&u32`, which is not FFI-safe
272+
--> $DIR/ctypes.rs:150:33
273+
|
274+
LL | static mut EXPORTED_STATIC_MUT: &u32 = &DEFAULT_U32;
275+
| ^^^^ not FFI-safe
276+
|
277+
= help: consider using a raw pointer, or wrapping `&u32` in an `Option<_>`
278+
= note: boxes, references, and function pointers are assumed to be valid (non-null, non-dangling, aligned) pointers,
279+
which cannot be garanteed if their values are produced by non-rust code
280+
281+
error: aborting due to 27 previous errors
258282

0 commit comments

Comments
 (0)