1use std::ffi::c_int;
7use std::fmt;
8use std::marker::PhantomData;
9use std::ptr::NonNull;
10
11use crate::protocol::PeerAccess;
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}
133
134impl fmt::Display for MacBindingError {
135 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
136 write!(formatter, "Mach/core binding failed: {self:?}")
137 }
138}
139
140impl std::error::Error for MacBindingError {}
141impl From<LayoutError> for MacBindingError {
142 fn from(value: LayoutError) -> Self {
143 Self::Layout(value)
144 }
145}
146impl From<MachError> for MacBindingError {
147 fn from(value: MachError) -> Self {
148 Self::Mach(value)
149 }
150}
151impl From<BindingError> for MacBindingError {
152 fn from(value: BindingError) -> Self {
153 Self::Binding(value)
154 }
155}
156impl From<bootstrap::BootstrapError> for MacBindingError {
157 fn from(value: bootstrap::BootstrapError) -> Self {
158 Self::Bootstrap(value)
159 }
160}
161
162impl fmt::Display for MachError {
163 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
164 write!(formatter, "Mach shared memory operation failed: {self:?}")
165 }
166}
167
168impl std::error::Error for MachError {}
169
170#[derive(Debug)]
175pub struct QuiescentRegion {
176 mapping: Mapping,
177 logical_len: usize,
178}
179
180impl QuiescentRegion {
181 pub fn new(len: usize) -> Result<Self, MachError> {
183 let page_size = page_size()?;
184 let mapped_len = page_align(len, page_size)?;
185 let task = current_task();
186 let mut mapping = Mapping::allocate(task, mapped_len)?;
187 unsafe { mapping.bytes_mut(mapped_len) }.fill(0);
189 mapping.protect(VM_PROT_READ | VM_PROT_WRITE, false)?;
190 mapping.protect(VM_PROT_READ | VM_PROT_WRITE, true)?;
191 Ok(Self {
192 mapping,
193 logical_len: len,
194 })
195 }
196
197 pub const fn len(&self) -> usize {
199 self.mapping.mapped_len
200 }
201
202 pub const fn logical_len(&self) -> usize {
204 self.logical_len
205 }
206
207 pub const fn is_empty(&self) -> bool {
209 false
210 }
211
212 pub fn as_bytes(&self) -> &[u8] {
214 unsafe { self.mapping.bytes(self.mapping.mapped_len) }
216 }
217
218 pub fn as_bytes_mut(&mut self) -> &mut [u8] {
220 unsafe { self.mapping.bytes_mut(self.mapping.mapped_len) }
222 }
223
224 pub fn into_local_writer(self, expected_len: usize) -> Result<LocalWriterRegion, MachError> {
226 self.validate_transition_size(expected_len)?;
227 let peer_entry = MemoryEntry::<ReadOnlyCapability>::new(self.mapping.task, &self.mapping)?;
228 Ok(LocalWriterRegion {
229 mapping: self.mapping,
230 peer_entry,
231 len: expected_len,
232 })
233 }
234
235 pub fn into_remote_writer(
237 mut self,
238 expected_len: usize,
239 ) -> Result<RemoteWriterRegion, MachError> {
240 self.validate_transition_size(expected_len)?;
241 let peer_entry = MemoryEntry::<ReadWriteCapability>::new(self.mapping.task, &self.mapping)?;
242 self.mapping.protect(VM_PROT_READ, false)?;
243 self.mapping.protect(VM_PROT_READ, true)?;
244 Ok(RemoteWriterRegion {
245 mapping: self.mapping,
246 peer_entry,
247 len: expected_len,
248 })
249 }
250
251 fn validate_transition_size(&self, expected_len: usize) -> Result<(), MachError> {
252 if expected_len == self.mapping.mapped_len && expected_len != 0 {
253 Ok(())
254 } else {
255 Err(MachError::InvalidViewSize {
256 requested: expected_len,
257 region: self.mapping.mapped_len,
258 })
259 }
260 }
261
262 pub fn into_bound_local_writer(
264 self,
265 expected: ValidationExpectations,
266 topology: RegionSetLayout,
267 ) -> Result<WriterRegion<MacWriterMapping>, MacBindingError> {
268 let layout =
271 unsafe { ValidatedRegionLayout::validate(self.as_bytes(), expected, &topology) }?;
272 let capability_len = self.len();
273 let region = self.into_local_writer(capability_len)?;
274 Ok(WriterRegion::new(
275 MacWriterMapping { region },
276 layout,
277 topology,
278 )?)
279 }
280
281 pub fn into_bound_remote_writer(
283 self,
284 expected: ValidationExpectations,
285 topology: RegionSetLayout,
286 ) -> Result<ReaderRegion<MacReaderMapping>, MacBindingError> {
287 let layout =
289 unsafe { ValidatedRegionLayout::validate(self.as_bytes(), expected, &topology) }?;
290 let capability_len = self.len();
291 let region = self.into_remote_writer(capability_len)?;
292 Ok(ReaderRegion::new(
293 MacReaderMapping { region },
294 layout,
295 topology,
296 )?)
297 }
298
299 pub fn transfer_local_writer(
310 self,
311 expected: ValidationExpectations,
312 topology: RegionSetLayout,
313 channel: &mut bootstrap::ParentChannel,
314 ) -> Result<PendingTransferredWriter, MacBindingError> {
315 let result = (|| {
316 let layout =
318 unsafe { ValidatedRegionLayout::validate(self.as_bytes(), expected, &topology) }?;
319 let capability_len = self.len();
320 let region = self.into_local_writer(capability_len)?;
321 let LocalWriterRegion {
322 mapping,
323 peer_entry,
324 len: _,
325 } = region;
326 let runtime =
327 WriterRegion::new(TransferredWriterMapping { mapping }, layout, topology)?;
328 channel.send(
329 peer_entry.name,
330 expected,
331 capability_len,
332 PeerAccess::ReadOnly,
333 )?;
334 drop(peer_entry);
335 Ok(PendingTransferredWriter { runtime })
336 })();
337 if result.is_err() {
338 channel.poison_transaction();
339 }
340 result
341 }
342
343 pub fn transfer_remote_writer(
352 self,
353 expected: ValidationExpectations,
354 topology: RegionSetLayout,
355 channel: &mut bootstrap::ParentChannel,
356 ) -> Result<PendingTransferredReader, MacBindingError> {
357 let result = (|| {
358 let layout =
360 unsafe { ValidatedRegionLayout::validate(self.as_bytes(), expected, &topology) }?;
361 let capability_len = self.len();
362 let region = self.into_remote_writer(capability_len)?;
363 let RemoteWriterRegion {
364 mapping,
365 peer_entry,
366 len: _,
367 } = region;
368 let runtime =
369 ReaderRegion::new(TransferredReaderMapping { mapping }, layout, topology)?;
370 channel.send(
371 peer_entry.name,
372 expected,
373 capability_len,
374 PeerAccess::SoleWriter,
375 )?;
376 drop(peer_entry);
377 Ok(PendingTransferredReader { runtime })
378 })();
379 if result.is_err() {
380 channel.poison_transaction();
381 }
382 result
383 }
384}
385
386pub struct PendingTransferredWriter {
395 runtime: WriterRegion<TransferredWriterMapping>,
396}
397
398pub struct PendingTransferredReader {
400 runtime: ReaderRegion<TransferredReaderMapping>,
401}
402
403pub struct PendingImportedReader {
412 runtime: ReaderRegion<ImportedReaderMapping>,
413}
414
415pub struct PendingImportedWriter {
417 runtime: WriterRegion<ImportedWriterMapping>,
418}
419
420pub struct TransferredWriterMapping {
422 mapping: Mapping,
423}
424unsafe impl SoleWriterMapping for TransferredWriterMapping {
426 fn base(&self) -> NonNull<u8> {
427 self.mapping.address
428 }
429 fn len(&self) -> usize {
430 self.mapping.mapped_len
431 }
432}
433
434pub struct TransferredReaderMapping {
436 mapping: Mapping,
437}
438unsafe impl ReadOnlyMapping for TransferredReaderMapping {
440 fn base(&self) -> NonNull<u8> {
441 self.mapping.address
442 }
443 fn len(&self) -> usize {
444 self.mapping.mapped_len
445 }
446}
447
448pub struct ImportedReaderMapping {
450 mapping: Mapping,
451}
452unsafe impl ReadOnlyMapping for ImportedReaderMapping {
454 fn base(&self) -> NonNull<u8> {
455 self.mapping.address
456 }
457 fn len(&self) -> usize {
458 self.mapping.mapped_len
459 }
460}
461
462pub struct ImportedWriterMapping {
464 mapping: Mapping,
465}
466unsafe impl SoleWriterMapping for ImportedWriterMapping {
468 fn base(&self) -> NonNull<u8> {
469 self.mapping.address
470 }
471 fn len(&self) -> usize {
472 self.mapping.mapped_len
473 }
474}
475
476impl bootstrap::ChildChannel {
477 pub fn receive_reader(
487 &mut self,
488 len: usize,
489 expected: ValidationExpectations,
490 topology: RegionSetLayout,
491 ) -> Result<PendingImportedReader, MacBindingError> {
492 let result = (|| {
493 let right = self.receive(expected, len, PeerAccess::ReadOnly)?;
494 let mapping = Mapping::map_port(current_task(), len, right.name(), VM_PROT_READ)?;
495 let bytes = unsafe { mapping.bytes(len) };
497 let layout = unsafe { ValidatedRegionLayout::validate(bytes, expected, &topology) }?;
498 drop(right);
499 Ok(PendingImportedReader {
500 runtime: ReaderRegion::new(ImportedReaderMapping { mapping }, layout, topology)?,
501 })
502 })();
503 if result.is_err() {
504 self.poison_transaction();
505 }
506 result
507 }
508
509 pub fn receive_writer(
516 &mut self,
517 len: usize,
518 expected: ValidationExpectations,
519 topology: RegionSetLayout,
520 ) -> Result<PendingImportedWriter, MacBindingError> {
521 let result = (|| {
522 let right = self.receive(expected, len, PeerAccess::SoleWriter)?;
523 let mapping = Mapping::map_port(
524 current_task(),
525 len,
526 right.name(),
527 VM_PROT_READ | VM_PROT_WRITE,
528 )?;
529 let bytes = unsafe { mapping.bytes(len) };
531 let layout = unsafe { ValidatedRegionLayout::validate(bytes, expected, &topology) }?;
532 drop(right);
533 Ok(PendingImportedWriter {
534 runtime: WriterRegion::new(ImportedWriterMapping { mapping }, layout, topology)?,
535 })
536 })();
537 if result.is_err() {
538 self.poison_transaction();
539 }
540 result
541 }
542}
543
544impl bootstrap::ParentChannel {
545 pub fn commit_transfers(
567 &mut self,
568 writer: PendingTransferredWriter,
569 reader: PendingTransferredReader,
570 ) -> Result<
571 (
572 WriterRegion<TransferredWriterMapping>,
573 ReaderRegion<TransferredReaderMapping>,
574 ),
575 MacBindingError,
576 > {
577 self.ready_and_commit()?;
578 Ok((writer.runtime, reader.runtime))
579 }
580}
581
582impl bootstrap::ChildChannel {
583 pub fn commit_imports(
591 &mut self,
592 reader: PendingImportedReader,
593 writer: PendingImportedWriter,
594 ) -> Result<
595 (
596 ReaderRegion<ImportedReaderMapping>,
597 WriterRegion<ImportedWriterMapping>,
598 ),
599 MacBindingError,
600 > {
601 self.ready_and_wait_commit()?;
602 Ok((reader.runtime, writer.runtime))
603 }
604}
605
606pub struct MacWriterMapping {
608 region: LocalWriterRegion,
609}
610
611unsafe impl SoleWriterMapping for MacWriterMapping {
614 fn base(&self) -> NonNull<u8> {
615 self.region.mapping.address
616 }
617 fn len(&self) -> usize {
618 self.region.mapping.mapped_len
619 }
620}
621
622pub struct MacReaderMapping {
624 region: RemoteWriterRegion,
625}
626
627unsafe impl ReadOnlyMapping for MacReaderMapping {
630 fn base(&self) -> NonNull<u8> {
631 self.region.mapping.address
632 }
633 fn len(&self) -> usize {
634 self.region.mapping.mapped_len
635 }
636}
637
638#[derive(Debug)]
642#[allow(dead_code)]
643pub struct LocalWriterRegion {
644 mapping: Mapping,
645 peer_entry: MemoryEntry<ReadOnlyCapability>,
646 len: usize,
647}
648
649impl LocalWriterRegion {
650 pub const fn len(&self) -> usize {
652 self.len
653 }
654
655 pub const fn is_empty(&self) -> bool {
657 self.len == 0
658 }
659}
660
661#[derive(Debug)]
665#[allow(dead_code)]
666pub struct RemoteWriterRegion {
667 mapping: Mapping,
668 peer_entry: MemoryEntry<ReadWriteCapability>,
669 len: usize,
670}
671
672impl RemoteWriterRegion {
673 pub const fn len(&self) -> usize {
675 self.len
676 }
677
678 pub const fn is_empty(&self) -> bool {
680 self.len == 0
681 }
682}
683
684#[derive(Debug)]
685struct Mapping {
686 task: MachPort,
687 address: NonNull<u8>,
688 mapped_len: usize,
689}
690
691impl Mapping {
692 fn allocate(task: MachPort, mapped_len: usize) -> Result<Self, MachError> {
693 let mut address = 0;
694 let result = unsafe {
696 mach_vm_allocate(
697 task,
698 &mut address,
699 mapped_len as MachVmSize,
700 VM_FLAGS_ANYWHERE,
701 )
702 };
703 check_kernel("mach_vm_allocate", result)?;
704 Self::from_allocated(task, address, mapped_len)
705 }
706
707 #[cfg(test)]
708 fn map_entry<Access: CapabilityAccess>(
709 task: MachPort,
710 mapped_len: usize,
711 entry: &MemoryEntry<Access>,
712 ) -> Result<Self, MachError> {
713 Self::map_port(task, mapped_len, entry.name, Access::PROTECTION)
714 }
715
716 fn map_port(
717 task: MachPort,
718 mapped_len: usize,
719 port: MachPort,
720 protection: VmProt,
721 ) -> Result<Self, MachError> {
722 debug_assert_eq!(protection & VM_PROT_EXECUTE, 0);
723 let mut address = 0;
724 let result = unsafe {
726 mach_vm_map(
727 task,
728 &mut address,
729 mapped_len as MachVmSize,
730 0,
731 VM_FLAGS_ANYWHERE,
732 port,
733 0,
734 0,
735 protection,
736 protection,
737 VM_INHERIT_NONE,
738 )
739 };
740 check_kernel("mach_vm_map", result)?;
741 Self::from_allocated(task, address, mapped_len)
742 }
743
744 fn protect(&mut self, protection: VmProt, set_maximum: bool) -> Result<(), MachError> {
745 debug_assert_eq!(protection & VM_PROT_EXECUTE, 0);
746 let result = unsafe {
748 mach_vm_protect(
749 self.task,
750 self.address(),
751 self.mapped_len as MachVmSize,
752 c_int::from(set_maximum),
753 protection,
754 )
755 };
756 check_kernel("mach_vm_protect", result)
757 }
758
759 fn from_allocated(
760 task: MachPort,
761 address: MachVmAddress,
762 mapped_len: usize,
763 ) -> Result<Self, MachError> {
764 let address_usize = match usize::try_from(address) {
765 Ok(value) => value,
766 Err(_) => {
767 deallocate_mapping(task, address, mapped_len);
768 return Err(MachError::InvalidAddress(address));
769 }
770 };
771 let Some(address) = NonNull::new(address_usize as *mut u8) else {
772 deallocate_mapping(task, 0, mapped_len);
773 return Err(MachError::InvalidAddress(0));
774 };
775 Ok(Self {
776 task,
777 address,
778 mapped_len,
779 })
780 }
781
782 fn address(&self) -> MachVmAddress {
783 self.address.as_ptr() as usize as MachVmAddress
784 }
785
786 unsafe fn bytes(&self, len: usize) -> &[u8] {
787 assert!(len <= self.mapped_len && len <= isize::MAX as usize);
788 unsafe { std::slice::from_raw_parts(self.address.as_ptr(), len) }
792 }
793
794 unsafe fn bytes_mut(&mut self, len: usize) -> &mut [u8] {
795 assert!(len <= self.mapped_len && len <= isize::MAX as usize);
796 unsafe { std::slice::from_raw_parts_mut(self.address.as_ptr(), len) }
800 }
801}
802
803impl Drop for Mapping {
804 fn drop(&mut self) {
805 deallocate_mapping(self.task, self.address(), self.mapped_len);
806 }
807}
808
809#[derive(Debug)]
810struct ReadOnlyCapability;
811#[derive(Debug)]
812struct ReadWriteCapability;
813
814trait CapabilityAccess {
815 const PROTECTION: VmProt;
816}
817
818impl CapabilityAccess for ReadOnlyCapability {
819 const PROTECTION: VmProt = VM_PROT_READ;
820}
821impl CapabilityAccess for ReadWriteCapability {
822 const PROTECTION: VmProt = VM_PROT_READ | VM_PROT_WRITE;
823}
824
825#[derive(Debug)]
826struct MemoryEntry<Access> {
827 task: MachPort,
828 name: MachPort,
829 _access: PhantomData<fn() -> Access>,
830}
831
832impl<Access: CapabilityAccess> MemoryEntry<Access> {
833 fn new(task: MachPort, mapping: &Mapping) -> Result<Self, MachError> {
834 let mut entry_size = mapping.mapped_len as MemoryObjectSize;
835 let mut name = MACH_PORT_NULL;
836 let permission = Access::PROTECTION | MAP_MEM_VM_SHARE;
837 debug_assert_eq!(permission & VM_PROT_EXECUTE, 0);
838 let result = unsafe {
840 mach_make_memory_entry_64(
841 task,
842 &mut entry_size,
843 mapping.address(),
844 permission,
845 &mut name,
846 MACH_PORT_NULL,
847 )
848 };
849 if result != KERN_SUCCESS {
850 if name != MACH_PORT_NULL {
851 deallocate_port(task, name);
852 }
853 return Err(MachError::Kernel {
854 operation: "mach_make_memory_entry_64",
855 code: result,
856 });
857 }
858 if name == MACH_PORT_NULL {
859 return Err(MachError::NullMemoryEntry);
860 }
861 let entry = Self {
862 task,
863 name,
864 _access: PhantomData,
865 };
866 if entry_size != mapping.mapped_len as MemoryObjectSize {
867 return Err(MachError::UnexpectedEntrySize {
868 expected: mapping.mapped_len,
869 actual: entry_size,
870 });
871 }
872 Ok(entry)
873 }
874}
875
876impl<Access> Drop for MemoryEntry<Access> {
877 fn drop(&mut self) {
878 deallocate_port(self.task, self.name);
879 }
880}
881
882fn current_task() -> MachPort {
883 unsafe { mach_task_self_ }
885}
886
887fn page_size() -> Result<usize, MachError> {
888 let size = unsafe { getpagesize() };
890 let Ok(converted) = usize::try_from(size) else {
891 return Err(MachError::InvalidPageSize(size));
892 };
893 if converted == 0 || !converted.is_power_of_two() {
894 return Err(MachError::InvalidPageSize(size));
895 }
896 Ok(converted)
897}
898
899fn page_align(size: usize, page_size: usize) -> Result<usize, MachError> {
900 if size == 0 {
901 return Err(MachError::ZeroSize);
902 }
903 let aligned = size
904 .checked_add(page_size - 1)
905 .map(|value| value & !(page_size - 1))
906 .ok_or(MachError::SizeOverflow { requested: size })?;
907 if aligned > isize::MAX as usize {
908 return Err(MachError::SizeOverflow { requested: size });
909 }
910 Ok(aligned)
911}
912
913fn check_kernel(operation: &'static str, code: KernReturn) -> Result<(), MachError> {
914 if code == KERN_SUCCESS {
915 Ok(())
916 } else {
917 Err(MachError::Kernel { operation, code })
918 }
919}
920
921fn deallocate_mapping(task: MachPort, address: MachVmAddress, mapped_len: usize) {
922 let _ = unsafe { mach_vm_deallocate(task, address, mapped_len as MachVmSize) };
924}
925
926fn deallocate_port(task: MachPort, name: MachPort) {
927 let _ = unsafe { mach_port_deallocate(task, name) };
929}
930
931#[cfg(test)]
932mod tests {
933 use super::*;
934 use native_ipc_core::layout::{
935 AcknowledgementRouteSpec, Endpoint, LayoutLimits, RegionSetLayout, RegionSpec, RoleId,
936 };
937 use std::mem::size_of;
938
939 struct TestWriterWitness<'a>(&'a mut Mapping);
940 struct TestReaderWitness<'a>(&'a Mapping);
941
942 unsafe impl SoleWriterMapping for TestWriterWitness<'_> {
945 fn base(&self) -> NonNull<u8> {
946 self.0.address
947 }
948 fn len(&self) -> usize {
949 self.0.mapped_len
950 }
951 }
952
953 unsafe impl ReadOnlyMapping for TestReaderWitness<'_> {
956 fn base(&self) -> NonNull<u8> {
957 self.0.address
958 }
959 fn len(&self) -> usize {
960 self.0.mapped_len
961 }
962 }
963
964 #[test]
965 fn read_only_capability_rejects_writable_mapping() {
966 let owner = QuiescentRegion::new(37).unwrap();
967 let capability_len = owner.len();
968 let runtime = owner.into_local_writer(capability_len).unwrap();
969 let mut address = 0;
970 let protection = VM_PROT_READ | VM_PROT_WRITE;
971 let result = unsafe {
973 mach_vm_map(
974 runtime.mapping.task,
975 &mut address,
976 runtime.mapping.mapped_len as MachVmSize,
977 0,
978 VM_FLAGS_ANYWHERE,
979 runtime.peer_entry.name,
980 0,
981 0,
982 protection,
983 protection,
984 VM_INHERIT_NONE,
985 )
986 };
987 if result == KERN_SUCCESS {
988 deallocate_mapping(runtime.mapping.task, address, runtime.mapping.mapped_len);
989 }
990 assert_ne!(result, KERN_SUCCESS);
991 }
992
993 #[test]
994 fn executable_protection_upgrade_is_rejected() {
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 result = unsafe {
1000 mach_vm_protect(
1001 runtime.mapping.task,
1002 runtime.mapping.address(),
1003 runtime.mapping.mapped_len as MachVmSize,
1004 0,
1005 VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE,
1006 )
1007 };
1008 assert_ne!(result, KERN_SUCCESS);
1009 }
1010
1011 #[test]
1012 fn remote_writer_downgrades_local_mapping_before_escape() {
1013 let mut owner = QuiescentRegion::new(19).unwrap();
1014 owner.as_bytes_mut()[0] = 7;
1015 let capability_len = owner.len();
1016 let mut runtime = owner.into_remote_writer(capability_len).unwrap();
1017 assert!(
1018 runtime
1019 .mapping
1020 .protect(VM_PROT_READ | VM_PROT_WRITE, false)
1021 .is_err()
1022 );
1023 let mut peer = Mapping::map_entry(
1024 runtime.mapping.task,
1025 runtime.mapping.mapped_len,
1026 &runtime.peer_entry,
1027 )
1028 .unwrap();
1029 let peer_bytes = unsafe { peer.bytes_mut(19) };
1031 peer_bytes[3..8].copy_from_slice(b"world");
1032 drop(peer);
1033 assert_eq!(&unsafe { runtime.mapping.bytes(19) }[3..8], b"world");
1035 }
1036
1037 #[test]
1038 fn local_writer_peer_observes_quiescent_initialization() {
1039 let mut owner = QuiescentRegion::new(37).unwrap();
1040 owner.as_bytes_mut()[..5].copy_from_slice(b"hello");
1041 let capability_len = owner.len();
1042 let runtime = owner.into_local_writer(capability_len).unwrap();
1043 let peer = Mapping::map_entry(
1044 runtime.mapping.task,
1045 runtime.mapping.mapped_len,
1046 &runtime.peer_entry,
1047 )
1048 .unwrap();
1049 assert_eq!(&unsafe { peer.bytes(37) }[..5], b"hello");
1051 }
1052
1053 #[test]
1054 fn rejects_bad_sizes_and_matches_sdk_scalars() {
1055 assert_eq!(QuiescentRegion::new(0).unwrap_err(), MachError::ZeroSize);
1056 assert_eq!(
1057 page_align(usize::MAX, 4096).unwrap_err(),
1058 MachError::SizeOverflow {
1059 requested: usize::MAX
1060 }
1061 );
1062 assert_eq!(size_of::<MachPort>(), 4);
1063 assert_eq!(size_of::<MachVmAddress>(), 8);
1064 assert_eq!(ReadOnlyCapability::PROTECTION, VM_PROT_READ);
1065 assert_eq!(
1066 ReadWriteCapability::PROTECTION,
1067 VM_PROT_READ | VM_PROT_WRITE
1068 );
1069 }
1070
1071 #[test]
1072 fn page_capability_padding_is_explicit_validated_and_bound() {
1073 let producer = RoleId::new(1).unwrap();
1074 let peer = RoleId::new(2).unwrap();
1075 let specs = [
1076 RegionSpec {
1077 role: producer,
1078 writer: Endpoint::Initiator,
1079 slot_count: 1,
1080 payload_bytes: 16,
1081 acknowledgement_count: 1,
1082 },
1083 RegionSpec {
1084 role: peer,
1085 writer: Endpoint::Responder,
1086 slot_count: 1,
1087 payload_bytes: 16,
1088 acknowledgement_count: 1,
1089 },
1090 ];
1091 let routes = [
1092 AcknowledgementRouteSpec {
1093 owner: peer,
1094 target: producer,
1095 slot_index: 0,
1096 cell_index: 0,
1097 },
1098 AcknowledgementRouteSpec {
1099 owner: producer,
1100 target: peer,
1101 slot_index: 0,
1102 cell_index: 0,
1103 },
1104 ];
1105 let set = RegionSetLayout::calculate(
1106 [3; 32],
1107 7,
1108 &specs,
1109 &routes,
1110 LayoutLimits {
1111 maximum_mapping_size: 1 << 20,
1112 maximum_slot_count: 2,
1113 maximum_acknowledgement_count: 2,
1114 maximum_payload_bytes: 64,
1115 },
1116 )
1117 .unwrap();
1118 let layout = set.region(producer).unwrap();
1119 let mut owner = QuiescentRegion::new(layout.total_size() as usize).unwrap();
1120 assert!(owner.len() >= owner.logical_len());
1121 assert!(owner.len().is_multiple_of(page_size().unwrap()));
1122 layout.encode_into(owner.as_bytes_mut()).unwrap();
1123 let expected = ValidationExpectations {
1124 schema_id: [3; 32],
1125 generation: 7,
1126 role: producer,
1127 writer: Endpoint::Initiator,
1128 maximum_mapping_size: owner.len() as u64,
1129 };
1130 let mut bound = owner
1131 .into_bound_local_writer(expected, set.clone())
1132 .unwrap();
1133 bound
1134 .slot(0)
1135 .unwrap()
1136 .prepare_publish(1, None)
1137 .unwrap()
1138 .publish(4)
1139 .unwrap();
1140
1141 let mut hostile = QuiescentRegion::new(layout.total_size() as usize).unwrap();
1142 layout.encode_into(hostile.as_bytes_mut()).unwrap();
1143 let last = hostile.len() - 1;
1144 hostile.as_bytes_mut()[last] = 1;
1145 let expected = ValidationExpectations {
1146 schema_id: [3; 32],
1147 generation: 7,
1148 role: producer,
1149 writer: Endpoint::Initiator,
1150 maximum_mapping_size: hostile.len() as u64,
1151 };
1152 assert!(matches!(
1153 hostile.into_bound_local_writer(expected, set),
1154 Err(MacBindingError::Layout(
1155 LayoutError::CapabilityPaddingNotZero
1156 ))
1157 ));
1158 }
1159
1160 #[test]
1161 fn mach_mapping_completes_core_publish_observe_and_ack_path() {
1162 let producer = RoleId::new(1).unwrap();
1163 let acknowledger = RoleId::new(2).unwrap();
1164 let specs = [
1165 RegionSpec {
1166 role: producer,
1167 writer: Endpoint::Initiator,
1168 slot_count: 1,
1169 payload_bytes: 16,
1170 acknowledgement_count: 1,
1171 },
1172 RegionSpec {
1173 role: acknowledger,
1174 writer: Endpoint::Responder,
1175 slot_count: 1,
1176 payload_bytes: 16,
1177 acknowledgement_count: 1,
1178 },
1179 ];
1180 let routes = [
1181 AcknowledgementRouteSpec {
1182 owner: acknowledger,
1183 target: producer,
1184 slot_index: 0,
1185 cell_index: 0,
1186 },
1187 AcknowledgementRouteSpec {
1188 owner: producer,
1189 target: acknowledger,
1190 slot_index: 0,
1191 cell_index: 0,
1192 },
1193 ];
1194 let topology = RegionSetLayout::calculate(
1195 [9; 32],
1196 11,
1197 &specs,
1198 &routes,
1199 LayoutLimits {
1200 maximum_mapping_size: 1 << 20,
1201 maximum_slot_count: 2,
1202 maximum_acknowledgement_count: 2,
1203 maximum_payload_bytes: 64,
1204 },
1205 )
1206 .unwrap();
1207
1208 let producer_layout = topology.region(producer).unwrap();
1209 let mut producer_owner =
1210 QuiescentRegion::new(producer_layout.total_size() as usize).unwrap();
1211 producer_layout
1212 .encode_into(producer_owner.as_bytes_mut())
1213 .unwrap();
1214 let producer_expected = ValidationExpectations {
1215 schema_id: [9; 32],
1216 generation: 11,
1217 role: producer,
1218 writer: Endpoint::Initiator,
1219 maximum_mapping_size: producer_owner.len() as u64,
1220 };
1221 let producer_validated = unsafe {
1222 ValidatedRegionLayout::validate(producer_owner.as_bytes(), producer_expected, &topology)
1223 }
1224 .unwrap();
1225 let producer_len = producer_owner.len();
1226 let mut producer_runtime = producer_owner.into_local_writer(producer_len).unwrap();
1227 let producer_peer = Mapping::map_entry(
1228 producer_runtime.mapping.task,
1229 producer_runtime.mapping.mapped_len,
1230 &producer_runtime.peer_entry,
1231 )
1232 .unwrap();
1233
1234 let ack_layout = topology.region(acknowledger).unwrap();
1235 let mut ack_owner = QuiescentRegion::new(ack_layout.total_size() as usize).unwrap();
1236 ack_layout.encode_into(ack_owner.as_bytes_mut()).unwrap();
1237 let ack_expected = ValidationExpectations {
1238 schema_id: [9; 32],
1239 generation: 11,
1240 role: acknowledger,
1241 writer: Endpoint::Responder,
1242 maximum_mapping_size: ack_owner.len() as u64,
1243 };
1244 let ack_validated = unsafe {
1245 ValidatedRegionLayout::validate(ack_owner.as_bytes(), ack_expected, &topology)
1246 }
1247 .unwrap();
1248 let ack_len = ack_owner.len();
1249 let mut ack_runtime = ack_owner.into_local_writer(ack_len).unwrap();
1250 let ack_peer = Mapping::map_entry(
1251 ack_runtime.mapping.task,
1252 ack_runtime.mapping.mapped_len,
1253 &ack_runtime.peer_entry,
1254 )
1255 .unwrap();
1256
1257 {
1258 let mut writer = WriterRegion::new(
1259 TestWriterWitness(&mut producer_runtime.mapping),
1260 producer_validated.clone(),
1261 topology.clone(),
1262 )
1263 .unwrap();
1264 writer.publish(0, 1, None, b"mach").unwrap();
1265 }
1266 let reader = ReaderRegion::new(
1267 TestReaderWitness(&producer_peer),
1268 producer_validated,
1269 topology.clone(),
1270 )
1271 .unwrap();
1272 let observation = reader.slot(0).unwrap().observe(1).unwrap();
1273 reader.slot(0).unwrap().recheck(observation).unwrap();
1274 assert_eq!(reader.copy_payload(0, 1).unwrap(), b"mach");
1275
1276 {
1277 let mut writer = WriterRegion::new(
1278 TestWriterWitness(&mut ack_runtime.mapping),
1279 ack_validated.clone(),
1280 topology.clone(),
1281 )
1282 .unwrap();
1283 writer
1284 .acknowledgement(producer, 0)
1285 .unwrap()
1286 .acknowledge(observation)
1287 .unwrap();
1288 }
1289 let reader =
1290 ReaderRegion::new(TestReaderWitness(&ack_peer), ack_validated, topology).unwrap();
1291 let acknowledged = reader.acknowledgement(producer, 0).unwrap().observe();
1292 assert_eq!(acknowledged.sequence(), 1);
1293 assert_eq!(acknowledged.slot_index(), 0);
1294 assert_eq!(acknowledged.cell_index(), 0);
1295 }
1296}