From 27a9cc2e63d07989fdb4efeeb6a6b3417281f177 Mon Sep 17 00:00:00 2001
From: Jens Schmer <jens.schmer+git@gmail.com>
Date: Wed, 12 Dec 2018 20:07:14 +0100
Subject: [PATCH] Fix Service object leak on emulation stop

Services created with the ServiceFramework base class install themselves as HleHandlers with an owning shared_ptr in the ServerPort ServiceFrameworkBase::port member variable, creating a cyclic ownership between ServiceFrameworkBase and the ServerPort, preventing deletion of the service objects.

Fix that by removing the ServiceFrameworkBase::port member because that was only used to detect multiple attempts at installing a port. Instead store a flag if the port was already installed to achieve the same functionality.
---
 src/core/hle/service/service.cpp | 14 +++++++++-----
 src/core/hle/service/service.h   |  8 +++-----
 2 files changed, 12 insertions(+), 10 deletions(-)

diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index d41df37325..d25b80ab09 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -97,29 +97,33 @@ ServiceFrameworkBase::ServiceFrameworkBase(const char* service_name, u32 max_ses
 ServiceFrameworkBase::~ServiceFrameworkBase() = default;
 
 void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager) {
-    ASSERT(port == nullptr);
-    port = service_manager.RegisterService(service_name, max_sessions).Unwrap();
+    ASSERT(!port_installed);
+
+    auto port = service_manager.RegisterService(service_name, max_sessions).Unwrap();
     port->SetHleHandler(shared_from_this());
+    port_installed = true;
 }
 
 void ServiceFrameworkBase::InstallAsNamedPort() {
-    ASSERT(port == nullptr);
+    ASSERT(!port_installed);
 
     auto& kernel = Core::System::GetInstance().Kernel();
     auto [server_port, client_port] =
         Kernel::ServerPort::CreatePortPair(kernel, max_sessions, service_name);
     server_port->SetHleHandler(shared_from_this());
     kernel.AddNamedPort(service_name, std::move(client_port));
+    port_installed = true;
 }
 
 Kernel::SharedPtr<Kernel::ClientPort> ServiceFrameworkBase::CreatePort() {
-    ASSERT(port == nullptr);
+    ASSERT(!port_installed);
 
     auto& kernel = Core::System::GetInstance().Kernel();
     auto [server_port, client_port] =
         Kernel::ServerPort::CreatePortPair(kernel, max_sessions, service_name);
-    port = MakeResult(std::move(server_port)).Unwrap();
+    auto port = MakeResult(std::move(server_port)).Unwrap();
     port->SetHleHandler(shared_from_this());
+    port_installed = true;
     return client_port;
 }
 
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index 98483ecf16..0295336287 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -96,11 +96,9 @@ private:
     /// Maximum number of concurrent sessions that this service can handle.
     u32 max_sessions;
 
-    /**
-     * Port where incoming connections will be received. Only created when InstallAsService() or
-     * InstallAsNamedPort() are called.
-     */
-    Kernel::SharedPtr<Kernel::ServerPort> port;
+    /// Flag to store if a port was already create/installed to detect multiple install attempts,
+    /// which is not supported.
+    bool port_installed = false;
 
     /// Function used to safely up-cast pointers to the derived class before invoking a handler.
     InvokerFn* handler_invoker;
-- 
GitLab