diff mbox series

[BlueZ,v4,1/2] a2dp: fix incorrect transaction label in setconf phase

Message ID 20240104171400.124128-1-xiaokeqinhealth@126.com (mailing list archive)
State New, archived
Headers show
Series [BlueZ,v4,1/2] a2dp: fix incorrect transaction label in setconf phase | expand

Checks

Context Check Description
tedd_an/pre-ci_am success Success
tedd_an/CheckPatch success CheckPatch PASS
tedd_an/GitLint success Gitlint PASS
tedd_an/BuildEll success Build ELL PASS
tedd_an/BluezMake success Bluez Make PASS
tedd_an/MakeCheck success Bluez Make Check PASS
tedd_an/MakeDistcheck success Make Distcheck PASS
tedd_an/CheckValgrind success Check Valgrind PASS
tedd_an/CheckSmatch success CheckSparse PASS
tedd_an/bluezmakeextell success Make External ELL PASS
tedd_an/IncrementalBuild success Incremental Build PASS
tedd_an/ScanBuild success Scan Build PASS

Commit Message

Yao Xiao Jan. 4, 2024, 5:14 p.m. UTC
From: Xiao Yao <xiaoyao@rock-chips.com>

BLUETOOTH SPECIFICATION Page 61 of 140
Audio/Video Distribution Transport Protocol Specification (V13)
8.4.6 Message integrity verification at receiver side

- The receiver of an AVDTP signaling message shall not interpret corrupted
messages. Those messages are discarded and no signaling message is returned
to the sender if no error code is applicable. Possible corrupted messages
are:

  * Response messages where the transaction label cannot match a previous
    command sent to the remote device

Consider the following scenario:
btmon log:
AVDTP: Discover (0x01) Command (0x00) type 0x00 label 5 nosp 0
... ...
< AVDTP: Set Configuration (0x03) Command (0x00) type 0x00 label 8 nosp 0
//Currently, a 'set configuration' message has been received from the
//sender, which contains a transaction label valued at 8. This message
//was then relayed to A2DP backend(PulseAudio/PipeWire) using the dbus
//interface.
  set_configuration()(media.c)
    dbus_message_new_method_call(..., "SetConfiguration", ...);
    g_dbus_send_message_with_reply(btd_get_dbus_connection(), ...);
    dbus_pending_call_set_notify(request->call, endpoint_reply, ...);
    ...

//The commit "02877c5e9" introduces a reverse discovery logic, resulting
//in a small probability that the discovery command is issued before the
//setconfig accept command.
//Tip: If an artificial delay is added to the audio backend, this issue
//will invariably occur."
> AVDTP: Discover (0x01) Command (0x00) type 0x00 label 0 nosp 0
//After receiving the discover reply, the session->in.transaction is
//changed to 0
< AVDTP: Discover (0x01) Response Accept (0x02) type 0x00 label 0 nosp 0

> AVDTP: Set Configuration (0x03) Resp Accept (0x02) type 0 label 0 nosp 0
//The audio backend reply the dbus message
  endpoint_reply (media.c)
    setconf_cb (avdtp.c)
      //Here avdtp_send sends an incorrect transaction value, causing
      //the sender to discard the message. (The correct transaction
      //value is 8)
      avdtp_send(session, session->in.transaction, AVDTP_MSG_TYPE_ACCEPT,
                 AVDTP_SET_CONFIGURATION, NULL, 0)

AVDTP: Delay Report (0x0d) Command (0x00) type 0x00 label 1 nosp 0
... ...

Therefore, the reverse discovery logic was adjusted to the back of
setconfig accept to avoid two transmission transactions at the same
time and fixed the problem.

Signed-off-by: Xiao Yao <xiaoyao@rock-chips.com>
---
v1 -> v2: Fixed "session->in.transaction" logic err.
v2 -> v3: Fixed some compile warnings
v3 -> v4: Adjust the timing of reverse discovery logic
---
 profiles/audio/a2dp.c | 27 ++++++++++++++-------------
 1 file changed, 14 insertions(+), 13 deletions(-)

Comments

bluez.test.bot@gmail.com Jan. 4, 2024, 6:32 p.m. UTC | #1
This is automated email and please do not reply to this email!

Dear submitter,

Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=814435

---Test result---

Test Summary:
CheckPatch                    PASS      0.72 seconds
GitLint                       PASS      0.32 seconds
BuildEll                      PASS      24.58 seconds
BluezMake                     PASS      741.22 seconds
MakeCheck                     PASS      11.95 seconds
MakeDistcheck                 PASS      165.31 seconds
CheckValgrind                 PASS      228.03 seconds
CheckSmatch                   PASS      335.48 seconds
bluezmakeextell               PASS      109.63 seconds
IncrementalBuild              PASS      696.75 seconds
ScanBuild                     PASS      1004.66 seconds



---
Regards,
Linux Bluetooth
Luiz Augusto von Dentz Jan. 4, 2024, 7:08 p.m. UTC | #2
Hi Xiao,

On Thu, Jan 4, 2024 at 12:16 PM Xiao Yao <xiaokeqinhealth@126.com> wrote:
>
> From: Xiao Yao <xiaoyao@rock-chips.com>
>
> BLUETOOTH SPECIFICATION Page 61 of 140
> Audio/Video Distribution Transport Protocol Specification (V13)
> 8.4.6 Message integrity verification at receiver side
>
> - The receiver of an AVDTP signaling message shall not interpret corrupted
> messages. Those messages are discarded and no signaling message is returned
> to the sender if no error code is applicable. Possible corrupted messages
> are:
>
>   * Response messages where the transaction label cannot match a previous
>     command sent to the remote device
>
> Consider the following scenario:
> btmon log:
> AVDTP: Discover (0x01) Command (0x00) type 0x00 label 5 nosp 0
> ... ...
> < AVDTP: Set Configuration (0x03) Command (0x00) type 0x00 label 8 nosp 0
> //Currently, a 'set configuration' message has been received from the
> //sender, which contains a transaction label valued at 8. This message
> //was then relayed to A2DP backend(PulseAudio/PipeWire) using the dbus
> //interface.
>   set_configuration()(media.c)
>     dbus_message_new_method_call(..., "SetConfiguration", ...);
>     g_dbus_send_message_with_reply(btd_get_dbus_connection(), ...);
>     dbus_pending_call_set_notify(request->call, endpoint_reply, ...);
>     ...
>
> //The commit "02877c5e9" introduces a reverse discovery logic, resulting
> //in a small probability that the discovery command is issued before the
> //setconfig accept command.
> //Tip: If an artificial delay is added to the audio backend, this issue
> //will invariably occur."
> > AVDTP: Discover (0x01) Command (0x00) type 0x00 label 0 nosp 0
> //After receiving the discover reply, the session->in.transaction is
> //changed to 0
> < AVDTP: Discover (0x01) Response Accept (0x02) type 0x00 label 0 nosp 0
>
> > AVDTP: Set Configuration (0x03) Resp Accept (0x02) type 0 label 0 nosp 0
> //The audio backend reply the dbus message
>   endpoint_reply (media.c)
>     setconf_cb (avdtp.c)
>       //Here avdtp_send sends an incorrect transaction value, causing
>       //the sender to discard the message. (The correct transaction
>       //value is 8)
>       avdtp_send(session, session->in.transaction, AVDTP_MSG_TYPE_ACCEPT,
>                  AVDTP_SET_CONFIGURATION, NULL, 0)
>
> AVDTP: Delay Report (0x0d) Command (0x00) type 0x00 label 1 nosp 0
> ... ...
>
> Therefore, the reverse discovery logic was adjusted to the back of
> setconfig accept to avoid two transmission transactions at the same
> time and fixed the problem.
>
> Signed-off-by: Xiao Yao <xiaoyao@rock-chips.com>
> ---
> v1 -> v2: Fixed "session->in.transaction" logic err.
> v2 -> v3: Fixed some compile warnings
> v3 -> v4: Adjust the timing of reverse discovery logic
> ---
>  profiles/audio/a2dp.c | 27 ++++++++++++++-------------
>  1 file changed, 14 insertions(+), 13 deletions(-)
>
> diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c
> index b43161a13..f4ef8aec2 100644
> --- a/profiles/audio/a2dp.c
> +++ b/profiles/audio/a2dp.c
> @@ -586,6 +586,12 @@ done:
>         return FALSE;
>  }
>
> +static void reverse_discover(struct avdtp *session, GSList *seps, int err,
> +                            void *user_data)
> +{
> +       DBG("err %d", err);
> +}
> +
>  static void endpoint_setconf_cb(struct a2dp_setup *setup, gboolean ret)
>  {
>         if (ret == FALSE) {
> @@ -595,6 +601,13 @@ static void endpoint_setconf_cb(struct a2dp_setup *setup, gboolean ret)
>         }
>
>         auto_config(setup);
> +
> +       /* Attempt to reverse discover if there are no remote
> +        * SEPs.
> +        */
> +       if (queue_isempty(setup->chan->seps))
> +               a2dp_discover(setup->session, reverse_discover, NULL);
> +
>         setup_unref(setup);
>  }
>
> @@ -634,12 +647,6 @@ static gboolean endpoint_match_codec_ind(struct avdtp *session,
>         return TRUE;
>  }
>
> -static void reverse_discover(struct avdtp *session, GSList *seps, int err,
> -                                                       void *user_data)
> -{
> -       DBG("err %d", err);
> -}
> -
>  static gboolean endpoint_setconf_ind(struct avdtp *session,
>                                                 struct avdtp_local_sep *sep,
>                                                 struct avdtp_stream *stream,
> @@ -695,14 +702,8 @@ static gboolean endpoint_setconf_ind(struct avdtp *session,
>                                                 setup_ref(setup),
>                                                 endpoint_setconf_cb,
>                                                 a2dp_sep->user_data);
> -               if (ret == 0) {
> -                       /* Attempt to reverse discover if there are no remote
> -                        * SEPs.
> -                        */
> -                       if (queue_isempty(setup->chan->seps))
> -                               a2dp_discover(session, reverse_discover, NULL);

Have you actually test these changes with read devices? I would be
really surprised if this works because you are essentially changing
the reverse discover to when we do initiate AVDTP_SetConfiguration
rather when we receive, which shall never need a reverse discover to
begin with since we are initiating we always perform a discover
anyway, so that most likely is dead code that will never going to
executed.

The real culprit here is that both commands and responses are stored
in the session.in while we should probably have a session.cmd and
session.rsp to be able to handle outstanding requests in each
direction.

> +               if (ret == 0)
>                         return TRUE;
> -               }
>
>                 setup_unref(setup);
>                 setup->err = g_new(struct avdtp_error, 1);
> --
> 2.34.1
>
>
Yao Xiao Jan. 11, 2024, 5:08 p.m. UTC | #3
Hi Luiz,
On 2024/1/5 3:08, Luiz Augusto von Dentz wrote:
> Hi Xiao,
> On Thu, Jan 4, 2024 at 12:16 PM Xiao Yao <xiaokeqinhealth@126.com> wrote:
>> From: Xiao Yao <xiaoyao@rock-chips.com>
>>
>> BLUETOOTH SPECIFICATION Page 61 of 140
>> Audio/Video Distribution Transport Protocol Specification (V13)
>> 8.4.6 Message integrity verification at receiver side
>>
>> - The receiver of an AVDTP signaling message shall not interpret corrupted
>> messages. Those messages are discarded and no signaling message is returned
>> to the sender if no error code is applicable. Possible corrupted messages
>> are:
>>
>>    * Response messages where the transaction label cannot match a previous
>>      command sent to the remote device
>>
>> Consider the following scenario:
>> btmon log:
>> AVDTP: Discover (0x01) Command (0x00) type 0x00 label 5 nosp 0
>> ... ...
>> < AVDTP: Set Configuration (0x03) Command (0x00) type 0x00 label 8 nosp 0
>> //Currently, a 'set configuration' message has been received from the
>> //sender, which contains a transaction label valued at 8. This message
>> //was then relayed to A2DP backend(PulseAudio/PipeWire) using the dbus
>> //interface.
>>    set_configuration()(media.c)
>>      dbus_message_new_method_call(..., "SetConfiguration", ...);
>>      g_dbus_send_message_with_reply(btd_get_dbus_connection(), ...);
>>      dbus_pending_call_set_notify(request->call, endpoint_reply, ...);
>>      ...
>>
>> //The commit "02877c5e9" introduces a reverse discovery logic, resulting
>> //in a small probability that the discovery command is issued before the
>> //setconfig accept command.
>> //Tip: If an artificial delay is added to the audio backend, this issue
>> //will invariably occur."
>>> AVDTP: Discover (0x01) Command (0x00) type 0x00 label 0 nosp 0
>> //After receiving the discover reply, the session->in.transaction is
>> //changed to 0
>> < AVDTP: Discover (0x01) Response Accept (0x02) type 0x00 label 0 nosp 0
>>
>>> AVDTP: Set Configuration (0x03) Resp Accept (0x02) type 0 label 0 nosp 0
>> //The audio backend reply the dbus message
>>    endpoint_reply (media.c)
>>      setconf_cb (avdtp.c)
>>        //Here avdtp_send sends an incorrect transaction value, causing
>>        //the sender to discard the message. (The correct transaction
>>        //value is 8)
>>        avdtp_send(session, session->in.transaction, AVDTP_MSG_TYPE_ACCEPT,
>>                   AVDTP_SET_CONFIGURATION, NULL, 0)
>>
>> AVDTP: Delay Report (0x0d) Command (0x00) type 0x00 label 1 nosp 0
>> ... ...
>>
>> Therefore, the reverse discovery logic was adjusted to the back of
>> setconfig accept to avoid two transmission transactions at the same
>> time and fixed the problem.
>>
>> Signed-off-by: Xiao Yao <xiaoyao@rock-chips.com>
>> ---
>> v1 -> v2: Fixed "session->in.transaction" logic err.
>> v2 -> v3: Fixed some compile warnings
>> v3 -> v4: Adjust the timing of reverse discovery logic
>> ---
>>   profiles/audio/a2dp.c | 27 ++++++++++++++-------------
>>   1 file changed, 14 insertions(+), 13 deletions(-)
>>
>> diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c
>> index b43161a13..f4ef8aec2 100644
>> --- a/profiles/audio/a2dp.c
>> +++ b/profiles/audio/a2dp.c
>> @@ -586,6 +586,12 @@ done:
>>          return FALSE;
>>   }
>>
>> +static void reverse_discover(struct avdtp *session, GSList *seps, int err,
>> +                            void *user_data)
>> +{
>> +       DBG("err %d", err);
>> +}
>> +
>>   static void endpoint_setconf_cb(struct a2dp_setup *setup, gboolean ret)
>>   {
>>          if (ret == FALSE) {
>> @@ -595,6 +601,13 @@ static void endpoint_setconf_cb(struct a2dp_setup *setup, gboolean ret)
>>          }
>>
>>          auto_config(setup);
>> +
>> +       /* Attempt to reverse discover if there are no remote
>> +        * SEPs.
>> +        */
>> +       if (queue_isempty(setup->chan->seps))
>> +               a2dp_discover(setup->session, reverse_discover, NULL);
>> +
>>          setup_unref(setup);
>>   }
>>
>> @@ -634,12 +647,6 @@ static gboolean endpoint_match_codec_ind(struct avdtp *session,
>>          return TRUE;
>>   }
>>
>> -static void reverse_discover(struct avdtp *session, GSList *seps, int err,
>> -                                                       void *user_data)
>> -{
>> -       DBG("err %d", err);
>> -}
>> -
>>   static gboolean endpoint_setconf_ind(struct avdtp *session,
>>                                                  struct avdtp_local_sep *sep,
>>                                                  struct avdtp_stream *stream,
>> @@ -695,14 +702,8 @@ static gboolean endpoint_setconf_ind(struct avdtp *session,
>>                                                  setup_ref(setup),
>>                                                  endpoint_setconf_cb,
>>                                                  a2dp_sep->user_data);
>> -               if (ret == 0) {
>> -                       /* Attempt to reverse discover if there are no remote
>> -                        * SEPs.
>> -                        */
>> -                       if (queue_isempty(setup->chan->seps))
>> -                               a2dp_discover(session, reverse_discover, NULL);
> Have you actually test these changes with read devices? I would be
> really surprised if this works because you are essentially changing
> the reverse discover to when we do initiate AVDTP_SetConfiguration
> rather when we receive, which shall never need a reverse discover to
> begin with since we are initiating we always perform a discover
> anyway, so that most likely is dead code that will never going to
> executed.
Apologies for my delayed response due to other commitments.
Here is the original logic:
  < AVDTP_SetConfiguration_Ind
 > AVDTP_Discover(reverse)
  < AVDTP_SetConfiguration_Rsp

Here is the modified logic:
  < AVDTP_SetConfiguration_Ind
  > AVDTP_SetConfiguration_Rsp
  < AVDTP_Discover (reverse)
The endpoint_setconf_cb above is called after AVDTP_SetConfiguration_Rsp

My test scenario is that my device acts as a sink, and when the
mobile phone connects to my device, a2dp reverse discover will
be triggered on the first connection (subsequent reconnections
will not send the reverse process due to caching) and they work
very well.

If there are any errors in the description above, please point them out.
I would greatly appreciate it.
> The real culprit here is that both commands and responses are stored
> in the session.in while we should probably have a session.cmd and
> session.rsp to be able to handle outstanding requests in each
> direction.
Very good suggestion, I will send new patch.
>
>> +               if (ret == 0)
>>                          return TRUE;
>> -               }
>>
>>                  setup_unref(setup);
>>                  setup->err = g_new(struct avdtp_error, 1);
>> --
>> 2.34.1
>>
>>
>
diff mbox series

Patch

diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c
index b43161a13..f4ef8aec2 100644
--- a/profiles/audio/a2dp.c
+++ b/profiles/audio/a2dp.c
@@ -586,6 +586,12 @@  done:
 	return FALSE;
 }
 
+static void reverse_discover(struct avdtp *session, GSList *seps, int err,
+			     void *user_data)
+{
+	DBG("err %d", err);
+}
+
 static void endpoint_setconf_cb(struct a2dp_setup *setup, gboolean ret)
 {
 	if (ret == FALSE) {
@@ -595,6 +601,13 @@  static void endpoint_setconf_cb(struct a2dp_setup *setup, gboolean ret)
 	}
 
 	auto_config(setup);
+
+	/* Attempt to reverse discover if there are no remote
+	 * SEPs.
+	 */
+	if (queue_isempty(setup->chan->seps))
+		a2dp_discover(setup->session, reverse_discover, NULL);
+
 	setup_unref(setup);
 }
 
@@ -634,12 +647,6 @@  static gboolean endpoint_match_codec_ind(struct avdtp *session,
 	return TRUE;
 }
 
-static void reverse_discover(struct avdtp *session, GSList *seps, int err,
-							void *user_data)
-{
-	DBG("err %d", err);
-}
-
 static gboolean endpoint_setconf_ind(struct avdtp *session,
 						struct avdtp_local_sep *sep,
 						struct avdtp_stream *stream,
@@ -695,14 +702,8 @@  static gboolean endpoint_setconf_ind(struct avdtp *session,
 						setup_ref(setup),
 						endpoint_setconf_cb,
 						a2dp_sep->user_data);
-		if (ret == 0) {
-			/* Attempt to reverse discover if there are no remote
-			 * SEPs.
-			 */
-			if (queue_isempty(setup->chan->seps))
-				a2dp_discover(session, reverse_discover, NULL);
+		if (ret == 0)
 			return TRUE;
-		}
 
 		setup_unref(setup);
 		setup->err = g_new(struct avdtp_error, 1);