There are various device & service discovery tasks that are initiated
based on a qmi_device object.  qmi_device object does not currently
keep track of these tasks.  Unfortunately the qmi_device object can
go away at any time, and these tasks can become orphaned.

The result of this can lead to crashes.  E.g. a discovery task timeout fires
after the qmi_device object has been destroyed.  Since the object is no
longer valid, any accesses to it will likely result in a SEGFAULT.

This patch attempts to track all discovery tasks on the qmi_device
object itself, so that they can be cleaned up properly.  This patch does
not handle the qmi_device_shutdown functionality.
---
 drivers/qmimodem/qmi.c | 149 ++++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 124 insertions(+), 25 deletions(-)

diff --git a/drivers/qmimodem/qmi.c b/drivers/qmimodem/qmi.c
index 39fbb19..9b80455 100644
--- a/drivers/qmimodem/qmi.c
+++ b/drivers/qmimodem/qmi.c
@@ -43,6 +43,11 @@
 typedef void (*qmi_message_func_t)(uint16_t message, uint16_t length,
                                        const void *buffer, void *user_data);
 
+struct discovery {
+       void *discover_data;
+       qmi_destroy_func_t destroy;
+};
+
 struct qmi_device {
        int ref_count;
        int fd;
@@ -53,6 +58,7 @@ struct qmi_device {
        GQueue *req_queue;
        GQueue *control_queue;
        GQueue *service_queue;
+       GQueue *discovery_queue;
        uint8_t next_control_tid;
        uint16_t next_service_tid;
        qmi_debug_func_t debug_func;
@@ -213,6 +219,24 @@ static gint __request_compare(gconstpointer a, 
gconstpointer b)
        return req->tid - tid;
 }
 
+static void __discovery_free(gpointer data, gpointer user_data)
+{
+       struct discovery *d = data;
+       qmi_destroy_func_t destroy = d->destroy;
+
+       destroy(d->discover_data);
+}
+
+static gint __discovery_compare(gconstpointer a, gconstpointer b)
+{
+       const struct discovery *d = a;
+
+       if (d->discover_data == b)
+               return 0;
+
+       return 1;
+}
+
 static void __notify_free(gpointer data, gpointer user_data)
 {
        struct qmi_notify *notify = data;
@@ -844,6 +868,37 @@ static void read_watch_destroy(gpointer user_data)
        device->read_watch = 0;
 }
 
+static void __qmi_device_discovery_started(struct qmi_device *device,
+                                               void *discover_data,
+                                               qmi_destroy_func_t destroy)
+{
+       struct discovery *d;
+
+       d = g_new0(struct discovery, 1);
+       d->discover_data = discover_data;
+       d->destroy = destroy;
+
+       g_queue_push_tail(device->discovery_queue, d);
+}
+
+static void __qmi_device_discovery_complete(struct qmi_device *device,
+                                               void *discover_data)
+{
+       GList *list;
+       struct discovery *d;
+
+       list = g_queue_find_custom(device->req_queue,
+                               discover_data, __discovery_compare);
+       if (!list)
+               return;
+
+       d = list->data;
+       g_queue_delete_link(device->discovery_queue, list);
+
+       d->destroy(d->discover_data);
+       __discovery_free(d, NULL);
+}
+
 static void service_destroy(gpointer data)
 {
        struct qmi_service *service = data;
@@ -897,6 +952,7 @@ struct qmi_device *qmi_device_new(int fd)
        device->req_queue = g_queue_new();
        device->control_queue = g_queue_new();
        device->service_queue = g_queue_new();
+       device->discovery_queue = g_queue_new();
 
        device->service_list = g_hash_table_new_full(g_direct_hash,
                                        g_direct_equal, NULL, service_destroy);
@@ -933,6 +989,9 @@ void qmi_device_unref(struct qmi_device *device)
        g_queue_foreach(device->req_queue, __request_free, NULL);
        g_queue_free(device->req_queue);
 
+       g_queue_foreach(device->discovery_queue, __discovery_free, NULL);
+       g_queue_free(device->discovery_queue);
+
        if (device->write_watch > 0)
                g_source_remove(device->write_watch);
 
@@ -1000,6 +1059,21 @@ struct discover_data {
        guint timeout;
 };
 
+static void discover_data_free(gpointer user_data)
+{
+       struct discover_data *data = user_data;
+
+       if (data->timeout) {
+               g_source_remove(data->timeout);
+               data->timeout = 0;
+       }
+
+       if (data->destroy)
+               data->destroy(data->user_data);
+
+       g_free(data);
+}
+
 static void discover_callback(uint16_t message, uint16_t length,
                                        const void *buffer, void *user_data)
 {
@@ -1013,8 +1087,6 @@ static void discover_callback(uint16_t message, uint16_t 
length,
        uint8_t count;
        unsigned int i;
 
-       g_source_remove(data->timeout);
-
        count = 0;
        list = NULL;
 
@@ -1085,10 +1157,7 @@ done:
        if (data->func)
                data->func(count, list, data->user_data);
 
-       if (data->destroy)
-               data->destroy(data->user_data);
-
-       g_free(data);
+       __qmi_device_discovery_complete(data->device, data);
 }
 
 static gboolean discover_reply(gpointer user_data)
@@ -1102,10 +1171,7 @@ static gboolean discover_reply(gpointer user_data)
                data->func(device->version_count,
                                device->version_list, data->user_data);
 
-       if (data->destroy)
-               data->destroy(data->user_data);
-
-       g_free(data);
+       __qmi_device_discovery_complete(data->device, data);
 
        return FALSE;
 }
@@ -1132,7 +1198,9 @@ bool qmi_device_discover(struct qmi_device *device, 
qmi_discover_func_t func,
        data->destroy = destroy;
 
        if (device->version_list) {
-               g_timeout_add_seconds(0, discover_reply, data);
+               data->timeout = g_timeout_add_seconds(0, discover_reply, data);
+               __qmi_device_discovery_started(device, data,
+                                                       discover_data_free);
                return true;
        }
 
@@ -1153,6 +1221,7 @@ bool qmi_device_discover(struct qmi_device *device, 
qmi_discover_func_t func,
        __request_submit(device, req, hdr->transaction);
 
        data->timeout = g_timeout_add_seconds(5, discover_reply, data);
+       __qmi_device_discovery_started(device, data, discover_data_free);
 
        return true;
 }
@@ -1714,16 +1783,29 @@ struct service_create_data {
        guint timeout;
 };
 
-static gboolean service_create_reply(gpointer user_data)
+static void service_create_data_free(gpointer user_data)
 {
        struct service_create_data *data = user_data;
 
-       data->func(NULL, data->user_data);
+       if (data->timeout) {
+               g_source_remove(data->timeout);
+               data->timeout = 0;
+       }
 
        if (data->destroy)
                data->destroy(data->user_data);
 
        g_free(data);
+}
+
+static gboolean service_create_reply(gpointer user_data)
+{
+       struct service_create_data *data = user_data;
+
+       data->timeout = 0;
+       data->func(NULL, data->user_data);
+
+       __qmi_device_discovery_complete(data->device, data);
 
        return FALSE;
 }
@@ -1739,8 +1821,6 @@ static void service_create_callback(uint16_t message, 
uint16_t length,
        uint16_t len;
        unsigned int hash_id;
 
-       g_source_remove(data->timeout);
-
        result_code = tlv_get(buffer, length, 0x02, &len);
        if (!result_code)
                goto done;
@@ -1782,13 +1862,9 @@ static void service_create_callback(uint16_t message, 
uint16_t length,
 
 done:
        data->func(service, data->user_data);
-
        qmi_service_unref(service);
 
-       if (data->destroy)
-               data->destroy(data->user_data);
-
-       g_free(data);
+       __qmi_device_discovery_complete(data->device, data);
 }
 
 static void service_create_discover(uint8_t count,
@@ -1819,7 +1895,10 @@ static void service_create_discover(uint8_t count,
                if (data->timeout > 0)
                        g_source_remove(data->timeout);
 
-               g_timeout_add_seconds(0, service_create_reply, data);
+               data->timeout = g_timeout_add_seconds(0,
+                                               service_create_reply, data);
+               __qmi_device_discovery_started(device, data,
+                                               service_create_data_free);
                return;
        }
 
@@ -1864,6 +1943,8 @@ static bool service_create(struct qmi_device *device, 
bool shared,
 
 done:
        data->timeout = g_timeout_add_seconds(8, service_create_reply, data);
+       __qmi_device_discovery_started(device, data,
+                                               service_create_data_free);
 
        return true;
 }
@@ -1883,16 +1964,21 @@ bool qmi_service_create(struct qmi_device *device,
 
 struct service_create_shared_data {
        struct qmi_service *service;
+       struct qmi_device *device;
        qmi_create_func_t func;
        void *user_data;
        qmi_destroy_func_t destroy;
+       guint timeout;
 };
 
-static gboolean service_create_shared_reply(gpointer user_data)
+static void service_create_shared_data_free(gpointer user_data)
 {
        struct service_create_shared_data *data = user_data;
 
-       data->func(data->service, data->user_data);
+       if (data->timeout) {
+               g_source_remove(data->timeout);
+               data->timeout = 0;
+       }
 
        qmi_service_unref(data->service);
 
@@ -1900,6 +1986,16 @@ static gboolean service_create_shared_reply(gpointer 
user_data)
                data->destroy(data->user_data);
 
        g_free(data);
+}
+
+static gboolean service_create_shared_reply(gpointer user_data)
+{
+       struct service_create_shared_data *data = user_data;
+
+       data->timeout = 0;
+       data->func(data->service, data->user_data);
+
+       __qmi_device_discovery_complete(data->device, data);
 
        return FALSE;
 }
@@ -1927,12 +2023,15 @@ bool qmi_service_create_shared(struct qmi_device 
*device,
                        return false;
 
                data->service = qmi_service_ref(service);
-
+               data->device = device;
                data->func = func;
                data->user_data = user_data;
                data->destroy = destroy;
 
-               g_timeout_add(0, service_create_shared_reply, data);
+               data->timeout = g_timeout_add(0,
+                                       service_create_shared_reply, data);
+               __qmi_device_discovery_started(device, data,
+                                       service_create_shared_data_free);
 
                return 0;
        }
-- 
2.10.2

_______________________________________________
ofono mailing list
[email protected]
https://lists.ofono.org/mailman/listinfo/ofono

Reply via email to