session: Handle bearers update correctly
authorDaniel Wagner <daniel.wagner@bmw-carit.de>
Fri, 30 Sep 2011 06:31:08 +0000 (08:31 +0200)
committerDaniel Wagner <daniel.wagner@bmw-carit.de>
Fri, 30 Sep 2011 06:32:01 +0000 (08:32 +0200)
We cannot create a new service_list in populate_service_list
while we still hold pointers to entries (session->entry).
deselect_and_disconnect() would then operate on bogus pointers
leading to memory corruption

Fixes BMC#23325

src/session.c

index 592916a..c0c3b90 100644 (file)
@@ -936,11 +936,9 @@ static void populate_service_list(struct connman_session *session)
        struct service_entry *entry;
        GSequenceIter *iter;
 
-       if (session->service_list != NULL) {
-               g_hash_table_remove_all(session->service_hash);
-               g_sequence_free(session->service_list);
-       }
-
+       session->service_hash =
+               g_hash_table_new_full(g_direct_hash, g_direct_equal,
+                                       NULL, NULL);
        session->service_list = __connman_service_get_list(session,
                                                        service_match,
                                                        create_service_entry,
@@ -962,8 +960,6 @@ static void populate_service_list(struct connman_session *session)
 
                iter = g_sequence_iter_next(iter);
        }
-
-       session->info_dirty = TRUE;
 }
 
 static void session_changed(struct connman_session *session,
@@ -971,7 +967,9 @@ static void session_changed(struct connman_session *session,
 {
        struct session_info *info = session->info;
        struct session_info *info_last = session->info_last;
-       GSequenceIter *iter;
+       GSequenceIter *service_iter = NULL, *service_iter_last = NULL;
+       GSequence *service_list_last;
+       GHashTable *service_hash_last;
 
        /*
         * TODO: This only a placeholder for the 'real' algorithm to
@@ -992,16 +990,32 @@ static void session_changed(struct connman_session *session,
                DBG("ignore session changed event");
                return;
        case CONNMAN_SESSION_TRIGGER_SETTING:
-               if (info->entry != NULL) {
-                       iter = g_hash_table_lookup(session->service_hash,
+               if (info->allowed_bearers != info_last->allowed_bearers) {
+
+                       service_hash_last = session->service_hash;
+                       service_list_last = session->service_list;
+
+                       populate_service_list(session);
+
+                       if (info->entry != NULL) {
+                               service_iter_last = g_hash_table_lookup(
+                                                       service_hash_last,
+                                                       info->entry->service);
+                               service_iter = g_hash_table_lookup(
+                                                       session->service_hash,
                                                        info->entry->service);
-                       if (iter == NULL) {
+                       }
+
+                       if (service_iter == NULL && service_iter_last != NULL) {
                                /*
-                                * This service is not part of this
-                                * session anymore.
+                                * The currently selected service is
+                                * not part of this session anymore.
                                 */
                                deselect_and_disconnect(session, info->reason);
                        }
+
+                       g_hash_table_remove_all(service_hash_last);
+                       g_sequence_free(service_list_last);
                }
 
                if (info->online == FALSE) {
@@ -1191,7 +1205,7 @@ static DBusMessage *change_session(DBusConnection *conn,
 
                        info->allowed_bearers = allowed_bearers;
 
-                       populate_service_list(session);
+                       session->info_dirty = TRUE;
                } else {
                        goto err;
                }
@@ -1478,9 +1492,6 @@ int __connman_session_create(DBusMessage *msg)
                g_dbus_add_disconnect_watch(connection, session->owner,
                                        owner_disconnect, session, NULL);
 
-       session->service_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal,
-                                               NULL, NULL);
-
        info->online = FALSE;
        info->priority = priority;
        info->avoid_handover = avoid_handover;