Skip to content

Commit 867104a

Browse files
authored
Rollup merge of #69762 - RalfJung:validity-errors, r=oli-obk
Ensure that validity only raises validity errors For now, only as a debug-assertion (similar to const-prop detecting errors that allocate). Now includes #69646. [Relative diff](RalfJung/rust@layout-visitor...RalfJung:validity-errors). r? @oli-obk
2 parents 8301e32 + ed3014a commit 867104a

File tree

13 files changed

+167
-94
lines changed

13 files changed

+167
-94
lines changed

src/librustc/mir/interpret/error.rs

+23-4
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ fn print_backtrace(backtrace: &mut Backtrace) {
245245
eprintln!("\n\nAn error occurred in miri:\n{:?}", backtrace);
246246
}
247247

248-
impl From<ErrorHandled> for InterpErrorInfo<'tcx> {
248+
impl From<ErrorHandled> for InterpErrorInfo<'_> {
249249
fn from(err: ErrorHandled) -> Self {
250250
match err {
251251
ErrorHandled::Reported => err_inval!(ReferencedConstant),
@@ -291,7 +291,7 @@ pub enum InvalidProgramInfo<'tcx> {
291291
Layout(layout::LayoutError<'tcx>),
292292
}
293293

294-
impl fmt::Debug for InvalidProgramInfo<'tcx> {
294+
impl fmt::Debug for InvalidProgramInfo<'_> {
295295
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
296296
use InvalidProgramInfo::*;
297297
match self {
@@ -321,6 +321,8 @@ pub enum UndefinedBehaviorInfo {
321321
RemainderByZero,
322322
/// Overflowing inbounds pointer arithmetic.
323323
PointerArithOverflow,
324+
/// Invalid metadata in a wide pointer (using `str` to avoid allocations).
325+
InvalidMeta(&'static str),
324326
}
325327

326328
impl fmt::Debug for UndefinedBehaviorInfo {
@@ -338,6 +340,7 @@ impl fmt::Debug for UndefinedBehaviorInfo {
338340
DivisionByZero => write!(f, "dividing by zero"),
339341
RemainderByZero => write!(f, "calculating the remainder with a divisor of zero"),
340342
PointerArithOverflow => write!(f, "overflowing in-bounds pointer arithmetic"),
343+
InvalidMeta(msg) => write!(f, "invalid metadata in wide pointer: {}", msg),
341344
}
342345
}
343346
}
@@ -354,8 +357,8 @@ pub enum UnsupportedOpInfo<'tcx> {
354357
Unsupported(String),
355358

356359
/// When const-prop encounters a situation it does not support, it raises this error.
357-
/// This must not allocate for performance reasons.
358-
ConstPropUnsupported(&'tcx str),
360+
/// This must not allocate for performance reasons (hence `str`, not `String`).
361+
ConstPropUnsupported(&'static str),
359362

360363
// -- Everything below is not categorized yet --
361364
FunctionAbiMismatch(Abi, Abi),
@@ -612,3 +615,19 @@ impl fmt::Debug for InterpError<'_> {
612615
}
613616
}
614617
}
618+
619+
impl InterpError<'_> {
620+
/// Some errors allocate to be created as they contain free-form strings.
621+
/// And sometimes we want to be sure that did not happen as it is a
622+
/// waste of resources.
623+
pub fn allocates(&self) -> bool {
624+
match self {
625+
InterpError::MachineStop(_)
626+
| InterpError::Unsupported(UnsupportedOpInfo::Unsupported(_))
627+
| InterpError::Unsupported(UnsupportedOpInfo::ValidationFailure(_))
628+
| InterpError::UndefinedBehavior(UndefinedBehaviorInfo::Ub(_))
629+
| InterpError::UndefinedBehavior(UndefinedBehaviorInfo::UbExperimental(_)) => true,
630+
_ => false,
631+
}
632+
}
633+
}

src/librustc_mir/const_eval/eval_queries.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,12 @@ fn validate_and_turn_into_const<'tcx>(
186186
if cid.promoted.is_none() {
187187
let mut ref_tracking = RefTracking::new(mplace);
188188
while let Some((mplace, path)) = ref_tracking.todo.pop() {
189-
ecx.validate_operand(mplace.into(), path, Some(&mut ref_tracking))?;
189+
ecx.const_validate_operand(
190+
mplace.into(),
191+
path,
192+
&mut ref_tracking,
193+
/*may_ref_to_static*/ is_static,
194+
)?;
190195
}
191196
}
192197
// Now that we validated, turn this into a proper constant.

src/librustc_mir/interpret/eval_context.rs

+3-9
Original file line numberDiff line numberDiff line change
@@ -457,10 +457,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
457457

458458
// Check if this brought us over the size limit.
459459
if size.bytes() >= self.tcx.data_layout().obj_size_bound() {
460-
throw_ub_format!(
461-
"wide pointer metadata contains invalid information: \
462-
total size is bigger than largest supported object"
463-
);
460+
throw_ub!(InvalidMeta("total size is bigger than largest supported object"));
464461
}
465462
Ok(Some((size, align)))
466463
}
@@ -476,10 +473,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
476473

477474
// Make sure the slice is not too big.
478475
let size = elem.size.checked_mul(len, &*self.tcx).ok_or_else(|| {
479-
err_ub_format!(
480-
"invalid slice: \
481-
total size is bigger than largest supported object"
482-
)
476+
err_ub!(InvalidMeta("slice is bigger than largest supported object"))
483477
})?;
484478
Ok(Some((size, elem.align.abi)))
485479
}
@@ -685,7 +679,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
685679
// invariant -- that is, unless a function somehow has a ptr to
686680
// its return place... but the way MIR is currently generated, the
687681
// return place is always a local and then this cannot happen.
688-
self.validate_operand(self.place_to_op(return_place)?, vec![], None)?;
682+
self.validate_operand(self.place_to_op(return_place)?)?;
689683
}
690684
} else {
691685
// Uh, that shouldn't happen... the function did not intend to return

src/librustc_mir/interpret/place.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -689,7 +689,7 @@ where
689689

690690
if M::enforce_validity(self) {
691691
// Data got changed, better make sure it matches the type!
692-
self.validate_operand(self.place_to_op(dest)?, vec![], None)?;
692+
self.validate_operand(self.place_to_op(dest)?)?;
693693
}
694694

695695
Ok(())
@@ -706,7 +706,7 @@ where
706706

707707
if M::enforce_validity(self) {
708708
// Data got changed, better make sure it matches the type!
709-
self.validate_operand(dest.into(), vec![], None)?;
709+
self.validate_operand(dest.into())?;
710710
}
711711

712712
Ok(())
@@ -843,7 +843,7 @@ where
843843

844844
if M::enforce_validity(self) {
845845
// Data got changed, better make sure it matches the type!
846-
self.validate_operand(self.place_to_op(dest)?, vec![], None)?;
846+
self.validate_operand(self.place_to_op(dest)?)?;
847847
}
848848

849849
Ok(())
@@ -951,7 +951,7 @@ where
951951

952952
if M::enforce_validity(self) {
953953
// Data got changed, better make sure it matches the type!
954-
self.validate_operand(dest.into(), vec![], None)?;
954+
self.validate_operand(dest.into())?;
955955
}
956956

957957
Ok(())

src/librustc_mir/interpret/validity.rs

+71-20
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,17 @@ macro_rules! try_validation {
4646
($e:expr, $what:expr, $where:expr, $details:expr) => {{
4747
match $e {
4848
Ok(x) => x,
49+
// We re-throw the error, so we are okay with allocation:
50+
// this can only slow down builds that fail anyway.
4951
Err(_) => throw_validation_failure!($what, $where, $details),
5052
}
5153
}};
5254

5355
($e:expr, $what:expr, $where:expr) => {{
5456
match $e {
5557
Ok(x) => x,
58+
// We re-throw the error, so we are okay with allocation:
59+
// this can only slow down builds that fail anyway.
5660
Err(_) => throw_validation_failure!($what, $where),
5761
}
5862
}};
@@ -167,6 +171,7 @@ struct ValidityVisitor<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> {
167171
path: Vec<PathElem>,
168172
ref_tracking_for_consts:
169173
Option<&'rt mut RefTracking<MPlaceTy<'tcx, M::PointerTag>, Vec<PathElem>>>,
174+
may_ref_to_static: bool,
170175
ecx: &'rt InterpCx<'mir, 'tcx, M>,
171176
}
172177

@@ -320,9 +325,17 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, 'tcx, M
320325
self.check_wide_ptr_meta(place.meta, place.layout)?;
321326
}
322327
// Make sure this is dereferenceable and all.
323-
let (size, align) = self
324-
.ecx
325-
.size_and_align_of(place.meta, place.layout)?
328+
let size_and_align = match self.ecx.size_and_align_of(place.meta, place.layout) {
329+
Ok(res) => res,
330+
Err(err) => match err.kind {
331+
err_ub!(InvalidMeta(msg)) => throw_validation_failure!(
332+
format_args!("invalid {} metadata: {}", kind, msg),
333+
self.path
334+
),
335+
_ => bug!("Unexpected error during ptr size_and_align_of: {}", err),
336+
},
337+
};
338+
let (size, align) = size_and_align
326339
// for the purpose of validity, consider foreign types to have
327340
// alignment and size determined by the layout (size will be 0,
328341
// alignment should take attributes into account).
@@ -359,10 +372,13 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, 'tcx, M
359372
format_args!("a dangling {} (created from integer)", kind),
360373
self.path
361374
),
362-
_ => throw_validation_failure!(
363-
format_args!("a dangling {} (not entirely in bounds)", kind),
364-
self.path
365-
),
375+
err_unsup!(PointerOutOfBounds { .. }) | err_unsup!(DanglingPointerDeref) => {
376+
throw_validation_failure!(
377+
format_args!("a dangling {} (not entirely in bounds)", kind),
378+
self.path
379+
)
380+
}
381+
_ => bug!("Unexpected error during ptr inbounds test: {}", err),
366382
}
367383
}
368384
};
@@ -380,6 +396,12 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, 'tcx, M
380396
if !did.is_local() || self.ecx.tcx.is_foreign_item(did) {
381397
return Ok(());
382398
}
399+
if !self.may_ref_to_static && self.ecx.tcx.is_static(did) {
400+
throw_validation_failure!(
401+
format_args!("a {} pointing to a static variable", kind),
402+
self.path
403+
);
404+
}
383405
}
384406
}
385407
// Proceed recursively even for ZST, no reason to skip them!
@@ -638,6 +660,7 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
638660
err_unsup!(ReadPointerAsBytes) => {
639661
throw_validation_failure!("a pointer", self.path, "plain (non-pointer) bytes")
640662
}
663+
// Propagate upwards (that will also check for unexpected errors).
641664
_ => return Err(err),
642665
},
643666
}
@@ -773,31 +796,59 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
773796
}
774797

775798
impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
776-
/// This function checks the data at `op`. `op` is assumed to cover valid memory if it
777-
/// is an indirect operand.
778-
/// It will error if the bits at the destination do not match the ones described by the layout.
779-
///
780-
/// `ref_tracking_for_consts` can be `None` to avoid recursive checking below references.
781-
/// This also toggles between "run-time" (no recursion) and "compile-time" (with recursion)
782-
/// validation (e.g., pointer values are fine in integers at runtime) and various other const
783-
/// specific validation checks.
784-
pub fn validate_operand(
799+
fn validate_operand_internal(
785800
&self,
786801
op: OpTy<'tcx, M::PointerTag>,
787802
path: Vec<PathElem>,
788803
ref_tracking_for_consts: Option<
789804
&mut RefTracking<MPlaceTy<'tcx, M::PointerTag>, Vec<PathElem>>,
790805
>,
806+
may_ref_to_static: bool,
791807
) -> InterpResult<'tcx> {
792-
trace!("validate_operand: {:?}, {:?}", *op, op.layout.ty);
808+
trace!("validate_operand_internal: {:?}, {:?}", *op, op.layout.ty);
793809

794810
// Construct a visitor
795-
let mut visitor = ValidityVisitor { path, ref_tracking_for_consts, ecx: self };
811+
let mut visitor =
812+
ValidityVisitor { path, ref_tracking_for_consts, may_ref_to_static, ecx: self };
796813

797814
// Try to cast to ptr *once* instead of all the time.
798815
let op = self.force_op_ptr(op).unwrap_or(op);
799816

800-
// Run it
801-
visitor.visit_value(op)
817+
// Run it.
818+
match visitor.visit_value(op) {
819+
Ok(()) => Ok(()),
820+
Err(err) if matches!(err.kind, err_unsup!(ValidationFailure { .. })) => Err(err),
821+
Err(err) if cfg!(debug_assertions) => {
822+
bug!("Unexpected error during validation: {}", err)
823+
}
824+
Err(err) => Err(err),
825+
}
826+
}
827+
828+
/// This function checks the data at `op` to be const-valid.
829+
/// `op` is assumed to cover valid memory if it is an indirect operand.
830+
/// It will error if the bits at the destination do not match the ones described by the layout.
831+
///
832+
/// `ref_tracking` is used to record references that we encounter so that they
833+
/// can be checked recursively by an outside driving loop.
834+
///
835+
/// `may_ref_to_static` controls whether references are allowed to point to statics.
836+
#[inline(always)]
837+
pub fn const_validate_operand(
838+
&self,
839+
op: OpTy<'tcx, M::PointerTag>,
840+
path: Vec<PathElem>,
841+
ref_tracking: &mut RefTracking<MPlaceTy<'tcx, M::PointerTag>, Vec<PathElem>>,
842+
may_ref_to_static: bool,
843+
) -> InterpResult<'tcx> {
844+
self.validate_operand_internal(op, path, Some(ref_tracking), may_ref_to_static)
845+
}
846+
847+
/// This function checks the data at `op` to be runtime-valid.
848+
/// `op` is assumed to cover valid memory if it is an indirect operand.
849+
/// It will error if the bits at the destination do not match the ones described by the layout.
850+
#[inline(always)]
851+
pub fn validate_operand(&self, op: OpTy<'tcx, M::PointerTag>) -> InterpResult<'tcx> {
852+
self.validate_operand_internal(op, vec![], None, false)
802853
}
803854
}

src/librustc_mir/transform/const_prop.rs

+12-28
Original file line numberDiff line numberDiff line change
@@ -404,32 +404,15 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
404404
let r = match f(self) {
405405
Ok(val) => Some(val),
406406
Err(error) => {
407-
use rustc::mir::interpret::{
408-
InterpError::*, UndefinedBehaviorInfo, UnsupportedOpInfo,
409-
};
410-
match error.kind {
411-
MachineStop(_) => bug!("ConstProp does not stop"),
412-
413-
// Some error shouldn't come up because creating them causes
414-
// an allocation, which we should avoid. When that happens,
415-
// dedicated error variants should be introduced instead.
416-
// Only test this in debug builds though to avoid disruptions.
417-
Unsupported(UnsupportedOpInfo::Unsupported(_))
418-
| Unsupported(UnsupportedOpInfo::ValidationFailure(_))
419-
| UndefinedBehavior(UndefinedBehaviorInfo::Ub(_))
420-
| UndefinedBehavior(UndefinedBehaviorInfo::UbExperimental(_))
421-
if cfg!(debug_assertions) =>
422-
{
423-
bug!("const-prop encountered allocating error: {:?}", error.kind);
424-
}
425-
426-
Unsupported(_)
427-
| UndefinedBehavior(_)
428-
| InvalidProgram(_)
429-
| ResourceExhaustion(_) => {
430-
// Ignore these errors.
431-
}
432-
}
407+
// Some errors shouldn't come up because creating them causes
408+
// an allocation, which we should avoid. When that happens,
409+
// dedicated error variants should be introduced instead.
410+
// Only test this in debug builds though to avoid disruptions.
411+
debug_assert!(
412+
!error.kind.allocates(),
413+
"const-prop encountered allocating error: {}",
414+
error
415+
);
433416
None
434417
}
435418
};
@@ -654,11 +637,12 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
654637
source_info: SourceInfo,
655638
) {
656639
trace!("attepting to replace {:?} with {:?}", rval, value);
657-
if let Err(e) = self.ecx.validate_operand(
640+
if let Err(e) = self.ecx.const_validate_operand(
658641
value,
659642
vec![],
660643
// FIXME: is ref tracking too expensive?
661-
Some(&mut interpret::RefTracking::empty()),
644+
&mut interpret::RefTracking::empty(),
645+
/*may_ref_to_static*/ true,
662646
) {
663647
trace!("validation error, attempt failed: {:?}", e);
664648
return;

src/test/ui/consts/const-eval/dangling.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use std::{mem, usize};
66
const TEST: () = { unsafe { //~ NOTE
77
let slice: *const [u8] = mem::transmute((1usize, usize::MAX));
88
let _val = &*slice; //~ ERROR: any use of this value will cause an error
9-
//~^ NOTE: total size is bigger than largest supported object
9+
//~^ NOTE: slice is bigger than largest supported object
1010
//~^^ on by default
1111
} };
1212

src/test/ui/consts/const-eval/dangling.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ error: any use of this value will cause an error
44
LL | / const TEST: () = { unsafe {
55
LL | | let slice: *const [u8] = mem::transmute((1usize, usize::MAX));
66
LL | | let _val = &*slice;
7-
| | ^^^^^^^ invalid slice: total size is bigger than largest supported object
7+
| | ^^^^^^^ invalid metadata in wide pointer: slice is bigger than largest supported object
88
LL | |
99
LL | |
1010
LL | | } };

0 commit comments

Comments
 (0)