From 0e049be58be00d384e986a663e53ded37c796978 Mon Sep 17 00:00:00 2001 From: Tony Michaels <43900316+Mr-Tomahawk@users.noreply.github.com> Date: Thu, 30 Jul 2026 23:21:41 -0700 Subject: [PATCH] Prevent jailbreakd spawn deadlock --- BaseBin/jailbreakd/src/main.m | 3 + BaseBin/jailbreakd/src/server.m | 34 ++++++++-- .../libjailbreak/src/roothider/exec_patch.h | 1 + .../libjailbreak/src/roothider/jailbreakd.c | 62 ++++++++++++++++++- .../libjailbreak/src/roothider/jailbreakd.h | 7 ++- 5 files changed, 99 insertions(+), 8 deletions(-) diff --git a/BaseBin/jailbreakd/src/main.m b/BaseBin/jailbreakd/src/main.m index 3a040e455a..3481219b2f 100644 --- a/BaseBin/jailbreakd/src/main.m +++ b/BaseBin/jailbreakd/src/main.m @@ -107,6 +107,9 @@ int main(int argc, char* argv[]) } } + JBLogDebug("initializing exec patch handling..."); + initExecPatch(); + JBLogDebug("check in jailbreakd port..."); mach_port_t serverPort = jbclient_jailbreakd_checkin(); if (!MACH_PORT_VALID(serverPort)) { diff --git a/BaseBin/jailbreakd/src/server.m b/BaseBin/jailbreakd/src/server.m index 38169c004e..2c5a492371 100644 --- a/BaseBin/jailbreakd/src/server.m +++ b/BaseBin/jailbreakd/src/server.m @@ -5,6 +5,18 @@ #include #include +static dispatch_queue_t execPatchRequestQueue(void) +{ + static dispatch_queue_t queue; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + // Keep the receive queue free for launchd's nested child-patch request + // while exec registration is waiting in kevent. + queue = dispatch_queue_create("com.roothide.jailbreakd.exec-patch", DISPATCH_QUEUE_SERIAL); + }); + return queue; +} + void jailbreakd_reply_message(JBD_MESSAGE_ID msgId, xpc_object_t reply) { char* desc = NULL; @@ -94,17 +106,27 @@ void jailbreakd_received_message(mach_port_t port) case JBD_MSG_SPAWN_EXEC_START: { bool resume = xpc_dictionary_get_bool(message, "resume"); const char* execfile = xpc_dictionary_get_string(message, "execfile"); - JBLogDebug("spawn exec start: %d %s", clientPid, execfile); - int64_t result = spawnExecPatchAdd(clientPid, resume); - xpc_dictionary_set_int64(reply, "result", result); + NSString* execPath = execfile ? @(execfile) : @"(null)"; + dispatch_async(execPatchRequestQueue(), ^{ + JBLogDebug("spawn exec start: %d %s", clientPid, execPath.UTF8String); + int64_t result = spawnExecPatchAdd(clientPid, resume); + xpc_dictionary_set_int64(reply, "result", result); + jailbreakd_reply_message(msgId, reply); + }); + reply = nil; break; } case JBD_MSG_SPAWN_EXEC_CANCEL: { const char* execfile = xpc_dictionary_get_string(message, "execfile"); - JBLogDebug("spawn exec cancel: %d %s", clientPid, execfile); - int64_t result = spawnExecPatchDel(clientPid); - xpc_dictionary_set_int64(reply, "result", result); + NSString* execPath = execfile ? @(execfile) : @"(null)"; + dispatch_async(execPatchRequestQueue(), ^{ + JBLogDebug("spawn exec cancel: %d %s", clientPid, execPath.UTF8String); + int64_t result = spawnExecPatchDel(clientPid); + xpc_dictionary_set_int64(reply, "result", result); + jailbreakd_reply_message(msgId, reply); + }); + reply = nil; break; } diff --git a/BaseBin/libjailbreak/src/roothider/exec_patch.h b/BaseBin/libjailbreak/src/roothider/exec_patch.h index e8e55426c1..285cfad71b 100644 --- a/BaseBin/libjailbreak/src/roothider/exec_patch.h +++ b/BaseBin/libjailbreak/src/roothider/exec_patch.h @@ -8,6 +8,7 @@ #import #include +void initExecPatch(void); int spawnExecPatchAdd(int pid, bool resume); int spawnExecPatchDel(int pid); diff --git a/BaseBin/libjailbreak/src/roothider/jailbreakd.c b/BaseBin/libjailbreak/src/roothider/jailbreakd.c index 271effed75..c6e9d60f1b 100644 --- a/BaseBin/libjailbreak/src/roothider/jailbreakd.c +++ b/BaseBin/libjailbreak/src/roothider/jailbreakd.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -296,6 +297,61 @@ xpc_object_t jailbreakdXpcRequest(xpc_object_t xdict) return xreply; } +typedef struct { + dispatch_semaphore_t semaphore; + xpc_object_t reply; + _Atomic uint32_t references; +} jbd_timed_request_t; + +static void jbdTimedRequestRelease(jbd_timed_request_t *request) +{ + if (atomic_fetch_sub_explicit(&request->references, 1, memory_order_acq_rel) != 1) { + return; + } + + if (request->reply) { + xpc_release(request->reply); + } +#if !OS_OBJECT_USE_OBJC + dispatch_release(request->semaphore); +#endif + free(request); +} + +xpc_object_t jailbreakdXpcRequestWithTimeout(xpc_object_t xdict, uint64_t timeoutNanoseconds) +{ + jbd_timed_request_t *request = calloc(1, sizeof(*request)); + if (!request) { + return NULL; + } + + request->semaphore = dispatch_semaphore_create(0); + if (!request->semaphore) { + free(request); + return NULL; + } + atomic_init(&request->references, 2); + + xpc_retain(xdict); + dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{ + request->reply = jailbreakdXpcRequest(xdict); + xpc_release(xdict); + dispatch_semaphore_signal(request->semaphore); + jbdTimedRequestRelease(request); + }); + + if (dispatch_semaphore_wait(request->semaphore, + dispatch_time(DISPATCH_TIME_NOW, timeoutNanoseconds)) != 0) { + jbdTimedRequestRelease(request); + return NULL; + } + + xpc_object_t reply = request->reply; + request->reply = NULL; + jbdTimedRequestRelease(request); + return reply; +} + int jbdTestCall(int value) { xpc_object_t message = xpc_dictionary_create_empty(); @@ -347,12 +403,16 @@ int jbdSpawnPatchChild(int pid, bool resume) xpc_dictionary_set_uint64(message, "id", JBD_MSG_SPAWN_PATCH_CHILD); xpc_dictionary_set_int64(message, "pid", pid); xpc_dictionary_set_bool(message, "resume", resume); - xpc_object_t reply = jailbreakdXpcRequest(message); + // launchd must be able to reach its existing failed-spawn cleanup instead + // of waiting indefinitely and triggering a system watchdog reboot. + xpc_object_t reply = jailbreakdXpcRequestWithTimeout(message, 10 * NSEC_PER_SEC); xpc_release(message); int64_t result = -1; if (reply) { result = xpc_dictionary_get_int64(reply, "result"); xpc_release(reply); + } else { + JBLogError("jbdSpawnPatchChild timed out or failed for pid %d", pid); } return result; } diff --git a/BaseBin/libjailbreak/src/roothider/jailbreakd.h b/BaseBin/libjailbreak/src/roothider/jailbreakd.h index 21481978e8..9730fecb15 100644 --- a/BaseBin/libjailbreak/src/roothider/jailbreakd.h +++ b/BaseBin/libjailbreak/src/roothider/jailbreakd.h @@ -1,7 +1,9 @@ #ifndef JAILBREAKD_H #define JAILBREAKD_H +#include #include +#include typedef enum { JBD_MSG_TEST_CALL = 101, @@ -23,6 +25,9 @@ void setJailbreakdProcess(pid_t pid); mach_port_t jailbreakdClientPort(); mach_port_t jailbreakdServerPort(); +XPC_RETURNS_RETAINED xpc_object_t jailbreakdXpcRequest(xpc_object_t xdict); +XPC_RETURNS_RETAINED xpc_object_t jailbreakdXpcRequestWithTimeout(xpc_object_t xdict, uint64_t timeoutNanoseconds); + int jbdTestCall(int value); int jbdSystemwideLog(const char* fmt, ...); @@ -33,4 +38,4 @@ int jbdExecTraceStart(const char* execfile, bool* traced); int jbdExecTraceCancel(const char* execfile, bool* detached); int jbdSpinlockFixOnly(int pid, bool resume); -#endif // JAILBREAKD_H \ No newline at end of file +#endif // JAILBREAKD_H