+ // Allocate a port to listen on in this monitoring task
+ mach_port_options_t options = { .flags = MPO_IMPORTANCE_RECEIVER | MPO_CONTEXT_AS_GUARD | MPO_STRICT,
+ .mpl = { MACH_PORT_QLIMIT_DEFAULT }};
+ if ((*kr = mach_port_construct(mach_task_self(), &options, (mach_port_context_t)this, &_receivePortInMonitor))) {
+ teardown();
+ return;
+ }
+ if (_targetTask == mach_task_self()) {
+ _sendPortInTarget = _receivePortInMonitor;
+ (void)mach_port_insert_right(_targetTask, _sendPortInTarget, _receivePortInMonitor, MACH_MSG_TYPE_MAKE_SEND);
+ } else {
+ // Insert a deadname right into the port to trigger notifications
+ kern_return_t r = KERN_NAME_EXISTS;
+ while (r == KERN_NAME_EXISTS) {
+ _sendPortInTarget = MACH_PORT_NULL;
+ //FIXME file radar
+ r = mach_port_allocate(_targetTask, MACH_PORT_RIGHT_DEAD_NAME, &_sendPortInTarget);
+ if (r != KERN_SUCCESS) {
+ *kr = r;
+ return;
+ }
+ (void)mach_port_deallocate(_targetTask, _sendPortInTarget);
+ r = mach_port_insert_right(_targetTask, _sendPortInTarget, _receivePortInMonitor, MACH_MSG_TYPE_MAKE_SEND);
+ }
+ if (r != KERN_SUCCESS) {
+ *kr = r;
+ return;
+ }
+
+ // Notify us if the target dies
+ mach_port_t previous = MACH_PORT_NULL;
+ if ((*kr = mach_port_request_notification(_targetTask, _sendPortInTarget, MACH_NOTIFY_DEAD_NAME, 0, _receivePortInMonitor, MACH_MSG_TYPE_MAKE_SEND_ONCE, &previous))) {
+ (void)mach_port_deallocate(_targetTask, _sendPortInTarget);
+ (void)mach_port_destruct(mach_task_self(), _receivePortInMonitor, 0, (mach_port_context_t)this);
+ teardown();
+ return;
+ }
+ // This is a new port, if there is a previous notifier attached then something is wrong... abort.
+ if (previous != MACH_PORT_NULL) {
+ (void)mach_port_deallocate(mach_task_self(), previous);
+ (void)mach_port_deallocate(_targetTask, _sendPortInTarget);
+ (void)mach_port_destruct(mach_task_self(), _receivePortInMonitor, 0, (mach_port_context_t)this);
+ teardown();
+ return;
+ }
+ }
+
+ // Setup the event handler for the port
+ _machSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, _receivePortInMonitor, 0, _queue);
+ if (_machSource == nullptr) {
+ (void)mach_port_deallocate(_targetTask, _sendPortInTarget);
+ (void)mach_port_destruct(mach_task_self(), _receivePortInMonitor, 0, (mach_port_context_t)this);
+ teardown();
+ return;
+ }
+ dispatch_source_set_event_handler(_machSource, ^{
+ handleEvent();
+ });
+ dispatch_source_set_cancel_handler(_machSource, ^{
+ if ( _receivePortInMonitor != 0 ) {
+ (void)mach_port_destruct(mach_task_self(), _receivePortInMonitor, 0, (mach_port_context_t)this);
+ _receivePortInMonitor = 0;
+ }
+ });
+ dispatch_activate(_machSource);
+
+ // get location on all_image_infos in the target task
+ task_dyld_info_data_t taskDyldInfo;
+ mach_msg_type_number_t taskDyldInfoCount = TASK_DYLD_INFO_COUNT;
+ if ((*kr = task_info(_targetTask, TASK_DYLD_INFO, (task_info_t)&taskDyldInfo, &taskDyldInfoCount))) {
+ (void)mach_port_deallocate(_targetTask, _sendPortInTarget);
+ teardown();
+ return;
+ }
+ // Poke the portname of our port into the target task
+ _remoteAllImageInfoBuffer = RemoteBuffer(_targetTask, taskDyldInfo.all_image_info_addr, taskDyldInfo.all_image_info_size, true, false);
+ *kr = _remoteAllImageInfoBuffer.getKernelReturn();
+ if (*kr) {
+ (void)mach_port_deallocate(_targetTask, _sendPortInTarget);
+ teardown();
+ return;
+ }
+
+ static_assert(sizeof(mach_port_t) == sizeof(uint32_t), "machport size not 32-bits");
+ if ( taskDyldInfo.all_image_info_format == TASK_DYLD_ALL_IMAGE_INFO_32 ) {
+ _notifyMachPorts = (uint32_t *)((uint8_t *)_remoteAllImageInfoBuffer.getLocalAddress() + offsetof(dyld_all_image_infos_32,notifyMachPorts));
+ } else {
+ _notifyMachPorts = (uint32_t *)((uint8_t *)_remoteAllImageInfoBuffer.getLocalAddress() + offsetof(dyld_all_image_infos_64,notifyMachPorts));
+ }
+
+#if 0
+ //If all the slots are filled we will sleep and retry a few times before giving up
+ for (uint32_t i=0; i<10; ++i) {
+ for (_notifySlot=0; _notifySlot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++_notifySlot) {
+ if (OSAtomicCompareAndSwap32(0, _sendPortInTarget, (volatile int32_t*)&_notifyMachPorts[_notifySlot])) {
+ break;
+ }
+ }
+ if (_notifySlot == DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT) {
+ // all the slots are filled, sleep and try again
+ usleep(1000 * 50); // 50ms
+ } else {
+ // if _notifySlot is set we are done
+ break;
+ }
+ }
+#else
+ for (_notifySlot=0; _notifySlot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++_notifySlot) {
+ if (OSAtomicCompareAndSwap32(0, _sendPortInTarget, (volatile int32_t*)&_notifyMachPorts[_notifySlot])) {
+ break;
+ }
+ }
+#endif
+
+ if (_notifySlot == DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT) {
+ (void)mach_port_deallocate(_targetTask, _sendPortInTarget);
+ teardown();
+ *kr = KERN_UREFS_OVERFLOW;
+ return;
+ }
+
+ *kr = KERN_SUCCESS;