Skip to content

Commit f71cfba

Browse files
committed
lint ImproperCTypes: clean up exported static variables
1 parent b9199e0 commit f71cfba

File tree

3 files changed

+98
-61
lines changed

3 files changed

+98
-61
lines changed

compiler/rustc_lint/src/types/improper_ctypes.rs

Lines changed: 62 additions & 59 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::{Integer, IntegerType, VariantIdx};
7+
use rustc_ast::Mutability;
78
use rustc_data_structures::fx::FxHashSet;
89
use rustc_errors::DiagMessage;
910
use rustc_hir::def::CtorKind;
@@ -423,33 +424,43 @@ fn get_type_sizedness<'tcx, 'a>(cx: &'a LateContext<'tcx>, ty: Ty<'tcx>) -> Type
423424

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

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

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

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

@@ -1662,10 +1650,21 @@ impl ImproperCTypesLint {
16621650
}
16631651

16641652
/// Check that a `#[no_mangle]`/`#[export_name = _]` static variable is of a ffi-safe type
1665-
fn check_exported_static<'tcx>(&self, cx: &LateContext<'tcx>, id: hir::OwnerId, span: Span) {
1653+
fn check_exported_static<'tcx>(
1654+
&self,
1655+
cx: &LateContext<'tcx>,
1656+
id: hir::OwnerId,
1657+
span: Span,
1658+
is_mut: bool,
1659+
) {
16661660
let ty = cx.tcx.type_of(id).instantiate_identity();
16671661
let visitor = ImproperCTypesVisitor::new(cx);
1668-
let ffi_res = visitor.check_for_type(CTypesVisitorState::ExportedStaticTy, ty);
1662+
let state = if is_mut {
1663+
CTypesVisitorState::ExportedStaticMutTy
1664+
} else {
1665+
CTypesVisitorState::ExportedStaticTy
1666+
};
1667+
let ffi_res = visitor.check_for_type(state, ty);
16691668
self.process_ffi_result(cx, span, ffi_res, CItemKind::ExportedStatic);
16701669
}
16711670

@@ -1852,11 +1851,15 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesLint {
18521851
cx.tcx.type_of(item.owner_id).instantiate_identity(),
18531852
);
18541853

1855-
if matches!(item.kind, hir::ItemKind::Static(..))
1854+
if let hir::ItemKind::Static(_, _, is_mut, _) = item.kind
18561855
&& (cx.tcx.has_attr(item.owner_id, sym::no_mangle)
18571856
|| cx.tcx.has_attr(item.owner_id, sym::export_name))
18581857
{
1859-
self.check_exported_static(cx, item.owner_id, ty.span);
1858+
let is_mut = match is_mut {
1859+
Mutability::Not => false,
1860+
Mutability::Mut => true,
1861+
};
1862+
self.check_exported_static(cx, item.owner_id, ty.span, is_mut);
18601863
}
18611864
}
18621865
// 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)