diff options
Diffstat (limited to 'darwin-bootstrap')
-rw-r--r-- | darwin-bootstrap/bundle-loader.c | 293 | ||||
-rw-r--r-- | darwin-bootstrap/substituted-messages.h | 39 | ||||
-rw-r--r-- | darwin-bootstrap/substituted.c | 127 | ||||
-rw-r--r-- | darwin-bootstrap/substituted.m (renamed from darwin-bootstrap/substituted-plist-loader.m) | 153 |
4 files changed, 260 insertions, 352 deletions
diff --git a/darwin-bootstrap/bundle-loader.c b/darwin-bootstrap/bundle-loader.c index b3c60d5..1369f74 100644 --- a/darwin-bootstrap/bundle-loader.c +++ b/darwin-bootstrap/bundle-loader.c @@ -2,38 +2,17 @@ #define IB_LOG_TO_SYSLOG #include "ib-log.h" #include "darwin/mach-decls.h" -#include "substituted-messages.h" +#include "xxpc.h" #include <dlfcn.h> #include <mach/mach.h> #include <mach/mig.h> #include <objc/runtime.h> #include <CoreFoundation/CoreFoundation.h> #include <syslog.h> +#include <pthread.h> +#include <sys/time.h> extern char ***_NSGetArgv(void); -static int peek(const void *buf, const void *end) { - if ((size_t) (end - buf) < sizeof(struct substituted_bundle_list_op)) - return -1; - return ((struct substituted_bundle_list_op *) buf)->opc; -} - -static bool pop(const void **buf, const void *end, - int *opc, - const char **name) { - const struct substituted_bundle_list_op *op; - if ((size_t) (end - *buf) < sizeof(*op)) - return false; - op = *buf; - *buf += sizeof(*op); - *opc = op->opc; - if ((size_t) (end - *buf) <= op->namelen || - ((const char *) *buf)[op->namelen] != '\0') - return false; - *name = *buf; - *buf += op->namelen + 1; - return true; -} - static struct { bool initialized; typeof(CFBundleGetBundleWithIdentifier) *CFBundleGetBundleWithIdentifier; @@ -47,6 +26,10 @@ static struct { typeof(objc_getClass) *objc_getClass; } objc_funcs; +static pthread_mutex_t hello_reply_mtx = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t hello_reply_cond = PTHREAD_COND_INITIALIZER; +static xxpc_object_t hello_reply; + #define GET(funcs, handle, name) (funcs)->name = dlsym(handle, #name) static void *dlopen_noload(const char *path) { @@ -103,142 +86,166 @@ static void use_dylib(const char *name) { dlopen(name, RTLD_LAZY); } -static void load_bundle_list(const void *buf, size_t size) { - if (IB_VERBOSE) { - ib_log("load_bundle_list: %p,%zu", buf, size); - ib_log_hex(buf, size); +enum bundle_test_result { + BUNDLE_TEST_RESULT_INVALID, + BUNDLE_TEST_RESULT_FAIL, + BUNDLE_TEST_RESULT_PASS, + BUNDLE_TEST_RESULT_EMPTY, +}; + +static enum bundle_test_result do_bundle_test_type( + xxpc_object_t info, const char *key, bool (*test)(const char *)) { + xxpc_object_t values = xxpc_dictionary_get_value(info, key); + if (!values || xxpc_get_type(values) != XXPC_TYPE_ARRAY) + return BUNDLE_TEST_RESULT_INVALID; + size_t count = xxpc_array_get_count(values); + if (count == 0) + return BUNDLE_TEST_RESULT_EMPTY; + for (size_t i = 0; i < count; i++) { + const char *value = xxpc_array_get_string(values, i); + if (!value) + return BUNDLE_TEST_RESULT_INVALID; + if (test(value)) + return BUNDLE_TEST_RESULT_PASS; } - int opc; - const char *name; - const void *end = buf + size; - while (buf != end) { - if (!pop(&buf, end, &opc, &name)) - goto invalid; - bool pass; - switch (opc) { - case SUBSTITUTED_TEST_BUNDLE: - pass = cf_has_bundle(name); - if (IB_VERBOSE) - ib_log("cf_has_bundle('%s'): %d", name, pass); - if (pass) - goto pass_type; - /* fail, so... */ - if (peek(buf, end) != SUBSTITUTED_TEST_BUNDLE) - goto fail; - break; - case SUBSTITUTED_TEST_CLASS: - pass = objc_has_class(name); - if (IB_VERBOSE) - ib_log("objc_has_class('%s'): %d", name, pass); - if (pass) - goto pass_type; - if (peek(buf, end) != SUBSTITUTED_TEST_CLASS) - goto fail; - break; - case SUBSTITUTED_USE_DYLIB: - use_dylib(name); - break; - pass_type: - while (peek(buf, end) == opc) { - if (!pop(&buf, end, &opc, &name)) - goto invalid; - } - break; - fail: - do { - if (!pop(&buf, end, &opc, &name)) - goto invalid; - } while (opc != SUBSTITUTED_USE_DYLIB); - break; - } - } - return; -invalid: - ib_log("invalid bundle list IPC data (substitute bug)"); + return BUNDLE_TEST_RESULT_FAIL; } -static kern_return_t substituted_hello(mach_port_t service, int proto_version, - const char *argv0, void **bundle_list_p, - size_t *bundle_list_size_p) { - struct { - mach_msg_header_t hdr; - union { - struct substituted_msg_body_hello req; - struct substituted_msg_body_hello_resp resp; - } u; - mach_msg_trailer_t trailer_space; - } buf; - mach_port_t reply_port = mig_get_reply_port(); - buf.hdr.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, - MACH_MSG_TYPE_MAKE_SEND_ONCE); - buf.hdr.msgh_remote_port = service; - buf.hdr.msgh_local_port = reply_port; - buf.hdr.msgh_reserved = 0; - buf.hdr.msgh_id = SUBSTITUTED_MSG_HELLO; - buf.u.req.proto_version = proto_version; - strlcpy(buf.u.req.argv0, argv0, sizeof(buf.u.req.argv0)); - size_t size = sizeof(buf.hdr) + - offsetof(struct substituted_msg_body_hello, argv0) + - strlen(buf.u.req.argv0); - size_t round = round_msg(size); - memset((char *) &buf + size, 0, round - size); - buf.hdr.msgh_size = round; - kern_return_t kr = mach_msg(&buf.hdr, MACH_RCV_MSG | MACH_SEND_MSG, - buf.hdr.msgh_size, sizeof(buf), reply_port, - MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); - if (kr) { - if (kr == MACH_RCV_BODY_ERROR) - mach_msg_destroy(&buf.hdr); - return kr; - } - mach_msg_ool_descriptor_t *ool = &buf.u.resp.bundle_list_ool; - - if (buf.hdr.msgh_size != sizeof(buf.hdr) + sizeof(buf.u.resp) || - !(buf.hdr.msgh_bits & MACH_MSGH_BITS_COMPLEX) || - buf.u.resp.body.msgh_descriptor_count != 1 || - ool->type != MACH_MSG_OOL_DESCRIPTOR) { - kr = KERN_INVALID_ARGUMENT; - goto out; +/* return false if the info was invalid */ +static bool load_bundle_with_info(xxpc_object_t info) { + if (IB_VERBOSE) { + char *desc = xxpc_copy_description(info); + ib_log("load_bundle_with_info: %s", desc); + free(desc); } + bool any = xxpc_dictionary_get_bool(info, "any"); + enum bundle_test_result btr = + do_bundle_test_type(info, "bundles", cf_has_bundle); + if (btr == BUNDLE_TEST_RESULT_INVALID) + return false; + if (!any && btr == BUNDLE_TEST_RESULT_FAIL) + goto no_load; + if (any && btr == BUNDLE_TEST_RESULT_PASS) + goto do_load; + btr = do_bundle_test_type(info, "classes", objc_has_class); + if (btr == BUNDLE_TEST_RESULT_INVALID) + return false; + if (btr == BUNDLE_TEST_RESULT_FAIL) + goto no_load; + else + goto do_load; - if (buf.u.resp.error) { - ib_log("substituted_hello returned error %x", buf.u.resp.error); - kr = KERN_FAILURE; - goto out; - } +no_load: + return true; +do_load:; + const char *name = xxpc_dictionary_get_string(info, "dylib"); + if (!name) + return false; + use_dylib(name); + return true; +} - *bundle_list_p = ool->address; - *bundle_list_size_p = ool->size; - return KERN_SUCCESS; +static bool handle_hello_reply(xxpc_object_t dict) { + xxpc_object_t bundles = xxpc_dictionary_get_value(dict, "bundles"); + if (!bundles || xxpc_get_type(bundles) != XXPC_TYPE_ARRAY) + return false; + for (size_t i = 0, count = xxpc_array_get_count(bundles); + i < count; i++) { + if (!load_bundle_with_info(xxpc_array_get_value(bundles, i))) + return false; + } + return true; +} -out: - mach_msg_destroy(&buf.hdr); - return kr; +static void handle_xxpc_object(xxpc_object_t object, bool is_reply) { + const char *msg; + xxpc_type_t ty = xxpc_get_type(object); + if (ty == XXPC_TYPE_DICTIONARY) { + if (is_reply) { + pthread_mutex_lock(&hello_reply_mtx); + hello_reply = object; + pthread_cond_signal(&hello_reply_cond); + pthread_mutex_unlock(&hello_reply_mtx); + return; + } + msg = "received extraneous message from substituted"; + goto complain; + } else if (ty == XXPC_TYPE_ERROR) { + msg = "XPC error communicating with substituted"; + goto complain; + } else { + msg = "unknown object received from XPC"; + goto complain; + } +complain:; + char *desc = xxpc_copy_description(object); + ib_log("%s: %s", msg, desc); + free(desc); } /* this is DYLD_INSERT_LIBRARIES'd, not injected. */ __attribute__((constructor)) static void init() { - mach_port_t service; - kern_return_t kr = bootstrap_look_up(bootstrap_port, "com.ex.substituted", - &service); - if (kr) { - ib_log("bootstrap_look_up com.ex.substituted: %x", kr); + xxpc_connection_t conn = xxpc_connection_create_mach_service( + "com.ex.substituted", NULL, 0); + /* it's not supposed to return null, but just in case */ + if (!conn) { + ib_log("xxpc_connection_create_mach_service returned null"); return; } + + __block xxpc_object_t received_dict = NULL; + __block bool did_receive_dict = false; + + xxpc_connection_set_event_handler(conn, ^(xxpc_object_t object) { + handle_xxpc_object(object, false); + }); + xxpc_connection_resume(conn); + const char *argv0 = (*_NSGetArgv())[0]; if (!argv0) argv0 = "???"; - void *bundle_list; - size_t bundle_list_size; - kr = substituted_hello(service, SUBSTITUTED_PROTO_VERSION, argv0, - &bundle_list, &bundle_list_size); - if (kr) { - ib_log("substituted_hello: %x", kr); - return; + + xxpc_object_t message = xxpc_dictionary_create(NULL, NULL, 0); + xxpc_dictionary_set_string(message, "type", "hello"); + xxpc_dictionary_set_string(message, "argv0", argv0); + xxpc_connection_send_message_with_reply(conn, message, NULL, + ^(xxpc_object_t reply) { + handle_xxpc_object(reply, true); + }); + + struct timeval now; + struct timespec give_up_time; + gettimeofday(&now, NULL); + /* Timing out *always* means a bug (or the user manually unloaded + * substituted). Therefore, a high timeout is actually a good thing, + * because it makes it clear (especially in testing) that something's wrong + * rather than more subtly slowing things down. */ + give_up_time.tv_sec = now.tv_sec + 10; + give_up_time.tv_nsec = now.tv_usec * 1000; + pthread_mutex_lock(&hello_reply_mtx); + while (!hello_reply) { + if (pthread_cond_timedwait(&hello_reply_cond, &hello_reply_mtx, + &give_up_time)) { + if (errno == ETIMEDOUT) { + ib_log("ACK - didn't receive a reply from substituted in time!"); + goto bad; + } else { + ib_log("pthread_cond_timedwait failed"); + goto bad; + } + } + } + pthread_mutex_unlock(&hello_reply_mtx); + + if (!handle_hello_reply(hello_reply)) { + char *desc = xxpc_copy_description(hello_reply); + ib_log("received invalid message from substituted: %s", desc); + free(desc); + goto bad; } - load_bundle_list(bundle_list, bundle_list_size); - vm_deallocate(mach_task_self(), (vm_address_t) bundle_list, bundle_list_size); - /* hang onto the port */ + return; +bad: + ib_log("giving up..."); } diff --git a/darwin-bootstrap/substituted-messages.h b/darwin-bootstrap/substituted-messages.h deleted file mode 100644 index 04a3378..0000000 --- a/darwin-bootstrap/substituted-messages.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once -#include <mach/mach.h> -#include <sys/param.h> - -enum { - SUBSTITUTED_PROTO_VERSION = 1 -}; - -enum substituted_msg_id { - SUBSTITUTED_MSG_HELLO = 10000, - SUBSTITUTED_MSG_HELLO_RESP = 10001, -}; - -struct substituted_msg_body_hello { - int proto_version; - char argv0[/*0..*/MAXPATHLEN]; -}; - -struct substituted_msg_body_hello_resp { - mach_msg_body_t body; - mach_msg_ool_descriptor_t bundle_list_ool; - int error; -}; - -/* bundle_list: a bunch of substituted_bundle_list_ops - * this is pretty silly because even low-level bootstrap_lookup uses xpc now - - * so could have just used xpc structures - but this is more fun */ - -enum substituted_bundle_list_opc { - SUBSTITUTED_TEST_BUNDLE, - SUBSTITUTED_TEST_CLASS, - SUBSTITUTED_USE_DYLIB, -}; - -struct substituted_bundle_list_op { - uint16_t namelen; - uint8_t opc; - /* char name[namelen + 1]; */ -} __attribute__((packed)); diff --git a/darwin-bootstrap/substituted.c b/darwin-bootstrap/substituted.c deleted file mode 100644 index 6b1693e..0000000 --- a/darwin-bootstrap/substituted.c +++ /dev/null @@ -1,127 +0,0 @@ -/* This is a daemon contacted by all processes which can load extensions. It - * currently does the work of reading the plists in - * /Library/Substitute/DynamicLibraries in order to avoid loading objc/CF - * libraries into the target binary (unless actually required by loaded - * libraries). By itself this would not merit the overhead of a separate - * daemon, but in the future this will also coordinate hot loading and - * unloading, for which purpose I think a daemon would be a cleaner solution - * than task_for_pid'ing everything in sight. */ - -#define IB_LOG_NAME "substituted" -#include "darwin/mach-decls.h" -#include "ib-log.h" -#include "substituted-messages.h" -#include "substituted-plist-loader.h" -#include <mach/mach.h> -#include <stddef.h> -//#include <sys/types.h> - -/* libbsm.h */ -struct au_tid; -void audit_token_to_au32(audit_token_t, uid_t *, uid_t *, gid_t *, uid_t *, - gid_t *, pid_t *, pid_t *, struct au_tid *); - -struct msgbuf { - mach_msg_header_t hdr; - union { - struct substituted_msg_body_hello hello; - } u; - mach_msg_audit_trailer_t trail_space; -}; - -static void handle_hello(struct msgbuf *buf, pid_t pid) { - if (buf->hdr.msgh_size < offsetof(struct msgbuf, u.hello.argv0)) { - ib_log("message too short"); - return; - } - ((char *) buf)[buf->hdr.msgh_size] = '\0'; /* overwrite trailer or whatever */ - const char *name = buf->u.hello.argv0; - if (IB_VERBOSE) - ib_log("got hello from pid %d [%s]", pid, name); - int error = 0; - const void *bundle_list = NULL; - size_t bundle_list_size = 0; - if (buf->u.hello.proto_version != SUBSTITUTED_PROTO_VERSION) { - error = 1; - } else { - get_bundle_list(name, &bundle_list, &bundle_list_size); - } - struct { - mach_msg_header_t hdr; - struct substituted_msg_body_hello_resp b; - } resp; - resp.hdr.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE, 0) | - MACH_MSGH_BITS_COMPLEX; - resp.hdr.msgh_size = sizeof(resp); - resp.hdr.msgh_remote_port = buf->hdr.msgh_remote_port; - resp.hdr.msgh_local_port = MACH_PORT_NULL; - resp.hdr.msgh_reserved = 0; - resp.hdr.msgh_id = SUBSTITUTED_MSG_HELLO_RESP; - resp.b.body.msgh_descriptor_count = 1; - mach_msg_ool_descriptor_t *ool = &resp.b.bundle_list_ool; - ool->pad1 = 0; - ool->type = MACH_MSG_OOL_DESCRIPTOR; - ool->deallocate = 0; - ool->copy = MACH_MSG_PHYSICAL_COPY; - ool->address = (void *) bundle_list; - ool->size = (mach_msg_size_t) bundle_list_size; - resp.b.error = error; - - kern_return_t kr = mach_msg(&resp.hdr, MACH_SEND_MSG, sizeof(resp), 0, - MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, - MACH_PORT_NULL); - if (kr) - ib_log("mach_msg(hello resp) -> %x", kr); - else /* don't re-destroy the moved right */ - buf->hdr.msgh_remote_port = MACH_PORT_NULL; -} - -int main() { - mach_port_t service; - kern_return_t kr = bootstrap_check_in(bootstrap_port, - "com.ex.substituted", - &service); - while (1) { - struct msgbuf buf; - mach_msg_option_t option = - MACH_RCV_MSG | - MACH_RCV_TRAILER_TYPE(MACH_RCV_TRAILER_AUDIT) | - MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT); - - kr = mach_msg(&buf.hdr, option, 0, sizeof(buf), service, - MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); - switch (kr) { - case MACH_MSG_SUCCESS: - break; - case MACH_RCV_BODY_ERROR: - mach_msg_destroy(&buf.hdr); - /* fallthrough */ - case MACH_RCV_TOO_LARGE: - case MACH_RCV_HEADER_ERROR: - case MACH_RCV_SCATTER_SMALL: - ib_log("mach_msg(rcv) -> %x", kr); - continue; - default: - ib_log("mach_msg(rcv) -> %x (fatal)", kr); - return 1; - } - - mach_msg_audit_trailer_t *real_trail = (void *) &buf.hdr + - round_msg(buf.hdr.msgh_size); - - pid_t pid; - audit_token_to_au32(real_trail->msgh_audit, NULL, NULL, NULL, NULL, - NULL, &pid, NULL, NULL); - - switch (buf.hdr.msgh_id) { - case SUBSTITUTED_MSG_HELLO: - handle_hello(&buf, pid); - break; - default: - ib_log("unknown message %d", buf.hdr.msgh_id); - break; - } - - mach_msg_destroy(&buf.hdr); - } -} diff --git a/darwin-bootstrap/substituted-plist-loader.m b/darwin-bootstrap/substituted.m index 0c8d66f..cfa1541 100644 --- a/darwin-bootstrap/substituted-plist-loader.m +++ b/darwin-bootstrap/substituted.m @@ -1,6 +1,6 @@ #import <Foundation/Foundation.h> #import <CoreFoundation/CoreFoundation.h> -#include "substituted-messages.h" +#include "xxpc.h" enum convert_filters_ret { PROVISIONAL_PASS, @@ -22,13 +22,18 @@ static double id_to_double(id o) { } } +static xxpc_object_t nsstring_to_xpc(NSString *in) { + return xxpc_string_create([in cStringUsingEncoding:NSUTF8StringEncoding]); +} + static enum convert_filters_ret convert_filters(NSDictionary *plist_dict, const char *exec_name, - NSMutableData *test_out) { + xxpc_object_t out_info) { NSDictionary *filter = [plist_dict objectForKey:@"Filter"]; if (!filter) return PROVISIONAL_PASS; + if (![filter isKindOfClass:[NSDictionary class]]) return INVALID; @@ -36,11 +41,22 @@ static enum convert_filters_ret convert_filters(NSDictionary *plist_dict, if (!([key isEqualToString:@"CoreFoundationVersion"] || [key isEqualToString:@"Classes"] || [key isEqualToString:@"Bundles"] || - [key isEqualToString:@"Executables"])) { + [key isEqualToString:@"Executables"] || + [key isEqualToString:@"Mode"])) { return INVALID; } } + bool any = false; + NSString *mode_str = [filter objectForKey:@"Mode"]; + if (mode_str) { + any = [mode_str isEqual:@"Any"]; + if (!any && ![mode_str isEqual:@"All"]) + return INVALID; + } + + xxpc_dictionary_set_bool(out_info, "any", any); + /* First do the two we can test here. */ NSArray *cfv = [filter objectForKey:@"CoreFoundationVersion"]; @@ -77,59 +93,53 @@ static enum convert_filters_ret convert_filters(NSDictionary *plist_dict, if ([name isEqualToString:exe]) goto ok; } - return FAIL; + if (any) + return PROVISIONAL_PASS; // without adding other conditions + else + return FAIL; ok:; } - /* Convert the rest to substituted_bundle_list_ops. */ + /* Convert the rest to tests for bundle-loader. */ struct { __unsafe_unretained NSString *key; - uint8_t opc; + const char *okey; } types[2] = { - {@"Classes", SUBSTITUTED_TEST_CLASS}, - {@"Bundles", SUBSTITUTED_TEST_BUNDLE}, + {@"Classes", "classes"}, + {@"Bundles", "bundles"}, }; for (int i = 0; i < 2; i++) { NSArray *things = [filter objectForKey:types[i].key]; if (things) { + xxpc_object_t out_things = xxpc_array_create(NULL, 0); if (![things isKindOfClass:[NSArray class]]) return INVALID; for (NSString *name in things) { if (![name isKindOfClass:[NSString class]]) return INVALID; - NSData *name_data = [name dataUsingEncoding:NSUTF8StringEncoding]; - size_t len = [name_data length]; - if (len > 65535) - return INVALID; - struct substituted_bundle_list_op op = {(uint16_t) len, - types[i].opc}; - [test_out appendBytes:&op length:sizeof(op)]; - [test_out appendData:name_data]; - static char zero = '\0'; - [test_out appendBytes:&zero length:1]; + xxpc_array_append_value(out_things, nsstring_to_xpc(name)); } + xxpc_dictionary_set_value(out_info, types[i].okey, out_things); + xxpc_release(out_things); } } return PROVISIONAL_PASS; } -static NSData *the_bundle_list; - -void get_bundle_list(const char *exec_name, - const void **bundle_list, size_t *bundle_list_size) { +xxpc_object_t get_hello_bundles(const char *exec_name) { /* TODO cache */ + xxpc_object_t bundles = xxpc_array_create(NULL, 0); + NSError *err; NSString *base = @"/Library/Substitute/DynamicLibraries"; NSArray *list = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:base error:&err]; if (!list) - return; - - NSMutableData *out = [NSMutableData data]; + return bundles; for (NSString *dylib in list) { if (![[dylib pathExtension] isEqualToString:@"dylib"]) @@ -140,34 +150,91 @@ void get_bundle_list(const char *exec_name, NSDictionary *plist_dict = [NSDictionary dictionaryWithContentsOfFile: full_plist]; if (!plist_dict) { - NSLog(@"missing, unreadable, or invalid plist '%@' for dylib '%@'; unlike Substrate, we require plists", full_plist, dylib); + NSLog(@"missing, unreadable, or invalid plist '%@' for dylib '%@'; unlike Substrate, we require plists", + full_plist, dylib); continue; } - NSMutableData *test = [NSMutableData data]; - enum convert_filters_ret ret = convert_filters(plist_dict, exec_name, test); + xxpc_object_t info = xxpc_dictionary_create(NULL, NULL, 0); + + enum convert_filters_ret ret = convert_filters(plist_dict, exec_name, info); if (ret == FAIL) { continue; } else if (ret == INVALID) { NSLog(@"bad data in plist '%@' for dylib '%@'", full_plist, dylib); continue; } + NSString *dylib_path = [base stringByAppendingPathComponent:dylib]; - NSData *dylib_path_data = [dylib_path dataUsingEncoding:NSUTF8StringEncoding]; - size_t len = [dylib_path length]; - if (len > 65535) { - NSLog(@"dylib '%@' somehow has an absurdly long path", dylib); - continue; + xxpc_dictionary_set_value(info, "dylib", nsstring_to_xpc(dylib_path)); + + xxpc_array_append_value(bundles, info); + xxpc_release(info); + } + + return bundles; +} + +#define PRECISE objc_precise_lifetime + +static bool handle_message(xxpc_object_t request, xxpc_object_t reply) { + const char *type = xxpc_dictionary_get_string(request, "type"); + if (!type || strcmp(type, "hello")) + return false; + const char *argv0 = xxpc_dictionary_get_string(request, "argv0"); + if (!argv0) + return false; + xxpc_object_t bundles = get_hello_bundles(argv0); + xxpc_dictionary_set_value(reply, "bundles", bundles); + xxpc_release(bundles); + return true; +} + +static void init_peer(xxpc_object_t peer) { + xxpc_connection_set_event_handler(peer, ^(xxpc_object_t event) { + xxpc_type_t ty = xxpc_get_type(event); + if (ty == XXPC_TYPE_DICTIONARY) { + xxpc_object_t reply = xxpc_dictionary_create_reply(event); + if (handle_message(event, reply)) + xxpc_connection_send_message(peer, reply); + else + xxpc_connection_cancel(peer); + xxpc_release(reply); + } else if (ty == XXPC_TYPE_ERROR) { + if (event == XXPC_ERROR_CONNECTION_INTERRUPTED) + return; + NSLog(@"XPC error from peer: %@", event); + } else { + NSLog(@"unknown object received from XPC (peer): %@", event); } - [out appendData:test]; - struct substituted_bundle_list_op op = {(uint16_t) len, - SUBSTITUTED_USE_DYLIB}; - [out appendBytes:&op length:sizeof(op)]; - [out appendData:dylib_path_data]; - static char zero = '\0'; - [out appendBytes:&zero length:1]; + }); + xxpc_connection_resume(peer); +} + +/* This is a daemon contacted by all processes which can load extensions. It + * currently does the work of reading the plists in + * /Library/Substitute/DynamicLibraries in order to avoid loading objc/CF + * libraries into the target binary (unless actually required by loaded + * libraries). In the future it will help with hot loading. */ + +int main() { + xxpc_connection_t listener = xxpc_connection_create_mach_service( + "com.ex.substituted", NULL, XXPC_CONNECTION_MACH_SERVICE_LISTENER); + if (!listener) { + NSLog(@"xxpc_connection_create_mach_service returned null"); + exit(1); } - the_bundle_list = out; - *bundle_list = [out bytes]; - *bundle_list_size = [out length]; + xxpc_connection_set_event_handler(listener, ^(xxpc_object_t object) { + xxpc_type_t ty = xxpc_get_type(object); + if (ty == XXPC_TYPE_CONNECTION) { + init_peer(object); + } else if (ty == XXPC_TYPE_ERROR) { + NSLog(@"XPC error in server: %@", object); + exit(1); + } else { + NSLog(@"unknown object received from XPC: %@", object); + } + }); + xxpc_connection_resume(listener); + [[NSRunLoop mainRunLoop] run]; } |