diff --git a/Userland/Libraries/LibC/cxxabi.cpp b/Userland/Libraries/LibC/cxxabi.cpp index c7af6660ba..44c0855ab6 100644 --- a/Userland/Libraries/LibC/cxxabi.cpp +++ b/Userland/Libraries/LibC/cxxabi.cpp @@ -1,15 +1,17 @@ /* - * Copyright (c) 2019-2020, Andrew Kaster + * Copyright (c) 2019-2021, Andrew Kaster * * SPDX-License-Identifier: BSD-2-Clause */ +#include #include #include #include #include #include #include +#include #include #include #include @@ -23,7 +25,8 @@ struct AtExitEntry { bool has_been_called { false }; }; -static constexpr size_t max_atexit_entry_count = PAGE_SIZE / sizeof(AtExitEntry); +// We'll re-allocate the region if it ends up being too small at runtime +static size_t atexit_entry_region_size = 2 * PAGE_SIZE; static AtExitEntry* atexit_entries; static size_t atexit_entry_count = 0; @@ -31,7 +34,7 @@ static pthread_mutex_t atexit_mutex = __PTHREAD_MUTEX_INITIALIZER; static void lock_atexit_handlers() { - if (mprotect(atexit_entries, PAGE_SIZE, PROT_READ) < 0) { + if (mprotect(atexit_entries, atexit_entry_region_size, PROT_READ) < 0) { perror("lock_atexit_handlers"); _exit(1); } @@ -39,7 +42,7 @@ static void lock_atexit_handlers() static void unlock_atexit_handlers() { - if (mprotect(atexit_entries, PAGE_SIZE, PROT_READ | PROT_WRITE) < 0) { + if (mprotect(atexit_entries, atexit_entry_region_size, PROT_READ | PROT_WRITE) < 0) { perror("unlock_atexit_handlers"); _exit(1); } @@ -49,13 +52,9 @@ int __cxa_atexit(AtExitFunction exit_function, void* parameter, void* dso_handle { __pthread_mutex_lock(&atexit_mutex); - if (atexit_entry_count >= max_atexit_entry_count) { - __pthread_mutex_unlock(&atexit_mutex); - return -1; - } - + // allocate initial atexit region if (!atexit_entries) { - atexit_entries = (AtExitEntry*)mmap(nullptr, PAGE_SIZE, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0); + atexit_entries = (AtExitEntry*)mmap(nullptr, atexit_entry_region_size, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0); if (atexit_entries == MAP_FAILED) { __pthread_mutex_unlock(&atexit_mutex); perror("__cxa_atexit mmap"); @@ -63,6 +62,30 @@ int __cxa_atexit(AtExitFunction exit_function, void* parameter, void* dso_handle } } + // reallocate atexit region, increasing size by PAGE_SIZE + if ((atexit_entry_count) >= (atexit_entry_region_size / sizeof(AtExitEntry))) { + if (Checked::addition_would_overflow(atexit_entry_region_size, PAGE_SIZE)) { + __pthread_mutex_unlock(&atexit_mutex); + return -1; + } + dbgln_if(GLOBAL_DTORS_DEBUG, "__cxa_atexit: Growing exit handler region from {} to {}", atexit_entry_region_size, atexit_entry_region_size + PAGE_SIZE); + size_t new_atexit_region_size = atexit_entry_region_size + PAGE_SIZE; + + auto* new_atexit_entries = (AtExitEntry*)mmap(nullptr, new_atexit_region_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0); + if (new_atexit_entries == MAP_FAILED) { + __pthread_mutex_unlock(&atexit_mutex); + perror("__cxa_atexit mmap (new size)"); + return -1; + } + memcpy(new_atexit_entries, atexit_entries, atexit_entry_region_size); + if (munmap(atexit_entries, atexit_entry_region_size) < 0) { + perror("__cxa_atexit munmap old region"); + // leak the old region on failure + } + atexit_entries = new_atexit_entries; + atexit_entry_region_size = new_atexit_region_size; + } + unlock_atexit_handlers(); atexit_entries[atexit_entry_count++] = { exit_function, parameter, dso_handle, false }; lock_atexit_handlers();