vpn: Reference of provider not taken
authorJukka Rissanen <jukka.rissanen@linux.intel.com>
Fri, 3 Feb 2012 11:31:28 +0000 (13:31 +0200)
committerSamuel Ortiz <sameo@linux.intel.com>
Sat, 4 Feb 2012 00:15:35 +0000 (01:15 +0100)
VPN plugin hooks itself into rtnl newlink watch and gives
provider pointer as user data. We must take reference of the
user data pointer as otherwise we might access invalid pointer
if provider is already removed when rtnl watch is triggered.
This seems to be a rare issue but I had one valgrind crash
because of this.

plugins/vpn.c

index a6ac5b9..e103d36 100644 (file)
@@ -135,6 +135,7 @@ void vpn_died(struct connman_task *task, int exit_code, void *user_data)
 
        stop_vpn(provider);
        connman_provider_set_data(provider, NULL);
+       connman_provider_unref(provider);
        connman_rtnl_remove_watch(data->watch);
 
 vpn_exit:
@@ -226,6 +227,7 @@ static DBusMessage *vpn_notify(struct connman_task *task,
        case VPN_STATE_CONNECT:
        case VPN_STATE_READY:
                index = connman_provider_get_index(provider);
+               connman_provider_ref(provider);
                data->watch = connman_rtnl_add_newlink_watch(index,
                                                     vpn_newlink, provider);
                connman_inet_ifup(index);
@@ -421,10 +423,12 @@ static int vpn_disconnect(struct connman_provider *provider)
        if (vpn_driver_data->vpn_driver->disconnect)
                vpn_driver_data->vpn_driver->disconnect();
 
-       if (data->watch != 0)
+       if (data->watch != 0) {
+               connman_provider_unref(provider);
                connman_rtnl_remove_watch(data->watch);
+               data->watch = 0;
+       }
 
-       data->watch = 0;
        data->state = VPN_STATE_DISCONNECT;
        connman_task_stop(data->task);
 
@@ -439,9 +443,12 @@ static int vpn_remove(struct connman_provider *provider)
        if (data == NULL)
                return 0;
 
-       if (data->watch != 0)
+       if (data->watch != 0) {
+               connman_provider_unref(provider);
                connman_rtnl_remove_watch(data->watch);
-       data->watch = 0;
+               data->watch = 0;
+       }
+
        connman_task_stop(data->task);
 
        g_usleep(G_USEC_PER_SEC);