From 8c0f6011057b5b2f7bd4d74ce63caf8d6630c39e Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 17 Aug 2019 12:57:44 +0200 Subject: [PATCH] fix Miri discriminant load/store when overflows are involved --- src/librustc_mir/interpret/operand.rs | 40 +++++++++++++++++++++---- src/librustc_mir/interpret/place.rs | 42 ++++++++++++++++++++------- 2 files changed, 65 insertions(+), 17 deletions(-) diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs index 9ad1542905b68..3b07202f898ba 100644 --- a/src/librustc_mir/interpret/operand.rs +++ b/src/librustc_mir/interpret/operand.rs @@ -609,15 +609,20 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ) -> InterpResult<'tcx, (u128, VariantIdx)> { trace!("read_discriminant_value {:#?}", rval.layout); - let (discr_kind, discr_index) = match rval.layout.variants { + let (discr_layout, discr_kind, discr_index) = match rval.layout.variants { layout::Variants::Single { index } => { let discr_val = rval.layout.ty.discriminant_for_variant(*self.tcx, index).map_or( index.as_u32() as u128, |discr| discr.val); return Ok((discr_val, index)); } - layout::Variants::Multiple { ref discr_kind, discr_index, .. } => - (discr_kind, discr_index), + layout::Variants::Multiple { + discr: ref discr_layout, + ref discr_kind, + discr_index, + .. + } => + (discr_layout, discr_kind, discr_index), }; // read raw discriminant value @@ -634,7 +639,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { .map_err(|_| err_unsup!(InvalidDiscriminant(raw_discr.erase_tag())))?; let real_discr = if discr_val.layout.ty.is_signed() { // going from layout tag type to typeck discriminant type - // requires first sign extending with the layout discriminant + // requires first sign extending with the discriminant layout let sexted = sign_extend(bits_discr, discr_val.layout.size) as i128; // and then zeroing with the typeck discriminant type let discr_ty = rval.layout.ty @@ -682,8 +687,31 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { (dataful_variant.as_u32() as u128, dataful_variant) }, Ok(raw_discr) => { - let adjusted_discr = raw_discr.wrapping_sub(niche_start) - .wrapping_add(variants_start); + // FIXME: WTF, some discriminants don't have integer type. + use layout::Primitive; + let discr_layout = self.layout_of(match discr_layout.value { + Primitive::Int(int, signed) => int.to_ty(*self.tcx, signed), + Primitive::Pointer => self.tcx.types.usize, + Primitive::Float(..) => bug!("there are no float discriminants"), + })?; + let discr_val = ImmTy::from_uint(raw_discr, discr_layout); + // We need to use machine arithmetic. + let niche_start_val = ImmTy::from_uint(niche_start, discr_layout); + let variants_start_val = ImmTy::from_uint(variants_start, discr_layout); + let adjusted_discr = self.binary_op( + mir::BinOp::Sub, + discr_val, + niche_start_val, + )?; + let adjusted_discr = self.binary_op( + mir::BinOp::Add, + adjusted_discr, + variants_start_val, + )?; + let adjusted_discr = adjusted_discr + .to_scalar()? + .assert_bits(discr_val.layout.size); + // Check if this is in the range that indicates an actual discriminant. if variants_start <= adjusted_discr && adjusted_discr <= variants_end { let index = adjusted_discr as usize; assert_eq!(index as u128, adjusted_discr); diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs index 1a28548618206..0a23c1222b4bf 100644 --- a/src/librustc_mir/interpret/place.rs +++ b/src/librustc_mir/interpret/place.rs @@ -8,7 +8,9 @@ use std::hash::Hash; use rustc::mir; use rustc::mir::interpret::truncate; use rustc::ty::{self, Ty}; -use rustc::ty::layout::{self, Size, Align, LayoutOf, TyLayout, HasDataLayout, VariantIdx}; +use rustc::ty::layout::{ + self, Size, Align, LayoutOf, TyLayout, HasDataLayout, VariantIdx, IntegerExt +}; use rustc::ty::TypeFoldable; use super::{ @@ -1027,7 +1029,7 @@ where } layout::Variants::Multiple { discr_kind: layout::DiscriminantKind::Tag, - ref discr, + discr: ref discr_layout, discr_index, .. } => { @@ -1038,7 +1040,7 @@ where // raw discriminants for enums are isize or bigger during // their computation, but the in-memory tag is the smallest possible // representation - let size = discr.value.size(self); + let size = discr_layout.value.size(self); let discr_val = truncate(discr_val, size); let discr_dest = self.place_field(dest, discr_index as u64)?; @@ -1050,6 +1052,7 @@ where ref niche_variants, niche_start, }, + discr: ref discr_layout, discr_index, .. } => { @@ -1057,15 +1060,32 @@ where variant_index.as_usize() < dest.layout.ty.ty_adt_def().unwrap().variants.len(), ); if variant_index != dataful_variant { - let niche_dest = - self.place_field(dest, discr_index as u64)?; - let niche_value = variant_index.as_u32() - niche_variants.start().as_u32(); - let niche_value = (niche_value as u128) - .wrapping_add(niche_start); - self.write_scalar( - Scalar::from_uint(niche_value, niche_dest.layout.size), - niche_dest + // FIXME: WTF, some discriminants don't have integer type. + use layout::Primitive; + let discr_layout = self.layout_of(match discr_layout.value { + Primitive::Int(int, signed) => int.to_ty(*self.tcx, signed), + Primitive::Pointer => self.tcx.types.usize, + Primitive::Float(..) => bug!("there are no float discriminants"), + })?; + + // We need to use machine arithmetic. + let variants_start = niche_variants.start().as_u32(); + let variants_start_val = ImmTy::from_uint(variants_start, discr_layout); + let niche_start_val = ImmTy::from_uint(niche_start, discr_layout); + let variant_index_val = ImmTy::from_uint(variant_index.as_u32(), discr_layout); + let niche_val = self.binary_op( + mir::BinOp::Sub, + variant_index_val, + variants_start_val, + )?; + let niche_val = self.binary_op( + mir::BinOp::Add, + niche_val, + niche_start_val, )?; + // Write result. + let niche_dest = self.place_field(dest, discr_index as u64)?; + self.write_immediate(*niche_val, niche_dest)?; } } }