interpret/validity: separately control checking numbers for being init and non-ptr
This commit is contained in:
parent
77972d2d01
commit
5514b1176f
14 changed files with 65 additions and 43 deletions
|
@ -133,9 +133,11 @@ pub trait Machine<'mir, 'tcx>: Sized {
|
|||
/// Whether to enforce the validity invariant
|
||||
fn enforce_validity(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool;
|
||||
|
||||
/// Whether to enforce validity (e.g., initialization and not having ptr provenance)
|
||||
/// of integers and floats.
|
||||
fn enforce_number_validity(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool;
|
||||
/// Whether to enforce integers and floats being initialized.
|
||||
fn enforce_number_init(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool;
|
||||
|
||||
/// Whether to enforce integers and floats not having provenance.
|
||||
fn enforce_number_no_provenance(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool;
|
||||
|
||||
/// Whether function calls should be [ABI](Abi)-checked.
|
||||
fn enforce_abi(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool {
|
||||
|
@ -453,7 +455,12 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn enforce_number_validity(_ecx: &InterpCx<$mir, $tcx, Self>) -> bool {
|
||||
fn enforce_number_init(_ecx: &InterpCx<$mir, $tcx, Self>) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn enforce_number_no_provenance(_ecx: &InterpCx<$mir, $tcx, Self>) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
|
|
|
@ -924,10 +924,15 @@ impl<'tcx, 'a, Tag: Provenance, Extra> AllocRef<'a, 'tcx, Tag, Extra> {
|
|||
self.read_scalar(alloc_range(offset, self.tcx.data_layout().pointer_size))
|
||||
}
|
||||
|
||||
pub fn check_bytes(&self, range: AllocRange, allow_uninit_and_ptr: bool) -> InterpResult<'tcx> {
|
||||
pub fn check_bytes(
|
||||
&self,
|
||||
range: AllocRange,
|
||||
allow_uninit: bool,
|
||||
allow_ptr: bool,
|
||||
) -> InterpResult<'tcx> {
|
||||
Ok(self
|
||||
.alloc
|
||||
.check_bytes(&self.tcx, self.range.subrange(range), allow_uninit_and_ptr)
|
||||
.check_bytes(&self.tcx, self.range.subrange(range), allow_uninit, allow_ptr)
|
||||
.map_err(|e| e.to_interp_error(self.alloc_id))?)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -536,15 +536,22 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
|
|||
let value = self.read_scalar(value)?;
|
||||
// NOTE: Keep this in sync with the array optimization for int/float
|
||||
// types below!
|
||||
if M::enforce_number_validity(self.ecx) {
|
||||
// Integers/floats with number validity: Must be scalar bits, pointers are dangerous.
|
||||
if M::enforce_number_init(self.ecx) {
|
||||
try_validation!(
|
||||
value.check_init(),
|
||||
self.path,
|
||||
err_ub!(InvalidUninitBytes(..)) =>
|
||||
{ "{:x}", value } expected { "initialized bytes" }
|
||||
);
|
||||
}
|
||||
if M::enforce_number_no_provenance(self.ecx) {
|
||||
// As a special exception we *do* match on a `Scalar` here, since we truly want
|
||||
// to know its underlying representation (and *not* cast it to an integer).
|
||||
let is_bits =
|
||||
value.check_init().map_or(false, |v| matches!(v, Scalar::Int(..)));
|
||||
if !is_bits {
|
||||
let is_ptr =
|
||||
value.check_init().map_or(false, |v| matches!(v, Scalar::Ptr(..)));
|
||||
if is_ptr {
|
||||
throw_validation_failure!(self.path,
|
||||
{ "{:x}", value } expected { "initialized plain (non-pointer) bytes" }
|
||||
{ "{:x}", value } expected { "plain (non-pointer) bytes" }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -651,7 +658,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
|
|||
let size = scalar_layout.size(self.ecx);
|
||||
let is_full_range = match scalar_layout {
|
||||
ScalarAbi::Initialized { .. } => {
|
||||
if M::enforce_number_validity(self.ecx) {
|
||||
if M::enforce_number_init(self.ecx) {
|
||||
false // not "full" since uninit is not accepted
|
||||
} else {
|
||||
scalar_layout.is_always_valid(self.ecx)
|
||||
|
@ -910,10 +917,10 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
|
|||
return Ok(());
|
||||
};
|
||||
|
||||
let allow_uninit_and_ptr = !M::enforce_number_validity(self.ecx);
|
||||
match alloc.check_bytes(
|
||||
alloc_range(Size::ZERO, size),
|
||||
allow_uninit_and_ptr,
|
||||
/*allow_uninit*/ !M::enforce_number_init(self.ecx),
|
||||
/*allow_ptr*/ !M::enforce_number_no_provenance(self.ecx),
|
||||
) {
|
||||
// In the happy case, we needn't check anything else.
|
||||
Ok(()) => {}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue