safe transmute: require that src referent is smaller than dst
The source referent absolutely must be smaller than the destination referent of a ref-to-ref transmute; the excess bytes referenced cannot arise from thin air, even if those bytes are uninitialized.
This commit is contained in:
parent
a165f1f650
commit
216df4a8e6
8 changed files with 122 additions and 5 deletions
|
@ -3091,6 +3091,13 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||||
rustc_transmute::Reason::DstIsTooBig => {
|
rustc_transmute::Reason::DstIsTooBig => {
|
||||||
format!("The size of `{src}` is smaller than the size of `{dst}`")
|
format!("The size of `{src}` is smaller than the size of `{dst}`")
|
||||||
}
|
}
|
||||||
|
rustc_transmute::Reason::DstRefIsTooBig { src, dst } => {
|
||||||
|
let src_size = src.size;
|
||||||
|
let dst_size = dst.size;
|
||||||
|
format!(
|
||||||
|
"The referent size of `{src}` ({src_size} bytes) is smaller than that of `{dst}` ({dst_size} bytes)"
|
||||||
|
)
|
||||||
|
}
|
||||||
rustc_transmute::Reason::SrcSizeOverflow => {
|
rustc_transmute::Reason::SrcSizeOverflow => {
|
||||||
format!(
|
format!(
|
||||||
"values of the type `{src}` are too big for the current architecture"
|
"values of the type `{src}` are too big for the current architecture"
|
||||||
|
|
|
@ -35,6 +35,8 @@ pub(crate) trait Def: Debug + Hash + Eq + PartialEq + Copy + Clone {
|
||||||
pub trait Ref: Debug + Hash + Eq + PartialEq + Copy + Clone {
|
pub trait Ref: Debug + Hash + Eq + PartialEq + Copy + Clone {
|
||||||
fn min_align(&self) -> usize;
|
fn min_align(&self) -> usize;
|
||||||
|
|
||||||
|
fn size(&self) -> usize;
|
||||||
|
|
||||||
fn is_mutable(&self) -> bool;
|
fn is_mutable(&self) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,6 +50,9 @@ impl Ref for ! {
|
||||||
fn min_align(&self) -> usize {
|
fn min_align(&self) -> usize {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
|
fn size(&self) -> usize {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
fn is_mutable(&self) -> bool {
|
fn is_mutable(&self) -> bool {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
|
@ -57,6 +62,7 @@ impl Ref for ! {
|
||||||
pub mod rustc {
|
pub mod rustc {
|
||||||
use rustc_middle::mir::Mutability;
|
use rustc_middle::mir::Mutability;
|
||||||
use rustc_middle::ty::{self, Ty};
|
use rustc_middle::ty::{self, Ty};
|
||||||
|
use std::fmt::{self, Write};
|
||||||
|
|
||||||
/// A reference in the layout.
|
/// A reference in the layout.
|
||||||
#[derive(Debug, Hash, Eq, PartialEq, PartialOrd, Ord, Clone, Copy)]
|
#[derive(Debug, Hash, Eq, PartialEq, PartialOrd, Ord, Clone, Copy)]
|
||||||
|
@ -65,6 +71,7 @@ pub mod rustc {
|
||||||
pub ty: Ty<'tcx>,
|
pub ty: Ty<'tcx>,
|
||||||
pub mutability: Mutability,
|
pub mutability: Mutability,
|
||||||
pub align: usize,
|
pub align: usize,
|
||||||
|
pub size: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> super::Ref for Ref<'tcx> {
|
impl<'tcx> super::Ref for Ref<'tcx> {
|
||||||
|
@ -72,6 +79,10 @@ pub mod rustc {
|
||||||
self.align
|
self.align
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn size(&self) -> usize {
|
||||||
|
self.size
|
||||||
|
}
|
||||||
|
|
||||||
fn is_mutable(&self) -> bool {
|
fn is_mutable(&self) -> bool {
|
||||||
match self.mutability {
|
match self.mutability {
|
||||||
Mutability::Mut => true,
|
Mutability::Mut => true,
|
||||||
|
@ -81,6 +92,16 @@ pub mod rustc {
|
||||||
}
|
}
|
||||||
impl<'tcx> Ref<'tcx> {}
|
impl<'tcx> Ref<'tcx> {}
|
||||||
|
|
||||||
|
impl<'tcx> fmt::Display for Ref<'tcx> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.write_char('&')?;
|
||||||
|
if self.mutability == Mutability::Mut {
|
||||||
|
f.write_str("mut ")?;
|
||||||
|
}
|
||||||
|
self.ty.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A visibility node in the layout.
|
/// A visibility node in the layout.
|
||||||
#[derive(Debug, Hash, Eq, PartialEq, Clone, Copy)]
|
#[derive(Debug, Hash, Eq, PartialEq, Clone, Copy)]
|
||||||
pub enum Def<'tcx> {
|
pub enum Def<'tcx> {
|
||||||
|
|
|
@ -372,12 +372,15 @@ pub(crate) mod rustc {
|
||||||
}
|
}
|
||||||
|
|
||||||
ty::Ref(lifetime, ty, mutability) => {
|
ty::Ref(lifetime, ty, mutability) => {
|
||||||
let align = layout_of(tcx, *ty)?.align();
|
let layout = layout_of(tcx, *ty)?;
|
||||||
|
let align = layout.align();
|
||||||
|
let size = layout.size();
|
||||||
Ok(Tree::Ref(Ref {
|
Ok(Tree::Ref(Ref {
|
||||||
lifetime: *lifetime,
|
lifetime: *lifetime,
|
||||||
ty: *ty,
|
ty: *ty,
|
||||||
mutability: *mutability,
|
mutability: *mutability,
|
||||||
align,
|
align,
|
||||||
|
size,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ pub struct Assume {
|
||||||
#[derive(Debug, Hash, Eq, PartialEq, Clone)]
|
#[derive(Debug, Hash, Eq, PartialEq, Clone)]
|
||||||
pub enum Answer<R> {
|
pub enum Answer<R> {
|
||||||
Yes,
|
Yes,
|
||||||
No(Reason),
|
No(Reason<R>),
|
||||||
If(Condition<R>),
|
If(Condition<R>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ pub enum Condition<R> {
|
||||||
|
|
||||||
/// Answers "why wasn't the source type transmutable into the destination type?"
|
/// Answers "why wasn't the source type transmutable into the destination type?"
|
||||||
#[derive(Debug, Hash, Eq, PartialEq, PartialOrd, Ord, Clone)]
|
#[derive(Debug, Hash, Eq, PartialEq, PartialOrd, Ord, Clone)]
|
||||||
pub enum Reason {
|
pub enum Reason<T> {
|
||||||
/// The layout of the source type is unspecified.
|
/// The layout of the source type is unspecified.
|
||||||
SrcIsUnspecified,
|
SrcIsUnspecified,
|
||||||
/// The layout of the destination type is unspecified.
|
/// The layout of the destination type is unspecified.
|
||||||
|
@ -53,6 +53,13 @@ pub enum Reason {
|
||||||
DstMayHaveSafetyInvariants,
|
DstMayHaveSafetyInvariants,
|
||||||
/// `Dst` is larger than `Src`, and the excess bytes were not exclusively uninitialized.
|
/// `Dst` is larger than `Src`, and the excess bytes were not exclusively uninitialized.
|
||||||
DstIsTooBig,
|
DstIsTooBig,
|
||||||
|
/// A referent of `Dst` is larger than a referent in `Src`.
|
||||||
|
DstRefIsTooBig {
|
||||||
|
/// The referent of the source type.
|
||||||
|
src: T,
|
||||||
|
/// The too-large referent of the destination type.
|
||||||
|
dst: T,
|
||||||
|
},
|
||||||
/// Src should have a stricter alignment than Dst, but it does not.
|
/// Src should have a stricter alignment than Dst, but it does not.
|
||||||
DstHasStricterAlignment { src_min_align: usize, dst_min_align: usize },
|
DstHasStricterAlignment { src_min_align: usize, dst_min_align: usize },
|
||||||
/// Can't go from shared pointer to unique pointer
|
/// Can't go from shared pointer to unique pointer
|
||||||
|
|
|
@ -266,6 +266,11 @@ where
|
||||||
src_min_align: src_ref.min_align(),
|
src_min_align: src_ref.min_align(),
|
||||||
dst_min_align: dst_ref.min_align(),
|
dst_min_align: dst_ref.min_align(),
|
||||||
})
|
})
|
||||||
|
} else if dst_ref.size() > src_ref.size() {
|
||||||
|
Answer::No(Reason::DstRefIsTooBig {
|
||||||
|
src: src_ref,
|
||||||
|
dst: dst_ref,
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
// ...such that `src` is transmutable into `dst`, if
|
// ...such that `src` is transmutable into `dst`, if
|
||||||
// `src_ref` is transmutability into `dst_ref`.
|
// `src_ref` is transmutability into `dst_ref`.
|
||||||
|
|
49
tests/ui/transmutability/references/reject_extension.rs
Normal file
49
tests/ui/transmutability/references/reject_extension.rs
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
//@ check-fail
|
||||||
|
|
||||||
|
//! Reject extensions behind references.
|
||||||
|
|
||||||
|
#![crate_type = "lib"]
|
||||||
|
#![feature(transmutability)]
|
||||||
|
|
||||||
|
mod assert {
|
||||||
|
use std::mem::{Assume, BikeshedIntrinsicFrom};
|
||||||
|
|
||||||
|
pub fn is_transmutable<Src, Dst>()
|
||||||
|
where
|
||||||
|
Dst: BikeshedIntrinsicFrom<
|
||||||
|
Src,
|
||||||
|
{
|
||||||
|
Assume {
|
||||||
|
alignment: true,
|
||||||
|
lifetimes: true,
|
||||||
|
safety: true,
|
||||||
|
validity: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
>,
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C, packed)]
|
||||||
|
struct Packed<T>(T);
|
||||||
|
|
||||||
|
fn reject_extension() {
|
||||||
|
#[repr(C, align(2))]
|
||||||
|
struct Two(u8);
|
||||||
|
|
||||||
|
#[repr(C, align(4))]
|
||||||
|
struct Four(u8);
|
||||||
|
|
||||||
|
// These two types differ in the number of trailing padding bytes they have.
|
||||||
|
type Src = Packed<Two>;
|
||||||
|
type Dst = Packed<Four>;
|
||||||
|
|
||||||
|
const _: () = {
|
||||||
|
use std::mem::size_of;
|
||||||
|
assert!(size_of::<Src>() == 2);
|
||||||
|
assert!(size_of::<Dst>() == 4);
|
||||||
|
};
|
||||||
|
|
||||||
|
assert::is_transmutable::<&Src, &Dst>(); //~ ERROR cannot be safely transmuted
|
||||||
|
}
|
25
tests/ui/transmutability/references/reject_extension.stderr
Normal file
25
tests/ui/transmutability/references/reject_extension.stderr
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
error[E0277]: `&Packed<Two>` cannot be safely transmuted into `&Packed<Four>`
|
||||||
|
--> $DIR/reject_extension.rs:48:37
|
||||||
|
|
|
||||||
|
LL | assert::is_transmutable::<&Src, &Dst>();
|
||||||
|
| ^^^^ The referent size of `&Packed<Two>` (2 bytes) is smaller than that of `&Packed<Four>` (4 bytes)
|
||||||
|
|
|
||||||
|
note: required by a bound in `is_transmutable`
|
||||||
|
--> $DIR/reject_extension.rs:13:14
|
||||||
|
|
|
||||||
|
LL | pub fn is_transmutable<Src, Dst>()
|
||||||
|
| --------------- required by a bound in this function
|
||||||
|
LL | where
|
||||||
|
LL | Dst: BikeshedIntrinsicFrom<
|
||||||
|
| ______________^
|
||||||
|
LL | | Src,
|
||||||
|
LL | | {
|
||||||
|
LL | | Assume {
|
||||||
|
... |
|
||||||
|
LL | | },
|
||||||
|
LL | | >,
|
||||||
|
| |_________^ required by this bound in `is_transmutable`
|
||||||
|
|
||||||
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0277`.
|
|
@ -1,8 +1,8 @@
|
||||||
error[E0277]: `Unit` cannot be safely transmuted into `u8`
|
error[E0277]: `&Unit` cannot be safely transmuted into `&u8`
|
||||||
--> $DIR/unit-to-u8.rs:22:52
|
--> $DIR/unit-to-u8.rs:22:52
|
||||||
|
|
|
|
||||||
LL | assert::is_maybe_transmutable::<&'static Unit, &'static u8>();
|
LL | assert::is_maybe_transmutable::<&'static Unit, &'static u8>();
|
||||||
| ^^^^^^^^^^^ The size of `Unit` is smaller than the size of `u8`
|
| ^^^^^^^^^^^ The referent size of `&Unit` (0 bytes) is smaller than that of `&u8` (1 bytes)
|
||||||
|
|
|
|
||||||
note: required by a bound in `is_maybe_transmutable`
|
note: required by a bound in `is_maybe_transmutable`
|
||||||
--> $DIR/unit-to-u8.rs:9:14
|
--> $DIR/unit-to-u8.rs:9:14
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue