Commit ad913dfd authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files
Luiz Augusto von Dentz says:

====================
bluetooth pull request for net:

 - SCO: Fix transparent voice setting
 - ISO: Locking fixes
 - hci_core: Fix sleeping function called from invalid context
 - hci_event: Fix using rcu_read_(un)lock while iterating
 - btmtk: avoid UAF in btmtk_process_coredump

* tag 'for-net-2024-12-12' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth:
  Bluetooth: btmtk: avoid UAF in btmtk_process_coredump
  Bluetooth: iso: Fix circular lock in iso_conn_big_sync
  Bluetooth: iso: Fix circular lock in iso_listen_bis
  Bluetooth: SCO: Add support for 16 bits transparent voice setting
  Bluetooth: iso: Fix recursive locking warning
  Bluetooth: iso: Always release hdev at the end of iso_listen_bis
  Bluetooth: hci_event: Fix using rcu_read_(un)lock while iterating
  Bluetooth: hci_core: Fix sleeping function called from invalid context
  Bluetooth: Improve setsockopt() handling of malformed user input
====================

Link: https://patch.msgid.link/20241212142806.2046274-1-luiz.dentz@gmail.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 36ff681d b548f5e9
Loading
Loading
Loading
Loading
+12 −8
Original line number Diff line number Diff line
@@ -395,6 +395,7 @@ int btmtk_process_coredump(struct hci_dev *hdev, struct sk_buff *skb)
{
	struct btmtk_data *data = hci_get_priv(hdev);
	int err;
	bool complete = false;

	if (!IS_ENABLED(CONFIG_DEV_COREDUMP)) {
		kfree_skb(skb);
@@ -416,16 +417,19 @@ int btmtk_process_coredump(struct hci_dev *hdev, struct sk_buff *skb)
		fallthrough;
	case HCI_DEVCOREDUMP_ACTIVE:
	default:
		/* Mediatek coredump data would be more than MTK_COREDUMP_NUM */
		if (data->cd_info.cnt >= MTK_COREDUMP_NUM &&
		    skb->len > MTK_COREDUMP_END_LEN)
			if (!memcmp((char *)&skb->data[skb->len - MTK_COREDUMP_END_LEN],
				    MTK_COREDUMP_END, MTK_COREDUMP_END_LEN - 1))
				complete = true;

		err = hci_devcd_append(hdev, skb);
		if (err < 0)
			break;
		data->cd_info.cnt++;

		/* Mediatek coredump data would be more than MTK_COREDUMP_NUM */
		if (data->cd_info.cnt > MTK_COREDUMP_NUM &&
		    skb->len > MTK_COREDUMP_END_LEN)
			if (!memcmp((char *)&skb->data[skb->len - MTK_COREDUMP_END_LEN],
				    MTK_COREDUMP_END, MTK_COREDUMP_END_LEN - 1)) {
		if (complete) {
			bt_dev_info(hdev, "Mediatek coredump end");
			hci_devcd_complete(hdev);
		}
+1 −9
Original line number Diff line number Diff line
@@ -123,6 +123,7 @@ struct bt_voice {

#define BT_VOICE_TRANSPARENT			0x0003
#define BT_VOICE_CVSD_16BIT			0x0060
#define BT_VOICE_TRANSPARENT_16BIT		0x0063

#define BT_SNDMTU		12
#define BT_RCVMTU		13
@@ -590,15 +591,6 @@ static inline struct sk_buff *bt_skb_sendmmsg(struct sock *sk,
	return skb;
}

static inline int bt_copy_from_sockptr(void *dst, size_t dst_size,
				       sockptr_t src, size_t src_size)
{
	if (dst_size > src_size)
		return -EINVAL;

	return copy_from_sockptr(dst, src, dst_size);
}

int bt_to_errno(u16 code);
__u8 bt_status(int err);

+70 −38
Original line number Diff line number Diff line
@@ -804,7 +804,6 @@ struct hci_conn_params {
extern struct list_head hci_dev_list;
extern struct list_head hci_cb_list;
extern rwlock_t hci_dev_list_lock;
extern struct mutex hci_cb_list_lock;

#define hci_dev_set_flag(hdev, nr)             set_bit((nr), (hdev)->dev_flags)
#define hci_dev_clear_flag(hdev, nr)           clear_bit((nr), (hdev)->dev_flags)
@@ -2017,6 +2016,7 @@ struct hci_cb {

	char *name;

	bool (*match)		(struct hci_conn *conn);
	void (*connect_cfm)	(struct hci_conn *conn, __u8 status);
	void (*disconn_cfm)	(struct hci_conn *conn, __u8 status);
	void (*security_cfm)	(struct hci_conn *conn, __u8 status,
@@ -2025,16 +2025,38 @@ struct hci_cb {
	void (*role_switch_cfm)	(struct hci_conn *conn, __u8 status, __u8 role);
};

static inline void hci_cb_lookup(struct hci_conn *conn, struct list_head *list)
{
	struct hci_cb *cb, *cpy;

	rcu_read_lock();
	list_for_each_entry_rcu(cb, &hci_cb_list, list) {
		if (cb->match && cb->match(conn)) {
			cpy = kmalloc(sizeof(*cpy), GFP_ATOMIC);
			if (!cpy)
				break;

			*cpy = *cb;
			INIT_LIST_HEAD(&cpy->list);
			list_add_rcu(&cpy->list, list);
		}
	}
	rcu_read_unlock();
}

static inline void hci_connect_cfm(struct hci_conn *conn, __u8 status)
{
	struct hci_cb *cb;
	struct list_head list;
	struct hci_cb *cb, *tmp;

	mutex_lock(&hci_cb_list_lock);
	list_for_each_entry(cb, &hci_cb_list, list) {
	INIT_LIST_HEAD(&list);
	hci_cb_lookup(conn, &list);

	list_for_each_entry_safe(cb, tmp, &list, list) {
		if (cb->connect_cfm)
			cb->connect_cfm(conn, status);
		kfree(cb);
	}
	mutex_unlock(&hci_cb_list_lock);

	if (conn->connect_cfm_cb)
		conn->connect_cfm_cb(conn, status);
@@ -2042,43 +2064,55 @@ static inline void hci_connect_cfm(struct hci_conn *conn, __u8 status)

static inline void hci_disconn_cfm(struct hci_conn *conn, __u8 reason)
{
	struct hci_cb *cb;
	struct list_head list;
	struct hci_cb *cb, *tmp;

	INIT_LIST_HEAD(&list);
	hci_cb_lookup(conn, &list);

	mutex_lock(&hci_cb_list_lock);
	list_for_each_entry(cb, &hci_cb_list, list) {
	list_for_each_entry_safe(cb, tmp, &list, list) {
		if (cb->disconn_cfm)
			cb->disconn_cfm(conn, reason);
		kfree(cb);
	}
	mutex_unlock(&hci_cb_list_lock);

	if (conn->disconn_cfm_cb)
		conn->disconn_cfm_cb(conn, reason);
}

static inline void hci_auth_cfm(struct hci_conn *conn, __u8 status)
static inline void hci_security_cfm(struct hci_conn *conn, __u8 status,
				    __u8 encrypt)
{
	struct hci_cb *cb;
	__u8 encrypt;

	if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags))
		return;
	struct list_head list;
	struct hci_cb *cb, *tmp;

	encrypt = test_bit(HCI_CONN_ENCRYPT, &conn->flags) ? 0x01 : 0x00;
	INIT_LIST_HEAD(&list);
	hci_cb_lookup(conn, &list);

	mutex_lock(&hci_cb_list_lock);
	list_for_each_entry(cb, &hci_cb_list, list) {
	list_for_each_entry_safe(cb, tmp, &list, list) {
		if (cb->security_cfm)
			cb->security_cfm(conn, status, encrypt);
		kfree(cb);
	}
	mutex_unlock(&hci_cb_list_lock);

	if (conn->security_cfm_cb)
		conn->security_cfm_cb(conn, status);
}

static inline void hci_auth_cfm(struct hci_conn *conn, __u8 status)
{
	__u8 encrypt;

	if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags))
		return;

	encrypt = test_bit(HCI_CONN_ENCRYPT, &conn->flags) ? 0x01 : 0x00;

	hci_security_cfm(conn, status, encrypt);
}

static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status)
{
	struct hci_cb *cb;
	__u8 encrypt;

	if (conn->state == BT_CONFIG) {
@@ -2105,40 +2139,38 @@ static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status)
			conn->sec_level = conn->pending_sec_level;
	}

	mutex_lock(&hci_cb_list_lock);
	list_for_each_entry(cb, &hci_cb_list, list) {
		if (cb->security_cfm)
			cb->security_cfm(conn, status, encrypt);
	}
	mutex_unlock(&hci_cb_list_lock);

	if (conn->security_cfm_cb)
		conn->security_cfm_cb(conn, status);
	hci_security_cfm(conn, status, encrypt);
}

static inline void hci_key_change_cfm(struct hci_conn *conn, __u8 status)
{
	struct hci_cb *cb;
	struct list_head list;
	struct hci_cb *cb, *tmp;

	INIT_LIST_HEAD(&list);
	hci_cb_lookup(conn, &list);

	mutex_lock(&hci_cb_list_lock);
	list_for_each_entry(cb, &hci_cb_list, list) {
	list_for_each_entry_safe(cb, tmp, &list, list) {
		if (cb->key_change_cfm)
			cb->key_change_cfm(conn, status);
		kfree(cb);
	}
	mutex_unlock(&hci_cb_list_lock);
}

static inline void hci_role_switch_cfm(struct hci_conn *conn, __u8 status,
								__u8 role)
{
	struct hci_cb *cb;
	struct list_head list;
	struct hci_cb *cb, *tmp;

	INIT_LIST_HEAD(&list);
	hci_cb_lookup(conn, &list);

	mutex_lock(&hci_cb_list_lock);
	list_for_each_entry(cb, &hci_cb_list, list) {
	list_for_each_entry_safe(cb, tmp, &list, list) {
		if (cb->role_switch_cfm)
			cb->role_switch_cfm(conn, status, role);
		kfree(cb);
	}
	mutex_unlock(&hci_cb_list_lock);
}

static inline bool hci_bdaddr_is_rpa(bdaddr_t *bdaddr, u8 addr_type)
+3 −7
Original line number Diff line number Diff line
@@ -57,7 +57,6 @@ DEFINE_RWLOCK(hci_dev_list_lock);

/* HCI callback list */
LIST_HEAD(hci_cb_list);
DEFINE_MUTEX(hci_cb_list_lock);

/* HCI ID Numbering */
static DEFINE_IDA(hci_index_ida);
@@ -2993,9 +2992,7 @@ int hci_register_cb(struct hci_cb *cb)
{
	BT_DBG("%p name %s", cb, cb->name);

	mutex_lock(&hci_cb_list_lock);
	list_add_tail(&cb->list, &hci_cb_list);
	mutex_unlock(&hci_cb_list_lock);
	list_add_tail_rcu(&cb->list, &hci_cb_list);

	return 0;
}
@@ -3005,9 +3002,8 @@ int hci_unregister_cb(struct hci_cb *cb)
{
	BT_DBG("%p name %s", cb, cb->name);

	mutex_lock(&hci_cb_list_lock);
	list_del(&cb->list);
	mutex_unlock(&hci_cb_list_lock);
	list_del_rcu(&cb->list);
	synchronize_rcu();

	return 0;
}
+11 −22
Original line number Diff line number Diff line
@@ -6870,38 +6870,27 @@ static void hci_le_create_big_complete_evt(struct hci_dev *hdev, void *data,
		return;

	hci_dev_lock(hdev);
	rcu_read_lock();

	/* Connect all BISes that are bound to the BIG */
	list_for_each_entry_rcu(conn, &hdev->conn_hash.list, list) {
		if (bacmp(&conn->dst, BDADDR_ANY) ||
		    conn->type != ISO_LINK ||
		    conn->iso_qos.bcast.big != ev->handle)
	while ((conn = hci_conn_hash_lookup_big_state(hdev, ev->handle,
						      BT_BOUND))) {
		if (ev->status) {
			hci_connect_cfm(conn, ev->status);
			hci_conn_del(conn);
			continue;
		}

		if (hci_conn_set_handle(conn,
					__le16_to_cpu(ev->bis_handle[i++])))
			continue;

		if (!ev->status) {
		conn->state = BT_CONNECTED;
		set_bit(HCI_CONN_BIG_CREATED, &conn->flags);
			rcu_read_unlock();
		hci_debugfs_create_conn(conn);
		hci_conn_add_sysfs(conn);
		hci_iso_setup_path(conn);
			rcu_read_lock();
			continue;
	}

		hci_connect_cfm(conn, ev->status);
		rcu_read_unlock();
		hci_conn_del(conn);
		rcu_read_lock();
	}

	rcu_read_unlock();

	if (!ev->status && !i)
		/* If no BISes have been connected for the BIG,
		 * terminate. This is in case all bound connections
Loading