aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--darwin-bootstrap/bundle-loader.c22
-rw-r--r--darwin-bootstrap/posixspawn-hook.c31
-rw-r--r--darwin-bootstrap/substituted.m134
-rw-r--r--lib/darwin/xxpc.h23
4 files changed, 129 insertions, 81 deletions
diff --git a/darwin-bootstrap/bundle-loader.c b/darwin-bootstrap/bundle-loader.c
index 49e6152..ed4dde4 100644
--- a/darwin-bootstrap/bundle-loader.c
+++ b/darwin-bootstrap/bundle-loader.c
@@ -27,6 +27,8 @@ static struct {
typeof(objc_getClass) *objc_getClass;
} objc_funcs;
+static xxpc_connection_t substituted_conn;
+
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;
@@ -191,6 +193,13 @@ static void handle_xxpc_object(xxpc_object_t object, bool is_reply) {
free(desc);
}
+static void inform_sud_of_clean_exit() {
+ xxpc_object_t message = xxpc_dictionary_create(NULL, NULL, 0);
+ xxpc_dictionary_set_string(message, "type", "bye");
+ xxpc_connection_send_message(substituted_conn, message);
+ xxpc_release(message);
+}
+
/* this is DYLD_INSERT_LIBRARIES'd, not injected. */
__attribute__((constructor))
static void init() {
@@ -199,11 +208,10 @@ static void init() {
/* it's not supposed to return null, but just in case */
if (!conn) {
ib_log("xxpc_connection_create_mach_service returned null");
- return;
+ goto bad;
}
- __block xxpc_object_t received_dict = NULL;
- __block bool did_receive_dict = false;
+ substituted_conn = conn;
xxpc_connection_set_event_handler(conn, ^(xxpc_object_t object) {
handle_xxpc_object(object, false);
@@ -259,14 +267,14 @@ static void init() {
char *desc = xxpc_copy_description(hello_reply);
ib_log("received invalid message from substituted: %s", desc);
free(desc);
+ xxpc_release(hello_reply);
goto bad;
}
+ xxpc_release(hello_reply);
+
+ atexit(inform_sud_of_clean_exit);
return;
bad:
- if (hello_reply) {
- xxpc_release(hello_reply);
- hello_reply = NULL;
- }
ib_log("giving up on loading bundles for this process...");
}
diff --git a/darwin-bootstrap/posixspawn-hook.c b/darwin-bootstrap/posixspawn-hook.c
index 4920d0a..3c37403 100644
--- a/darwin-bootstrap/posixspawn-hook.c
+++ b/darwin-bootstrap/posixspawn-hook.c
@@ -36,8 +36,6 @@ extern char ***_NSGetEnviron(void);
static __typeof__(posix_spawn) *old_posix_spawn, *old_posix_spawnp,
hook_posix_spawn, hook_posix_spawnp;
-static __typeof__(wait4) *old_wait4, hook_wait4;
-static typeof(waitpid) *old_waitpid, hook_waitpid;
static int (*old_sandbox_check)(pid_t, const char *, int type, ...);
static bool is_launchd;
@@ -187,10 +185,9 @@ static int hook_posix_spawn_generic(__typeof__(posix_spawn) *old,
/* which dylib should we add, if any? */
const char *dylib_to_add;
if (is_launchd) {
- if (!strcmp(path, "/usr/libexec/xpcproxy"))
- dylib_to_add = psh_dylib;
- else
+ if (strcmp(path, "/usr/libexec/xpcproxy"))
goto skip;
+ dylib_to_add = psh_dylib;
} else {
/* - substituted obviously doesn't want to have bundle_loader run in it
* and try to contact substituted. I have _MSSafeMode=1 in the plist
@@ -220,8 +217,7 @@ static int hook_posix_spawn_generic(__typeof__(posix_spawn) *old,
!strcmp(path, "/usr/sbin/notifyd") ||
!strcmp(xbasename(argv[0] ?: ""), "sshd"))
goto skip;
- else
- dylib_to_add = bl_dylib;
+ dylib_to_add = bl_dylib;
}
if (access(dylib_to_add, R_OK)) {
@@ -347,6 +343,7 @@ static int hook_posix_spawn_generic(__typeof__(posix_spawn) *old,
int ret = old(pidp, path, file_actions, &my_attr, argv, envp_to_use);
if (IB_VERBOSE)
ib_log("ret=%d pid=%ld", ret, (long) *pidp);
+
if (ret)
goto cleanup;
/* Since it returned, obviously it was not SETEXEC, so we need to
@@ -366,12 +363,6 @@ cleanup:
return ret;
}
-static void after_wait_generic(pid_t pid, int stat) {
- /* TODO safety */
- (void) pid;
- (void) stat;
-}
-
int hook_posix_spawn(pid_t *restrict pid, const char *restrict path,
const posix_spawn_file_actions_t *file_actions,
const posix_spawnattr_t *restrict attrp,
@@ -388,18 +379,6 @@ int hook_posix_spawnp(pid_t *restrict pid, const char *restrict path,
attrp, argv, envp);
}
-pid_t hook_wait4(pid_t pid, int *stat_loc, int options, struct rusage *rusage) {
- pid_t ret = old_wait4(pid, stat_loc, options, rusage);
- after_wait_generic(ret, *stat_loc);
- return ret;
-}
-
-pid_t hook_waitpid(pid_t pid, int *stat_loc, int options) {
- pid_t ret = old_waitpid(pid, stat_loc, options);
- after_wait_generic(ret, *stat_loc);
- return ret;
-}
-
int hook_sandbox_check(pid_t pid, const char *op, int type, ...) {
/* Can't easily determine the number of arguments, so just assume there's
* less than 5 pointers' worth. */
@@ -468,8 +447,6 @@ static void init() {
static const struct substitute_import_hook hooks[] = {
{"_posix_spawn", hook_posix_spawn, &old_posix_spawn},
{"_posix_spawnp", hook_posix_spawnp, &old_posix_spawnp},
- {"_waitpid", hook_waitpid, &old_waitpid},
- {"_wait4", hook_wait4, &old_wait4},
{"_sandbox_check", hook_sandbox_check, &old_sandbox_check},
};
diff --git a/darwin-bootstrap/substituted.m b/darwin-bootstrap/substituted.m
index ca793ba..5cb1b11 100644
--- a/darwin-bootstrap/substituted.m
+++ b/darwin-bootstrap/substituted.m
@@ -9,6 +9,8 @@
* libraries into the target binary (unless actually required by loaded
* libraries). In the future it will help with hot loading. */
+static bool g_springboard_needs_safe;
+
extern kern_return_t bootstrap_look_up3(mach_port_t bp,
const char *service_name, mach_port_t *sp, pid_t target_pid,
const uuid_t instance_id, uint64_t flags);
@@ -32,12 +34,6 @@ static void install_deadlock_warning() {
}
}
-enum convert_filters_ret {
- PROVISIONAL_PASS,
- FAIL,
- INVALID
-};
-
static double id_to_double(id o) {
if ([o isKindOfClass:[NSString class]]) {
NSScanner *scan = [NSScanner scannerWithString:o];
@@ -56,10 +52,27 @@ 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,
- xxpc_object_t out_info) {
+@interface PeerHandler : NSObject {
+ xxpc_object_t _connection;
+ NSString *_argv0;
+ bool _is_springboard, _got_bye;
+}
+
+@end
+
+enum convert_filters_ret TEST;
+@implementation PeerHandler
+
+enum convert_filters_ret {
+ PROVISIONAL_PASS,
+ FAIL,
+ INVALID
+};
+
+- (enum convert_filters_ret)
+ convertFiltersForBundleInfo:(NSDictionary *)plist_dict
+ toXPCReply:(xxpc_object_t)out_info {
NSDictionary *filter = [plist_dict objectForKey:@"Filter"];
if (!filter)
return PROVISIONAL_PASS;
@@ -72,11 +85,24 @@ static enum convert_filters_ret convert_filters(NSDictionary *plist_dict,
[key isEqualToString:@"Classes"] ||
[key isEqualToString:@"Bundles"] ||
[key isEqualToString:@"Executables"] ||
- [key isEqualToString:@"Mode"])) {
+ [key isEqualToString:@"Mode"] ||
+ [key isEqualToString:@"SafeMode"])) {
return INVALID;
}
}
+ bool safe_mode = false;
+ NSNumber *safe_mode_num = [filter objectForKey:@"SafeMode"];
+ if (safe_mode_num) {
+ if ([safe_mode_num isEqual:[NSNumber numberWithBool:true]])
+ safe_mode = true;
+ else if (![safe_mode_num isEqual:[NSNumber numberWithBool:false]])
+ return INVALID;
+ }
+ if ((safe_mode && !_is_springboard) ||
+ safe_mode != g_springboard_needs_safe)
+ return FAIL;
+
bool any = false;
NSString *mode_str = [filter objectForKey:@"Mode"];
if (mode_str) {
@@ -113,18 +139,16 @@ static enum convert_filters_ret convert_filters(NSDictionary *plist_dict,
NSArray *executables = [filter objectForKey:@"Executables"];
if (executables) {
- NSString *exe = [NSString stringWithCString:exec_name
- encoding:NSUTF8StringEncoding];
if (![executables isKindOfClass:[NSArray class]])
return INVALID;
for (NSString *name in executables) {
if (![name isKindOfClass:[NSString class]])
return INVALID;
- if ([name isEqualToString:exe])
+ if ([name isEqualToString:_argv0])
goto ok;
}
if (any)
- return PROVISIONAL_PASS; // without adding other conditions
+ return PROVISIONAL_PASS; /* without adding other conditions */
else
return FAIL;
ok:;
@@ -152,15 +176,24 @@ static enum convert_filters_ret convert_filters(NSDictionary *plist_dict,
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;
}
-xxpc_object_t get_hello_bundles(const char *exec_name) {
- /* TODO cache */
+- (bool)handleMessageHello:(xxpc_object_t)request {
+ if (_argv0 != NULL)
+ return false;
+
+ const char *argv0 = xxpc_dictionary_get_string(request, "argv0");
+ if (!argv0)
+ return false;
+ _argv0 = [NSString stringWithCString:argv0
+ encoding:NSUTF8StringEncoding];
+
+ _is_springboard = [_argv0 isEqualToString:@"SpringBoard"];
+
xxpc_object_t bundles = xxpc_array_create(NULL, 0);
NSError *err;
@@ -187,7 +220,8 @@ xxpc_object_t get_hello_bundles(const char *exec_name) {
xxpc_object_t info = xxpc_dictionary_create(NULL, NULL, 0);
- enum convert_filters_ret ret = convert_filters(plist_dict, exec_name, info);
+ enum convert_filters_ret ret =
+ [self convertFiltersForBundleInfo:plist_dict toXPCReply:info];
if (ret == FAIL) {
continue;
} else if (ret == INVALID) {
@@ -199,47 +233,63 @@ xxpc_object_t get_hello_bundles(const char *exec_name) {
xxpc_dictionary_set_value(info, "dylib", nsstring_to_xpc(dylib_path));
xxpc_array_append_value(bundles, info);
- xxpc_release(info);
}
- return bundles;
+ xxpc_object_t reply = xxpc_dictionary_create_reply(request);
+ xxpc_dictionary_set_value(reply, "bundles", bundles);
+ xxpc_connection_send_message(_connection, reply);
+ return true;
+}
+
+- (bool)handleMessageBye:(xxpc_object_t)request {
+ _got_bye = true;
+ return true;
+}
+
+- (void)handleHangup {
+ /* this could be false because hello hasn't been sent, but in that case it
+ * hasn't loaded any substitute dylibs, so not our problem *whistle* */
+ if (_is_springboard && !_got_bye) {
+ NSLog(@"SpringBoard hung up without saying bye; using safe mode next time.");
+ g_springboard_needs_safe = true;
+ }
}
-#define PRECISE objc_precise_lifetime
-static bool handle_message(xxpc_object_t request, xxpc_object_t reply) {
+- (bool)handleMessage:(xxpc_object_t)request {
const char *type = xxpc_dictionary_get_string(request, "type");
- if (!type || strcmp(type, "hello"))
+ if (!type)
return false;
- const char *argv0 = xxpc_dictionary_get_string(request, "argv0");
- if (!argv0)
+ if (!strcmp(type, "hello"))
+ return [self handleMessageHello:request];
+ else if (!strcmp(type, "bye"))
+ return [self handleMessageBye:request];
+ else
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) {
+- (instancetype)initWithConnection:(xxpc_object_t)connection {
+ _connection = connection;
+ xxpc_connection_set_event_handler(connection, ^(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);
+ if (![self handleMessage:event])
+ xxpc_connection_cancel(connection);
} else if (ty == XXPC_TYPE_ERROR) {
- if (event == XXPC_ERROR_CONNECTION_INVALID)
- return;
- NSLog(@"XPC error from peer: %@", event);
+ if (event == XXPC_ERROR_CONNECTION_INVALID) {
+ [self handleHangup];
+ } else {
+ NSLog(@"XPC error from connection: %@", event);
+ xxpc_connection_cancel(connection);
+ }
} else {
NSLog(@"unknown object received from XPC (peer): %@", event);
}
});
- xxpc_connection_resume(peer);
+ xxpc_connection_resume(connection);
+ return self;
}
+@end
int main() {
NSLog(@"hello from substituted");
@@ -253,7 +303,7 @@ int main() {
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);
+ (void) [[PeerHandler alloc] initWithConnection:object];
} else if (ty == XXPC_TYPE_ERROR) {
NSLog(@"XPC error in server: %@", object);
exit(1);
diff --git a/lib/darwin/xxpc.h b/lib/darwin/xxpc.h
index 88aab7a..5caed15 100644
--- a/lib/darwin/xxpc.h
+++ b/lib/darwin/xxpc.h
@@ -1,15 +1,22 @@
#pragma once
/* distinct names to avoid incompatibility if <xpc/xpc.h> gets included somehow
- * on OS X. No ARC support! */
+ * on OS X. */
#include <dispatch/dispatch.h>
+#include <os/object.h>
-typedef struct _xxpc_object {
- int x;
-} *xxpc_object_t, *xxpc_connection_t, *xxpc_type_t;
+#if OS_OBJECT_USE_OBJC
+#define DC_CAST (__bridge xxpc_object_t)
+OS_OBJECT_DECL(xxpc_object);
+#else
+#define DC_CAST
+typedef struct xxpc_object *xxpc_object_t;
+#endif
+typedef xxpc_object_t xxpc_connection_t, xxpc_type_t;
typedef void (^xxpc_handler_t)(xxpc_object_t);
#define DEFINE_CONST(name, sym) \
- extern struct _xxpc_object name[1] asm("_" #sym)
+ extern struct xxpc_object x_##name asm("_" #sym); \
+ static const xxpc_object_t name = DC_CAST &x_##name
DEFINE_CONST(XXPC_TYPE_CONNECTION, _xpc_type_connection);
DEFINE_CONST(XXPC_TYPE_ERROR, _xpc_type_error);
@@ -28,21 +35,26 @@ DEFINE_CONST(XXPC_ERROR_CONNECTION_INVALID,
#define WRAP(name, args...) \
x##name args asm("_" #name)
+#if !OS_OBJECT_USE_OBJC
void WRAP(xpc_release, (xxpc_object_t));
xxpc_object_t WRAP(xpc_retain, (xxpc_object_t));
+#endif
char *WRAP(xpc_copy_description, (xxpc_object_t));
xxpc_type_t WRAP(xpc_get_type, (xxpc_object_t));
xxpc_object_t WRAP(xpc_string_create, (const char *));
+const char *WRAP(xpc_string_get_string_ptr, (xxpc_object_t));
void WRAP(xpc_array_append_value, (xxpc_object_t, xxpc_object_t));
xxpc_object_t WRAP(xpc_array_create, (const xxpc_object_t *, size_t));
size_t WRAP(xpc_array_get_count, (const xxpc_object_t));
xxpc_object_t WRAP(xpc_array_get_value, (xxpc_object_t, size_t));
const char *WRAP(xpc_array_get_string, (xxpc_object_t, size_t));
void WRAP(xpc_array_set_string, (xxpc_object_t, size_t, const char *));
+OS_OBJECT_RETURNS_RETAINED
xxpc_object_t WRAP(xpc_dictionary_create, (const char *const *,
const xxpc_object_t *, size_t));
+OS_OBJECT_RETURNS_RETAINED
xxpc_object_t WRAP(xpc_dictionary_create_reply, (xxpc_object_t));
bool WRAP(xpc_dictionary_get_bool, (xxpc_object_t, const char *));
const char *WRAP(xpc_dictionary_get_string, (xxpc_object_t, const char *));
@@ -64,3 +76,4 @@ void WRAP(xpc_connection_cancel, (xxpc_connection_t));
#undef DEFINE_TYPE
#undef DEFINE_CONST
#undef WRAP
+#undef DC_CAST