From b6ee84794737608865457f3f3af3f9947770731b Mon Sep 17 00:00:00 2001 From: xbzk Date: Mon, 29 Jun 2026 02:39:11 +0200 Subject: [PATCH] [hle/am] make Service::Process move-only to fix #3908 KProcess use-after-free (#4137) #3908 changed process_creation.*, CreateProcess/CreateApplicationProcess, to return std::optional instead of std::unique_ptr, so the AM sites now transfer a Process by value via make_unique(*std::move(opt)). The consequence: Process owns a refcounted KProcess* but its user-declared dtor suppressed the implicit move ctor, so that "move" silently shallow-copied and the temporary's dtor Close()/RemoveProcess()'d the shared handle -> use-after-free. It's seems to be user end based, so whether it crashes may depend on machine, compiler, allocator reuse, refcount slack, and the AM event-observer thread race, idk. It reliably crashed my MSVC build at launching games (cstack: ProcessHolder -> MultiWait -> KSynchronizationObject::Wait -> null) multiple times. Fix: give Process a move ctor that steals the handle (nulling the source so the moved-from dtor is a no-op) and delete copy/move-assign, making the optional<->unique_ptr transfer safe. Bonus: explicited delete for the 3 kinds of assignment: copy ctor (the one used in eden), copy assign and move assign (currently unused) to force compile error if they ever come to use. Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4137 Reviewed-by: Lizzie Reviewed-by: MaranBr --- src/core/hle/service/os/process.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/core/hle/service/os/process.h b/src/core/hle/service/os/process.h index ca10945f84..103ba7aa6c 100644 --- a/src/core/hle/service/os/process.h +++ b/src/core/hle/service/os/process.h @@ -6,6 +6,8 @@ #pragma once +#include + #include "common/common_types.h" namespace Core { @@ -28,6 +30,15 @@ public: inline explicit Process(Core::System& system) noexcept : m_system(system) {} inline ~Process() { this->Finalize(); } + Process(const Process&) = delete; + Process& operator=(const Process&) = delete; + Process& operator=(Process&&) = delete; + inline Process(Process&& other) noexcept + : m_system(other.m_system), m_process(std::exchange(other.m_process, nullptr)), + m_main_thread_stack_size(std::exchange(other.m_main_thread_stack_size, 0)), + m_main_thread_priority(std::exchange(other.m_main_thread_priority, 0)), + m_process_started(std::exchange(other.m_process_started, false)) {} + bool Initialize(Loader::AppLoader& loader, Loader::ResultStatus& out_load_result); void Finalize();