diff --git a/Kernel/API/prctl_numbers.h b/Kernel/API/prctl_numbers.h index 1165712562b..b9641671198 100644 --- a/Kernel/API/prctl_numbers.h +++ b/Kernel/API/prctl_numbers.h @@ -15,3 +15,4 @@ #define PR_GET_PROCESS_NAME 7 #define PR_SET_THREAD_NAME 8 #define PR_GET_THREAD_NAME 9 +#define PR_SET_NO_TRANSITION_TO_EXECUTABLE_FROM_WRITABLE_PROT 10 diff --git a/Kernel/Syscalls/mmap.cpp b/Kernel/Syscalls/mmap.cpp index eb07ad386d8..d9b52189679 100644 --- a/Kernel/Syscalls/mmap.cpp +++ b/Kernel/Syscalls/mmap.cpp @@ -28,55 +28,8 @@ namespace Kernel { -static bool should_make_executable_exception_for_dynamic_loader(bool make_readable, bool make_writable, bool make_executable, Memory::Region const& region) -{ - // Normally we don't allow W -> X transitions, but we have to make an exception - // for the dynamic loader, which needs to do this after performing text relocations. - - // FIXME: Investigate whether we could get rid of all text relocations entirely. - - // The exception is only made if all the following criteria is fulfilled: - - // The region must be RW - if (!(region.is_readable() && region.is_writable() && !region.is_executable())) - return false; - - // The region wants to become RX - if (!(make_readable && !make_writable && make_executable)) - return false; - - // The region is backed by a file - if (!region.vmobject().is_inode()) - return false; - - // The file mapping is private, not shared (no relocations in a shared mapping!) - if (!region.vmobject().is_private_inode()) - return false; - - auto const& inode_vm = static_cast(region.vmobject()); - auto const& inode = inode_vm.inode(); - - Elf_Ehdr header; - auto buffer = UserOrKernelBuffer::for_kernel_buffer((u8*)&header); - auto result = inode.read_bytes(0, sizeof(header), buffer, nullptr); - if (result.is_error() || result.value() != sizeof(header)) - return false; - - // The file is a valid ELF binary - if (!ELF::validate_elf_header(header, inode.size())) - return false; - - // The file is an ELF shared object - if (header.e_type != ET_DYN) - return false; - - // FIXME: Are there any additional checks/validations we could do here? - return true; -} - ErrorOr Process::validate_mmap_prot(int prot, bool map_stack, bool map_anonymous, Memory::Region const* region) const { - bool make_readable = prot & PROT_READ; bool make_writable = prot & PROT_WRITE; bool make_executable = prot & PROT_EXEC; @@ -96,13 +49,8 @@ ErrorOr Process::validate_mmap_prot(int prot, bool map_stack, bool map_ano if (make_writable && region->has_been_executable()) return EINVAL; - if (make_executable && region->has_been_writable()) { - if (should_make_executable_exception_for_dynamic_loader(make_readable, make_writable, make_executable, *region)) { - return {}; - } else { - return EINVAL; - }; - } + if (make_executable && region->has_been_writable() && should_reject_transition_to_executable_from_writable_prot()) + return EINVAL; } return {}; diff --git a/Kernel/Syscalls/prctl.cpp b/Kernel/Syscalls/prctl.cpp index 6d6c1224e37..0a4ddc93a47 100644 --- a/Kernel/Syscalls/prctl.cpp +++ b/Kernel/Syscalls/prctl.cpp @@ -92,6 +92,13 @@ ErrorOr Process::sys$prctl(int option, FlatPtr arg1, FlatPtr arg2, Flat })); return 0; } + case PR_SET_NO_TRANSITION_TO_EXECUTABLE_FROM_WRITABLE_PROT: { + TRY(require_promise(Pledge::prot_exec)); + with_mutable_protected_data([](auto& protected_data) { + return protected_data.reject_transition_to_executable_from_writable_prot.set(); + }); + return 0; + } } return EINVAL; diff --git a/Kernel/Tasks/Process.h b/Kernel/Tasks/Process.h index 7edbc79f13d..f83b429b664 100644 --- a/Kernel/Tasks/Process.h +++ b/Kernel/Tasks/Process.h @@ -143,6 +143,7 @@ class Process final Atomic thread_count { 0 }; u8 termination_status { 0 }; u8 termination_signal { 0 }; + SetOnce reject_transition_to_executable_from_writable_prot; }; public: @@ -624,6 +625,13 @@ public: ErrorOr require_promise(Pledge); ErrorOr require_no_promises() const; + bool should_reject_transition_to_executable_from_writable_prot() const + { + return with_protected_data([](auto& protected_data) { + return protected_data.reject_transition_to_executable_from_writable_prot.was_set(); + }); + } + ErrorOr validate_mmap_prot(int prot, bool map_stack, bool map_anonymous, Memory::Region const* region = nullptr) const; ErrorOr validate_inode_mmap_prot(int prot, bool description_readable, bool description_writable, bool map_shared) const; diff --git a/Userland/Libraries/LibELF/DynamicLinker.cpp b/Userland/Libraries/LibELF/DynamicLinker.cpp index 679a9b1401f..5ed91404f1e 100644 --- a/Userland/Libraries/LibELF/DynamicLinker.cpp +++ b/Userland/Libraries/LibELF/DynamicLinker.cpp @@ -744,6 +744,11 @@ Examples of static-pie ELF objects are ELF packers, and the system dynamic loade VERIFY_NOT_REACHED(); } + rc = syscall(SC_prctl, PR_SET_NO_TRANSITION_TO_EXECUTABLE_FROM_WRITABLE_PROT, 0, 0, nullptr); + if (rc < 0) { + VERIFY_NOT_REACHED(); + } + dbgln_if(DYNAMIC_LOAD_DEBUG, "Jumping to entry point: {:p}", entry_point_function); if (s_do_breakpoint_trap_before_entry) { #if ARCH(AARCH64)