Skip to content

Commit f92ee9f

Browse files
committed
fix handling of unsized types in validation; validate str to be UTF-8
1 parent fdd962e commit f92ee9f

12 files changed

+399
-315
lines changed

src/librustc_mir/const_eval.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ use rustc::mir::interpret::{
2929
Scalar, AllocId, Allocation, ConstValue, AllocType,
3030
};
3131
use interpret::{self,
32-
Place, PlaceExtra, PlaceTy, MemPlace, OpTy, Operand, Value,
32+
Place, PlaceTy, MemPlace, OpTy, Operand, Value,
3333
EvalContext, StackPopCleanup, MemoryKind, Memory,
3434
};
3535

@@ -103,7 +103,7 @@ pub fn op_to_const<'tcx>(
103103
let val = match normalized_op {
104104
Err(MemPlace { ptr, align, extra }) => {
105105
// extract alloc-offset pair
106-
assert_eq!(extra, PlaceExtra::None);
106+
assert!(extra.is_none());
107107
let ptr = ptr.to_ptr()?;
108108
let alloc = ecx.memory.get(ptr.alloc_id)?;
109109
assert!(alloc.align.abi() >= align.abi());

src/librustc_mir/interpret/eval_context.rs

+83-79
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ use rustc::mir::interpret::{
3434
use syntax::source_map::{self, Span};
3535

3636
use super::{
37-
Value, Operand, MemPlace, MPlaceTy, Place, PlaceExtra,
37+
Value, Operand, MemPlace, MPlaceTy, Place,
3838
Memory, Machine
3939
};
4040

@@ -462,90 +462,94 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M
462462
}
463463

464464
/// Return the actual dynamic size and alignment of the place at the given type.
465-
/// Note that the value does not matter if the type is sized. For unsized types,
466-
/// the value has to be a fat pointer, and we only care about the "extra" data in it.
467-
pub fn size_and_align_of_mplace(
465+
/// Only the "extra" (metadata) part of the place matters.
466+
pub(super) fn size_and_align_of(
468467
&self,
469-
mplace: MPlaceTy<'tcx>,
468+
metadata: Option<Scalar>,
469+
layout: TyLayout<'tcx>,
470470
) -> EvalResult<'tcx, (Size, Align)> {
471-
if let PlaceExtra::None = mplace.extra {
472-
assert!(!mplace.layout.is_unsized());
473-
Ok(mplace.layout.size_and_align())
474-
} else {
475-
let layout = mplace.layout;
476-
assert!(layout.is_unsized());
477-
match layout.ty.sty {
478-
ty::Adt(..) | ty::Tuple(..) => {
479-
// First get the size of all statically known fields.
480-
// Don't use type_of::sizing_type_of because that expects t to be sized,
481-
// and it also rounds up to alignment, which we want to avoid,
482-
// as the unsized field's alignment could be smaller.
483-
assert!(!layout.ty.is_simd());
484-
debug!("DST layout: {:?}", layout);
485-
486-
let sized_size = layout.fields.offset(layout.fields.count() - 1);
487-
let sized_align = layout.align;
488-
debug!(
489-
"DST {} statically sized prefix size: {:?} align: {:?}",
490-
layout.ty,
491-
sized_size,
492-
sized_align
493-
);
494-
495-
// Recurse to get the size of the dynamically sized field (must be
496-
// the last field).
497-
let field = self.mplace_field(mplace, layout.fields.count() as u64 - 1)?;
498-
let (unsized_size, unsized_align) = self.size_and_align_of_mplace(field)?;
499-
500-
// FIXME (#26403, #27023): We should be adding padding
501-
// to `sized_size` (to accommodate the `unsized_align`
502-
// required of the unsized field that follows) before
503-
// summing it with `sized_size`. (Note that since #26403
504-
// is unfixed, we do not yet add the necessary padding
505-
// here. But this is where the add would go.)
506-
507-
// Return the sum of sizes and max of aligns.
508-
let size = sized_size + unsized_size;
509-
510-
// Choose max of two known alignments (combined value must
511-
// be aligned according to more restrictive of the two).
512-
let align = sized_align.max(unsized_align);
513-
514-
// Issue #27023: must add any necessary padding to `size`
515-
// (to make it a multiple of `align`) before returning it.
516-
//
517-
// Namely, the returned size should be, in C notation:
518-
//
519-
// `size + ((size & (align-1)) ? align : 0)`
520-
//
521-
// emulated via the semi-standard fast bit trick:
522-
//
523-
// `(size + (align-1)) & -align`
524-
525-
Ok((size.abi_align(align), align))
526-
}
527-
ty::Dynamic(..) => {
528-
let vtable = match mplace.extra {
529-
PlaceExtra::Vtable(vtable) => vtable,
530-
_ => bug!("Expected vtable"),
531-
};
532-
// the second entry in the vtable is the dynamic size of the object.
533-
self.read_size_and_align_from_vtable(vtable)
534-
}
535-
536-
ty::Slice(_) | ty::Str => {
537-
let len = match mplace.extra {
538-
PlaceExtra::Length(len) => len,
539-
_ => bug!("Expected length"),
540-
};
541-
let (elem_size, align) = layout.field(self, 0)?.size_and_align();
542-
Ok((elem_size * len, align))
543-
}
471+
let metadata = match metadata {
472+
None => {
473+
assert!(!layout.is_unsized());
474+
return Ok(layout.size_and_align())
475+
}
476+
Some(metadata) => {
477+
assert!(layout.is_unsized());
478+
metadata
479+
}
480+
};
481+
match layout.ty.sty {
482+
ty::Adt(..) | ty::Tuple(..) => {
483+
// First get the size of all statically known fields.
484+
// Don't use type_of::sizing_type_of because that expects t to be sized,
485+
// and it also rounds up to alignment, which we want to avoid,
486+
// as the unsized field's alignment could be smaller.
487+
assert!(!layout.ty.is_simd());
488+
debug!("DST layout: {:?}", layout);
489+
490+
let sized_size = layout.fields.offset(layout.fields.count() - 1);
491+
let sized_align = layout.align;
492+
debug!(
493+
"DST {} statically sized prefix size: {:?} align: {:?}",
494+
layout.ty,
495+
sized_size,
496+
sized_align
497+
);
498+
499+
// Recurse to get the size of the dynamically sized field (must be
500+
// the last field).
501+
let field = layout.field(self, layout.fields.count() - 1)?;
502+
let (unsized_size, unsized_align) = self.size_and_align_of(Some(metadata), field)?;
503+
504+
// FIXME (#26403, #27023): We should be adding padding
505+
// to `sized_size` (to accommodate the `unsized_align`
506+
// required of the unsized field that follows) before
507+
// summing it with `sized_size`. (Note that since #26403
508+
// is unfixed, we do not yet add the necessary padding
509+
// here. But this is where the add would go.)
510+
511+
// Return the sum of sizes and max of aligns.
512+
let size = sized_size + unsized_size;
513+
514+
// Choose max of two known alignments (combined value must
515+
// be aligned according to more restrictive of the two).
516+
let align = sized_align.max(unsized_align);
517+
518+
// Issue #27023: must add any necessary padding to `size`
519+
// (to make it a multiple of `align`) before returning it.
520+
//
521+
// Namely, the returned size should be, in C notation:
522+
//
523+
// `size + ((size & (align-1)) ? align : 0)`
524+
//
525+
// emulated via the semi-standard fast bit trick:
526+
//
527+
// `(size + (align-1)) & -align`
528+
529+
Ok((size.abi_align(align), align))
530+
}
531+
ty::Dynamic(..) => {
532+
let vtable = metadata.to_ptr()?;
533+
// the second entry in the vtable is the dynamic size of the object.
534+
self.read_size_and_align_from_vtable(vtable)
535+
}
544536

545-
_ => bug!("size_of_val::<{:?}> not supported", layout.ty),
537+
ty::Slice(_) | ty::Str => {
538+
let len = metadata.to_usize(self)?;
539+
let (elem_size, align) = layout.field(self, 0)?.size_and_align();
540+
Ok((elem_size * len, align))
546541
}
542+
543+
_ => bug!("size_and_align_of::<{:?}> not supported", layout.ty),
547544
}
548545
}
546+
#[inline]
547+
pub fn size_and_align_of_mplace(
548+
&self,
549+
mplace: MPlaceTy<'tcx>
550+
) -> EvalResult<'tcx, (Size, Align)> {
551+
self.size_and_align_of(mplace.extra, mplace.layout)
552+
}
549553

550554
pub fn push_stack_frame(
551555
&mut self,

src/librustc_mir/interpret/intrinsics.rs

+8-4
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
142142
self.mplace_field(place, 3)?,
143143
);
144144

145-
let msg = Symbol::intern(self.read_str(msg.into())?);
146-
let file = Symbol::intern(self.read_str(file.into())?);
145+
let msg_place = self.ref_to_mplace(self.read_value(msg.into())?)?;
146+
let msg = Symbol::intern(self.read_str(msg_place)?);
147+
let file_place = self.ref_to_mplace(self.read_value(file.into())?)?;
148+
let file = Symbol::intern(self.read_str(file_place)?);
147149
let line = self.read_scalar(line.into())?.to_u32()?;
148150
let col = self.read_scalar(col.into())?.to_u32()?;
149151
return Err(EvalErrorKind::Panic { msg, file, line, col }.into());
@@ -159,8 +161,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
159161
self.mplace_field(place, 2)?,
160162
);
161163

162-
let msg = Symbol::intern(self.read_str(msg)?);
163-
let file = Symbol::intern(self.read_str(file.into())?);
164+
let msg_place = self.ref_to_mplace(self.read_value(msg.into())?)?;
165+
let msg = Symbol::intern(self.read_str(msg_place)?);
166+
let file_place = self.ref_to_mplace(self.read_value(file.into())?)?;
167+
let file = Symbol::intern(self.read_str(file_place)?);
164168
let line = self.read_scalar(line.into())?.to_u32()?;
165169
let col = self.read_scalar(col.into())?.to_u32()?;
166170
return Err(EvalErrorKind::Panic { msg, file, line, col }.into());

src/librustc_mir/interpret/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ pub use self::eval_context::{
2727
EvalContext, Frame, StackPopCleanup, LocalValue,
2828
};
2929

30-
pub use self::place::{Place, PlaceExtra, PlaceTy, MemPlace, MPlaceTy};
30+
pub use self::place::{Place, PlaceTy, MemPlace, MPlaceTy};
3131

3232
pub use self::memory::{Memory, MemoryKind};
3333

src/librustc_mir/interpret/operand.rs

+19-31
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use rustc_data_structures::indexed_vec::Idx;
2121
use rustc::mir::interpret::{
2222
GlobalId, ConstValue, Scalar, EvalResult, Pointer, ScalarMaybeUndef, EvalErrorKind
2323
};
24-
use super::{EvalContext, Machine, MemPlace, MPlaceTy, PlaceExtra, MemoryKind};
24+
use super::{EvalContext, Machine, MemPlace, MPlaceTy, MemoryKind};
2525

2626
/// A `Value` represents a single immediate self-contained Rust value.
2727
///
@@ -65,6 +65,14 @@ impl<'tcx> Value {
6565
self.to_scalar_or_undef().not_undef()
6666
}
6767

68+
#[inline]
69+
pub fn to_scalar_pair(self) -> EvalResult<'tcx, (Scalar, Scalar)> {
70+
match self {
71+
Value::Scalar(..) => bug!("Got a thin pointer where a scalar pair was expected"),
72+
Value::ScalarPair(a, b) => Ok((a.not_undef()?, b.not_undef()?))
73+
}
74+
}
75+
6876
/// Convert the value into a pointer (or a pointer-sized integer).
6977
/// Throws away the second half of a ScalarPair!
7078
#[inline]
@@ -74,24 +82,6 @@ impl<'tcx> Value {
7482
Value::ScalarPair(ptr, _) => ptr.not_undef(),
7583
}
7684
}
77-
78-
pub fn to_scalar_dyn_trait(self) -> EvalResult<'tcx, (Scalar, Pointer)> {
79-
match self {
80-
Value::ScalarPair(ptr, vtable) =>
81-
Ok((ptr.not_undef()?, vtable.to_ptr()?)),
82-
_ => bug!("expected ptr and vtable, got {:?}", self),
83-
}
84-
}
85-
86-
pub fn to_scalar_slice(self, cx: impl HasDataLayout) -> EvalResult<'tcx, (Scalar, u64)> {
87-
match self {
88-
Value::ScalarPair(ptr, val) => {
89-
let len = val.to_bits(cx.data_layout().pointer_size)?;
90-
Ok((ptr.not_undef()?, len as u64))
91-
}
92-
_ => bug!("expected ptr and length, got {:?}", self),
93-
}
94-
}
9585
}
9686

9787
// ScalarPair needs a type to interpret, so we often have a value and a type together
@@ -242,7 +232,9 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
242232
&self,
243233
mplace: MPlaceTy<'tcx>,
244234
) -> EvalResult<'tcx, Option<Value>> {
245-
if mplace.extra != PlaceExtra::None {
235+
debug_assert_eq!(mplace.extra.is_some(), mplace.layout.is_unsized());
236+
if mplace.extra.is_some() {
237+
// Dont touch unsized
246238
return Ok(None);
247239
}
248240
let (ptr, ptr_align) = mplace.to_scalar_ptr_align();
@@ -315,20 +307,16 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
315307
}
316308
}
317309

318-
// operand must be a &str or compatible layout
310+
// Turn the MPlace into a string (must already be dereferenced!)
319311
pub fn read_str(
320312
&self,
321-
op: OpTy<'tcx>,
313+
mplace: MPlaceTy<'tcx>,
322314
) -> EvalResult<'tcx, &str> {
323-
let val = self.read_value(op)?;
324-
if let Value::ScalarPair(ptr, len) = *val {
325-
let len = len.not_undef()?.to_bits(self.memory.pointer_size())?;
326-
let bytes = self.memory.read_bytes(ptr.not_undef()?, Size::from_bytes(len as u64))?;
327-
let str = ::std::str::from_utf8(bytes).map_err(|err| EvalErrorKind::ValidationFailure(err.to_string()))?;
328-
Ok(str)
329-
} else {
330-
bug!("read_str: not a str")
331-
}
315+
let len = mplace.len(self)?;
316+
let bytes = self.memory.read_bytes(mplace.ptr, Size::from_bytes(len as u64))?;
317+
let str = ::std::str::from_utf8(bytes)
318+
.map_err(|err| EvalErrorKind::ValidationFailure(err.to_string()))?;
319+
Ok(str)
332320
}
333321

334322
pub fn uninit_operand(&mut self, layout: TyLayout<'tcx>) -> EvalResult<'tcx, Operand> {

0 commit comments

Comments
 (0)