Skip to main content

hopper_native/
borrow.rs

1//! Deterministic borrow guards for account data.
2//!
3//! `Ref` and `RefMut` provide RAII borrow tracking on the `borrow_state`
4//! field of `RuntimeAccount`. When dropped, they restore the borrow
5//! state, preventing use-after-free and double-mutable-borrow bugs.
6//!
7//! These replace `core::cell::RefCell` without requiring alloc.
8
9use crate::NOT_BORROWED;
10
11/// Shared (immutable) borrow guard for account data.
12///
13/// On drop, decrements the borrow count in `RuntimeAccount.borrow_state`.
14pub struct Ref<'a, T: ?Sized> {
15    value: &'a T,
16    state: *mut u8,
17}
18
19impl<'a, T: ?Sized> Ref<'a, T> {
20    /// Narrow a guard to a field or slice without releasing its account borrow.
21    #[inline]
22    pub fn map<U: ?Sized>(orig: Self, f: impl FnOnce(&T) -> &U) -> Ref<'a, U> {
23        let value = f(orig.value);
24        let (_, state) = orig.into_raw_parts();
25        Ref { value, state }
26    }
27
28    /// Narrow a guard, returning the original guard and error on failure.
29    #[inline]
30    pub fn try_map<U: ?Sized, E>(
31        orig: Self,
32        f: impl FnOnce(&T) -> Result<&U, E>,
33    ) -> Result<Ref<'a, U>, (Self, E)> {
34        match f(orig.value) {
35            Ok(value) => {
36                let (_, state) = orig.into_raw_parts();
37                Ok(Ref { value, state })
38            }
39            Err(error) => Err((orig, error)),
40        }
41    }
42
43    /// Narrow a guard, returning the original guard if the field is absent.
44    #[inline]
45    pub fn filter_map<U: ?Sized>(
46        orig: Self,
47        f: impl FnOnce(&T) -> Option<&U>,
48    ) -> Result<Ref<'a, U>, Self> {
49        Self::try_map(orig, |value| f(value).ok_or(())).map_err(|(orig, ())| orig)
50    }
51
52    /// Create a new shared borrow guard.
53    ///
54    /// The caller must have already incremented `*state` to reflect
55    /// the new shared borrow.
56    #[inline(always)]
57    pub(crate) fn new(value: &'a T, state: *mut u8) -> Self {
58        Self { value, state }
59    }
60
61    /// Create a shared guard whose aliasing is enforced outside the native
62    /// account borrow byte.
63    ///
64    /// Runtime segment access uses this after `SegmentBorrowRegistry` has
65    /// leased the exact byte range. Drop must therefore avoid changing the
66    /// whole-account `borrow_state` byte.
67    #[inline(always)]
68    pub(crate) fn new_external(value: &'a T) -> Self {
69        Self {
70            value,
71            state: core::ptr::null_mut(),
72        }
73    }
74
75    /// Create a shared borrow guard from raw parts.
76    ///
77    /// # Safety
78    ///
79    /// The caller must ensure:
80    /// - The borrow state at `state` was already incremented
81    /// - `value` is valid for lifetime `'a`
82    /// - `state` points to a valid `RuntimeAccount.borrow_state`
83    #[inline(always)]
84    pub unsafe fn from_raw_parts(value: &'a T, state: *mut u8) -> Self {
85        Self { value, state }
86    }
87
88    /// Decompose into raw parts without running the destructor.
89    ///
90    /// The caller takes responsibility for eventually releasing the
91    /// borrow (decrementing `*state`).
92    #[inline(always)]
93    pub fn into_raw_parts(self) -> (&'a T, *mut u8) {
94        let value = self.value;
95        let state = self.state;
96        core::mem::forget(self);
97        (value, state)
98    }
99}
100
101impl<T: ?Sized> core::ops::Deref for Ref<'_, T> {
102    type Target = T;
103
104    #[inline(always)]
105    fn deref(&self) -> &T {
106        self.value
107    }
108}
109
110/// Prints the value the guard points at, as `core::cell::Ref` does.
111impl<T: ?Sized + core::fmt::Debug> core::fmt::Debug for Ref<'_, T> {
112    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
113        core::fmt::Debug::fmt(&**self, f)
114    }
115}
116
117impl<T: ?Sized> Drop for Ref<'_, T> {
118    fn drop(&mut self) {
119        if self.state.is_null() {
120            return;
121        }
122        // SAFETY: state points to RuntimeAccount.borrow_state in the
123        // BPF input buffer. We decrement the shared borrow count,
124        // restoring NOT_BORROWED when the last shared borrow is released.
125        unsafe {
126            let current = *self.state;
127            if current == 1 {
128                *self.state = NOT_BORROWED;
129            } else {
130                *self.state = current - 1;
131            }
132        }
133    }
134}
135
136/// Exclusive (mutable) borrow guard for account data.
137///
138/// On drop, restores `RuntimeAccount.borrow_state` to `NOT_BORROWED`.
139pub struct RefMut<'a, T: ?Sized> {
140    value: &'a mut T,
141    state: *mut u8,
142}
143
144impl<'a, T: ?Sized> RefMut<'a, T> {
145    /// Narrow an exclusive guard without releasing its account borrow.
146    #[inline]
147    pub fn map<U: ?Sized>(orig: Self, f: impl FnOnce(&mut T) -> &mut U) -> RefMut<'a, U> {
148        match Self::try_map(orig, |value| Ok::<_, core::convert::Infallible>(f(value))) {
149            Ok(mapped) => mapped,
150            Err((_, never)) => match never {},
151        }
152    }
153
154    /// Narrow a guard, preserving the original guard on an error. A closure
155    /// may itself mutate data before returning an error; those edits are retained.
156    #[inline]
157    pub fn try_map<U: ?Sized, E>(
158        orig: Self,
159        f: impl FnOnce(&mut T) -> Result<&mut U, E>,
160    ) -> Result<RefMut<'a, U>, (Self, E)> {
161        // Move the original exclusive reference before deriving the projection.
162        // Moving it afterwards would retag the parent and invalidate the child
163        // pointer under Stacked Borrows. Keep unwind release separate from that
164        // reference so a panicking closure does not strand the account lease.
165        struct ReleaseOnUnwind(*mut u8);
166        impl Drop for ReleaseOnUnwind {
167            fn drop(&mut self) {
168                if !self.0.is_null() {
169                    // SAFETY: this guard owns the original exclusive lease;
170                    // its account header outlives the mapping call.
171                    unsafe { *self.0 = crate::NOT_BORROWED };
172                }
173            }
174        }
175        let mut orig = core::mem::ManuallyDrop::new(orig);
176        let state = orig.state;
177        let unwind = ReleaseOnUnwind(state);
178        match f(&mut **orig) {
179            Ok(value) => {
180                let ptr = value as *mut U;
181                core::mem::forget(unwind);
182                // SAFETY: the closure's reference is derived from the original
183                // guard or is independently valid for that borrow. The original
184                // guard is consumed without releasing its exclusive lease; the
185                // new guard owns that same lease for the original lifetime.
186                Ok(RefMut {
187                    // SAFETY: `ptr` retains the exclusive lease described above.
188                    value: unsafe { &mut *ptr },
189                    state,
190                })
191            }
192            Err(error) => {
193                core::mem::forget(unwind);
194                Err((core::mem::ManuallyDrop::into_inner(orig), error))
195            }
196        }
197    }
198
199    /// Narrow a guard, returning the original guard if the field is absent.
200    #[inline]
201    pub fn filter_map<U: ?Sized>(
202        orig: Self,
203        f: impl FnOnce(&mut T) -> Option<&mut U>,
204    ) -> Result<RefMut<'a, U>, Self> {
205        Self::try_map(orig, |value| f(value).ok_or(())).map_err(|(orig, ())| orig)
206    }
207
208    /// Create a new exclusive borrow guard.
209    ///
210    /// The caller must have already set `*state = 0` to indicate
211    /// exclusive borrow.
212    #[inline(always)]
213    pub(crate) fn new(value: &'a mut T, state: *mut u8) -> Self {
214        Self { value, state }
215    }
216
217    /// Create an exclusive guard whose aliasing is enforced by an external
218    /// segment lease rather than the whole-account borrow byte.
219    #[inline(always)]
220    pub(crate) fn new_external(value: &'a mut T) -> Self {
221        Self {
222            value,
223            state: core::ptr::null_mut(),
224        }
225    }
226
227    /// Create an exclusive borrow guard from raw parts.
228    ///
229    /// # Safety
230    ///
231    /// The caller must ensure:
232    /// - The borrow state at `state` was set to 0 (exclusive)
233    /// - `value` is valid and unique for lifetime `'a`
234    /// - `state` points to a valid `RuntimeAccount.borrow_state`
235    #[inline(always)]
236    pub unsafe fn from_raw_parts(value: &'a mut T, state: *mut u8) -> Self {
237        Self { value, state }
238    }
239
240    /// Decompose into raw parts without running the destructor.
241    ///
242    /// The caller takes responsibility for eventually releasing the
243    /// borrow (restoring `*state` to `NOT_BORROWED`).
244    #[inline(always)]
245    pub fn into_raw_parts(self) -> (&'a mut T, *mut u8) {
246        let manual = core::mem::ManuallyDrop::new(self);
247        // SAFETY: `manual` is never dropped, so the `&mut T` moved out by
248        // `read` is the only copy that is ever used; the borrow state it
249        // guards is handed to the caller with it.
250        let value = unsafe { core::ptr::read(&manual.value) };
251        let state = manual.state;
252        (value, state)
253    }
254}
255
256impl<T: ?Sized> core::ops::Deref for RefMut<'_, T> {
257    type Target = T;
258
259    #[inline(always)]
260    fn deref(&self) -> &T {
261        self.value
262    }
263}
264
265impl<T: ?Sized> core::ops::DerefMut for RefMut<'_, T> {
266    #[inline(always)]
267    fn deref_mut(&mut self) -> &mut T {
268        self.value
269    }
270}
271
272/// Prints the value the guard points at, as `core::cell::RefMut` does.
273impl<T: ?Sized + core::fmt::Debug> core::fmt::Debug for RefMut<'_, T> {
274    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
275        core::fmt::Debug::fmt(&**self, f)
276    }
277}
278
279impl<T: ?Sized> Drop for RefMut<'_, T> {
280    fn drop(&mut self) {
281        if self.state.is_null() {
282            return;
283        }
284        // SAFETY: state points to RuntimeAccount.borrow_state.
285        // Restore to NOT_BORROWED when the exclusive borrow is released.
286        unsafe {
287            *self.state = NOT_BORROWED;
288        }
289    }
290}