Skip to content

Commit 1e137a7

Browse files
committed
fix drop typing; use same machinery for validating (sanity checking) dyn trait ptrs and slices
1 parent 09b15e9 commit 1e137a7

File tree

6 files changed

+115
-63
lines changed

6 files changed

+115
-63
lines changed

src/librustc_mir/interpret/eval_context.rs

Lines changed: 32 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use rustc::ty::layout::{
1010
self, Size, Align, HasDataLayout, LayoutOf, TyLayout, Primitive
1111
};
1212
use rustc::ty::subst::{Subst, Substs};
13-
use rustc::ty::{self, Ty, TyCtxt};
13+
use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
1414
use rustc::ty::query::TyCtxtAt;
1515
use rustc_data_structures::fx::{FxHashSet, FxHasher};
1616
use rustc_data_structures::indexed_vec::IndexVec;
@@ -24,7 +24,7 @@ use syntax::source_map::{self, Span};
2424
use syntax::ast::Mutability;
2525

2626
use super::{
27-
Value, ValTy, Operand, MemPlace, MPlaceTy, Place,
27+
Value, ValTy, Operand, MemPlace, MPlaceTy, Place, PlaceExtra,
2828
Memory, Machine
2929
};
3030

@@ -442,10 +442,14 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M
442442
}
443443
}
444444

445-
pub fn monomorphize(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> Ty<'tcx> {
445+
pub fn monomorphize<T: TypeFoldable<'tcx> + Subst<'tcx>>(
446+
&self,
447+
t: T,
448+
substs: &'tcx Substs<'tcx>
449+
) -> T {
446450
// miri doesn't care about lifetimes, and will choke on some crazy ones
447451
// let's simply get rid of them
448-
let substituted = ty.subst(*self.tcx, substs);
452+
let substituted = t.subst(*self.tcx, substs);
449453
self.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), substituted)
450454
}
451455

@@ -844,35 +848,41 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M
844848
match dest.layout.ty.builtin_deref(false).map(|tam| &tam.ty.sty) {
845849
| Some(ty::TyStr)
846850
| Some(ty::TySlice(_)) => {
847-
// check the length
851+
// check the length (for nicer error messages)
848852
let len_mplace = self.mplace_field(dest, 1)?;
849853
let len = self.read_scalar(len_mplace.into())?;
850854
let len = match len.to_bits(len_mplace.layout.size) {
851855
Err(_) => return validation_failure!("length is not a valid integer", path),
852856
Ok(len) => len as u64,
853857
};
854-
// get the fat ptr
858+
// get the fat ptr, and recursively check it
855859
let ptr = self.ref_to_mplace(self.read_value(dest.into())?)?;
856-
let mut path = path.clone();
857-
self.dump_field_name(&mut path, dest.layout.ty, 0, variant).unwrap();
858-
// check all fields
859-
for i in 0..len {
860+
assert_eq!(ptr.extra, PlaceExtra::Length(len));
861+
let unpacked_ptr = self.unpack_unsized_mplace(ptr)?;
862+
if seen.insert(unpacked_ptr) {
860863
let mut path = path.clone();
861-
self.dump_field_name(&mut path, ptr.layout.ty, i as usize, 0).unwrap();
862-
let field = self.mplace_field(ptr, i)?;
863-
self.validate_mplace(field, path, seen, todo)?;
864+
self.dump_field_name(&mut path, dest.layout.ty, 0, 0).unwrap();
865+
todo.push((unpacked_ptr, path))
864866
}
865-
// FIXME: For a TyStr, check that this is valid UTF-8
866867
},
867868
Some(ty::TyDynamic(..)) => {
868-
let vtable_mplace = self.mplace_field(dest, 1)?;
869-
let vtable = self.read_scalar(vtable_mplace.into())?;
870-
if vtable.to_ptr().is_err() {
871-
return validation_failure!("vtable address is not a pointer", path);
869+
// check the vtable (for nicer error messages)
870+
let vtable = self.read_scalar(self.mplace_field(dest, 1)?.into())?;
871+
let vtable = match vtable.to_ptr() {
872+
Err(_) => return validation_failure!("vtable address is not a pointer", path),
873+
Ok(vtable) => vtable,
874+
};
875+
// get the fat ptr, and recursively check it
876+
let ptr = self.ref_to_mplace(self.read_value(dest.into())?)?;
877+
assert_eq!(ptr.extra, PlaceExtra::Vtable(vtable));
878+
let unpacked_ptr = self.unpack_unsized_mplace(ptr)?;
879+
if seen.insert(unpacked_ptr) {
880+
let mut path = path.clone();
881+
self.dump_field_name(&mut path, dest.layout.ty, 0, 0).unwrap();
882+
todo.push((unpacked_ptr, path))
872883
}
873-
// get the fat ptr
874-
let _ptr = self.ref_to_mplace(self.read_value(dest.into())?)?;
875-
// FIXME: What can we verify about this?
884+
// FIXME: More checks for the vtable... making sure it is exactly
885+
// the one one would expect for this type.
876886
},
877887
Some(ty) =>
878888
bug!("Unexpected fat pointer target type {:?}", ty),
@@ -884,6 +894,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M
884894
let field = self.mplace_field(dest, i as u64)?;
885895
self.validate_mplace(field, path, seen, todo)?;
886896
}
897+
// FIXME: For a TyStr, check that this is valid UTF-8.
887898
},
888899
}
889900

src/librustc_mir/interpret/place.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -721,4 +721,39 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
721721
};
722722
Ok(OpTy { op, layout: place.layout })
723723
}
724+
725+
/// Turn a place that is a dyn trait (i.e., PlaceExtra::Vtable and the appropriate layout)
726+
/// or a slice into the specific fixed-size place and layout that is given by the vtable/len.
727+
/// This "unpacks" the existential quantifier, so to speak.
728+
pub fn unpack_unsized_mplace(&self, mplace: MPlaceTy<'tcx>) -> EvalResult<'tcx, MPlaceTy<'tcx>> {
729+
trace!("Unpacking {:?} ({:?})", *mplace, mplace.layout.ty);
730+
let layout = match mplace.extra {
731+
PlaceExtra::Vtable(vtable) => {
732+
// the drop function signature
733+
let drop_instance = self.read_drop_type_from_vtable(vtable)?;
734+
trace!("Found drop fn: {:?}", drop_instance);
735+
let fn_sig = drop_instance.ty(*self.tcx).fn_sig(*self.tcx);
736+
let fn_sig = self.tcx.normalize_erasing_late_bound_regions(self.param_env, &fn_sig);
737+
// the drop function takes *mut T where T is the type being dropped, so get that
738+
let ty = fn_sig.inputs()[0].builtin_deref(true).unwrap().ty;
739+
let layout = self.layout_of(ty)?;
740+
// Sanity checks
741+
let (size, align) = self.read_size_and_align_from_vtable(vtable)?;
742+
assert_eq!(size, layout.size);
743+
assert_eq!(align.abi(), layout.align.abi()); // only ABI alignment is preserved
744+
// Done!
745+
layout
746+
},
747+
PlaceExtra::Length(len) => {
748+
let ty = self.tcx.mk_array(mplace.layout.field(self, 0)?.ty, len);
749+
self.layout_of(ty)?
750+
}
751+
PlaceExtra::None => bug!("Expected a fat pointer"),
752+
};
753+
trace!("Unpacked type: {:?}", layout.ty);
754+
Ok(MPlaceTy {
755+
mplace: MemPlace { extra: PlaceExtra::None, ..*mplace },
756+
layout
757+
})
758+
}
724759
}

src/librustc_mir/interpret/terminator/drop.rs

Lines changed: 20 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,45 @@
11
use rustc::mir::BasicBlock;
2-
use rustc::ty::{self, Ty, layout::LayoutOf};
2+
use rustc::ty::{self, layout::LayoutOf};
33
use syntax::source_map::Span;
44

55
use rustc::mir::interpret::{EvalResult};
6-
use interpret::{Machine, EvalContext, PlaceTy, Value, OpTy, Operand};
6+
use interpret::{Machine, EvalContext, PlaceTy, PlaceExtra, OpTy, Operand};
77

88
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
9-
pub(crate) fn drop_place(
9+
pub(crate) fn drop_in_place(
1010
&mut self,
1111
place: PlaceTy<'tcx>,
1212
instance: ty::Instance<'tcx>,
1313
span: Span,
1414
target: BasicBlock,
1515
) -> EvalResult<'tcx> {
16+
trace!("drop_in_place: {:?},\n {:?}, {:?}", *place, place.layout.ty, instance);
1617
// We take the address of the object. This may well be unaligned, which is fine for us here.
1718
// However, unaligned accesses will probably make the actual drop implementation fail -- a problem shared
1819
// by rustc.
19-
let val = self.force_allocation(place)?.to_ref(&self);
20-
self.drop(val, instance, place.layout.ty, span, target)
21-
}
22-
23-
fn drop(
24-
&mut self,
25-
arg: Value,
26-
instance: ty::Instance<'tcx>,
27-
ty: Ty<'tcx>,
28-
span: Span,
29-
target: BasicBlock,
30-
) -> EvalResult<'tcx> {
31-
trace!("drop: {:?},\n {:?}, {:?}", arg, ty.sty, instance.def);
20+
let place = self.force_allocation(place)?;
3221

33-
let (instance, arg) = match ty.sty {
22+
let (instance, place) = match place.layout.ty.sty {
3423
ty::TyDynamic(..) => {
35-
if let Value::ScalarPair(ptr, vtable) = arg {
36-
// Figure out the specific drop function to call, and just pass along
37-
// the thin part of the pointer.
38-
let instance = self.read_drop_type_from_vtable(vtable.to_ptr()?)?;
39-
trace!("Dropping via vtable: {:?}", instance.def);
40-
(instance, Value::Scalar(ptr))
41-
} else {
42-
bug!("expected fat ptr, got {:?}", arg);
43-
}
24+
// Dropping a trait object.
25+
let vtable = match place.extra {
26+
PlaceExtra::Vtable(vtable) => vtable,
27+
_ => bug!("Expected vtable when dropping {:#?}", place),
28+
};
29+
let place = self.unpack_unsized_mplace(place)?;
30+
let instance = self.read_drop_type_from_vtable(vtable)?;
31+
(instance, place)
4432
}
45-
_ => (instance, arg),
33+
_ => (instance, place),
4634
};
4735

48-
// the drop function expects a reference to the value
49-
let fn_sig = self.tcx.fn_sig(instance.def_id()).skip_binder().clone();
36+
let fn_sig = instance.ty(*self.tcx).fn_sig(*self.tcx);
37+
let fn_sig = self.tcx.normalize_erasing_late_bound_regions(self.param_env, &fn_sig);
38+
5039
let arg = OpTy {
51-
op: Operand::Immediate(arg),
52-
layout: self.layout_of(fn_sig.output())?,
40+
op: Operand::Immediate(place.to_ref(&self)),
41+
layout: self.layout_of(self.tcx.mk_mut_ptr(place.layout.ty))?,
5342
};
54-
trace!("Dropped type: {:?}", fn_sig.output());
5543

5644
// This should always be (), but getting it from the sig seems
5745
// easier than creating a layout of ().

src/librustc_mir/interpret/terminator/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
129129
trace!("TerminatorKind::drop: {:?}, type {}", location, ty);
130130

131131
let instance = ::monomorphize::resolve_drop_in_place(*self.tcx, ty);
132-
self.drop_place(
132+
self.drop_in_place(
133133
place,
134134
instance,
135135
terminator.source_info.span,

src/test/ui/union-ub-fat-ptr.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,16 +66,18 @@ trait Trait {}
6666

6767
// OK
6868
const A: &str = unsafe { SliceTransmute { repr: SliceRepr { ptr: &42, len: 1 } }.str};
69-
// should lint
69+
// bad
7070
const B: &str = unsafe { SliceTransmute { repr: SliceRepr { ptr: &42, len: 999 } }.str};
71+
//~^ ERROR this constant likely exhibits undefined behavior
7172
// bad
7273
const C: &str = unsafe { SliceTransmute { bad: BadSliceRepr { ptr: &42, len: &3 } }.str};
7374
//~^ ERROR this constant likely exhibits undefined behavior
7475

7576
// OK
7677
const A2: &[u8] = unsafe { SliceTransmute { repr: SliceRepr { ptr: &42, len: 1 } }.slice};
77-
// should lint
78+
// bad
7879
const B2: &[u8] = unsafe { SliceTransmute { repr: SliceRepr { ptr: &42, len: 999 } }.slice};
80+
//~^ ERROR this constant likely exhibits undefined behavior
7981
// bad
8082
const C2: &[u8] = unsafe { SliceTransmute { bad: BadSliceRepr { ptr: &42, len: &3 } }.slice};
8183
//~^ ERROR this constant likely exhibits undefined behavior

src/test/ui/union-ub-fat-ptr.stderr

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,59 @@
11
error[E0080]: this constant likely exhibits undefined behavior
2-
--> $DIR/union-ub-fat-ptr.rs:72:1
2+
--> $DIR/union-ub-fat-ptr.rs:70:1
3+
|
4+
LL | const B: &str = unsafe { SliceTransmute { repr: SliceRepr { ptr: &42, len: 999 } }.str};
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access at offset N, outside bounds of allocation N which has size N
6+
|
7+
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
8+
9+
error[E0080]: this constant likely exhibits undefined behavior
10+
--> $DIR/union-ub-fat-ptr.rs:73:1
311
|
412
LL | const C: &str = unsafe { SliceTransmute { bad: BadSliceRepr { ptr: &42, len: &3 } }.str};
513
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered length is not a valid integer
614
|
715
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
816

917
error[E0080]: this constant likely exhibits undefined behavior
10-
--> $DIR/union-ub-fat-ptr.rs:80:1
18+
--> $DIR/union-ub-fat-ptr.rs:79:1
19+
|
20+
LL | const B2: &[u8] = unsafe { SliceTransmute { repr: SliceRepr { ptr: &42, len: 999 } }.slice};
21+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access at offset N, outside bounds of allocation N which has size N
22+
|
23+
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
24+
25+
error[E0080]: this constant likely exhibits undefined behavior
26+
--> $DIR/union-ub-fat-ptr.rs:82:1
1127
|
1228
LL | const C2: &[u8] = unsafe { SliceTransmute { bad: BadSliceRepr { ptr: &42, len: &3 } }.slice};
1329
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered length is not a valid integer
1430
|
1531
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
1632

1733
error[E0080]: this constant likely exhibits undefined behavior
18-
--> $DIR/union-ub-fat-ptr.rs:84:1
34+
--> $DIR/union-ub-fat-ptr.rs:86:1
1935
|
2036
LL | const D: &Trait = unsafe { DynTransmute { repr: DynRepr { ptr: &92, vtable: &3 } }.rust};
2137
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ tried to access memory with alignment N, but alignment N is required
2238
|
2339
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
2440

2541
error[E0080]: this constant likely exhibits undefined behavior
26-
--> $DIR/union-ub-fat-ptr.rs:87:1
42+
--> $DIR/union-ub-fat-ptr.rs:89:1
2743
|
2844
LL | const E: &Trait = unsafe { DynTransmute { repr2: DynRepr2 { ptr: &92, vtable: &3 } }.rust};
29-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access at offset N, outside bounds of allocation N which has size N
45+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ a memory access tried to interpret some bytes as a pointer
3046
|
3147
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
3248

3349
error[E0080]: this constant likely exhibits undefined behavior
34-
--> $DIR/union-ub-fat-ptr.rs:90:1
50+
--> $DIR/union-ub-fat-ptr.rs:92:1
3551
|
3652
LL | const F: &Trait = unsafe { DynTransmute { bad: BadDynRepr { ptr: &92, vtable: 3 } }.rust};
3753
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered vtable address is not a pointer
3854
|
3955
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
4056

41-
error: aborting due to 5 previous errors
57+
error: aborting due to 7 previous errors
4258

4359
For more information about this error, try `rustc --explain E0080`.

0 commit comments

Comments
 (0)