From 7bd796b7e3fc1dbde0c3af56a38a2d1a7db3c55d Mon Sep 17 00:00:00 2001 From: Itamar Date: Fri, 30 Apr 2021 15:04:08 +0300 Subject: [PATCH] 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. --- Userland/Libraries/LibELF/DynamicLinker.cpp | 37 +++++++++++++++++++++ 1 file changed, 37 insertions(+) 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 __dlclose(void* handle) return {}; } +static Optional 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 __dlopen(const char* filename, int flags) { // FIXME: RTLD_NOW and RTLD_LOCAL are not supported @@ -355,6 +387,9 @@ static Result __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 __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." };