1
Fork 0

const validation: point at where we found a pointer but expected an integer

This commit is contained in:
Ralf Jung 2023-08-02 16:14:36 +02:00
parent 7637653b9f
commit 7767cbb3b0
34 changed files with 544 additions and 408 deletions

View file

@ -18,9 +18,9 @@ use rustc_span::DUMMY_SP;
use rustc_target::abi::{Align, HasDataLayout, Size};
use super::{
read_target_uint, write_target_uint, AllocId, InterpError, InterpResult, Pointer, Provenance,
ResourceExhaustionInfo, Scalar, ScalarSizeMismatch, UndefinedBehaviorInfo, UninitBytesAccess,
UnsupportedOpInfo,
read_target_uint, write_target_uint, AllocId, BadBytesAccess, InterpError, InterpResult,
Pointer, PointerArithmetic, Provenance, ResourceExhaustionInfo, Scalar, ScalarSizeMismatch,
UndefinedBehaviorInfo, UnsupportedOpInfo,
};
use crate::ty;
use init_mask::*;
@ -173,13 +173,13 @@ pub enum AllocError {
/// A scalar had the wrong size.
ScalarSizeMismatch(ScalarSizeMismatch),
/// Encountered a pointer where we needed raw bytes.
ReadPointerAsBytes,
ReadPointerAsInt(Option<BadBytesAccess>),
/// Partially overwriting a pointer.
PartialPointerOverwrite(Size),
OverwritePartialPointer(Size),
/// Partially copying a pointer.
PartialPointerCopy(Size),
ReadPartialPointer(Size),
/// Using uninitialized data where it is not allowed.
InvalidUninitBytes(Option<UninitBytesAccess>),
InvalidUninitBytes(Option<BadBytesAccess>),
}
pub type AllocResult<T = ()> = Result<T, AllocError>;
@ -196,12 +196,14 @@ impl AllocError {
ScalarSizeMismatch(s) => {
InterpError::UndefinedBehavior(UndefinedBehaviorInfo::ScalarSizeMismatch(s))
}
ReadPointerAsBytes => InterpError::Unsupported(UnsupportedOpInfo::ReadPointerAsBytes),
PartialPointerOverwrite(offset) => InterpError::Unsupported(
UnsupportedOpInfo::PartialPointerOverwrite(Pointer::new(alloc_id, offset)),
ReadPointerAsInt(info) => InterpError::Unsupported(
UnsupportedOpInfo::ReadPointerAsInt(info.map(|b| (alloc_id, b))),
),
PartialPointerCopy(offset) => InterpError::Unsupported(
UnsupportedOpInfo::PartialPointerCopy(Pointer::new(alloc_id, offset)),
OverwritePartialPointer(offset) => InterpError::Unsupported(
UnsupportedOpInfo::OverwritePartialPointer(Pointer::new(alloc_id, offset)),
),
ReadPartialPointer(offset) => InterpError::Unsupported(
UnsupportedOpInfo::ReadPartialPointer(Pointer::new(alloc_id, offset)),
),
InvalidUninitBytes(info) => InterpError::UndefinedBehavior(
UndefinedBehaviorInfo::InvalidUninitBytes(info.map(|b| (alloc_id, b))),
@ -433,14 +435,26 @@ impl<Prov: Provenance, Extra, Bytes: AllocBytes> Allocation<Prov, Extra, Bytes>
range: AllocRange,
) -> AllocResult<&[u8]> {
self.init_mask.is_range_initialized(range).map_err(|uninit_range| {
AllocError::InvalidUninitBytes(Some(UninitBytesAccess {
AllocError::InvalidUninitBytes(Some(BadBytesAccess {
access: range,
uninit: uninit_range,
bad: uninit_range,
}))
})?;
if !Prov::OFFSET_IS_ADDR {
if !self.provenance.range_empty(range, cx) {
return Err(AllocError::ReadPointerAsBytes);
// Find the provenance.
let (offset, _prov) = self
.provenance
.range_get_ptrs(range, cx)
.first()
.copied()
.expect("there must be provenance somewhere here");
let start = offset.max(range.start); // the pointer might begin before `range`!
let end = (offset + cx.pointer_size()).min(range.end()); // the pointer might end after `range`!
return Err(AllocError::ReadPointerAsInt(Some(BadBytesAccess {
access: range,
bad: AllocRange::from(start..end),
})));
}
}
Ok(self.get_bytes_unchecked(range))
@ -536,23 +550,25 @@ impl<Prov: Provenance, Extra, Bytes: AllocBytes> Allocation<Prov, Extra, Bytes>
// Now use this provenance.
let ptr = Pointer::new(prov, Size::from_bytes(bits));
return Ok(Scalar::from_maybe_pointer(ptr, cx));
} else {
// Without OFFSET_IS_ADDR, the only remaining case we can handle is total absence of
// provenance.
if self.provenance.range_empty(range, cx) {
return Ok(Scalar::from_uint(bits, range.size));
}
// Else we have mixed provenance, that doesn't work.
return Err(AllocError::ReadPartialPointer(range.start));
}
} else {
// We are *not* reading a pointer.
// If we can just ignore provenance, do exactly that.
if Prov::OFFSET_IS_ADDR {
// If we can just ignore provenance or there is none, that's easy.
if Prov::OFFSET_IS_ADDR || self.provenance.range_empty(range, cx) {
// We just strip provenance.
return Ok(Scalar::from_uint(bits, range.size));
}
// There is some provenance and we don't have OFFSET_IS_ADDR. This doesn't work.
return Err(AllocError::ReadPointerAsInt(None));
}
// Fallback path for when we cannot treat provenance bytewise or ignore it.
assert!(!Prov::OFFSET_IS_ADDR);
if !self.provenance.range_empty(range, cx) {
return Err(AllocError::ReadPointerAsBytes);
}
// There is no provenance, we can just return the bits.
Ok(Scalar::from_uint(bits, range.size))
}
/// Writes a *non-ZST* scalar.