this repo has no description
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Store per-thread RPC FDs on the ELF side (in mldr)

RPC FDs need to be accessible in libkernel even after libpthread trashes
the thread structure, so we either have to roll our own TLS (yuck)
or defer to elfcalls.

The weird thing is that I actually don't see where libpthread invalidates
the thread structure before calling bsdthread_terminate, but I was
intermittently getting segfaults in bsdthread_terminate when trying to
read the RPC FD. Probably a race condition (maybe with joining threads);
either way, this fixes that.

Also, we now guard the RPC FDs using the guard table introduced in the previous commit.

+116 -112
-2
src/dyld/src/dyldAPIs.cpp
··· 261 261 {"__dyld_link_module", (void*)_dyld_link_module }, 262 262 #endif 263 263 #ifdef DARLING 264 - {"__dyld_get_mach_driver_fd", (void*)mach_driver_get_dyld_fd }, 265 264 {"__dyld_get_elfcalls", (void*)elfcalls_get_pointer }, 266 - {"__dyld_set_mach_driver_fd", (void*)mach_driver_set_dyld_fd }, 267 265 #endif 268 266 #pragma clang diagnostic pop 269 267 #endif //DEPRECATED_APIS_SUPPORTED
+11
src/kernel/emulation/linux/bsdthread/bsdthread_create.c
··· 14 14 #include "../mach/mach_traps.h" 15 15 16 16 #include <darlingserver/rpc.h> 17 + #include "../guarded/table.h" 17 18 18 19 extern void *memset(void *s, int c, size_t n); 19 20 ··· 30 31 31 32 // http://www.tldp.org/FAQ/Threads-FAQ/clone.c 32 33 34 + static void rpc_guard(int fd) { 35 + guard_table_add(fd, guard_flag_prevent_close | guard_flag_close_on_fork); 36 + }; 37 + 38 + static void rpc_unguard(int fd) { 39 + guard_table_remove(fd); 40 + }; 41 + 33 42 static const struct darling_thread_create_callbacks callbacks = { 34 43 .thread_self_trap = &thread_self_trap_impl, 35 44 .thread_set_tsd_base = &sys_thread_set_tsd_base, 45 + .rpc_guard = rpc_guard, 46 + .rpc_unguard = rpc_unguard, 36 47 }; 37 48 38 49 long sys_bsdthread_create(void* thread_start, void* arg,
+5
src/kernel/emulation/linux/bsdthread/bsdthread_terminate.c
··· 7 7 #include <stddef.h> 8 8 #include <stdint.h> 9 9 #include "../elfcalls_wrapper.h" 10 + #include "../guarded/table.h" 11 + #include "../mach/lkm.h" 10 12 11 13 int bsdthread_terminate_trap( 12 14 uintptr_t stackaddr, ··· 30 32 31 33 // point of no return; let xtrace know 32 34 _xtrace_thread_exit(); 35 + 36 + // we can also unguard the RPC FD for this thread now 37 + guard_table_remove(mach_driver_get_fd()); 33 38 34 39 return __darling_thread_terminate(stackaddr, freesize, pthread_obj_size); 35 40 #else
+11
src/kernel/emulation/linux/bsdthread/workq_kernreturn.c
··· 22 22 #include "../../../../startup/mldr/elfcalls/dthreads.h" 23 23 24 24 #include <darlingserver/rpc.h> 25 + #include "../guarded/table.h" 25 26 26 27 #define WQ_MAX_THREADS 64 27 28 ··· 91 92 // This is horrible, but it may work 92 93 static struct wq_kevent_data* wq_event_pending = NULL; 93 94 95 + static void rpc_guard(int fd) { 96 + guard_table_add(fd, guard_flag_prevent_close | guard_flag_close_on_fork); 97 + }; 98 + 99 + static void rpc_unguard(int fd) { 100 + guard_table_remove(fd); 101 + }; 102 + 94 103 static const struct darling_thread_create_callbacks callbacks = { 95 104 .thread_self_trap = &thread_self_trap_impl, 96 105 .thread_set_tsd_base = &sys_thread_set_tsd_base, 106 + .rpc_guard = rpc_guard, 107 + .rpc_unguard = rpc_unguard, 97 108 }; 98 109 99 110 static int extract_wq_flags(int priority) {
+6 -2
src/kernel/emulation/linux/elfcalls_wrapper.c
··· 80 80 return elfcalls()->dserver_socket_address(); 81 81 }; 82 82 83 - int __dserver_new_socket(void) { 84 - return elfcalls()->dserver_new_socket(); 83 + int __dserver_per_thread_socket(void) { 84 + return elfcalls()->dserver_per_thread_socket(); 85 + }; 86 + 87 + void __dserver_per_thread_socket_refresh(void) { 88 + return elfcalls()->dserver_per_thread_socket_refresh(); 85 89 };
+2 -1
src/kernel/emulation/linux/elfcalls_wrapper.h
··· 29 29 void* __darling_thread_get_stack(void); 30 30 31 31 const void* __dserver_socket_address(void); 32 - int __dserver_new_socket(void); 32 + int __dserver_per_thread_socket(void); 33 + void __dserver_per_thread_socket_refresh(void); 33 34 34 35 #ifdef __cplusplus 35 36 }
+7 -43
src/kernel/emulation/linux/mach/lkm.c
··· 10 10 #include "../simple.h" 11 11 #include "../misc/ioctl.h" 12 12 #include <elfcalls.h> 13 + #include "../guarded/table.h" 14 + #include "../elfcalls_wrapper.h" 13 15 14 16 extern int sys_open(const char*, int, int); 15 17 extern int close_internal(int); ··· 19 21 extern int sys_dup2(int, int); 20 22 extern int sys_fcntl(int, int, int); 21 23 extern _libkernel_functions_t _libkernel_functions; 22 - 23 - 24 - int driver_fd = -1; 25 24 26 25 VISIBLE 27 26 struct elf_calls* _elfcalls; ··· 38 37 int i; 39 38 for (i = 0; applep[i] != NULL; i++) 40 39 { 41 - if (strncmp(applep[i], "kernfd=", 7) == 0) 42 - { 43 - driver_fd = __simple_atoi(applep[i] + 7, NULL); 44 - } 45 40 if (strncmp(applep[i], "elf_calls=", 10) == 0) 46 41 { 47 42 uintptr_t table = (uintptr_t) __simple_atoi16(applep[i] + 10, NULL); ··· 50 45 } 51 46 } 52 47 #else 53 - // Ask for fd already set up by dyld 54 - int (*p)(void); 55 - _libkernel_functions->dyld_func_lookup("__dyld_get_mach_driver_fd", (void**) &p); 56 - 57 - driver_fd = (*p)(); 58 - 59 48 // ask for elfcalls already set up by dyld 60 49 void* (*p2)(void); 61 50 _libkernel_functions->dyld_func_lookup("__dyld_get_elfcalls", (void**)&p2); 62 51 63 52 _elfcalls = p2(); 64 - #endif 65 53 66 - #if 0 67 - // If mach_driver_init() is being called in the fork child, the LKM will now 68 - // swap out driver_fd for a new one. 69 - if (__real_ioctl(driver_fd, NR_get_api_version, 0) != DARLING_MACH_API_VERSION) 70 - { 71 - const char* msg = "Darling Mach kernel module reports different API level. Aborting.\n"; 72 - sys_write(2, msg, strlen(msg)); 73 - sys_kill(0, 6); 54 + if (applep) { 55 + // this is not a fork; guard the main thread's RPC FD we get from mldr 56 + // (in the child after a fork, sys_fork already takes care of this) 57 + guard_table_add(__dserver_per_thread_socket(), guard_flag_prevent_close | guard_flag_close_on_fork); 74 58 } 75 59 #endif 76 60 ··· 105 89 } 106 90 } 107 91 108 - void mach_driver_init_pthread(void) { 109 - _os_tsd_set_direct(__TSD_DSERVER_RPC_FD, (void*)(intptr_t)driver_fd); 110 - use_per_thread_driver_fd = true; 111 - }; 112 - 113 92 __attribute__((visibility("default"))) 114 93 int lkm_call(int call_nr, void* arg) 115 94 { ··· 124 103 __builtin_unreachable(); 125 104 } 126 105 127 - __attribute__((visibility("default"))) 128 - int mach_driver_get_dyld_fd(void) 129 - { 130 - return driver_fd; 131 - } 132 - 133 - __attribute__((visibility("default"))) 134 - void mach_driver_set_dyld_fd(int fd) { 135 - driver_fd = fd; 136 - }; 137 - 138 106 VISIBLE 139 107 int mach_driver_get_fd(void) { 140 - if (use_per_thread_driver_fd) { 141 - return (int)(intptr_t)_os_tsd_get_direct(__TSD_DSERVER_RPC_FD); 142 - } else { 143 - return driver_fd; 144 - } 108 + return __dserver_per_thread_socket(); 145 109 }; 146 110 147 111 VISIBLE
-6
src/kernel/emulation/linux/mach/lkm.h
··· 3 3 4 4 #include <os/tsd.h> 5 5 6 - // TSD slot 11 is reserved by Apple for Windows/WINE compatibility. 7 - // Since WINE can never run under Darling, we can use that slot for our own purposes. 8 - // Namely, we use it to store the the per-thread darlingserver socket. 9 - #define __TSD_DSERVER_RPC_FD 11 10 - 11 6 void mach_driver_init(const char** applep); 12 7 int lkm_call(int call_nr, void* arg); 13 8 int lkm_call_raw(int call_nr, void* arg); // w/o errno translation 14 9 int mach_driver_get_fd(void); 15 - int mach_driver_get_dyld_fd(void); 16 10 17 11 #endif 18 12
+8 -18
src/kernel/emulation/linux/process/fork.c
··· 12 12 #include "../mach/lkm.h" 13 13 #include "../unistd/close.h" 14 14 #include "../../../libsyscall/wrappers/_libkernel_init.h" 15 + #include "../guarded/table.h" 15 16 16 - extern int driver_fd; 17 17 extern _libkernel_functions_t _libkernel_functions; 18 18 19 19 long sys_fork(void) ··· 32 32 { 33 33 // in the child 34 34 35 - // close the old RPC fd 36 - // FIXME: we actually have to close ALL the old RPC fds for any threads the parent process may have had 37 - close_internal((int)(intptr_t)_os_tsd_get_direct(__TSD_DSERVER_RPC_FD)); 35 + // the old RPC FD will be closed in `guard_table_postfork_child`; 36 + // we don't need to close it ourselves. 37 + // that should also take care of closing descriptors for any other threads. 38 + guard_table_postfork_child(); 38 39 39 40 // create a new dserver RPC socket 40 - int new_rpc_fd = __dserver_new_socket(); 41 - if (new_rpc_fd < 0) { 42 - // we can't do anything if we don't get our own separate connection to darlingserver 43 - __simple_printf("Failed to create socket after fork\n"); 44 - __simple_abort(); 45 - } 41 + __dserver_per_thread_socket_refresh(); 46 42 47 - // set the new RPC fd 48 - _os_tsd_set_direct(__TSD_DSERVER_RPC_FD, (void*)(intptr_t)new_rpc_fd); 49 - driver_fd = new_rpc_fd; 50 - 51 - // update the fd stored in dyld, too 52 - void (*dyld_set_mach_driver_fd)(int fd); 53 - _libkernel_functions->dyld_func_lookup("__dyld_set_mach_driver_fd", (void**)&dyld_set_mach_driver_fd); 54 - dyld_set_mach_driver_fd(new_rpc_fd); 43 + // guard it 44 + guard_table_add(__dserver_per_thread_socket(), guard_flag_prevent_close | guard_flag_close_on_fork); 55 45 56 46 if (dserver_rpc_checkin(true) < 0) { 57 47 // we can't do ANYTHING if darlingserver fails to acknowledge us
-4
src/kernel/libsyscall/mach/mach_init.c
··· 204 204 205 205 _init_cpu_capabilities(); 206 206 _pthread_set_self(0); 207 - 208 - #ifdef DARLING 209 - mach_driver_init_pthread(); 210 - #endif 211 207 }
-3
src/startup/mldr/elfcalls/dthreads.h
··· 50 50 #define DTHREAD_TSD_SLOT_PTR_MUNGE 7 51 51 #define DTHREAD_TSD_SLOT_MACH_SPECIAL_REPLY 8 52 52 53 - // see src/kernel/emulation/linux/mach/lkm.h 54 - #define DTHREAD_TSD_SLOT_DSERVER_RPC_FD 11 55 - 56 53 #define DTHREAD_START_TSD_BASE_SET 0x10000000 57 54 58 55 #define DTHREAD_CANCEL_ENABLE 0x01
+4 -28
src/startup/mldr/elfcalls/elfcalls.c
··· 12 12 #include <sys/socket.h> 13 13 #include <fcntl.h> 14 14 15 + #include <darlingserver/rpc.h> 16 + 15 17 static void* dlopen_simple(const char* name) 16 18 { 17 19 return dlopen(name, RTLD_LAZY); ··· 60 62 return &__dserver_socket_address_data; 61 63 }; 62 64 63 - static int __dserver_new_socket(void) { 64 - int new_rpc_fd = socket(AF_UNIX, SOCK_DGRAM, 0); 65 - if (new_rpc_fd < 0) { 66 - return -1; 67 - } 68 - 69 - // make it close-on-exec 70 - int fd_flags = fcntl(new_rpc_fd, F_GETFD); 71 - if (fd_flags < 0) { 72 - close(new_rpc_fd); 73 - return -1; 74 - } 75 - if (fcntl(new_rpc_fd, F_SETFD, fd_flags | FD_CLOEXEC) < 0) { 76 - close(new_rpc_fd); 77 - return -1; 78 - } 79 - 80 - // auto-bind it 81 - sa_family_t family = AF_UNIX; 82 - if (bind(new_rpc_fd, (const struct sockaddr*)&family, sizeof(family)) < 0) { 83 - close(new_rpc_fd); 84 - return -1; 85 - } 86 - 87 - return new_rpc_fd; 88 - }; 89 - 90 65 void elfcalls_make(struct elf_calls* calls) 91 66 { 92 67 calls->dlopen = dlopen_simple; ··· 118 93 *((void**)&calls->shm_unlink) = shm_unlink; 119 94 120 95 calls->dserver_socket_address = __dserver_socket_address; 121 - calls->dserver_new_socket = __dserver_new_socket; 96 + calls->dserver_per_thread_socket = __darling_thread_rpc_socket; 97 + calls->dserver_per_thread_socket_refresh = __darling_thread_rpc_socket_refresh; 122 98 }
+5 -1
src/startup/mldr/elfcalls/elfcalls.h
··· 3 3 4 4 #include <stdint.h> 5 5 #include <stdbool.h> 6 + #include <stddef.h> 6 7 7 8 struct darling_thread_create_callbacks { 8 9 int (*thread_self_trap)(void); 9 10 void (*thread_set_tsd_base)(void*, int); 11 + void (*rpc_guard)(int); 12 + void (*rpc_unguard)(int); 10 13 }; 11 14 12 15 typedef const struct darling_thread_create_callbacks* darling_thread_create_callbacks_t; ··· 53 56 54 57 // darlingserver RPC info 55 58 const void* (*dserver_socket_address)(void); 56 - int (*dserver_new_socket)(void); 59 + int (*dserver_per_thread_socket)(void); 60 + void (*dserver_per_thread_socket_refresh)(void); 57 61 }; 58 62 59 63 #endif
+55 -4
src/startup/mldr/elfcalls/threads.c
··· 44 44 static __thread void* t_freeaddr; 45 45 static __thread size_t t_freesize; 46 46 static __thread int t_server_socket = -1; 47 + static __thread darling_thread_create_callbacks_t t_callbacks = NULL; 47 48 48 49 typedef void (*thread_ep)(void**, int, ...); 49 50 struct arg_struct ··· 82 83 dthread->tsd[DTHREAD_TSD_SLOT_ERRNO] = &dthread->err_no; 83 84 dthread->tsd[DTHREAD_TSD_SLOT_PTHREAD_QOS_CLASS] = (void*)(uintptr_t)(DTHREAD_DEFAULT_PRIORITY); 84 85 dthread->tsd[DTHREAD_TSD_SLOT_PTR_MUNGE] = 0; 85 - dthread->tsd[DTHREAD_TSD_SLOT_DSERVER_RPC_FD] = (void*)(intptr_t)-1; 86 86 dthread->tl_has_custom_stack = 0; 87 87 dthread->lock = (darwin_os_unfair_lock){0}; 88 88 ··· 231 231 exit(1); 232 232 } 233 233 234 - // the socket is ready; assign it now 235 - dthread->tsd[DTHREAD_TSD_SLOT_DSERVER_RPC_FD] = (void*)(intptr_t)new_rpc_fd; 234 + // guard the new RPC FD 235 + args.callbacks->rpc_guard(new_rpc_fd); 236 236 237 + // the socket is ready; assign it now 237 238 t_server_socket = new_rpc_fd; 239 + t_callbacks = args.callbacks; 238 240 239 241 // libpthread now expects the kernel to set the TSD 240 242 // so, since we're pretending to be the kernel handling threads... 241 243 args.callbacks->thread_set_tsd_base(&dthread->tsd[0], 0); 242 244 *flags |= args.is_workqueue ? DWQ_FLAG_THREAD_TSD_BASE_SET : DTHREAD_START_TSD_BASE_SET; 243 245 244 - // now that we've set the TSD, darlingserver RPC can now use our per-thread socket; 245 246 // let's check-in with darlingserver on this new thread 246 247 if (dserver_rpc_explicit_checkin(t_server_socket, false) < 0) { 247 248 // we can't do ANYTHING if darlingserver doesn't acknowledge us successfully ··· 326 327 } else { 327 328 dserver_rpc_kprintf(CHECKOUT_FAILURE_MESSAGE, sizeof(CHECKOUT_FAILURE_MESSAGE) - 1); 328 329 } 330 + } 331 + 332 + // close the RPC FD (if necessary) 333 + // it should already have been unguarded by our caller 334 + if (t_server_socket != -1) { 335 + close(t_server_socket); 329 336 } 330 337 331 338 if (getpid() == syscall(SYS_gettid)) ··· 359 366 360 367 return ((char*)stackaddr) + stacksize - 0x2000; 361 368 } 369 + 370 + extern int __dserver_main_thread_socket_fd; 371 + 372 + int __darling_thread_rpc_socket(void) { 373 + if (t_server_socket == -1) { 374 + if (getpid() == syscall(SYS_gettid)) { 375 + // this is the main thread 376 + t_server_socket = __dserver_main_thread_socket_fd; 377 + } else { 378 + // threads should already have a per-thread socket assigned when they're created 379 + abort(); 380 + } 381 + } 382 + return t_server_socket; 383 + }; 384 + 385 + void __darling_thread_rpc_socket_refresh(void) { 386 + int new_rpc_fd = socket(AF_UNIX, SOCK_DGRAM, 0); 387 + if (new_rpc_fd < 0) { 388 + abort(); 389 + } 390 + 391 + // make it close-on-exec 392 + int fd_flags = fcntl(new_rpc_fd, F_GETFD); 393 + if (fd_flags < 0) { 394 + abort(); 395 + } 396 + if (fcntl(new_rpc_fd, F_SETFD, fd_flags | FD_CLOEXEC) < 0) { 397 + abort(); 398 + } 399 + 400 + // auto-bind it 401 + sa_family_t family = AF_UNIX; 402 + if (bind(new_rpc_fd, (const struct sockaddr*)&family, sizeof(family)) < 0) { 403 + abort(); 404 + } 405 + 406 + t_server_socket = new_rpc_fd; 407 + 408 + // if this is the main thread, also update the socket used by mldr 409 + if (getpid() == syscall(SYS_gettid)) { 410 + __dserver_main_thread_socket_fd = t_server_socket; 411 + } 412 + };
+2
src/startup/mldr/elfcalls/threads.h
··· 33 33 int __darling_thread_terminate(void* stackaddr, 34 34 unsigned long freesize, unsigned long pthobj_size); 35 35 void* __darling_thread_get_stack(void); 36 + int __darling_thread_rpc_socket(void); 37 + void __darling_thread_rpc_socket_refresh(void); 36 38 37 39 #ifdef __cplusplus 38 40 }