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