mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-12-29 17:25:38 +00:00
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 -----BEGIN PGP SIGNATURE----- iQJNBAABCAA3FiEE7E6oRXp8w05ovYr/9JCA4xAyCykFAmda8pEZHGx1aXoudm9u LmRlbnR6QGludGVsLmNvbQAKCRD0kIDjEDILKTf/D/sFRQb6FTdttMV934GDH+1W DCS2prkqGUi6KGTxFFpT1rKafR0+h1osta1yvtM7h1tbXqpjAsv06ksFEP1vsXgl Kw1gTn/cEGlK6KQ+oX4ObEBWXtlnYparl86m+OJY8xVc2GEA7GGdbwwsuDLeEv8H JVoMN2FLb/Io3VHYBO595xh0BK4K61gM0zh/nxwWNxaOH1AzCwoh4oVEyCtlwQpn 5okDjfawHUfU9T/VlzL0TwlXP/Rwi6afvaJ+vt7N6wqgrJ51Q1cXf00kTqKWD2mQ vsRDjIMn4YYSjH3X27i6xif3jFQ4z1fjto0N4PjE5IgNe+VUtEiQsT2h5NnxA/mt MWNx9EvUYXLrOkVot91FPJqYTNjGpKr4EBxRdFW1MW3sJX4rCGDVpW7gJF3fG44+ iEFHaZpJ8XlyyT7gD6BffkePv5iicbJtmgk++Dx1Z0ekkvvjA4RkQHFMTwWh2a+Y s+1qS8rfhmyWf8IdUVCAxbrOW9nXRNFEaRh2ooqzEI/ycXtogzmoAI8g/xnZ7VHg H2sSOyO4HfsH/nHHkPvaIQL+8pt8EWYIuGUgMhNFuRIYpYvcoDNNpVuhKrvk9Qp+ SsmmIw/ov5M9ucEE24fTf3jaSwin+fORUOMye31yF6tmxJOuTFnMyil8kOPovlu3 IdxLOmCDxYXZCYNZslc65w== =1Po/ -----END PGP SIGNATURE----- Merge tag 'for-net-2024-12-12' of git://git.kernel.org/pub/scm/linux/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/20241212142806.2046274-1-luiz.dentz@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
ad913dfd8b
@ -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,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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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,24 +2016,47 @@ 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;
|
||||
|
||||
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,22 +2064,43 @@ 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;
|
||||
|
||||
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->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_security_cfm(struct hci_conn *conn, __u8 status,
|
||||
__u8 encrypt)
|
||||
{
|
||||
struct list_head list;
|
||||
struct hci_cb *cb, *tmp;
|
||||
|
||||
INIT_LIST_HEAD(&list);
|
||||
hci_cb_lookup(conn, &list);
|
||||
|
||||
list_for_each_entry_safe(cb, tmp, &list, list) {
|
||||
if (cb->security_cfm)
|
||||
cb->security_cfm(conn, status, encrypt);
|
||||
kfree(cb);
|
||||
}
|
||||
|
||||
if (conn->security_cfm_cb)
|
||||
conn->security_cfm_cb(conn, status);
|
||||
}
|
||||
|
||||
static inline void hci_auth_cfm(struct hci_conn *conn, __u8 status)
|
||||
{
|
||||
struct hci_cb *cb;
|
||||
__u8 encrypt;
|
||||
|
||||
if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags))
|
||||
@ -2065,20 +2108,11 @@ static inline void hci_auth_cfm(struct hci_conn *conn, __u8 status)
|
||||
|
||||
encrypt = test_bit(HCI_CONN_ENCRYPT, &conn->flags) ? 0x01 : 0x00;
|
||||
|
||||
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_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;
|
||||
|
||||
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->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;
|
||||
|
||||
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->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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
@ -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;
|
||||
@ -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;
|
||||
|
||||
|
@ -1129,6 +1129,7 @@ static int iso_listen_bis(struct sock *sk)
|
||||
return -EHOSTUNREACH;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
lock_sock(sk);
|
||||
|
||||
/* Fail if user set invalid QoS */
|
||||
if (iso_pi(sk)->qos_user_set && !check_bcast_qos(&iso_pi(sk)->qos)) {
|
||||
@ -1158,10 +1159,10 @@ static int iso_listen_bis(struct sock *sk)
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
hci_dev_put(hdev);
|
||||
|
||||
unlock:
|
||||
release_sock(sk);
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_put(hdev);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -1188,6 +1189,7 @@ static int iso_sock_listen(struct socket *sock, int backlog)
|
||||
|
||||
BT_DBG("sk %p backlog %d", sk, backlog);
|
||||
|
||||
sock_hold(sk);
|
||||
lock_sock(sk);
|
||||
|
||||
if (sk->sk_state != BT_BOUND) {
|
||||
@ -1200,10 +1202,16 @@ static int iso_sock_listen(struct socket *sock, int backlog)
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!bacmp(&iso_pi(sk)->dst, BDADDR_ANY))
|
||||
if (!bacmp(&iso_pi(sk)->dst, BDADDR_ANY)) {
|
||||
err = iso_listen_cis(sk);
|
||||
else
|
||||
} else {
|
||||
/* Drop sock lock to avoid potential
|
||||
* deadlock with the hdev lock.
|
||||
*/
|
||||
release_sock(sk);
|
||||
err = iso_listen_bis(sk);
|
||||
lock_sock(sk);
|
||||
}
|
||||
|
||||
if (err)
|
||||
goto done;
|
||||
@ -1215,6 +1223,7 @@ static int iso_sock_listen(struct socket *sock, int backlog)
|
||||
|
||||
done:
|
||||
release_sock(sk);
|
||||
sock_put(sk);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -1226,7 +1235,11 @@ static int iso_sock_accept(struct socket *sock, struct socket *newsock,
|
||||
long timeo;
|
||||
int err = 0;
|
||||
|
||||
lock_sock(sk);
|
||||
/* Use explicit nested locking to avoid lockdep warnings generated
|
||||
* because the parent socket and the child socket are locked on the
|
||||
* same thread.
|
||||
*/
|
||||
lock_sock_nested(sk, SINGLE_DEPTH_NESTING);
|
||||
|
||||
timeo = sock_rcvtimeo(sk, arg->flags & O_NONBLOCK);
|
||||
|
||||
@ -1257,7 +1270,7 @@ static int iso_sock_accept(struct socket *sock, struct socket *newsock,
|
||||
release_sock(sk);
|
||||
|
||||
timeo = wait_woken(&wait, TASK_INTERRUPTIBLE, timeo);
|
||||
lock_sock(sk);
|
||||
lock_sock_nested(sk, SINGLE_DEPTH_NESTING);
|
||||
}
|
||||
remove_wait_queue(sk_sleep(sk), &wait);
|
||||
|
||||
@ -1398,6 +1411,7 @@ static void iso_conn_big_sync(struct sock *sk)
|
||||
* change.
|
||||
*/
|
||||
hci_dev_lock(hdev);
|
||||
lock_sock(sk);
|
||||
|
||||
if (!test_and_set_bit(BT_SK_BIG_SYNC, &iso_pi(sk)->flags)) {
|
||||
err = hci_le_big_create_sync(hdev, iso_pi(sk)->conn->hcon,
|
||||
@ -1410,6 +1424,7 @@ static void iso_conn_big_sync(struct sock *sk)
|
||||
err);
|
||||
}
|
||||
|
||||
release_sock(sk);
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
@ -1418,39 +1433,57 @@ static int iso_sock_recvmsg(struct socket *sock, struct msghdr *msg,
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
struct iso_pinfo *pi = iso_pi(sk);
|
||||
bool early_ret = false;
|
||||
int err = 0;
|
||||
|
||||
BT_DBG("sk %p", sk);
|
||||
|
||||
if (test_and_clear_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) {
|
||||
sock_hold(sk);
|
||||
lock_sock(sk);
|
||||
|
||||
switch (sk->sk_state) {
|
||||
case BT_CONNECT2:
|
||||
if (test_bit(BT_SK_PA_SYNC, &pi->flags)) {
|
||||
release_sock(sk);
|
||||
iso_conn_big_sync(sk);
|
||||
lock_sock(sk);
|
||||
|
||||
sk->sk_state = BT_LISTEN;
|
||||
} else {
|
||||
iso_conn_defer_accept(pi->conn->hcon);
|
||||
sk->sk_state = BT_CONFIG;
|
||||
}
|
||||
release_sock(sk);
|
||||
return 0;
|
||||
|
||||
early_ret = true;
|
||||
break;
|
||||
case BT_CONNECTED:
|
||||
if (test_bit(BT_SK_PA_SYNC, &iso_pi(sk)->flags)) {
|
||||
iso_conn_big_sync(sk);
|
||||
sk->sk_state = BT_LISTEN;
|
||||
release_sock(sk);
|
||||
return 0;
|
||||
iso_conn_big_sync(sk);
|
||||
lock_sock(sk);
|
||||
|
||||
sk->sk_state = BT_LISTEN;
|
||||
early_ret = true;
|
||||
}
|
||||
|
||||
release_sock(sk);
|
||||
break;
|
||||
case BT_CONNECT:
|
||||
release_sock(sk);
|
||||
return iso_connect_cis(sk);
|
||||
err = iso_connect_cis(sk);
|
||||
lock_sock(sk);
|
||||
|
||||
early_ret = true;
|
||||
break;
|
||||
default:
|
||||
release_sock(sk);
|
||||
break;
|
||||
}
|
||||
|
||||
release_sock(sk);
|
||||
sock_put(sk);
|
||||
|
||||
if (early_ret)
|
||||
return err;
|
||||
}
|
||||
|
||||
return bt_sock_recvmsg(sock, msg, len, flags);
|
||||
@ -1566,7 +1599,7 @@ static int iso_sock_setsockopt(struct socket *sock, int level, int optname,
|
||||
break;
|
||||
}
|
||||
|
||||
err = bt_copy_from_sockptr(&opt, sizeof(opt), optval, optlen);
|
||||
err = copy_safe_from_sockptr(&opt, sizeof(opt), optval, optlen);
|
||||
if (err)
|
||||
break;
|
||||
|
||||
@ -1577,7 +1610,7 @@ static int iso_sock_setsockopt(struct socket *sock, int level, int optname,
|
||||
break;
|
||||
|
||||
case BT_PKT_STATUS:
|
||||
err = bt_copy_from_sockptr(&opt, sizeof(opt), optval, optlen);
|
||||
err = copy_safe_from_sockptr(&opt, sizeof(opt), optval, optlen);
|
||||
if (err)
|
||||
break;
|
||||
|
||||
@ -1596,7 +1629,7 @@ static int iso_sock_setsockopt(struct socket *sock, int level, int optname,
|
||||
break;
|
||||
}
|
||||
|
||||
err = bt_copy_from_sockptr(&qos, sizeof(qos), optval, optlen);
|
||||
err = copy_safe_from_sockptr(&qos, sizeof(qos), optval, optlen);
|
||||
if (err)
|
||||
break;
|
||||
|
||||
@ -1617,8 +1650,8 @@ static int iso_sock_setsockopt(struct socket *sock, int level, int optname,
|
||||
break;
|
||||
}
|
||||
|
||||
err = bt_copy_from_sockptr(iso_pi(sk)->base, optlen, optval,
|
||||
optlen);
|
||||
err = copy_safe_from_sockptr(iso_pi(sk)->base, optlen, optval,
|
||||
optlen);
|
||||
if (err)
|
||||
break;
|
||||
|
||||
@ -2118,6 +2151,11 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
|
||||
return HCI_LM_ACCEPT;
|
||||
}
|
||||
|
||||
static bool iso_match(struct hci_conn *hcon)
|
||||
{
|
||||
return hcon->type == ISO_LINK || hcon->type == LE_LINK;
|
||||
}
|
||||
|
||||
static void iso_connect_cfm(struct hci_conn *hcon, __u8 status)
|
||||
{
|
||||
if (hcon->type != ISO_LINK) {
|
||||
@ -2299,6 +2337,7 @@ void iso_recv(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
|
||||
|
||||
static struct hci_cb iso_cb = {
|
||||
.name = "ISO",
|
||||
.match = iso_match,
|
||||
.connect_cfm = iso_connect_cfm,
|
||||
.disconn_cfm = iso_disconn_cfm,
|
||||
};
|
||||
|
@ -7217,6 +7217,11 @@ static struct l2cap_chan *l2cap_global_fixed_chan(struct l2cap_chan *c,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool l2cap_match(struct hci_conn *hcon)
|
||||
{
|
||||
return hcon->type == ACL_LINK || hcon->type == LE_LINK;
|
||||
}
|
||||
|
||||
static void l2cap_connect_cfm(struct hci_conn *hcon, u8 status)
|
||||
{
|
||||
struct hci_dev *hdev = hcon->hdev;
|
||||
@ -7224,9 +7229,6 @@ static void l2cap_connect_cfm(struct hci_conn *hcon, u8 status)
|
||||
struct l2cap_chan *pchan;
|
||||
u8 dst_type;
|
||||
|
||||
if (hcon->type != ACL_LINK && hcon->type != LE_LINK)
|
||||
return;
|
||||
|
||||
BT_DBG("hcon %p bdaddr %pMR status %d", hcon, &hcon->dst, status);
|
||||
|
||||
if (status) {
|
||||
@ -7291,9 +7293,6 @@ int l2cap_disconn_ind(struct hci_conn *hcon)
|
||||
|
||||
static void l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason)
|
||||
{
|
||||
if (hcon->type != ACL_LINK && hcon->type != LE_LINK)
|
||||
return;
|
||||
|
||||
BT_DBG("hcon %p reason %d", hcon, reason);
|
||||
|
||||
l2cap_conn_del(hcon, bt_to_errno(reason));
|
||||
@ -7572,6 +7571,7 @@ void l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
|
||||
|
||||
static struct hci_cb l2cap_cb = {
|
||||
.name = "L2CAP",
|
||||
.match = l2cap_match,
|
||||
.connect_cfm = l2cap_connect_cfm,
|
||||
.disconn_cfm = l2cap_disconn_cfm,
|
||||
.security_cfm = l2cap_security_cfm,
|
||||
|
@ -755,7 +755,8 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname,
|
||||
opts.max_tx = chan->max_tx;
|
||||
opts.txwin_size = chan->tx_win;
|
||||
|
||||
err = bt_copy_from_sockptr(&opts, sizeof(opts), optval, optlen);
|
||||
err = copy_safe_from_sockptr(&opts, sizeof(opts), optval,
|
||||
optlen);
|
||||
if (err)
|
||||
break;
|
||||
|
||||
@ -800,7 +801,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname,
|
||||
break;
|
||||
|
||||
case L2CAP_LM:
|
||||
err = bt_copy_from_sockptr(&opt, sizeof(opt), optval, optlen);
|
||||
err = copy_safe_from_sockptr(&opt, sizeof(opt), optval, optlen);
|
||||
if (err)
|
||||
break;
|
||||
|
||||
@ -909,7 +910,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
|
||||
|
||||
sec.level = BT_SECURITY_LOW;
|
||||
|
||||
err = bt_copy_from_sockptr(&sec, sizeof(sec), optval, optlen);
|
||||
err = copy_safe_from_sockptr(&sec, sizeof(sec), optval, optlen);
|
||||
if (err)
|
||||
break;
|
||||
|
||||
@ -956,7 +957,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
|
||||
break;
|
||||
}
|
||||
|
||||
err = bt_copy_from_sockptr(&opt, sizeof(opt), optval, optlen);
|
||||
err = copy_safe_from_sockptr(&opt, sizeof(opt), optval, optlen);
|
||||
if (err)
|
||||
break;
|
||||
|
||||
@ -970,7 +971,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
|
||||
break;
|
||||
|
||||
case BT_FLUSHABLE:
|
||||
err = bt_copy_from_sockptr(&opt, sizeof(opt), optval, optlen);
|
||||
err = copy_safe_from_sockptr(&opt, sizeof(opt), optval, optlen);
|
||||
if (err)
|
||||
break;
|
||||
|
||||
@ -1004,7 +1005,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
|
||||
|
||||
pwr.force_active = BT_POWER_FORCE_ACTIVE_ON;
|
||||
|
||||
err = bt_copy_from_sockptr(&pwr, sizeof(pwr), optval, optlen);
|
||||
err = copy_safe_from_sockptr(&pwr, sizeof(pwr), optval, optlen);
|
||||
if (err)
|
||||
break;
|
||||
|
||||
@ -1015,7 +1016,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
|
||||
break;
|
||||
|
||||
case BT_CHANNEL_POLICY:
|
||||
err = bt_copy_from_sockptr(&opt, sizeof(opt), optval, optlen);
|
||||
err = copy_safe_from_sockptr(&opt, sizeof(opt), optval, optlen);
|
||||
if (err)
|
||||
break;
|
||||
|
||||
@ -1046,7 +1047,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
|
||||
break;
|
||||
}
|
||||
|
||||
err = bt_copy_from_sockptr(&mtu, sizeof(mtu), optval, optlen);
|
||||
err = copy_safe_from_sockptr(&mtu, sizeof(mtu), optval, optlen);
|
||||
if (err)
|
||||
break;
|
||||
|
||||
@ -1076,7 +1077,8 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
|
||||
break;
|
||||
}
|
||||
|
||||
err = bt_copy_from_sockptr(&mode, sizeof(mode), optval, optlen);
|
||||
err = copy_safe_from_sockptr(&mode, sizeof(mode), optval,
|
||||
optlen);
|
||||
if (err)
|
||||
break;
|
||||
|
||||
|
@ -2134,6 +2134,11 @@ static int rfcomm_run(void *unused)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool rfcomm_match(struct hci_conn *hcon)
|
||||
{
|
||||
return hcon->type == ACL_LINK;
|
||||
}
|
||||
|
||||
static void rfcomm_security_cfm(struct hci_conn *conn, u8 status, u8 encrypt)
|
||||
{
|
||||
struct rfcomm_session *s;
|
||||
@ -2180,6 +2185,7 @@ static void rfcomm_security_cfm(struct hci_conn *conn, u8 status, u8 encrypt)
|
||||
|
||||
static struct hci_cb rfcomm_cb = {
|
||||
.name = "RFCOMM",
|
||||
.match = rfcomm_match,
|
||||
.security_cfm = rfcomm_security_cfm
|
||||
};
|
||||
|
||||
|
@ -629,10 +629,9 @@ static int rfcomm_sock_setsockopt_old(struct socket *sock, int optname,
|
||||
|
||||
switch (optname) {
|
||||
case RFCOMM_LM:
|
||||
if (bt_copy_from_sockptr(&opt, sizeof(opt), optval, optlen)) {
|
||||
err = -EFAULT;
|
||||
err = copy_safe_from_sockptr(&opt, sizeof(opt), optval, optlen);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
|
||||
if (opt & RFCOMM_LM_FIPS) {
|
||||
err = -EINVAL;
|
||||
@ -685,7 +684,7 @@ static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname,
|
||||
|
||||
sec.level = BT_SECURITY_LOW;
|
||||
|
||||
err = bt_copy_from_sockptr(&sec, sizeof(sec), optval, optlen);
|
||||
err = copy_safe_from_sockptr(&sec, sizeof(sec), optval, optlen);
|
||||
if (err)
|
||||
break;
|
||||
|
||||
@ -703,7 +702,7 @@ static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname,
|
||||
break;
|
||||
}
|
||||
|
||||
err = bt_copy_from_sockptr(&opt, sizeof(opt), optval, optlen);
|
||||
err = copy_safe_from_sockptr(&opt, sizeof(opt), optval, optlen);
|
||||
if (err)
|
||||
break;
|
||||
|
||||
|
@ -319,10 +319,13 @@ static int sco_connect(struct sock *sk)
|
||||
else
|
||||
type = SCO_LINK;
|
||||
|
||||
if (sco_pi(sk)->setting == BT_VOICE_TRANSPARENT &&
|
||||
(!lmp_transp_capable(hdev) || !lmp_esco_capable(hdev))) {
|
||||
err = -EOPNOTSUPP;
|
||||
goto unlock;
|
||||
switch (sco_pi(sk)->setting & SCO_AIRMODE_MASK) {
|
||||
case SCO_AIRMODE_TRANSP:
|
||||
if (!lmp_transp_capable(hdev) || !lmp_esco_capable(hdev)) {
|
||||
err = -EOPNOTSUPP;
|
||||
goto unlock;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
hcon = hci_connect_sco(hdev, type, &sco_pi(sk)->dst,
|
||||
@ -896,7 +899,7 @@ static int sco_sock_setsockopt(struct socket *sock, int level, int optname,
|
||||
break;
|
||||
}
|
||||
|
||||
err = bt_copy_from_sockptr(&opt, sizeof(opt), optval, optlen);
|
||||
err = copy_safe_from_sockptr(&opt, sizeof(opt), optval, optlen);
|
||||
if (err)
|
||||
break;
|
||||
|
||||
@ -915,18 +918,11 @@ static int sco_sock_setsockopt(struct socket *sock, int level, int optname,
|
||||
|
||||
voice.setting = sco_pi(sk)->setting;
|
||||
|
||||
err = bt_copy_from_sockptr(&voice, sizeof(voice), optval,
|
||||
optlen);
|
||||
err = copy_safe_from_sockptr(&voice, sizeof(voice), optval,
|
||||
optlen);
|
||||
if (err)
|
||||
break;
|
||||
|
||||
/* Explicitly check for these values */
|
||||
if (voice.setting != BT_VOICE_TRANSPARENT &&
|
||||
voice.setting != BT_VOICE_CVSD_16BIT) {
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
sco_pi(sk)->setting = voice.setting;
|
||||
hdev = hci_get_route(&sco_pi(sk)->dst, &sco_pi(sk)->src,
|
||||
BDADDR_BREDR);
|
||||
@ -934,14 +930,19 @@ static int sco_sock_setsockopt(struct socket *sock, int level, int optname,
|
||||
err = -EBADFD;
|
||||
break;
|
||||
}
|
||||
if (enhanced_sync_conn_capable(hdev) &&
|
||||
voice.setting == BT_VOICE_TRANSPARENT)
|
||||
sco_pi(sk)->codec.id = BT_CODEC_TRANSPARENT;
|
||||
|
||||
switch (sco_pi(sk)->setting & SCO_AIRMODE_MASK) {
|
||||
case SCO_AIRMODE_TRANSP:
|
||||
if (enhanced_sync_conn_capable(hdev))
|
||||
sco_pi(sk)->codec.id = BT_CODEC_TRANSPARENT;
|
||||
break;
|
||||
}
|
||||
|
||||
hci_dev_put(hdev);
|
||||
break;
|
||||
|
||||
case BT_PKT_STATUS:
|
||||
err = bt_copy_from_sockptr(&opt, sizeof(opt), optval, optlen);
|
||||
err = copy_safe_from_sockptr(&opt, sizeof(opt), optval, optlen);
|
||||
if (err)
|
||||
break;
|
||||
|
||||
@ -984,7 +985,8 @@ static int sco_sock_setsockopt(struct socket *sock, int level, int optname,
|
||||
break;
|
||||
}
|
||||
|
||||
err = bt_copy_from_sockptr(buffer, optlen, optval, optlen);
|
||||
err = copy_struct_from_sockptr(buffer, sizeof(buffer), optval,
|
||||
optlen);
|
||||
if (err) {
|
||||
hci_dev_put(hdev);
|
||||
break;
|
||||
@ -1396,11 +1398,13 @@ int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
|
||||
return lm;
|
||||
}
|
||||
|
||||
static bool sco_match(struct hci_conn *hcon)
|
||||
{
|
||||
return hcon->type == SCO_LINK || hcon->type == ESCO_LINK;
|
||||
}
|
||||
|
||||
static void sco_connect_cfm(struct hci_conn *hcon, __u8 status)
|
||||
{
|
||||
if (hcon->type != SCO_LINK && hcon->type != ESCO_LINK)
|
||||
return;
|
||||
|
||||
BT_DBG("hcon %p bdaddr %pMR status %u", hcon, &hcon->dst, status);
|
||||
|
||||
if (!status) {
|
||||
@ -1415,9 +1419,6 @@ static void sco_connect_cfm(struct hci_conn *hcon, __u8 status)
|
||||
|
||||
static void sco_disconn_cfm(struct hci_conn *hcon, __u8 reason)
|
||||
{
|
||||
if (hcon->type != SCO_LINK && hcon->type != ESCO_LINK)
|
||||
return;
|
||||
|
||||
BT_DBG("hcon %p reason %d", hcon, reason);
|
||||
|
||||
sco_conn_del(hcon, bt_to_errno(reason));
|
||||
@ -1443,6 +1444,7 @@ void sco_recv_scodata(struct hci_conn *hcon, struct sk_buff *skb)
|
||||
|
||||
static struct hci_cb sco_cb = {
|
||||
.name = "SCO",
|
||||
.match = sco_match,
|
||||
.connect_cfm = sco_connect_cfm,
|
||||
.disconn_cfm = sco_disconn_cfm,
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user