Re: [PATCH v3 3/3] a2dp: Fix not calling SelectConfiguration on other available endpoints

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Thursday 02 May 2019 22:41:55 Pali Rohár wrote:
> On Monday 29 April 2019 18:39:16 Pali Rohár wrote:
> > On Monday 29 April 2019 19:35:24 Luiz Augusto von Dentz wrote:
> > > Hi Pali,
> > > 
> > > On Mon, Apr 29, 2019 at 3:03 PM Luiz Augusto von Dentz
> > > <luiz.dentz@xxxxxxxxx> wrote:
> > > >
> > > > From: Luiz Augusto von Dentz <luiz.von.dentz@xxxxxxxxx>
> > > >
> > > > Endpoint may not be able to select a valid configuration but there could
> > > > be other endpoints available that could be used, so instead of just
> > > > using the first match this collects all the matching endpoints and put
> > > > them into a queue (ordered by priority) then proceed to next endpoint
> > > > only failing if none of the available endpoits can select a valid
> > > > configuration.
> > > > ---
> > > >  profiles/audio/a2dp.c | 77 ++++++++++++++++++++++++++++---------------
> > > >  1 file changed, 50 insertions(+), 27 deletions(-)
> > > >
> > > > diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c
> > > > index a23abdd97..4d378a91a 100644
> > > > --- a/profiles/audio/a2dp.c
> > > > +++ b/profiles/audio/a2dp.c
> > > > @@ -105,6 +105,7 @@ struct a2dp_setup_cb {
> > > >  struct a2dp_setup {
> > > >         struct a2dp_channel *chan;
> > > >         struct avdtp *session;
> > > > +       struct queue *eps;
> > > >         struct a2dp_sep *sep;
> > > >         struct a2dp_remote_sep *rsep;
> > > >         struct avdtp_stream *stream;
> > > > @@ -2406,23 +2407,44 @@ void a2dp_remove_sep(struct a2dp_sep *sep)
> > > >
> > > >  static void select_cb(struct a2dp_setup *setup, void *ret, int size)
> > > >  {
> > > > -       if (size < 0) {
> > > > -               DBG("Endpoint replied an invalid configuration");
> > > > +       struct avdtp_service_capability *service;
> > > > +       struct avdtp_media_codec_capability *codec;
> > > > +       int err;
> > > > +
> > > > +       if (size) {
> > > > +               caps_add_codec(&setup->caps, setup->sep->codec, ret, size);
> > > >                 goto done;
> > > >         }
> > > >
> > > > -       caps_add_codec(&setup->caps, setup->sep->codec, ret, size);
> > > > +       setup->sep = queue_pop_head(setup->eps);
> > > > +       if (!setup->sep) {
> > > > +               error("Unable to select a valid configuration");
> > > > +               queue_destroy(setup->eps, NULL);
> > > > +               goto done;
> > > > +       }
> > > > +
> > > > +       setup->rsep = find_remote_sep(setup->chan, setup->sep);
> > > > +       service = avdtp_get_codec(setup->rsep->sep);
> > > > +       codec = (struct avdtp_media_codec_capability *) service->data;
> > > > +
> > > > +       err = setup->sep->endpoint->select_configuration(setup->sep,
> > > > +                                       codec->data,
> > > > +                                       service->length - sizeof(*codec),
> > > > +                                       setup_ref(setup),
> > > > +                                       select_cb, setup->sep->user_data);
> > > > +       if (err == 0)
> > > > +               return;
> > > >
> > > >  done:
> > > >         finalize_select(setup);
> > > >         setup_unref(setup);
> > > >  }
> > > >
> > > > -static struct a2dp_sep *a2dp_find_sep(struct avdtp *session, GSList *list,
> > > > +static struct queue *a2dp_find_eps(struct avdtp *session, GSList *list,
> > > >                                         const char *sender)
> > > >  {
> > > > -       struct a2dp_sep *first = NULL;
> > > >         struct a2dp_channel *chan = find_channel(session);
> > > > +       struct queue *seps = NULL;
> > > >
> > > >         for (; list; list = list->next) {
> > > >                 struct a2dp_sep *sep = list->data;
> > > > @@ -2444,26 +2466,25 @@ static struct a2dp_sep *a2dp_find_sep(struct avdtp *session, GSList *list,
> > > >                 if (!rsep)
> > > >                         continue;
> > > >
> > > > -               /* Locate last used if set */
> > > > -               if (chan->last_used) {
> > > > -                       if (chan->last_used->sep == rsep)
> > > > -                               return sep;
> > > > -                       first = sep;
> > > > -                       continue;
> > > > -               }
> > > > +               if (!seps)
> > > > +                       seps = queue_new();
> > > >
> > > > -               return sep;
> > > > +               /* Prepend last used so it is preferred over others */
> > > > +               if (chan->last_used && chan->last_used->sep == rsep)
> > > > +                       queue_push_head(seps, sep);
> > > > +               else
> > > > +                       queue_push_tail(seps, sep);
> > > >
> > > >         }
> > > >
> > > > -       return first;
> > > > +       return seps;
> > > >  }
> > > >
> > > > -static struct a2dp_sep *a2dp_select_sep(struct avdtp *session, uint8_t type,
> > > > +static struct queue *a2dp_select_eps(struct avdtp *session, uint8_t type,
> > > >                                         const char *sender)
> > > >  {
> > > >         struct a2dp_server *server;
> > > > -       struct a2dp_sep *sep;
> > > > +       struct queue *seps;
> > > >         GSList *l;
> > > >
> > > >         server = find_server(servers, avdtp_get_adapter(session));
> > > > @@ -2473,11 +2494,11 @@ static struct a2dp_sep *a2dp_select_sep(struct avdtp *session, uint8_t type,
> > > >         l = type == AVDTP_SEP_TYPE_SINK ? server->sources : server->sinks;
> > > >
> > > >         /* Check sender's seps first */
> > > > -       sep = a2dp_find_sep(session, l, sender);
> > > > -       if (sep != NULL)
> > > > -               return sep;
> > > > +       seps = a2dp_find_eps(session, l, sender);
> > > > +       if (seps != NULL)
> > > > +               return seps;
> > > >
> > > > -       return a2dp_find_sep(session, l, NULL);
> > > > +       return a2dp_find_eps(session, l, NULL);
> > > >  }
> > > >
> > > >  static void store_remote_sep(void *data, void *user_data)
> > > > @@ -2580,13 +2601,13 @@ unsigned int a2dp_select_capabilities(struct avdtp *session,
> > > >  {
> > > >         struct a2dp_setup *setup;
> > > >         struct a2dp_setup_cb *cb_data;
> > > > -       struct a2dp_sep *sep;
> > > > +       struct queue *eps;
> > > >         struct avdtp_service_capability *service;
> > > >         struct avdtp_media_codec_capability *codec;
> > > >         int err;
> > > >
> > > > -       sep = a2dp_select_sep(session, type, sender);
> > > > -       if (!sep) {
> > > > +       eps = a2dp_select_eps(session, type, sender);
> > > > +       if (!eps) {
> > > >                 error("Unable to select SEP");
> > > >                 return 0;
> > > >         }
> > > > @@ -2599,8 +2620,9 @@ unsigned int a2dp_select_capabilities(struct avdtp *session,
> > > >         cb_data->select_cb = cb;
> > > >         cb_data->user_data = user_data;
> > > >
> > > > -       setup->sep = sep;
> > > > -       setup->rsep = find_remote_sep(setup->chan, sep);
> > > > +       setup->eps = eps;
> > > > +       setup->sep = queue_pop_head(eps);
> > > > +       setup->rsep = find_remote_sep(setup->chan, setup->sep);
> > > >
> > > >         if (setup->rsep == NULL) {
> > > >                 error("Could not find remote sep");
> > > > @@ -2610,10 +2632,11 @@ unsigned int a2dp_select_capabilities(struct avdtp *session,
> > > >         service = avdtp_get_codec(setup->rsep->sep);
> > > >         codec = (struct avdtp_media_codec_capability *) service->data;
> > > >
> > > > -       err = sep->endpoint->select_configuration(sep, codec->data,
> > > > +       err = setup->sep->endpoint->select_configuration(setup->sep,
> > > > +                                       codec->data,
> > > >                                         service->length - sizeof(*codec),
> > > >                                         setup_ref(setup),
> > > > -                                       select_cb, sep->user_data);
> > > > +                                       select_cb, setup->sep->user_data);
> > > >         if (err == 0)
> > > >                 return cb_data->id;
> > > >
> > > > --
> > > > 2.20.1
> > > 
> > > Le me know if you find any problem with this change, my headsets seems
> > > to always succeed the first try so I cannot really reproduce the
> > > problem.
> > 
> > Ok, I will try it in next days. I can register PA endpoints which always
> > "fails" so I can test if this is working correctly as expected.
> 
> Nope, this your patch does not work. It cause segfaulting of bluetoothd
> daemon itself. Here is stacktrace of current git master branch (where is
> your patch too):
> 
> Program received signal SIGSEGV, Segmentation fault.
> __memmove_avx_unaligned_erms () at ../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S:416
> 416     ../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S: Adresár alebo súbor neexistuje.
> (gdb) bt
> #0  __memmove_avx_unaligned_erms () at ../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S:416
> #1  0x0000557d2d2f3164 in caps_add_codec (l=0x557d2f1a9708, codec=<optimized out>, caps=0x0, size=18446744073709551615) at profiles/audio/a2dp.c:1519
> #2  0x0000557d2d2f626a in select_cb (setup=0x557d2f1a96c0, ret=<optimized out>, size=<optimized out>) at profiles/audio/a2dp.c:2422
> #3  0x0000557d2d2fe016 in endpoint_reply (call=<optimized out>, user_data=0x557d2f187810) at profiles/audio/media.c:316
> #4  0x00007fd2dc6aa002 in ?? () from /lib/x86_64-linux-gnu/libdbus-1.so.3
> #5  0x00007fd2dc6ada5f in dbus_connection_dispatch () from /lib/x86_64-linux-gnu/libdbus-1.so.3
> #6  0x0000557d2d36f320 in message_dispatch (data=0x557d2f184560) at gdbus/mainloop.c:72
> #7  0x00007fd2dc9326aa in g_main_context_dispatch () from /lib/x86_64-linux-gnu/libglib-2.0.so.0
> #8  0x00007fd2dc932a60 in ?? () from /lib/x86_64-linux-gnu/libglib-2.0.so.0
> #9  0x00007fd2dc932d82 in g_main_loop_run () from /lib/x86_64-linux-gnu/libglib-2.0.so.0
> #10 0x0000557d2d384a45 in mainloop_run () at src/shared/mainloop-glib.c:79
> #11 0x0000557d2d384e0f in mainloop_run_with_signal (func=<optimized out>, user_data=0x0) at src/shared/mainloop-notify.c:201
> #12 0x0000557d2d2eb0a8 in main (argc=<optimized out>, argv=<optimized out>) at src/main.c:729
> 
> In syslog for bluetoothd is this last line:
> bluetoothd[12605]: Endpoint replied with an error: org.bluez.Error.InvalidArguments
> 
> pulseaudio refused only one SelectConfiguration call.

In profiles/audio/a2dp.c is:

static void select_cb(struct a2dp_setup *setup, void *ret, int size)
{
...
	if (size) {
		caps_add_codec(&setup->caps, setup->sep->codec, ret, size);
...

and then there is:

tatic void caps_add_codec(GSList **l, uint8_t codec, uint8_t *caps,
							size_t size)
{
...
	memcpy(cap->data, caps, size);
...

When select_cb() is called with size=-1 then is calls caps_add_codec()
as size is non-zero and cast signed 32bit int -1 to unsigned 64bit int
as value 2^64-1. Later is called memcpy with size 2^64-1 and crash is
there. Moreover caps is NULLL pointer, therefore void *ret in select_cb
is NULL too. Seems like wrong error handling in select_cb() function.

-- 
Pali Rohár
pali.rohar@xxxxxxxxx

Attachment: signature.asc
Description: PGP signature


[Index of Archives]     [Bluez Devel]     [Linux Wireless Networking]     [Linux Wireless Personal Area Networking]     [Linux ATH6KL]     [Linux USB Devel]     [Linux Media Drivers]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux