1use std::ffi::c_int;
7use std::fmt;
8use std::marker::PhantomData;
9use std::ptr::NonNull;
10
11use crate::protocol::{PeerAccess, TransferProvenance};
12use native_ipc_core::layout::{
13 LayoutError, RegionSetLayout, ValidatedRegionLayout, ValidationExpectations,
14};
15use native_ipc_core::mapping::{
16 BindingError, ReadOnlyMapping, ReaderRegion, SoleWriterMapping, WriterRegion,
17};
18
19pub mod bootstrap;
20
21type KernReturn = c_int;
22type MachPort = u32;
23type MachVmAddress = u64;
24type MachVmSize = u64;
25type MemoryObjectOffset = u64;
26type MemoryObjectSize = u64;
27type VmInherit = u32;
28type VmProt = c_int;
29
30const KERN_SUCCESS: KernReturn = 0;
31const MACH_PORT_NULL: MachPort = 0;
32const VM_FLAGS_ANYWHERE: c_int = 1;
33const VM_PROT_READ: VmProt = 1;
34const VM_PROT_WRITE: VmProt = 2;
35const VM_PROT_EXECUTE: VmProt = 4;
36const MAP_MEM_VM_SHARE: VmProt = 0x0040_0000;
37const VM_INHERIT_NONE: VmInherit = 2;
38
39unsafe extern "C" {
40 static mach_task_self_: MachPort;
41
42 fn getpagesize() -> c_int;
43 fn mach_vm_allocate(
44 target: MachPort,
45 address: *mut MachVmAddress,
46 size: MachVmSize,
47 flags: c_int,
48 ) -> KernReturn;
49 fn mach_vm_deallocate(target: MachPort, address: MachVmAddress, size: MachVmSize)
50 -> KernReturn;
51 fn mach_vm_protect(
52 target_task: MachPort,
53 address: MachVmAddress,
54 size: MachVmSize,
55 set_maximum: c_int,
56 new_protection: VmProt,
57 ) -> KernReturn;
58 fn mach_make_memory_entry_64(
59 target_task: MachPort,
60 size: *mut MemoryObjectSize,
61 offset: MemoryObjectOffset,
62 permission: VmProt,
63 object_handle: *mut MachPort,
64 parent_entry: MachPort,
65 ) -> KernReturn;
66 fn mach_vm_map(
67 target_task: MachPort,
68 address: *mut MachVmAddress,
69 size: MachVmSize,
70 mask: MachVmAddress,
71 flags: c_int,
72 object: MachPort,
73 offset: MemoryObjectOffset,
74 copy: c_int,
75 current_protection: VmProt,
76 maximum_protection: VmProt,
77 inheritance: VmInherit,
78 ) -> KernReturn;
79 fn mach_port_deallocate(task: MachPort, name: MachPort) -> KernReturn;
80}
81
82#[derive(Clone, Copy, Debug, Eq, PartialEq)]
84pub enum MachError {
85 ZeroSize,
87 SizeOverflow {
89 requested: usize,
91 },
92 InvalidViewSize {
94 requested: usize,
96 region: usize,
98 },
99 InvalidPageSize(c_int),
101 InvalidAddress(MachVmAddress),
103 NullMemoryEntry,
105 UnexpectedEntrySize {
107 expected: usize,
109 actual: u64,
111 },
112 Kernel {
114 operation: &'static str,
116 code: KernReturn,
118 },
119}
120
121#[derive(Debug)]
123pub enum MacBindingError {
124 Layout(LayoutError),
126 Mach(MachError),
128 Binding(BindingError),
130 Bootstrap(bootstrap::BootstrapError),
132 ForeignPending,
134}
135
136impl fmt::Display for MacBindingError {
137 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
138 write!(formatter, "Mach/core binding failed: {self:?}")
139 }
140}
141
142impl std::error::Error for MacBindingError {}
143impl From<LayoutError> for MacBindingError {
144 fn from(value: LayoutError) -> Self {
145 Self::Layout(value)
146 }
147}
148impl From<MachError> for MacBindingError {
149 fn from(value: MachError) -> Self {
150 Self::Mach(value)
151 }
152}
153impl From<BindingError> for MacBindingError {
154 fn from(value: BindingError) -> Self {
155 Self::Binding(value)
156 }
157}
158impl From<bootstrap::BootstrapError> for MacBindingError {
159 fn from(value: bootstrap::BootstrapError) -> Self {
160 Self::Bootstrap(value)
161 }
162}
163
164impl fmt::Display for MachError {
165 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
166 write!(formatter, "Mach shared memory operation failed: {self:?}")
167 }
168}
169
170impl std::error::Error for MachError {}
171
172#[derive(Debug)]
177pub struct QuiescentRegion {
178 mapping: Mapping,
179 logical_len: usize,
180}
181
182impl QuiescentRegion {
183 pub fn new(len: usize) -> Result<Self, MachError> {
185 let page_size = page_size()?;
186 let mapped_len = page_align(len, page_size)?;
187 let task = current_task();
188 let mut mapping = Mapping::allocate(task, mapped_len)?;
189 unsafe { mapping.bytes_mut(mapped_len) }.fill(0);
191 mapping.protect(VM_PROT_READ | VM_PROT_WRITE, false)?;
192 mapping.protect(VM_PROT_READ | VM_PROT_WRITE, true)?;
193 Ok(Self {
194 mapping,
195 logical_len: len,
196 })
197 }
198
199 pub const fn len(&self) -> usize {
201 self.mapping.mapped_len
202 }
203
204 pub const fn logical_len(&self) -> usize {
206 self.logical_len
207 }
208
209 pub const fn is_empty(&self) -> bool {
211 false
212 }
213
214 pub fn as_bytes(&self) -> &[u8] {
216 unsafe { self.mapping.bytes(self.mapping.mapped_len) }
218 }
219
220 pub fn as_bytes_mut(&mut self) -> &mut [u8] {
222 unsafe { self.mapping.bytes_mut(self.mapping.mapped_len) }
224 }
225
226 pub fn into_local_writer(self, expected_len: usize) -> Result<LocalWriterRegion, MachError> {
228 self.validate_transition_size(expected_len)?;
229 let peer_entry = MemoryEntry::<ReadOnlyCapability>::new(self.mapping.task, &self.mapping)?;
230 Ok(LocalWriterRegion {
231 mapping: self.mapping,
232 peer_entry,
233 len: expected_len,
234 })
235 }
236
237 pub fn into_remote_writer(
239 mut self,
240 expected_len: usize,
241 ) -> Result<RemoteWriterRegion, MachError> {
242 self.validate_transition_size(expected_len)?;
243 let peer_entry = MemoryEntry::<ReadWriteCapability>::new(self.mapping.task, &self.mapping)?;
244 self.mapping.protect(VM_PROT_READ, false)?;
245 self.mapping.protect(VM_PROT_READ, true)?;
246 Ok(RemoteWriterRegion {
247 mapping: self.mapping,
248 peer_entry,
249 len: expected_len,
250 })
251 }
252
253 fn validate_transition_size(&self, expected_len: usize) -> Result<(), MachError> {
254 if expected_len == self.mapping.mapped_len && expected_len != 0 {
255 Ok(())
256 } else {
257 Err(MachError::InvalidViewSize {
258 requested: expected_len,
259 region: self.mapping.mapped_len,
260 })
261 }
262 }
263
264 pub fn into_bound_local_writer(
266 self,
267 expected: ValidationExpectations,
268 topology: RegionSetLayout,
269 ) -> Result<WriterRegion<MacWriterMapping>, MacBindingError> {
270 let layout =
273 unsafe { ValidatedRegionLayout::validate(self.as_bytes(), expected, &topology) }?;
274 let capability_len = self.len();
275 let region = self.into_local_writer(capability_len)?;
276 Ok(WriterRegion::new(
277 MacWriterMapping { region },
278 layout,
279 topology,
280 )?)
281 }
282
283 pub fn into_bound_remote_writer(
285 self,
286 expected: ValidationExpectations,
287 topology: RegionSetLayout,
288 ) -> Result<ReaderRegion<MacReaderMapping>, MacBindingError> {
289 let layout =
291 unsafe { ValidatedRegionLayout::validate(self.as_bytes(), expected, &topology) }?;
292 let capability_len = self.len();
293 let region = self.into_remote_writer(capability_len)?;
294 Ok(ReaderRegion::new(
295 MacReaderMapping { region },
296 layout,
297 topology,
298 )?)
299 }
300
301 pub fn transfer_local_writer(
312 self,
313 expected: ValidationExpectations,
314 topology: RegionSetLayout,
315 channel: &mut bootstrap::ParentChannel,
316 ) -> Result<PendingTransferredWriter, MacBindingError> {
317 let result = (|| {
318 let layout =
320 unsafe { ValidatedRegionLayout::validate(self.as_bytes(), expected, &topology) }?;
321 let capability_len = self.len();
322 let region = self.into_local_writer(capability_len)?;
323 let LocalWriterRegion {
324 mapping,
325 peer_entry,
326 len: _,
327 } = region;
328 let runtime =
329 WriterRegion::new(TransferredWriterMapping { mapping }, layout, topology)?;
330 channel.send(
331 peer_entry.name,
332 expected,
333 capability_len,
334 PeerAccess::ReadOnly,
335 )?;
336 drop(peer_entry);
337 Ok(PendingTransferredWriter {
338 runtime,
339 provenance: channel.pending_provenance(),
340 })
341 })();
342 if result.is_err() {
343 channel.poison_transaction();
344 }
345 result
346 }
347
348 pub fn transfer_remote_writer(
357 self,
358 expected: ValidationExpectations,
359 topology: RegionSetLayout,
360 channel: &mut bootstrap::ParentChannel,
361 ) -> Result<PendingTransferredReader, MacBindingError> {
362 let result = (|| {
363 let layout =
365 unsafe { ValidatedRegionLayout::validate(self.as_bytes(), expected, &topology) }?;
366 let capability_len = self.len();
367 let region = self.into_remote_writer(capability_len)?;
368 let RemoteWriterRegion {
369 mapping,
370 peer_entry,
371 len: _,
372 } = region;
373 let runtime =
374 ReaderRegion::new(TransferredReaderMapping { mapping }, layout, topology)?;
375 channel.send(
376 peer_entry.name,
377 expected,
378 capability_len,
379 PeerAccess::SoleWriter,
380 )?;
381 drop(peer_entry);
382 Ok(PendingTransferredReader {
383 runtime,
384 provenance: channel.pending_provenance(),
385 })
386 })();
387 if result.is_err() {
388 channel.poison_transaction();
389 }
390 result
391 }
392}
393
394pub struct PendingTransferredWriter {
403 runtime: WriterRegion<TransferredWriterMapping>,
404 provenance: TransferProvenance,
405}
406
407pub struct PendingTransferredReader {
409 runtime: ReaderRegion<TransferredReaderMapping>,
410 provenance: TransferProvenance,
411}
412
413pub struct PendingImportedReader {
422 runtime: ReaderRegion<ImportedReaderMapping>,
423 provenance: TransferProvenance,
424}
425
426pub struct PendingImportedWriter {
428 runtime: WriterRegion<ImportedWriterMapping>,
429 provenance: TransferProvenance,
430}
431
432pub struct TransferredWriterMapping {
434 mapping: Mapping,
435}
436unsafe impl SoleWriterMapping for TransferredWriterMapping {
438 fn base(&self) -> NonNull<u8> {
439 self.mapping.address
440 }
441 fn len(&self) -> usize {
442 self.mapping.mapped_len
443 }
444}
445
446pub struct TransferredReaderMapping {
448 mapping: Mapping,
449}
450unsafe impl ReadOnlyMapping for TransferredReaderMapping {
452 fn base(&self) -> NonNull<u8> {
453 self.mapping.address
454 }
455 fn len(&self) -> usize {
456 self.mapping.mapped_len
457 }
458}
459
460pub struct ImportedReaderMapping {
462 mapping: Mapping,
463}
464unsafe impl ReadOnlyMapping for ImportedReaderMapping {
466 fn base(&self) -> NonNull<u8> {
467 self.mapping.address
468 }
469 fn len(&self) -> usize {
470 self.mapping.mapped_len
471 }
472}
473
474pub struct ImportedWriterMapping {
476 mapping: Mapping,
477}
478unsafe impl SoleWriterMapping for ImportedWriterMapping {
480 fn base(&self) -> NonNull<u8> {
481 self.mapping.address
482 }
483 fn len(&self) -> usize {
484 self.mapping.mapped_len
485 }
486}
487
488impl bootstrap::ChildChannel {
489 pub fn receive_reader(
499 &mut self,
500 len: usize,
501 expected: ValidationExpectations,
502 topology: RegionSetLayout,
503 ) -> Result<PendingImportedReader, MacBindingError> {
504 let result = (|| {
505 let right = self.receive(expected, len, PeerAccess::ReadOnly)?;
506 let mapping = Mapping::map_port(current_task(), len, right.name(), VM_PROT_READ)?;
507 let bytes = unsafe { mapping.bytes(len) };
509 let layout = unsafe { ValidatedRegionLayout::validate(bytes, expected, &topology) }?;
510 drop(right);
511 Ok(PendingImportedReader {
512 runtime: ReaderRegion::new(ImportedReaderMapping { mapping }, layout, topology)?,
513 provenance: self.pending_provenance(),
514 })
515 })();
516 if result.is_err() {
517 self.poison_transaction();
518 }
519 result
520 }
521
522 pub fn receive_writer(
529 &mut self,
530 len: usize,
531 expected: ValidationExpectations,
532 topology: RegionSetLayout,
533 ) -> Result<PendingImportedWriter, MacBindingError> {
534 let result = (|| {
535 let right = self.receive(expected, len, PeerAccess::SoleWriter)?;
536 let mapping = Mapping::map_port(
537 current_task(),
538 len,
539 right.name(),
540 VM_PROT_READ | VM_PROT_WRITE,
541 )?;
542 let bytes = unsafe { mapping.bytes(len) };
544 let layout = unsafe { ValidatedRegionLayout::validate(bytes, expected, &topology) }?;
545 drop(right);
546 Ok(PendingImportedWriter {
547 runtime: WriterRegion::new(ImportedWriterMapping { mapping }, layout, topology)?,
548 provenance: self.pending_provenance(),
549 })
550 })();
551 if result.is_err() {
552 self.poison_transaction();
553 }
554 result
555 }
556}
557
558impl bootstrap::ParentChannel {
559 pub fn commit_transfers(
583 &mut self,
584 writer: PendingTransferredWriter,
585 reader: PendingTransferredReader,
586 ) -> Result<
587 (
588 WriterRegion<TransferredWriterMapping>,
589 ReaderRegion<TransferredReaderMapping>,
590 ),
591 MacBindingError,
592 > {
593 let expected = self.pending_provenance();
594 if writer.provenance != expected || reader.provenance != expected {
595 self.poison_transaction();
596 return Err(MacBindingError::ForeignPending);
597 }
598 self.ready_and_commit()?;
599 Ok((writer.runtime, reader.runtime))
600 }
601}
602
603impl bootstrap::ChildChannel {
604 pub fn commit_imports(
613 &mut self,
614 reader: PendingImportedReader,
615 writer: PendingImportedWriter,
616 ) -> Result<
617 (
618 ReaderRegion<ImportedReaderMapping>,
619 WriterRegion<ImportedWriterMapping>,
620 ),
621 MacBindingError,
622 > {
623 let expected = self.pending_provenance();
624 if reader.provenance != expected || writer.provenance != expected {
625 self.poison_transaction();
626 return Err(MacBindingError::ForeignPending);
627 }
628 self.ready_and_wait_commit()?;
629 Ok((reader.runtime, writer.runtime))
630 }
631}
632
633pub struct MacWriterMapping {
635 region: LocalWriterRegion,
636}
637
638unsafe impl SoleWriterMapping for MacWriterMapping {
641 fn base(&self) -> NonNull<u8> {
642 self.region.mapping.address
643 }
644 fn len(&self) -> usize {
645 self.region.mapping.mapped_len
646 }
647}
648
649pub struct MacReaderMapping {
651 region: RemoteWriterRegion,
652}
653
654unsafe impl ReadOnlyMapping for MacReaderMapping {
657 fn base(&self) -> NonNull<u8> {
658 self.region.mapping.address
659 }
660 fn len(&self) -> usize {
661 self.region.mapping.mapped_len
662 }
663}
664
665#[derive(Debug)]
669#[allow(dead_code)]
670pub struct LocalWriterRegion {
671 mapping: Mapping,
672 peer_entry: MemoryEntry<ReadOnlyCapability>,
673 len: usize,
674}
675
676impl LocalWriterRegion {
677 pub const fn len(&self) -> usize {
679 self.len
680 }
681
682 pub const fn is_empty(&self) -> bool {
684 self.len == 0
685 }
686}
687
688#[derive(Debug)]
692#[allow(dead_code)]
693pub struct RemoteWriterRegion {
694 mapping: Mapping,
695 peer_entry: MemoryEntry<ReadWriteCapability>,
696 len: usize,
697}
698
699impl RemoteWriterRegion {
700 pub const fn len(&self) -> usize {
702 self.len
703 }
704
705 pub const fn is_empty(&self) -> bool {
707 self.len == 0
708 }
709}
710
711#[derive(Debug)]
712struct Mapping {
713 task: MachPort,
714 address: NonNull<u8>,
715 mapped_len: usize,
716}
717
718impl Mapping {
719 fn allocate(task: MachPort, mapped_len: usize) -> Result<Self, MachError> {
720 let mut address = 0;
721 let result = unsafe {
723 mach_vm_allocate(
724 task,
725 &mut address,
726 mapped_len as MachVmSize,
727 VM_FLAGS_ANYWHERE,
728 )
729 };
730 check_kernel("mach_vm_allocate", result)?;
731 Self::from_allocated(task, address, mapped_len)
732 }
733
734 #[cfg(test)]
735 fn map_entry<Access: CapabilityAccess>(
736 task: MachPort,
737 mapped_len: usize,
738 entry: &MemoryEntry<Access>,
739 ) -> Result<Self, MachError> {
740 Self::map_port(task, mapped_len, entry.name, Access::PROTECTION)
741 }
742
743 fn map_port(
744 task: MachPort,
745 mapped_len: usize,
746 port: MachPort,
747 protection: VmProt,
748 ) -> Result<Self, MachError> {
749 debug_assert_eq!(protection & VM_PROT_EXECUTE, 0);
750 let mut address = 0;
751 let result = unsafe {
753 mach_vm_map(
754 task,
755 &mut address,
756 mapped_len as MachVmSize,
757 0,
758 VM_FLAGS_ANYWHERE,
759 port,
760 0,
761 0,
762 protection,
763 protection,
764 VM_INHERIT_NONE,
765 )
766 };
767 check_kernel("mach_vm_map", result)?;
768 Self::from_allocated(task, address, mapped_len)
769 }
770
771 fn protect(&mut self, protection: VmProt, set_maximum: bool) -> Result<(), MachError> {
772 debug_assert_eq!(protection & VM_PROT_EXECUTE, 0);
773 let result = unsafe {
775 mach_vm_protect(
776 self.task,
777 self.address(),
778 self.mapped_len as MachVmSize,
779 c_int::from(set_maximum),
780 protection,
781 )
782 };
783 check_kernel("mach_vm_protect", result)
784 }
785
786 fn from_allocated(
787 task: MachPort,
788 address: MachVmAddress,
789 mapped_len: usize,
790 ) -> Result<Self, MachError> {
791 let address_usize = match usize::try_from(address) {
792 Ok(value) => value,
793 Err(_) => {
794 deallocate_mapping(task, address, mapped_len);
795 return Err(MachError::InvalidAddress(address));
796 }
797 };
798 let Some(address) = NonNull::new(address_usize as *mut u8) else {
799 return Err(MachError::InvalidAddress(0));
803 };
804 Ok(Self {
805 task,
806 address,
807 mapped_len,
808 })
809 }
810
811 fn address(&self) -> MachVmAddress {
812 self.address.as_ptr() as usize as MachVmAddress
813 }
814
815 unsafe fn bytes(&self, len: usize) -> &[u8] {
816 assert!(len <= self.mapped_len && len <= isize::MAX as usize);
817 unsafe { std::slice::from_raw_parts(self.address.as_ptr(), len) }
821 }
822
823 unsafe fn bytes_mut(&mut self, len: usize) -> &mut [u8] {
824 assert!(len <= self.mapped_len && len <= isize::MAX as usize);
825 unsafe { std::slice::from_raw_parts_mut(self.address.as_ptr(), len) }
829 }
830}
831
832impl Drop for Mapping {
833 fn drop(&mut self) {
834 deallocate_mapping(self.task, self.address(), self.mapped_len);
835 }
836}
837
838#[derive(Debug)]
839struct ReadOnlyCapability;
840#[derive(Debug)]
841struct ReadWriteCapability;
842
843trait CapabilityAccess {
844 const PROTECTION: VmProt;
845}
846
847impl CapabilityAccess for ReadOnlyCapability {
848 const PROTECTION: VmProt = VM_PROT_READ;
849}
850impl CapabilityAccess for ReadWriteCapability {
851 const PROTECTION: VmProt = VM_PROT_READ | VM_PROT_WRITE;
852}
853
854#[derive(Debug)]
855struct MemoryEntry<Access> {
856 task: MachPort,
857 name: MachPort,
858 _access: PhantomData<fn() -> Access>,
859}
860
861impl<Access: CapabilityAccess> MemoryEntry<Access> {
862 fn new(task: MachPort, mapping: &Mapping) -> Result<Self, MachError> {
863 let mut entry_size = mapping.mapped_len as MemoryObjectSize;
864 let mut name = MACH_PORT_NULL;
865 let permission = Access::PROTECTION | MAP_MEM_VM_SHARE;
866 debug_assert_eq!(permission & VM_PROT_EXECUTE, 0);
867 let result = unsafe {
869 mach_make_memory_entry_64(
870 task,
871 &mut entry_size,
872 mapping.address(),
873 permission,
874 &mut name,
875 MACH_PORT_NULL,
876 )
877 };
878 if result != KERN_SUCCESS {
879 if name != MACH_PORT_NULL {
880 deallocate_port(task, name);
881 }
882 return Err(MachError::Kernel {
883 operation: "mach_make_memory_entry_64",
884 code: result,
885 });
886 }
887 if name == MACH_PORT_NULL {
888 return Err(MachError::NullMemoryEntry);
889 }
890 let entry = Self {
891 task,
892 name,
893 _access: PhantomData,
894 };
895 if entry_size != mapping.mapped_len as MemoryObjectSize {
896 return Err(MachError::UnexpectedEntrySize {
897 expected: mapping.mapped_len,
898 actual: entry_size,
899 });
900 }
901 Ok(entry)
902 }
903}
904
905impl<Access> Drop for MemoryEntry<Access> {
906 fn drop(&mut self) {
907 deallocate_port(self.task, self.name);
908 }
909}
910
911fn current_task() -> MachPort {
912 unsafe { mach_task_self_ }
914}
915
916fn page_size() -> Result<usize, MachError> {
917 let size = unsafe { getpagesize() };
919 let Ok(converted) = usize::try_from(size) else {
920 return Err(MachError::InvalidPageSize(size));
921 };
922 if converted == 0 || !converted.is_power_of_two() {
923 return Err(MachError::InvalidPageSize(size));
924 }
925 Ok(converted)
926}
927
928fn page_align(size: usize, page_size: usize) -> Result<usize, MachError> {
929 if size == 0 {
930 return Err(MachError::ZeroSize);
931 }
932 let aligned = size
933 .checked_add(page_size - 1)
934 .map(|value| value & !(page_size - 1))
935 .ok_or(MachError::SizeOverflow { requested: size })?;
936 if aligned > isize::MAX as usize {
937 return Err(MachError::SizeOverflow { requested: size });
938 }
939 Ok(aligned)
940}
941
942fn check_kernel(operation: &'static str, code: KernReturn) -> Result<(), MachError> {
943 if code == KERN_SUCCESS {
944 Ok(())
945 } else {
946 Err(MachError::Kernel { operation, code })
947 }
948}
949
950fn deallocate_mapping(task: MachPort, address: MachVmAddress, mapped_len: usize) {
951 let _ = unsafe { mach_vm_deallocate(task, address, mapped_len as MachVmSize) };
953}
954
955fn deallocate_port(task: MachPort, name: MachPort) {
956 let _ = unsafe { mach_port_deallocate(task, name) };
958}
959
960#[cfg(test)]
961mod tests {
962 use super::*;
963 use native_ipc_core::layout::{
964 AcknowledgementRouteSpec, Endpoint, LayoutLimits, RegionSetLayout, RegionSpec, RoleId,
965 };
966 use std::mem::size_of;
967
968 struct TestWriterWitness<'a>(&'a mut Mapping);
969 struct TestReaderWitness<'a>(&'a Mapping);
970
971 unsafe impl SoleWriterMapping for TestWriterWitness<'_> {
974 fn base(&self) -> NonNull<u8> {
975 self.0.address
976 }
977 fn len(&self) -> usize {
978 self.0.mapped_len
979 }
980 }
981
982 unsafe impl ReadOnlyMapping for TestReaderWitness<'_> {
985 fn base(&self) -> NonNull<u8> {
986 self.0.address
987 }
988 fn len(&self) -> usize {
989 self.0.mapped_len
990 }
991 }
992
993 #[test]
994 fn read_only_capability_rejects_writable_mapping() {
995 let owner = QuiescentRegion::new(37).unwrap();
996 let capability_len = owner.len();
997 let runtime = owner.into_local_writer(capability_len).unwrap();
998 let mut address = 0;
999 let protection = VM_PROT_READ | VM_PROT_WRITE;
1000 let result = unsafe {
1002 mach_vm_map(
1003 runtime.mapping.task,
1004 &mut address,
1005 runtime.mapping.mapped_len as MachVmSize,
1006 0,
1007 VM_FLAGS_ANYWHERE,
1008 runtime.peer_entry.name,
1009 0,
1010 0,
1011 protection,
1012 protection,
1013 VM_INHERIT_NONE,
1014 )
1015 };
1016 if result == KERN_SUCCESS {
1017 deallocate_mapping(runtime.mapping.task, address, runtime.mapping.mapped_len);
1018 }
1019 assert_ne!(result, KERN_SUCCESS);
1020 }
1021
1022 #[test]
1023 fn executable_protection_upgrade_is_rejected() {
1024 let owner = QuiescentRegion::new(37).unwrap();
1025 let capability_len = owner.len();
1026 let runtime = owner.into_local_writer(capability_len).unwrap();
1027 let result = unsafe {
1029 mach_vm_protect(
1030 runtime.mapping.task,
1031 runtime.mapping.address(),
1032 runtime.mapping.mapped_len as MachVmSize,
1033 0,
1034 VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE,
1035 )
1036 };
1037 assert_ne!(result, KERN_SUCCESS);
1038 }
1039
1040 #[test]
1041 fn remote_writer_downgrades_local_mapping_before_escape() {
1042 let mut owner = QuiescentRegion::new(19).unwrap();
1043 owner.as_bytes_mut()[0] = 7;
1044 let capability_len = owner.len();
1045 let mut runtime = owner.into_remote_writer(capability_len).unwrap();
1046 assert!(
1047 runtime
1048 .mapping
1049 .protect(VM_PROT_READ | VM_PROT_WRITE, false)
1050 .is_err()
1051 );
1052 let mut peer = Mapping::map_entry(
1053 runtime.mapping.task,
1054 runtime.mapping.mapped_len,
1055 &runtime.peer_entry,
1056 )
1057 .unwrap();
1058 let peer_bytes = unsafe { peer.bytes_mut(19) };
1060 peer_bytes[3..8].copy_from_slice(b"world");
1061 drop(peer);
1062 assert_eq!(&unsafe { runtime.mapping.bytes(19) }[3..8], b"world");
1064 }
1065
1066 #[test]
1067 fn local_writer_peer_observes_quiescent_initialization() {
1068 let mut owner = QuiescentRegion::new(37).unwrap();
1069 owner.as_bytes_mut()[..5].copy_from_slice(b"hello");
1070 let capability_len = owner.len();
1071 let runtime = owner.into_local_writer(capability_len).unwrap();
1072 let peer = Mapping::map_entry(
1073 runtime.mapping.task,
1074 runtime.mapping.mapped_len,
1075 &runtime.peer_entry,
1076 )
1077 .unwrap();
1078 assert_eq!(&unsafe { peer.bytes(37) }[..5], b"hello");
1080 }
1081
1082 #[test]
1083 fn rejects_bad_sizes_and_matches_sdk_scalars() {
1084 assert_eq!(QuiescentRegion::new(0).unwrap_err(), MachError::ZeroSize);
1085 assert_eq!(
1086 page_align(usize::MAX, 4096).unwrap_err(),
1087 MachError::SizeOverflow {
1088 requested: usize::MAX
1089 }
1090 );
1091 assert_eq!(size_of::<MachPort>(), 4);
1092 assert_eq!(size_of::<MachVmAddress>(), 8);
1093 assert_eq!(ReadOnlyCapability::PROTECTION, VM_PROT_READ);
1094 assert_eq!(
1095 ReadWriteCapability::PROTECTION,
1096 VM_PROT_READ | VM_PROT_WRITE
1097 );
1098 }
1099
1100 #[test]
1101 fn page_capability_padding_is_explicit_validated_and_bound() {
1102 let producer = RoleId::new(1).unwrap();
1103 let peer = RoleId::new(2).unwrap();
1104 let specs = [
1105 RegionSpec {
1106 role: producer,
1107 writer: Endpoint::Initiator,
1108 slot_count: 1,
1109 payload_bytes: 16,
1110 acknowledgement_count: 1,
1111 },
1112 RegionSpec {
1113 role: peer,
1114 writer: Endpoint::Responder,
1115 slot_count: 1,
1116 payload_bytes: 16,
1117 acknowledgement_count: 1,
1118 },
1119 ];
1120 let routes = [
1121 AcknowledgementRouteSpec {
1122 owner: peer,
1123 target: producer,
1124 slot_index: 0,
1125 cell_index: 0,
1126 },
1127 AcknowledgementRouteSpec {
1128 owner: producer,
1129 target: peer,
1130 slot_index: 0,
1131 cell_index: 0,
1132 },
1133 ];
1134 let set = RegionSetLayout::calculate(
1135 [3; 32],
1136 7,
1137 &specs,
1138 &routes,
1139 LayoutLimits {
1140 maximum_mapping_size: 1 << 20,
1141 maximum_slot_count: 2,
1142 maximum_acknowledgement_count: 2,
1143 maximum_payload_bytes: 64,
1144 },
1145 )
1146 .unwrap();
1147 let layout = set.region(producer).unwrap();
1148 let mut owner = QuiescentRegion::new(layout.total_size() as usize).unwrap();
1149 assert!(owner.len() >= owner.logical_len());
1150 assert!(owner.len().is_multiple_of(page_size().unwrap()));
1151 layout.encode_into(owner.as_bytes_mut()).unwrap();
1152 let expected = ValidationExpectations {
1153 schema_id: [3; 32],
1154 generation: 7,
1155 role: producer,
1156 writer: Endpoint::Initiator,
1157 maximum_mapping_size: owner.len() as u64,
1158 };
1159 let mut bound = owner
1160 .into_bound_local_writer(expected, set.clone())
1161 .unwrap();
1162 bound
1163 .slot(0)
1164 .unwrap()
1165 .prepare_publish(1, None)
1166 .unwrap()
1167 .publish(4)
1168 .unwrap();
1169
1170 let mut hostile = QuiescentRegion::new(layout.total_size() as usize).unwrap();
1171 layout.encode_into(hostile.as_bytes_mut()).unwrap();
1172 let last = hostile.len() - 1;
1173 hostile.as_bytes_mut()[last] = 1;
1174 let expected = ValidationExpectations {
1175 schema_id: [3; 32],
1176 generation: 7,
1177 role: producer,
1178 writer: Endpoint::Initiator,
1179 maximum_mapping_size: hostile.len() as u64,
1180 };
1181 assert!(matches!(
1182 hostile.into_bound_local_writer(expected, set),
1183 Err(MacBindingError::Layout(
1184 LayoutError::CapabilityPaddingNotZero
1185 ))
1186 ));
1187 }
1188
1189 #[test]
1190 fn mach_mapping_completes_core_publish_observe_and_ack_path() {
1191 let producer = RoleId::new(1).unwrap();
1192 let acknowledger = RoleId::new(2).unwrap();
1193 let specs = [
1194 RegionSpec {
1195 role: producer,
1196 writer: Endpoint::Initiator,
1197 slot_count: 1,
1198 payload_bytes: 16,
1199 acknowledgement_count: 1,
1200 },
1201 RegionSpec {
1202 role: acknowledger,
1203 writer: Endpoint::Responder,
1204 slot_count: 1,
1205 payload_bytes: 16,
1206 acknowledgement_count: 1,
1207 },
1208 ];
1209 let routes = [
1210 AcknowledgementRouteSpec {
1211 owner: acknowledger,
1212 target: producer,
1213 slot_index: 0,
1214 cell_index: 0,
1215 },
1216 AcknowledgementRouteSpec {
1217 owner: producer,
1218 target: acknowledger,
1219 slot_index: 0,
1220 cell_index: 0,
1221 },
1222 ];
1223 let topology = RegionSetLayout::calculate(
1224 [9; 32],
1225 11,
1226 &specs,
1227 &routes,
1228 LayoutLimits {
1229 maximum_mapping_size: 1 << 20,
1230 maximum_slot_count: 2,
1231 maximum_acknowledgement_count: 2,
1232 maximum_payload_bytes: 64,
1233 },
1234 )
1235 .unwrap();
1236
1237 let producer_layout = topology.region(producer).unwrap();
1238 let mut producer_owner =
1239 QuiescentRegion::new(producer_layout.total_size() as usize).unwrap();
1240 producer_layout
1241 .encode_into(producer_owner.as_bytes_mut())
1242 .unwrap();
1243 let producer_expected = ValidationExpectations {
1244 schema_id: [9; 32],
1245 generation: 11,
1246 role: producer,
1247 writer: Endpoint::Initiator,
1248 maximum_mapping_size: producer_owner.len() as u64,
1249 };
1250 let producer_validated = unsafe {
1251 ValidatedRegionLayout::validate(producer_owner.as_bytes(), producer_expected, &topology)
1252 }
1253 .unwrap();
1254 let producer_len = producer_owner.len();
1255 let mut producer_runtime = producer_owner.into_local_writer(producer_len).unwrap();
1256 let producer_peer = Mapping::map_entry(
1257 producer_runtime.mapping.task,
1258 producer_runtime.mapping.mapped_len,
1259 &producer_runtime.peer_entry,
1260 )
1261 .unwrap();
1262
1263 let ack_layout = topology.region(acknowledger).unwrap();
1264 let mut ack_owner = QuiescentRegion::new(ack_layout.total_size() as usize).unwrap();
1265 ack_layout.encode_into(ack_owner.as_bytes_mut()).unwrap();
1266 let ack_expected = ValidationExpectations {
1267 schema_id: [9; 32],
1268 generation: 11,
1269 role: acknowledger,
1270 writer: Endpoint::Responder,
1271 maximum_mapping_size: ack_owner.len() as u64,
1272 };
1273 let ack_validated = unsafe {
1274 ValidatedRegionLayout::validate(ack_owner.as_bytes(), ack_expected, &topology)
1275 }
1276 .unwrap();
1277 let ack_len = ack_owner.len();
1278 let mut ack_runtime = ack_owner.into_local_writer(ack_len).unwrap();
1279 let ack_peer = Mapping::map_entry(
1280 ack_runtime.mapping.task,
1281 ack_runtime.mapping.mapped_len,
1282 &ack_runtime.peer_entry,
1283 )
1284 .unwrap();
1285
1286 {
1287 let mut writer = WriterRegion::new(
1288 TestWriterWitness(&mut producer_runtime.mapping),
1289 producer_validated.clone(),
1290 topology.clone(),
1291 )
1292 .unwrap();
1293 writer.publish(0, 1, None, b"mach").unwrap();
1294 }
1295 let reader = ReaderRegion::new(
1296 TestReaderWitness(&producer_peer),
1297 producer_validated,
1298 topology.clone(),
1299 )
1300 .unwrap();
1301 let observation = reader.slot(0).unwrap().observe(1).unwrap();
1302 reader.slot(0).unwrap().recheck(observation).unwrap();
1303 assert_eq!(reader.copy_payload(0, 1).unwrap(), b"mach");
1304
1305 {
1306 let mut writer = WriterRegion::new(
1307 TestWriterWitness(&mut ack_runtime.mapping),
1308 ack_validated.clone(),
1309 topology.clone(),
1310 )
1311 .unwrap();
1312 writer
1313 .acknowledgement(producer, 0)
1314 .unwrap()
1315 .acknowledge(observation)
1316 .unwrap();
1317 }
1318 let reader =
1319 ReaderRegion::new(TestReaderWitness(&ack_peer), ack_validated, topology).unwrap();
1320 let acknowledged = reader.acknowledgement(producer, 0).unwrap().observe();
1321 assert_eq!(acknowledged.sequence(), 1);
1322 assert_eq!(acknowledged.slot_index(), 0);
1323 assert_eq!(acknowledged.cell_index(), 0);
1324 }
1325}