hyperlight_host/mem/
ptr.rs

1/*
2Copyright 2025  The Hyperlight Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17use std::ops::Add;
18
19use tracing::{Span, instrument};
20
21use super::ptr_addr_space::{AddressSpace, GuestAddressSpace};
22use super::ptr_offset::Offset;
23use crate::Result;
24use crate::error::HyperlightError::{self, CheckedAddOverflow, RawPointerLessThanBaseAddress};
25
26/// A representation of a raw pointer inside a given address space.
27///
28/// Use this type to distinguish between an offset and a raw pointer
29#[derive(Debug, Clone, Eq, PartialEq)]
30pub struct RawPtr(u64);
31
32impl From<u64> for RawPtr {
33    #[instrument(skip_all, parent = Span::current(), level= "Trace")]
34    fn from(val: u64) -> Self {
35        Self(val)
36    }
37}
38
39impl Add<Offset> for RawPtr {
40    type Output = RawPtr;
41    #[instrument(skip_all, parent = Span::current(), level= "Trace")]
42    fn add(self, rhs: Offset) -> RawPtr {
43        let val = self.0 + u64::from(rhs);
44        RawPtr(val)
45    }
46}
47
48impl TryFrom<usize> for RawPtr {
49    type Error = HyperlightError;
50    #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")]
51    fn try_from(val: usize) -> Result<Self> {
52        let val_u64 = u64::try_from(val)?;
53        Ok(Self::from(val_u64))
54    }
55}
56
57impl TryFrom<RawPtr> for usize {
58    type Error = HyperlightError;
59    #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")]
60    fn try_from(val: RawPtr) -> Result<usize> {
61        Ok(usize::try_from(val.0)?)
62    }
63}
64
65impl From<RawPtr> for u64 {
66    #[instrument(skip_all, parent = Span::current(), level= "Trace")]
67    fn from(val: RawPtr) -> u64 {
68        val.0
69    }
70}
71
72impl From<&RawPtr> for u64 {
73    #[instrument(skip_all, parent = Span::current(), level= "Trace")]
74    fn from(val: &RawPtr) -> u64 {
75        val.0
76    }
77}
78
79/// Convenience type for representing a pointer into the guest address space
80pub(crate) type GuestPtr = Ptr<GuestAddressSpace>;
81
82impl TryFrom<RawPtr> for GuestPtr {
83    type Error = HyperlightError;
84    /// Create a new `GuestPtr` from the given `guest_raw_ptr`, which must
85    /// be a pointer in the guest's address space.
86    #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")]
87    fn try_from(raw: RawPtr) -> Result<Self> {
88        GuestPtr::from_raw_ptr(GuestAddressSpace::new()?, raw)
89    }
90}
91
92impl TryFrom<Offset> for GuestPtr {
93    type Error = HyperlightError;
94    #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")]
95    fn try_from(val: Offset) -> Result<Self> {
96        let addr_space = GuestAddressSpace::new()?;
97        Ok(Ptr::from_offset(addr_space, val))
98    }
99}
100
101impl TryFrom<i64> for GuestPtr {
102    type Error = HyperlightError;
103    #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")]
104    fn try_from(val: i64) -> Result<Self> {
105        let offset = Offset::try_from(val)?;
106        GuestPtr::try_from(offset)
107    }
108}
109
110impl TryFrom<GuestPtr> for i64 {
111    type Error = HyperlightError;
112    #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")]
113    fn try_from(val: GuestPtr) -> Result<Self> {
114        let offset = val.offset();
115        i64::try_from(offset)
116    }
117}
118
119/// A pointer into a specific `AddressSpace` `T`.
120#[derive(Debug, Copy, Clone)]
121pub(crate) struct Ptr<T: AddressSpace> {
122    addr_space: T,
123    offset: Offset,
124}
125
126impl<T: AddressSpace> std::cmp::PartialEq for Ptr<T> {
127    #[instrument(skip_all, parent = Span::current(), level= "Trace")]
128    fn eq(&self, other: &Self) -> bool {
129        other.addr_space == self.addr_space && other.offset == self.offset
130    }
131}
132
133impl<T: AddressSpace> std::cmp::Eq for Ptr<T> {}
134#[instrument(skip_all, parent = Span::current(), level= "Trace")]
135fn cmp_helper<T: AddressSpace>(left: &Ptr<T>, right: &Ptr<T>) -> std::cmp::Ordering {
136    // We know both left and right have the same address space, thus
137    // they have the same base, so we can get away with just comparing
138    // the offsets and assume we're in the same address space, practically
139    // speaking.
140    left.offset.cmp(&right.offset)
141}
142
143#[allow(clippy::non_canonical_partial_ord_impl)]
144impl<T: AddressSpace> std::cmp::PartialOrd for Ptr<T> {
145    #[instrument(skip_all, parent = Span::current(), level= "Trace")]
146    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
147        Some(cmp_helper(self, other))
148    }
149}
150
151impl<T: AddressSpace> std::cmp::Ord for Ptr<T> {
152    #[instrument(skip_all, parent = Span::current(), level= "Trace")]
153    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
154        cmp_helper(self, other)
155    }
156}
157
158impl<T: AddressSpace> Ptr<T> {
159    /// Create a new pointer in the given `AddressSpace` `addr_space`
160    /// from the given pointer `raw_ptr`. Returns `Ok` if subtracting
161    /// the base address from `raw_ptr` succeeds (i.e. does not overflow)
162    /// and a `Ptr<T>` can be successfully created
163    #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")]
164    fn from_raw_ptr(addr_space: T, raw_ptr: RawPtr) -> Result<Ptr<T>> {
165        let offset = raw_ptr
166            .0
167            .checked_sub(addr_space.base())
168            .ok_or_else(|| RawPointerLessThanBaseAddress(raw_ptr, addr_space.base()))?;
169        Ok(Self {
170            addr_space,
171            offset: Offset::from(offset),
172        })
173    }
174
175    /// Create a new `Ptr` into the given `addr_space` from the given
176    /// `offset`.
177    #[instrument(skip_all, parent = Span::current(), level= "Trace")]
178    fn from_offset(addr_space: T, offset: Offset) -> Ptr<T> {
179        Self { addr_space, offset }
180    }
181
182    /// Get the base address for this pointer
183    #[instrument(skip_all, parent = Span::current(), level= "Trace")]
184    fn base(&self) -> u64 {
185        self.addr_space.base()
186    }
187
188    /// Get the offset into the pointer's address space
189    #[instrument(skip_all, parent = Span::current(), level= "Trace")]
190    pub(super) fn offset(&self) -> Offset {
191        self.offset
192    }
193
194    /// Get the absolute value for the pointer represented by `self`.
195    ///
196    /// This function should rarely be used. Prefer to use offsets
197    /// instead.
198    #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")]
199    pub(crate) fn absolute(&self) -> Result<u64> {
200        let offset_u64: u64 = self.offset.into();
201        self.base()
202            .checked_add(offset_u64)
203            .ok_or_else(|| CheckedAddOverflow(self.base(), offset_u64))
204    }
205}
206
207impl<T: AddressSpace> Add<Offset> for Ptr<T> {
208    type Output = Ptr<T>;
209    #[instrument(skip_all, parent = Span::current(), level= "Trace")]
210    fn add(self, rhs: Offset) -> Self::Output {
211        Self {
212            addr_space: self.addr_space,
213            offset: self.offset + rhs,
214        }
215    }
216}
217
218impl<T: AddressSpace> TryFrom<Ptr<T>> for usize {
219    type Error = HyperlightError;
220    #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")]
221    fn try_from(val: Ptr<T>) -> Result<usize> {
222        let abs = val.absolute()?;
223        Ok(usize::try_from(abs)?)
224    }
225}
226
227#[cfg(test)]
228mod tests {
229    use super::{GuestPtr, RawPtr};
230    use crate::mem::layout::SandboxMemoryLayout;
231    const OFFSET: u64 = 1;
232
233    #[test]
234    fn ptr_basic_ops() {
235        {
236            let raw_guest_ptr = RawPtr(OFFSET + SandboxMemoryLayout::BASE_ADDRESS as u64);
237            let guest_ptr = GuestPtr::try_from(raw_guest_ptr).unwrap();
238            assert_eq!(
239                OFFSET + SandboxMemoryLayout::BASE_ADDRESS as u64,
240                guest_ptr.absolute().unwrap()
241            );
242        }
243    }
244}