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}