Blob Blame History Raw
From ae4a03a2c6928d0d3c8fbcdba4831ce1d212f50b Mon Sep 17 00:00:00 2001
From: Debarshi Ray <debarshir@freedesktop.org>
Date: Tue, 20 Jan 2015 13:59:33 +0100
Subject: [PATCH] [UPnP] Disconnect signal handlers during destruction

A GUPnPContextManager can outlive a dlr_upnp_t because it might be
using asynchronous operations during its construction (eg.,
GUPnPNetworkManager) which retain references to it. This can be
demonstrated if the service is spawned as a result of the following
command:
$ gdbus call \
    --session \
    --dest com.intel.dleyna-renderer \
    --object-path /com/intel/dLeynaRenderer \
    --method com.intel.dLeynaRenderer.Manager.Release

This leads to the signal handlers being invoked with an invalid
dlr_upnp_t and the outcome is a crash.

To avoid this, we should disconnect the callbacks listening to the
context manager and the control points belonging to it.
---
 libdleyna/renderer/upnp.c | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/libdleyna/renderer/upnp.c b/libdleyna/renderer/upnp.c
index fefc340..cce4f4f 100644
--- a/libdleyna/renderer/upnp.c
+++ b/libdleyna/renderer/upnp.c
@@ -45,6 +45,7 @@ struct dlr_upnp_t_ {
 	void *user_data;
 	GHashTable *server_udn_map;
 	GHashTable *server_uc_map;
+	GList *cps;
 	guint counter;
 	dlr_host_service_t *host_service;
 };
@@ -357,6 +358,7 @@ static void prv_on_context_available(GUPnPContextManager *context_manager,
 
 	gssdp_resource_browser_set_active(GSSDP_RESOURCE_BROWSER(cp), TRUE);
 	gupnp_context_manager_manage_control_point(upnp->context_manager, cp);
+	upnp->cps = g_list_prepend (upnp->cps, g_object_ref (cp));
 	g_object_unref(cp);
 }
 
@@ -393,10 +395,28 @@ dlr_upnp_t *dlr_upnp_new(dleyna_connector_id_t connection,
 void dlr_upnp_delete(dlr_upnp_t *upnp)
 {
 	if (upnp) {
+		GList *l;
+
+		for (l = upnp->cps; l != NULL; l = l->next) {
+			GUPnPControlPoint *cp = GUPNP_CONTROL_POINT (l->data);
+
+			g_signal_handlers_disconnect_by_func (cp,
+							      prv_server_available_cb,
+							      upnp);
+			g_signal_handlers_disconnect_by_func (cp,
+							      prv_server_unavailable_cb,
+							      upnp);
+		}
+
+		g_signal_handlers_disconnect_by_func (upnp->context_manager,
+						      prv_on_context_available,
+						      upnp);
+
 		dlr_host_service_delete(upnp->host_service);
 		g_object_unref(upnp->context_manager);
 		g_hash_table_unref(upnp->server_udn_map);
 		g_hash_table_unref(upnp->server_uc_map);
+		g_list_free_full (upnp->cps, g_object_unref);
 
 		g_free(upnp);
 	}
-- 
2.1.0