native-ipc 0.3.0

Secure-by-construction foundations for native cross-process IPC
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
//! Common lifecycle and policy interface for the best native shared-memory backend.

use core::fmt;
use core::sync::atomic::{Ordering, compiler_fence};

/// Native backend selected for the compilation target.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum NativePlatform {
    /// Linux sealed anonymous `memfd` mappings.
    Linux,
    /// macOS Mach VM memory-entry mappings.
    MacOs,
    /// Windows unnamed paging-file section mappings.
    Windows,
}

/// Processor architecture selected for the native backend.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum NativeArchitecture {
    /// 64-bit Arm (Rust `aarch64`) architecture.
    Arm64,
    /// 64-bit x86 (Rust `x86_64`) architecture.
    Amd64,
}

/// Kernel mechanism that freezes shared-memory authority before transfer.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum AuthorityMechanism {
    /// Linux descriptor seals plus a read-only imported mapping.
    DescriptorSeals,
    /// Mach memory-entry maximum protections.
    MaximumPortRights,
    /// Windows exact-rights duplicated section handles.
    ExactHandleRights,
}

/// Cross-platform capabilities of the selected native backend.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct NativeMemoryCapabilities {
    platform: NativePlatform,
    architecture: NativeArchitecture,
    authority: AuthorityMechanism,
}

impl NativeMemoryCapabilities {
    /// Selected operating-system backend.
    pub const fn platform(self) -> NativePlatform {
        self.platform
    }

    /// Selected processor architecture.
    pub const fn architecture(self) -> NativeArchitecture {
        self.architecture
    }

    /// Native mechanism used to freeze or attenuate authority.
    pub const fn authority_mechanism(self) -> AuthorityMechanism {
        self.authority
    }

    /// Whether a private region can grow by allocating a replacement mapping.
    pub const fn supports_replacement_growth(self) -> bool {
        true
    }

    /// Whether a shared mapping can grow in place.
    pub const fn supports_in_place_growth(self) -> bool {
        false
    }

    /// Whether permission changes are accepted after sharing.
    pub const fn supports_post_share_permission_changes(self) -> bool {
        false
    }

    /// Whether dropping all owners automatically releases the anonymous object.
    pub const fn releases_on_drop(self) -> bool {
        true
    }
}

/// Reports the native memory behavior selected for this target.
pub const fn native_memory_capabilities() -> NativeMemoryCapabilities {
    NativeMemoryCapabilities {
        platform: native_platform(),
        architecture: native_architecture(),
        authority: authority_mechanism(),
    }
}

/// Endpoint that will retain the sole writable mapping after transfer.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum WriterOwner {
    /// The process creating and transferring the region remains the writer.
    Creator,
    /// The authenticated peer becomes the sole writer.
    Peer,
}

/// Planned access for one endpoint after the sharing transition.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum MemoryAccess {
    /// Mapping may load but cannot store.
    ReadOnly,
    /// Mapping is the sole store-capable view.
    ReadWrite,
}

/// Exact creator/peer access requested for the future sharing transition.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct PermissionPlan {
    writer: WriterOwner,
}

impl PermissionPlan {
    /// Creates the only supported permission plan: one writer and one reader.
    pub const fn new(writer: WriterOwner) -> Self {
        Self { writer }
    }

    /// Endpoint selected as sole writer.
    pub const fn writer(self) -> WriterOwner {
        self.writer
    }

    /// Creator's access after authenticated transfer.
    pub const fn creator_access(self) -> MemoryAccess {
        match self.writer {
            WriterOwner::Creator => MemoryAccess::ReadWrite,
            WriterOwner::Peer => MemoryAccess::ReadOnly,
        }
    }

    /// Peer's access after authenticated transfer.
    pub const fn peer_access(self) -> MemoryAccess {
        match self.writer {
            WriterOwner::Creator => MemoryAccess::ReadOnly,
            WriterOwner::Peer => MemoryAccess::ReadWrite,
        }
    }

    /// Shared executable mappings are never permitted.
    pub const fn executable(self) -> bool {
        false
    }
}

/// Capacity policy while a region is still private and quiescent.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum GrowthPolicy {
    /// Mapping size is fixed at allocation.
    Fixed,
    /// Growth replaces the private mapping up to an inclusive logical limit.
    ReplaceBeforeShare {
        /// Maximum logical byte length.
        maximum_len: usize,
    },
}

/// Cleanup policy applied while the common wrapper still owns the region.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum CleanupPolicy {
    /// Release the anonymous native object without an explicit clearing pass.
    ReleaseOnDrop,
    /// Clear the complete mapping before releasing it.
    ClearThenRelease,
}

/// Mandatory authority-sealing policy for shared regions.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum SealPolicy {
    /// Seal or attenuate native authority during the consuming share transition.
    RequiredOnShare,
}

/// Immutable configuration for one native shared-memory region.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct RegionOptions {
    logical_len: usize,
    growth: GrowthPolicy,
    cleanup: CleanupPolicy,
    permissions: PermissionPlan,
    seal: SealPolicy,
}

impl RegionOptions {
    /// Creates a fixed-size region with clear-on-drop cleanup.
    pub const fn fixed(logical_len: usize, writer: WriterOwner) -> Self {
        Self {
            logical_len,
            growth: GrowthPolicy::Fixed,
            cleanup: CleanupPolicy::ClearThenRelease,
            permissions: PermissionPlan::new(writer),
            seal: SealPolicy::RequiredOnShare,
        }
    }

    /// Creates a private growable region with clear-on-drop cleanup.
    ///
    /// Growth allocates a replacement mapping. It is never available after
    /// the region is consumed for native sharing.
    pub const fn growable(logical_len: usize, maximum_len: usize, writer: WriterOwner) -> Self {
        Self {
            logical_len,
            growth: GrowthPolicy::ReplaceBeforeShare { maximum_len },
            cleanup: CleanupPolicy::ClearThenRelease,
            permissions: PermissionPlan::new(writer),
            seal: SealPolicy::RequiredOnShare,
        }
    }

    /// Overrides cleanup behavior before the native sharing transition.
    pub const fn with_cleanup(mut self, cleanup: CleanupPolicy) -> Self {
        self.cleanup = cleanup;
        self
    }

    /// Initial logical bytes requested by the caller.
    pub const fn logical_len(self) -> usize {
        self.logical_len
    }

    /// Private growth policy.
    pub const fn growth(self) -> GrowthPolicy {
        self.growth
    }

    /// Pre-transfer cleanup policy.
    pub const fn cleanup(self) -> CleanupPolicy {
        self.cleanup
    }

    /// Required one-writer permission plan.
    pub const fn permissions(self) -> PermissionPlan {
        self.permissions
    }

    /// Mandatory native authority-sealing behavior.
    pub const fn seal(self) -> SealPolicy {
        self.seal
    }

    /// Inclusive logical capacity limit.
    pub const fn maximum_len(self) -> usize {
        match self.growth {
            GrowthPolicy::Fixed => self.logical_len,
            GrowthPolicy::ReplaceBeforeShare { maximum_len } => maximum_len,
        }
    }
}

/// Current common lifecycle state.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum RegionState {
    /// Region is private, writable, unshared, and not yet authority-sealed.
    Quiescent,
}

/// Snapshot of a managed region's portable state and policy.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct RegionStatus {
    /// Current lifecycle state.
    pub state: RegionState,
    /// Logical application-visible length.
    pub logical_len: usize,
    /// Native page-rounded mapping length.
    pub mapped_len: usize,
    /// Maximum pre-share logical length.
    pub maximum_len: usize,
    /// Whether another pre-share growth operation is possible.
    pub can_grow: bool,
    /// Future creator/peer permission assignment.
    pub permissions: PermissionPlan,
    /// Cleanup behavior while managed by this wrapper.
    pub cleanup: CleanupPolicy,
    /// Mandatory seal applied by the consuming native share transition.
    pub seal: SealPolicy,
}

/// Portable allocation, policy, or lifecycle failure.
#[derive(Debug)]
pub enum MemoryError {
    /// Shared-memory regions cannot be empty.
    ZeroLength,
    /// Configured maximum is smaller than the initial logical length.
    MaximumBelowInitial {
        /// Initial requested logical length.
        initial: usize,
        /// Configured maximum logical length.
        maximum: usize,
    },
    /// Fixed-size policy rejects growth.
    FixedSize,
    /// Shrinking would silently discard user-owned bytes.
    ShrinkUnsupported {
        /// Current logical length.
        current: usize,
        /// Requested smaller logical length.
        requested: usize,
    },
    /// Requested growth exceeds the configured limit.
    MaximumExceeded {
        /// Requested logical length.
        requested: usize,
        /// Configured maximum logical length.
        maximum: usize,
    },
    /// Selected native backend rejected allocation.
    Platform(NativeMemoryPlatformError),
}

impl fmt::Display for MemoryError {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(formatter, "native shared-memory operation failed: {self:?}")
    }
}

impl std::error::Error for MemoryError {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        match self {
            Self::Platform(error) => Some(error),
            _ => None,
        }
    }
}

impl From<NativeMemoryPlatformError> for MemoryError {
    fn from(value: NativeMemoryPlatformError) -> Self {
        Self::Platform(value)
    }
}

/// Common owner of the best private native shared-memory object on this target.
///
/// It exposes logical initialization bytes only through a closure. Consuming
/// [`NativeRegion::prepare_for_sharing`] hands the region to the existing
/// authenticated platform transfer typestate, which applies real native seals
/// and exact permissions. Growth and arbitrary permission changes are no longer
/// possible after that transition.
pub struct NativeRegion {
    inner: Option<PlatformQuiescentRegion>,
    logical_len: usize,
    options: RegionOptions,
}

impl NativeRegion {
    /// Allocates a zeroed, anonymous, non-executable native region.
    pub fn allocate(options: RegionOptions) -> Result<Self, MemoryError> {
        validate_options(options)?;
        let inner = PlatformQuiescentRegion::new(options.logical_len())?;
        Ok(Self {
            inner: Some(inner),
            logical_len: options.logical_len(),
            options,
        })
    }

    /// Returns the immutable allocation and authority policy.
    pub const fn options(&self) -> RegionOptions {
        self.options
    }

    /// Returns a portable status snapshot.
    pub fn status(&self) -> RegionStatus {
        let maximum_len = self.options.maximum_len();
        RegionStatus {
            state: RegionState::Quiescent,
            logical_len: self.logical_len,
            mapped_len: self.inner().len(),
            maximum_len,
            can_grow: matches!(
                self.options.growth(),
                GrowthPolicy::ReplaceBeforeShare { .. }
            ) && self.logical_len < maximum_len,
            permissions: self.options.permissions(),
            cleanup: self.options.cleanup(),
            seal: self.options.seal(),
        }
    }

    /// Runs one initialization operation over logical bytes only.
    ///
    /// Page-rounded padding remains zero and inaccessible through this common
    /// method. No capability has escaped while the closure runs.
    pub fn initialize<R>(&mut self, operation: impl FnOnce(&mut [u8]) -> R) -> R {
        let logical_len = self.logical_len;
        operation(&mut self.inner_mut().as_bytes_mut()[..logical_len])
    }

    /// Clears the complete page-rounded mapping and retains it for reuse.
    ///
    /// Every byte, including native page padding, is overwritten through the
    /// live mapping before this method returns.
    pub fn clear(&mut self) {
        clear_mapping(self.inner_mut());
    }

    /// Grows by replacing the still-private mapping and preserving logical bytes.
    pub fn grow(&mut self, requested: usize) -> Result<(), MemoryError> {
        if requested == 0 {
            return Err(MemoryError::ZeroLength);
        }
        if requested < self.logical_len {
            return Err(MemoryError::ShrinkUnsupported {
                current: self.logical_len,
                requested,
            });
        }
        if requested == self.logical_len {
            return Ok(());
        }
        let maximum = match self.options.growth() {
            GrowthPolicy::Fixed => return Err(MemoryError::FixedSize),
            GrowthPolicy::ReplaceBeforeShare { maximum_len } => maximum_len,
        };
        if requested > maximum {
            return Err(MemoryError::MaximumExceeded { requested, maximum });
        }

        let mut replacement = PlatformQuiescentRegion::new(requested)?;
        replacement.as_bytes_mut()[..self.logical_len]
            .copy_from_slice(&self.inner().as_bytes()[..self.logical_len]);
        let mut previous = self
            .inner
            .replace(replacement)
            .expect("managed region always owns its mapping");
        if self.options.cleanup() == CleanupPolicy::ClearThenRelease {
            clear_mapping(&mut previous);
        }
        self.logical_len = requested;
        Ok(())
    }

    /// Explicitly clears according to policy and releases the anonymous object.
    pub fn close(mut self) {
        self.release();
    }

    /// Overwrites the complete mapping, then explicitly releases it.
    ///
    /// This ignores [`CleanupPolicy`] and always performs the clearing pass.
    /// It cannot erase copies previously made by a process, the kernel, or a
    /// device. The region must still be quiescent and exclusively owned here;
    /// shared regions are destroyed by their transferred lifecycle owner.
    pub fn destroy(mut self) {
        if let Some(inner) = self.inner.as_mut() {
            clear_mapping(inner);
        }
        drop(self.inner.take());
    }

    /// Freezes common initialization/growth and creates a native share request.
    ///
    /// The request retains the permission and mandatory seal policy alongside
    /// the native region. Its platform parts must be consumed by the matching
    /// authenticated prepare/transfer operation.
    pub fn prepare_for_sharing(mut self) -> NativeShareRequest {
        NativeShareRequest {
            inner: self.inner.take(),
            permissions: self.options.permissions(),
            seal: self.options.seal(),
            cleanup: self.options.cleanup(),
        }
    }

    fn inner(&self) -> &PlatformQuiescentRegion {
        self.inner
            .as_ref()
            .expect("managed region always owns its mapping")
    }

    fn inner_mut(&mut self) -> &mut PlatformQuiescentRegion {
        self.inner
            .as_mut()
            .expect("managed region always owns its mapping")
    }

    fn release(&mut self) {
        if let Some(inner) = self.inner.as_mut()
            && self.options.cleanup() == CleanupPolicy::ClearThenRelease
        {
            clear_mapping(inner);
        }
        drop(self.inner.take());
    }
}

/// Consuming boundary between common memory management and native transfer.
///
/// This type exposes no bytes and supports no growth. It keeps the requested
/// one-writer permission plan attached until the caller enters the existing
/// platform-specific authenticated transfer typestate.
pub struct NativeShareRequest {
    inner: Option<PlatformQuiescentRegion>,
    permissions: PermissionPlan,
    seal: SealPolicy,
    cleanup: CleanupPolicy,
}

impl NativeShareRequest {
    /// Required creator/peer access assignment.
    pub const fn permissions(&self) -> PermissionPlan {
        self.permissions
    }

    /// Mandatory native authority-sealing policy.
    pub const fn seal_policy(&self) -> SealPolicy {
        self.seal
    }

    /// Complete native page-rounded mapping length.
    pub fn mapped_len(&self) -> usize {
        self.inner
            .as_ref()
            .expect("share request always owns its mapping")
            .len()
    }

    /// Enters the platform authenticated transfer layer.
    ///
    /// The returned permission plan must select the matching creator-writer or
    /// peer-writer consuming transition. Native code applies the actual seal or
    /// maximum-rights attenuation; safe code cannot disable the seal policy.
    pub fn into_platform_parts(mut self) -> (PlatformQuiescentRegion, PermissionPlan) {
        let region = self
            .inner
            .take()
            .expect("share request always owns its mapping");
        (region, self.permissions)
    }

    /// Clears and releases a prepared request without sharing it.
    pub fn destroy(mut self) {
        if let Some(inner) = self.inner.as_mut() {
            clear_mapping(inner);
        }
        drop(self.inner.take());
    }

    fn release(&mut self) {
        if let Some(inner) = self.inner.as_mut()
            && self.cleanup == CleanupPolicy::ClearThenRelease
        {
            clear_mapping(inner);
        }
        drop(self.inner.take());
    }
}

impl Drop for NativeShareRequest {
    fn drop(&mut self) {
        self.release();
    }
}

fn clear_mapping(region: &mut PlatformQuiescentRegion) {
    for byte in region.as_bytes_mut() {
        // SAFETY: the quiescent typestate uniquely owns the complete live
        // mapping, and each byte pointer is valid for one volatile store.
        unsafe { core::ptr::write_volatile(byte, 0) };
    }
    compiler_fence(Ordering::SeqCst);
}

impl Drop for NativeRegion {
    fn drop(&mut self) {
        self.release();
    }
}

fn validate_options(options: RegionOptions) -> Result<(), MemoryError> {
    if options.logical_len() == 0 {
        return Err(MemoryError::ZeroLength);
    }
    let maximum = options.maximum_len();
    if maximum < options.logical_len() {
        return Err(MemoryError::MaximumBelowInitial {
            initial: options.logical_len(),
            maximum,
        });
    }
    Ok(())
}

#[cfg(target_os = "linux")]
/// Native quiescent region selected on Linux.
pub type PlatformQuiescentRegion = native_ipc_platform::linux::QuiescentRegion;
#[cfg(target_os = "linux")]
/// Native allocation error selected on Linux.
pub type NativeMemoryPlatformError = native_ipc_platform::linux::LinuxError;

#[cfg(target_os = "macos")]
/// Native quiescent region selected on macOS.
pub type PlatformQuiescentRegion = native_ipc_platform::macos::QuiescentRegion;
#[cfg(target_os = "macos")]
/// Native allocation error selected on macOS.
pub type NativeMemoryPlatformError = native_ipc_platform::macos::MachError;

#[cfg(target_os = "windows")]
/// Native quiescent region selected on Windows.
pub type PlatformQuiescentRegion = native_ipc_platform::windows::QuiescentRegion;
#[cfg(target_os = "windows")]
/// Native allocation error selected on Windows.
pub type NativeMemoryPlatformError = native_ipc_platform::windows::WindowsError;

#[cfg(target_os = "linux")]
const fn native_platform() -> NativePlatform {
    NativePlatform::Linux
}
#[cfg(target_os = "macos")]
const fn native_platform() -> NativePlatform {
    NativePlatform::MacOs
}
#[cfg(target_os = "windows")]
const fn native_platform() -> NativePlatform {
    NativePlatform::Windows
}

#[cfg(target_arch = "aarch64")]
const fn native_architecture() -> NativeArchitecture {
    NativeArchitecture::Arm64
}

#[cfg(target_arch = "x86_64")]
const fn native_architecture() -> NativeArchitecture {
    NativeArchitecture::Amd64
}

#[cfg(target_os = "linux")]
const fn authority_mechanism() -> AuthorityMechanism {
    AuthorityMechanism::DescriptorSeals
}
#[cfg(target_os = "macos")]
const fn authority_mechanism() -> AuthorityMechanism {
    AuthorityMechanism::MaximumPortRights
}
#[cfg(target_os = "windows")]
const fn authority_mechanism() -> AuthorityMechanism {
    AuthorityMechanism::ExactHandleRights
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn capabilities_report_the_compilation_architecture() {
        let capabilities = native_memory_capabilities();

        #[cfg(target_arch = "aarch64")]
        assert_eq!(capabilities.architecture(), NativeArchitecture::Arm64);
        #[cfg(target_arch = "x86_64")]
        assert_eq!(capabilities.architecture(), NativeArchitecture::Amd64);
    }

    #[test]
    fn growable_region_preserves_bytes_and_reports_policy() {
        let options = RegionOptions::growable(32, 128, WriterOwner::Creator);
        let mut region = NativeRegion::allocate(options).unwrap();
        region.initialize(|bytes| bytes[..4].copy_from_slice(b"NIPC"));
        region.grow(96).unwrap();

        let status = region.status();
        assert_eq!(status.logical_len, 96);
        assert_eq!(status.maximum_len, 128);
        assert!(status.can_grow);
        assert_eq!(status.permissions.creator_access(), MemoryAccess::ReadWrite);
        assert_eq!(status.permissions.peer_access(), MemoryAccess::ReadOnly);
        region.initialize(|bytes| assert_eq!(&bytes[..4], b"NIPC"));
    }

    #[test]
    fn fixed_and_bounded_growth_fail_closed() {
        let mut fixed =
            NativeRegion::allocate(RegionOptions::fixed(32, WriterOwner::Peer)).unwrap();
        assert!(matches!(fixed.grow(64), Err(MemoryError::FixedSize)));

        let mut bounded =
            NativeRegion::allocate(RegionOptions::growable(32, 64, WriterOwner::Peer)).unwrap();
        assert!(matches!(
            bounded.grow(65),
            Err(MemoryError::MaximumExceeded {
                requested: 65,
                maximum: 64
            })
        ));
        assert!(matches!(
            bounded.grow(16),
            Err(MemoryError::ShrinkUnsupported {
                current: 32,
                requested: 16
            })
        ));
    }

    #[test]
    fn clear_covers_logical_bytes() {
        let mut region =
            NativeRegion::allocate(RegionOptions::fixed(32, WriterOwner::Creator)).unwrap();
        region.initialize(|bytes| bytes.fill(0xaa));
        region.clear();
        region.initialize(|bytes| assert!(bytes.iter().all(|byte| *byte == 0)));
        region.initialize(|bytes| bytes[..4].copy_from_slice(b"REUS"));
        region.initialize(|bytes| assert_eq!(&bytes[..4], b"REUS"));
        region.destroy();
    }

    #[test]
    fn share_request_preserves_permissions_and_removes_byte_access() {
        let region = NativeRegion::allocate(RegionOptions::fixed(32, WriterOwner::Peer)).unwrap();
        let request = region.prepare_for_sharing();
        assert_eq!(request.seal_policy(), SealPolicy::RequiredOnShare);
        assert_eq!(
            request.permissions().creator_access(),
            MemoryAccess::ReadOnly
        );
        assert_eq!(request.permissions().peer_access(), MemoryAccess::ReadWrite);
        assert!(request.mapped_len() >= 32);
        request.destroy();
    }
}