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    /// Create a new shared borrow guard.
21    ///
22    /// The caller must have already incremented `*state` to reflect
23    /// the new shared borrow.
24    #[inline(always)]
25    pub(crate) fn new(value: &'a T, state: *mut u8) -> Self {
26        Self { value, state }
27    }
28
29    /// Create a shared guard whose aliasing is enforced outside the native
30    /// account borrow byte.
31    ///
32    /// Runtime segment access uses this after `SegmentBorrowRegistry` has
33    /// leased the exact byte range. Drop must therefore avoid changing the
34    /// whole-account `borrow_state` byte.
35    #[inline(always)]
36    pub(crate) fn new_external(value: &'a T) -> Self {
37        Self {
38            value,
39            state: core::ptr::null_mut(),
40        }
41    }
42
43    /// Create a shared borrow guard from raw parts.
44    ///
45    /// # Safety
46    ///
47    /// The caller must ensure:
48    /// - The borrow state at `state` was already incremented
49    /// - `value` is valid for lifetime `'a`
50    /// - `state` points to a valid `RuntimeAccount.borrow_state`
51    #[inline(always)]
52    pub unsafe fn from_raw_parts(value: &'a T, state: *mut u8) -> Self {
53        Self { value, state }
54    }
55
56    /// Decompose into raw parts without running the destructor.
57    ///
58    /// The caller takes responsibility for eventually releasing the
59    /// borrow (decrementing `*state`).
60    #[inline(always)]
61    pub fn into_raw_parts(self) -> (&'a T, *mut u8) {
62        let value = self.value;
63        let state = self.state;
64        core::mem::forget(self);
65        (value, state)
66    }
67}
68
69impl<T: ?Sized> core::ops::Deref for Ref<'_, T> {
70    type Target = T;
71
72    #[inline(always)]
73    fn deref(&self) -> &T {
74        self.value
75    }
76}
77
78impl<T: ?Sized> Drop for Ref<'_, T> {
79    fn drop(&mut self) {
80        if self.state.is_null() {
81            return;
82        }
83        // SAFETY: state points to RuntimeAccount.borrow_state in the
84        // BPF input buffer. We decrement the shared borrow count,
85        // restoring NOT_BORROWED when the last shared borrow is released.
86        unsafe {
87            let current = *self.state;
88            if current == 1 {
89                *self.state = NOT_BORROWED;
90            } else {
91                *self.state = current - 1;
92            }
93        }
94    }
95}
96
97/// Exclusive (mutable) borrow guard for account data.
98///
99/// On drop, restores `RuntimeAccount.borrow_state` to `NOT_BORROWED`.
100pub struct RefMut<'a, T: ?Sized> {
101    value: &'a mut T,
102    state: *mut u8,
103}
104
105impl<'a, T: ?Sized> RefMut<'a, T> {
106    /// Create a new exclusive borrow guard.
107    ///
108    /// The caller must have already set `*state = 0` to indicate
109    /// exclusive borrow.
110    #[inline(always)]
111    pub(crate) fn new(value: &'a mut T, state: *mut u8) -> Self {
112        Self { value, state }
113    }
114
115    /// Create an exclusive guard whose aliasing is enforced by an external
116    /// segment lease rather than the whole-account borrow byte.
117    #[inline(always)]
118    pub(crate) fn new_external(value: &'a mut T) -> Self {
119        Self {
120            value,
121            state: core::ptr::null_mut(),
122        }
123    }
124
125    /// Create an exclusive borrow guard from raw parts.
126    ///
127    /// # Safety
128    ///
129    /// The caller must ensure:
130    /// - The borrow state at `state` was set to 0 (exclusive)
131    /// - `value` is valid and unique for lifetime `'a`
132    /// - `state` points to a valid `RuntimeAccount.borrow_state`
133    #[inline(always)]
134    pub unsafe fn from_raw_parts(value: &'a mut T, state: *mut u8) -> Self {
135        Self { value, state }
136    }
137
138    /// Decompose into raw parts without running the destructor.
139    ///
140    /// The caller takes responsibility for eventually releasing the
141    /// borrow (restoring `*state` to `NOT_BORROWED`).
142    #[inline(always)]
143    pub fn into_raw_parts(self) -> (&'a mut T, *mut u8) {
144        let manual = core::mem::ManuallyDrop::new(self);
145        // 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.
146        let value = unsafe { core::ptr::read(&manual.value) };
147        let state = manual.state;
148        (value, state)
149    }
150}
151
152impl<T: ?Sized> core::ops::Deref for RefMut<'_, T> {
153    type Target = T;
154
155    #[inline(always)]
156    fn deref(&self) -> &T {
157        self.value
158    }
159}
160
161impl<T: ?Sized> core::ops::DerefMut for RefMut<'_, T> {
162    #[inline(always)]
163    fn deref_mut(&mut self) -> &mut T {
164        self.value
165    }
166}
167
168impl<T: ?Sized> Drop for RefMut<'_, T> {
169    fn drop(&mut self) {
170        if self.state.is_null() {
171            return;
172        }
173        // SAFETY: state points to RuntimeAccount.borrow_state.
174        // Restore to NOT_BORROWED when the exclusive borrow is released.
175        unsafe {
176            *self.state = NOT_BORROWED;
177        }
178    }
179}