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
110impl<T: ?Sized> Drop for Ref<'_, T> {
111    fn drop(&mut self) {
112        if self.state.is_null() {
113            return;
114        }
115        // SAFETY: state points to RuntimeAccount.borrow_state in the
116        // BPF input buffer. We decrement the shared borrow count,
117        // restoring NOT_BORROWED when the last shared borrow is released.
118        unsafe {
119            let current = *self.state;
120            if current == 1 {
121                *self.state = NOT_BORROWED;
122            } else {
123                *self.state = current - 1;
124            }
125        }
126    }
127}
128
129/// Exclusive (mutable) borrow guard for account data.
130///
131/// On drop, restores `RuntimeAccount.borrow_state` to `NOT_BORROWED`.
132pub struct RefMut<'a, T: ?Sized> {
133    value: &'a mut T,
134    state: *mut u8,
135}
136
137impl<'a, T: ?Sized> RefMut<'a, T> {
138    /// Narrow an exclusive guard without releasing its account borrow.
139    #[inline]
140    pub fn map<U: ?Sized>(orig: Self, f: impl FnOnce(&mut T) -> &mut U) -> RefMut<'a, U> {
141        match Self::try_map(orig, |value| Ok::<_, core::convert::Infallible>(f(value))) {
142            Ok(mapped) => mapped,
143            Err((_, never)) => match never {},
144        }
145    }
146
147    /// Narrow a guard, preserving the original guard on an error. A closure
148    /// may itself mutate data before returning an error; those edits are retained.
149    #[inline]
150    pub fn try_map<U: ?Sized, E>(
151        orig: Self,
152        f: impl FnOnce(&mut T) -> Result<&mut U, E>,
153    ) -> Result<RefMut<'a, U>, (Self, E)> {
154        // Move the original exclusive reference before deriving the projection.
155        // Moving it afterwards would retag the parent and invalidate the child
156        // pointer under Stacked Borrows. Keep unwind release separate from that
157        // reference so a panicking closure does not strand the account lease.
158        struct ReleaseOnUnwind(*mut u8);
159        impl Drop for ReleaseOnUnwind {
160            fn drop(&mut self) {
161                if !self.0.is_null() {
162                    // SAFETY: this guard owns the original exclusive lease;
163                    // its account header outlives the mapping call.
164                    unsafe { *self.0 = crate::NOT_BORROWED };
165                }
166            }
167        }
168        let mut orig = core::mem::ManuallyDrop::new(orig);
169        let state = orig.state;
170        let unwind = ReleaseOnUnwind(state);
171        match f(&mut **orig) {
172            Ok(value) => {
173                let ptr = value as *mut U;
174                core::mem::forget(unwind);
175                // SAFETY: the closure's reference is derived from the original
176                // guard or is independently valid for that borrow. The original
177                // guard is consumed without releasing its exclusive lease; the
178                // new guard owns that same lease for the original lifetime.
179                Ok(RefMut {
180                    // SAFETY: `ptr` retains the exclusive lease described above.
181                    value: unsafe { &mut *ptr },
182                    state,
183                })
184            }
185            Err(error) => {
186                core::mem::forget(unwind);
187                Err((core::mem::ManuallyDrop::into_inner(orig), error))
188            }
189        }
190    }
191
192    /// Narrow a guard, returning the original guard if the field is absent.
193    #[inline]
194    pub fn filter_map<U: ?Sized>(
195        orig: Self,
196        f: impl FnOnce(&mut T) -> Option<&mut U>,
197    ) -> Result<RefMut<'a, U>, Self> {
198        Self::try_map(orig, |value| f(value).ok_or(())).map_err(|(orig, ())| orig)
199    }
200
201    /// Create a new exclusive borrow guard.
202    ///
203    /// The caller must have already set `*state = 0` to indicate
204    /// exclusive borrow.
205    #[inline(always)]
206    pub(crate) fn new(value: &'a mut T, state: *mut u8) -> Self {
207        Self { value, state }
208    }
209
210    /// Create an exclusive guard whose aliasing is enforced by an external
211    /// segment lease rather than the whole-account borrow byte.
212    #[inline(always)]
213    pub(crate) fn new_external(value: &'a mut T) -> Self {
214        Self {
215            value,
216            state: core::ptr::null_mut(),
217        }
218    }
219
220    /// Create an exclusive borrow guard from raw parts.
221    ///
222    /// # Safety
223    ///
224    /// The caller must ensure:
225    /// - The borrow state at `state` was set to 0 (exclusive)
226    /// - `value` is valid and unique for lifetime `'a`
227    /// - `state` points to a valid `RuntimeAccount.borrow_state`
228    #[inline(always)]
229    pub unsafe fn from_raw_parts(value: &'a mut T, state: *mut u8) -> Self {
230        Self { value, state }
231    }
232
233    /// Decompose into raw parts without running the destructor.
234    ///
235    /// The caller takes responsibility for eventually releasing the
236    /// borrow (restoring `*state` to `NOT_BORROWED`).
237    #[inline(always)]
238    pub fn into_raw_parts(self) -> (&'a mut T, *mut u8) {
239        let manual = core::mem::ManuallyDrop::new(self);
240        // SAFETY: This block is part of Hopper's reviewed zero-copy/backend boundary; surrounding checks and caller contracts uphold the required raw-pointer, layout, and aliasing invariants.
241        let value = unsafe { core::ptr::read(&manual.value) };
242        let state = manual.state;
243        (value, state)
244    }
245}
246
247impl<T: ?Sized> core::ops::Deref for RefMut<'_, T> {
248    type Target = T;
249
250    #[inline(always)]
251    fn deref(&self) -> &T {
252        self.value
253    }
254}
255
256impl<T: ?Sized> core::ops::DerefMut for RefMut<'_, T> {
257    #[inline(always)]
258    fn deref_mut(&mut self) -> &mut T {
259        self.value
260    }
261}
262
263impl<T: ?Sized> Drop for RefMut<'_, T> {
264    fn drop(&mut self) {
265        if self.state.is_null() {
266            return;
267        }
268        // SAFETY: state points to RuntimeAccount.borrow_state.
269        // Restore to NOT_BORROWED when the exclusive borrow is released.
270        unsafe {
271            *self.state = NOT_BORROWED;
272        }
273    }
274}