// Copyright (C) 2019 Alibaba Cloud Computing. All rights reserved. // // Portions Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Portions Copyright 2017 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE-BSD-3-Clause file. // // SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause //! Traits to represent an address within an address space. //! //! Two traits are defined to represent an address within an address space: //! - [`AddressValue`](trait.AddressValue.html): stores the raw value of an address. Typically //! `u32`,`u64` or `usize` is used to store the raw value. But pointers, such as `*u8`, can't be used //! because they don't implement the [`Add`](https://doc.rust-lang.org/std/ops/trait.Add.html) and //! [`Sub`](https://doc.rust-lang.org/std/ops/trait.Sub.html) traits. //! - [Address](trait.Address.html): encapsulates an [`AddressValue`](trait.AddressValue.html) //! object and defines methods to access and manipulate it. use std::cmp::{Eq, Ord, PartialEq, PartialOrd}; use std::fmt::Debug; use std::ops::{Add, BitAnd, BitOr, Not, Sub}; /// Simple helper trait used to store a raw address value. pub trait AddressValue { /// Type of the raw address value. type V: Copy + PartialEq + Eq + PartialOrd + Ord + Not + Add + Sub + BitAnd + BitOr + Debug + From; /// Return the value zero, coerced into the value type `Self::V` fn zero() -> Self::V { 0u8.into() } /// Return the value zero, coerced into the value type `Self::V` fn one() -> Self::V { 1u8.into() } } /// Trait to represent an address within an address space. /// /// To simplify the design and implementation, assume the same raw data type `(AddressValue::V)` /// could be used to store address, size and offset for the address space. Thus the `Address` trait /// could be used to manage address, size and offset. On the other hand, type aliases may be /// defined to improve code readability. /// /// One design rule is applied to the `Address` trait, namely that operators (+, -, &, | etc) are /// not supported and it forces clients to explicitly invoke corresponding methods. But there are /// always exceptions: /// `Address` (BitAnd|BitOr) `AddressValue` are supported. pub trait Address: AddressValue + Sized + Default + Copy + Eq + PartialEq + Ord + PartialOrd + BitAnd<::V, Output = Self> + BitOr<::V, Output = Self> { /// Creates an address from a raw address value. fn new(addr: Self::V) -> Self; /// Returns the raw value of the address. fn raw_value(&self) -> Self::V; /// Returns the bitwise and of the address with the given mask. fn mask(&self, mask: Self::V) -> Self::V { self.raw_value() & mask } /// Computes the offset from this address to the given base address. /// /// Returns `None` if there is underflow. fn checked_offset_from(&self, base: Self) -> Option; /// Computes the offset from this address to the given base address. /// /// In the event of overflow, follows standard Rust behavior, i.e. panic in debug builds, /// silently wrap in release builds. /// /// Note that, unlike the `unchecked_*` methods in std, this method never invokes undefined /// behavior. /// # Examples /// /// ``` /// # use vm_memory::{Address, GuestAddress}; /// # /// let base = GuestAddress(0x100); /// let addr = GuestAddress(0x150); /// assert_eq!(addr.unchecked_offset_from(base), 0x50); /// ``` fn unchecked_offset_from(&self, base: Self) -> Self::V { self.raw_value() - base.raw_value() } /// Returns self, aligned to the given power of two. fn checked_align_up(&self, power_of_two: Self::V) -> Option { let mask = power_of_two - Self::one(); assert_ne!(power_of_two, Self::zero()); assert_eq!(power_of_two & mask, Self::zero()); self.checked_add(mask).map(|x| x & !mask) } /// Returns self, aligned to the given power of two. /// Only use this when the result is guaranteed not to overflow. fn unchecked_align_up(&self, power_of_two: Self::V) -> Self { let mask = power_of_two - Self::one(); self.unchecked_add(mask) & !mask } /// Computes `self + other`, returning `None` if overflow occurred. fn checked_add(&self, other: Self::V) -> Option; /// Computes `self + other`. /// /// Returns a tuple of the addition result along with a boolean indicating whether an arithmetic /// overflow would occur. If an overflow would have occurred then the wrapped address /// is returned. fn overflowing_add(&self, other: Self::V) -> (Self, bool); /// Computes `self + offset`. /// /// In the event of overflow, follows standard Rust behavior, i.e. panic in debug builds, /// silently wrap in release builds. /// /// Note that, unlike the `unchecked_*` methods in std, this method never invokes undefined /// behavior.. fn unchecked_add(&self, offset: Self::V) -> Self; /// Subtracts two addresses, checking for underflow. If underflow happens, `None` is returned. fn checked_sub(&self, other: Self::V) -> Option; /// Computes `self - other`. /// /// Returns a tuple of the subtraction result along with a boolean indicating whether an /// arithmetic overflow would occur. If an overflow would have occurred then the wrapped /// address is returned. fn overflowing_sub(&self, other: Self::V) -> (Self, bool); /// Computes `self - other`. /// /// In the event of underflow, follows standard Rust behavior, i.e. panic in debug builds, /// silently wrap in release builds. /// /// Note that, unlike the `unchecked_*` methods in std, this method never invokes undefined /// behavior. fn unchecked_sub(&self, other: Self::V) -> Self; } macro_rules! impl_address_ops { ($T:ident, $V:ty) => { impl AddressValue for $T { type V = $V; } impl Address for $T { fn new(value: $V) -> $T { $T(value) } fn raw_value(&self) -> $V { self.0 } fn checked_offset_from(&self, base: $T) -> Option<$V> { self.0.checked_sub(base.0) } fn checked_add(&self, other: $V) -> Option<$T> { self.0.checked_add(other).map($T) } fn overflowing_add(&self, other: $V) -> ($T, bool) { let (t, ovf) = self.0.overflowing_add(other); ($T(t), ovf) } fn unchecked_add(&self, offset: $V) -> $T { $T(self.0 + offset) } fn checked_sub(&self, other: $V) -> Option<$T> { self.0.checked_sub(other).map($T) } fn overflowing_sub(&self, other: $V) -> ($T, bool) { let (t, ovf) = self.0.overflowing_sub(other); ($T(t), ovf) } fn unchecked_sub(&self, other: $V) -> $T { $T(self.0 - other) } } impl Default for $T { fn default() -> $T { Self::new(0 as $V) } } impl BitAnd<$V> for $T { type Output = $T; fn bitand(self, other: $V) -> $T { $T(self.0 & other) } } impl BitOr<$V> for $T { type Output = $T; fn bitor(self, other: $V) -> $T { $T(self.0 | other) } } }; } #[cfg(test)] mod tests { use super::*; #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)] struct MockAddress(pub u64); impl_address_ops!(MockAddress, u64); #[test] fn test_new() { assert_eq!(MockAddress::new(0), MockAddress(0)); assert_eq!(MockAddress::new(std::u64::MAX), MockAddress(std::u64::MAX)); } #[test] fn test_offset_from() { let base = MockAddress(0x100); let addr = MockAddress(0x150); assert_eq!(addr.unchecked_offset_from(base), 0x50u64); assert_eq!(addr.checked_offset_from(base), Some(0x50u64)); assert_eq!(base.checked_offset_from(addr), None); } #[test] fn test_equals() { let a = MockAddress(0x300); let b = MockAddress(0x300); let c = MockAddress(0x301); assert_eq!(a, MockAddress(a.raw_value())); assert_eq!(a, b); assert_eq!(b, a); assert_ne!(a, c); assert_ne!(c, a); } #[test] fn test_cmp() { let a = MockAddress(0x300); let b = MockAddress(0x301); assert!(a < b); } #[test] fn test_checked_align_up() { assert_eq!( MockAddress::new(0x128).checked_align_up(8), Some(MockAddress(0x128)) ); assert_eq!( MockAddress::new(0x128).checked_align_up(16), Some(MockAddress(0x130)) ); assert_eq!( MockAddress::new(std::u64::MAX - 0x3fff).checked_align_up(0x10000), None ); } #[test] #[should_panic] fn test_checked_align_up_invalid() { let _ = MockAddress::new(0x128).checked_align_up(12); } #[test] fn test_unchecked_align_up() { assert_eq!( MockAddress::new(0x128).unchecked_align_up(8), MockAddress(0x128) ); assert_eq!( MockAddress::new(0x128).unchecked_align_up(16), MockAddress(0x130) ); } #[test] fn test_mask() { let a = MockAddress(0x5050); assert_eq!(MockAddress(0x5000), a & 0xff00u64); assert_eq!(0x5000, a.mask(0xff00u64)); assert_eq!(MockAddress(0x5055), a | 0x0005u64); } fn check_add(a: u64, b: u64, expected_overflow: bool, expected_result: u64) { assert_eq!( (MockAddress(expected_result), expected_overflow), MockAddress(a).overflowing_add(b) ); if expected_overflow { assert!(MockAddress(a).checked_add(b).is_none()); #[cfg(debug_assertions)] assert!(std::panic::catch_unwind(|| MockAddress(a).unchecked_add(b)).is_err()); } else { assert_eq!( Some(MockAddress(expected_result)), MockAddress(a).checked_add(b) ); assert_eq!( MockAddress(expected_result), MockAddress(a).unchecked_add(b) ); } } #[test] fn test_add() { // without overflow // normal case check_add(10, 10, false, 20); // edge case check_add(std::u64::MAX - 1, 1, false, std::u64::MAX); // with overflow check_add(std::u64::MAX, 1, true, 0); } fn check_sub(a: u64, b: u64, expected_overflow: bool, expected_result: u64) { assert_eq!( (MockAddress(expected_result), expected_overflow), MockAddress(a).overflowing_sub(b) ); if expected_overflow { assert!(MockAddress(a).checked_sub(b).is_none()); assert!(MockAddress(a).checked_offset_from(MockAddress(b)).is_none()); #[cfg(debug_assertions)] assert!(std::panic::catch_unwind(|| MockAddress(a).unchecked_sub(b)).is_err()); } else { assert_eq!( Some(MockAddress(expected_result)), MockAddress(a).checked_sub(b) ); assert_eq!( Some(expected_result), MockAddress(a).checked_offset_from(MockAddress(b)) ); assert_eq!( MockAddress(expected_result), MockAddress(a).unchecked_sub(b) ); } } #[test] fn test_sub() { // without overflow // normal case check_sub(20, 10, false, 10); // edge case check_sub(1, 1, false, 0); // with underflow check_sub(0, 1, true, std::u64::MAX); } #[test] fn test_default() { assert_eq!(MockAddress::default(), MockAddress(0)); } #[test] fn test_bit_and() { let a = MockAddress(0x0ff0); assert_eq!(a & 0xf00f, MockAddress(0)); } #[test] fn test_bit_or() { let a = MockAddress(0x0ff0); assert_eq!(a | 0xf00f, MockAddress(0xffff)); } }