From 322c161ee4c318c8493473a8104f4bc02c1ed100 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Thu, 28 Jan 2021 15:03:35 +0100 Subject: [PATCH] LibELF: Implement ASLR for shared libraries :^) Use mmap() with the new MAP_RANDOMIZED flag to load shared libraries at random addresses in each process. To avoid address space collisions, we start by doing a large chunk mmap that covers enough VM for both text and data, then we unmap and remap the data segment separately, once we know everything will fit. This is pretty cool! :^) --- Userland/Libraries/LibELF/DynamicLoader.cpp | 33 +++++++++++++++++++-- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/Userland/Libraries/LibELF/DynamicLoader.cpp b/Userland/Libraries/LibELF/DynamicLoader.cpp index 2af228400b..7591411c3d 100644 --- a/Userland/Libraries/LibELF/DynamicLoader.cpp +++ b/Userland/Libraries/LibELF/DynamicLoader.cpp @@ -42,6 +42,8 @@ static void* mmap_with_name(void* addr, size_t length, int prot, int flags, int { return mmap(addr, length, prot, flags, fd, offset); } + +# define MAP_RANDOMIZED 0 #endif namespace ELF { @@ -270,13 +272,28 @@ void DynamicLoader::load_program_headers() auto* region = text_region_ptr; void* requested_load_address = m_elf_image.is_dynamic() ? nullptr : region->desired_load_address().as_ptr(); + int text_mmap_flags = MAP_SHARED; + + if (m_elf_image.is_dynamic()) + text_mmap_flags |= MAP_RANDOMIZED; + else + text_mmap_flags |= MAP_FIXED; + ASSERT(!region->is_writable()); + // First, we map the text *and* data segments, in order to allocate enough VM + // to hold both contiguously in the address space. + + Checked total_mapping_size; + total_mapping_size = text_region_ptr->required_load_size(); + total_mapping_size += data_region_ptr->required_load_size(); + ASSERT(!total_mapping_size.has_overflow()); + void* text_segment_begin = mmap_with_name( requested_load_address, - region->required_load_size(), + total_mapping_size.value(), region->mmap_prot(), - MAP_SHARED | MAP_FIXED, + text_mmap_flags, m_image_fd, region->offset(), String::formatted("{}: .text", m_filename).characters()); @@ -287,14 +304,23 @@ void DynamicLoader::load_program_headers() m_text_segment_size = region->required_load_size(); m_text_segment_load_address = VirtualAddress { (FlatPtr)text_segment_begin }; + // Then, we unmap the data segment part of the above combined VM allocation. + auto* data_segment_address = (u8*)text_segment_begin + text_region_ptr->required_load_size(); + if (munmap(data_segment_address, data_region_ptr->required_load_size()) < 0) { + perror("munmap"); + ASSERT_NOT_REACHED(); + } + if (m_elf_image.is_dynamic()) m_dynamic_section_address = dynamic_region_desired_vaddr.offset(m_text_segment_load_address.get()); else m_dynamic_section_address = dynamic_region_desired_vaddr; region = data_region_ptr; + + // Finally, we remap the data segment, this time privately. void* data_segment_begin = mmap_with_name( - (u8*)text_segment_begin + m_text_segment_size, + data_segment_address, region->required_load_size(), region->mmap_prot(), MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, @@ -302,6 +328,7 @@ void DynamicLoader::load_program_headers() 0, String::formatted("{}: .data", m_filename).characters()); if (MAP_FAILED == data_segment_begin) { + perror("mmap data segment"); ASSERT_NOT_REACHED(); } VirtualAddress data_segment_actual_addr;