From 11c833e0f1b371638d487b87add8f88df30dd916 Mon Sep 17 00:00:00 2001 From: 5225225 <5225225@mailbox.org> Date: Wed, 8 Jun 2022 13:57:12 +0100 Subject: [PATCH 01/12] Initial work on runtime type validity checks --- .../rustc_codegen_ssa/src/mir/intrinsic.rs | 3 +- .../src/interpret/intrinsics.rs | 8 ++ .../intrinsics/validity_invariants_of.rs | 119 ++++++++++++++++++ compiler/rustc_middle/src/ty/context.rs | 5 + compiler/rustc_span/src/symbol.rs | 1 + compiler/rustc_typeck/src/check/intrinsic.rs | 4 +- library/core/src/intrinsics.rs | 80 ++++++++++++ library/core/src/mem/maybe_uninit.rs | 15 +++ 8 files changed, 233 insertions(+), 2 deletions(-) create mode 100644 compiler/rustc_const_eval/src/interpret/intrinsics/validity_invariants_of.rs diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index 0ed4c3f1d9430..fa567d139497b 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -106,7 +106,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { | sym::needs_drop | sym::type_id | sym::type_name - | sym::variant_count => { + | sym::variant_count + | sym::validity_invariants_of => { let value = bx .tcx() .const_eval_instance(ty::ParamEnv::reveal_all(), instance, None) diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index e51c51cf45e5e..4e3d098539cff 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -24,6 +24,7 @@ use super::{ mod caller_location; mod type_name; +mod validity_invariants_of; fn numeric_intrinsic(name: Symbol, bits: u128, kind: Primitive) -> Scalar { let size = match kind { @@ -103,6 +104,11 @@ pub(crate) fn eval_nullary_intrinsic<'tcx>( | ty::Tuple(_) | ty::Error(_) => ConstValue::from_machine_usize(0u64, &tcx), }, + sym::validity_invariants_of => { + ensure_monomorphic_enough(tcx, tp_ty)?; + let alloc = validity_invariants_of::alloc_validity_invariants_of(tcx, tp_ty); + ConstValue::Slice { data: alloc, start: 0, end: alloc.inner().len() } + } other => bug!("`{}` is not a zero arg intrinsic", other), }) } @@ -162,6 +168,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { | sym::needs_drop | sym::type_id | sym::type_name + | sym::validity_invariants_of | sym::variant_count => { let gid = GlobalId { instance, promoted: None }; let ty = match intrinsic_name { @@ -169,6 +176,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { sym::needs_drop => self.tcx.types.bool, sym::type_id => self.tcx.types.u64, sym::type_name => self.tcx.mk_static_str(), + sym::validity_invariants_of => self.tcx.mk_static_bytes(), _ => bug!(), }; let val = diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics/validity_invariants_of.rs b/compiler/rustc_const_eval/src/interpret/intrinsics/validity_invariants_of.rs new file mode 100644 index 0000000000000..9cb127c3363ee --- /dev/null +++ b/compiler/rustc_const_eval/src/interpret/intrinsics/validity_invariants_of.rs @@ -0,0 +1,119 @@ +use rustc_middle::mir::interpret::{Allocation, ConstAllocation}; +use rustc_middle::mir::Mutability; +use rustc_middle::ty::layout::LayoutCx; +use rustc_middle::ty::{ParamEnv, ParamEnvAnd}; +use rustc_middle::ty::{Ty, TyCtxt}; +use rustc_target::abi::{ + Abi, Align, Endian, FieldsShape, HasDataLayout, Scalar, Size, WrappingRange, +}; + +#[derive(Debug, Clone, Copy)] +struct Invariant { + offset: Size, + size: Size, + start: u128, + end: u128, +} + +fn add_invariants<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, invs: &mut Vec, offset: Size) { + let x = tcx.layout_of(ParamEnvAnd { param_env: ParamEnv::reveal_all(), value: ty }); + + if let Ok(layout) = x { + if let Abi::Scalar(Scalar::Initialized { value, valid_range }) = layout.layout.abi() { + let size = value.size(&tcx); + let WrappingRange { start, end } = valid_range; + invs.push(Invariant { offset, size, start, end }) + } + + let param_env = ParamEnv::reveal_all(); + let unwrap = LayoutCx { tcx, param_env }; + + match layout.layout.fields() { + FieldsShape::Primitive => {} + FieldsShape::Union(_) => {} + FieldsShape::Array { stride, count } => { + // TODO: should we just bail if we're making a Too Large type? + // (Like [bool; 1_000_000]) + for idx in 0..*count { + let off = offset + *stride * idx; + let f = layout.field(&unwrap, idx as usize); + add_invariants(tcx, f.ty, invs, off); + } + } + FieldsShape::Arbitrary { offsets, .. } => { + for (idx, &field_offset) in offsets.iter().enumerate() { + let f = layout.field(&unwrap, idx); + if f.ty == ty { + // Some types contain themselves as fields, such as + // &mut [T] + // Easy solution is to just not recurse then. + } else { + add_invariants(tcx, f.ty, invs, offset + field_offset); + } + } + } + } + } +} + +fn extend_encoded_int(to: &mut Vec, endian: Endian, ptr_size: PointerSize, value: Size) { + match (endian, ptr_size) { + (Endian::Little, PointerSize::Bits16) => to.extend((value.bytes() as u16).to_le_bytes()), + (Endian::Little, PointerSize::Bits32) => to.extend((value.bytes() as u32).to_le_bytes()), + (Endian::Little, PointerSize::Bits64) => to.extend((value.bytes()).to_le_bytes()), + (Endian::Big, PointerSize::Bits16) => to.extend((value.bytes() as u16).to_be_bytes()), + (Endian::Big, PointerSize::Bits32) => to.extend((value.bytes() as u32).to_be_bytes()), + (Endian::Big, PointerSize::Bits64) => to.extend((value.bytes()).to_be_bytes()), + } +} + +#[derive(Clone, Copy)] +enum PointerSize { + Bits16, + Bits32, + Bits64, +} + +/// Directly returns a `ConstAllocation` containing a list of validity invariants of the given type. +pub(crate) fn alloc_validity_invariants_of<'tcx>( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, +) -> ConstAllocation<'tcx> { + let mut invs: Vec = Vec::new(); + + let layout = tcx.data_layout(); + + let ptr_size = match layout.pointer_size.bits() { + 16 => PointerSize::Bits16, + 32 => PointerSize::Bits32, + 64 => PointerSize::Bits64, + _ => { + // Not sure if this can happen, but just return an empty slice? + let alloc = + Allocation::from_bytes(Vec::new(), Align::from_bytes(8).unwrap(), Mutability::Not); + return tcx.intern_const_alloc(alloc); + } + }; + + add_invariants(tcx, ty, &mut invs, Size::ZERO); + + let encode_range = match layout.endian { + Endian::Little => |r: u128| r.to_le_bytes(), + Endian::Big => |r: u128| r.to_be_bytes(), + }; + + let mut encoded = Vec::new(); + + // TODO: this needs to match the layout of `Invariant` in core/src/intrinsics.rs + // how do we ensure that? + for inv in invs { + extend_encoded_int(&mut encoded, layout.endian, ptr_size, inv.offset); + extend_encoded_int(&mut encoded, layout.endian, ptr_size, inv.size); + encoded.extend(encode_range(inv.start)); + encoded.extend(encode_range(inv.end)); + } + + // TODO: The alignment here should be calculated from the struct definition, I guess? + let alloc = Allocation::from_bytes(encoded, Align::from_bytes(8).unwrap(), Mutability::Not); + tcx.intern_const_alloc(alloc) +} diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index c43cf07b3ad0a..fda734fab2904 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -2339,6 +2339,11 @@ impl<'tcx> TyCtxt<'tcx> { self.mk_imm_ref(self.lifetimes.re_static, self.types.str_) } + #[inline] + pub fn mk_static_bytes(self) -> Ty<'tcx> { + self.mk_imm_ref(self.lifetimes.re_static, self.mk_slice(self.types.u8)) + } + #[inline] pub fn mk_adt(self, def: AdtDef<'tcx>, substs: SubstsRef<'tcx>) -> Ty<'tcx> { // Take a copy of substs so that we own the vectors inside. diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 6daf811e26f14..56502dab93283 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1524,6 +1524,7 @@ symbols! { va_list, va_start, val, + validity_invariants_of, values, var, variant_count, diff --git a/compiler/rustc_typeck/src/check/intrinsic.rs b/compiler/rustc_typeck/src/check/intrinsic.rs index 7fe710cf8f4f2..c766787a5cc22 100644 --- a/compiler/rustc_typeck/src/check/intrinsic.rs +++ b/compiler/rustc_typeck/src/check/intrinsic.rs @@ -102,7 +102,8 @@ pub fn intrinsic_operation_unsafety(intrinsic: Symbol) -> hir::Unsafety { | sym::type_name | sym::forget | sym::black_box - | sym::variant_count => hir::Unsafety::Normal, + | sym::variant_count + | sym::validity_invariants_of => hir::Unsafety::Normal, _ => hir::Unsafety::Unsafe, } } @@ -191,6 +192,7 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { sym::needs_drop => (1, Vec::new(), tcx.types.bool), sym::type_name => (1, Vec::new(), tcx.mk_static_str()), + sym::validity_invariants_of => (1, Vec::new(), tcx.mk_static_bytes()), sym::type_id => (1, Vec::new(), tcx.types.u64), sym::offset | sym::arith_offset => ( 1, diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index ba837ea9a7898..ff7d7e7f23cd6 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -2138,6 +2138,30 @@ pub const unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: us } } +#[repr(C)] +#[derive(Debug, Clone, Copy)] +pub struct Invariant { + offset: u64, + size: u64, + start: u128, + end: u128, +} + +#[cfg(not(bootstrap))] +/// Returns a list of all validity invariants of the type. +pub const fn validity_invariants_of() -> &'static [Invariant] { + extern "rust-intrinsic" { + #[rustc_const_stable(feature = "validity_invariants_of", since = "1.40.0")] + pub fn validity_invariants_of() -> &'static [u8]; + } + + let invariants: &'static [u8] = validity_invariants_of::(); + let sz = invariants.len() / core::mem::size_of::(); + + // SAFETY: we know this is valid. + unsafe { core::slice::from_raw_parts(invariants.as_ptr().cast(), sz) } +} + /// Copies `count * size_of::()` bytes from `src` to `dst`. The source /// and destination may overlap. /// @@ -2394,3 +2418,59 @@ where { called_in_const.call_once(arg) } + +#[cfg(bootstrap)] +pub(crate) const unsafe fn assert_validity_of(_: *const T) -> bool { + true +} + +#[cfg(not(bootstrap))] +/// Asserts that the value at `value` is valid at type T. +/// Best effort, and is UB if the value is invalid. +pub(crate) unsafe fn assert_validity_of(value: *const T) -> bool { + #[repr(packed)] + struct Unaligned(T); + + // SAFETY: + unsafe { + let invariants = validity_invariants_of::(); + for invariant in invariants { + let off = invariant.offset as usize; + let start = invariant.start; + let end = invariant.end; + + let (value, max): (u128, u128) = match invariant.size { + 1 => ((*(value.cast::().add(off))).into(), u8::MAX.into()), + 2 => ( + (*value.cast::().add(off).cast::>()).0.into(), + u16::MAX.into(), + ), + 4 => ( + (*value.cast::().add(off).cast::>()).0.into(), + u32::MAX.into(), + ), + 8 => ( + (*value.cast::().add(off).cast::>()).0.into(), + u64::MAX.into(), + ), + 16 => ( + (*value.cast::().add(off).cast::>()).0.into(), + u128::MAX.into(), + ), + s => panic!("unexpected size {s}"), + }; + + if start > end { + if !((start..=max).contains(&value) || (0..=end).contains(&value)) { + return false; + } + } else { + if !(start..=end).contains(&value) { + return false; + } + } + } + + true + } +} diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index b4ea536083392..e1d64af64902b 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -631,6 +631,11 @@ impl MaybeUninit { // This also means that `self` must be a `value` variant. unsafe { intrinsics::assert_inhabited::(); + + intrinsics::assert_unsafe_precondition!(intrinsics::assert_validity_of::( + &self as *const MaybeUninit as *const T + )); + ManuallyDrop::into_inner(self.value) } } @@ -795,6 +800,11 @@ impl MaybeUninit { // This also means that `self` must be a `value` variant. unsafe { intrinsics::assert_inhabited::(); + + intrinsics::assert_unsafe_precondition!(intrinsics::assert_validity_of::( + self as *const MaybeUninit as *const T + )); + &*self.as_ptr() } } @@ -912,6 +922,11 @@ impl MaybeUninit { // This also means that `self` must be a `value` variant. unsafe { intrinsics::assert_inhabited::(); + + intrinsics::assert_unsafe_precondition!(intrinsics::assert_validity_of::( + self as *const MaybeUninit as *const T + )); + &mut *self.as_mut_ptr() } } From ea5be8b2a454118b347431c907b7a556f98c85c1 Mon Sep 17 00:00:00 2001 From: 5225225 <5225225@mailbox.org> Date: Mon, 13 Jun 2022 21:25:34 +0100 Subject: [PATCH 02/12] Add more todos/safety comments --- .../src/interpret/intrinsics/validity_invariants_of.rs | 1 + library/core/src/intrinsics.rs | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics/validity_invariants_of.rs b/compiler/rustc_const_eval/src/interpret/intrinsics/validity_invariants_of.rs index 9cb127c3363ee..c23fcf0f47298 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics/validity_invariants_of.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics/validity_invariants_of.rs @@ -15,6 +15,7 @@ struct Invariant { end: u128, } +// TODO: Don't add duplicate invariants (maybe use a HashMap?) fn add_invariants<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, invs: &mut Vec, offset: Size) { let x = tcx.layout_of(ParamEnvAnd { param_env: ParamEnv::reveal_all(), value: ty }); diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index ff7d7e7f23cd6..adbd62ebcb07d 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -2158,7 +2158,7 @@ pub const fn validity_invariants_of() -> &'static [Invariant] { let invariants: &'static [u8] = validity_invariants_of::(); let sz = invariants.len() / core::mem::size_of::(); - // SAFETY: we know this is valid. + // SAFETY: we know this is valid because the intrinsic promises an aligned slice. unsafe { core::slice::from_raw_parts(invariants.as_ptr().cast(), sz) } } @@ -2428,10 +2428,13 @@ pub(crate) const unsafe fn assert_validity_of(_: *const T) -> bool { /// Asserts that the value at `value` is valid at type T. /// Best effort, and is UB if the value is invalid. pub(crate) unsafe fn assert_validity_of(value: *const T) -> bool { + // We have to do this, since we call assert_validity_of inside MaybeUninit::assume_init + // and if we had used ptr::read_unaligned, that would be a recursive call. #[repr(packed)] struct Unaligned(T); - // SAFETY: + // SAFETY: The pointer dereferences here are valid if `value` is valid. + // though TODO: introduce a new size for "pointer", since reading a pointer as an int *is* UB. unsafe { let invariants = validity_invariants_of::(); for invariant in invariants { @@ -2439,6 +2442,7 @@ pub(crate) unsafe fn assert_validity_of(value: *const T) -> bool { let start = invariant.start; let end = invariant.end; + // TODO: Maybe replace this with an enum? let (value, max): (u128, u128) = match invariant.size { 1 => ((*(value.cast::().add(off))).into(), u8::MAX.into()), 2 => ( From 5d42de240d38e48f8fbadf77a9b835b3388cc6a5 Mon Sep 17 00:00:00 2001 From: 5225225 <5225225@mailbox.org> Date: Mon, 13 Jun 2022 22:40:38 +0100 Subject: [PATCH 03/12] Add allow unstable, fix stability of validity_invariants_of --- library/core/src/intrinsics.rs | 2 +- library/core/src/mem/maybe_uninit.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index adbd62ebcb07d..f6bc13d98d358 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -2151,7 +2151,7 @@ pub struct Invariant { /// Returns a list of all validity invariants of the type. pub const fn validity_invariants_of() -> &'static [Invariant] { extern "rust-intrinsic" { - #[rustc_const_stable(feature = "validity_invariants_of", since = "1.40.0")] + #[rustc_const_unstable(feature = "validity_invariants_of", issue = "none")] pub fn validity_invariants_of() -> &'static [u8]; } diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index e1d64af64902b..0ceb0b6f41c83 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -626,6 +626,7 @@ impl MaybeUninit { #[inline(always)] #[rustc_diagnostic_item = "assume_init"] #[track_caller] + #[rustc_allow_const_fn_unstable(const_refs_to_cell)] pub const unsafe fn assume_init(self) -> T { // SAFETY: the caller must guarantee that `self` is initialized. // This also means that `self` must be a `value` variant. From a515b9f7f60bba57fc6a86b5c8a29c322ea37904 Mon Sep 17 00:00:00 2001 From: 5225225 <5225225@mailbox.org> Date: Mon, 13 Jun 2022 23:01:57 +0100 Subject: [PATCH 04/12] Fix feature flag for validity_invariants_of --- library/core/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 093c7d298734a..2daac258ba336 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -156,6 +156,7 @@ #![feature(const_slice_from_ref)] #![feature(const_slice_index)] #![feature(const_is_char_boundary)] +#![cfg_attr(not(bootstrap), feature(validity_invariants_of))] // // Language features: #![feature(abi_unadjusted)] From 831d2edaf154896489b736ac9b7e16951ed3cc1a Mon Sep 17 00:00:00 2001 From: 5225225 <5225225@mailbox.org> Date: Tue, 14 Jun 2022 00:11:44 +0100 Subject: [PATCH 05/12] Add validity_invariants_of to cranelift codegen (untested) --- compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs index 6937e658ed5ee..bad2bca244c54 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs @@ -720,7 +720,7 @@ fn codegen_regular_intrinsic_call<'tcx>( dest.write_cvalue(fx, val); }; - pref_align_of | needs_drop | type_id | type_name | variant_count, () { + pref_align_of | needs_drop | type_id | type_name | variant_count | validity_invariants_of, () { let const_val = fx.tcx.const_eval_instance(ParamEnv::reveal_all(), instance, None).unwrap(); let val = crate::constant::codegen_const_value( From 16f7536bc4871f40dba352a1b05ff63fec0d4d90 Mon Sep 17 00:00:00 2001 From: 5225225 <5225225@mailbox.org> Date: Tue, 14 Jun 2022 01:30:12 +0100 Subject: [PATCH 06/12] Adjust documentation wording --- library/core/src/intrinsics.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index f6bc13d98d358..984b3388ce7f4 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -2425,7 +2425,8 @@ pub(crate) const unsafe fn assert_validity_of(_: *const T) -> bool { } #[cfg(not(bootstrap))] -/// Asserts that the value at `value` is valid at type T. +/// Asserts that the value at `value` is a valid T. +/// /// Best effort, and is UB if the value is invalid. pub(crate) unsafe fn assert_validity_of(value: *const T) -> bool { // We have to do this, since we call assert_validity_of inside MaybeUninit::assume_init From 19e0f6f4683ef5a144ef850a1048698838de3399 Mon Sep 17 00:00:00 2001 From: 5225225 <5225225@mailbox.org> Date: Wed, 15 Jun 2022 00:14:24 +0100 Subject: [PATCH 07/12] Calculate layout of Invariant as needed, use enum for size --- .../intrinsics/validity_invariants_of.rs | 132 +++++++++++------- compiler/rustc_hir/src/lang_items.rs | 1 + .../rustc_middle/src/mir/interpret/value.rs | 5 + compiler/rustc_span/src/symbol.rs | 1 + library/core/src/intrinsics.rs | 59 +++++--- 5 files changed, 128 insertions(+), 70 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics/validity_invariants_of.rs b/compiler/rustc_const_eval/src/interpret/intrinsics/validity_invariants_of.rs index c23fcf0f47298..90bbed19d29e9 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics/validity_invariants_of.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics/validity_invariants_of.rs @@ -1,29 +1,48 @@ -use rustc_middle::mir::interpret::{Allocation, ConstAllocation}; -use rustc_middle::mir::Mutability; +use rustc_hir::lang_items::LangItem; +use rustc_middle::mir::interpret::{AllocRange, Allocation, ConstAllocation, Scalar as MirScalar}; use rustc_middle::ty::layout::LayoutCx; use rustc_middle::ty::{ParamEnv, ParamEnvAnd}; use rustc_middle::ty::{Ty, TyCtxt}; use rustc_target::abi::{ - Abi, Align, Endian, FieldsShape, HasDataLayout, Scalar, Size, WrappingRange, + Abi, FieldsShape, HasDataLayout, Integer, Primitive, Scalar, Size, TyAndLayout, WrappingRange, }; +#[derive(Debug, Clone, Copy)] +enum InvariantSize { + U8, + U16, + U32, + U64, + U128, + Pointer, +} + #[derive(Debug, Clone, Copy)] struct Invariant { offset: Size, - size: Size, - start: u128, - end: u128, + size: InvariantSize, + valid_range_start: u128, + valid_range_end: u128, } -// TODO: Don't add duplicate invariants (maybe use a HashMap?) +// FIXME: Don't add duplicate invariants (maybe use a HashMap?) fn add_invariants<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, invs: &mut Vec, offset: Size) { let x = tcx.layout_of(ParamEnvAnd { param_env: ParamEnv::reveal_all(), value: ty }); if let Ok(layout) = x { if let Abi::Scalar(Scalar::Initialized { value, valid_range }) = layout.layout.abi() { - let size = value.size(&tcx); + let size = match value { + Primitive::Int(Integer::I8, _) => InvariantSize::U8, + Primitive::Int(Integer::I16, _) => InvariantSize::U16, + Primitive::Int(Integer::I32, _) => InvariantSize::U32, + Primitive::Int(Integer::I64, _) => InvariantSize::U64, + Primitive::Int(Integer::I128, _) => InvariantSize::U128, + Primitive::F32 => InvariantSize::U32, + Primitive::F64 => InvariantSize::U64, + Primitive::Pointer => InvariantSize::Pointer, + }; let WrappingRange { start, end } = valid_range; - invs.push(Invariant { offset, size, start, end }) + invs.push(Invariant { offset, size, valid_range_start: start, valid_range_end: end }) } let param_env = ParamEnv::reveal_all(); @@ -33,8 +52,8 @@ fn add_invariants<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, invs: &mut Vec {} FieldsShape::Union(_) => {} FieldsShape::Array { stride, count } => { - // TODO: should we just bail if we're making a Too Large type? - // (Like [bool; 1_000_000]) + // We may wish to bail out if we're generating too many invariants. + // That would lead to false negatives, though. for idx in 0..*count { let off = offset + *stride * idx; let f = layout.field(&unwrap, idx as usize); @@ -57,22 +76,13 @@ fn add_invariants<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, invs: &mut Vec, endian: Endian, ptr_size: PointerSize, value: Size) { - match (endian, ptr_size) { - (Endian::Little, PointerSize::Bits16) => to.extend((value.bytes() as u16).to_le_bytes()), - (Endian::Little, PointerSize::Bits32) => to.extend((value.bytes() as u32).to_le_bytes()), - (Endian::Little, PointerSize::Bits64) => to.extend((value.bytes()).to_le_bytes()), - (Endian::Big, PointerSize::Bits16) => to.extend((value.bytes() as u16).to_be_bytes()), - (Endian::Big, PointerSize::Bits32) => to.extend((value.bytes() as u32).to_be_bytes()), - (Endian::Big, PointerSize::Bits64) => to.extend((value.bytes()).to_be_bytes()), - } -} - -#[derive(Clone, Copy)] -enum PointerSize { - Bits16, - Bits32, - Bits64, +fn get_layout_of_invariant<'tcx>(tcx: TyCtxt<'tcx>) -> TyAndLayout<'tcx, Ty<'tcx>> { + let item = tcx.require_lang_item(LangItem::ValidityInvariant, None); + let ty = tcx.type_of(item); + let layout = tcx + .layout_of(ParamEnv::reveal_all().and(ty)) + .expect("invalid layout for ValidityInvariant lang item"); + layout } /// Directly returns a `ConstAllocation` containing a list of validity invariants of the given type. @@ -83,38 +93,54 @@ pub(crate) fn alloc_validity_invariants_of<'tcx>( let mut invs: Vec = Vec::new(); let layout = tcx.data_layout(); - - let ptr_size = match layout.pointer_size.bits() { - 16 => PointerSize::Bits16, - 32 => PointerSize::Bits32, - 64 => PointerSize::Bits64, - _ => { - // Not sure if this can happen, but just return an empty slice? - let alloc = - Allocation::from_bytes(Vec::new(), Align::from_bytes(8).unwrap(), Mutability::Not); - return tcx.intern_const_alloc(alloc); - } - }; + let validity_invariant = get_layout_of_invariant(tcx); add_invariants(tcx, ty, &mut invs, Size::ZERO); - let encode_range = match layout.endian { - Endian::Little => |r: u128| r.to_le_bytes(), - Endian::Big => |r: u128| r.to_be_bytes(), - }; + let allocation_size = validity_invariant.layout.size() * invs.len() as u64; + let mut alloc = + Allocation::uninit(allocation_size, validity_invariant.layout.align().abi, true).unwrap(); + + let offset_off = validity_invariant.layout.fields().offset(0); + let size_off = validity_invariant.layout.fields().offset(1); + let start_off = validity_invariant.layout.fields().offset(2); + let end_off = validity_invariant.layout.fields().offset(3); + + for (idx, invariant) in invs.iter().enumerate() { + let offset = idx as u64 * validity_invariant.layout.size(); + + let offset_range = AllocRange { start: offset + offset_off, size: layout.pointer_size }; + alloc + .write_scalar( + &tcx, + offset_range, + MirScalar::from_machine_usize(invariant.offset.bytes(), &tcx).into(), + ) + .unwrap(); + + let size_range = AllocRange { start: offset + size_off, size: Size::from_bytes(1) }; + alloc + .write_scalar(&tcx, size_range, MirScalar::from_u8(invariant.size as u8).into()) + .unwrap(); - let mut encoded = Vec::new(); + let offset_range = AllocRange { start: offset + start_off, size: Size::from_bytes(16) }; + alloc + .write_scalar( + &tcx, + offset_range, + MirScalar::from_u128(invariant.valid_range_start).into(), + ) + .unwrap(); - // TODO: this needs to match the layout of `Invariant` in core/src/intrinsics.rs - // how do we ensure that? - for inv in invs { - extend_encoded_int(&mut encoded, layout.endian, ptr_size, inv.offset); - extend_encoded_int(&mut encoded, layout.endian, ptr_size, inv.size); - encoded.extend(encode_range(inv.start)); - encoded.extend(encode_range(inv.end)); + let offset_range = AllocRange { start: offset + end_off, size: Size::from_bytes(16) }; + alloc + .write_scalar( + &tcx, + offset_range, + MirScalar::from_u128(invariant.valid_range_end).into(), + ) + .unwrap(); } - // TODO: The alignment here should be calculated from the struct definition, I guess? - let alloc = Allocation::from_bytes(encoded, Align::from_bytes(8).unwrap(), Mutability::Not); tcx.intern_const_alloc(alloc) } diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 9e5d781892487..1f38b8219fe60 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -327,6 +327,7 @@ language_item_table! { Range, sym::Range, range_struct, Target::Struct, GenericRequirement::None; RangeToInclusive, sym::RangeToInclusive, range_to_inclusive_struct, Target::Struct, GenericRequirement::None; RangeTo, sym::RangeTo, range_to_struct, Target::Struct, GenericRequirement::None; + ValidityInvariant, sym::ValidityInvariant, validity_invariant_struct, Target::Struct, GenericRequirement::None; } pub enum GenericRequirement { diff --git a/compiler/rustc_middle/src/mir/interpret/value.rs b/compiler/rustc_middle/src/mir/interpret/value.rs index e80918d5e5d03..ac6ee3f768da5 100644 --- a/compiler/rustc_middle/src/mir/interpret/value.rs +++ b/compiler/rustc_middle/src/mir/interpret/value.rs @@ -258,6 +258,11 @@ impl Scalar { Scalar::Int(i.into()) } + #[inline] + pub fn from_u128(i: u128) -> Self { + Scalar::Int(i.into()) + } + #[inline] pub fn from_machine_usize(i: u64, cx: &impl HasDataLayout) -> Self { Self::from_uint(i, cx.data_layout().pointer_size) diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 56502dab93283..e0afbccff53e0 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -277,6 +277,7 @@ symbols! { TyKind, Unknown, UnsafeArg, + ValidityInvariant, Vec, VecDeque, Wrapper, diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index 984b3388ce7f4..005bffee36b06 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -2138,18 +2138,42 @@ pub const unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: us } } -#[repr(C)] #[derive(Debug, Clone, Copy)] -pub struct Invariant { - offset: u64, - size: u64, - start: u128, - end: u128, +#[repr(u8)] +enum InvariantSize { + U8 = 0, + U16 = 1, + U32 = 2, + U64 = 3, + U128 = 4, + Pointer = 5, +} + +/// An Invariant is a field and its valid range. The valid range may be full, in which case it +/// should still be branched on, to allow tools like memory sanitizer to verify the memory is not +/// uninit. +// Be sure to keep the fields in order (offset, size, start, end), validity_invariants_of.rs needs +// that. +#[cfg(not(bootstrap))] +#[derive(Debug, Clone, Copy)] +#[lang = "ValidityInvariant"] +struct Invariant { + /// The offset in bytes from the start of the struct + offset: usize, + /// The size/type of the invariant. + size: InvariantSize, + /// The start point of the valid range of this field. Is allowed to be > valid_range_end, see + /// + /// which this follows the semantics of. + valid_range_start: u128, + + /// The end point of the range. + valid_range_end: u128, } #[cfg(not(bootstrap))] /// Returns a list of all validity invariants of the type. -pub const fn validity_invariants_of() -> &'static [Invariant] { +const fn validity_invariants_of() -> &'static [Invariant] { extern "rust-intrinsic" { #[rustc_const_unstable(feature = "validity_invariants_of", issue = "none")] pub fn validity_invariants_of() -> &'static [u8]; @@ -2435,34 +2459,35 @@ pub(crate) unsafe fn assert_validity_of(value: *const T) -> bool { struct Unaligned(T); // SAFETY: The pointer dereferences here are valid if `value` is valid. - // though TODO: introduce a new size for "pointer", since reading a pointer as an int *is* UB. unsafe { let invariants = validity_invariants_of::(); for invariant in invariants { let off = invariant.offset as usize; - let start = invariant.start; - let end = invariant.end; + let start = invariant.valid_range_start; + let end = invariant.valid_range_end; - // TODO: Maybe replace this with an enum? let (value, max): (u128, u128) = match invariant.size { - 1 => ((*(value.cast::().add(off))).into(), u8::MAX.into()), - 2 => ( + InvariantSize::U8 => ((*(value.cast::().add(off))).into(), u8::MAX.into()), + InvariantSize::U16 => ( (*value.cast::().add(off).cast::>()).0.into(), u16::MAX.into(), ), - 4 => ( + InvariantSize::U32 => ( (*value.cast::().add(off).cast::>()).0.into(), u32::MAX.into(), ), - 8 => ( + InvariantSize::U64 => ( (*value.cast::().add(off).cast::>()).0.into(), u64::MAX.into(), ), - 16 => ( + InvariantSize::U128 => ( (*value.cast::().add(off).cast::>()).0.into(), u128::MAX.into(), ), - s => panic!("unexpected size {s}"), + InvariantSize::Pointer => ( + (*value.cast::().add(off).cast::>()).0.addr() as u128, + usize::MAX as u128, + ), }; if start > end { From 97ce1fb3766ccb097c6aeecf7ab231b3dcbb9ef8 Mon Sep 17 00:00:00 2001 From: 5225225 <5225225@mailbox.org> Date: Thu, 16 Jun 2022 11:34:16 +0100 Subject: [PATCH 08/12] Deduplicate invariants generated --- .../intrinsics/validity_invariants_of.rs | 27 ++++++++++--------- library/core/src/intrinsics.rs | 1 + 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics/validity_invariants_of.rs b/compiler/rustc_const_eval/src/interpret/intrinsics/validity_invariants_of.rs index 90bbed19d29e9..20d46c00011d0 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics/validity_invariants_of.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics/validity_invariants_of.rs @@ -1,3 +1,4 @@ +use rustc_data_structures::fx::FxHashMap; use rustc_hir::lang_items::LangItem; use rustc_middle::mir::interpret::{AllocRange, Allocation, ConstAllocation, Scalar as MirScalar}; use rustc_middle::ty::layout::LayoutCx; @@ -7,7 +8,7 @@ use rustc_target::abi::{ Abi, FieldsShape, HasDataLayout, Integer, Primitive, Scalar, Size, TyAndLayout, WrappingRange, }; -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] enum InvariantSize { U8, U16, @@ -17,16 +18,14 @@ enum InvariantSize { Pointer, } -#[derive(Debug, Clone, Copy)] -struct Invariant { +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +struct InvariantKey { offset: Size, size: InvariantSize, - valid_range_start: u128, - valid_range_end: u128, } // FIXME: Don't add duplicate invariants (maybe use a HashMap?) -fn add_invariants<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, invs: &mut Vec, offset: Size) { +fn add_invariants<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, invs: &mut FxHashMap, offset: Size) { let x = tcx.layout_of(ParamEnvAnd { param_env: ParamEnv::reveal_all(), value: ty }); if let Ok(layout) = x { @@ -41,8 +40,10 @@ fn add_invariants<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, invs: &mut Vec InvariantSize::U64, Primitive::Pointer => InvariantSize::Pointer, }; - let WrappingRange { start, end } = valid_range; - invs.push(Invariant { offset, size, valid_range_start: start, valid_range_end: end }) + + // Pick the first scalar we see, this means NonZeroU8(u8) ends up with only one + // invariant, the stricter one. + let _: Result<_, _> = invs.try_insert(InvariantKey { offset, size }, valid_range); } let param_env = ParamEnv::reveal_all(); @@ -90,7 +91,7 @@ pub(crate) fn alloc_validity_invariants_of<'tcx>( tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, ) -> ConstAllocation<'tcx> { - let mut invs: Vec = Vec::new(); + let mut invs = FxHashMap::default(); let layout = tcx.data_layout(); let validity_invariant = get_layout_of_invariant(tcx); @@ -114,13 +115,13 @@ pub(crate) fn alloc_validity_invariants_of<'tcx>( .write_scalar( &tcx, offset_range, - MirScalar::from_machine_usize(invariant.offset.bytes(), &tcx).into(), + MirScalar::from_machine_usize(invariant.0.offset.bytes(), &tcx).into(), ) .unwrap(); let size_range = AllocRange { start: offset + size_off, size: Size::from_bytes(1) }; alloc - .write_scalar(&tcx, size_range, MirScalar::from_u8(invariant.size as u8).into()) + .write_scalar(&tcx, size_range, MirScalar::from_u8(invariant.0.size as u8).into()) .unwrap(); let offset_range = AllocRange { start: offset + start_off, size: Size::from_bytes(16) }; @@ -128,7 +129,7 @@ pub(crate) fn alloc_validity_invariants_of<'tcx>( .write_scalar( &tcx, offset_range, - MirScalar::from_u128(invariant.valid_range_start).into(), + MirScalar::from_u128(invariant.1.start).into(), ) .unwrap(); @@ -137,7 +138,7 @@ pub(crate) fn alloc_validity_invariants_of<'tcx>( .write_scalar( &tcx, offset_range, - MirScalar::from_u128(invariant.valid_range_end).into(), + MirScalar::from_u128(invariant.1.end).into(), ) .unwrap(); } diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index 005bffee36b06..caf7d1d9c7395 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -2140,6 +2140,7 @@ pub const unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: us #[derive(Debug, Clone, Copy)] #[repr(u8)] +#[allow(dead_code)] // https://github.com/rust-lang/rust/issues/85677 enum InvariantSize { U8 = 0, U16 = 1, From 3d94e2514c31ddd79f04369a7763c8362007d135 Mon Sep 17 00:00:00 2001 From: 5225225 <5225225@mailbox.org> Date: Thu, 16 Jun 2022 15:03:53 +0100 Subject: [PATCH 09/12] Properly emit a slice of Invariant, add tests --- compiler/rustc_codegen_ssa/src/mir/operand.rs | 16 +++++ .../src/interpret/intrinsics.rs | 11 ++- .../intrinsics/validity_invariants_of.rs | 41 ++++++----- .../rustc_const_eval/src/interpret/operand.rs | 13 ++++ .../rustc_middle/src/mir/interpret/value.rs | 10 ++- compiler/rustc_middle/src/mir/pretty.rs | 5 +- compiler/rustc_middle/src/ty/context.rs | 5 -- compiler/rustc_typeck/src/check/intrinsic.rs | 8 ++- library/core/src/intrinsics.rs | 48 ++++++------- library/core/src/lib.rs | 2 - .../ui/intrinsics/validity_invariants_of.rs | 68 +++++++++++++++++++ 11 files changed, 169 insertions(+), 58 deletions(-) create mode 100644 src/test/ui/intrinsics/validity_invariants_of.rs diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs index 08be4c0a7b622..ddb88d97b7f9b 100644 --- a/compiler/rustc_codegen_ssa/src/mir/operand.rs +++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs @@ -100,6 +100,22 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { let b_llval = bx.const_usize((end - start) as u64); OperandValue::Pair(a_llval, b_llval) } + ConstValue::CustomSlice { data, length } => { + let Abi::ScalarPair(a_scalar, _) = layout.abi else { + bug!("from_const: invalid ScalarPair layout: {:#?}", layout); + }; + let a = Scalar::from_pointer( + Pointer::new(bx.tcx().create_memory_alloc(data), Size::ZERO), + &bx.tcx(), + ); + let a_llval = bx.scalar_to_backend( + a, + a_scalar, + bx.scalar_pair_element_backend_type(layout, 0, true), + ); + let b_llval = bx.const_usize(length as u64); + OperandValue::Pair(a_llval, b_llval) + } ConstValue::ByRef { alloc, offset } => { return bx.load_operand(bx.from_const_alloc(layout, alloc, offset)); } diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 4e3d098539cff..a5e91ef51d7dc 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -5,6 +5,7 @@ use std::convert::TryFrom; use rustc_hir::def_id::DefId; +use rustc_hir::lang_items::LangItem; use rustc_middle::mir::{ self, interpret::{ConstValue, GlobalId, InterpResult, Scalar}, @@ -106,8 +107,8 @@ pub(crate) fn eval_nullary_intrinsic<'tcx>( }, sym::validity_invariants_of => { ensure_monomorphic_enough(tcx, tp_ty)?; - let alloc = validity_invariants_of::alloc_validity_invariants_of(tcx, tp_ty); - ConstValue::Slice { data: alloc, start: 0, end: alloc.inner().len() } + let (data, length) = validity_invariants_of::alloc_validity_invariants_of(tcx, tp_ty); + ConstValue::CustomSlice { data, length } } other => bug!("`{}` is not a zero arg intrinsic", other), }) @@ -176,7 +177,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { sym::needs_drop => self.tcx.types.bool, sym::type_id => self.tcx.types.u64, sym::type_name => self.tcx.mk_static_str(), - sym::validity_invariants_of => self.tcx.mk_static_bytes(), + sym::validity_invariants_of => { + let item = self.tcx.require_lang_item(LangItem::ValidityInvariant, None); + let ty = self.tcx.type_of(item); + self.tcx.mk_imm_ref(self.tcx.lifetimes.re_static, self.tcx.mk_slice(ty)) + } _ => bug!(), }; let val = diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics/validity_invariants_of.rs b/compiler/rustc_const_eval/src/interpret/intrinsics/validity_invariants_of.rs index 20d46c00011d0..35b0cc998ef2b 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics/validity_invariants_of.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics/validity_invariants_of.rs @@ -1,11 +1,12 @@ use rustc_data_structures::fx::FxHashMap; use rustc_hir::lang_items::LangItem; use rustc_middle::mir::interpret::{AllocRange, Allocation, ConstAllocation, Scalar as MirScalar}; +use rustc_middle::mir::Mutability; use rustc_middle::ty::layout::LayoutCx; use rustc_middle::ty::{ParamEnv, ParamEnvAnd}; use rustc_middle::ty::{Ty, TyCtxt}; use rustc_target::abi::{ - Abi, FieldsShape, HasDataLayout, Integer, Primitive, Scalar, Size, TyAndLayout, WrappingRange, + Abi, FieldsShape, HasDataLayout, Integer, Primitive, Scalar, Size, TyAndLayout, WrappingRange, Variants, }; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -25,7 +26,12 @@ struct InvariantKey { } // FIXME: Don't add duplicate invariants (maybe use a HashMap?) -fn add_invariants<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, invs: &mut FxHashMap, offset: Size) { +fn add_invariants<'tcx>( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, + invs: &mut FxHashMap, + offset: Size, +) { let x = tcx.layout_of(ParamEnvAnd { param_env: ParamEnv::reveal_all(), value: ty }); if let Ok(layout) = x { @@ -46,8 +52,14 @@ fn add_invariants<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, invs: &mut FxHashMap = invs.try_insert(InvariantKey { offset, size }, valid_range); } + //dbg!(&ty, &layout); + if !matches!(layout.layout.variants(), Variants::Single { .. }) { + // We *don't* want to look for fields inside enums. + return; + } + let param_env = ParamEnv::reveal_all(); - let unwrap = LayoutCx { tcx, param_env }; + let layout_cx = LayoutCx { tcx, param_env }; match layout.layout.fields() { FieldsShape::Primitive => {} @@ -57,13 +69,13 @@ fn add_invariants<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, invs: &mut FxHashMap { for (idx, &field_offset) in offsets.iter().enumerate() { - let f = layout.field(&unwrap, idx); + let f = layout.field(&layout_cx, idx); if f.ty == ty { // Some types contain themselves as fields, such as // &mut [T] @@ -90,7 +102,7 @@ fn get_layout_of_invariant<'tcx>(tcx: TyCtxt<'tcx>) -> TyAndLayout<'tcx, Ty<'tcx pub(crate) fn alloc_validity_invariants_of<'tcx>( tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, -) -> ConstAllocation<'tcx> { +) -> (ConstAllocation<'tcx>, usize) { let mut invs = FxHashMap::default(); let layout = tcx.data_layout(); @@ -126,22 +138,17 @@ pub(crate) fn alloc_validity_invariants_of<'tcx>( let offset_range = AllocRange { start: offset + start_off, size: Size::from_bytes(16) }; alloc - .write_scalar( - &tcx, - offset_range, - MirScalar::from_u128(invariant.1.start).into(), - ) + .write_scalar(&tcx, offset_range, MirScalar::from_u128(invariant.1.start).into()) .unwrap(); let offset_range = AllocRange { start: offset + end_off, size: Size::from_bytes(16) }; alloc - .write_scalar( - &tcx, - offset_range, - MirScalar::from_u128(invariant.1.end).into(), - ) + .write_scalar(&tcx, offset_range, MirScalar::from_u128(invariant.1.end).into()) .unwrap(); } - tcx.intern_const_alloc(alloc) + // The allocation is not mutable, we just needed write_scalar. + alloc.mutability = Mutability::Not; + + (tcx.intern_const_alloc(alloc), invs.len()) } diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index 6b05a49575fd9..ebbff75f0a102 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -692,6 +692,19 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { self, )) } + ConstValue::CustomSlice { data, length } => { + // We rely on mutability being set correctly in `data` to prevent writes + // where none should happen. + let ptr = Pointer::new( + self.tcx.create_memory_alloc(data), + Size::ZERO, // offset: 0 + ); + Operand::Immediate(Immediate::new_slice( + Scalar::from_pointer(self.global_base_pointer(ptr)?, &*self.tcx), + u64::try_from(length).unwrap(), + self, + )) + } }; Ok(OpTy { op, layout }) } diff --git a/compiler/rustc_middle/src/mir/interpret/value.rs b/compiler/rustc_middle/src/mir/interpret/value.rs index ac6ee3f768da5..610abe4b187a1 100644 --- a/compiler/rustc_middle/src/mir/interpret/value.rs +++ b/compiler/rustc_middle/src/mir/interpret/value.rs @@ -37,6 +37,9 @@ pub enum ConstValue<'tcx> { /// Used only for `&[u8]` and `&str` Slice { data: ConstAllocation<'tcx>, start: usize, end: usize }, + /// Like `Slice`, but for types that aren't 1 byte long. + CustomSlice { data: ConstAllocation<'tcx>, length: usize }, + /// A value not represented/representable by `Scalar` or `Slice` ByRef { /// The backing memory of the value, may contain more memory than needed for just the value @@ -61,6 +64,9 @@ impl<'a, 'tcx> Lift<'tcx> for ConstValue<'a> { ConstValue::ByRef { alloc, offset } => { ConstValue::ByRef { alloc: tcx.lift(alloc)?, offset } } + ConstValue::CustomSlice { data, length } => { + ConstValue::CustomSlice { data: tcx.lift(data)?, length } + } }) } } @@ -69,7 +75,9 @@ impl<'tcx> ConstValue<'tcx> { #[inline] pub fn try_to_scalar(&self) -> Option> { match *self { - ConstValue::ByRef { .. } | ConstValue::Slice { .. } => None, + ConstValue::ByRef { .. } + | ConstValue::Slice { .. } + | ConstValue::CustomSlice { .. } => None, ConstValue::Scalar(val) => Some(val), } } diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 462c0ada3cf87..d44d2567cc388 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -451,6 +451,7 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> { let fmt_val = |val: &ConstValue<'tcx>| match val { ConstValue::Scalar(s) => format!("Scalar({:?})", s), ConstValue::Slice { .. } => format!("Slice(..)"), + ConstValue::CustomSlice { .. } => format!("CustomSlice(..)"), ConstValue::ByRef { .. } => format!("ByRef(..)"), }; @@ -679,7 +680,9 @@ pub fn write_allocations<'tcx>( ConstValue::Scalar(interpret::Scalar::Int { .. }) => { Either::Left(Either::Right(std::iter::empty())) } - ConstValue::ByRef { alloc, .. } | ConstValue::Slice { data: alloc, .. } => { + ConstValue::ByRef { alloc, .. } + | ConstValue::Slice { data: alloc, .. } + | ConstValue::CustomSlice { data: alloc, .. } => { Either::Right(alloc_ids_from_alloc(alloc)) } } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index fda734fab2904..c43cf07b3ad0a 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -2339,11 +2339,6 @@ impl<'tcx> TyCtxt<'tcx> { self.mk_imm_ref(self.lifetimes.re_static, self.types.str_) } - #[inline] - pub fn mk_static_bytes(self) -> Ty<'tcx> { - self.mk_imm_ref(self.lifetimes.re_static, self.mk_slice(self.types.u8)) - } - #[inline] pub fn mk_adt(self, def: AdtDef<'tcx>, substs: SubstsRef<'tcx>) -> Ty<'tcx> { // Take a copy of substs so that we own the vectors inside. diff --git a/compiler/rustc_typeck/src/check/intrinsic.rs b/compiler/rustc_typeck/src/check/intrinsic.rs index c766787a5cc22..3e73864d7a777 100644 --- a/compiler/rustc_typeck/src/check/intrinsic.rs +++ b/compiler/rustc_typeck/src/check/intrinsic.rs @@ -9,6 +9,7 @@ use crate::require_same_types; use rustc_errors::struct_span_err; use rustc_hir as hir; +use rustc_hir::lang_items::LangItem; use rustc_middle::traits::{ObligationCause, ObligationCauseCode}; use rustc_middle::ty::subst::Subst; use rustc_middle::ty::{self, TyCtxt}; @@ -192,7 +193,12 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { sym::needs_drop => (1, Vec::new(), tcx.types.bool), sym::type_name => (1, Vec::new(), tcx.mk_static_str()), - sym::validity_invariants_of => (1, Vec::new(), tcx.mk_static_bytes()), + sym::validity_invariants_of => { + let item = tcx.require_lang_item(LangItem::ValidityInvariant, None); + let ty = tcx.type_of(item); + let slice = tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.mk_slice(ty)); + (1, Vec::new(), slice) + } sym::type_id => (1, Vec::new(), tcx.types.u64), sym::offset | sym::arith_offset => ( 1, diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index caf7d1d9c7395..15cc8b71ef486 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -1976,6 +1976,13 @@ extern "rust-intrinsic" { /// [`std::hint::black_box`]: crate::hint::black_box #[rustc_const_unstable(feature = "const_black_box", issue = "none")] pub fn black_box(dummy: T) -> T; + + /// Returns a list of invaraints that must be valid in order for T to be valid. + /// + /// This is used internally to allow for runtime assertions inside `MaybeUninit::assume_init` + #[cfg(not(bootstrap))] + #[rustc_const_unstable(feature = "const_intrinsic_validity_invariants_of", issue = "none")] + pub fn validity_invariants_of() -> &'static [Invariant]; } // Some functions are defined here because they accidentally got made @@ -2138,10 +2145,10 @@ pub const unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: us } } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq)] #[repr(u8)] #[allow(dead_code)] // https://github.com/rust-lang/rust/issues/85677 -enum InvariantSize { +pub enum InvariantSize { U8 = 0, U16 = 1, U32 = 2, @@ -2156,35 +2163,20 @@ enum InvariantSize { // Be sure to keep the fields in order (offset, size, start, end), validity_invariants_of.rs needs // that. #[cfg(not(bootstrap))] -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq)] #[lang = "ValidityInvariant"] -struct Invariant { +pub struct Invariant { /// The offset in bytes from the start of the struct - offset: usize, + pub offset: usize, /// The size/type of the invariant. - size: InvariantSize, + pub size: InvariantSize, /// The start point of the valid range of this field. Is allowed to be > valid_range_end, see /// /// which this follows the semantics of. - valid_range_start: u128, + pub valid_range_start: u128, /// The end point of the range. - valid_range_end: u128, -} - -#[cfg(not(bootstrap))] -/// Returns a list of all validity invariants of the type. -const fn validity_invariants_of() -> &'static [Invariant] { - extern "rust-intrinsic" { - #[rustc_const_unstable(feature = "validity_invariants_of", issue = "none")] - pub fn validity_invariants_of() -> &'static [u8]; - } - - let invariants: &'static [u8] = validity_invariants_of::(); - let sz = invariants.len() / core::mem::size_of::(); - - // SAFETY: we know this is valid because the intrinsic promises an aligned slice. - unsafe { core::slice::from_raw_parts(invariants.as_ptr().cast(), sz) } + pub valid_range_end: u128, } /// Copies `count * size_of::()` bytes from `src` to `dst`. The source @@ -2445,15 +2437,15 @@ where } #[cfg(bootstrap)] -pub(crate) const unsafe fn assert_validity_of(_: *const T) -> bool { +pub const unsafe fn assert_validity_of(_: *const T) -> bool { true } #[cfg(not(bootstrap))] /// Asserts that the value at `value` is a valid T. /// -/// Best effort, and is UB if the value is invalid. -pub(crate) unsafe fn assert_validity_of(value: *const T) -> bool { +/// Best effort, can miss some UB, and is UB if the value is invalid. +pub unsafe fn assert_validity_of(value: *const T) -> bool { // We have to do this, since we call assert_validity_of inside MaybeUninit::assume_init // and if we had used ptr::read_unaligned, that would be a recursive call. #[repr(packed)] @@ -2493,11 +2485,11 @@ pub(crate) unsafe fn assert_validity_of(value: *const T) -> bool { if start > end { if !((start..=max).contains(&value) || (0..=end).contains(&value)) { - return false; + panic!("read value {value} which was not in range 0..={end} or {start}..={max} at offset {off} in type {}", core::any::type_name::()); } } else { if !(start..=end).contains(&value) { - return false; + panic!("read value {value} which was not in range {start}..={end} at offset {off} in type {}", core::any::type_name::()); } } } diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 2daac258ba336..6e1d304e50cd2 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -156,8 +156,6 @@ #![feature(const_slice_from_ref)] #![feature(const_slice_index)] #![feature(const_is_char_boundary)] -#![cfg_attr(not(bootstrap), feature(validity_invariants_of))] -// // Language features: #![feature(abi_unadjusted)] #![feature(allow_internal_unsafe)] diff --git a/src/test/ui/intrinsics/validity_invariants_of.rs b/src/test/ui/intrinsics/validity_invariants_of.rs new file mode 100644 index 0000000000000..f7991b0b3fc3d --- /dev/null +++ b/src/test/ui/intrinsics/validity_invariants_of.rs @@ -0,0 +1,68 @@ +// run-pass +#![feature(core_intrinsics, const_intrinsic_validity_invariants_of)] + +use std::mem::MaybeUninit; +use std::intrinsics::{Invariant, InvariantSize, validity_invariants_of, assert_validity_of}; +use std::num::NonZeroU16; + +#[repr(C)] +struct MyStruct { + a: NonZeroU16, + b: u8, + c: bool, +} + +const MY_STRUCT_INVARIANTS: &'static [Invariant] = validity_invariants_of::(); +const OPTION_INVARIANTS: &'static [Invariant] = validity_invariants_of::>(); + +fn main() { + let mut invs: Vec = MY_STRUCT_INVARIANTS.to_vec(); + + invs.sort_by_key(|x| x.offset); + + assert_eq!(&invs, &[ + Invariant { + offset: 0, + size: InvariantSize::U16, + valid_range_start: 1, + valid_range_end: 65535 + }, + Invariant { + offset: 2, + size: InvariantSize::U8, + valid_range_start: 0, + valid_range_end: 255 + }, + Invariant { + offset: 3, + size: InvariantSize::U8, + valid_range_start: 0, + valid_range_end: 1 + }, + ]); + + unsafe { + let v = MyStruct { a: NonZeroU16::new(1).unwrap(), b: 2, c: true }; + assert!(assert_validity_of(&v as *const _)); + } + + assert_eq!(OPTION_INVARIANTS, &[ + Invariant { + offset: 0, + size: InvariantSize::Pointer, + valid_range_start: 1, + valid_range_end: 0, + }, + ]); + + + unsafe { + let p = MaybeUninit::>::zeroed().as_ptr(); + assert!(assert_validity_of(p)); + } + + // There's two code paths for generating the data, be sure to test that compile time and + // runtime matches. + assert_eq!(validity_invariants_of::>(), OPTION_INVARIANTS); + assert_eq!(validity_invariants_of::(), MY_STRUCT_INVARIANTS); +} From c24ef460bcd2522b54c665d33006d098262100c5 Mon Sep 17 00:00:00 2001 From: 5225225 <5225225@mailbox.org> Date: Fri, 17 Jun 2022 12:15:08 +0100 Subject: [PATCH 10/12] Reformat --- .../src/interpret/intrinsics/validity_invariants_of.rs | 3 ++- library/core/src/intrinsics.rs | 10 ++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics/validity_invariants_of.rs b/compiler/rustc_const_eval/src/interpret/intrinsics/validity_invariants_of.rs index 35b0cc998ef2b..1869016821054 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics/validity_invariants_of.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics/validity_invariants_of.rs @@ -6,7 +6,8 @@ use rustc_middle::ty::layout::LayoutCx; use rustc_middle::ty::{ParamEnv, ParamEnvAnd}; use rustc_middle::ty::{Ty, TyCtxt}; use rustc_target::abi::{ - Abi, FieldsShape, HasDataLayout, Integer, Primitive, Scalar, Size, TyAndLayout, WrappingRange, Variants, + Abi, FieldsShape, HasDataLayout, Integer, Primitive, Scalar, Size, TyAndLayout, Variants, + WrappingRange, }; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index 15cc8b71ef486..01f3d419bfa7e 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -2485,11 +2485,17 @@ pub unsafe fn assert_validity_of(value: *const T) -> bool { if start > end { if !((start..=max).contains(&value) || (0..=end).contains(&value)) { - panic!("read value {value} which was not in range 0..={end} or {start}..={max} at offset {off} in type {}", core::any::type_name::()); + panic!( + "read value {value} which was not in range 0..={end} or {start}..={max} at offset {off} in type {}", + core::any::type_name::() + ); } } else { if !(start..=end).contains(&value) { - panic!("read value {value} which was not in range {start}..={end} at offset {off} in type {}", core::any::type_name::()); + panic!( + "read value {value} which was not in range {start}..={end} at offset {off} in type {}", + core::any::type_name::() + ); } } } From 8f79b5a7e4c195a227f38aadd591a309b779a0ad Mon Sep 17 00:00:00 2001 From: 5225225 <5225225@mailbox.org> Date: Fri, 17 Jun 2022 19:03:46 +0100 Subject: [PATCH 11/12] Add option to disable validity invariant generation --- .../src/interpret/intrinsics.rs | 15 +++- .../intrinsics/validity_invariants_of.rs | 28 ++++-- compiler/rustc_session/src/options.rs | 2 + .../ui/intrinsics/validity_invariants_of.rs | 85 +++++++++++++------ src/test/ui/sanitize/memory.rs | 4 +- 5 files changed, 98 insertions(+), 36 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index a5e91ef51d7dc..95d3951160e44 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -17,6 +17,7 @@ use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::{Ty, TyCtxt}; use rustc_span::symbol::{sym, Symbol}; use rustc_target::abi::{Abi, Align, InitKind, Primitive, Size}; +use rustc_target::spec::SanitizerSet; use super::{ util::ensure_monomorphic_enough, CheckInAllocMsg, ImmTy, InterpCx, Machine, OpTy, PlaceTy, @@ -106,8 +107,20 @@ pub(crate) fn eval_nullary_intrinsic<'tcx>( | ty::Error(_) => ConstValue::from_machine_usize(0u64, &tcx), }, sym::validity_invariants_of => { + let msan = tcx.sess.opts.debugging_opts.sanitizer.contains(SanitizerSet::MEMORY); + let disable = tcx.sess.opts.debugging_opts.no_validity_invariant_checks; + + let strictness = if disable { + validity_invariants_of::InvariantStrictness::Disable + } else if msan { + validity_invariants_of::InvariantStrictness::All + } else { + validity_invariants_of::InvariantStrictness::Normal + }; + ensure_monomorphic_enough(tcx, tp_ty)?; - let (data, length) = validity_invariants_of::alloc_validity_invariants_of(tcx, tp_ty); + let (data, length) = + validity_invariants_of::alloc_validity_invariants_of(tcx, tp_ty, strictness); ConstValue::CustomSlice { data, length } } other => bug!("`{}` is not a zero arg intrinsic", other), diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics/validity_invariants_of.rs b/compiler/rustc_const_eval/src/interpret/intrinsics/validity_invariants_of.rs index 1869016821054..4ff1629798fc1 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics/validity_invariants_of.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics/validity_invariants_of.rs @@ -32,7 +32,12 @@ fn add_invariants<'tcx>( ty: Ty<'tcx>, invs: &mut FxHashMap, offset: Size, + strictness: InvariantStrictness, ) { + if strictness == InvariantStrictness::Disable { + return; + } + let x = tcx.layout_of(ParamEnvAnd { param_env: ParamEnv::reveal_all(), value: ty }); if let Ok(layout) = x { @@ -48,9 +53,12 @@ fn add_invariants<'tcx>( Primitive::Pointer => InvariantSize::Pointer, }; - // Pick the first scalar we see, this means NonZeroU8(u8) ends up with only one - // invariant, the stricter one. - let _: Result<_, _> = invs.try_insert(InvariantKey { offset, size }, valid_range); + if !valid_range.is_full_for(value.size(&tcx)) || strictness == InvariantStrictness::All + { + // Pick the first scalar we see, this means NonZeroU8(u8) ends up with only one + // invariant, the stricter one. + let _: Result<_, _> = invs.try_insert(InvariantKey { offset, size }, valid_range); + } } //dbg!(&ty, &layout); @@ -71,7 +79,7 @@ fn add_invariants<'tcx>( for idx in 0..*count { let off = offset + *stride * idx; let f = layout.field(&layout_cx, idx as usize); - add_invariants(tcx, f.ty, invs, off); + add_invariants(tcx, f.ty, invs, off, strictness); } } FieldsShape::Arbitrary { offsets, .. } => { @@ -82,7 +90,7 @@ fn add_invariants<'tcx>( // &mut [T] // Easy solution is to just not recurse then. } else { - add_invariants(tcx, f.ty, invs, offset + field_offset); + add_invariants(tcx, f.ty, invs, offset + field_offset, strictness); } } } @@ -99,17 +107,25 @@ fn get_layout_of_invariant<'tcx>(tcx: TyCtxt<'tcx>) -> TyAndLayout<'tcx, Ty<'tcx layout } +#[derive(PartialEq, Clone, Copy, Eq)] +pub(crate) enum InvariantStrictness { + Disable, + Normal, + All, +} + /// Directly returns a `ConstAllocation` containing a list of validity invariants of the given type. pub(crate) fn alloc_validity_invariants_of<'tcx>( tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, + strictness: InvariantStrictness, ) -> (ConstAllocation<'tcx>, usize) { let mut invs = FxHashMap::default(); let layout = tcx.data_layout(); let validity_invariant = get_layout_of_invariant(tcx); - add_invariants(tcx, ty, &mut invs, Size::ZERO); + add_invariants(tcx, ty, &mut invs, Size::ZERO, strictness); let allocation_size = validity_invariant.layout.size() * invs.len() as u64; let mut alloc = diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 441e1f9f6a2b8..ccb591a552256 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1380,6 +1380,8 @@ options! { "run LLVM in non-parallel mode (while keeping codegen-units and ThinLTO)"), no_unique_section_names: bool = (false, parse_bool, [TRACKED], "do not use unique names for text and data sections when -Z function-sections is used"), + no_validity_invariant_checks: bool = (false, parse_bool, [TRACKED], + "do not generate any validity invariants in the validity_invariants_of intrinsic"), no_profiler_runtime: bool = (false, parse_no_flag, [TRACKED], "prevent automatic injection of the profiler_builtins crate"), normalize_docs: bool = (false, parse_bool, [TRACKED], diff --git a/src/test/ui/intrinsics/validity_invariants_of.rs b/src/test/ui/intrinsics/validity_invariants_of.rs index f7991b0b3fc3d..d5156053316ab 100644 --- a/src/test/ui/intrinsics/validity_invariants_of.rs +++ b/src/test/ui/intrinsics/validity_invariants_of.rs @@ -1,4 +1,7 @@ // run-pass +// revisions: disabled normal sanitized +// [disabled]compile-flags: -Zno-validity-invariant-checks +// [sanitized]compile-flags: -Z sanitizer=memory #![feature(core_intrinsics, const_intrinsic_validity_invariants_of)] use std::mem::MaybeUninit; @@ -20,40 +23,66 @@ fn main() { invs.sort_by_key(|x| x.offset); - assert_eq!(&invs, &[ - Invariant { - offset: 0, - size: InvariantSize::U16, - valid_range_start: 1, - valid_range_end: 65535 - }, - Invariant { - offset: 2, - size: InvariantSize::U8, - valid_range_start: 0, - valid_range_end: 255 - }, - Invariant { - offset: 3, - size: InvariantSize::U8, - valid_range_start: 0, - valid_range_end: 1 - }, - ]); + if cfg!(disabled) { + assert_eq!(&invs, &[]); + } else if cfg!(normal) { + assert_eq!(&invs, &[ + Invariant { + offset: 0, + size: InvariantSize::U16, + valid_range_start: 1, + valid_range_end: 65535 + }, + Invariant { + offset: 3, + size: InvariantSize::U8, + valid_range_start: 0, + valid_range_end: 1 + }, + ]); + } else { + assert!(cfg!(sanitized)); + + assert_eq!(&invs, &[ + Invariant { + offset: 0, + size: InvariantSize::U16, + valid_range_start: 1, + valid_range_end: 65535 + }, + Invariant { + offset: 2, + size: InvariantSize::U8, + valid_range_start: 0, + valid_range_end: 255 + }, + Invariant { + offset: 3, + size: InvariantSize::U8, + valid_range_start: 0, + valid_range_end: 1 + }, + ]); + } + unsafe { let v = MyStruct { a: NonZeroU16::new(1).unwrap(), b: 2, c: true }; assert!(assert_validity_of(&v as *const _)); } - assert_eq!(OPTION_INVARIANTS, &[ - Invariant { - offset: 0, - size: InvariantSize::Pointer, - valid_range_start: 1, - valid_range_end: 0, - }, - ]); + if cfg!(sanitized) { + assert_eq!(OPTION_INVARIANTS, &[ + Invariant { + offset: 0, + size: InvariantSize::Pointer, + valid_range_start: 1, + valid_range_end: 0, + }, + ]); + } else { + assert_eq!(OPTION_INVARIANTS, &[]); + } unsafe { diff --git a/src/test/ui/sanitize/memory.rs b/src/test/ui/sanitize/memory.rs index b53f19a5b01aa..2da871a62fcb9 100644 --- a/src/test/ui/sanitize/memory.rs +++ b/src/test/ui/sanitize/memory.rs @@ -1,7 +1,7 @@ // needs-sanitizer-support // needs-sanitizer-memory // -// compile-flags: -Z sanitizer=memory -Zsanitizer-memory-track-origins -O +// compile-flags: -Z sanitizer=memory -Zno-validity-invariant-checks -Zsanitizer-memory-track-origins -O // // run-fail // error-pattern: MemorySanitizer: use-of-uninitialized-value @@ -10,6 +10,8 @@ // // This test case intentionally limits the usage of the std, // since it will be linked with an uninstrumented version of it. +// +// -Zno-validity-invariant-checks is needed in order to not fail inside the assume_init #![feature(core_intrinsics)] #![feature(start)] From 81bda49329811a9aa4c255c579df7423fc6a1ad6 Mon Sep 17 00:00:00 2001 From: 5225225 <5225225@mailbox.org> Date: Sun, 19 Jun 2022 00:02:01 +0100 Subject: [PATCH 12/12] Fix cranelift CustomSlice codegen --- compiler/rustc_codegen_cranelift/src/constant.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index ef72e6efb946b..0d2642299d825 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -251,6 +251,11 @@ pub(crate) fn codegen_const_value<'tcx>( .iconst(fx.pointer_type, i64::try_from(end.checked_sub(start).unwrap()).unwrap()); CValue::by_val_pair(ptr, len, layout) } + ConstValue::CustomSlice { data, length } => { + let ptr = pointer_for_allocation(fx, data).get_addr(fx); + let len = fx.bcx.ins().iconst(fx.pointer_type, i64::try_from(length).unwrap()); + CValue::by_val_pair(ptr, len, layout) + } } }