Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit cc72a92

Browse files
committedJul 29, 2024·
Lint against &T to &mut T and &T to &UnsafeCell<T> transmutes
This adds the lint against `&T`->`&UnsafeCell<T>` transmutes, and also check in struct fields, and reference casts (`&*(&a as *const u8 as *const UnsafeCell<u8>)`). The code is quite complex; I've tried my best to simplify and comment it. This is missing one parts: array transmutes. When transmuting an array, this only consider the first element. The reason for that is that the code is already quite complex, and I didn't want to complicate it more. This catches the most common pattern of transmuting an array into an array of the same length with type of the same size; more complex cases are likely not properly handled. We could take a bigger sample, for example the first and last elements to increase the chance that the lint will catch mistakes, but then the runtime complexity becomes exponential with the nesting of the arrays (`[[[[[T; 2]; 2]; 2]; 2]; 2]` has complexity of O(2**5), for instance).
1 parent 4db3d12 commit cc72a92

35 files changed

+1120
-75
lines changed
 

‎compiler/rustc_lint/messages.ftl

+10
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ lint_builtin_missing_doc = missing documentation for {$article} {$desc}
119119
120120
lint_builtin_mutable_transmutes =
121121
transmuting &T to &mut T is undefined behavior, even if the reference is unused, consider instead using an UnsafeCell
122+
.note = transmute from `{$from}` to `{$to}`
122123
123124
lint_builtin_no_mangle_fn = declaration of a `no_mangle` function
124125
lint_builtin_no_mangle_generic = functions generic over types or consts must be mangled
@@ -169,6 +170,10 @@ lint_builtin_unreachable_pub = unreachable `pub` {$what}
169170
170171
lint_builtin_unsafe_block = usage of an `unsafe` block
171172
173+
lint_builtin_unsafe_cell_transmutes =
174+
transmuting &T to &UnsafeCell<T> is undefined behavior, even if the reference is unused, consider using UnsafeCell on the original data
175+
.note = transmute from `{$from}` to `{$to}`
176+
172177
lint_builtin_unsafe_extern_block = usage of an `unsafe extern` block
173178
174179
lint_builtin_unsafe_impl = implementation of an `unsafe` trait
@@ -862,6 +867,11 @@ lint_unsafe_attr_outside_unsafe = unsafe attribute used without unsafe
862867
.label = usage of unsafe attribute
863868
lint_unsafe_attr_outside_unsafe_suggestion = wrap the attribute in `unsafe(...)`
864869
870+
lint_unsafe_cell_reference_casting =
871+
casting `&T` to `&UnsafeCell<T>` is undefined behavior, even if the reference is unused, consider using an `UnsafeCell` on the original data
872+
.label = casting happend here
873+
.note = cast from `{$from}` to `{$to}`
874+
865875
lint_unsupported_group = `{$lint_group}` lint group is not supported with ´--force-warn´
866876
867877
lint_untranslatable_diag = diagnostics should be created using translatable messages

‎compiler/rustc_lint/src/builtin.rs

+5-73
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,11 @@ use crate::lints::{
6161
BuiltinEllipsisInclusiveRangePatternsLint, BuiltinExplicitOutlives,
6262
BuiltinExplicitOutlivesSuggestion, BuiltinFeatureIssueNote, BuiltinIncompleteFeatures,
6363
BuiltinIncompleteFeaturesHelp, BuiltinInternalFeatures, BuiltinKeywordIdents,
64-
BuiltinMissingCopyImpl, BuiltinMissingDebugImpl, BuiltinMissingDoc, BuiltinMutablesTransmutes,
65-
BuiltinNoMangleGeneric, BuiltinNonShorthandFieldPatterns, BuiltinSpecialModuleNameUsed,
66-
BuiltinTrivialBounds, BuiltinTypeAliasBounds, BuiltinUngatedAsyncFnTrackCaller,
67-
BuiltinUnpermittedTypeInit, BuiltinUnpermittedTypeInitSub, BuiltinUnreachablePub,
68-
BuiltinUnsafe, BuiltinUnstableFeatures, BuiltinUnusedDocComment, BuiltinUnusedDocCommentSub,
69-
BuiltinWhileTrue, InvalidAsmLabel,
64+
BuiltinMissingCopyImpl, BuiltinMissingDebugImpl, BuiltinMissingDoc, BuiltinNoMangleGeneric,
65+
BuiltinNonShorthandFieldPatterns, BuiltinSpecialModuleNameUsed, BuiltinTrivialBounds,
66+
BuiltinTypeAliasBounds, BuiltinUngatedAsyncFnTrackCaller, BuiltinUnpermittedTypeInit,
67+
BuiltinUnpermittedTypeInitSub, BuiltinUnreachablePub, BuiltinUnsafe, BuiltinUnstableFeatures,
68+
BuiltinUnusedDocComment, BuiltinUnusedDocCommentSub, BuiltinWhileTrue, InvalidAsmLabel,
7069
};
7170
use crate::nonstandard_style::{method_context, MethodLateContext};
7271
use crate::{
@@ -1100,72 +1099,6 @@ impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems {
11001099
}
11011100
}
11021101

1103-
declare_lint! {
1104-
/// The `mutable_transmutes` lint catches transmuting from `&T` to `&mut
1105-
/// T` because it is [undefined behavior].
1106-
///
1107-
/// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
1108-
///
1109-
/// ### Example
1110-
///
1111-
/// ```rust,compile_fail
1112-
/// unsafe {
1113-
/// let y = std::mem::transmute::<&i32, &mut i32>(&5);
1114-
/// }
1115-
/// ```
1116-
///
1117-
/// {{produces}}
1118-
///
1119-
/// ### Explanation
1120-
///
1121-
/// Certain assumptions are made about aliasing of data, and this transmute
1122-
/// violates those assumptions. Consider using [`UnsafeCell`] instead.
1123-
///
1124-
/// [`UnsafeCell`]: https://doc.rust-lang.org/std/cell/struct.UnsafeCell.html
1125-
MUTABLE_TRANSMUTES,
1126-
Deny,
1127-
"transmuting &T to &mut T is undefined behavior, even if the reference is unused"
1128-
}
1129-
1130-
declare_lint_pass!(MutableTransmutes => [MUTABLE_TRANSMUTES]);
1131-
1132-
impl<'tcx> LateLintPass<'tcx> for MutableTransmutes {
1133-
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) {
1134-
if let Some((&ty::Ref(_, _, from_mutbl), &ty::Ref(_, _, to_mutbl))) =
1135-
get_transmute_from_to(cx, expr).map(|(ty1, ty2)| (ty1.kind(), ty2.kind()))
1136-
{
1137-
if from_mutbl < to_mutbl {
1138-
cx.emit_span_lint(MUTABLE_TRANSMUTES, expr.span, BuiltinMutablesTransmutes);
1139-
}
1140-
}
1141-
1142-
fn get_transmute_from_to<'tcx>(
1143-
cx: &LateContext<'tcx>,
1144-
expr: &hir::Expr<'_>,
1145-
) -> Option<(Ty<'tcx>, Ty<'tcx>)> {
1146-
let def = if let hir::ExprKind::Path(ref qpath) = expr.kind {
1147-
cx.qpath_res(qpath, expr.hir_id)
1148-
} else {
1149-
return None;
1150-
};
1151-
if let Res::Def(DefKind::Fn, did) = def {
1152-
if !def_id_is_transmute(cx, did) {
1153-
return None;
1154-
}
1155-
let sig = cx.typeck_results().node_type(expr.hir_id).fn_sig(cx.tcx);
1156-
let from = sig.inputs().skip_binder()[0];
1157-
let to = sig.output().skip_binder();
1158-
return Some((from, to));
1159-
}
1160-
None
1161-
}
1162-
1163-
fn def_id_is_transmute(cx: &LateContext<'_>, def_id: DefId) -> bool {
1164-
cx.tcx.is_intrinsic(def_id, sym::transmute)
1165-
}
1166-
}
1167-
}
1168-
11691102
declare_lint! {
11701103
/// The `unstable_features` lint detects uses of `#![feature]`.
11711104
///
@@ -1612,7 +1545,6 @@ declare_lint_pass!(
16121545
UNUSED_DOC_COMMENTS,
16131546
NO_MANGLE_CONST_ITEMS,
16141547
NO_MANGLE_GENERIC_ITEMS,
1615-
MUTABLE_TRANSMUTES,
16161548
UNSTABLE_FEATURES,
16171549
UNREACHABLE_PUB,
16181550
TYPE_ALIAS_BOUNDS,

‎compiler/rustc_lint/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ mod macro_expr_fragment_specifier_2024_migration;
6565
mod map_unit_fn;
6666
mod methods;
6767
mod multiple_supertrait_upcastable;
68+
mod mutable_transmutes;
6869
mod non_ascii_idents;
6970
mod non_fmt_panic;
7071
mod non_local_def;
@@ -99,6 +100,7 @@ use macro_expr_fragment_specifier_2024_migration::*;
99100
use map_unit_fn::*;
100101
use methods::*;
101102
use multiple_supertrait_upcastable::*;
103+
use mutable_transmutes::*;
102104
use non_ascii_idents::*;
103105
use non_fmt_panic::NonPanicFmt;
104106
use non_local_def::*;
@@ -209,6 +211,7 @@ late_lint_methods!(
209211
// Depends on referenced function signatures in expressions
210212
PtrNullChecks: PtrNullChecks,
211213
MutableTransmutes: MutableTransmutes,
214+
UnsafeCellReferenceCasting: UnsafeCellReferenceCasting,
212215
TypeAliasBounds: TypeAliasBounds,
213216
TrivialConstraints: TrivialConstraints,
214217
TypeLimits: TypeLimits::new(),

‎compiler/rustc_lint/src/lints.rs

+26-1
Original file line numberDiff line numberDiff line change
@@ -224,9 +224,34 @@ pub struct BuiltinConstNoMangle {
224224
pub suggestion: Span,
225225
}
226226

227+
// mutable_transmutes.rs
227228
#[derive(LintDiagnostic)]
228229
#[diag(lint_builtin_mutable_transmutes)]
229-
pub struct BuiltinMutablesTransmutes;
230+
#[note]
231+
pub struct BuiltinMutablesTransmutes {
232+
pub from: String,
233+
pub to: String,
234+
}
235+
236+
// mutable_transmutes.rs
237+
#[derive(LintDiagnostic)]
238+
#[diag(lint_builtin_unsafe_cell_transmutes)]
239+
#[note]
240+
pub struct BuiltinUnsafeCellTransmutes {
241+
pub from: String,
242+
pub to: String,
243+
}
244+
245+
// mutable_transmutes.rs
246+
#[derive(LintDiagnostic)]
247+
#[diag(lint_unsafe_cell_reference_casting)]
248+
#[note]
249+
pub struct UnsafeCellReferenceCastingDiag {
250+
#[label]
251+
pub orig_cast: Option<Span>,
252+
pub from: String,
253+
pub to: String,
254+
}
230255

231256
#[derive(LintDiagnostic)]
232257
#[diag(lint_builtin_unstable_features)]

0 commit comments

Comments
 (0)
Please sign in to comment.