Commit 6c3ea155 authored by Luiz Augusto von Dentz's avatar Luiz Augusto von Dentz
Browse files

Bluetooth: L2CAP: Fix not tracking outstanding TX ident

This attempts to proper track outstanding request by using struct ida
and allocating from it in l2cap_get_ident using ida_alloc_range which
would reuse ids as they are free, then upon completion release
the id using ida_free.

This fixes the qualification test case L2CAP/COS/CED/BI-29-C which
attempts to check if the host stack is able to work after 256 attempts
to connect which requires Ident field to use the full range of possible
values in order to pass the test.

Link: https://github.com/bluez/bluez/issues/1829


Signed-off-by: default avatarLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Reviewed-by: default avatarPaul Menzel <pmenzel@molgen.mpg.de>
parent 4db19bfd
Loading
Loading
Loading
Loading
+1 −2
Original line number Diff line number Diff line
@@ -655,8 +655,7 @@ struct l2cap_conn {

	struct sk_buff		*rx_skb;
	__u32			rx_len;
	__u8			tx_ident;
	struct mutex		ident_lock;
	struct ida		tx_ida;

	struct sk_buff_head	pending_rx;
	struct work_struct	pending_rx_work;
+32 −14
Original line number Diff line number Diff line
@@ -924,26 +924,18 @@ int l2cap_chan_check_security(struct l2cap_chan *chan, bool initiator)
				 initiator);
}

static u8 l2cap_get_ident(struct l2cap_conn *conn)
static int l2cap_get_ident(struct l2cap_conn *conn)
{
	u8 id;
	/* LE link does not support tools like l2ping so use the full range */
	if (conn->hcon->type == LE_LINK)
		return ida_alloc_range(&conn->tx_ida, 1, 255, GFP_ATOMIC);

	/* Get next available identificator.
	 *    1 - 128 are used by kernel.
	 *  129 - 199 are reserved.
	 *  200 - 254 are used by utilities like l2ping, etc.
	 */

	mutex_lock(&conn->ident_lock);

	if (++conn->tx_ident > 128)
		conn->tx_ident = 1;

	id = conn->tx_ident;

	mutex_unlock(&conn->ident_lock);

	return id;
	return ida_alloc_range(&conn->tx_ida, 1, 128, GFP_ATOMIC);
}

static void l2cap_send_acl(struct l2cap_conn *conn, struct sk_buff *skb,
@@ -1773,6 +1765,8 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err)
	if (work_pending(&conn->pending_rx_work))
		cancel_work_sync(&conn->pending_rx_work);

	ida_destroy(&conn->tx_ida);

	cancel_delayed_work_sync(&conn->id_addr_timer);

	l2cap_unregister_all_users(conn);
@@ -4782,12 +4776,34 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn,
	return err;
}

static void l2cap_put_ident(struct l2cap_conn *conn, u8 code, u8 id)
{
	switch (code) {
	case L2CAP_COMMAND_REJ:
	case L2CAP_CONN_RSP:
	case L2CAP_CONF_RSP:
	case L2CAP_DISCONN_RSP:
	case L2CAP_ECHO_RSP:
	case L2CAP_INFO_RSP:
	case L2CAP_CONN_PARAM_UPDATE_RSP:
	case L2CAP_ECRED_CONN_RSP:
	case L2CAP_ECRED_RECONF_RSP:
		/* First do a lookup since the remote may send bogus ids that
		 * would make ida_free to generate warnings.
		 */
		if (ida_find_first_range(&conn->tx_ida, id, id) >= 0)
			ida_free(&conn->tx_ida, id);
	}
}

static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
				      struct l2cap_cmd_hdr *cmd, u16 cmd_len,
				      u8 *data)
{
	int err = 0;

	l2cap_put_ident(conn, cmd->code, cmd->ident);

	switch (cmd->code) {
	case L2CAP_COMMAND_REJ:
		l2cap_command_rej(conn, cmd, cmd_len, data);
@@ -5419,6 +5435,8 @@ static inline int l2cap_le_sig_cmd(struct l2cap_conn *conn,
{
	int err = 0;

	l2cap_put_ident(conn, cmd->code, cmd->ident);

	switch (cmd->code) {
	case L2CAP_COMMAND_REJ:
		l2cap_le_command_rej(conn, cmd, cmd_len, data);
@@ -6907,13 +6925,13 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon)
	     hci_dev_test_flag(hcon->hdev, HCI_FORCE_BREDR_SMP)))
		conn->local_fixed_chan |= L2CAP_FC_SMP_BREDR;

	mutex_init(&conn->ident_lock);
	mutex_init(&conn->lock);

	INIT_LIST_HEAD(&conn->chan_l);
	INIT_LIST_HEAD(&conn->users);

	INIT_DELAYED_WORK(&conn->info_timer, l2cap_info_timeout);
	ida_init(&conn->tx_ida);

	skb_queue_head_init(&conn->pending_rx);
	INIT_WORK(&conn->pending_rx_work, process_pending_rx);