audio: Fix possible crash when removing device 90/51490/1
authorBharat Panda <bharat.panda@samsung.com>
Tue, 10 Nov 2015 05:37:38 +0000 (11:07 +0530)
committerBharat Panda <bharat.panda@samsung.com>
Tue, 10 Nov 2015 05:37:38 +0000 (11:07 +0530)
Currently it is not possible to cancel avdtp_discover procedure
leading to crashe if the device is removed while avdtp_discover
is pending since its callback is still reachable.

git repo link:
http://git.kernel.org/cgit/bluetooth/bluez.git/commit/?id=33b447daaa3adfc04dfbc123538360d998e2d7d7

Change-Id: I607f818bbe0fe1287b421548fb41d1047bb88210

profiles/audio/a2dp.c
profiles/audio/a2dp.h
profiles/audio/avdtp.c
profiles/audio/sink.c
profiles/audio/source.c

index 464faeb..14de80c 100644 (file)
@@ -82,6 +82,7 @@ struct a2dp_sep {
 
 struct a2dp_setup_cb {
        struct a2dp_setup *setup;
+       a2dp_discover_cb_t discover_cb;
        a2dp_select_cb_t select_cb;
        a2dp_config_cb_t config_cb;
        a2dp_stream_cb_t resume_cb;
@@ -98,6 +99,7 @@ struct a2dp_setup {
        struct avdtp_stream *stream;
        struct avdtp_error *err;
        avdtp_set_configuration_cb setconf_cb;
+       GSList *seps;
        GSList *caps;
        gboolean reconfigure;
        gboolean start;
@@ -302,6 +304,23 @@ static void finalize_select(struct a2dp_setup *s)
        }
 }
 
+static void finalize_discover(struct a2dp_setup *s)
+{
+       GSList *l;
+
+       for (l = s->cb; l != NULL; ) {
+               struct a2dp_setup_cb *cb = l->data;
+
+               l = l->next;
+
+               if (!cb->discover_cb)
+                       continue;
+
+               cb->discover_cb(s->session, s->seps, s->err, cb->user_data);
+               setup_cb_free(cb);
+       }
+}
+
 static struct a2dp_setup *find_setup_by_session(struct avdtp *session)
 {
        GSList *l;
@@ -1838,6 +1857,40 @@ static struct a2dp_sep *a2dp_select_sep(struct avdtp *session, uint8_t type,
        return a2dp_find_sep(session, l, NULL);
 }
 
+static void discover_cb(struct avdtp *session, GSList *seps,
+                               struct avdtp_error *err, void *user_data)
+{
+       struct a2dp_setup *setup = user_data;
+
+       DBG("err %p", err);
+
+       setup->seps = seps;
+       setup->err = err;
+
+       finalize_discover(setup);
+}
+
+unsigned int a2dp_discover(struct avdtp *session, a2dp_discover_cb_t cb,
+                                                       void *user_data)
+{
+       struct a2dp_setup *setup;
+       struct a2dp_setup_cb *cb_data;
+
+       setup = a2dp_setup_get(session);
+       if (!setup)
+               return 0;
+
+       cb_data = setup_cb_new(setup);
+       cb_data->discover_cb = cb;
+       cb_data->user_data = user_data;
+
+       if (avdtp_discover(session, discover_cb, setup) == 0)
+               return cb_data->id;
+
+       setup_cb_free(cb_data);
+       return 0;
+}
+
 unsigned int a2dp_select_capabilities(struct avdtp *session,
                                        uint8_t type, const char *sender,
                                        a2dp_select_cb_t cb,
@@ -2131,8 +2184,8 @@ gboolean a2dp_cancel(unsigned int id)
 
                        if (!setup->cb) {
                                DBG("aborting setup %p", setup);
-                               avdtp_abort(setup->session, setup->stream);
-                               return TRUE;
+                               if (!avdtp_abort(setup->session, setup->stream))
+                                       return TRUE;
                        }
 
                        setup_unref(setup);
index 544eea1..50c1eeb 100644 (file)
@@ -52,6 +52,9 @@ struct a2dp_endpoint {
                                                        void *user_data);
 };
 
+typedef void (*a2dp_discover_cb_t) (struct avdtp *session, GSList *seps,
+                                       struct avdtp_error *err,
+                                       void *user_data);
 typedef void (*a2dp_select_cb_t) (struct avdtp *session,
                                        struct a2dp_sep *sep, GSList *caps,
                                        void *user_data);
@@ -70,6 +73,8 @@ struct a2dp_sep *a2dp_add_sep(struct btd_adapter *adapter, uint8_t type,
                                int *err);
 void a2dp_remove_sep(struct a2dp_sep *sep);
 
+unsigned int a2dp_discover(struct avdtp *session, a2dp_discover_cb_t cb,
+                                       void *user_data);
 unsigned int a2dp_select_capabilities(struct avdtp *session,
                                        uint8_t type, const char *sender,
                                        a2dp_select_cb_t cb,
index b9db7f3..aba2d03 100644 (file)
@@ -3703,6 +3703,13 @@ int avdtp_abort(struct avdtp *session, struct avdtp_stream *stream)
        struct seid_req req;
        int ret;
 
+       if (!stream && session->discover) {
+               /* Don't call cb since it being aborted */
+               session->discover->cb = NULL;
+               finalize_discovery(session, -ECANCELED);
+               return -EALREADY;
+       }
+
        if (!g_slist_find(session->streams, stream))
                return -EINVAL;
 
index 750b710..cdd89b4 100644 (file)
@@ -287,7 +287,9 @@ gboolean sink_setup_stream(struct btd_service *service, struct avdtp *session)
        if (!sink->session)
                return FALSE;
 
-       if (avdtp_discover(sink->session, discovery_complete, sink) < 0)
+       sink->connect_id = a2dp_discover(sink->session, discovery_complete,
+                                                               sink);
+       if (sink->connect_id == 0)
                return FALSE;
 
        return TRUE;
index 43c20c2..11e4cff 100644 (file)
@@ -273,7 +273,9 @@ gboolean source_setup_stream(struct btd_service *service,
        if (!source->session)
                return FALSE;
 
-       if (avdtp_discover(source->session, discovery_complete, source) < 0)
+       source->connect_id = a2dp_discover(source->session, discovery_complete,
+                                                               source);
+       if (source->connect_id == 0)
                return FALSE;
 
        return TRUE;