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}