Loading...
--- dyld/dyld-852/src/dyld_process_info_notify.cpp
+++ dyld/dyld-733.8/src/dyld_process_info_notify.cpp
@@ -33,13 +33,11 @@
#include <mach-o/dyld_priv.h>
#include <mach-o/dyld_process_info.h>
#include <mach-o/dyld_images.h>
-#include <Block.h>
-#include <dlfcn.h>
+
#include "dyld_process_info_internal.h"
#include "Loading.h"
-#include "Tracing.h"
#include "AllImages.h"
extern "C" int _dyld_func_lookup(const char* name, void** address);
@@ -59,11 +57,7 @@
void retain();
void release();
- void setNotifyMain(NotifyMain notifyMain) const {
- if (_notifyMain == notifyMain) { return; }
- Block_release(_notifyMain);
- _notifyMain = Block_copy(notifyMain);
- }
+ void setNotifyMain(NotifyMain notifyMain) const { _notifyMain = notifyMain; }
// override new and delete so we don't need to link with libc++
static void* operator new(size_t sz) { return malloc(sz); }
@@ -71,217 +65,189 @@
private:
void handleEvent();
- void disconnect();
- void teardownMachPorts();
+ void teardown();
void replyToMonitoredProcess(mach_msg_header_t& header);
- kern_return_t task_dyld_process_info_notify_register(task_read_t target_task, mach_port_t notify);
- kern_return_t task_dyld_process_info_notify_deregister(task_read_t target_task, mach_port_t notify);
-
RemoteBuffer _remoteAllImageInfoBuffer;
- mutable std::atomic<uint32_t> _retainCount;
+ uint32_t* _notifyMachPorts;
+ uint32_t _notifySlot;
+ mutable std::atomic<int32_t> _retainCount;
dispatch_queue_t _queue;
- mutable Notify _notify;
- mutable NotifyExit _notifyExit;
- mutable NotifyMain _notifyMain;
- dispatch_source_t _machSource;
- task_t _task;
- mach_port_t _port; // monitor is process being notified of image loading/unloading
- std::atomic<bool> _connected;
-#if TARGET_OS_SIMULATOR
- uint32_t _portInTarget;
-#endif
+ Notify _notify;
+ NotifyExit _notifyExit;
+ mutable NotifyMain _notifyMain;
+ task_t _targetTask;
+ dispatch_source_t _machSource;
+ mach_port_t _sendPortInTarget; // target is process being watched for image loading/unloading
+ mach_port_t _receivePortInMonitor; // monitor is process being notified of image loading/unloading
+ std::atomic<bool> _disabled;
};
-#if TARGET_OS_SIMULATOR
-
-template<typename F>
-kern_return_t withRemotePortArray(task_t target_task, F f) {
- // Get the all image info
+
+dyld_process_info_notify_base::dyld_process_info_notify_base(dispatch_queue_t queue, Notify notify, NotifyExit notifyExit,
+ task_t task, kern_return_t* kr) :
+ _notifyMachPorts(nullptr), _notifySlot(0), _retainCount(1), _queue(queue), _notify(notify), _notifyExit(notifyExit),
+ _notifyMain(nullptr), _targetTask(task), _machSource(nullptr), _sendPortInTarget(0), _receivePortInMonitor(0),
+ _disabled(false)
+{
+ assert(_disabled == false);
+ dispatch_retain(_queue);
+ // 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;
- auto kr = task_info(target_task, TASK_DYLD_INFO, (task_info_t)&taskDyldInfo, &taskDyldInfoCount);
- if (kr != KERN_SUCCESS) {
- return kr;
- }
-
- vm_prot_t cur_protection = VM_PROT_NONE;
- vm_prot_t max_protection = VM_PROT_NONE;
- mach_vm_address_t localAddress = 0;
- mach_vm_size_t size = sizeof(dyld_all_image_infos_64);
+ 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, (size_t)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 ) {
- size = sizeof(dyld_all_image_infos_32);
- }
- kr = mach_vm_remap(mach_task_self(),
- &localAddress,
- size,
- 0, // mask
- VM_FLAGS_ANYWHERE | VM_FLAGS_RETURN_DATA_ADDR| VM_FLAGS_RESILIENT_CODESIGN | VM_FLAGS_RESILIENT_MEDIA,
- target_task,
- taskDyldInfo.all_image_info_addr,
- false,
- &cur_protection,
- &max_protection,
- VM_INHERIT_NONE);
-
- static_assert(sizeof(mach_port_t) == sizeof(uint32_t), "machport size not 32-bits");
- uint32_t* notifyMachPorts;
- if ( taskDyldInfo.all_image_info_format == TASK_DYLD_ALL_IMAGE_INFO_32 ) {
- notifyMachPorts = (uint32_t *)((uint8_t *)localAddress + offsetof(dyld_all_image_infos_32,notifyMachPorts));
+ _notifyMachPorts = (uint32_t *)((uint8_t *)_remoteAllImageInfoBuffer.getLocalAddress() + offsetof(dyld_all_image_infos_32,notifyMachPorts));
} else {
- notifyMachPorts = (uint32_t *)((uint8_t *)localAddress + offsetof(dyld_all_image_infos_64,notifyMachPorts));
- }
- kr = f(notifyMachPorts);
- (void)vm_deallocate(target_task, localAddress, size);
- return kr;
-}
-
+ _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
-kern_return_t dyld_process_info_notify_base::task_dyld_process_info_notify_register(task_t target_task, mach_port_t notify) {
-#if TARGET_OS_SIMULATOR
- static dispatch_once_t onceToken;
- static kern_return_t (*tdpinr)(task_t, mach_port_t) = nullptr;
- dispatch_once(&onceToken, ^{
- tdpinr = (kern_return_t (*)(task_t, mach_port_t))dlsym(RTLD_DEFAULT, "task_dyld_process_info_notify_register");
- });
- if (tdpinr) {
- return tdpinr(target_task, notify);
- }
- // Our libsystem does not have task_dyld_process_info_notify_register, emulate
- return withRemotePortArray(target_task, [this,target_task,notify](uint32_t* portArray){
- mach_port_t portInTarget = MACH_PORT_NULL;
- // Insert the right
- kern_return_t kr = KERN_NAME_EXISTS;
- while (kr == KERN_NAME_EXISTS) {
- portInTarget = MACH_PORT_NULL;
- kr = mach_port_allocate(target_task, MACH_PORT_RIGHT_DEAD_NAME, &portInTarget);
- if (kr != KERN_SUCCESS) {
- return kr;
- }
- (void)mach_port_deallocate(target_task, portInTarget);
- kr = mach_port_insert_right(target_task, portInTarget, notify, MACH_MSG_TYPE_MAKE_SEND);
- }
- // The call is not succesfull return
- if (kr != KERN_SUCCESS) {
- (void)mach_port_deallocate(target_task, portInTarget);
- return kr;
- }
- // Find a slot for the right
- for (uint8_t notifySlot=0; notifySlot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++notifySlot) {
- if (OSAtomicCompareAndSwap32(0, portInTarget, (volatile int32_t*)&portArray[notifySlot])) {
- _portInTarget = portInTarget;
- return KERN_SUCCESS;
- }
- }
- // The array was full, we need to fail
- (void)mach_port_deallocate(target_task, portInTarget);
- return KERN_UREFS_OVERFLOW;
- });
-#else
- return ::task_dyld_process_info_notify_register(target_task, notify);
-#endif
-}
-
-kern_return_t dyld_process_info_notify_base::task_dyld_process_info_notify_deregister(task_t target_task, mach_port_t notify) {
-#if TARGET_OS_SIMULATOR
- static dispatch_once_t onceToken;
- static kern_return_t (*tdpind)(task_t, mach_port_t) = nullptr;
- dispatch_once(&onceToken, ^{
- tdpind = (kern_return_t (*)(task_t, mach_port_t))dlsym(RTLD_DEFAULT, "task_dyld_process_info_notify_deregister");
- });
- if (tdpind) {
- return tdpind(target_task, notify);
- }
- // Our libsystem does not have task_dyld_process_info_notify_deregister, emulate
- return withRemotePortArray(target_task, [this](uint32_t* portArray){
- // Find a slot for the right
- for (uint8_t notifySlot=0; notifySlot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++notifySlot) {
- if (OSAtomicCompareAndSwap32(0, _portInTarget, (volatile int32_t*)&portArray[notifySlot])) {
- return KERN_SUCCESS;
- }
- }
- return KERN_FAILURE;
- });
-#else
- // Our libsystem does not have task_dyld_process_info_notify_deregister, emulate
- return ::task_dyld_process_info_notify_deregister(target_task, notify);
-#endif
-}
-
-dyld_process_info_notify_base::dyld_process_info_notify_base(dispatch_queue_t queue, Notify notify, NotifyExit notifyExit,
- task_t task, kern_return_t* kr) :
- _retainCount(0), _queue(queue), _notify(Block_copy(notify)), _notifyExit(Block_copy(notifyExit)),
- _notifyMain(nullptr), _machSource(nullptr), _task(task), _port(MACH_PORT_NULL), _connected(false)
-#if TARGET_OS_SIMULATOR
- , _portInTarget(0)
-#endif
-{
- assert(kr != NULL);
- dispatch_retain(_queue);
- // 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 }};
- *kr = mach_port_construct(mach_task_self(), &options, (mach_port_context_t)this, &_port);
- if (*kr != KERN_SUCCESS) {
- teardownMachPorts();
- return;
- }
-
- mach_port_t previous = MACH_PORT_NULL;
- *kr = mach_port_request_notification(mach_task_self(), _port, MACH_NOTIFY_NO_SENDERS, 1, _port, MACH_MSG_TYPE_MAKE_SEND_ONCE, &previous);
- if ((*kr != KERN_SUCCESS) || previous != MACH_PORT_NULL) {
- teardownMachPorts();
- return;
- }
- //FIXME: Should we retry here if we fail?
- *kr = task_dyld_process_info_notify_register(_task, _port);
- dyld3::kdebug_trace_dyld_marker(DBG_DYLD_TASK_NOTIFY_REGISTER, (uint64_t)_task, (uint64_t)_port, *kr, 0);
- if (*kr != KERN_SUCCESS) {
- teardownMachPorts();
- return;
- }
-
- // Setup the event handler for the port
- _machSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, _port, 0, _queue);
- if (_machSource == nullptr) {
- teardownMachPorts();
- return;
- }
- dispatch_source_set_event_handler(_machSource, ^{ handleEvent(); });
- dispatch_source_set_cancel_handler(_machSource, ^{ teardownMachPorts(); });
- dispatch_activate(_machSource);
- _connected = true;
+ if (_notifySlot == DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT) {
+ (void)mach_port_deallocate(_targetTask, _sendPortInTarget);
+ teardown();
+ *kr = KERN_UREFS_OVERFLOW;
+ return;
+ }
+
+ *kr = KERN_SUCCESS;
}
dyld_process_info_notify_base::~dyld_process_info_notify_base() {
- if (_connected) { fprintf(stderr, "dyld: ~dyld_process_info_notify_base called while still connected\n"); }
- Block_release(_notify);
- Block_release(_notifyMain);
- Block_release(_notifyExit);
+ if (!_disabled) {
+ fprintf(stderr, "dyld: ~dyld_process_info_notify_base called while still enabled\n");
+ }
dispatch_release(_queue);
}
-void dyld_process_info_notify_base::teardownMachPorts() {
- if ( _port != 0 ) {
- kern_return_t kr = task_dyld_process_info_notify_deregister(_task, _port);
- dyld3::kdebug_trace_dyld_marker(DBG_DYLD_TASK_NOTIFY_DEREGISTER, (uint64_t)_task, (uint64_t)_port, kr, 0);
- (void)mach_port_destruct(mach_task_self(), _port, 0, (mach_port_context_t)this);
- _port = 0;
- }
-}
-
-void dyld_process_info_notify_base::disconnect() {
- if (_connected) {
- _connected = false;
+void dyld_process_info_notify_base::teardown() {
+ if (!_disabled) {
+ _disabled = true;
// The connection to the target is dead. Clean up ports
+ if ( _remoteAllImageInfoBuffer.getLocalAddress() != 0 && _notifySlot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT) {
+ mach_port_t extractedPort = MACH_PORT_NULL;
+ mach_msg_type_name_t extractedPortType;
+ kern_return_t kr = mach_port_extract_right(_targetTask, _sendPortInTarget, MACH_MSG_TYPE_COPY_SEND, &extractedPort, &extractedPortType);
+ if (kr == KERN_SUCCESS) {
+ if (extractedPort == _receivePortInMonitor) {
+ if (OSAtomicCompareAndSwap32(_sendPortInTarget, 0, (volatile int32_t*)&_notifyMachPorts[_notifySlot])) {
+ (void)mach_port_deallocate(_targetTask, _sendPortInTarget);
+ }
+ }
+ (void)mach_port_deallocate(mach_task_self(), extractedPort);
+ }
+ }
+ _sendPortInTarget = 0;
if ( _machSource ) {
dispatch_source_cancel(_machSource);
dispatch_release(_machSource);
_machSource = NULL;
- }
+ }
if (_notifyExit) {
dispatch_async(_queue, ^{
- // There was a not a mach source, so if we have any ports they will not get torn down by its cancel handler
_notifyExit();
});
}
@@ -290,23 +256,24 @@
bool dyld_process_info_notify_base::enabled() const
{
- return _connected;
+ return !_disabled;
}
void dyld_process_info_notify_base::retain()
{
- _retainCount.fetch_add(1, std::memory_order_relaxed);
+ _retainCount++;
}
void dyld_process_info_notify_base::release()
{
- if (_retainCount.fetch_sub(1, std::memory_order_acq_rel) == 0) {
- // When we subtracted the ref count was 0, which means it was the last reference
- disconnect();
- dispatch_async(_queue, ^{
- delete this;
- });
- }
+ uint32_t newCount = --_retainCount;
+
+ if ( newCount == 0 ) {
+ teardown();
+ }
+ dispatch_async(_queue, ^{
+ delete this;
+ });
}
void dyld_process_info_notify_base::replyToMonitoredProcess(mach_msg_header_t& header) {
@@ -321,21 +288,22 @@
if (r == KERN_SUCCESS) {
header.msgh_remote_port = MACH_PORT_NULL;
} else {
- disconnect();
+ teardown();
}
}
void dyld_process_info_notify_base::handleEvent() {
// References object may still exist even after the ports are dead. Disable event dispatching
// if the ports have been torn down.
- if (!_connected) { return; }
-
+ if (_disabled) {
+ return;
+ }
// This event handler block has an implicit reference to "this"
// if incrementing the count goes to one, that means the object may have already been destroyed
uint8_t messageBuffer[DYLD_PROCESS_INFO_NOTIFY_MAX_BUFFER_SIZE] = {};
mach_msg_header_t* h = (mach_msg_header_t*)messageBuffer;
- kern_return_t r = mach_msg(h, MACH_RCV_MSG | MACH_RCV_VOUCHER| MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT) | MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0), 0, sizeof(messageBuffer)-sizeof(mach_msg_audit_trailer_t), _port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+ kern_return_t r = mach_msg(h, MACH_RCV_MSG | MACH_RCV_VOUCHER| MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT) | MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0), 0, sizeof(messageBuffer)-sizeof(mach_msg_audit_trailer_t), _receivePortInMonitor, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
if ( r == KERN_SUCCESS && !(h->msgh_bits & MACH_MSGH_BITS_COMPLEX)) {
//fprintf(stderr, "received message id=0x%X, size=%d\n", h->msgh_id, h->msgh_size);
@@ -354,39 +322,39 @@
//fprintf(stderr, "Notifying about: %s\n", stringPool + entries[i].pathStringOffset);
_notify(isUnload, header->timestamp, entries[i].loadAddress, entries[i].uuid, stringPool + entries[i].pathStringOffset);
} else {
- disconnect();
+ teardown();
break;
}
}
// reply to dyld, so it can continue
replyToMonitoredProcess(*h);
} else {
- disconnect();
+ teardown();
}
}
else if ( h->msgh_id == DYLD_PROCESS_INFO_NOTIFY_MAIN_ID ) {
if (h->msgh_size != sizeof(mach_msg_header_t)) {
- disconnect();
+ teardown();
} else if ( _notifyMain != NULL ) {
_notifyMain();
}
replyToMonitoredProcess(*h);
- } else if ( h->msgh_id == MACH_NOTIFY_NO_SENDERS ) {
+ } else if ( h->msgh_id == MACH_NOTIFY_PORT_DELETED ) {
+ mach_port_t deadPort = ((mach_port_deleted_notification_t *)h)->not_port;
// Validate this notification came from the kernel
const mach_msg_audit_trailer_t *audit_tlr = (mach_msg_audit_trailer_t *)((uint8_t *)h + round_msg(h->msgh_size));
if (audit_tlr->msgh_trailer_type == MACH_MSG_TRAILER_FORMAT_0
&& audit_tlr->msgh_trailer_size >= sizeof(mach_msg_audit_trailer_t)
// We cannot link to libbsm, so we are hardcoding the audit token offset (5)
// And the value the represents the kernel (0)
- && audit_tlr->msgh_audit.val[5] == 0) {
- disconnect();
+ && audit_tlr->msgh_audit.val[5] == 0
+ && deadPort == _sendPortInTarget ) {
+ teardown();
}
}
else {
fprintf(stderr, "dyld: received unknown message id=0x%X, size=%d\n", h->msgh_id, h->msgh_size);
}
- } else {
- fprintf(stderr, "dyld: received unknown message id=0x%X, size=%d\n", h->msgh_id, h->msgh_size);
}
mach_msg_destroy(h);
}