summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibELF/DynamicLinker.cpp
diff options
context:
space:
mode:
authorItamar <itamar8910@gmail.com>2021-04-30 15:04:08 +0300
committerAndreas Kling <kling@serenityos.org>2021-04-30 18:47:39 +0200
commit7bd796b7e3fc1dbde0c3af56a38a2d1a7db3c55d (patch)
treef19f0e1c9618ec936d66c16ee914d1084b37f144 /Userland/Libraries/LibELF/DynamicLinker.cpp
parent101ac45c1a1e0bcfe67c95c16590dbe970b24abc (diff)
downloadserenity-7bd796b7e3fc1dbde0c3af56a38a2d1a7db3c55d.zip
LibELF: Perform verification of TLS data in dlopen
When loading a library at runtime with dlopen(), we now check that: 1. The library's TLS size does not overflow the size of the allocated TLS block. 2. The Library's TLS data is all zeroed. We check for both of these cases because we currently do not support them correctly. When we do add support for them, we can remove these checks.
Diffstat (limited to 'Userland/Libraries/LibELF/DynamicLinker.cpp')
-rw-r--r--Userland/Libraries/LibELF/DynamicLinker.cpp37
1 files changed, 37 insertions, 0 deletions
diff --git a/Userland/Libraries/LibELF/DynamicLinker.cpp b/Userland/Libraries/LibELF/DynamicLinker.cpp
index ba6d1dbccd..3423e517a4 100644
--- a/Userland/Libraries/LibELF/DynamicLinker.cpp
+++ b/Userland/Libraries/LibELF/DynamicLinker.cpp
@@ -43,6 +43,7 @@ using DlIteratePhdrFunction = int (*)(DlIteratePhdrCallbackFunction, void*);
static size_t s_current_tls_offset = 0;
static size_t s_total_tls_size = 0;
+static size_t s_allocated_tls_block_size = 0;
static char** s_envp = nullptr;
static LibCExitFunction s_libc_exit = nullptr;
static __pthread_mutex_t s_loader_lock = __PTHREAD_MUTEX_INITIALIZER;
@@ -176,6 +177,8 @@ static void allocate_tls()
void* master_tls = ::allocate_tls((char*)initial_tls_data.data(), initial_tls_data.size());
VERIFY(master_tls != (void*)-1);
dbgln_if(DYNAMIC_LOAD_DEBUG, "from userspace, master_tls: {:p}", master_tls);
+
+ s_allocated_tls_block_size = initial_tls_data.size();
}
static int __dl_iterate_phdr(DlIteratePhdrCallbackFunction callback, void* data)
@@ -326,6 +329,35 @@ static Result<void, DlErrorMessage> __dlclose(void* handle)
return {};
}
+static Optional<DlErrorMessage> verify_tls_for_dlopen(const DynamicLoader& loader)
+{
+ if (loader.tls_size_of_current_object() == 0)
+ return {};
+
+ if (s_total_tls_size + loader.tls_size_of_current_object() > s_allocated_tls_block_size)
+ return DlErrorMessage("TLS size too large");
+
+ bool tls_data_is_all_zero = true;
+ loader.image().for_each_program_header([&loader, &tls_data_is_all_zero](ELF::Image::ProgramHeader program_header) {
+ if (program_header.type() != PT_TLS)
+ return IterationDecision::Continue;
+
+ auto* tls_data = (const u8*)loader.image().base_address() + program_header.offset();
+ for (size_t i = 0; i < program_header.size_in_image(); ++i) {
+ if (tls_data[i] != 0) {
+ tls_data_is_all_zero = false;
+ break;
+ }
+ }
+ return IterationDecision::Break;
+ });
+
+ if (tls_data_is_all_zero)
+ return {};
+
+ return DlErrorMessage("Using dlopen() with libraries that have non-zeroed TLS is currently not supported");
+}
+
static Result<void*, DlErrorMessage> __dlopen(const char* filename, int flags)
{
// FIXME: RTLD_NOW and RTLD_LOCAL are not supported
@@ -355,6 +387,9 @@ static Result<void*, DlErrorMessage> __dlopen(const char* filename, int flags)
return result1.error();
}
+ if (auto error = verify_tls_for_dlopen(result1.value()); error.has_value())
+ return error.value();
+
auto result2 = map_dependencies(library_name);
if (result2.is_error()) {
return result2.error();
@@ -364,6 +399,8 @@ static Result<void*, DlErrorMessage> __dlopen(const char* filename, int flags)
if (result.is_error())
return result.error();
+ s_total_tls_size += result1.value()->tls_size_of_current_object();
+
auto object = s_global_objects.get(library_name);
if (!object.has_value())
return DlErrorMessage { "Could not load ELF object." };