70 lines
2.6 KiB
Rust
70 lines
2.6 KiB
Rust
use rustc_miri::interpret::{
|
|
Pointer,
|
|
EvalResult, EvalError,
|
|
PrimVal,
|
|
EvalContext,
|
|
};
|
|
|
|
use rustc::ty::Ty;
|
|
|
|
pub trait EvalContextExt<'tcx> {
|
|
fn wrapping_pointer_offset(
|
|
&self,
|
|
ptr: Pointer,
|
|
pointee_ty: Ty<'tcx>,
|
|
offset: i64,
|
|
) -> EvalResult<'tcx, Pointer>;
|
|
|
|
fn pointer_offset(
|
|
&self,
|
|
ptr: Pointer,
|
|
pointee_ty: Ty<'tcx>,
|
|
offset: i64,
|
|
) -> EvalResult<'tcx, Pointer>;
|
|
}
|
|
|
|
impl<'a, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'tcx, super::Evaluator> {
|
|
fn wrapping_pointer_offset(
|
|
&self,
|
|
ptr: Pointer,
|
|
pointee_ty: Ty<'tcx>,
|
|
offset: i64,
|
|
) -> EvalResult<'tcx, Pointer> {
|
|
// FIXME: assuming here that type size is < i64::max_value()
|
|
let pointee_size = self.type_size(pointee_ty)?.expect("cannot offset a pointer to an unsized type") as i64;
|
|
let offset = offset.overflowing_mul(pointee_size).0;
|
|
ptr.wrapping_signed_offset(offset, self)
|
|
}
|
|
|
|
fn pointer_offset(
|
|
&self,
|
|
ptr: Pointer,
|
|
pointee_ty: Ty<'tcx>,
|
|
offset: i64,
|
|
) -> EvalResult<'tcx, Pointer> {
|
|
// This function raises an error if the offset moves the pointer outside of its allocation. We consider
|
|
// ZSTs their own huge allocation that doesn't overlap with anything (and nothing moves in there because the size is 0).
|
|
// We also consider the NULL pointer its own separate allocation, and all the remaining integers pointers their own
|
|
// allocation.
|
|
|
|
if ptr.is_null()? { // NULL pointers must only be offset by 0
|
|
return if offset == 0 { Ok(ptr) } else { Err(EvalError::InvalidNullPointerUsage) };
|
|
}
|
|
// FIXME: assuming here that type size is < i64::max_value()
|
|
let pointee_size = self.type_size(pointee_ty)?.expect("cannot offset a pointer to an unsized type") as i64;
|
|
return if let Some(offset) = offset.checked_mul(pointee_size) {
|
|
let ptr = ptr.signed_offset(offset, self)?;
|
|
// Do not do bounds-checking for integers; they can never alias a normal pointer anyway.
|
|
if let PrimVal::Ptr(ptr) = ptr.into_inner_primval() {
|
|
self.memory.check_bounds(ptr, false)?;
|
|
} else if ptr.is_null()? {
|
|
// We moved *to* a NULL pointer. That seems wrong, LLVM considers the NULL pointer its own small allocation. Reject this, for now.
|
|
return Err(EvalError::InvalidNullPointerUsage);
|
|
}
|
|
Ok(ptr)
|
|
} else {
|
|
Err(EvalError::OverflowingMath)
|
|
}
|
|
}
|
|
}
|