libnm-glib: fix a crash when using multiple NMClients

NMObjectCache was assuming there would never be more than one object
with the same path, but since NMClient is an NMObject, it was getting
cached too, so if you created two clients and then unreffed one of
them, it's possible the wrong one could get left in the cache, causing
a crash the next time the other one called nm_object_cache_clear().

Fix this by only adding NMObjects to the cache in the codepaths where
we also check to see if the object was already in the cache.

(This also means we can remove the "except" argument to
nm_object_cache_clear(), since the NMClient won't be cached any more.)
This commit is contained in:
Dan Winship
2014-07-31 09:21:05 -04:00
parent 08b91199fb
commit fe264a2d01
4 changed files with 19 additions and 17 deletions

View File

@@ -1383,7 +1383,7 @@ proxy_name_owner_changed (DBusGProxy *proxy,
/* Clear object cache to ensure bad refcounting by clients doesn't /* Clear object cache to ensure bad refcounting by clients doesn't
* keep objects in the cache. * keep objects in the cache.
*/ */
_nm_object_cache_clear (NM_OBJECT (client)); _nm_object_cache_clear ();
} else { } else {
_nm_object_suppress_property_updates (NM_OBJECT (client), FALSE); _nm_object_suppress_property_updates (NM_OBJECT (client), FALSE);
_nm_object_reload_properties_async (NM_OBJECT (client), updated_properties, client); _nm_object_reload_properties_async (NM_OBJECT (client), updated_properties, client);

View File

@@ -63,26 +63,26 @@ _nm_object_cache_get (const char *path)
} }
void void
_nm_object_cache_clear (NMObject *except) _nm_object_cache_clear (void)
{ {
GHashTableIter iter; GHashTableIter iter;
NMObject *obj; GObject *obj;
const char *path; const char *path;
char *foo; char *foo;
_init_cache (); if (!cache)
return;
g_hash_table_iter_init (&iter, cache); g_hash_table_iter_init (&iter, cache);
while (g_hash_table_iter_next (&iter, (gpointer) &path, (gpointer) &obj)) { while (g_hash_table_iter_next (&iter, (gpointer) &path, (gpointer) &obj)) {
if (obj != except) {
/* Remove the callback so that if the object isn't yet released /* Remove the callback so that if the object isn't yet released
* by a client, when it does finally get unrefed, it won't trigger * by a client, when it does finally get unrefed, it won't trigger
* the cache removal for a new object with the same path as the * the cache removal for a new object with the same path as the
* one being released. * one being released.
*/ */
foo = g_object_steal_data (G_OBJECT (obj), "nm-object-cache-tag"); foo = g_object_steal_data (obj, "nm-object-cache-tag");
g_free (foo); g_free (foo);
g_hash_table_iter_remove (&iter); g_hash_table_iter_remove (&iter);
} }
} }
}

View File

@@ -30,7 +30,7 @@ G_BEGIN_DECLS
/* Returns referenced object from the cache */ /* Returns referenced object from the cache */
NMObject *_nm_object_cache_get (const char *path); NMObject *_nm_object_cache_get (const char *path);
void _nm_object_cache_add (NMObject *object); void _nm_object_cache_add (NMObject *object);
void _nm_object_cache_clear (NMObject *except); void _nm_object_cache_clear (void);
G_END_DECLS G_END_DECLS

View File

@@ -159,8 +159,6 @@ constructor (GType type,
return NULL; return NULL;
} }
_nm_object_cache_add (NM_OBJECT (object));
return object; return object;
} }
@@ -573,6 +571,8 @@ _nm_object_create (GType type, DBusGConnection *connection, const char *path)
NM_OBJECT_DBUS_CONNECTION, connection, NM_OBJECT_DBUS_CONNECTION, connection,
NM_OBJECT_DBUS_PATH, path, NM_OBJECT_DBUS_PATH, path,
NULL); NULL);
if (NM_IS_OBJECT (object))
_nm_object_cache_add (NM_OBJECT (object));
if (!g_initable_init (G_INITABLE (object), NULL, &error)) { if (!g_initable_init (G_INITABLE (object), NULL, &error)) {
dbgmsg ("Could not create object for %s: %s", path, error->message); dbgmsg ("Could not create object for %s: %s", path, error->message);
g_error_free (error); g_error_free (error);
@@ -656,6 +656,8 @@ async_got_type (GType type, gpointer user_data)
NM_OBJECT_DBUS_PATH, async_data->path, NM_OBJECT_DBUS_PATH, async_data->path,
NULL); NULL);
g_warn_if_fail (object != NULL); g_warn_if_fail (object != NULL);
if (NM_IS_OBJECT (object))
_nm_object_cache_add (NM_OBJECT (object));
g_async_initable_init_async (G_ASYNC_INITABLE (object), G_PRIORITY_DEFAULT, g_async_initable_init_async (G_ASYNC_INITABLE (object), G_PRIORITY_DEFAULT,
NULL, async_inited, async_data); NULL, async_inited, async_data);
} }