ifnet: fix use-after-free and refcounting of invalid changed connections

If a valid connection was updated and still valid, and then was
updated and become invalid, the connection would not be properly
removed from the ifnet plugin's priv->connections hash, and thus
would never be disposed.

This was due to using the direct pointer to the connection's UUID
as the key for the hash table.  When a connection is updated and
its settings are replaced, the old UUID is freed and replaced with
a new pointer.  But the ifnet plugin hash table still uses the
old (now freed) UUID pointer as the key.  Thus when the connection
is updated and becomes invalid, looking up the UUID in the hash
table fails to find the connection, and the connection is not
removed from the hash.

This bug could cause a crash in some cases, if two keys of the
GHashTable hashed to the same value, in which case GLib would
call g_str_equal() on the freed pointer.

Since code other than in the ifnet plugin replaces settings,
we cannot be guaranteed that the pointer won't change.  Avoid all
that and just strdup() the UUID when using it as a key.
This commit is contained in:
Dan Williams 2014-06-11 13:40:19 -05:00
parent 72fb1f897f
commit c44556a1d5

View file

@ -231,7 +231,7 @@ static void
track_new_connection (SCPluginIfnet *self, NMIfnetConnection *connection)
{
g_hash_table_insert (SC_PLUGIN_IFNET_GET_PRIVATE (self)->connections,
(gpointer) nm_connection_get_uuid (NM_CONNECTION (connection)),
g_strdup (nm_connection_get_uuid (NM_CONNECTION (connection))),
g_object_ref (connection));
g_signal_connect (connection, NM_SETTINGS_CONNECTION_REMOVED,
G_CALLBACK (connection_removed_cb),
@ -432,7 +432,7 @@ init (NMSystemConfigInterface *config)
nm_log_info (LOGD_SETTINGS, "Initializing!");
priv->connections = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref);
priv->connections = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
priv->unmanaged_well_known = !is_managed_plugin ();
nm_log_info (LOGD_SETTINGS, "management mode: %s",
priv->unmanaged_well_known ? "unmanaged" : "managed");