Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Some more fixes for transmute_undefined_repr #8440

Merged
merged 5 commits into from
Feb 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 1 addition & 17 deletions clippy_lints/src/casts/cast_ptr_alignment.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use clippy_utils::diagnostics::span_lint;
use clippy_utils::is_hir_ty_cfg_dependant;
use clippy_utils::ty::is_c_void;
use if_chain::if_chain;
use rustc_hir::{Expr, ExprKind, GenericArg};
use rustc_lint::LateContext;
use rustc_middle::ty::layout::LayoutOf;
use rustc_middle::ty::{self, Ty};
use rustc_span::symbol::sym;

use super::CAST_PTR_ALIGNMENT;

Expand Down Expand Up @@ -62,19 +62,3 @@ fn lint_cast_ptr_alignment<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, cast_f
}
}
}

/// Check if the given type is either `core::ffi::c_void` or
/// one of the platform specific `libc::<platform>::c_void` of libc.
fn is_c_void(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
if let ty::Adt(adt, _) = ty.kind() {
let names = cx.get_def_path(adt.did);

if names.is_empty() {
return false;
}
if names[0] == sym::libc || names[0] == sym::core && *names.last().unwrap() == sym!(c_void) {
return true;
}
}
false
}
109 changes: 75 additions & 34 deletions clippy_lints/src/transmute/transmute_undefined_repr.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use super::TRANSMUTE_UNDEFINED_REPR;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::ty::is_c_void;
use rustc_hir::Expr;
use rustc_lint::LateContext;
use rustc_middle::ty::subst::{GenericArg, Subst};
Expand All @@ -18,33 +19,55 @@ pub(super) fn check<'tcx>(

while from_ty != to_ty {
match reduce_refs(cx, e.span, from_ty, to_ty) {
ReducedTys::FromFatPtr { unsized_ty, .. } => {
span_lint_and_then(
cx,
TRANSMUTE_UNDEFINED_REPR,
e.span,
&format!("transmute from `{}` which has an undefined layout", from_ty_orig),
|diag| {
if from_ty_orig.peel_refs() != unsized_ty {
diag.note(&format!("the contained type `&{}` has an undefined layout", unsized_ty));
}
},
);
return true;
ReducedTys::FromFatPtr {
unsized_ty,
to_ty: to_sub_ty,
} => match reduce_ty(cx, to_sub_ty) {
ReducedTy::IntArray | ReducedTy::TypeErasure => break,
ReducedTy::Ref(to_sub_ty) => {
from_ty = unsized_ty;
to_ty = to_sub_ty;
continue;
},
_ => {
span_lint_and_then(
cx,
TRANSMUTE_UNDEFINED_REPR,
e.span,
&format!("transmute from `{}` which has an undefined layout", from_ty_orig),
|diag| {
if from_ty_orig.peel_refs() != unsized_ty {
diag.note(&format!("the contained type `&{}` has an undefined layout", unsized_ty));
}
},
);
return true;
},
},
ReducedTys::ToFatPtr { unsized_ty, .. } => {
span_lint_and_then(
cx,
TRANSMUTE_UNDEFINED_REPR,
e.span,
&format!("transmute to `{}` which has an undefined layout", to_ty_orig),
|diag| {
if to_ty_orig.peel_refs() != unsized_ty {
diag.note(&format!("the contained type `&{}` has an undefined layout", unsized_ty));
}
},
);
return true;
ReducedTys::ToFatPtr {
unsized_ty,
from_ty: from_sub_ty,
} => match reduce_ty(cx, from_sub_ty) {
ReducedTy::IntArray | ReducedTy::TypeErasure => break,
ReducedTy::Ref(from_sub_ty) => {
from_ty = from_sub_ty;
to_ty = unsized_ty;
continue;
},
_ => {
span_lint_and_then(
cx,
TRANSMUTE_UNDEFINED_REPR,
e.span,
&format!("transmute to `{}` which has an undefined layout", to_ty_orig),
|diag| {
if to_ty_orig.peel_refs() != unsized_ty {
diag.note(&format!("the contained type `&{}` has an undefined layout", unsized_ty));
}
},
);
return true;
},
},
ReducedTys::ToPtr {
from_ty: from_sub_ty,
Expand Down Expand Up @@ -100,7 +123,8 @@ pub(super) fn check<'tcx>(
from_ty: from_sub_ty,
to_ty: to_sub_ty,
} => match (reduce_ty(cx, from_sub_ty), reduce_ty(cx, to_sub_ty)) {
(ReducedTy::IntArray, _) | (_, ReducedTy::IntArray) => return false,
(ReducedTy::IntArray | ReducedTy::TypeErasure, _)
| (_, ReducedTy::IntArray | ReducedTy::TypeErasure) => return false,
(ReducedTy::UnorderedFields(from_ty), ReducedTy::UnorderedFields(to_ty)) if from_ty != to_ty => {
span_lint_and_then(
cx,
Expand Down Expand Up @@ -182,13 +206,14 @@ pub(super) fn check<'tcx>(
}

enum ReducedTys<'tcx> {
FromFatPtr { unsized_ty: Ty<'tcx> },
ToFatPtr { unsized_ty: Ty<'tcx> },
FromFatPtr { unsized_ty: Ty<'tcx>, to_ty: Ty<'tcx> },
ToFatPtr { unsized_ty: Ty<'tcx>, from_ty: Ty<'tcx> },
ToPtr { from_ty: Ty<'tcx>, to_ty: Ty<'tcx> },
FromPtr { from_ty: Ty<'tcx>, to_ty: Ty<'tcx> },
Other { from_ty: Ty<'tcx>, to_ty: Ty<'tcx> },
}

/// Remove references so long as both types are references.
fn reduce_refs<'tcx>(
cx: &LateContext<'tcx>,
span: Span,
Expand All @@ -208,12 +233,12 @@ fn reduce_refs<'tcx>(
(ty::Ref(_, unsized_ty, _) | ty::RawPtr(TypeAndMut { ty: unsized_ty, .. }), _)
if !unsized_ty.is_sized(cx.tcx.at(span), cx.param_env) =>
{
ReducedTys::FromFatPtr { unsized_ty }
ReducedTys::FromFatPtr { unsized_ty, to_ty }
},
(_, ty::Ref(_, unsized_ty, _) | ty::RawPtr(TypeAndMut { ty: unsized_ty, .. }))
if !unsized_ty.is_sized(cx.tcx.at(span), cx.param_env) =>
{
ReducedTys::ToFatPtr { unsized_ty }
ReducedTys::ToFatPtr { unsized_ty, from_ty }
},
(ty::Ref(_, from_ty, _) | ty::RawPtr(TypeAndMut { ty: from_ty, .. }), _) => {
ReducedTys::FromPtr { from_ty, to_ty }
Expand All @@ -227,13 +252,23 @@ fn reduce_refs<'tcx>(
}

enum ReducedTy<'tcx> {
/// The type can be used for type erasure.
TypeErasure,
/// The type is a struct containing either zero non-zero sized fields, or multiple non-zero
/// sized fields with a defined order.
OrderedFields(Ty<'tcx>),
/// The type is a struct containing multiple non-zero sized fields with no defined order.
UnorderedFields(Ty<'tcx>),
/// The type is a reference to the contained type.
Ref(Ty<'tcx>),
Other(Ty<'tcx>),
/// The type is an array of a primitive integer type. These can be used as storage for a value
/// of another type.
IntArray,
/// Any other type.
Other(Ty<'tcx>),
}

/// Reduce structs containing a single non-zero sized field to it's contained type.
fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx> {
loop {
ty = cx.tcx.try_normalize_erasing_regions(cx.param_env, ty).unwrap_or(ty);
Expand All @@ -243,6 +278,7 @@ fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx>
ty = sub_ty;
continue;
},
ty::Tuple(args) if args.is_empty() => ReducedTy::TypeErasure,
ty::Tuple(args) => {
let mut iter = args.iter().map(GenericArg::expect_ty);
let Some(sized_ty) = iter.find(|ty| !is_zero_sized_ty(cx, ty)) else {
Expand All @@ -261,7 +297,7 @@ fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx>
.iter()
.map(|f| cx.tcx.type_of(f.did).subst(cx.tcx, substs));
let Some(sized_ty) = iter.find(|ty| !is_zero_sized_ty(cx, ty)) else {
return ReducedTy::OrderedFields(ty);
return ReducedTy::TypeErasure;
};
if iter.all(|ty| is_zero_sized_ty(cx, ty)) {
ty = sized_ty;
Expand All @@ -273,7 +309,12 @@ fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx>
ReducedTy::UnorderedFields(ty)
}
},
ty::Ref(..) | ty::RawPtr(_) => ReducedTy::Ref(ty),
ty::Adt(def, _) if def.is_enum() && (def.variants.is_empty() || is_c_void(cx, ty)) => {
ReducedTy::TypeErasure
},
ty::Foreign(_) => ReducedTy::TypeErasure,
ty::Ref(_, ty, _) => ReducedTy::Ref(ty),
ty::RawPtr(ty) => ReducedTy::Ref(ty.ty),
_ => ReducedTy::Other(ty),
};
}
Expand Down
1 change: 1 addition & 0 deletions clippy_utils/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#![feature(box_patterns)]
#![feature(control_flow_enum)]
#![feature(let_else)]
#![feature(let_chains)]
#![feature(once_cell)]
#![feature(rustc_private)]
#![recursion_limit = "512"]
Expand Down
14 changes: 14 additions & 0 deletions clippy_utils/src/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -572,3 +572,17 @@ pub fn get_discriminant_value(tcx: TyCtxt<'_>, adt: &'_ AdtDef, i: VariantIdx) -
},
}
}

/// Check if the given type is either `core::ffi::c_void`, `std::os::raw::c_void`, or one of the
/// platform specific `libc::<platform>::c_void` types in libc.
pub fn is_c_void(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
if let ty::Adt(adt, _) = ty.kind()
&& let &[krate, .., name] = &*cx.get_def_path(adt.did)
&& let sym::libc | sym::core | sym::std = krate
&& name.as_str() == "c_void"
{
true
} else {
false
}
}
85 changes: 64 additions & 21 deletions tests/ui/transmute_undefined_repr.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
#![warn(clippy::transmute_undefined_repr)]
#![allow(clippy::unit_arg)]
#![allow(clippy::unit_arg, clippy::transmute_ptr_to_ref)]

use core::ffi::c_void;
use core::mem::{size_of, transmute};

fn value<T>() -> T {
unimplemented!()
Expand All @@ -14,35 +17,75 @@ struct Ty2C<T, U>(T, U);

fn main() {
unsafe {
let _: () = core::mem::transmute(value::<Empty>());
let _: Empty = core::mem::transmute(value::<()>());
let _: () = transmute(value::<Empty>());
let _: Empty = transmute(value::<()>());

let _: Ty<u32> = core::mem::transmute(value::<u32>());
let _: Ty<u32> = core::mem::transmute(value::<u32>());
let _: Ty<u32> = transmute(value::<u32>());
let _: Ty<u32> = transmute(value::<u32>());

let _: Ty2C<u32, i32> = core::mem::transmute(value::<Ty2<u32, i32>>()); // Lint, Ty2 is unordered
let _: Ty2<u32, i32> = core::mem::transmute(value::<Ty2C<u32, i32>>()); // Lint, Ty2 is unordered
let _: Ty2C<u32, i32> = transmute(value::<Ty2<u32, i32>>()); // Lint, Ty2 is unordered
let _: Ty2<u32, i32> = transmute(value::<Ty2C<u32, i32>>()); // Lint, Ty2 is unordered

let _: Ty2<u32, i32> = core::mem::transmute(value::<Ty<Ty2<u32, i32>>>()); // Ok, Ty2 types are the same
let _: Ty<Ty2<u32, i32>> = core::mem::transmute(value::<Ty2<u32, i32>>()); // Ok, Ty2 types are the same
let _: Ty2<u32, i32> = transmute(value::<Ty<Ty2<u32, i32>>>()); // Ok, Ty2 types are the same
let _: Ty<Ty2<u32, i32>> = transmute(value::<Ty2<u32, i32>>()); // Ok, Ty2 types are the same

let _: Ty2<u32, f32> = core::mem::transmute(value::<Ty<Ty2<u32, i32>>>()); // Lint, different Ty2 instances
let _: Ty<Ty2<u32, i32>> = core::mem::transmute(value::<Ty2<u32, f32>>()); // Lint, different Ty2 instances
let _: Ty2<u32, f32> = transmute(value::<Ty<Ty2<u32, i32>>>()); // Lint, different Ty2 instances
let _: Ty<Ty2<u32, i32>> = transmute(value::<Ty2<u32, f32>>()); // Lint, different Ty2 instances

let _: Ty<&()> = core::mem::transmute(value::<&()>());
let _: &() = core::mem::transmute(value::<Ty<&()>>());
let _: Ty<&()> = transmute(value::<&()>());
let _: &() = transmute(value::<Ty<&()>>());

let _: &Ty2<u32, f32> = core::mem::transmute(value::<Ty<&Ty2<u32, i32>>>()); // Lint, different Ty2 instances
let _: Ty<&Ty2<u32, i32>> = core::mem::transmute(value::<&Ty2<u32, f32>>()); // Lint, different Ty2 instances
let _: &Ty2<u32, f32> = transmute(value::<Ty<&Ty2<u32, i32>>>()); // Lint, different Ty2 instances
let _: Ty<&Ty2<u32, i32>> = transmute(value::<&Ty2<u32, f32>>()); // Lint, different Ty2 instances

let _: Ty<usize> = core::mem::transmute(value::<&Ty2<u32, i32>>()); // Ok, pointer to usize conversion
let _: &Ty2<u32, i32> = core::mem::transmute(value::<Ty<usize>>()); // Ok, pointer to usize conversion
let _: Ty<usize> = transmute(value::<&Ty2<u32, i32>>()); // Ok, pointer to usize conversion
let _: &Ty2<u32, i32> = transmute(value::<Ty<usize>>()); // Ok, pointer to usize conversion

let _: Ty<[u8; 8]> = core::mem::transmute(value::<Ty2<u32, i32>>()); // Ok, transmute to byte array
let _: Ty2<u32, i32> = core::mem::transmute(value::<Ty<[u8; 8]>>()); // Ok, transmute from byte array
let _: Ty<[u8; 8]> = transmute(value::<Ty2<u32, i32>>()); // Ok, transmute to byte array
let _: Ty2<u32, i32> = transmute(value::<Ty<[u8; 8]>>()); // Ok, transmute from byte array

// issue #8417
let _: Ty2C<Ty2<u32, i32>, ()> = core::mem::transmute(value::<Ty2<u32, i32>>()); // Ok, Ty2 types are the same
let _: Ty2<u32, i32> = core::mem::transmute(value::<Ty2C<Ty2<u32, i32>, ()>>()); // Ok, Ty2 types are the same
let _: Ty2C<Ty2<u32, i32>, ()> = transmute(value::<Ty2<u32, i32>>()); // Ok, Ty2 types are the same
let _: Ty2<u32, i32> = transmute(value::<Ty2C<Ty2<u32, i32>, ()>>()); // Ok, Ty2 types are the same

let _: &'static mut Ty2<u32, u32> = transmute(value::<Box<Ty2<u32, u32>>>()); // Ok, Ty2 types are the same
let _: Box<Ty2<u32, u32>> = transmute(value::<&'static mut Ty2<u32, u32>>()); // Ok, Ty2 types are the same
let _: *mut Ty2<u32, u32> = transmute(value::<Box<Ty2<u32, u32>>>()); // Ok, Ty2 types are the same
let _: Box<Ty2<u32, u32>> = transmute(value::<*mut Ty2<u32, u32>>()); // Ok, Ty2 types are the same

let _: &'static mut Ty2<u32, f32> = transmute(value::<Box<Ty2<u32, u32>>>()); // Lint, different Ty2 instances
let _: Box<Ty2<u32, u32>> = transmute(value::<&'static mut Ty2<u32, f32>>()); // Lint, different Ty2 instances

let _: *const () = transmute(value::<Ty<&Ty2<u32, f32>>>()); // Ok, type erasure
let _: Ty<&Ty2<u32, f32>> = transmute(value::<*const ()>()); // Ok, reverse type erasure

let _: *const c_void = transmute(value::<Ty<&Ty2<u32, f32>>>()); // Ok, type erasure
let _: Ty<&Ty2<u32, f32>> = transmute(value::<*const c_void>()); // Ok, reverse type erasure

enum Erase {}
let _: *const Erase = transmute(value::<Ty<&Ty2<u32, f32>>>()); // Ok, type erasure
let _: Ty<&Ty2<u32, f32>> = transmute(value::<*const Erase>()); // Ok, reverse type erasure

struct Erase2(
[u8; 0],
core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
);
let _: *const Erase2 = transmute(value::<Ty<&Ty2<u32, f32>>>()); // Ok, type erasure
let _: Ty<&Ty2<u32, f32>> = transmute(value::<*const Erase2>()); // Ok, reverse type erasure

let _: *const () = transmute(value::<&&[u8]>()); // Ok, type erasure
let _: &&[u8] = transmute(value::<*const ()>()); // Ok, reverse type erasure

let _: *mut c_void = transmute(value::<&mut &[u8]>()); // Ok, type erasure
let _: &mut &[u8] = transmute(value::<*mut c_void>()); // Ok, reverse type erasure

let _: [u8; size_of::<&[u8]>()] = transmute(value::<&[u8]>()); // Ok, transmute to byte array
let _: &[u8] = transmute(value::<[u8; size_of::<&[u8]>()]>()); // Ok, transmute from byte array

let _: [usize; 2] = transmute(value::<&[u8]>()); // Ok, transmute to int array
let _: &[u8] = transmute(value::<[usize; 2]>()); // Ok, transmute from int array

let _: *const [u8] = transmute(value::<Box<[u8]>>()); // Ok
let _: Box<[u8]> = transmute(value::<*mut [u8]>()); // Ok
}
}
Loading