a2dp: Fix race when connecting and being connected at the same time
authorArchie Pusaka <apusaka@chromium.org>
Thu, 12 Mar 2020 05:54:37 +0000 (13:54 +0800)
committerAyush Garg <ayush.garg@samsung.com>
Mon, 12 Apr 2021 09:00:48 +0000 (14:30 +0530)
There is a possibility where BlueZ initiate an A2DP connection just
around the same time as the peripheral also initiate it.

One scenario is the peripheral initiate the connection first, so
confirm_cb() on /profiles/audio/a2dp.c is called. However, while we
are waiting for the authentication step, BlueZ initiate a connection
to the peripheral, therefore a2dp_sink_connect() is called, which
from there a2dp_avdtp_get() is called.

If this happens: When calling confirm_cb(), chan for the
corresponding device is created.

Then when calling a2dp_avdtp_get(), chan will be found as it is
created in confirm_cb(), and the value of chan->io is not NULL.
However, a NULL is supplied instead to create a new session and
assigned to chan->session.

Then when calling connect_cb(), chan->session will NOT be NULL, as
it is assigned in a2dp_avdtp_get(). Nevertheless, chan->session is
always assigned a new value.

These cause failure in connection.

Therefore, fixing this by supplying the value of chan->io inside
a2dp_avdtp_get() (it's going to be NULL on the normal case so it is
fine), and check whether chan->session already assigned inside
connect_cb().

Signed-off-by: Anuj Jain <anuj01.jain@samsung.com>
Signed-off-by: Ayush Garg <ayush.garg@samsung.com>
profiles/audio/a2dp.c

index 31b523a..30e012a 100644 (file)
@@ -2237,7 +2237,7 @@ struct avdtp *a2dp_avdtp_get(struct btd_device *device)
        return NULL;
 
 found:
-       chan->session = avdtp_new(NULL, device, server->seps);
+       chan->session = avdtp_new(chan->io, device, server->seps);
        if (!chan->session) {
                channel_remove(chan);
                return NULL;
@@ -2255,10 +2255,13 @@ static void connect_cb(GIOChannel *io, GError *err, gpointer user_data)
                goto fail;
        }
 
-       chan->session = avdtp_new(chan->io, chan->device, chan->server->seps);
        if (!chan->session) {
-               error("Unable to create AVDTP session");
-               goto fail;
+               chan->session = avdtp_new(chan->io, chan->device,
+                                                       chan->server->seps);
+               if (!chan->session) {
+                       error("Unable to create AVDTP session");
+                       goto fail;
+               }
        }
 
        g_io_channel_unref(chan->io);