validation: descend from consts into statics

This commit is contained in:
Ralf Jung 2024-01-06 13:48:48 +01:00
parent 4e77e368eb
commit 9c0623fe8f
25 changed files with 334 additions and 212 deletions

View file

@ -27,8 +27,9 @@ use rustc_target::abi::{
use std::hash::Hash;
use super::{
AllocId, CheckInAllocMsg, GlobalAlloc, ImmTy, Immediate, InterpCx, InterpResult, MPlaceTy,
Machine, MemPlaceMeta, OpTy, Pointer, Projectable, Scalar, ValueVisitor,
format_interp_error, AllocId, CheckInAllocMsg, GlobalAlloc, ImmTy, Immediate, InterpCx,
InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy, Pointer, Projectable, Scalar,
ValueVisitor,
};
// for the validation errors
@ -460,46 +461,49 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
// Special handling for pointers to statics (irrespective of their type).
assert!(!self.ecx.tcx.is_thread_local_static(did));
assert!(self.ecx.tcx.is_static(did));
let is_mut =
matches!(self.ecx.tcx.def_kind(did), DefKind::Static(Mutability::Mut))
|| !self
.ecx
.tcx
.type_of(did)
.no_bound_vars()
.expect("statics should not have generic parameters")
.is_freeze(*self.ecx.tcx, ty::ParamEnv::reveal_all());
// Mutability check.
if ptr_expected_mutbl == Mutability::Mut {
if matches!(
self.ecx.tcx.def_kind(did),
DefKind::Static(Mutability::Not)
) && self
.ecx
.tcx
.type_of(did)
.no_bound_vars()
.expect("statics should not have generic parameters")
.is_freeze(*self.ecx.tcx, ty::ParamEnv::reveal_all())
{
if !is_mut {
throw_validation_failure!(self.path, MutableRefToImmutable);
}
}
// We skip recursively checking other statics. These statics must be sound by
// themselves, and the only way to get broken statics here is by using
// unsafe code.
// The reasons we don't check other statics is twofold. For one, in all
// sound cases, the static was already validated on its own, and second, we
// trigger cycle errors if we try to compute the value of the other static
// and that static refers back to us.
// We might miss const-invalid data,
// but things are still sound otherwise (in particular re: consts
// referring to statics).
return Ok(());
match self.ctfe_mode {
Some(CtfeValidationMode::Static { .. }) => {
// We skip recursively checking other statics. These statics must be sound by
// themselves, and the only way to get broken statics here is by using
// unsafe code.
// The reasons we don't check other statics is twofold. For one, in all
// sound cases, the static was already validated on its own, and second, we
// trigger cycle errors if we try to compute the value of the other static
// and that static refers back to us.
// This could miss some UB, but that's fine.
return Ok(());
}
Some(CtfeValidationMode::Const { .. }) => {
// For consts on the other hand we have to recursively check;
// pattern matching assumes a valid value. However we better make
// sure this is not mutable.
if is_mut {
throw_validation_failure!(self.path, ConstRefToMutable);
}
}
None => {}
}
}
GlobalAlloc::Memory(alloc) => {
if alloc.inner().mutability == Mutability::Mut
&& matches!(self.ctfe_mode, Some(CtfeValidationMode::Const { .. }))
{
// This is impossible: this can only be some inner allocation of a
// `static mut` (everything else either hits the `GlobalAlloc::Static`
// case or is interned immutably). To get such a pointer we'd have to
// load it from a static, but such loads lead to a CTFE error.
span_bug!(
self.ecx.tcx.span,
"encountered reference to mutable memory inside a `const`"
);
throw_validation_failure!(self.path, ConstRefToMutable);
}
if ptr_expected_mutbl == Mutability::Mut
&& alloc.inner().mutability == Mutability::Not
@ -978,7 +982,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// Complain about any other kind of error -- those are bad because we'd like to
// report them in a way that shows *where* in the value the issue lies.
Err(err) => {
bug!("Unexpected error during validation: {}", self.format_error(err));
bug!(
"Unexpected error during validation: {}",
format_interp_error(self.tcx.dcx(), err)
);
}
}
}