From 9591971b31713cea01040d9dc417f92584dd4d55 Mon Sep 17 00:00:00 2001
From: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
Date: Thu, 22 Nov 2018 15:29:45 -0500
Subject: [PATCH] fix deadlock when registering SIP accounts

This patch fixes a double lock on the same thread during
doRegister whilst protecting configuration data and also
implements the same mechanism for ringaccount.

Change-Id: I0d5dfd7e6285f85042152092d4ff0d50cd89e5db
---
 src/ringdht/ringaccount.cpp | 18 ++++++++++------
 src/sip/sipaccount.cpp      | 43 ++++++++++++++++++-------------------
 src/sip/sipaccount.h        | 13 +++++++++++
 3 files changed, 45 insertions(+), 29 deletions(-)

diff --git a/src/ringdht/ringaccount.cpp b/src/ringdht/ringaccount.cpp
index f764543885..348cd2c546 100644
--- a/src/ringdht/ringaccount.cpp
+++ b/src/ringdht/ringaccount.cpp
@@ -1875,6 +1875,7 @@ RingAccount::mapPortUPnP()
 void
 RingAccount::doRegister()
 {
+    std::unique_lock<std::mutex> lock(configurationMutex_);
     if (not isUsable()) {
         RING_WARN("Account must be enabled and active to register, ignoring");
         return;
@@ -1893,17 +1894,20 @@ RingAccount::doRegister()
 
     /* if UPnP is enabled, then wait for IGD to complete registration */
     if (upnp_) {
-        auto shared = shared_from_this();
         RING_DBG("UPnP: waiting for IGD to register RING account");
+        lock.unlock();
         setRegistrationState(RegistrationState::TRYING);
-        std::thread{ [shared] {
-            auto this_ = std::static_pointer_cast<RingAccount>(shared).get();
-            if ( not this_->mapPortUPnP())
-                RING_WARN("UPnP: Could not successfully map DHT port with UPnP, continuing with account registration anyways.");
-            this_->doRegister_();
+        std::thread{ [w=weak()] {
+            if (auto acc = w.lock()) {
+                if (not acc->mapPortUPnP())
+                    RING_WARN("UPnP: Could not successfully map DHT port with UPnP, continuing with account registration anyways.");
+                acc->doRegister_();
+            }
         }}.detach();
-    } else
+    } else {
+        lock.unlock();
         doRegister_();
+    }
 }
 
 
diff --git a/src/sip/sipaccount.cpp b/src/sip/sipaccount.cpp
index 0bc395865d..f405a82067 100644
--- a/src/sip/sipaccount.cpp
+++ b/src/sip/sipaccount.cpp
@@ -697,10 +697,9 @@ bool SIPAccount::mapPortUPnP()
         }
     }
 
-    std::weak_ptr<SIPAccount> w = std::static_pointer_cast<SIPAccount>(shared_from_this());
-    upnp_->setIGDListener([w] {
-        if (auto shared = w.lock())
-            shared->doRegister();
+    upnp_->setIGDListener([w=weak()] {
+        if (auto acc = w.lock())
+            acc->doRegister();
     });
 
     return added;
@@ -708,8 +707,7 @@ bool SIPAccount::mapPortUPnP()
 
 void SIPAccount::doRegister()
 {
-    std::lock_guard<std::mutex> lock(configurationMutex_);
-
+    std::unique_lock<std::mutex> lock(configurationMutex_);
     if (not isUsable()) {
         RING_WARN("Account must be enabled and active to register, ignoring");
         return;
@@ -720,17 +718,20 @@ void SIPAccount::doRegister()
     /* if UPnP is enabled, then wait for IGD to complete registration */
     if (upnp_) {
         RING_DBG("UPnP: waiting for IGD to register SIP account");
+        lock.unlock();
         setRegistrationState(RegistrationState::TRYING);
-        auto shared = shared_from_this();
-        std::thread{ [shared] {
-            sip_utils::register_thread();
-            auto this_ = std::static_pointer_cast<SIPAccount>(shared).get();
-            if ( not this_->mapPortUPnP())
-                RING_WARN("UPnP: Could not successfully map SIP port with UPnP, continuing with account registration anyways.");
-            this_->doRegister1_();
+        std::thread{ [w=weak()] {
+            if (auto acc = w.lock()) {
+                sip_utils::register_thread();
+                if (not acc->mapPortUPnP())
+                    RING_WARN("UPnP: Could not successfully map SIP port with UPnP, continuing with account registration anyways.");
+                acc->doRegister1_();
+            }
         }}.detach();
-    } else
+    } else {
+        lock.unlock();
         doRegister1_();
+    }
 }
 
 void SIPAccount::doRegister1_()
@@ -743,12 +744,11 @@ void SIPAccount::doRegister1_()
         }
     }
 
-    std::weak_ptr<SIPAccount> weak_acc = std::static_pointer_cast<SIPAccount>(shared_from_this());
     link_->resolveSrvName(
         hostname_,
         tlsEnable_ ? PJSIP_TRANSPORT_TLS : PJSIP_TRANSPORT_UDP,
-        [weak_acc](std::vector<IpAddr> host_ips) {
-            if (auto acc = weak_acc.lock()) {
+        [w = weak()](std::vector<IpAddr> host_ips) {
+            if (auto acc = w.lock()) {
                 std::lock_guard<std::mutex> lock(acc->configurationMutex_);
                 if (host_ips.empty()) {
                     RING_ERR("Can't resolve hostname for registration.");
@@ -884,10 +884,9 @@ SIPAccount::connectivityChanged()
         return;
     }
 
-    auto shared = std::static_pointer_cast<SIPAccount>(shared_from_this());
-    doUnregister([shared](bool /* transport_free */) {
-        if (shared->isUsable())
-            shared->doRegister();
+    doUnregister([acc=shared()](bool /* transport_free */) {
+        if (acc->isUsable())
+            acc->doRegister();
     });
 }
 
@@ -2101,7 +2100,7 @@ SIPAccount::sendTextMessage(const std::string& to, const std::map<std::string, s
         uint64_t id;
     };
     ctx* t = new ctx;
-    t->acc = std::static_pointer_cast<SIPAccount>(shared_from_this());
+    t->acc = shared();
     t->id = id;
 
     status = pjsip_endpt_send_request(link_->getEndpoint(), tdata, -1, t, [](void *token, pjsip_event *e) {
diff --git a/src/sip/sipaccount.h b/src/sip/sipaccount.h
index 7085eb18ed..efeab5946e 100644
--- a/src/sip/sipaccount.h
+++ b/src/sip/sipaccount.h
@@ -70,6 +70,19 @@ class SIPAccount : public SIPAccountBase {
     public:
         constexpr static const char * const ACCOUNT_TYPE = "SIP";
 
+        std::shared_ptr<SIPAccount> shared() {
+            return std::static_pointer_cast<SIPAccount>(shared_from_this());
+        }
+        std::shared_ptr<SIPAccount const> shared() const {
+            return std::static_pointer_cast<SIPAccount const>(shared_from_this());
+        }
+        std::weak_ptr<SIPAccount> weak() {
+            return std::static_pointer_cast<SIPAccount>(shared_from_this());
+        }
+        std::weak_ptr<SIPAccount const> weak() const {
+            return std::static_pointer_cast<SIPAccount const>(shared_from_this());
+        }
+
         /**
          * Constructor
          * @param accountID The account identifier
-- 
GitLab