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:
Jakub Kicinski 2024-12-12 07:10:39 -08:00
commit ad913dfd8b
12 changed files with 216 additions and 155 deletions

View File

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

View File

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

View File

@ -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)

View File

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

View File

@ -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

View File

@ -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;

View File

@ -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,
};

View File

@ -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,

View File

@ -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;

View File

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

View File

@ -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;

View File

@ -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,
};