mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-01-07 03:28:25 +03:00
LibC: Expand region for global destructors when it reaches capacity
In 553361d
we started mprotecting the atexit handlers when they are not
being modified or executed. As part of that commit, we unintentionally
changed the max number of global destructors from 1024 to 256 (on x86,
only 128 on x86_64). This patch expands the initial size of the global
destructors page to 2 pages from 1, and allows the pool to be expanded
at runtime by mapping a new set of pages and copying the AtExitEntries
over.
This commit is contained in:
parent
7589cc2494
commit
fe701052ea
Notes:
sideshowbarker
2024-07-18 04:39:28 +09:00
Author: https://github.com/ADKaster Commit: https://github.com/SerenityOS/serenity/commit/fe701052ea4 Pull-request: https://github.com/SerenityOS/serenity/pull/9843 Issue: https://github.com/SerenityOS/serenity/issues/9094
@ -1,15 +1,17 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020, Andrew Kaster <akaster@serenityos.org>
|
||||
* Copyright (c) 2019-2021, Andrew Kaster <akaster@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Checked.h>
|
||||
#include <AK/Debug.h>
|
||||
#include <AK/Format.h>
|
||||
#include <LibC/bits/pthread_integration.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/internals.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
@ -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<size_t>::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();
|
||||
|
Loading…
Reference in New Issue
Block a user