Skip to content

Commit f5e8830

Browse files
committed
validity in non-const mode relies on ref_to_mplace checking bounds; (de)reference hooks work on places
1 parent 048900c commit f5e8830

File tree

4 files changed

+119
-121
lines changed

4 files changed

+119
-121
lines changed

src/librustc_mir/const_eval.rs

+3-25
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ use rustc::hir::{self, def_id::DefId};
2020
use rustc::hir::def::Def;
2121
use rustc::mir::interpret::{ConstEvalErr, ErrorHandled};
2222
use rustc::mir;
23-
use rustc::ty::{self, Ty, TyCtxt, Instance, query::TyCtxtAt};
24-
use rustc::ty::layout::{self, Size, LayoutOf, TyLayout};
23+
use rustc::ty::{self, TyCtxt, Instance, query::TyCtxtAt};
24+
use rustc::ty::layout::{self, LayoutOf, TyLayout};
2525
use rustc::ty::subst::Subst;
2626
use rustc::traits::Reveal;
2727
use rustc_data_structures::indexed_vec::IndexVec;
@@ -32,7 +32,7 @@ use syntax::ast::Mutability;
3232
use syntax::source_map::{Span, DUMMY_SP};
3333

3434
use interpret::{self,
35-
PlaceTy, MemPlace, OpTy, Operand, Value, Pointer, Scalar, ConstValue,
35+
PlaceTy, MemPlace, OpTy, Operand, Value, Scalar, ConstValue,
3636
EvalResult, EvalError, EvalErrorKind, GlobalId, EvalContext, StackPopCleanup,
3737
Allocation, AllocId, MemoryKind,
3838
snapshot, RefTracking,
@@ -465,28 +465,6 @@ impl<'a, 'mir, 'tcx> interpret::Machine<'a, 'mir, 'tcx>
465465
&ecx.stack[..],
466466
)
467467
}
468-
469-
#[inline(always)]
470-
fn tag_reference(
471-
_ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
472-
_ptr: Pointer<Self::PointerTag>,
473-
_pointee_ty: Ty<'tcx>,
474-
_pointee_size: Size,
475-
_borrow_kind: Option<hir::Mutability>,
476-
) -> EvalResult<'tcx, Self::PointerTag> {
477-
Ok(())
478-
}
479-
480-
#[inline(always)]
481-
fn tag_dereference(
482-
_ecx: &EvalContext<'a, 'mir, 'tcx, Self>,
483-
_ptr: Pointer<Self::PointerTag>,
484-
_pointee_ty: Ty<'tcx>,
485-
_pointee_size: Size,
486-
_borrow_kind: Option<hir::Mutability>,
487-
) -> EvalResult<'tcx, Self::PointerTag> {
488-
Ok(())
489-
}
490468
}
491469

492470
/// Project to a field of a (variant of a) const

src/librustc_mir/interpret/machine.rs

+21-15
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use rustc::ty::{self, Ty, layout::{Size, TyLayout}, query::TyCtxtAt};
2121

2222
use super::{
2323
Allocation, AllocId, EvalResult, Scalar,
24-
EvalContext, PlaceTy, OpTy, Pointer, MemoryKind,
24+
EvalContext, PlaceTy, OpTy, Pointer, MemPlace, MemoryKind,
2525
};
2626

2727
/// Classifying memory accesses
@@ -205,26 +205,32 @@ pub trait Machine<'a, 'mir, 'tcx>: Sized {
205205
}
206206

207207
/// Executed when evaluating the `&` operator: Creating a new reference.
208-
/// This has the chance to adjust the tag.
208+
/// This has the chance to adjust the tag. It should not change anything else!
209209
/// `mutability` can be `None` in case a raw ptr is being created.
210+
#[inline]
210211
fn tag_reference(
211-
ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
212-
ptr: Pointer<Self::PointerTag>,
213-
pointee_ty: Ty<'tcx>,
214-
pointee_size: Size,
215-
mutability: Option<hir::Mutability>,
216-
) -> EvalResult<'tcx, Self::PointerTag>;
212+
_ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
213+
place: MemPlace<Self::PointerTag>,
214+
_ty: Ty<'tcx>,
215+
_size: Size,
216+
_mutability: Option<hir::Mutability>,
217+
) -> EvalResult<'tcx, MemPlace<Self::PointerTag>> {
218+
Ok(place)
219+
}
217220

218221
/// Executed when evaluating the `*` operator: Following a reference.
219-
/// This has the change to adjust the tag.
222+
/// This has the change to adjust the tag. It should not change anything else!
220223
/// `mutability` can be `None` in case a raw ptr is being dereferenced.
224+
#[inline]
221225
fn tag_dereference(
222-
ecx: &EvalContext<'a, 'mir, 'tcx, Self>,
223-
ptr: Pointer<Self::PointerTag>,
224-
pointee_ty: Ty<'tcx>,
225-
pointee_size: Size,
226-
mutability: Option<hir::Mutability>,
227-
) -> EvalResult<'tcx, Self::PointerTag>;
226+
_ecx: &EvalContext<'a, 'mir, 'tcx, Self>,
227+
place: MemPlace<Self::PointerTag>,
228+
_ty: Ty<'tcx>,
229+
_size: Size,
230+
_mutability: Option<hir::Mutability>,
231+
) -> EvalResult<'tcx, MemPlace<Self::PointerTag>> {
232+
Ok(place)
233+
}
228234

229235
/// Execute a validation operation
230236
#[inline]

src/librustc_mir/interpret/place.rs

+35-35
Original file line numberDiff line numberDiff line change
@@ -276,26 +276,25 @@ where
276276

277277
let align = layout.align;
278278
let meta = val.to_meta()?;
279-
280-
let ptr = match val.to_scalar_ptr()? {
281-
Scalar::Ptr(ptr) if M::ENABLE_PTR_TRACKING_HOOKS => {
282-
// Machine might want to track the `*` operator
283-
let (size, _) = self.size_and_align_of(meta, layout)?
284-
.expect("ref_to_mplace cannot determine size");
285-
let mutbl = match val.layout.ty.sty {
286-
// `builtin_deref` considers boxes immutable, that's useless for our purposes
287-
ty::Ref(_, _, mutbl) => Some(mutbl),
288-
ty::Adt(def, _) if def.is_box() => Some(hir::MutMutable),
289-
ty::RawPtr(_) => None,
290-
_ => bug!("Unexpected pointer type {}", val.layout.ty.sty),
291-
};
292-
let tag = M::tag_dereference(self, ptr, pointee_type, size, mutbl)?;
293-
Scalar::Ptr(Pointer::new_with_tag(ptr.alloc_id, ptr.offset, tag))
294-
}
295-
other => other,
279+
let ptr = val.to_scalar_ptr()?;
280+
let mplace = MemPlace { ptr, align, meta };
281+
// Pointer tag tracking might want to adjust the tag.
282+
let mplace = if M::ENABLE_PTR_TRACKING_HOOKS {
283+
let (size, _) = self.size_and_align_of(meta, layout)?
284+
// for extern types, just cover what we can
285+
.unwrap_or_else(|| layout.size_and_align());
286+
let mutbl = match val.layout.ty.sty {
287+
// `builtin_deref` considers boxes immutable, that's useless for our purposes
288+
ty::Ref(_, _, mutbl) => Some(mutbl),
289+
ty::Adt(def, _) if def.is_box() => Some(hir::MutMutable),
290+
ty::RawPtr(_) => None,
291+
_ => bug!("Unexpected pointer type {}", val.layout.ty.sty),
292+
};
293+
M::tag_dereference(self, mplace, pointee_type, size, mutbl)?
294+
} else {
295+
mplace
296296
};
297-
298-
Ok(MPlaceTy { mplace: MemPlace { ptr, align, meta }, layout })
297+
Ok(MPlaceTy { mplace, layout })
299298
}
300299

301300
/// Turn a mplace into a (thin or fat) pointer, as a reference, pointing to the same space.
@@ -305,24 +304,25 @@ where
305304
place: MPlaceTy<'tcx, M::PointerTag>,
306305
borrow_kind: Option<mir::BorrowKind>,
307306
) -> EvalResult<'tcx, Value<M::PointerTag>> {
308-
let ptr = match place.ptr {
309-
Scalar::Ptr(ptr) if M::ENABLE_PTR_TRACKING_HOOKS => {
310-
// Machine might want to track the `&` operator
311-
let (size, _) = self.size_and_align_of_mplace(place)?
312-
.expect("create_ref cannot determine size");
313-
let mutbl = match borrow_kind {
314-
Some(mir::BorrowKind::Mut { .. }) => Some(hir::MutMutable),
315-
Some(_) => Some(hir::MutImmutable),
316-
None => None,
317-
};
318-
let tag = M::tag_reference(self, ptr, place.layout.ty, size, mutbl)?;
319-
Scalar::Ptr(Pointer::new_with_tag(ptr.alloc_id, ptr.offset, tag))
320-
},
321-
other => other,
307+
// Pointer tag tracking might want to adjust the tag
308+
let place = if M::ENABLE_PTR_TRACKING_HOOKS {
309+
let (size, _) = self.size_and_align_of_mplace(place)?
310+
// for extern types, just cover what we can
311+
.unwrap_or_else(|| place.layout.size_and_align());
312+
let mutbl = match borrow_kind {
313+
Some(mir::BorrowKind::Mut { .. }) |
314+
Some(mir::BorrowKind::Unique) =>
315+
Some(hir::MutMutable),
316+
Some(_) => Some(hir::MutImmutable),
317+
None => None,
318+
};
319+
M::tag_reference(self, *place, place.layout.ty, size, mutbl)?
320+
} else {
321+
*place
322322
};
323323
Ok(match place.meta {
324-
None => Value::Scalar(ptr.into()),
325-
Some(meta) => Value::ScalarPair(ptr.into(), meta.into()),
324+
None => Value::Scalar(place.ptr.into()),
325+
Some(meta) => Value::ScalarPair(place.ptr.into(), meta.into()),
326326
})
327327
}
328328

src/librustc_mir/interpret/validity.rs

+60-46
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use std::fmt::Write;
1212
use std::hash::Hash;
1313

1414
use syntax_pos::symbol::Symbol;
15-
use rustc::ty::layout::{self, Size, Align, TyLayout};
15+
use rustc::ty::layout::{self, Size, Align, TyLayout, LayoutOf};
1616
use rustc::ty;
1717
use rustc_data_structures::fx::FxHashSet;
1818
use rustc::mir::interpret::{
@@ -176,19 +176,27 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
176176
// undef. We should fix that, but let's start low.
177177
}
178178
}
179-
_ if ty.is_box() || ty.is_region_ptr() || ty.is_unsafe_ptr() => {
180-
// Handle fat pointers. We also check fat raw pointers,
181-
// their metadata must be valid!
182-
// This also checks that the ptr itself is initialized, which
183-
// seems reasonable even for raw pointers.
184-
let place = try_validation!(self.ref_to_mplace(value),
185-
"undefined data in pointer", path);
179+
ty::RawPtr(..) => {
180+
// No undef allowed here. Eventually this should be consistent with
181+
// the integer types.
182+
let _ptr = try_validation!(value.to_scalar_ptr(),
183+
"undefined address in pointer", path);
184+
let _meta = try_validation!(value.to_meta(),
185+
"uninitialized data in fat pointer metadata", path);
186+
}
187+
_ if ty.is_box() || ty.is_region_ptr() => {
188+
// Handle fat pointers.
186189
// Check metadata early, for better diagnostics
187-
if place.layout.is_unsized() {
188-
let tail = self.tcx.struct_tail(place.layout.ty);
190+
let ptr = try_validation!(value.to_scalar_ptr(),
191+
"undefined address in pointer", path);
192+
let meta = try_validation!(value.to_meta(),
193+
"uninitialized data in fat pointer metadata", path);
194+
let layout = self.layout_of(value.layout.ty.builtin_deref(true).unwrap().ty)?;
195+
if layout.is_unsized() {
196+
let tail = self.tcx.struct_tail(layout.ty);
189197
match tail.sty {
190198
ty::Dynamic(..) => {
191-
let vtable = try_validation!(place.meta.unwrap().to_ptr(),
199+
let vtable = try_validation!(meta.unwrap().to_ptr(),
192200
"non-pointer vtable in fat pointer", path);
193201
try_validation!(self.read_drop_type_from_vtable(vtable),
194202
"invalid drop fn in vtable", path);
@@ -197,7 +205,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
197205
// FIXME: More checks for the vtable.
198206
}
199207
ty::Slice(..) | ty::Str => {
200-
try_validation!(place.meta.unwrap().to_usize(self),
208+
try_validation!(meta.unwrap().to_usize(self),
201209
"non-integer slice length in fat pointer", path);
202210
}
203211
ty::Foreign(..) => {
@@ -207,59 +215,65 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
207215
bug!("Unexpected unsized type tail: {:?}", tail),
208216
}
209217
}
210-
// for safe ptrs, also check the ptr values itself
211-
if !ty.is_unsafe_ptr() {
212-
// Make sure this is non-NULL and aligned
213-
let (size, align) = self.size_and_align_of(place.meta, place.layout)?
214-
// for the purpose of validity, consider foreign types to have
215-
// alignment and size determined by the layout (size will be 0,
216-
// alignment should take attributes into account).
217-
.unwrap_or_else(|| place.layout.size_and_align());
218-
match self.memory.check_align(place.ptr, align) {
219-
Ok(_) => {},
220-
Err(err) => match err.kind {
218+
// Make sure this is non-NULL and aligned
219+
let (size, align) = self.size_and_align_of(meta, layout)?
220+
// for the purpose of validity, consider foreign types to have
221+
// alignment and size determined by the layout (size will be 0,
222+
// alignment should take attributes into account).
223+
.unwrap_or_else(|| layout.size_and_align());
224+
match self.memory.check_align(ptr, align) {
225+
Ok(_) => {},
226+
Err(err) => {
227+
error!("{:?} is not aligned to {:?}", ptr, align);
228+
match err.kind {
221229
EvalErrorKind::InvalidNullPointerUsage =>
222230
return validation_failure!("NULL reference", path),
223231
EvalErrorKind::AlignmentCheckFailed { .. } =>
224232
return validation_failure!("unaligned reference", path),
225233
_ =>
226234
return validation_failure!(
227235
"dangling (out-of-bounds) reference (might be NULL at \
228-
run-time)",
236+
run-time)",
229237
path
230238
),
231239
}
232240
}
233-
// non-ZST also have to be dereferenceable
241+
}
242+
// Turn ptr into place.
243+
// `ref_to_mplace` also calls the machine hook for (re)activating the tag,
244+
// which in turn will (in full miri) check if the pointer is dereferencable.
245+
let place = self.ref_to_mplace(value)?;
246+
// Recursive checking
247+
if let Some(ref_tracking) = ref_tracking {
248+
assert!(const_mode, "We should only do recursie checking in const mode");
234249
if size != Size::ZERO {
250+
// Non-ZST also have to be dereferencable
235251
let ptr = try_validation!(place.ptr.to_ptr(),
236252
"integer pointer in non-ZST reference", path);
237-
if const_mode {
238-
// Skip validation entirely for some external statics
239-
let alloc_kind = self.tcx.alloc_map.lock().get(ptr.alloc_id);
240-
if let Some(AllocType::Static(did)) = alloc_kind {
241-
// `extern static` cannot be validated as they have no body.
242-
// FIXME: Statics from other crates are also skipped.
243-
// They might be checked at a different type, but for now we
244-
// want to avoid recursing too deeply. This is not sound!
245-
if !did.is_local() || self.tcx.is_foreign_item(did) {
246-
return Ok(());
247-
}
253+
// Skip validation entirely for some external statics
254+
let alloc_kind = self.tcx.alloc_map.lock().get(ptr.alloc_id);
255+
if let Some(AllocType::Static(did)) = alloc_kind {
256+
// `extern static` cannot be validated as they have no body.
257+
// FIXME: Statics from other crates are also skipped.
258+
// They might be checked at a different type, but for now we
259+
// want to avoid recursing too deeply. This is not sound!
260+
if !did.is_local() || self.tcx.is_foreign_item(did) {
261+
return Ok(());
248262
}
249263
}
264+
// Maintain the invariant that the place we are checking is
265+
// already verified to be in-bounds.
250266
try_validation!(self.memory.check_bounds(ptr, size, false),
251267
"dangling (not entirely in bounds) reference", path);
252268
}
253-
if let Some(ref_tracking) = ref_tracking {
254-
// Check if we have encountered this pointer+layout combination
255-
// before. Proceed recursively even for integer pointers, no
256-
// reason to skip them! They are (recursively) valid for some ZST,
257-
// but not for others (e.g. `!` is a ZST).
258-
let op = place.into();
259-
if ref_tracking.seen.insert(op) {
260-
trace!("Recursing below ptr {:#?}", *op);
261-
ref_tracking.todo.push((op, path_clone_and_deref(path)));
262-
}
269+
// Check if we have encountered this pointer+layout combination
270+
// before. Proceed recursively even for integer pointers, no
271+
// reason to skip them! They are (recursively) valid for some ZST,
272+
// but not for others (e.g. `!` is a ZST).
273+
let op = place.into();
274+
if ref_tracking.seen.insert(op) {
275+
trace!("Recursing below ptr {:#?}", *op);
276+
ref_tracking.todo.push((op, path_clone_and_deref(path)));
263277
}
264278
}
265279
}

0 commit comments

Comments
 (0)