diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp index e99682714a..278e2f7b58 100644 --- a/Kernel/Process.cpp +++ b/Kernel/Process.cpp @@ -151,7 +151,9 @@ ErrorOr> Process::try_create_user_process(RefPtr& setup_description(1); setup_description(2); - if (auto result = process->exec(move(path_string), move(arguments), move(environment)); result.is_error()) { + Thread* new_main_thread = nullptr; + u32 prev_flags = 0; + if (auto result = process->exec(move(path_string), move(arguments), move(environment), new_main_thread, prev_flags); result.is_error()) { dbgln("Failed to exec {}: {}", path, result.error()); first_thread = nullptr; return result.release_error(); diff --git a/Kernel/Process.h b/Kernel/Process.h index 090a7d9da9..a18398824c 100644 --- a/Kernel/Process.h +++ b/Kernel/Process.h @@ -438,7 +438,7 @@ public: NonnullOwnPtrVector const& arguments() const { return m_arguments; }; NonnullOwnPtrVector const& environment() const { return m_environment; }; - ErrorOr exec(NonnullOwnPtr path, NonnullOwnPtrVector arguments, NonnullOwnPtrVector environment, int recursion_depth = 0); + ErrorOr exec(NonnullOwnPtr path, NonnullOwnPtrVector arguments, NonnullOwnPtrVector environment, Thread*& new_main_thread, u32& prev_flags, int recursion_depth = 0); ErrorOr load(NonnullRefPtr main_program_description, RefPtr interpreter_description, const ElfW(Ehdr) & main_program_header); diff --git a/Kernel/Syscalls/execve.cpp b/Kernel/Syscalls/execve.cpp index 04b9d29d61..5446ec49c2 100644 --- a/Kernel/Syscalls/execve.cpp +++ b/Kernel/Syscalls/execve.cpp @@ -777,7 +777,7 @@ ErrorOr> Process::find_elf_interpreter_for_executabl return nullptr; } -ErrorOr Process::exec(NonnullOwnPtr path, NonnullOwnPtrVector arguments, NonnullOwnPtrVector environment, int recursion_depth) +ErrorOr Process::exec(NonnullOwnPtr path, NonnullOwnPtrVector arguments, NonnullOwnPtrVector environment, Thread*& new_main_thread, u32& prev_flags, int recursion_depth) { if (recursion_depth > 2) { dbgln("exec({}): SHENANIGANS! recursed too far trying to find #! interpreter", path); @@ -815,7 +815,7 @@ ErrorOr Process::exec(NonnullOwnPtr path, NonnullOwnPtrVector Process::exec(NonnullOwnPtr path, NonnullOwnPtrVectorview(), *main_program_header, nread, metadata.size)); - TRY(do_exec(move(description), move(arguments), move(environment), move(interpreter_description), new_main_thread, prev_flags, *main_program_header)); - - VERIFY_INTERRUPTS_DISABLED(); - VERIFY(Processor::in_critical()); - - auto* current_thread = Thread::current(); - if (current_thread == new_main_thread) { - { - // Make sure that `path` gets deleted before we teleport into the new process. - // If we don't do this, it will leak (since we never return from this function.) - OwnPtr path_deleter = move(path); - } - - // We need to enter the scheduler lock before changing the state - // and it will be released after the context switch into that - // thread. We should also still be in our critical section - VERIFY(!g_scheduler_lock.is_locked_by_current_processor()); - VERIFY(Processor::in_critical() == 1); - g_scheduler_lock.lock(); - current_thread->set_state(Thread::State::Running); - Processor::assume_context(*current_thread, prev_flags); - VERIFY_NOT_REACHED(); - } - - // NOTE: This code path is taken in the non-syscall case, i.e when the kernel spawns - // a userspace process directly (such as /bin/SystemServer on startup) - - if (prev_flags & 0x200) - sti(); - Processor::leave_critical(); - return {}; + return do_exec(move(description), move(arguments), move(environment), move(interpreter_description), new_main_thread, prev_flags, *main_program_header); } ErrorOr Process::sys$execve(Userspace user_params) @@ -873,7 +838,7 @@ ErrorOr Process::sys$execve(Userspace VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this); TRY(require_promise(Pledge::exec)); - // NOTE: Be extremely careful with allocating any kernel memory in exec(). + // NOTE: Be extremely careful with allocating any kernel memory in this function. // On success, the kernel stack will be lost. auto params = TRY(copy_typed_from_user(user_params)); @@ -905,9 +870,37 @@ ErrorOr Process::sys$execve(Userspace NonnullOwnPtrVector environment; TRY(copy_user_strings(params.environment, environment)); - TRY(exec(move(path), move(arguments), move(environment))); - // We should never continue after a successful exec! - VERIFY_NOT_REACHED(); + Thread* new_main_thread = nullptr; + u32 prev_flags = 0; + TRY(exec(move(path), move(arguments), move(environment), new_main_thread, prev_flags)); + + // NOTE: If we're here, the exec has succeeded and we've got a new executable image! + // We will not return normally from this function. Instead, the next time we + // get scheduled, it'll be at the entry point of the new executable. + + VERIFY_INTERRUPTS_DISABLED(); + VERIFY(Processor::in_critical()); + + auto* current_thread = Thread::current(); + if (current_thread == new_main_thread) { + // We need to enter the scheduler lock before changing the state + // and it will be released after the context switch into that + // thread. We should also still be in our critical section + VERIFY(!g_scheduler_lock.is_locked_by_current_processor()); + VERIFY(Processor::in_critical() == 1); + g_scheduler_lock.lock(); + current_thread->set_state(Thread::State::Running); + Processor::assume_context(*current_thread, prev_flags); + VERIFY_NOT_REACHED(); + } + + // NOTE: This code path is taken in the non-syscall case, i.e when the kernel spawns + // a userspace process directly (such as /bin/SystemServer on startup) + + if (prev_flags & 0x200) + sti(); + Processor::leave_critical(); + return 0; } }