write_path(&mut msg, where_);
}
write!(&mut msg, ", but expected {}", $details).unwrap();
- throw_unsup!(ValidationFailure(msg))
+ throw_ub!(ValidationFailure(msg))
}};
($what:expr, $where:expr) => {{
let mut msg = format!("encountered {}", $what);
msg.push_str(" at ");
write_path(&mut msg, where_);
}
- throw_unsup!(ValidationFailure(msg))
+ throw_ub!(ValidationFailure(msg))
}};
}
($e:expr, $what:expr, $where:expr, $details:expr) => {{
match $e {
Ok(x) => x,
+ // We re-throw the error, so we are okay with allocation:
+ // this can only slow down builds that fail anyway.
Err(_) => throw_validation_failure!($what, $where, $details),
}
}};
($e:expr, $what:expr, $where:expr) => {{
match $e {
Ok(x) => x,
+ // We re-throw the error, so we are okay with allocation:
+ // this can only slow down builds that fail anyway.
Err(_) => throw_validation_failure!($what, $where),
}
}};
path: Vec<PathElem>,
ref_tracking_for_consts:
Option<&'rt mut RefTracking<MPlaceTy<'tcx, M::PointerTag>, Vec<PathElem>>>,
+ may_ref_to_static: bool,
ecx: &'rt InterpCx<'mir, 'tcx, M>,
}
self.check_wide_ptr_meta(place.meta, place.layout)?;
}
// Make sure this is dereferenceable and all.
- let (size, align) = self
- .ecx
- .size_and_align_of(place.meta, place.layout)?
+ let size_and_align = match self.ecx.size_and_align_of(place.meta, place.layout) {
+ Ok(res) => res,
+ Err(err) => match err.kind {
+ err_ub!(InvalidMeta(msg)) => throw_validation_failure!(
+ format_args!("invalid {} metadata: {}", kind, msg),
+ self.path
+ ),
+ _ => bug!("Unexpected error during ptr size_and_align_of: {}", err),
+ },
+ };
+ let (size, align) = size_and_align
// for the purpose of validity, consider foreign types to have
// alignment and size determined by the layout (size will be 0,
// alignment should take attributes into account).
place.ptr, size, align
);
match err.kind {
- err_unsup!(InvalidNullPointerUsage) => {
+ err_ub!(InvalidIntPointerUsage(0)) => {
throw_validation_failure!(format_args!("a NULL {}", kind), self.path)
}
- err_unsup!(AlignmentCheckFailed { required, has }) => {
- throw_validation_failure!(
- format_args!(
- "an unaligned {} \
- (required {} byte alignment but found {})",
- kind,
- required.bytes(),
- has.bytes()
- ),
- self.path
- )
- }
+ err_ub!(InvalidIntPointerUsage(i)) => throw_validation_failure!(
+ format_args!("a {} to unallocated address {}", kind, i),
+ self.path
+ ),
+ err_ub!(AlignmentCheckFailed { required, has }) => throw_validation_failure!(
+ format_args!(
+ "an unaligned {} (required {} byte alignment but found {})",
+ kind,
+ required.bytes(),
+ has.bytes()
+ ),
+ self.path
+ ),
err_unsup!(ReadBytesAsPointer) => throw_validation_failure!(
format_args!("a dangling {} (created from integer)", kind),
self.path
),
- _ => throw_validation_failure!(
- format_args!("a dangling {} (not entirely in bounds)", kind),
+ err_ub!(PointerOutOfBounds { .. }) => throw_validation_failure!(
+ format_args!(
+ "a dangling {} (going beyond the bounds of its allocation)",
+ kind
+ ),
self.path
),
+ // This cannot happen during const-eval (because interning already detects
+ // dangling pointers), but it can happen in Miri.
+ err_ub!(PointerUseAfterFree(_)) => throw_validation_failure!(
+ format_args!("a dangling {} (use-after-free)", kind),
+ self.path
+ ),
+ _ => bug!("Unexpected error during ptr inbounds test: {}", err),
}
}
};
if !did.is_local() || self.ecx.tcx.is_foreign_item(did) {
return Ok(());
}
+ if !self.may_ref_to_static && self.ecx.tcx.is_static(did) {
+ throw_validation_failure!(
+ format_args!("a {} pointing to a static variable", kind),
+ self.path
+ );
+ }
}
}
// Proceed recursively even for ZST, no reason to skip them!
err_unsup!(ReadPointerAsBytes) => {
throw_validation_failure!("a pointer", self.path, "plain (non-pointer) bytes")
}
+ // Propagate upwards (that will also check for unexpected errors).
_ => return Err(err),
},
}
Err(err) => {
// For some errors we might be able to provide extra information
match err.kind {
- err_unsup!(ReadUndefBytes(offset)) => {
+ err_ub!(InvalidUndefBytes(Some(ptr))) => {
// Some byte was undefined, determine which
// element that byte belongs to so we can
// provide an index.
- let i = (offset.bytes() / layout.size.bytes()) as usize;
+ let i = (ptr.offset.bytes() / layout.size.bytes()) as usize;
self.path.push(PathElem::ArrayElem(i));
throw_validation_failure!("undefined bytes", self.path)
}
impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
- /// This function checks the data at `op`. `op` is assumed to cover valid memory if it
- /// is an indirect operand.
- /// It will error if the bits at the destination do not match the ones described by the layout.
- ///
- /// `ref_tracking_for_consts` can be `None` to avoid recursive checking below references.
- /// This also toggles between "run-time" (no recursion) and "compile-time" (with recursion)
- /// validation (e.g., pointer values are fine in integers at runtime) and various other const
- /// specific validation checks.
- pub fn validate_operand(
+ fn validate_operand_internal(
&self,
op: OpTy<'tcx, M::PointerTag>,
path: Vec<PathElem>,
ref_tracking_for_consts: Option<
&mut RefTracking<MPlaceTy<'tcx, M::PointerTag>, Vec<PathElem>>,
>,
+ may_ref_to_static: bool,
) -> InterpResult<'tcx> {
- trace!("validate_operand: {:?}, {:?}", *op, op.layout.ty);
+ trace!("validate_operand_internal: {:?}, {:?}", *op, op.layout.ty);
// Construct a visitor
- let mut visitor = ValidityVisitor { path, ref_tracking_for_consts, ecx: self };
+ let mut visitor =
+ ValidityVisitor { path, ref_tracking_for_consts, may_ref_to_static, ecx: self };
// Try to cast to ptr *once* instead of all the time.
let op = self.force_op_ptr(op).unwrap_or(op);
- // Run it
- visitor.visit_value(op)
+ // Run it.
+ match visitor.visit_value(op) {
+ Ok(()) => Ok(()),
+ Err(err) if matches!(err.kind, err_ub!(ValidationFailure { .. })) => Err(err),
+ Err(err) if cfg!(debug_assertions) => {
+ bug!("Unexpected error during validation: {}", err)
+ }
+ Err(err) => Err(err),
+ }
+ }
+
+ /// This function checks the data at `op` to be const-valid.
+ /// `op` is assumed to cover valid memory if it is an indirect operand.
+ /// It will error if the bits at the destination do not match the ones described by the layout.
+ ///
+ /// `ref_tracking` is used to record references that we encounter so that they
+ /// can be checked recursively by an outside driving loop.
+ ///
+ /// `may_ref_to_static` controls whether references are allowed to point to statics.
+ #[inline(always)]
+ pub fn const_validate_operand(
+ &self,
+ op: OpTy<'tcx, M::PointerTag>,
+ path: Vec<PathElem>,
+ ref_tracking: &mut RefTracking<MPlaceTy<'tcx, M::PointerTag>, Vec<PathElem>>,
+ may_ref_to_static: bool,
+ ) -> InterpResult<'tcx> {
+ self.validate_operand_internal(op, path, Some(ref_tracking), may_ref_to_static)
+ }
+
+ /// This function checks the data at `op` to be runtime-valid.
+ /// `op` is assumed to cover valid memory if it is an indirect operand.
+ /// It will error if the bits at the destination do not match the ones described by the layout.
+ #[inline(always)]
+ pub fn validate_operand(&self, op: OpTy<'tcx, M::PointerTag>) -> InterpResult<'tcx> {
+ self.validate_operand_internal(op, vec![], None, false)
}
}