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