Skip to main content

subetha_pointers/
adaptive_cheri_pointer.rs

1//! `AdaptiveCheriPointer<T>` - CHERI-style capability pointer with
2//! software emulation for portability.
3//!
4//! CHERI (Capability Hardware Enhanced RISC Instructions) augments
5//! every pointer with bounds, permissions, otype, and a sealed bit,
6//! enforced by hardware on every dereference. ARM Morello is the
7//! production silicon as of 2026; iOS may adopt the model.
8//!
9//! CHERI is by design a RISC instruction-set extension. There is no
10//! equivalent capability ISA on x86 / x86_64. The x86-side equivalent
11//! is a Register-Aligned SIMD Pointer primitive: vector instructions
12//! express bounds + permission checks rather than emulating capability
13//! hardware that does not exist on the silicon.
14//!
15//! # Read vs Write: distinct types
16//!
17//! This module separates capability semantics at the type level
18//! rather than the runtime permission-bit level:
19//!
20//! - [`ReadableCapability<T>`] - bounds-checked read-only view of T.
21//!   Constructed from `&[T]` borrow. Compile-time guarantee: no
22//!   `write()` method exists. Permissions silently strip the Write
23//!   bit at construction (a ReadableCapability with Write perm is
24//!   nonsensical).
25//!
26//! - [`WritableCapability<T>`] - bounds-checked read+write access
27//!   to T. Constructed from `&mut [T]` borrow (or owned `Box<T>`).
28//!   `!Copy + !Clone` so the borrow checker prevents aliasing the
29//!   unique-writer status.
30//!
31//! - [`OwnedReadableCapability<T>`] / [`OwnedWritableCapability<T>`] -
32//!   RAII wrappers that own a `Box<T>` and reclaim it on `Drop`.
33//!   Use these when you want capability semantics over an owned
34//!   value without manual `Box::from_raw` cleanup.
35//!
36//! # Constructor matrix
37//!
38//! | Type                       | Safe constructor    | Unsafe constructor |
39//! |----------------------------|---------------------|--------------------|
40//! | ReadableCapability         | from_slice          | new (raw ptr)      |
41//! | WritableCapability         | from_slice_mut      | new (raw ptr)      |
42//! | OwnedReadableCapability    | new(value), from_box | -                 |
43//! | OwnedWritableCapability    | new(value), from_box | -                 |
44//!
45//! # Backend
46//!
47//! Bounds and permissions are checked in software on every target. The
48//! crate has no hardware CHERI backend and uses no Morello capability
49//! instructions.
50//!
51//! # Safety contract
52//!
53//! All bounds arithmetic uses `checked_add` so a near-overflow
54//! `base + length` cannot wrap and bypass the check.
55//!
56//! Adjacent capability-like hardware features on non-x86 silicon:
57//! ARM PAC (Pointer Authentication Codes), Apple Silicon MTE
58//! (Memory Tagging Extension), SPARC ADI (Application Data Integrity).
59
60use std::marker::PhantomData;
61
62/// Permission bits. Same shape across both Readable and Writable
63/// capabilities so narrow() can reason about them uniformly.
64#[repr(u32)]
65#[derive(Debug, Clone, Copy, PartialEq, Eq)]
66pub enum CapabilityPermission {
67    None    = 0,
68    Read    = 1 << 0,
69    Write   = 1 << 1,
70    Execute = 1 << 2,
71}
72
73#[derive(Debug, Clone, Copy, PartialEq, Eq)]
74pub enum CapabilityError {
75    OutOfBounds,
76    PermissionDenied,
77    Sealed,
78    AddressOverflow,
79}
80
81const SEALED_BIT: u32 = 1 << 31;
82const WRITE_BIT: u32 = CapabilityPermission::Write as u32;
83
84// =========================================================================
85// ReadableCapability<T> - bounds-checked read-only access.
86// =========================================================================
87
88/// Read-only bounds-checked capability. The Write permission bit is
89/// silently stripped at construction; no `write()` method exists.
90///
91/// Layout (24 bytes):
92/// ```text
93/// ptr:    *const T   (8 bytes) - base address
94/// base:   usize      (8 bytes) - lower bound (often equals ptr)
95/// length: u32        (4 bytes) - bytes from base
96/// perms:  u32        (4 bytes) - permission bitmask + sealed bit;
97///                                 Write bit guaranteed cleared
98/// ```
99#[derive(Debug, Clone, Copy)]
100#[repr(C)]
101pub struct ReadableCapability<T> {
102    ptr: *const T,
103    base: usize,
104    length: u32,
105    perms: u32,
106    _phantom: PhantomData<*const T>,
107}
108
109impl<T> ReadableCapability<T> {
110    /// Direction signature of `ReadableCapability<T>`. Engages the
111    /// `K_bounds` axis (runtime base / length / permissions stored
112    /// at slot for CHERI-style bounds enforcement on every deref).
113    pub const SIGNATURE: subetha_core::AxisMask = subetha_core::AxisMask::from_axes(
114        &[subetha_core::Axis::Bounds],
115    );
116
117    /// # Safety
118    ///
119    /// Caller guarantees `[base, base + length)` is valid memory for
120    /// the lifetime of this capability and `ptr` lies within that
121    /// region. The Write permission bit is silently stripped.
122    pub unsafe fn new(ptr: *const T, base: usize, length: u32, perms: u32)
123        -> Result<Self, CapabilityError>
124    {
125        let region_end = base.checked_add(length as usize)
126            .ok_or(CapabilityError::AddressOverflow)?;
127        let addr = ptr as usize;
128        if addr < base { return Err(CapabilityError::OutOfBounds); }
129        let access_end = addr.checked_add(std::mem::size_of::<T>())
130            .ok_or(CapabilityError::AddressOverflow)?;
131        if access_end > region_end {
132            return Err(CapabilityError::OutOfBounds);
133        }
134        Ok(Self {
135            ptr, base, length,
136            perms: perms & !WRITE_BIT,  // strip Write
137            _phantom: PhantomData,
138        })
139    }
140
141    /// Safe constructor: build a read-only capability over a
142    /// borrowed slice. Write bit silently stripped.
143    pub fn from_slice(slice: &[T], perms: u32)
144        -> (ReadableCapability<T>, &[T])
145    {
146        let ptr = slice.as_ptr();
147        let base = ptr as usize;
148        let length = std::mem::size_of_val(slice) as u32;
149        let cap = ReadableCapability {
150            ptr, base, length,
151            perms: perms & !WRITE_BIT,
152            _phantom: PhantomData,
153        };
154        (cap, slice)
155    }
156
157    #[inline]
158    pub fn has_permission(&self, p: CapabilityPermission) -> bool {
159        if self.is_sealed() { return false; }
160        // Write bit was stripped at construction; even if caller
161        // asks for Write, has_permission returns false.
162        (self.perms & p as u32) != 0
163    }
164
165    #[inline]
166    pub fn is_sealed(&self) -> bool { (self.perms & SEALED_BIT) != 0 }
167
168    pub fn sealed(mut self) -> Self {
169        self.perms |= SEALED_BIT;
170        self
171    }
172
173    pub fn unsealed(mut self) -> Self {
174        self.perms &= !SEALED_BIT;
175        self
176    }
177
178    /// Read the value through the capability.
179    pub fn read(&self) -> Result<T, CapabilityError>
180    where T: Copy,
181    {
182        if self.is_sealed() { return Err(CapabilityError::Sealed); }
183        if !self.has_permission(CapabilityPermission::Read) {
184            return Err(CapabilityError::PermissionDenied);
185        }
186        let addr = self.ptr as usize;
187        if addr < self.base { return Err(CapabilityError::OutOfBounds); }
188        let access_end = addr.checked_add(std::mem::size_of::<T>())
189            .ok_or(CapabilityError::AddressOverflow)?;
190        let region_end = self.base.checked_add(self.length as usize)
191            .ok_or(CapabilityError::AddressOverflow)?;
192        if access_end > region_end { return Err(CapabilityError::OutOfBounds); }
193        // SAFETY: bounds + permission + sealed checks above. Caller's
194        // constructor-time contract guarantees the underlying memory
195        // is live.
196        Ok(unsafe { std::ptr::read(self.ptr) })
197    }
198
199    /// Narrow this capability to a sub-range. Write bit is silently
200    /// stripped (Readable cannot grant Write).
201    pub fn narrow(&self, sub_base: usize, sub_length: u32, sub_perms: u32)
202        -> Result<Self, CapabilityError>
203    {
204        let sub_end = sub_base.checked_add(sub_length as usize)
205            .ok_or(CapabilityError::AddressOverflow)?;
206        let region_end = self.base.checked_add(self.length as usize)
207            .ok_or(CapabilityError::AddressOverflow)?;
208        if sub_base < self.base || sub_end > region_end {
209            return Err(CapabilityError::OutOfBounds);
210        }
211        let new_perms = (self.perms & sub_perms) & !SEALED_BIT & !WRITE_BIT;
212        Ok(Self {
213            ptr: sub_base as *const T,
214            base: sub_base,
215            length: sub_length,
216            perms: new_perms,
217            _phantom: PhantomData,
218        })
219    }
220}
221
222// =========================================================================
223// WritableCapability<T> - bounds-checked read+write access. !Copy/!Clone.
224// =========================================================================
225
226/// Read+Write bounds-checked capability. Not Copy/Clone so the
227/// borrow checker prevents aliasing the unique-writer status.
228///
229/// Constructed from `&mut [T]` or an owned `Box<T>` (via
230/// [`OwnedWritableCapability::from_box`]). The `&mut` borrow is the
231/// unique-writer guarantee; without it, multiple WritableCapability
232/// instances could simultaneously write the same region.
233#[derive(Debug)]
234#[repr(C)]
235pub struct WritableCapability<T> {
236    ptr: *mut T,
237    base: usize,
238    length: u32,
239    perms: u32,
240    _phantom: PhantomData<*mut T>,
241}
242
243impl<T> WritableCapability<T> {
244    /// Direction signature of `WritableCapability<T>`. Engages the
245    /// `K_bounds` axis (runtime base / length / permissions stored
246    /// at slot for CHERI-style bounds enforcement on every deref).
247    pub const SIGNATURE: subetha_core::AxisMask = subetha_core::AxisMask::from_axes(
248        &[subetha_core::Axis::Bounds],
249    );
250
251    /// # Safety
252    ///
253    /// Caller guarantees `[base, base + length)` is valid memory and
254    /// no other writer accesses the region while this capability is
255    /// alive.
256    pub unsafe fn new(ptr: *mut T, base: usize, length: u32, perms: u32)
257        -> Result<Self, CapabilityError>
258    {
259        let region_end = base.checked_add(length as usize)
260            .ok_or(CapabilityError::AddressOverflow)?;
261        let addr = ptr as usize;
262        if addr < base { return Err(CapabilityError::OutOfBounds); }
263        let access_end = addr.checked_add(std::mem::size_of::<T>())
264            .ok_or(CapabilityError::AddressOverflow)?;
265        if access_end > region_end {
266            return Err(CapabilityError::OutOfBounds);
267        }
268        Ok(Self { ptr, base, length, perms, _phantom: PhantomData })
269    }
270
271    /// Safe constructor: build a writable capability over a mutable
272    /// slice borrow. Grants Read + Write permissions.
273    pub fn from_slice_mut(slice: &mut [T])
274        -> (WritableCapability<T>, &mut [T])
275    {
276        let base = slice.as_ptr() as usize;
277        let length = std::mem::size_of_val(slice) as u32;
278        let ptr = slice.as_mut_ptr();
279        let perms = CapabilityPermission::Read as u32
280                  | CapabilityPermission::Write as u32;
281        let cap = WritableCapability {
282            ptr, base, length, perms, _phantom: PhantomData,
283        };
284        (cap, slice)
285    }
286
287    #[inline]
288    pub fn has_permission(&self, p: CapabilityPermission) -> bool {
289        if self.is_sealed() { return false; }
290        (self.perms & p as u32) != 0
291    }
292
293    #[inline]
294    pub fn is_sealed(&self) -> bool { (self.perms & SEALED_BIT) != 0 }
295
296    pub fn sealed(mut self) -> Self {
297        self.perms |= SEALED_BIT;
298        self
299    }
300
301    pub fn unsealed(mut self) -> Self {
302        self.perms &= !SEALED_BIT;
303        self
304    }
305
306    /// Read through the capability.
307    pub fn read(&self) -> Result<T, CapabilityError>
308    where T: Copy,
309    {
310        if self.is_sealed() { return Err(CapabilityError::Sealed); }
311        if !self.has_permission(CapabilityPermission::Read) {
312            return Err(CapabilityError::PermissionDenied);
313        }
314        let addr = self.ptr as usize;
315        if addr < self.base { return Err(CapabilityError::OutOfBounds); }
316        let access_end = addr.checked_add(std::mem::size_of::<T>())
317            .ok_or(CapabilityError::AddressOverflow)?;
318        let region_end = self.base.checked_add(self.length as usize)
319            .ok_or(CapabilityError::AddressOverflow)?;
320        if access_end > region_end { return Err(CapabilityError::OutOfBounds); }
321        // SAFETY: bounds + permission + sealed checks above.
322        Ok(unsafe { std::ptr::read(self.ptr) })
323    }
324
325    /// Write through the capability. The `&mut self` receiver + the
326    /// constructor's `&mut [T]` / Box-consuming nature provide the
327    /// unique-writer guarantee.
328    pub fn write(&mut self, value: T) -> Result<(), CapabilityError> {
329        if self.is_sealed() { return Err(CapabilityError::Sealed); }
330        if !self.has_permission(CapabilityPermission::Write) {
331            return Err(CapabilityError::PermissionDenied);
332        }
333        let addr = self.ptr as usize;
334        if addr < self.base { return Err(CapabilityError::OutOfBounds); }
335        let access_end = addr.checked_add(std::mem::size_of::<T>())
336            .ok_or(CapabilityError::AddressOverflow)?;
337        let region_end = self.base.checked_add(self.length as usize)
338            .ok_or(CapabilityError::AddressOverflow)?;
339        if access_end > region_end { return Err(CapabilityError::OutOfBounds); }
340        // SAFETY: bounds + permission + sealed checks above; unique
341        // writer guaranteed by &mut self + construction path.
342        unsafe { std::ptr::write(self.ptr, value); }
343        Ok(())
344    }
345
346    /// Narrow to a sub-range, returning a new WritableCapability.
347    /// To narrow to a read-only view, use `narrow_readable`.
348    pub fn narrow(&self, sub_base: usize, sub_length: u32, sub_perms: u32)
349        -> Result<Self, CapabilityError>
350    {
351        let sub_end = sub_base.checked_add(sub_length as usize)
352            .ok_or(CapabilityError::AddressOverflow)?;
353        let region_end = self.base.checked_add(self.length as usize)
354            .ok_or(CapabilityError::AddressOverflow)?;
355        if sub_base < self.base || sub_end > region_end {
356            return Err(CapabilityError::OutOfBounds);
357        }
358        let new_perms = (self.perms & sub_perms) & !SEALED_BIT;
359        Ok(Self {
360            ptr: sub_base as *mut T,
361            base: sub_base,
362            length: sub_length,
363            perms: new_perms,
364            _phantom: PhantomData,
365        })
366    }
367
368    /// Narrow to a read-only view. Returned ReadableCapability does
369    /// not have Write perm regardless of what `sub_perms` contains.
370    pub fn narrow_readable(&self, sub_base: usize, sub_length: u32, sub_perms: u32)
371        -> Result<ReadableCapability<T>, CapabilityError>
372    {
373        let sub_end = sub_base.checked_add(sub_length as usize)
374            .ok_or(CapabilityError::AddressOverflow)?;
375        let region_end = self.base.checked_add(self.length as usize)
376            .ok_or(CapabilityError::AddressOverflow)?;
377        if sub_base < self.base || sub_end > region_end {
378            return Err(CapabilityError::OutOfBounds);
379        }
380        let new_perms = (self.perms & sub_perms) & !SEALED_BIT & !WRITE_BIT;
381        Ok(ReadableCapability {
382            ptr: sub_base as *const T,
383            base: sub_base,
384            length: sub_length,
385            perms: new_perms,
386            _phantom: PhantomData,
387        })
388    }
389
390    /// Borrow this WritableCapability as a ReadableCapability view
391    /// (no Write perm, no transfer of ownership). The borrow checker
392    /// prevents the writable cap from being used while the readable
393    /// view is alive.
394    pub fn as_readable(&self) -> ReadableCapability<T> {
395        ReadableCapability {
396            ptr: self.ptr as *const T,
397            base: self.base,
398            length: self.length,
399            perms: self.perms & !WRITE_BIT,
400            _phantom: PhantomData,
401        }
402    }
403}
404
405// =========================================================================
406// OwnedReadableCapability<T> + OwnedWritableCapability<T> - RAII wrappers.
407// =========================================================================
408
409/// RAII wrapper around a [`ReadableCapability<T>`] that owns the
410/// underlying `Box<T>` allocation and reclaims it on `Drop`.
411pub struct OwnedReadableCapability<T> {
412    cap: ReadableCapability<T>,
413}
414
415impl<T> OwnedReadableCapability<T> {
416    /// Heap-allocate `value` and wrap it in a read-only capability.
417    pub fn new(value: T) -> Self {
418        Self::from_box(Box::new(value))
419    }
420
421    /// Wrap an existing `Box<T>`. The capability has Read permission
422    /// only (no Write).
423    pub fn from_box(b: Box<T>) -> Self {
424        let ptr = Box::into_raw(b) as *const T;
425        let base = ptr as usize;
426        let length = std::mem::size_of::<T>() as u32;
427        // SAFETY: Box::into_raw produces a valid, aligned pointer
428        // for size_of::<T>() bytes; base + length cannot overflow
429        // because the allocator would not yield such a region.
430        let cap = unsafe {
431            ReadableCapability::new(
432                ptr, base, length, CapabilityPermission::Read as u32,
433            )
434        }.expect("Box::into_raw region cannot fail bounds check");
435        Self { cap }
436    }
437
438    pub fn cap(&self) -> &ReadableCapability<T> { &self.cap }
439
440    /// Consume and reclaim the underlying Box.
441    pub fn into_box(self) -> Box<T> {
442        let raw = self.cap.ptr as *mut T;
443        std::mem::forget(self);
444        // SAFETY: raw was obtained from Box::into_raw in from_box.
445        unsafe { Box::from_raw(raw) }
446    }
447}
448
449impl<T> std::ops::Deref for OwnedReadableCapability<T> {
450    type Target = ReadableCapability<T>;
451    fn deref(&self) -> &Self::Target { &self.cap }
452}
453
454impl<T> Drop for OwnedReadableCapability<T> {
455    fn drop(&mut self) {
456        let raw = self.cap.ptr as *mut T;
457        // SAFETY: raw was obtained from Box::into_raw in from_box;
458        // Box::from_raw is the matching reclaim.
459        let _reclaimed = unsafe { Box::from_raw(raw) };
460    }
461}
462
463/// RAII wrapper around a [`WritableCapability<T>`] that owns the
464/// underlying `Box<T>` allocation and reclaims it on `Drop`.
465pub struct OwnedWritableCapability<T> {
466    cap: WritableCapability<T>,
467}
468
469impl<T> OwnedWritableCapability<T> {
470    /// Heap-allocate `value` and wrap it in a writable capability.
471    pub fn new(value: T) -> Self {
472        Self::from_box(Box::new(value))
473    }
474
475    /// Wrap an existing `Box<T>`. Full Read + Write perms.
476    pub fn from_box(b: Box<T>) -> Self {
477        let ptr = Box::into_raw(b);
478        let base = ptr as usize;
479        let length = std::mem::size_of::<T>() as u32;
480        let perms = CapabilityPermission::Read as u32
481                  | CapabilityPermission::Write as u32;
482        // SAFETY: Box::into_raw produces a valid, aligned pointer.
483        let cap = unsafe {
484            WritableCapability::new(ptr, base, length, perms)
485        }.expect("Box::into_raw region cannot fail bounds check");
486        Self { cap }
487    }
488
489    pub fn cap(&self) -> &WritableCapability<T> { &self.cap }
490    pub fn cap_mut(&mut self) -> &mut WritableCapability<T> { &mut self.cap }
491
492    /// Consume and reclaim the underlying Box.
493    pub fn into_box(self) -> Box<T> {
494        let raw = self.cap.ptr;
495        std::mem::forget(self);
496        // SAFETY: raw was obtained from Box::into_raw in from_box.
497        unsafe { Box::from_raw(raw) }
498    }
499}
500
501impl<T> std::ops::Deref for OwnedWritableCapability<T> {
502    type Target = WritableCapability<T>;
503    fn deref(&self) -> &Self::Target { &self.cap }
504}
505
506impl<T> std::ops::DerefMut for OwnedWritableCapability<T> {
507    fn deref_mut(&mut self) -> &mut Self::Target { &mut self.cap }
508}
509
510impl<T> Drop for OwnedWritableCapability<T> {
511    fn drop(&mut self) {
512        let raw = self.cap.ptr;
513        // SAFETY: raw was obtained from Box::into_raw in from_box.
514        let _reclaimed = unsafe { Box::from_raw(raw) };
515    }
516}
517
518#[cfg(test)]
519mod tests {
520    use super::*;
521
522    // ============== Layout ==============
523
524    #[test]
525    fn readable_layout_is_24_bytes() {
526        assert_eq!(std::mem::size_of::<ReadableCapability<u64>>(), 24);
527    }
528
529    #[test]
530    fn writable_layout_is_24_bytes() {
531        assert_eq!(std::mem::size_of::<WritableCapability<u64>>(), 24);
532    }
533
534    // ============== ReadableCapability ==============
535
536    #[test]
537    fn readable_from_slice_strips_write_bit() {
538        let storage: Vec<u64> = vec![42];
539        let (cap, _anchor) = ReadableCapability::from_slice(
540            &storage,
541            CapabilityPermission::Read as u32 | CapabilityPermission::Write as u32,
542        );
543        assert!(cap.has_permission(CapabilityPermission::Read));
544        // Write bit silently stripped at construction.
545        assert!(!cap.has_permission(CapabilityPermission::Write));
546    }
547
548    #[test]
549    fn readable_read_with_permission() {
550        let storage: Vec<u64> = vec![42];
551        let (cap, _anchor) = ReadableCapability::from_slice(
552            &storage, CapabilityPermission::Read as u32,
553        );
554        assert_eq!(cap.read().unwrap(), 42);
555    }
556
557    #[test]
558    fn readable_read_without_permission_fails() {
559        let storage: Vec<u64> = vec![99];
560        let (cap, _anchor) = ReadableCapability::from_slice(&storage, 0);
561        assert_eq!(cap.read().err(), Some(CapabilityError::PermissionDenied));
562    }
563
564    #[test]
565    fn readable_sealed_blocks_read() {
566        let storage: Vec<u64> = vec![1];
567        let (cap, _anchor) = ReadableCapability::from_slice(
568            &storage, CapabilityPermission::Read as u32,
569        );
570        let sealed = cap.sealed();
571        assert_eq!(sealed.read().err(), Some(CapabilityError::Sealed));
572    }
573
574    #[test]
575    fn readable_unseal_restores() {
576        let storage: Vec<u64> = vec![7];
577        let (cap, _anchor) = ReadableCapability::from_slice(
578            &storage, CapabilityPermission::Read as u32,
579        );
580        let unsealed = cap.sealed().unsealed();
581        assert_eq!(unsealed.read().unwrap(), 7);
582    }
583
584    #[test]
585    fn readable_narrow_strips_write() {
586        let storage: Vec<u64> = vec![0, 0, 0, 0];
587        let (cap, anchor) = ReadableCapability::from_slice(
588            &storage,
589            CapabilityPermission::Read as u32,
590        );
591        let base = anchor.as_ptr() as usize;
592        let narrowed = cap.narrow(
593            base, 16,
594            CapabilityPermission::Read as u32 | CapabilityPermission::Write as u32,
595        ).unwrap();
596        assert!(narrowed.has_permission(CapabilityPermission::Read));
597        assert!(!narrowed.has_permission(CapabilityPermission::Write));
598    }
599
600    #[test]
601    fn readable_unsafe_new_overflow_guards() {
602        let ptr = usize::MAX as *const u64;
603        let r = unsafe {
604            ReadableCapability::<u64>::new(
605                ptr, usize::MAX, 16, CapabilityPermission::Read as u32,
606            )
607        };
608        assert_eq!(r.err(), Some(CapabilityError::AddressOverflow));
609    }
610
611    // ============== WritableCapability ==============
612
613    #[test]
614    fn writable_from_slice_mut_grants_read_and_write() {
615        let mut storage: Vec<u64> = vec![0];
616        let (cap, _anchor) = WritableCapability::from_slice_mut(&mut storage);
617        assert!(cap.has_permission(CapabilityPermission::Read));
618        assert!(cap.has_permission(CapabilityPermission::Write));
619    }
620
621    #[test]
622    fn writable_write_then_read() {
623        let mut storage: Vec<u64> = vec![0];
624        {
625            let (mut cap, _anchor) = WritableCapability::from_slice_mut(&mut storage);
626            cap.write(7777).unwrap();
627            assert_eq!(cap.read().unwrap(), 7777);
628        }
629        assert_eq!(storage[0], 7777);
630    }
631
632    #[test]
633    fn writable_sealed_blocks_write() {
634        let mut storage: Vec<u64> = vec![0];
635        let (cap, _anchor) = WritableCapability::from_slice_mut(&mut storage);
636        let mut sealed = cap.sealed();
637        assert_eq!(sealed.write(99u64).err(), Some(CapabilityError::Sealed));
638    }
639
640    #[test]
641    fn writable_as_readable_view_strips_write() {
642        let mut storage: Vec<u64> = vec![42];
643        let (cap, _anchor) = WritableCapability::from_slice_mut(&mut storage);
644        let read_view = cap.as_readable();
645        assert!(read_view.has_permission(CapabilityPermission::Read));
646        assert!(!read_view.has_permission(CapabilityPermission::Write));
647        assert_eq!(read_view.read().unwrap(), 42);
648    }
649
650    #[test]
651    fn writable_narrow_readable_strips_write() {
652        let mut storage: Vec<u64> = vec![0, 0];
653        let (cap, _anchor) = WritableCapability::from_slice_mut(&mut storage);
654        let base = cap.ptr as usize;
655        let narrowed: ReadableCapability<u64> = cap.narrow_readable(
656            base, 8,
657            CapabilityPermission::Read as u32 | CapabilityPermission::Write as u32,
658        ).unwrap();
659        assert!(narrowed.has_permission(CapabilityPermission::Read));
660        assert!(!narrowed.has_permission(CapabilityPermission::Write));
661    }
662
663    #[test]
664    fn writable_unsafe_new_overflow_guards() {
665        let ptr = usize::MAX as *mut u64;
666        let r = unsafe {
667            WritableCapability::<u64>::new(
668                ptr, usize::MAX, 16,
669                CapabilityPermission::Read as u32 | CapabilityPermission::Write as u32,
670            )
671        };
672        assert_eq!(r.err(), Some(CapabilityError::AddressOverflow));
673    }
674
675    // ============== OwnedReadableCapability ==============
676
677    #[test]
678    fn owned_readable_new_and_read() {
679        let owned = OwnedReadableCapability::new(42u64);
680        assert_eq!(owned.read().unwrap(), 42);
681    }
682
683    #[test]
684    fn owned_readable_drop_reclaims() {
685        use std::sync::atomic::{AtomicUsize, Ordering};
686        static DROPS: AtomicUsize = AtomicUsize::new(0);
687        struct DropCounter;
688        impl Drop for DropCounter {
689            fn drop(&mut self) { DROPS.fetch_add(1, Ordering::Relaxed); }
690        }
691        let before = DROPS.load(Ordering::Relaxed);
692        { let _o = OwnedReadableCapability::new(DropCounter); }
693        assert_eq!(DROPS.load(Ordering::Relaxed), before + 1);
694    }
695
696    // ============== OwnedWritableCapability ==============
697
698    #[test]
699    fn owned_writable_write_then_read() {
700        let mut owned = OwnedWritableCapability::new(0u64);
701        owned.cap_mut().write(555).unwrap();
702        assert_eq!(owned.read().unwrap(), 555);
703    }
704
705    #[test]
706    fn owned_writable_drop_reclaims() {
707        use std::sync::atomic::{AtomicUsize, Ordering};
708        static DROPS: AtomicUsize = AtomicUsize::new(0);
709        struct DropCounter;
710        impl Drop for DropCounter {
711            fn drop(&mut self) { DROPS.fetch_add(1, Ordering::Relaxed); }
712        }
713        let before = DROPS.load(Ordering::Relaxed);
714        { let _o = OwnedWritableCapability::new(DropCounter); }
715        assert_eq!(DROPS.load(Ordering::Relaxed), before + 1);
716    }
717
718    #[test]
719    fn owned_writable_into_box_suppresses_drop() {
720        use std::sync::atomic::{AtomicUsize, Ordering};
721        static DROPS: AtomicUsize = AtomicUsize::new(0);
722        struct DropCounter(u32);
723        impl Drop for DropCounter {
724            fn drop(&mut self) { DROPS.fetch_add(1, Ordering::Relaxed); }
725        }
726        let before = DROPS.load(Ordering::Relaxed);
727        let owned = OwnedWritableCapability::new(DropCounter(99));
728        let b = owned.into_box();
729        // into_box must not have fired Drop on the wrapper.
730        assert_eq!(DROPS.load(Ordering::Relaxed), before);
731        assert_eq!(b.0, 99);
732        drop(b);
733        // The returned Box drops normally, firing DropCounter once.
734        assert_eq!(DROPS.load(Ordering::Relaxed), before + 1);
735    }
736
737    #[test]
738    fn owned_writable_into_box_round_trip_value() {
739        let owned = OwnedWritableCapability::new(12345u64);
740        let b = owned.into_box();
741        assert_eq!(*b, 12345);
742    }
743}