1use std::cell::Cell;
4use std::panic::{self, AssertUnwindSafe};
5use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
6use std::sync::Arc;
7
8pub struct BoundsChecker {
9 size: usize,
10 base: usize,
11}
12
13impl BoundsChecker {
14 pub fn new(base: *const u8, size: usize) -> Self {
15 Self {
16 size,
17 base: base as usize,
18 }
19 }
20
21 pub fn is_valid_offset(&self, offset: usize) -> bool {
22 offset < self.size
23 }
24
25 pub fn is_valid_pointer(&self, ptr: *const u8) -> bool {
26 let addr = ptr as usize;
27 addr >= self.base && addr < self.base + self.size
28 }
29
30 pub fn validate_offset(&self, offset: usize) -> Result<usize, BoundsError> {
31 if !self.is_valid_offset(offset) {
32 return Err(BoundsError::OutOfBounds {
33 offset,
34 size: self.size,
35 });
36 }
37 Ok(offset)
38 }
39
40 pub fn validate_range(
41 &self,
42 offset: usize,
43 len: usize,
44 ) -> Result<(), BoundsError> {
45 if offset >= self.size {
46 return Err(BoundsError::OutOfBounds {
47 offset,
48 size: self.size,
49 });
50 }
51
52 let end = offset.checked_add(len).ok_or(BoundsError::Overflow)?;
53 if end > self.size {
54 return Err(BoundsError::OutOfBounds {
55 offset: end,
56 size: self.size,
57 });
58 }
59
60 Ok(())
61 }
62
63 pub fn size(&self) -> usize {
64 self.size
65 }
66}
67
68#[derive(Debug, Clone, Copy, PartialEq, Eq)]
69pub enum BoundsError {
70 OutOfBounds { offset: usize, size: usize },
71 Overflow,
72}
73
74impl std::fmt::Display for BoundsError {
75 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
76 match self {
77 BoundsError::OutOfBounds { offset, size } => {
78 write!(f, "Offset {} is out of bounds (size {})", offset, size)
79 }
80 BoundsError::Overflow => write!(f, "Arithmetic overflow in offset calculation"),
81 }
82 }
83}
84
85impl std::error::Error for BoundsError {}
86
87pub struct PoisonGuard {
88 poisoned: Arc<AtomicBool>,
89 disarmed: Cell<bool>,
90}
91
92impl PoisonGuard {
93 pub fn new(poisoned: Arc<AtomicBool>) -> Self {
94 poisoned.store(true, Ordering::Release);
95 Self {
96 poisoned,
97 disarmed: Cell::new(false),
98 }
99 }
100
101 pub fn disarm(&self) {
102 self.disarmed.set(true);
103 self.poisoned.store(false, Ordering::Release);
104 }
105
106 pub fn is_poisoned(&self) -> bool {
107 self.poisoned.load(Ordering::Acquire)
108 }
109}
110
111impl Drop for PoisonGuard {
112 fn drop(&mut self) {
113 if !self.disarmed.get() {
114 }
115 }
116}
117
118pub struct PoisonState {
119 poisoned: Arc<AtomicBool>,
120}
121
122impl PoisonState {
123 pub fn new() -> Self {
124 Self {
125 poisoned: Arc::new(AtomicBool::new(false)),
126 }
127 }
128
129 pub fn is_poisoned(&self) -> bool {
130 self.poisoned.load(Ordering::Acquire)
131 }
132
133 pub fn set_poisoned(&self) {
134 self.poisoned.store(true, Ordering::Release);
135 }
136
137 pub fn clear_poisoned(&self) {
138 self.poisoned.store(false, Ordering::Release);
139 }
140
141 pub fn guard(&self) -> PoisonGuard {
142 PoisonGuard::new(Arc::clone(&self.poisoned))
143 }
144}
145
146impl Default for PoisonState {
147 fn default() -> Self {
148 Self::new()
149 }
150}
151
152pub fn with_panic_protection<T, F>(
153 poison: &PoisonState,
154 f: F,
155) -> Result<T, Box<dyn std::any::Any + Send>>
156where
157 F: FnOnce() -> T,
158{
159 let guard = poison.guard();
160 let result = panic::catch_unwind(AssertUnwindSafe(f));
161
162 match result {
163 Ok(val) => {
164 guard.disarm();
165 Ok(val)
166 }
167 Err(e) => {
168 poison.set_poisoned();
169 Err(e)
170 }
171 }
172}
173
174pub struct RobustFutex {
175 value: AtomicUsize,
176 poisoned: AtomicBool,
177}
178
179impl RobustFutex {
180 pub fn new(initial: usize) -> Self {
181 Self {
182 value: AtomicUsize::new(initial),
183 poisoned: AtomicBool::new(false),
184 }
185 }
186
187 pub fn try_lock(&self) -> Result<bool, FutexError> {
188 if self.poisoned.load(Ordering::Acquire) {
189 return Err(FutexError::Poisoned);
190 }
191
192 let current = self.value.load(Ordering::Acquire);
193 if current == 0 {
194 match self.value.compare_exchange(
195 0,
196 1,
197 Ordering::AcqRel,
198 Ordering::Acquire,
199 ) {
200 Ok(_) => Ok(true),
201 Err(_) => Ok(false),
202 }
203 } else {
204 Ok(false)
205 }
206 }
207
208 pub fn unlock(&self) {
209 self.value.store(0, Ordering::Release);
210 }
211
212 pub fn is_poisoned(&self) -> bool {
213 self.poisoned.load(Ordering::Acquire)
214 }
215
216 pub fn mark_poisoned(&self) {
217 self.poisoned.store(true, Ordering::Release);
218 }
219
220 pub fn clear_poisoned(&self) {
221 self.poisoned.store(false, Ordering::Release);
222 self.value.store(0, Ordering::Release);
223 }
224}
225
226#[derive(Debug, Clone, Copy, PartialEq, Eq)]
227pub enum FutexError {
228 Poisoned,
229 WouldBlock,
230 Other,
231}
232
233impl std::fmt::Display for FutexError {
234 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
235 match self {
236 FutexError::Poisoned => write!(f, "Futex is poisoned (owner died)"),
237 FutexError::WouldBlock => write!(f, "Operation would block"),
238 FutexError::Other => write!(f, "Futex error"),
239 }
240 }
241}
242
243impl std::error::Error for FutexError {}
244
245pub struct SafeShmAccess {
246 bounds: BoundsChecker,
247 poison: PoisonState,
248}
249
250impl SafeShmAccess {
251 pub fn new(base: *const u8, size: usize) -> Self {
252 Self {
253 bounds: BoundsChecker::new(base, size),
254 poison: PoisonState::new(),
255 }
256 }
257
258 pub fn is_poisoned(&self) -> bool {
259 self.poison.is_poisoned()
260 }
261
262 pub fn validate_offset(&self, offset: usize) -> Result<(), BoundsError> {
263 self.bounds.validate_offset(offset).map(|_| ())
264 }
265
266 pub fn validate_range(&self, offset: usize, len: usize) -> Result<(), BoundsError> {
267 self.bounds.validate_range(offset, len)
268 }
269
270 pub fn with_safe_access<T, F>(&self, offset: usize, len: usize, f: F) -> Result<T, SafeAccessError>
271 where
272 F: FnOnce() -> T,
273 {
274 self.bounds.validate_range(offset, len)?;
275
276 if self.poison.is_poisoned() {
277 return Err(SafeAccessError::Poisoned);
278 }
279
280 match with_panic_protection(&self.poison, f) {
281 Ok(val) => Ok(val),
282 Err(_) => Err(SafeAccessError::Panicked),
283 }
284 }
285
286 pub fn bounds(&self) -> &BoundsChecker {
287 &self.bounds
288 }
289
290 pub fn poison(&self) -> &PoisonState {
291 &self.poison
292 }
293}
294
295#[derive(Debug, Clone, Copy, PartialEq, Eq)]
296pub enum SafeAccessError {
297 OutOfBounds,
298 Poisoned,
299 Panicked,
300}
301
302impl std::fmt::Display for SafeAccessError {
303 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
304 match self {
305 SafeAccessError::OutOfBounds => write!(f, "Access out of bounds"),
306 SafeAccessError::Poisoned => write!(f, "Access is poisoned"),
307 SafeAccessError::Panicked => write!(f, "Operation panicked"),
308 }
309 }
310}
311
312impl std::error::Error for SafeAccessError {}
313
314impl From<BoundsError> for SafeAccessError {
315 fn from(_: BoundsError) -> Self {
316 SafeAccessError::OutOfBounds
317 }
318}