/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use mach2::{ kern_return::{kern_return_t, KERN_SUCCESS}, mach_port::{ mach_port_allocate, mach_port_deallocate, mach_port_insert_right, mach_port_mod_refs, }, message::{ mach_msg, mach_msg_base_t, mach_msg_body_t, mach_msg_header_t, mach_msg_port_descriptor_t, mach_msg_trailer_t, MACH_MSGH_BITS, MACH_MSGH_BITS_COMPLEX, MACH_MSG_SUCCESS, MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND, MACH_RCV_MSG, MACH_RCV_TIMEOUT, MACH_SEND_MSG, MACH_SEND_TIMEOUT, }, port::{mach_port_name_t, mach_port_t, MACH_PORT_NULL, MACH_PORT_RIGHT_RECEIVE}, traps::mach_task_self, }; use nix::errno::Errno; use std::{ alloc::{alloc_zeroed, dealloc, Layout}, cmp::max, fmt::Display, mem::ManuallyDrop, result, slice, }; use thiserror::Error; use crate::IO_TIMEOUT; pub type Result = result::Result; pub type ProcessHandle = (); #[derive(Error, Debug)] pub enum PlatformError { #[error("Bootstrap look-up failure: {0}")] BootstrapLookUp(kern_return_t), #[error("Bootstrap registration failure: {0}")] BootstrapRegister(kern_return_t), #[error("Could not get random value: {0}")] GetRandom(getrandom::Error), #[error("Could not insert a send right: {0}")] InsertSendRight(kern_return_t), #[error("kevent() call failed with error: {0}")] KernelEventError(Errno), #[error("kqueue() call failed with error: {0}")] KernelQueueError(Errno), #[error("Could not create a mach port: {0}")] MachPortAllocate(kern_return_t), #[error("No more senders remain for this port")] NoMoreSenders, #[error("mach_msg() call failed with error: {0}")] ReceiveFailure(kern_return_t), #[error("mach_port_request_notification failed with error: {0}")] RequestNotification(kern_return_t), #[error("mach_msg() call failed with error: {0}")] SendFailure(kern_return_t), #[error("Value exceeds a 32-bit integer")] ValueTooLarge, } /**************************************************************************** * Ports and rights * ****************************************************************************/ pub trait AsRawPort { fn as_raw_port(&self) -> mach_port_t; } #[repr(transparent)] pub struct ReceiveRight(mach_port_name_t); impl ReceiveRight { pub fn new() -> Result { let mut receive_right: mach_port_name_t = MACH_PORT_NULL; let rv = unsafe { mach_port_allocate( mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &mut receive_right, ) }; if rv == KERN_SUCCESS { Ok(ReceiveRight(receive_right)) } else { Err(PlatformError::MachPortAllocate(rv)) } } pub(crate) unsafe fn from_raw_port(port: mach_port_name_t) -> ReceiveRight { ReceiveRight(port) } pub fn into_raw_port(self) -> mach_port_name_t { ManuallyDrop::new(self).0 } pub(crate) fn insert_send_right(&self) -> Result { let rv = unsafe { mach_port_insert_right( mach_task_self(), /* name */ self.0, /* right */ self.0, MACH_MSG_TYPE_MAKE_SEND, ) }; if rv == KERN_SUCCESS { // SAFETY: We verified that `self.0` contains a valid send right // which we just inserted. Ok(unsafe { SendRight::from_raw_port(self.0) }) } else { Err(PlatformError::InsertSendRight(rv)) } } } impl Drop for ReceiveRight { fn drop(&mut self) { let rv = unsafe { mach_port_mod_refs(mach_task_self(), self.0, MACH_PORT_RIGHT_RECEIVE, -1) }; assert!( rv == KERN_SUCCESS, "Could not dispose of a receive right, error {rv}" ); } } impl AsRawPort for ReceiveRight { fn as_raw_port(&self) -> mach_port_name_t { self.0 } } #[repr(transparent)] pub struct SendRight(mach_port_name_t); impl SendRight { /// Create an owned send right from a raw port right name. /// /// # Safety /// /// `port` must be a valid send right to a Mach port. pub unsafe fn from_raw_port(port: mach_port_name_t) -> SendRight { SendRight(port) } pub fn into_raw_port(self) -> mach_port_name_t { ManuallyDrop::new(self).0 } } impl AsRawPort for SendRight { fn as_raw_port(&self) -> mach_port_name_t { self.0 } } impl Drop for SendRight { fn drop(&mut self) { // We use `mach_port_deallocate()` instead of `mach_port_mod_refs()` // because it doesn't fail if the corresponding receive right has // already been destroyed. let rv = unsafe { mach_port_deallocate(mach_task_self(), self.0) }; assert!( rv == KERN_SUCCESS, "Could not dispose of a send right {}, error {rv}", self.0, ); } } pub enum OwnedRight { Send(SendRight), Receive(ReceiveRight), } /**************************************************************************** * Mach messages * ****************************************************************************/ pub struct MachMessageWrapper { layout: Layout, ptr: *mut u8, } impl MachMessageWrapper { pub fn for_send(size: usize, descriptors: u32) -> MachMessageWrapper { let total_size = MachMessageWrapper::compute_size(size, descriptors); let layout = Layout::from_size_align(total_size as usize, align_of::()) .expect("Invalid layout for a Mach message"); let ptr = unsafe { alloc_zeroed(layout) }; let base_msg = unsafe { &mut *(ptr as *mut mach_msg_base_t) }; base_msg.header.msgh_size = total_size; base_msg.body.msgh_descriptor_count = descriptors; MachMessageWrapper { layout, ptr } } pub fn for_recv(expected_size: usize, expected_descriptors: u32) -> MachMessageWrapper { let total_size = MachMessageWrapper::compute_size( expected_size + size_of::(), expected_descriptors, ); let layout = Layout::from_size_align(total_size as usize, align_of::()) .expect("Invalid layout for a Mach message"); let ptr = unsafe { alloc_zeroed(layout) }; let base_msg = unsafe { &mut *(ptr as *mut mach_msg_base_t) }; base_msg.header.msgh_size = total_size; base_msg.body.msgh_descriptor_count = expected_descriptors; MachMessageWrapper { layout, ptr } } pub fn as_ptr(&self) -> *mut mach_msg_header_t { self.ptr as *mut mach_msg_header_t } pub fn data_size(&self) -> usize { // `data_offset()` measures everything but the data in the message. self.header().msgh_size as usize - self.data_offset() } pub fn descriptors_len(&self) -> usize { self.body().msgh_descriptor_count as usize } pub fn header(&self) -> &mach_msg_header_t { unsafe { &*(self.ptr as *const mach_msg_header_t) } } pub fn header_mut(&mut self) -> &mut mach_msg_header_t { unsafe { &mut *(self.ptr as *mut mach_msg_header_t) } } fn body_offset() -> usize { size_of::() } pub fn body(&self) -> &mach_msg_body_t { unsafe { &*(self.ptr.add(Self::body_offset()) as *const mach_msg_body_t) } } pub fn body_mut(&mut self) -> &mut mach_msg_body_t { unsafe { &mut *(self.ptr.add(Self::body_offset()) as *mut mach_msg_body_t) } } fn descriptors_offset() -> usize { Self::body_offset() + size_of::() } pub fn descriptors(&self) -> &[mach_msg_port_descriptor_t] { unsafe { let ptr = self.ptr.add(Self::descriptors_offset()) as *const mach_msg_port_descriptor_t; slice::from_raw_parts(ptr, self.descriptors_len()) } } pub fn descriptors_mut(&mut self) -> &mut [mach_msg_port_descriptor_t] { unsafe { let ptr = self.ptr.add(Self::descriptors_offset()) as *mut mach_msg_port_descriptor_t; slice::from_raw_parts_mut(ptr, self.descriptors_len()) } } fn data_offset(&self) -> usize { Self::descriptors_offset() + (self.descriptors_len() * size_of::()) } pub fn data(&self) -> &[u8] { unsafe { let ptr = self.ptr.add(self.data_offset()); slice::from_raw_parts(ptr, self.data_size()) } } pub fn data_mut(&mut self) -> &mut [u8] { unsafe { let ptr = self.ptr.add(self.data_offset()); slice::from_raw_parts_mut(ptr, self.data_size()) } } fn size(&self) -> u32 { self.header().msgh_size } fn compute_size(size: usize, descriptors: u32) -> u32 { // mach message data is packed on 4-bytes boundaries let rounded_data = size.next_multiple_of(4); (size_of::() + size_of::() + (size_of::() * (descriptors as usize)) + rounded_data) .try_into() .expect("Mach messages cannot be larger than 4 GiB") } } impl Display for MachMessageWrapper { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let header = self.header(); let body = self.body(); write!(f, "{header:?} {body:?}") } } impl Drop for MachMessageWrapper { fn drop(&mut self) { unsafe { dealloc(self.ptr, self.layout) } } } pub fn mach_msg_send(port: &SendRight, msg: &mut MachMessageWrapper) -> Result<()> { let msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0) | if msg.body().msgh_descriptor_count != 0 { MACH_MSGH_BITS_COMPLEX } else { 0 }; let header = msg.header_mut(); header.msgh_bits |= msgh_bits; header.msgh_remote_port = port.as_raw_port(); let rv = unsafe { mach_msg( msg.as_ptr(), MACH_SEND_MSG | MACH_SEND_TIMEOUT, msg.size(), 0, MACH_PORT_NULL, IO_TIMEOUT as u32, MACH_PORT_NULL, ) }; if rv == MACH_MSG_SUCCESS { Ok(()) } else { Err(PlatformError::SendFailure(rv)) } } // Remove when we vendor mach2 0.6.0 static MACH_NOTIFY_NO_SENDERS: i32 = 0o106; pub fn mach_msg_recv( port: &ReceiveRight, expected_size: usize, expected_descriptors: u32, ) -> Result { // We can receive a MACH_NOTIFY_NO_SENDERS message at any time and it comes // with an 8-byte sized payload which we must account for. const MIN_MESSAGE_SIZE: usize = 8; let expected_size = max(expected_size, MIN_MESSAGE_SIZE); let mut msg = MachMessageWrapper::for_recv(expected_size, expected_descriptors); msg.header_mut().msgh_local_port = port.as_raw_port(); let rv = unsafe { mach_msg( msg.as_ptr(), MACH_RCV_MSG | MACH_RCV_TIMEOUT, 0, msg.size(), port.as_raw_port(), IO_TIMEOUT as u32, MACH_PORT_NULL, ) }; if msg.header().msgh_id == MACH_NOTIFY_NO_SENDERS { // We've got a message, but it's a disconnection return Err(PlatformError::NoMoreSenders); } if rv == MACH_MSG_SUCCESS { Ok(msg) } else { Err(PlatformError::ReceiveFailure(rv)) } }