From db11cfa2c573536f1df0c04b5a0a7166f2de26e8 Mon Sep 17 00:00:00 2001 From: Itamar Date: Sat, 16 Jul 2022 17:44:25 +0300 Subject: [PATCH] Utilities+LibELF: Temporary promises for dynamic linker in "pledge" This adds a "temporary promises for the dynamic-linker" flag ('-d') to the "pledge" utility. Example usage: pledge -d -p "stdio rpath" id Without the '-d' flag, id would crash because the dynamic linker requires 'prot_exec'. When this flag is used and the program to be run is dynamically linked, "pledge" adds promises that are required by the dynamic linker to the promise set provided by the user. The dynamic linker will later "give up" the pledge promises it no longer requires. --- Userland/Libraries/LibELF/DynamicLinker.cpp | 36 +++++++++++++++++++++ Userland/Utilities/pledge.cpp | 25 +++++++++++++- 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/Userland/Libraries/LibELF/DynamicLinker.cpp b/Userland/Libraries/LibELF/DynamicLinker.cpp index 174781592d..6df64da6a8 100644 --- a/Userland/Libraries/LibELF/DynamicLinker.cpp +++ b/Userland/Libraries/LibELF/DynamicLinker.cpp @@ -55,6 +55,8 @@ static __pthread_mutex_t s_loader_lock = __PTHREAD_MUTEX_INITIALIZER; static bool s_allowed_to_check_environment_variables { false }; static bool s_do_breakpoint_trap_before_entry { false }; static StringView s_ld_library_path; +static StringView s_main_program_pledge_promises; +static String s_loader_pledge_promises; static Result __dlclose(void* handle); static Result __dlopen(char const* filename, int flags); @@ -339,6 +341,25 @@ static NonnullRefPtrVector collect_loaders_for_library(String con return loaders; } +static void drop_loader_promise(StringView promise_to_drop) +{ + if (s_main_program_pledge_promises.is_empty() || s_loader_pledge_promises.is_empty()) + return; + + s_loader_pledge_promises = s_loader_pledge_promises.replace(promise_to_drop, ""sv, ReplaceMode::All); + + auto extended_promises = String::formatted("{} {}", s_main_program_pledge_promises, s_loader_pledge_promises); + Syscall::SC_pledge_params params { + { extended_promises.characters(), extended_promises.length() }, + { nullptr, 0 }, + }; + int rc = syscall(SC_pledge, ¶ms); + if (rc < 0 && rc > -EMAXERRNO) { + warnln("Failed to drop loader pledge promise: {}. errno={}", promise_to_drop, errno); + _exit(1); + } +} + static Result link_main_library(String const& name, int flags) { auto loaders = collect_loaders_for_library(name); @@ -375,6 +396,8 @@ static Result link_main_library(String const& name, int fl } } + drop_loader_promise("prot_exec"sv); + for (auto& loader : loaders) { loader.load_stage_4(); } @@ -562,6 +585,16 @@ static void read_environment_variables() if (env_string.starts_with(library_path_string)) { s_ld_library_path = env_string.substring_view(library_path_string.length()); } + + constexpr auto main_pledge_promises_key = "_LOADER_MAIN_PROGRAM_PLEDGE_PROMISES="sv; + if (env_string.starts_with(main_pledge_promises_key)) { + s_main_program_pledge_promises = env_string.substring_view(main_pledge_promises_key.length()); + } + + constexpr auto loader_pledge_promises_key = "_LOADER_PLEDGE_PROMISES="sv; + if (env_string.starts_with(loader_pledge_promises_key)) { + s_loader_pledge_promises = env_string.substring_view(loader_pledge_promises_key.length()); + } } } @@ -608,6 +641,9 @@ void ELF::DynamicLinker::linker_main(String&& main_program_name, int main_progra warnln("{}", result.error().text); _exit(1); } + + drop_loader_promise("rpath"sv); + auto& main_executable_loader = *s_loaders.get(library_name); auto entry_point = main_executable_loader->image().entry(); if (main_executable_loader->is_dynamic()) diff --git a/Userland/Utilities/pledge.cpp b/Userland/Utilities/pledge.cpp index d54892dbab..5d4b5e4653 100644 --- a/Userland/Utilities/pledge.cpp +++ b/Userland/Utilities/pledge.cpp @@ -5,19 +5,42 @@ */ #include +#include #include +#include #include +static ErrorOr is_dynamically_linked_executable(StringView filename) +{ + String exec_filename = filename; + if (!filename.contains('/')) { + exec_filename = TRY(Core::System::find_file_in_path(filename)); + } + + auto file = TRY(Core::MappedFile::map(exec_filename)); + ELF::Image elf_image(file->bytes()); + return elf_image.is_dynamic(); +} + ErrorOr serenity_main(Main::Arguments arguments) { - StringView promises; + String promises; Vector command; + bool add_promises_for_dynamic_linker; Core::ArgsParser args_parser; args_parser.add_option(promises, "Space-separated list of pledge promises", "promises", 'p', "promises"); + args_parser.add_option(add_promises_for_dynamic_linker, "Add temporary promises for dynamic linker", "dynamic-linker-promises", 'd'); args_parser.add_positional_argument(command, "Command to execute", "command"); args_parser.parse(arguments); + if (add_promises_for_dynamic_linker && TRY(is_dynamically_linked_executable(command[0]))) { + auto constexpr loader_promises = "stdio rpath prot_exec"sv; + MUST(Core::System::setenv("_LOADER_PLEDGE_PROMISES"sv, loader_promises, true)); + MUST(Core::System::setenv("_LOADER_MAIN_PROGRAM_PLEDGE_PROMISES"sv, promises, true)); + promises = String::formatted("{} {}", promises, loader_promises); + } + TRY(Core::System::pledge(StringView(), promises)); TRY(Core::System::exec(command[0], command.span(), Core::System::SearchInPath::Yes)); return 0;