Skip to content

Commit

Permalink
Merge tag 'for-net-2024-12-12' of git://git.kernel.org/pub/scm/linux/…
Browse files Browse the repository at this point in the history
…kernel/git/bluetooth/bluetooth

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/[email protected]
Signed-off-by: Jakub Kicinski <[email protected]>
  • Loading branch information
kuba-moo committed Dec 12, 2024
2 parents 36ff681 + b548f5e commit ad913df
Show file tree
Hide file tree
Showing 12 changed files with 215 additions and 154 deletions.
20 changes: 12 additions & 8 deletions drivers/bluetooth/btmtk.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -416,19 +417,22 @@ 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)) {
bt_dev_info(hdev, "Mediatek coredump end");
hci_devcd_complete(hdev);
}
if (complete) {
bt_dev_info(hdev, "Mediatek coredump end");
hci_devcd_complete(hdev);
}

break;
}
Expand Down
10 changes: 1 addition & 9 deletions include/net/bluetooth/bluetooth.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);

Expand Down
108 changes: 70 additions & 38 deletions include/net/bluetooth/hci_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -2017,68 +2016,103 @@ 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,
__u8 encrypt);
__u8 encrypt);
void (*key_change_cfm) (struct hci_conn *conn, __u8 status);
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;

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->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);
}

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) {
Expand All @@ -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)
Expand Down
10 changes: 3 additions & 7 deletions net/bluetooth/hci_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}
Expand All @@ -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;
}
Expand Down
33 changes: 11 additions & 22 deletions net/bluetooth/hci_event.c
Original file line number Diff line number Diff line change
Expand Up @@ -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();
conn->state = BT_CONNECTED;
set_bit(HCI_CONN_BIG_CREATED, &conn->flags);
hci_debugfs_create_conn(conn);
hci_conn_add_sysfs(conn);
hci_iso_setup_path(conn);
}

rcu_read_unlock();

if (!ev->status && !i)
/* If no BISes have been connected for the BIG,
* terminate. This is in case all bound connections
Expand Down
14 changes: 7 additions & 7 deletions net/bluetooth/hci_sock.c
Original file line number Diff line number Diff line change
Expand Up @@ -1926,7 +1926,7 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
}

static int hci_sock_setsockopt_old(struct socket *sock, int level, int optname,
sockptr_t optval, unsigned int len)
sockptr_t optval, unsigned int optlen)
{
struct hci_ufilter uf = { .opcode = 0 };
struct sock *sk = sock->sk;
Expand All @@ -1943,7 +1943,7 @@ static int hci_sock_setsockopt_old(struct socket *sock, int level, int optname,

switch (optname) {
case HCI_DATA_DIR:
err = bt_copy_from_sockptr(&opt, sizeof(opt), optval, len);
err = copy_safe_from_sockptr(&opt, sizeof(opt), optval, optlen);
if (err)
break;

Expand All @@ -1954,7 +1954,7 @@ static int hci_sock_setsockopt_old(struct socket *sock, int level, int optname,
break;

case HCI_TIME_STAMP:
err = bt_copy_from_sockptr(&opt, sizeof(opt), optval, len);
err = copy_safe_from_sockptr(&opt, sizeof(opt), optval, optlen);
if (err)
break;

Expand All @@ -1974,7 +1974,7 @@ static int hci_sock_setsockopt_old(struct socket *sock, int level, int optname,
uf.event_mask[1] = *((u32 *) f->event_mask + 1);
}

err = bt_copy_from_sockptr(&uf, sizeof(uf), optval, len);
err = copy_safe_from_sockptr(&uf, sizeof(uf), optval, optlen);
if (err)
break;

Expand Down Expand Up @@ -2005,7 +2005,7 @@ static int hci_sock_setsockopt_old(struct socket *sock, int level, int optname,
}

static int hci_sock_setsockopt(struct socket *sock, int level, int optname,
sockptr_t optval, unsigned int len)
sockptr_t optval, unsigned int optlen)
{
struct sock *sk = sock->sk;
int err = 0;
Expand All @@ -2015,7 +2015,7 @@ static int hci_sock_setsockopt(struct socket *sock, int level, int optname,

if (level == SOL_HCI)
return hci_sock_setsockopt_old(sock, level, optname, optval,
len);
optlen);

if (level != SOL_BLUETOOTH)
return -ENOPROTOOPT;
Expand All @@ -2035,7 +2035,7 @@ static int hci_sock_setsockopt(struct socket *sock, int level, int optname,
goto done;
}

err = bt_copy_from_sockptr(&opt, sizeof(opt), optval, len);
err = copy_safe_from_sockptr(&opt, sizeof(opt), optval, optlen);
if (err)
break;

Expand Down
Loading

0 comments on commit ad913df

Please sign in to comment.