Skip to content

Commit

Permalink
fix Miri discriminant load/store when overflows are involved
Browse files Browse the repository at this point in the history
  • Loading branch information
RalfJung committed Sep 16, 2019
1 parent 3e3e06d commit 8c0f601
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 17 deletions.
40 changes: 34 additions & 6 deletions src/librustc_mir/interpret/operand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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);
Expand Down
42 changes: 31 additions & 11 deletions src/librustc_mir/interpret/place.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down Expand Up @@ -1027,7 +1029,7 @@ where
}
layout::Variants::Multiple {
discr_kind: layout::DiscriminantKind::Tag,
ref discr,
discr: ref discr_layout,
discr_index,
..
} => {
Expand All @@ -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)?;
Expand All @@ -1050,22 +1052,40 @@ where
ref niche_variants,
niche_start,
},
discr: ref discr_layout,
discr_index,
..
} => {
assert!(
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)?;
}
}
}
Expand Down

0 comments on commit 8c0f601

Please sign in to comment.