this repo has no description
1
fork

Configure Feed

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

libkernel: Implement guarded descriptor table

This table can be used to guard arbitrary descriptors for certain actions.

A future commit will use the guard table to protect RPC FDs.

+293 -10
+5 -1
src/CMakeLists.txt
··· 57 57 add_subdirectory(buildtools) 58 58 add_subdirectory(hosttools) 59 59 add_subdirectory(libelfloader/wrapgen) 60 - add_subdirectory(libsimple) 60 + # add the Linux build of libsimple now 61 + add_subdirectory(libsimple "${CMAKE_CURRENT_BINARY_DIR}/libsimple-linux") 61 62 add_subdirectory(external/darlingserver) 62 63 add_subdirectory(startup) 63 64 ··· 108 109 ${CMAKE_SOURCE_DIR}/${DARLING_SDK_RELATIVE_PATH}/usr/include/libxml2 109 110 ${CMAKE_BINARY_DIR}/${DARLING_SDK_RELATIVE_PATH}/usr/include/libxml2 110 111 ) 112 + 113 + # add the Mach-O build of libsimple now 114 + add_subdirectory(libsimple "${CMAKE_CURRENT_BINARY_DIR}/libsimple-darling") 111 115 112 116 add_subdirectory(external/libkqueue) 113 117
+2
src/kernel/CMakeLists.txt
··· 47 47 system_c 48 48 compiler_rt 49 49 system_dyld 50 + DEPENDENCIES 51 + libsimple_darling 50 52 ) 51 53 #target_link_libraries(system_kernel system_duct platform_static32 platform_static64) 52 54 make_fat(system_kernel)
+5
src/kernel/emulation/linux/CMakeLists.txt
··· 49 49 guarded/guarded_open_np.c 50 50 guarded/guarded_close_np.c 51 51 guarded/guarded_kqueue_np.c 52 + guarded/table.c 52 53 machdep/machdeps.c 53 54 machdep/machdep-table.S 54 55 machdep/tls.c ··· 313 314 set_source_files_properties(${CMAKE_BINARY_DIR}/src/external/darlingserver/src/rpc.c PROPERTIES 314 315 GENERATED TRUE 315 316 COMPILE_FLAGS "-include ${CMAKE_CURRENT_SOURCE_DIR}/resources/dserver-rpc-defs.h" 317 + ) 318 + 319 + include_directories( 320 + ../../../libsimple/include 316 321 ) 317 322 318 323 add_darling_object_library(emulation ${emulation_sources})
+197
src/kernel/emulation/linux/guarded/table.c
··· 1 + #include "table.h" 2 + #include <libsimple/lock.h> 3 + #include <stddef.h> 4 + #include <errno.h> 5 + #include "../unistd/close.h" 6 + 7 + #define MAX_GUARD_COUNT 1024 8 + 9 + #define GUARD_ENTRY_EMPTY_FD (-1) 10 + #define GUARD_ENTRY_DELETED_FD (-2) 11 + 12 + typedef struct guard_entry { 13 + int fd; 14 + guard_flags_t flags; 15 + } guard_entry_t; 16 + 17 + // this is a linearly-probed, open-addressed hash table; 18 + // the hash is the FD number itself. 19 + // 20 + // basically, we find the optimal index for the FD, and if it's occupied, 21 + // we keep going until we find an unoccupied slot, wrapping around 22 + // if necessary (until we return to the optimal index). 23 + // 24 + // when deleting, we mark entries as deleted rather than empty. 25 + // yes, this incurs a performance penalty, but: 26 + // 1) we shouldn't ever have collisions, really. most systems limit userspace to 1024 FDs. 27 + // 2) we don't expect to be guarding too many descriptors (mainly just the darlingserver RPC descriptors). 28 + // 3) the additional complexity of eager deletion just isn't worth it (vs lazy deletion, which is what we're doing). 29 + static guard_entry_t guard_table[MAX_GUARD_COUNT] = { 30 + [0 ... MAX_GUARD_COUNT - 1] = { 31 + .fd = GUARD_ENTRY_EMPTY_FD, 32 + .flags = guard_flag_none, 33 + }, 34 + }; 35 + // TODO: this should be an RW lock 36 + static libsimple_lock_t guard_table_lock = LIBSIMPLE_LOCK_INITIALIZER; 37 + 38 + // normal search 39 + static guard_entry_t* guard_table_find_locked(int fd) { 40 + size_t start_index = fd % MAX_GUARD_COUNT; 41 + 42 + // check the optimal index first, then sequentially until the end of the table 43 + for (size_t i = start_index; i < MAX_GUARD_COUNT; ++i) { 44 + guard_entry_t* entry = &guard_table[i]; 45 + 46 + if (entry->fd == fd) { 47 + return entry; 48 + } 49 + 50 + if (entry->fd == GUARD_ENTRY_EMPTY_FD) { 51 + return NULL; 52 + } 53 + } 54 + 55 + // rinse and repeat starting from index 0 56 + for (size_t i = 0; i < start_index; ++i) { 57 + guard_entry_t* entry = &guard_table[i]; 58 + 59 + if (entry->fd == fd) { 60 + return entry; 61 + } 62 + 63 + if (entry->fd == GUARD_ENTRY_EMPTY_FD) { 64 + return NULL; 65 + } 66 + } 67 + 68 + // the table was full and our FD wasn't there 69 + return NULL; 70 + }; 71 + 72 + // insertion search 73 + static guard_entry_t* guard_table_find_available_locked(int fd) { 74 + size_t start_index = fd % MAX_GUARD_COUNT; 75 + 76 + // check the optimal index first, then sequentially until the end of the table 77 + for (size_t i = start_index; i < MAX_GUARD_COUNT; ++i) { 78 + guard_entry_t* entry = &guard_table[i]; 79 + 80 + if (entry->fd == fd || entry->fd == GUARD_ENTRY_EMPTY_FD || entry->fd == GUARD_ENTRY_DELETED_FD) { 81 + return entry; 82 + } 83 + } 84 + 85 + // rinse and repeat starting from index 0 86 + for (size_t i = 0; i < start_index; ++i) { 87 + guard_entry_t* entry = &guard_table[i]; 88 + 89 + if (entry->fd == fd || entry->fd == GUARD_ENTRY_EMPTY_FD || entry->fd == GUARD_ENTRY_DELETED_FD) { 90 + return entry; 91 + } 92 + } 93 + 94 + // the table was full and we had no available slot for our FD 95 + return NULL; 96 + }; 97 + 98 + int guard_table_add(int fd, guard_flags_t flags) { 99 + #ifdef VARIANT_DYLD 100 + return -ENOSYS; 101 + #else 102 + int result = 0; 103 + 104 + libsimple_lock_lock(&guard_table_lock); 105 + 106 + guard_entry_t* entry = guard_table_find_locked(fd); 107 + if (entry) { 108 + result = -EEXIST; 109 + } else { 110 + entry = guard_table_find_available_locked(fd); 111 + if (entry) { 112 + entry->fd = fd; 113 + entry->flags = flags; 114 + } else { 115 + result = -ENOMEM; 116 + } 117 + } 118 + 119 + libsimple_lock_unlock(&guard_table_lock); 120 + 121 + return result; 122 + #endif 123 + }; 124 + 125 + int guard_table_modify(int fd, guard_flags_t flags) { 126 + #ifdef VARIANT_DYLD 127 + return -ENOSYS; 128 + #else 129 + int result = 0; 130 + 131 + libsimple_lock_lock(&guard_table_lock); 132 + 133 + guard_entry_t* entry = guard_table_find_locked(fd); 134 + if (entry) { 135 + entry->flags = flags; 136 + } else { 137 + result = -ENOENT; 138 + } 139 + 140 + libsimple_lock_unlock(&guard_table_lock); 141 + 142 + return result; 143 + #endif 144 + }; 145 + 146 + int guard_table_remove(int fd) { 147 + #ifdef VARIANT_DYLD 148 + return -ENOSYS; 149 + #else 150 + int result = 0; 151 + 152 + libsimple_lock_lock(&guard_table_lock); 153 + 154 + guard_entry_t* entry = guard_table_find_locked(fd); 155 + if (entry) { 156 + entry->fd = GUARD_ENTRY_DELETED_FD; 157 + entry->flags = guard_flag_none; 158 + } else { 159 + result = -ENOENT; 160 + } 161 + 162 + libsimple_lock_unlock(&guard_table_lock); 163 + 164 + return result; 165 + #endif 166 + }; 167 + 168 + int guard_table_check(int fd, guard_flags_t flags) { 169 + #ifdef VARIANT_DYLD 170 + return 0; 171 + #else 172 + int result = 0; 173 + libsimple_lock_lock(&guard_table_lock); 174 + guard_entry_t* entry = guard_table_find_locked(fd); 175 + result = entry && (entry->flags & flags) != 0; 176 + libsimple_lock_unlock(&guard_table_lock); 177 + return result; 178 + #endif 179 + }; 180 + 181 + void guard_table_postfork_child(void) { 182 + #ifndef VARIANT_DYLD 183 + // after forking, the lock is invalid in the child; let's reinitialize it 184 + libsimple_lock_init(&guard_table_lock); 185 + 186 + // now let's close any descriptors marked as close-on-fork 187 + for (size_t i = 0; i < MAX_GUARD_COUNT; ++i) { 188 + guard_entry_t* entry = &guard_table[i]; 189 + if (entry->fd >= 0 && (entry->flags & guard_flag_close_on_fork) != 0) { 190 + // FIXME: if the FD is in a kqueue, this won't notify the kqueue 191 + close_internal(entry->fd); 192 + entry->fd = GUARD_ENTRY_DELETED_FD; 193 + entry->flags = guard_flag_none; 194 + } 195 + } 196 + #endif 197 + };
+40
src/kernel/emulation/linux/guarded/table.h
··· 1 + #ifndef _DARLING_EMULATION_LINUX_GUARDED_TABLE_H_ 2 + #define _DARLING_EMULATION_LINUX_GUARDED_TABLE_H_ 3 + 4 + typedef enum guard_flags { 5 + guard_flag_none = 0, 6 + guard_flag_prevent_close = 1U << 0, 7 + guard_flag_close_on_fork = 1U << 1, 8 + } __attribute__((flag_enum)) guard_flags_t; 9 + 10 + // TODO: expand this code to support different types of guards (e.g. close, dup, socket IPC, etc.). 11 + // right now, it's only checked when closing descriptors. 12 + 13 + /** 14 + * @retval `0` on success 15 + * @retval `-EEXIST` if the entry already exists 16 + * @retval `-ENOMEM` if no space available for new entry 17 + */ 18 + int guard_table_add(int fd, guard_flags_t flags); 19 + 20 + /** 21 + * @retval `0` on success 22 + * @retval `-ENOENT` if the entry doesn't exist 23 + */ 24 + int guard_table_modify(int fd, guard_flags_t flags); 25 + 26 + /** 27 + * @retval `0` on success 28 + * @retval `-ENOENT` if the entry doesn't exist 29 + */ 30 + int guard_table_remove(int fd); 31 + 32 + /** 33 + * @returns `0` if FD is unguarded for all the specified guard flags, 1 otherwise (if it is guarded for at least one). 34 + * This includes the case when the FD isn't present in the table at all (it is simply treated as unguarded). 35 + */ 36 + int guard_table_check(int fd, guard_flags_t flags); 37 + 38 + void guard_table_postfork_child(void); 39 + 40 + #endif // _DARLING_EMULATION_LINUX_GUARDED_TABLE_H_
+5 -2
src/kernel/emulation/linux/unistd/close.c
··· 6 6 #include "../mach/lkm.h" 7 7 #include <lkm/api.h> 8 8 #include "../simple.h" 9 + #include "../guarded/table.h" 9 10 10 11 __attribute__((weak)) 11 12 __attribute__((visibility("default"))) ··· 21 22 { 22 23 int ret; 23 24 24 - if (fd == mach_driver_get_dyld_fd()) { 25 - __simple_kprintf("*** Someone tried to close the special LKM fd! ***"); 25 + if (guard_table_check(fd, guard_flag_prevent_close)) { 26 + // we should crash, actually. 27 + // for now, let's silently (as far as the caller is concerned) ignore it. 28 + __simple_kprintf("*** Someone tried to close a guarded FD (%d) via close! ***", fd); 26 29 return 0; 27 30 } 28 31
+7
src/kernel/emulation/linux/unistd/dup2.c
··· 5 5 #include "../duct_errno.h" 6 6 #include "../mach/lkm.h" 7 7 #include <lkm/api.h> 8 + #include "../simple.h" 9 + #include "../guarded/table.h" 8 10 9 11 extern void kqueue_dup(int oldfd, int newfd); 10 12 11 13 long sys_dup2(int fd_from, int fd_to) 12 14 { 13 15 int ret; 16 + 17 + if (guard_table_check(fd_to, guard_flag_prevent_close)) { 18 + __simple_kprintf("*** Someone tried to close a guarded FD (%d) via dup2! ***", fd_to); 19 + __simple_abort(); 20 + } 14 21 15 22 #if defined(__NR_dup2) 16 23 ret = LINUX_SYSCALL2(__NR_dup2, fd_from, fd_to);
+26 -7
src/libsimple/CMakeLists.txt
··· 1 1 project(libsimple) 2 2 3 - add_library(libsimple_darlingserver 3 + set(libsimple_sources 4 4 src/lock.c 5 5 ) 6 6 7 - target_include_directories(libsimple_darlingserver PUBLIC 8 - include 9 - ) 7 + if (NOT DEFINED libsimple_linux_added) 8 + set(libsimple_linux_added TRUE PARENT_SCOPE) 9 + 10 + add_library(libsimple_darlingserver "${libsimple_sources}") 11 + 12 + target_include_directories(libsimple_darlingserver PUBLIC 13 + include 14 + ) 15 + 16 + target_compile_definitions(libsimple_darlingserver PRIVATE 17 + LIBSIMPLE_LINUX=1 18 + ) 19 + else() 20 + add_darling_static_library(libsimple_darling 21 + FAT 22 + SOURCES "${libsimple_sources}" 23 + ) 10 24 11 - target_compile_definitions(libsimple_darlingserver PRIVATE 12 - LIBSIMPLE_LINUX=1 13 - ) 25 + target_include_directories(libsimple_darling PUBLIC 26 + include 27 + ) 28 + 29 + target_compile_definitions(libsimple_darling PRIVATE 30 + LIBSIMPLE_DARLING=1 31 + ) 32 + endif()
+6
src/libsimple/src/lock.c
··· 27 27 #if LIBSIMPLE_LINUX 28 28 #include <sys/syscall.h> 29 29 #include <unistd.h> 30 + #elif LIBSIMPLE_DARLING 31 + extern int __linux_futex(int* uaddr, int op, int val, const struct timespec* timeout, int* uaddr2, int val3); 30 32 #endif 31 33 34 + #if LIBSIMPLE_DARLING 35 + #define linux_futex __linux_futex 36 + #else // !LIBSIMPLE_DARLING 32 37 static int linux_futex(int* uaddr, int op, int val, const struct timespec* timeout, int* uaddr2, int val3) { 33 38 #if LIBSIMPLE_LINUX 34 39 return syscall(SYS_futex, uaddr, op, val, timeout, uaddr2, val3); ··· 36 41 #error linux_futex not implemented for this platform 37 42 #endif 38 43 }; 44 + #endif // !LIBSIMPLE_DARLING 39 45 40 46 // 41 47 // libsimple_lock