1use std::ffi::c_int;
7use std::fmt;
8use std::marker::PhantomData;
9use std::ptr::NonNull;
10
11use native_ipc_core::layout::{
12 LayoutError, RegionSetLayout, ValidatedRegionLayout, ValidationExpectations,
13};
14use native_ipc_core::mapping::{
15 BindingError, ReadOnlyMapping, ReaderRegion, SoleWriterMapping, WriterRegion,
16};
17
18pub mod bootstrap;
19
20type KernReturn = c_int;
21type MachPort = u32;
22type MachVmAddress = u64;
23type MachVmSize = u64;
24type MemoryObjectOffset = u64;
25type MemoryObjectSize = u64;
26type VmInherit = u32;
27type VmProt = c_int;
28
29const KERN_SUCCESS: KernReturn = 0;
30const MACH_PORT_NULL: MachPort = 0;
31const VM_FLAGS_ANYWHERE: c_int = 1;
32const VM_PROT_READ: VmProt = 1;
33const VM_PROT_WRITE: VmProt = 2;
34const VM_PROT_EXECUTE: VmProt = 4;
35const MAP_MEM_VM_SHARE: VmProt = 0x0040_0000;
36const VM_INHERIT_NONE: VmInherit = 2;
37
38unsafe extern "C" {
39 static mach_task_self_: MachPort;
40
41 fn getpagesize() -> c_int;
42 fn mach_vm_allocate(
43 target: MachPort,
44 address: *mut MachVmAddress,
45 size: MachVmSize,
46 flags: c_int,
47 ) -> KernReturn;
48 fn mach_vm_deallocate(target: MachPort, address: MachVmAddress, size: MachVmSize)
49 -> KernReturn;
50 fn mach_vm_protect(
51 target_task: MachPort,
52 address: MachVmAddress,
53 size: MachVmSize,
54 set_maximum: c_int,
55 new_protection: VmProt,
56 ) -> KernReturn;
57 fn mach_make_memory_entry_64(
58 target_task: MachPort,
59 size: *mut MemoryObjectSize,
60 offset: MemoryObjectOffset,
61 permission: VmProt,
62 object_handle: *mut MachPort,
63 parent_entry: MachPort,
64 ) -> KernReturn;
65 fn mach_vm_map(
66 target_task: MachPort,
67 address: *mut MachVmAddress,
68 size: MachVmSize,
69 mask: MachVmAddress,
70 flags: c_int,
71 object: MachPort,
72 offset: MemoryObjectOffset,
73 copy: c_int,
74 current_protection: VmProt,
75 maximum_protection: VmProt,
76 inheritance: VmInherit,
77 ) -> KernReturn;
78 fn mach_port_deallocate(task: MachPort, name: MachPort) -> KernReturn;
79}
80
81#[allow(missing_docs)]
83#[derive(Clone, Copy, Debug, Eq, PartialEq)]
84pub enum MachError {
85 ZeroSize,
87 SizeOverflow { requested: usize },
89 InvalidViewSize { requested: usize, region: usize },
91 InvalidPageSize(c_int),
93 InvalidAddress(MachVmAddress),
95 NullMemoryEntry,
97 UnexpectedEntrySize { expected: usize, actual: u64 },
99 Kernel {
101 operation: &'static str,
103 code: KernReturn,
105 },
106}
107
108#[derive(Debug)]
110pub enum MacBindingError {
111 Layout(LayoutError),
113 Mach(MachError),
115 Binding(BindingError),
117 Bootstrap(bootstrap::BootstrapError),
119}
120
121impl fmt::Display for MacBindingError {
122 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
123 write!(formatter, "Mach/core binding failed: {self:?}")
124 }
125}
126
127impl std::error::Error for MacBindingError {}
128impl From<LayoutError> for MacBindingError {
129 fn from(value: LayoutError) -> Self {
130 Self::Layout(value)
131 }
132}
133impl From<MachError> for MacBindingError {
134 fn from(value: MachError) -> Self {
135 Self::Mach(value)
136 }
137}
138impl From<BindingError> for MacBindingError {
139 fn from(value: BindingError) -> Self {
140 Self::Binding(value)
141 }
142}
143impl From<bootstrap::BootstrapError> for MacBindingError {
144 fn from(value: bootstrap::BootstrapError) -> Self {
145 Self::Bootstrap(value)
146 }
147}
148
149impl fmt::Display for MachError {
150 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
151 write!(formatter, "Mach shared memory operation failed: {self:?}")
152 }
153}
154
155impl std::error::Error for MachError {}
156
157#[derive(Debug)]
162pub struct QuiescentRegion {
163 mapping: Mapping,
164 logical_len: usize,
165}
166
167impl QuiescentRegion {
168 pub fn new(len: usize) -> Result<Self, MachError> {
170 let page_size = page_size()?;
171 let mapped_len = page_align(len, page_size)?;
172 let task = current_task();
173 let mut mapping = Mapping::allocate(task, mapped_len)?;
174 unsafe { mapping.bytes_mut(mapped_len) }.fill(0);
176 mapping.protect(VM_PROT_READ | VM_PROT_WRITE, false)?;
177 mapping.protect(VM_PROT_READ | VM_PROT_WRITE, true)?;
178 Ok(Self {
179 mapping,
180 logical_len: len,
181 })
182 }
183
184 pub const fn len(&self) -> usize {
186 self.mapping.mapped_len
187 }
188
189 pub const fn logical_len(&self) -> usize {
191 self.logical_len
192 }
193
194 pub const fn is_empty(&self) -> bool {
196 false
197 }
198
199 pub fn as_bytes(&self) -> &[u8] {
201 unsafe { self.mapping.bytes(self.mapping.mapped_len) }
203 }
204
205 pub fn as_bytes_mut(&mut self) -> &mut [u8] {
207 unsafe { self.mapping.bytes_mut(self.mapping.mapped_len) }
209 }
210
211 pub fn into_local_writer(self, expected_len: usize) -> Result<LocalWriterRegion, MachError> {
213 self.validate_transition_size(expected_len)?;
214 let peer_entry = MemoryEntry::<ReadOnlyCapability>::new(self.mapping.task, &self.mapping)?;
215 Ok(LocalWriterRegion {
216 mapping: self.mapping,
217 peer_entry,
218 len: expected_len,
219 })
220 }
221
222 pub fn into_remote_writer(
224 mut self,
225 expected_len: usize,
226 ) -> Result<RemoteWriterRegion, MachError> {
227 self.validate_transition_size(expected_len)?;
228 let peer_entry = MemoryEntry::<ReadWriteCapability>::new(self.mapping.task, &self.mapping)?;
229 self.mapping.protect(VM_PROT_READ, false)?;
230 self.mapping.protect(VM_PROT_READ, true)?;
231 Ok(RemoteWriterRegion {
232 mapping: self.mapping,
233 peer_entry,
234 len: expected_len,
235 })
236 }
237
238 fn validate_transition_size(&self, expected_len: usize) -> Result<(), MachError> {
239 if expected_len == self.mapping.mapped_len && expected_len != 0 {
240 Ok(())
241 } else {
242 Err(MachError::InvalidViewSize {
243 requested: expected_len,
244 region: self.mapping.mapped_len,
245 })
246 }
247 }
248
249 pub fn into_bound_local_writer(
251 self,
252 expected: ValidationExpectations,
253 topology: RegionSetLayout,
254 ) -> Result<WriterRegion<MacWriterMapping>, MacBindingError> {
255 let layout =
258 unsafe { ValidatedRegionLayout::validate(self.as_bytes(), expected, &topology) }?;
259 let capability_len = self.len();
260 let region = self.into_local_writer(capability_len)?;
261 Ok(WriterRegion::new(
262 MacWriterMapping { region },
263 layout,
264 topology,
265 )?)
266 }
267
268 pub fn into_bound_remote_writer(
270 self,
271 expected: ValidationExpectations,
272 topology: RegionSetLayout,
273 ) -> Result<ReaderRegion<MacReaderMapping>, MacBindingError> {
274 let layout =
276 unsafe { ValidatedRegionLayout::validate(self.as_bytes(), expected, &topology) }?;
277 let capability_len = self.len();
278 let region = self.into_remote_writer(capability_len)?;
279 Ok(ReaderRegion::new(
280 MacReaderMapping { region },
281 layout,
282 topology,
283 )?)
284 }
285
286 pub fn transfer_local_writer(
288 self,
289 expected: ValidationExpectations,
290 topology: RegionSetLayout,
291 channel: &bootstrap::ParentChannel,
292 ) -> Result<WriterRegion<TransferredWriterMapping>, MacBindingError> {
293 let layout =
295 unsafe { ValidatedRegionLayout::validate(self.as_bytes(), expected, &topology) }?;
296 let capability_len = self.len();
297 let region = self.into_local_writer(capability_len)?;
298 channel.send(region.peer_entry.name)?;
299 let LocalWriterRegion {
300 mapping,
301 peer_entry,
302 len: _,
303 } = region;
304 drop(peer_entry);
305 Ok(WriterRegion::new(
306 TransferredWriterMapping { mapping },
307 layout,
308 topology,
309 )?)
310 }
311
312 pub fn transfer_remote_writer(
314 self,
315 expected: ValidationExpectations,
316 topology: RegionSetLayout,
317 channel: &bootstrap::ParentChannel,
318 ) -> Result<ReaderRegion<TransferredReaderMapping>, MacBindingError> {
319 let layout =
321 unsafe { ValidatedRegionLayout::validate(self.as_bytes(), expected, &topology) }?;
322 let capability_len = self.len();
323 let region = self.into_remote_writer(capability_len)?;
324 channel.send(region.peer_entry.name)?;
325 let RemoteWriterRegion {
326 mapping,
327 peer_entry,
328 len: _,
329 } = region;
330 drop(peer_entry);
331 Ok(ReaderRegion::new(
332 TransferredReaderMapping { mapping },
333 layout,
334 topology,
335 )?)
336 }
337}
338
339pub struct TransferredWriterMapping {
341 mapping: Mapping,
342}
343unsafe impl SoleWriterMapping for TransferredWriterMapping {
345 fn base(&self) -> NonNull<u8> {
346 self.mapping.address
347 }
348 fn len(&self) -> usize {
349 self.mapping.mapped_len
350 }
351}
352
353pub struct TransferredReaderMapping {
355 mapping: Mapping,
356}
357unsafe impl ReadOnlyMapping for TransferredReaderMapping {
359 fn base(&self) -> NonNull<u8> {
360 self.mapping.address
361 }
362 fn len(&self) -> usize {
363 self.mapping.mapped_len
364 }
365}
366
367pub struct ImportedReaderMapping {
369 mapping: Mapping,
370}
371unsafe impl ReadOnlyMapping for ImportedReaderMapping {
373 fn base(&self) -> NonNull<u8> {
374 self.mapping.address
375 }
376 fn len(&self) -> usize {
377 self.mapping.mapped_len
378 }
379}
380
381pub struct ImportedWriterMapping {
383 mapping: Mapping,
384}
385unsafe impl SoleWriterMapping for ImportedWriterMapping {
387 fn base(&self) -> NonNull<u8> {
388 self.mapping.address
389 }
390 fn len(&self) -> usize {
391 self.mapping.mapped_len
392 }
393}
394
395impl bootstrap::ChildChannel {
396 pub fn receive_reader(
398 &self,
399 len: usize,
400 expected: ValidationExpectations,
401 topology: RegionSetLayout,
402 ) -> Result<ReaderRegion<ImportedReaderMapping>, MacBindingError> {
403 let right = self.receive()?;
404 let mapping = Mapping::map_port(current_task(), len, right.name(), VM_PROT_READ)?;
405 let bytes = unsafe { mapping.bytes(len) };
407 let layout = unsafe { ValidatedRegionLayout::validate(bytes, expected, &topology) }?;
408 drop(right);
409 Ok(ReaderRegion::new(
410 ImportedReaderMapping { mapping },
411 layout,
412 topology,
413 )?)
414 }
415
416 pub fn receive_writer(
418 &self,
419 len: usize,
420 expected: ValidationExpectations,
421 topology: RegionSetLayout,
422 ) -> Result<WriterRegion<ImportedWriterMapping>, MacBindingError> {
423 let right = self.receive()?;
424 let mapping = Mapping::map_port(
425 current_task(),
426 len,
427 right.name(),
428 VM_PROT_READ | VM_PROT_WRITE,
429 )?;
430 let bytes = unsafe { mapping.bytes(len) };
432 let layout = unsafe { ValidatedRegionLayout::validate(bytes, expected, &topology) }?;
433 drop(right);
434 Ok(WriterRegion::new(
435 ImportedWriterMapping { mapping },
436 layout,
437 topology,
438 )?)
439 }
440}
441
442pub struct MacWriterMapping {
444 region: LocalWriterRegion,
445}
446
447unsafe impl SoleWriterMapping for MacWriterMapping {
450 fn base(&self) -> NonNull<u8> {
451 self.region.mapping.address
452 }
453 fn len(&self) -> usize {
454 self.region.mapping.mapped_len
455 }
456}
457
458pub struct MacReaderMapping {
460 region: RemoteWriterRegion,
461}
462
463unsafe impl ReadOnlyMapping for MacReaderMapping {
466 fn base(&self) -> NonNull<u8> {
467 self.region.mapping.address
468 }
469 fn len(&self) -> usize {
470 self.region.mapping.mapped_len
471 }
472}
473
474#[derive(Debug)]
478#[allow(dead_code)]
479pub struct LocalWriterRegion {
480 mapping: Mapping,
481 peer_entry: MemoryEntry<ReadOnlyCapability>,
482 len: usize,
483}
484
485impl LocalWriterRegion {
486 pub const fn len(&self) -> usize {
488 self.len
489 }
490
491 pub const fn is_empty(&self) -> bool {
493 self.len == 0
494 }
495}
496
497#[derive(Debug)]
501#[allow(dead_code)]
502pub struct RemoteWriterRegion {
503 mapping: Mapping,
504 peer_entry: MemoryEntry<ReadWriteCapability>,
505 len: usize,
506}
507
508impl RemoteWriterRegion {
509 pub const fn len(&self) -> usize {
511 self.len
512 }
513
514 pub const fn is_empty(&self) -> bool {
516 self.len == 0
517 }
518}
519
520#[derive(Debug)]
521struct Mapping {
522 task: MachPort,
523 address: NonNull<u8>,
524 mapped_len: usize,
525}
526
527impl Mapping {
528 fn allocate(task: MachPort, mapped_len: usize) -> Result<Self, MachError> {
529 let mut address = 0;
530 let result = unsafe {
532 mach_vm_allocate(
533 task,
534 &mut address,
535 mapped_len as MachVmSize,
536 VM_FLAGS_ANYWHERE,
537 )
538 };
539 check_kernel("mach_vm_allocate", result)?;
540 Self::from_allocated(task, address, mapped_len)
541 }
542
543 #[cfg(test)]
544 fn map_entry<Access: CapabilityAccess>(
545 task: MachPort,
546 mapped_len: usize,
547 entry: &MemoryEntry<Access>,
548 ) -> Result<Self, MachError> {
549 Self::map_port(task, mapped_len, entry.name, Access::PROTECTION)
550 }
551
552 fn map_port(
553 task: MachPort,
554 mapped_len: usize,
555 port: MachPort,
556 protection: VmProt,
557 ) -> Result<Self, MachError> {
558 debug_assert_eq!(protection & VM_PROT_EXECUTE, 0);
559 let mut address = 0;
560 let result = unsafe {
562 mach_vm_map(
563 task,
564 &mut address,
565 mapped_len as MachVmSize,
566 0,
567 VM_FLAGS_ANYWHERE,
568 port,
569 0,
570 0,
571 protection,
572 protection,
573 VM_INHERIT_NONE,
574 )
575 };
576 check_kernel("mach_vm_map", result)?;
577 Self::from_allocated(task, address, mapped_len)
578 }
579
580 fn protect(&mut self, protection: VmProt, set_maximum: bool) -> Result<(), MachError> {
581 debug_assert_eq!(protection & VM_PROT_EXECUTE, 0);
582 let result = unsafe {
584 mach_vm_protect(
585 self.task,
586 self.address(),
587 self.mapped_len as MachVmSize,
588 c_int::from(set_maximum),
589 protection,
590 )
591 };
592 check_kernel("mach_vm_protect", result)
593 }
594
595 fn from_allocated(
596 task: MachPort,
597 address: MachVmAddress,
598 mapped_len: usize,
599 ) -> Result<Self, MachError> {
600 let address_usize = match usize::try_from(address) {
601 Ok(value) => value,
602 Err(_) => {
603 deallocate_mapping(task, address, mapped_len);
604 return Err(MachError::InvalidAddress(address));
605 }
606 };
607 let Some(address) = NonNull::new(address_usize as *mut u8) else {
608 deallocate_mapping(task, 0, mapped_len);
609 return Err(MachError::InvalidAddress(0));
610 };
611 Ok(Self {
612 task,
613 address,
614 mapped_len,
615 })
616 }
617
618 fn address(&self) -> MachVmAddress {
619 self.address.as_ptr() as usize as MachVmAddress
620 }
621
622 unsafe fn bytes(&self, len: usize) -> &[u8] {
623 assert!(len <= self.mapped_len && len <= isize::MAX as usize);
624 unsafe { std::slice::from_raw_parts(self.address.as_ptr(), len) }
628 }
629
630 unsafe fn bytes_mut(&mut self, len: usize) -> &mut [u8] {
631 assert!(len <= self.mapped_len && len <= isize::MAX as usize);
632 unsafe { std::slice::from_raw_parts_mut(self.address.as_ptr(), len) }
636 }
637}
638
639impl Drop for Mapping {
640 fn drop(&mut self) {
641 deallocate_mapping(self.task, self.address(), self.mapped_len);
642 }
643}
644
645#[derive(Debug)]
646struct ReadOnlyCapability;
647#[derive(Debug)]
648struct ReadWriteCapability;
649
650trait CapabilityAccess {
651 const PROTECTION: VmProt;
652}
653
654impl CapabilityAccess for ReadOnlyCapability {
655 const PROTECTION: VmProt = VM_PROT_READ;
656}
657impl CapabilityAccess for ReadWriteCapability {
658 const PROTECTION: VmProt = VM_PROT_READ | VM_PROT_WRITE;
659}
660
661#[derive(Debug)]
662struct MemoryEntry<Access> {
663 task: MachPort,
664 name: MachPort,
665 _access: PhantomData<fn() -> Access>,
666}
667
668impl<Access: CapabilityAccess> MemoryEntry<Access> {
669 fn new(task: MachPort, mapping: &Mapping) -> Result<Self, MachError> {
670 let mut entry_size = mapping.mapped_len as MemoryObjectSize;
671 let mut name = MACH_PORT_NULL;
672 let permission = Access::PROTECTION | MAP_MEM_VM_SHARE;
673 debug_assert_eq!(permission & VM_PROT_EXECUTE, 0);
674 let result = unsafe {
676 mach_make_memory_entry_64(
677 task,
678 &mut entry_size,
679 mapping.address(),
680 permission,
681 &mut name,
682 MACH_PORT_NULL,
683 )
684 };
685 if result != KERN_SUCCESS {
686 if name != MACH_PORT_NULL {
687 deallocate_port(task, name);
688 }
689 return Err(MachError::Kernel {
690 operation: "mach_make_memory_entry_64",
691 code: result,
692 });
693 }
694 if name == MACH_PORT_NULL {
695 return Err(MachError::NullMemoryEntry);
696 }
697 let entry = Self {
698 task,
699 name,
700 _access: PhantomData,
701 };
702 if entry_size != mapping.mapped_len as MemoryObjectSize {
703 return Err(MachError::UnexpectedEntrySize {
704 expected: mapping.mapped_len,
705 actual: entry_size,
706 });
707 }
708 Ok(entry)
709 }
710}
711
712impl<Access> Drop for MemoryEntry<Access> {
713 fn drop(&mut self) {
714 deallocate_port(self.task, self.name);
715 }
716}
717
718fn current_task() -> MachPort {
719 unsafe { mach_task_self_ }
721}
722
723fn page_size() -> Result<usize, MachError> {
724 let size = unsafe { getpagesize() };
726 let Ok(converted) = usize::try_from(size) else {
727 return Err(MachError::InvalidPageSize(size));
728 };
729 if converted == 0 || !converted.is_power_of_two() {
730 return Err(MachError::InvalidPageSize(size));
731 }
732 Ok(converted)
733}
734
735fn page_align(size: usize, page_size: usize) -> Result<usize, MachError> {
736 if size == 0 {
737 return Err(MachError::ZeroSize);
738 }
739 let aligned = size
740 .checked_add(page_size - 1)
741 .map(|value| value & !(page_size - 1))
742 .ok_or(MachError::SizeOverflow { requested: size })?;
743 if aligned > isize::MAX as usize {
744 return Err(MachError::SizeOverflow { requested: size });
745 }
746 Ok(aligned)
747}
748
749fn check_kernel(operation: &'static str, code: KernReturn) -> Result<(), MachError> {
750 if code == KERN_SUCCESS {
751 Ok(())
752 } else {
753 Err(MachError::Kernel { operation, code })
754 }
755}
756
757fn deallocate_mapping(task: MachPort, address: MachVmAddress, mapped_len: usize) {
758 let _ = unsafe { mach_vm_deallocate(task, address, mapped_len as MachVmSize) };
760}
761
762fn deallocate_port(task: MachPort, name: MachPort) {
763 let _ = unsafe { mach_port_deallocate(task, name) };
765}
766
767#[cfg(test)]
768mod tests {
769 use super::*;
770 use native_ipc_core::layout::{
771 AcknowledgementRouteSpec, Endpoint, LayoutLimits, RegionSetLayout, RegionSpec, RoleId,
772 };
773 use std::mem::size_of;
774
775 struct TestWriterWitness<'a>(&'a mut Mapping);
776 struct TestReaderWitness<'a>(&'a Mapping);
777
778 unsafe impl SoleWriterMapping for TestWriterWitness<'_> {
781 fn base(&self) -> NonNull<u8> {
782 self.0.address
783 }
784 fn len(&self) -> usize {
785 self.0.mapped_len
786 }
787 }
788
789 unsafe impl ReadOnlyMapping for TestReaderWitness<'_> {
792 fn base(&self) -> NonNull<u8> {
793 self.0.address
794 }
795 fn len(&self) -> usize {
796 self.0.mapped_len
797 }
798 }
799
800 #[test]
801 fn read_only_capability_rejects_writable_mapping() {
802 let owner = QuiescentRegion::new(37).unwrap();
803 let capability_len = owner.len();
804 let runtime = owner.into_local_writer(capability_len).unwrap();
805 let mut address = 0;
806 let protection = VM_PROT_READ | VM_PROT_WRITE;
807 let result = unsafe {
809 mach_vm_map(
810 runtime.mapping.task,
811 &mut address,
812 runtime.mapping.mapped_len as MachVmSize,
813 0,
814 VM_FLAGS_ANYWHERE,
815 runtime.peer_entry.name,
816 0,
817 0,
818 protection,
819 protection,
820 VM_INHERIT_NONE,
821 )
822 };
823 if result == KERN_SUCCESS {
824 deallocate_mapping(runtime.mapping.task, address, runtime.mapping.mapped_len);
825 }
826 assert_ne!(result, KERN_SUCCESS);
827 }
828
829 #[test]
830 fn executable_protection_upgrade_is_rejected() {
831 let owner = QuiescentRegion::new(37).unwrap();
832 let capability_len = owner.len();
833 let runtime = owner.into_local_writer(capability_len).unwrap();
834 let result = unsafe {
836 mach_vm_protect(
837 runtime.mapping.task,
838 runtime.mapping.address(),
839 runtime.mapping.mapped_len as MachVmSize,
840 0,
841 VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE,
842 )
843 };
844 assert_ne!(result, KERN_SUCCESS);
845 }
846
847 #[test]
848 fn remote_writer_downgrades_local_mapping_before_escape() {
849 let mut owner = QuiescentRegion::new(19).unwrap();
850 owner.as_bytes_mut()[0] = 7;
851 let capability_len = owner.len();
852 let mut runtime = owner.into_remote_writer(capability_len).unwrap();
853 assert!(
854 runtime
855 .mapping
856 .protect(VM_PROT_READ | VM_PROT_WRITE, false)
857 .is_err()
858 );
859 let mut peer = Mapping::map_entry(
860 runtime.mapping.task,
861 runtime.mapping.mapped_len,
862 &runtime.peer_entry,
863 )
864 .unwrap();
865 let peer_bytes = unsafe { peer.bytes_mut(19) };
867 peer_bytes[3..8].copy_from_slice(b"world");
868 drop(peer);
869 assert_eq!(&unsafe { runtime.mapping.bytes(19) }[3..8], b"world");
871 }
872
873 #[test]
874 fn local_writer_peer_observes_quiescent_initialization() {
875 let mut owner = QuiescentRegion::new(37).unwrap();
876 owner.as_bytes_mut()[..5].copy_from_slice(b"hello");
877 let capability_len = owner.len();
878 let runtime = owner.into_local_writer(capability_len).unwrap();
879 let peer = Mapping::map_entry(
880 runtime.mapping.task,
881 runtime.mapping.mapped_len,
882 &runtime.peer_entry,
883 )
884 .unwrap();
885 assert_eq!(&unsafe { peer.bytes(37) }[..5], b"hello");
887 }
888
889 #[test]
890 fn rejects_bad_sizes_and_matches_sdk_scalars() {
891 assert_eq!(QuiescentRegion::new(0).unwrap_err(), MachError::ZeroSize);
892 assert_eq!(
893 page_align(usize::MAX, 4096).unwrap_err(),
894 MachError::SizeOverflow {
895 requested: usize::MAX
896 }
897 );
898 assert_eq!(size_of::<MachPort>(), 4);
899 assert_eq!(size_of::<MachVmAddress>(), 8);
900 assert_eq!(ReadOnlyCapability::PROTECTION, VM_PROT_READ);
901 assert_eq!(
902 ReadWriteCapability::PROTECTION,
903 VM_PROT_READ | VM_PROT_WRITE
904 );
905 }
906
907 #[test]
908 fn page_capability_padding_is_explicit_validated_and_bound() {
909 let producer = RoleId::new(1).unwrap();
910 let peer = RoleId::new(2).unwrap();
911 let specs = [
912 RegionSpec {
913 role: producer,
914 writer: Endpoint::Initiator,
915 slot_count: 1,
916 payload_bytes: 16,
917 acknowledgement_count: 1,
918 },
919 RegionSpec {
920 role: peer,
921 writer: Endpoint::Responder,
922 slot_count: 1,
923 payload_bytes: 16,
924 acknowledgement_count: 1,
925 },
926 ];
927 let routes = [
928 AcknowledgementRouteSpec {
929 owner: peer,
930 target: producer,
931 slot_index: 0,
932 cell_index: 0,
933 },
934 AcknowledgementRouteSpec {
935 owner: producer,
936 target: peer,
937 slot_index: 0,
938 cell_index: 0,
939 },
940 ];
941 let set = RegionSetLayout::calculate(
942 [3; 32],
943 7,
944 &specs,
945 &routes,
946 LayoutLimits {
947 maximum_mapping_size: 1 << 20,
948 maximum_slot_count: 2,
949 maximum_acknowledgement_count: 2,
950 maximum_payload_bytes: 64,
951 },
952 )
953 .unwrap();
954 let layout = set.region(producer).unwrap();
955 let mut owner = QuiescentRegion::new(layout.total_size() as usize).unwrap();
956 assert!(owner.len() >= owner.logical_len());
957 assert!(owner.len().is_multiple_of(page_size().unwrap()));
958 layout.encode_into(owner.as_bytes_mut()).unwrap();
959 let expected = ValidationExpectations {
960 schema_id: [3; 32],
961 generation: 7,
962 role: producer,
963 writer: Endpoint::Initiator,
964 maximum_mapping_size: owner.len() as u64,
965 };
966 let mut bound = owner
967 .into_bound_local_writer(expected, set.clone())
968 .unwrap();
969 bound
970 .slot(0)
971 .unwrap()
972 .prepare_publish(1, None)
973 .unwrap()
974 .publish(4)
975 .unwrap();
976
977 let mut hostile = QuiescentRegion::new(layout.total_size() as usize).unwrap();
978 layout.encode_into(hostile.as_bytes_mut()).unwrap();
979 let last = hostile.len() - 1;
980 hostile.as_bytes_mut()[last] = 1;
981 let expected = ValidationExpectations {
982 schema_id: [3; 32],
983 generation: 7,
984 role: producer,
985 writer: Endpoint::Initiator,
986 maximum_mapping_size: hostile.len() as u64,
987 };
988 assert!(matches!(
989 hostile.into_bound_local_writer(expected, set),
990 Err(MacBindingError::Layout(
991 LayoutError::CapabilityPaddingNotZero
992 ))
993 ));
994 }
995
996 #[test]
997 fn mach_mapping_completes_core_publish_observe_and_ack_path() {
998 let producer = RoleId::new(1).unwrap();
999 let acknowledger = RoleId::new(2).unwrap();
1000 let specs = [
1001 RegionSpec {
1002 role: producer,
1003 writer: Endpoint::Initiator,
1004 slot_count: 1,
1005 payload_bytes: 16,
1006 acknowledgement_count: 1,
1007 },
1008 RegionSpec {
1009 role: acknowledger,
1010 writer: Endpoint::Responder,
1011 slot_count: 1,
1012 payload_bytes: 16,
1013 acknowledgement_count: 1,
1014 },
1015 ];
1016 let routes = [
1017 AcknowledgementRouteSpec {
1018 owner: acknowledger,
1019 target: producer,
1020 slot_index: 0,
1021 cell_index: 0,
1022 },
1023 AcknowledgementRouteSpec {
1024 owner: producer,
1025 target: acknowledger,
1026 slot_index: 0,
1027 cell_index: 0,
1028 },
1029 ];
1030 let topology = RegionSetLayout::calculate(
1031 [9; 32],
1032 11,
1033 &specs,
1034 &routes,
1035 LayoutLimits {
1036 maximum_mapping_size: 1 << 20,
1037 maximum_slot_count: 2,
1038 maximum_acknowledgement_count: 2,
1039 maximum_payload_bytes: 64,
1040 },
1041 )
1042 .unwrap();
1043
1044 let producer_layout = topology.region(producer).unwrap();
1045 let mut producer_owner =
1046 QuiescentRegion::new(producer_layout.total_size() as usize).unwrap();
1047 producer_layout
1048 .encode_into(producer_owner.as_bytes_mut())
1049 .unwrap();
1050 let producer_expected = ValidationExpectations {
1051 schema_id: [9; 32],
1052 generation: 11,
1053 role: producer,
1054 writer: Endpoint::Initiator,
1055 maximum_mapping_size: producer_owner.len() as u64,
1056 };
1057 let producer_validated = unsafe {
1058 ValidatedRegionLayout::validate(producer_owner.as_bytes(), producer_expected, &topology)
1059 }
1060 .unwrap();
1061 let producer_len = producer_owner.len();
1062 let mut producer_runtime = producer_owner.into_local_writer(producer_len).unwrap();
1063 let producer_peer = Mapping::map_entry(
1064 producer_runtime.mapping.task,
1065 producer_runtime.mapping.mapped_len,
1066 &producer_runtime.peer_entry,
1067 )
1068 .unwrap();
1069
1070 let ack_layout = topology.region(acknowledger).unwrap();
1071 let mut ack_owner = QuiescentRegion::new(ack_layout.total_size() as usize).unwrap();
1072 ack_layout.encode_into(ack_owner.as_bytes_mut()).unwrap();
1073 let ack_expected = ValidationExpectations {
1074 schema_id: [9; 32],
1075 generation: 11,
1076 role: acknowledger,
1077 writer: Endpoint::Responder,
1078 maximum_mapping_size: ack_owner.len() as u64,
1079 };
1080 let ack_validated = unsafe {
1081 ValidatedRegionLayout::validate(ack_owner.as_bytes(), ack_expected, &topology)
1082 }
1083 .unwrap();
1084 let ack_len = ack_owner.len();
1085 let mut ack_runtime = ack_owner.into_local_writer(ack_len).unwrap();
1086 let ack_peer = Mapping::map_entry(
1087 ack_runtime.mapping.task,
1088 ack_runtime.mapping.mapped_len,
1089 &ack_runtime.peer_entry,
1090 )
1091 .unwrap();
1092
1093 {
1094 let mut writer = WriterRegion::new(
1095 TestWriterWitness(&mut producer_runtime.mapping),
1096 producer_validated.clone(),
1097 topology.clone(),
1098 )
1099 .unwrap();
1100 writer.publish(0, 1, None, b"mach").unwrap();
1101 }
1102 let reader = ReaderRegion::new(
1103 TestReaderWitness(&producer_peer),
1104 producer_validated,
1105 topology.clone(),
1106 )
1107 .unwrap();
1108 let observation = reader.slot(0).unwrap().observe(1).unwrap();
1109 reader.slot(0).unwrap().recheck(observation).unwrap();
1110 assert_eq!(reader.copy_payload(0, 1).unwrap(), b"mach");
1111
1112 {
1113 let mut writer = WriterRegion::new(
1114 TestWriterWitness(&mut ack_runtime.mapping),
1115 ack_validated.clone(),
1116 topology.clone(),
1117 )
1118 .unwrap();
1119 writer
1120 .acknowledgement(producer, 0)
1121 .unwrap()
1122 .acknowledge(observation)
1123 .unwrap();
1124 }
1125 let reader =
1126 ReaderRegion::new(TestReaderWitness(&ack_peer), ack_validated, topology).unwrap();
1127 let acknowledged = reader.acknowledgement(producer, 0).unwrap().observe();
1128 assert_eq!(acknowledged.sequence(), 1);
1129 assert_eq!(acknowledged.slot_index(), 0);
1130 assert_eq!(acknowledged.cell_index(), 0);
1131 }
1132}