summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Williams <dcbw@redhat.com>2010-04-14 15:16:48 -0700
committerDan Williams <dcbw@redhat.com>2010-04-14 15:24:20 -0700
commitf4067e6204fa5ac87269949e8572b329cf20383c (patch)
tree56dd750a9ecd65875519ab1fb8e6c6bddeb409e4
parent6e1051c66f2bf9c20dfc85ab0fd6efd04826cc1f (diff)
wifi: prevent infinite loop when disposing of supplicant proxy (rh #538717)
This has been around a long time, but is very hard to trigger. It appears to happen mostly if the supplicant segfaults on resume but has been seen in other cases as well. For whatever reason, the DBusGProxy's refcount reaches 0 and the proxy gets disposed of. That in turn disposes of all the pending calls that are in-progress on the proxy. Since we give the pending calls a closure, that closure (nm_supplicant_info_destroy) gets called when the pending calls are destroyed. That closure unrefs the proxy again. Since DBusGProxy doesn't have any protection in its dispose() handler against re-entrant disposes (which is arguably a bug of the client) we end up infinite looping in nm_supplicant_info_destroy(). Fix that by ensuring we return early if we detect that we are already freeing the NMSupplicantInfo object, and thus don't try to dispose of the proxy yet again.
-rw-r--r--src/supplicant-manager/nm-supplicant-interface.c32
1 files changed, 23 insertions, 9 deletions
diff --git a/src/supplicant-manager/nm-supplicant-interface.c b/src/supplicant-manager/nm-supplicant-interface.c
index 00f0c307ee..6fa2ee5c31 100644
--- a/src/supplicant-manager/nm-supplicant-interface.c
+++ b/src/supplicant-manager/nm-supplicant-interface.c
@@ -144,6 +144,7 @@ typedef struct {
DBusGProxy *proxy;
NMCallStore *store;
DBusGProxyCall *call;
+ gboolean disposing;
} NMSupplicantInfo;
static NMSupplicantInfo *
@@ -164,10 +165,11 @@ nm_supplicant_info_new (NMSupplicantInterface *interface,
static void
nm_supplicant_info_set_call (NMSupplicantInfo *info, DBusGProxyCall *call)
{
- if (call) {
- nm_call_store_add (info->store, G_OBJECT (info->proxy), (gpointer) call);
- info->call = call;
- }
+ g_return_if_fail (info != NULL);
+ g_return_if_fail (call != NULL);
+
+ nm_call_store_add (info->store, G_OBJECT (info->proxy), (gpointer) call);
+ info->call = call;
}
static void
@@ -175,13 +177,25 @@ nm_supplicant_info_destroy (gpointer user_data)
{
NMSupplicantInfo *info = (NMSupplicantInfo *) user_data;
- if (info->call)
- nm_call_store_remove (info->store, G_OBJECT (info->proxy), info->call);
+ /* Guard against double-disposal; since DBusGProxy doesn't guard against
+ * double-disposal, we could infinite loop here if we're in the middle of
+ * some wpa_supplicant D-Bus calls. When the supplicant dies we'll dispose
+ * of the proxy, which kills all its pending calls, which brings us here.
+ * Then when we unref the proxy here, its dispose() function will get called
+ * again, and we get right back here until we segfault because our callstack
+ * is too long.
+ */
+ if (!info->disposing) {
+ info->disposing = TRUE;
- g_object_unref (info->proxy);
- g_object_unref (info->interface);
+ if (info->call)
+ nm_call_store_remove (info->store, G_OBJECT (info->proxy), info->call);
- g_slice_free (NMSupplicantInfo, info);
+ g_object_unref (info->proxy);
+ g_object_unref (info->interface);
+
+ g_slice_free (NMSupplicantInfo, info);
+ }
}