interpret/validity: separately control checking numbers for being init and non-ptr

This commit is contained in:
Ralf Jung 2022-05-19 20:16:25 +02:00
parent 77972d2d01
commit 5514b1176f
14 changed files with 65 additions and 43 deletions

View file

@ -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
}

View file

@ -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))?)
}
}

View file

@ -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(()) => {}