Commit 160c1317 authored by Jakub Kicinski's avatar Jakub Kicinski

Merge tag 'for-net-2023-04-10' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth

Luiz Augusto von Dentz says:

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

 - Fix not setting Dath Path for broadcast sink
 - Fix not cleaning up on LE Connection failure
 - SCO: Fix possible circular locking dependency
 - L2CAP: Fix use-after-free in l2cap_disconnect_{req,rsp}
 - Fix race condition in hidp_session_thread
 - btbcm: Fix logic error in forming the board name
 - btbcm: Fix use after free in btsdio_remove

* tag 'for-net-2023-04-10' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth:
  Bluetooth: L2CAP: Fix use-after-free in l2cap_disconnect_{req,rsp}
  Bluetooth: Set ISO Data Path on broadcast sink
  Bluetooth: hci_conn: Fix possible UAF
  Bluetooth: SCO: Fix possible circular locking dependency sco_sock_getsockopt
  Bluetooth: SCO: Fix possible circular locking dependency on sco_connect_cfm
  bluetooth: btbcm: Fix logic error in forming the board name.
  Bluetooth: btsdio: fix use after free bug in btsdio_remove due to race condition
  Bluetooth: Fix race condition in hidp_session_thread
  Bluetooth: Fix printing errors if LE Connection times out
  Bluetooth: hci_conn: Fix not cleaning up on LE Connection failure
====================

Link: https://lore.kernel.org/r/20230410172718.4067798-1-luiz.dentz@gmail.comSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents a4506722 a2a9339e
...@@ -511,7 +511,7 @@ static const char *btbcm_get_board_name(struct device *dev) ...@@ -511,7 +511,7 @@ static const char *btbcm_get_board_name(struct device *dev)
len = strlen(tmp) + 1; len = strlen(tmp) + 1;
board_type = devm_kzalloc(dev, len, GFP_KERNEL); board_type = devm_kzalloc(dev, len, GFP_KERNEL);
strscpy(board_type, tmp, len); strscpy(board_type, tmp, len);
for (i = 0; i < board_type[i]; i++) { for (i = 0; i < len; i++) {
if (board_type[i] == '/') if (board_type[i] == '/')
board_type[i] = '-'; board_type[i] = '-';
} }
......
...@@ -358,6 +358,7 @@ static void btsdio_remove(struct sdio_func *func) ...@@ -358,6 +358,7 @@ static void btsdio_remove(struct sdio_func *func)
if (!data) if (!data)
return; return;
cancel_work_sync(&data->work);
hdev = data->hdev; hdev = data->hdev;
sdio_set_drvdata(func, NULL); sdio_set_drvdata(func, NULL);
......
...@@ -954,6 +954,7 @@ enum { ...@@ -954,6 +954,7 @@ enum {
HCI_CONN_STK_ENCRYPT, HCI_CONN_STK_ENCRYPT,
HCI_CONN_AUTH_INITIATOR, HCI_CONN_AUTH_INITIATOR,
HCI_CONN_DROP, HCI_CONN_DROP,
HCI_CONN_CANCEL,
HCI_CONN_PARAM_REMOVAL_PEND, HCI_CONN_PARAM_REMOVAL_PEND,
HCI_CONN_NEW_LINK_KEY, HCI_CONN_NEW_LINK_KEY,
HCI_CONN_SCANNING, HCI_CONN_SCANNING,
......
...@@ -68,7 +68,7 @@ static const struct sco_param esco_param_msbc[] = { ...@@ -68,7 +68,7 @@ static const struct sco_param esco_param_msbc[] = {
}; };
/* This function requires the caller holds hdev->lock */ /* This function requires the caller holds hdev->lock */
static void hci_connect_le_scan_cleanup(struct hci_conn *conn) static void hci_connect_le_scan_cleanup(struct hci_conn *conn, u8 status)
{ {
struct hci_conn_params *params; struct hci_conn_params *params;
struct hci_dev *hdev = conn->hdev; struct hci_dev *hdev = conn->hdev;
...@@ -88,9 +88,28 @@ static void hci_connect_le_scan_cleanup(struct hci_conn *conn) ...@@ -88,9 +88,28 @@ static void hci_connect_le_scan_cleanup(struct hci_conn *conn)
params = hci_pend_le_action_lookup(&hdev->pend_le_conns, bdaddr, params = hci_pend_le_action_lookup(&hdev->pend_le_conns, bdaddr,
bdaddr_type); bdaddr_type);
if (!params || !params->explicit_connect) if (!params)
return; return;
if (params->conn) {
hci_conn_drop(params->conn);
hci_conn_put(params->conn);
params->conn = NULL;
}
if (!params->explicit_connect)
return;
/* If the status indicates successful cancellation of
* the attempt (i.e. Unknown Connection Id) there's no point of
* notifying failure since we'll go back to keep trying to
* connect. The only exception is explicit connect requests
* where a timeout + cancel does indicate an actual failure.
*/
if (status && status != HCI_ERROR_UNKNOWN_CONN_ID)
mgmt_connect_failed(hdev, &conn->dst, conn->type,
conn->dst_type, status);
/* The connection attempt was doing scan for new RPA, and is /* The connection attempt was doing scan for new RPA, and is
* in scan phase. If params are not associated with any other * in scan phase. If params are not associated with any other
* autoconnect action, remove them completely. If they are, just unmark * autoconnect action, remove them completely. If they are, just unmark
...@@ -178,7 +197,7 @@ static void le_scan_cleanup(struct work_struct *work) ...@@ -178,7 +197,7 @@ static void le_scan_cleanup(struct work_struct *work)
rcu_read_unlock(); rcu_read_unlock();
if (c == conn) { if (c == conn) {
hci_connect_le_scan_cleanup(conn); hci_connect_le_scan_cleanup(conn, 0x00);
hci_conn_cleanup(conn); hci_conn_cleanup(conn);
} }
...@@ -1049,6 +1068,17 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst, ...@@ -1049,6 +1068,17 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst,
return conn; return conn;
} }
static bool hci_conn_unlink(struct hci_conn *conn)
{
if (!conn->link)
return false;
conn->link->link = NULL;
conn->link = NULL;
return true;
}
int hci_conn_del(struct hci_conn *conn) int hci_conn_del(struct hci_conn *conn)
{ {
struct hci_dev *hdev = conn->hdev; struct hci_dev *hdev = conn->hdev;
...@@ -1060,15 +1090,16 @@ int hci_conn_del(struct hci_conn *conn) ...@@ -1060,15 +1090,16 @@ int hci_conn_del(struct hci_conn *conn)
cancel_delayed_work_sync(&conn->idle_work); cancel_delayed_work_sync(&conn->idle_work);
if (conn->type == ACL_LINK) { if (conn->type == ACL_LINK) {
struct hci_conn *sco = conn->link; struct hci_conn *link = conn->link;
if (sco) {
sco->link = NULL; if (link) {
hci_conn_unlink(conn);
/* Due to race, SCO connection might be not established /* Due to race, SCO connection might be not established
* yet at this point. Delete it now, otherwise it is * yet at this point. Delete it now, otherwise it is
* possible for it to be stuck and can't be deleted. * possible for it to be stuck and can't be deleted.
*/ */
if (sco->handle == HCI_CONN_HANDLE_UNSET) if (link->handle == HCI_CONN_HANDLE_UNSET)
hci_conn_del(sco); hci_conn_del(link);
} }
/* Unacked frames */ /* Unacked frames */
...@@ -1084,7 +1115,7 @@ int hci_conn_del(struct hci_conn *conn) ...@@ -1084,7 +1115,7 @@ int hci_conn_del(struct hci_conn *conn)
struct hci_conn *acl = conn->link; struct hci_conn *acl = conn->link;
if (acl) { if (acl) {
acl->link = NULL; hci_conn_unlink(conn);
hci_conn_drop(acl); hci_conn_drop(acl);
} }
...@@ -1179,31 +1210,8 @@ EXPORT_SYMBOL(hci_get_route); ...@@ -1179,31 +1210,8 @@ EXPORT_SYMBOL(hci_get_route);
static void hci_le_conn_failed(struct hci_conn *conn, u8 status) static void hci_le_conn_failed(struct hci_conn *conn, u8 status)
{ {
struct hci_dev *hdev = conn->hdev; struct hci_dev *hdev = conn->hdev;
struct hci_conn_params *params;
params = hci_pend_le_action_lookup(&hdev->pend_le_conns, &conn->dst, hci_connect_le_scan_cleanup(conn, status);
conn->dst_type);
if (params && params->conn) {
hci_conn_drop(params->conn);
hci_conn_put(params->conn);
params->conn = NULL;
}
/* If the status indicates successful cancellation of
* the attempt (i.e. Unknown Connection Id) there's no point of
* notifying failure since we'll go back to keep trying to
* connect. The only exception is explicit connect requests
* where a timeout + cancel does indicate an actual failure.
*/
if (status != HCI_ERROR_UNKNOWN_CONN_ID ||
(params && params->explicit_connect))
mgmt_connect_failed(hdev, &conn->dst, conn->type,
conn->dst_type, status);
/* Since we may have temporarily stopped the background scanning in
* favor of connection establishment, we should restart it.
*/
hci_update_passive_scan(hdev);
/* Enable advertising in case this was a failed connection /* Enable advertising in case this was a failed connection
* attempt as a peripheral. * attempt as a peripheral.
...@@ -1237,15 +1245,15 @@ static void create_le_conn_complete(struct hci_dev *hdev, void *data, int err) ...@@ -1237,15 +1245,15 @@ static void create_le_conn_complete(struct hci_dev *hdev, void *data, int err)
{ {
struct hci_conn *conn = data; struct hci_conn *conn = data;
bt_dev_dbg(hdev, "err %d", err);
hci_dev_lock(hdev); hci_dev_lock(hdev);
if (!err) { if (!err) {
hci_connect_le_scan_cleanup(conn); hci_connect_le_scan_cleanup(conn, 0x00);
goto done; goto done;
} }
bt_dev_err(hdev, "request failed to create LE connection: err %d", err);
/* Check if connection is still pending */ /* Check if connection is still pending */
if (conn != hci_lookup_le_connect(hdev)) if (conn != hci_lookup_le_connect(hdev))
goto done; goto done;
...@@ -2438,6 +2446,12 @@ void hci_conn_hash_flush(struct hci_dev *hdev) ...@@ -2438,6 +2446,12 @@ void hci_conn_hash_flush(struct hci_dev *hdev)
c->state = BT_CLOSED; c->state = BT_CLOSED;
hci_disconn_cfm(c, HCI_ERROR_LOCAL_HOST_TERM); hci_disconn_cfm(c, HCI_ERROR_LOCAL_HOST_TERM);
/* Unlink before deleting otherwise it is possible that
* hci_conn_del removes the link which may cause the list to
* contain items already freed.
*/
hci_conn_unlink(c);
hci_conn_del(c); hci_conn_del(c);
} }
} }
...@@ -2775,6 +2789,9 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason) ...@@ -2775,6 +2789,9 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason)
{ {
int r = 0; int r = 0;
if (test_and_set_bit(HCI_CONN_CANCEL, &conn->flags))
return 0;
switch (conn->state) { switch (conn->state) {
case BT_CONNECTED: case BT_CONNECTED:
case BT_CONFIG: case BT_CONFIG:
......
...@@ -2881,16 +2881,6 @@ static void cs_le_create_conn(struct hci_dev *hdev, bdaddr_t *peer_addr, ...@@ -2881,16 +2881,6 @@ static void cs_le_create_conn(struct hci_dev *hdev, bdaddr_t *peer_addr,
conn->resp_addr_type = peer_addr_type; conn->resp_addr_type = peer_addr_type;
bacpy(&conn->resp_addr, peer_addr); bacpy(&conn->resp_addr, peer_addr);
/* We don't want the connection attempt to stick around
* indefinitely since LE doesn't have a page timeout concept
* like BR/EDR. Set a timer for any connection that doesn't use
* the accept list for connecting.
*/
if (filter_policy == HCI_LE_USE_PEER_ADDR)
queue_delayed_work(conn->hdev->workqueue,
&conn->le_conn_timeout,
conn->conn_timeout);
} }
static void hci_cs_le_create_conn(struct hci_dev *hdev, u8 status) static void hci_cs_le_create_conn(struct hci_dev *hdev, u8 status)
...@@ -5902,6 +5892,12 @@ static void le_conn_complete_evt(struct hci_dev *hdev, u8 status, ...@@ -5902,6 +5892,12 @@ static void le_conn_complete_evt(struct hci_dev *hdev, u8 status,
if (status) if (status)
goto unlock; goto unlock;
/* Drop the connection if it has been aborted */
if (test_bit(HCI_CONN_CANCEL, &conn->flags)) {
hci_conn_drop(conn);
goto unlock;
}
if (conn->dst_type == ADDR_LE_DEV_PUBLIC) if (conn->dst_type == ADDR_LE_DEV_PUBLIC)
addr_type = BDADDR_LE_PUBLIC; addr_type = BDADDR_LE_PUBLIC;
else else
...@@ -6995,7 +6991,7 @@ static void hci_le_big_sync_established_evt(struct hci_dev *hdev, void *data, ...@@ -6995,7 +6991,7 @@ static void hci_le_big_sync_established_evt(struct hci_dev *hdev, void *data,
bis->iso_qos.in.latency = le16_to_cpu(ev->interval) * 125 / 100; bis->iso_qos.in.latency = le16_to_cpu(ev->interval) * 125 / 100;
bis->iso_qos.in.sdu = le16_to_cpu(ev->max_pdu); bis->iso_qos.in.sdu = le16_to_cpu(ev->max_pdu);
hci_connect_cfm(bis, ev->status); hci_iso_setup_path(bis);
} }
hci_dev_unlock(hdev); hci_dev_unlock(hdev);
......
...@@ -246,6 +246,7 @@ int __hci_cmd_sync_status_sk(struct hci_dev *hdev, u16 opcode, u32 plen, ...@@ -246,6 +246,7 @@ int __hci_cmd_sync_status_sk(struct hci_dev *hdev, u16 opcode, u32 plen,
skb = __hci_cmd_sync_sk(hdev, opcode, plen, param, event, timeout, sk); skb = __hci_cmd_sync_sk(hdev, opcode, plen, param, event, timeout, sk);
if (IS_ERR(skb)) { if (IS_ERR(skb)) {
if (!event)
bt_dev_err(hdev, "Opcode 0x%4x failed: %ld", opcode, bt_dev_err(hdev, "Opcode 0x%4x failed: %ld", opcode,
PTR_ERR(skb)); PTR_ERR(skb));
return PTR_ERR(skb); return PTR_ERR(skb);
...@@ -5126,8 +5127,11 @@ static int hci_le_connect_cancel_sync(struct hci_dev *hdev, ...@@ -5126,8 +5127,11 @@ static int hci_le_connect_cancel_sync(struct hci_dev *hdev,
if (test_bit(HCI_CONN_SCANNING, &conn->flags)) if (test_bit(HCI_CONN_SCANNING, &conn->flags))
return 0; return 0;
if (test_and_set_bit(HCI_CONN_CANCEL, &conn->flags))
return 0;
return __hci_cmd_sync_status(hdev, HCI_OP_LE_CREATE_CONN_CANCEL, return __hci_cmd_sync_status(hdev, HCI_OP_LE_CREATE_CONN_CANCEL,
6, &conn->dst, HCI_CMD_TIMEOUT); 0, NULL, HCI_CMD_TIMEOUT);
} }
static int hci_connect_cancel_sync(struct hci_dev *hdev, struct hci_conn *conn) static int hci_connect_cancel_sync(struct hci_dev *hdev, struct hci_conn *conn)
...@@ -6102,6 +6106,9 @@ int hci_le_create_conn_sync(struct hci_dev *hdev, struct hci_conn *conn) ...@@ -6102,6 +6106,9 @@ int hci_le_create_conn_sync(struct hci_dev *hdev, struct hci_conn *conn)
conn->conn_timeout, NULL); conn->conn_timeout, NULL);
done: done:
if (err == -ETIMEDOUT)
hci_le_connect_cancel_sync(hdev, conn);
/* Re-enable advertising after the connection attempt is finished. */ /* Re-enable advertising after the connection attempt is finished. */
hci_resume_advertising_sync(hdev); hci_resume_advertising_sync(hdev);
return err; return err;
......
...@@ -433,7 +433,7 @@ static void hidp_set_timer(struct hidp_session *session) ...@@ -433,7 +433,7 @@ static void hidp_set_timer(struct hidp_session *session)
static void hidp_del_timer(struct hidp_session *session) static void hidp_del_timer(struct hidp_session *session)
{ {
if (session->idle_to > 0) if (session->idle_to > 0)
del_timer(&session->timer); del_timer_sync(&session->timer);
} }
static void hidp_process_report(struct hidp_session *session, int type, static void hidp_process_report(struct hidp_session *session, int type,
......
...@@ -4652,33 +4652,27 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, ...@@ -4652,33 +4652,27 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn,
BT_DBG("scid 0x%4.4x dcid 0x%4.4x", scid, dcid); BT_DBG("scid 0x%4.4x dcid 0x%4.4x", scid, dcid);
mutex_lock(&conn->chan_lock); chan = l2cap_get_chan_by_scid(conn, dcid);
chan = __l2cap_get_chan_by_scid(conn, dcid);
if (!chan) { if (!chan) {
mutex_unlock(&conn->chan_lock);
cmd_reject_invalid_cid(conn, cmd->ident, dcid, scid); cmd_reject_invalid_cid(conn, cmd->ident, dcid, scid);
return 0; return 0;
} }
l2cap_chan_hold(chan);
l2cap_chan_lock(chan);
rsp.dcid = cpu_to_le16(chan->scid); rsp.dcid = cpu_to_le16(chan->scid);
rsp.scid = cpu_to_le16(chan->dcid); rsp.scid = cpu_to_le16(chan->dcid);
l2cap_send_cmd(conn, cmd->ident, L2CAP_DISCONN_RSP, sizeof(rsp), &rsp); l2cap_send_cmd(conn, cmd->ident, L2CAP_DISCONN_RSP, sizeof(rsp), &rsp);
chan->ops->set_shutdown(chan); chan->ops->set_shutdown(chan);
mutex_lock(&conn->chan_lock);
l2cap_chan_del(chan, ECONNRESET); l2cap_chan_del(chan, ECONNRESET);
mutex_unlock(&conn->chan_lock);
chan->ops->close(chan); chan->ops->close(chan);
l2cap_chan_unlock(chan); l2cap_chan_unlock(chan);
l2cap_chan_put(chan); l2cap_chan_put(chan);
mutex_unlock(&conn->chan_lock);
return 0; return 0;
} }
...@@ -4698,33 +4692,27 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, ...@@ -4698,33 +4692,27 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn,
BT_DBG("dcid 0x%4.4x scid 0x%4.4x", dcid, scid); BT_DBG("dcid 0x%4.4x scid 0x%4.4x", dcid, scid);
mutex_lock(&conn->chan_lock); chan = l2cap_get_chan_by_scid(conn, scid);
chan = __l2cap_get_chan_by_scid(conn, scid);
if (!chan) { if (!chan) {
mutex_unlock(&conn->chan_lock); mutex_unlock(&conn->chan_lock);
return 0; return 0;
} }
l2cap_chan_hold(chan);
l2cap_chan_lock(chan);
if (chan->state != BT_DISCONN) { if (chan->state != BT_DISCONN) {
l2cap_chan_unlock(chan); l2cap_chan_unlock(chan);
l2cap_chan_put(chan); l2cap_chan_put(chan);
mutex_unlock(&conn->chan_lock);
return 0; return 0;
} }
mutex_lock(&conn->chan_lock);
l2cap_chan_del(chan, 0); l2cap_chan_del(chan, 0);
mutex_unlock(&conn->chan_lock);
chan->ops->close(chan); chan->ops->close(chan);
l2cap_chan_unlock(chan); l2cap_chan_unlock(chan);
l2cap_chan_put(chan); l2cap_chan_put(chan);
mutex_unlock(&conn->chan_lock);
return 0; return 0;
} }
......
...@@ -235,27 +235,41 @@ static int sco_chan_add(struct sco_conn *conn, struct sock *sk, ...@@ -235,27 +235,41 @@ static int sco_chan_add(struct sco_conn *conn, struct sock *sk,
return err; return err;
} }
static int sco_connect(struct hci_dev *hdev, struct sock *sk) static int sco_connect(struct sock *sk)
{ {
struct sco_conn *conn; struct sco_conn *conn;
struct hci_conn *hcon; struct hci_conn *hcon;
struct hci_dev *hdev;
int err, type; int err, type;
BT_DBG("%pMR -> %pMR", &sco_pi(sk)->src, &sco_pi(sk)->dst); BT_DBG("%pMR -> %pMR", &sco_pi(sk)->src, &sco_pi(sk)->dst);
hdev = hci_get_route(&sco_pi(sk)->dst, &sco_pi(sk)->src, BDADDR_BREDR);
if (!hdev)
return -EHOSTUNREACH;
hci_dev_lock(hdev);
if (lmp_esco_capable(hdev) && !disable_esco) if (lmp_esco_capable(hdev) && !disable_esco)
type = ESCO_LINK; type = ESCO_LINK;
else else
type = SCO_LINK; type = SCO_LINK;
if (sco_pi(sk)->setting == BT_VOICE_TRANSPARENT && if (sco_pi(sk)->setting == BT_VOICE_TRANSPARENT &&
(!lmp_transp_capable(hdev) || !lmp_esco_capable(hdev))) (!lmp_transp_capable(hdev) || !lmp_esco_capable(hdev))) {
return -EOPNOTSUPP; err = -EOPNOTSUPP;
goto unlock;
}
hcon = hci_connect_sco(hdev, type, &sco_pi(sk)->dst, hcon = hci_connect_sco(hdev, type, &sco_pi(sk)->dst,
sco_pi(sk)->setting, &sco_pi(sk)->codec); sco_pi(sk)->setting, &sco_pi(sk)->codec);
if (IS_ERR(hcon)) if (IS_ERR(hcon)) {
return PTR_ERR(hcon); err = PTR_ERR(hcon);
goto unlock;
}
hci_dev_unlock(hdev);
hci_dev_put(hdev);
conn = sco_conn_add(hcon); conn = sco_conn_add(hcon);
if (!conn) { if (!conn) {
...@@ -263,13 +277,15 @@ static int sco_connect(struct hci_dev *hdev, struct sock *sk) ...@@ -263,13 +277,15 @@ static int sco_connect(struct hci_dev *hdev, struct sock *sk)
return -ENOMEM; return -ENOMEM;
} }
/* Update source addr of the socket */
bacpy(&sco_pi(sk)->src, &hcon->src);
err = sco_chan_add(conn, sk, NULL); err = sco_chan_add(conn, sk, NULL);
if (err) if (err)
return err; return err;
lock_sock(sk);
/* Update source addr of the socket */
bacpy(&sco_pi(sk)->src, &hcon->src);
if (hcon->state == BT_CONNECTED) { if (hcon->state == BT_CONNECTED) {
sco_sock_clear_timer(sk); sco_sock_clear_timer(sk);
sk->sk_state = BT_CONNECTED; sk->sk_state = BT_CONNECTED;
...@@ -278,6 +294,13 @@ static int sco_connect(struct hci_dev *hdev, struct sock *sk) ...@@ -278,6 +294,13 @@ static int sco_connect(struct hci_dev *hdev, struct sock *sk)
sco_sock_set_timer(sk, sk->sk_sndtimeo); sco_sock_set_timer(sk, sk->sk_sndtimeo);
} }
release_sock(sk);
return err;
unlock:
hci_dev_unlock(hdev);
hci_dev_put(hdev);
return err; return err;
} }
...@@ -565,7 +588,6 @@ static int sco_sock_connect(struct socket *sock, struct sockaddr *addr, int alen ...@@ -565,7 +588,6 @@ static int sco_sock_connect(struct socket *sock, struct sockaddr *addr, int alen
{ {
struct sockaddr_sco *sa = (struct sockaddr_sco *) addr; struct sockaddr_sco *sa = (struct sockaddr_sco *) addr;
struct sock *sk = sock->sk; struct sock *sk = sock->sk;
struct hci_dev *hdev;
int err; int err;
BT_DBG("sk %p", sk); BT_DBG("sk %p", sk);
...@@ -574,37 +596,26 @@ static int sco_sock_connect(struct socket *sock, struct sockaddr *addr, int alen ...@@ -574,37 +596,26 @@ static int sco_sock_connect(struct socket *sock, struct sockaddr *addr, int alen
addr->sa_family != AF_BLUETOOTH) addr->sa_family != AF_BLUETOOTH)
return -EINVAL; return -EINVAL;
lock_sock(sk); if (sk->sk_state != BT_OPEN && sk->sk_state != BT_BOUND)
if (sk->sk_state != BT_OPEN && sk->sk_state != BT_BOUND) { return -EBADFD;
err = -EBADFD;
goto done;
}
if (sk->sk_type != SOCK_SEQPACKET) { if (sk->sk_type != SOCK_SEQPACKET)
err = -EINVAL; err = -EINVAL;
goto done;
}
hdev = hci_get_route(&sa->sco_bdaddr, &sco_pi(sk)->src, BDADDR_BREDR);
if (!hdev) {
err = -EHOSTUNREACH;
goto done;
}
hci_dev_lock(hdev);
lock_sock(sk);
/* Set destination address and psm */ /* Set destination address and psm */
bacpy(&sco_pi(sk)->dst, &sa->sco_bdaddr); bacpy(&sco_pi(sk)->dst, &sa->sco_bdaddr);
release_sock(sk);
err = sco_connect(hdev, sk); err = sco_connect(sk);
hci_dev_unlock(hdev);
hci_dev_put(hdev);
if (err) if (err)
goto done; return err;
lock_sock(sk);
err = bt_sock_wait_state(sk, BT_CONNECTED, err = bt_sock_wait_state(sk, BT_CONNECTED,
sock_sndtimeo(sk, flags & O_NONBLOCK)); sock_sndtimeo(sk, flags & O_NONBLOCK));
done:
release_sock(sk); release_sock(sk);
return err; return err;
} }
...@@ -1129,6 +1140,8 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname, ...@@ -1129,6 +1140,8 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname,
break; break;
} }
release_sock(sk);
/* find total buffer size required to copy codec + caps */ /* find total buffer size required to copy codec + caps */
hci_dev_lock(hdev); hci_dev_lock(hdev);
list_for_each_entry(c, &hdev->local_codecs, list) { list_for_each_entry(c, &hdev->local_codecs, list) {
...@@ -1146,15 +1159,13 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname, ...@@ -1146,15 +1159,13 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname,
buf_len += sizeof(struct bt_codecs); buf_len += sizeof(struct bt_codecs);
if (buf_len > len) { if (buf_len > len) {
hci_dev_put(hdev); hci_dev_put(hdev);
err = -ENOBUFS; return -ENOBUFS;
break;
} }
ptr = optval; ptr = optval;
if (put_user(num_codecs, ptr)) { if (put_user(num_codecs, ptr)) {
hci_dev_put(hdev); hci_dev_put(hdev);
err = -EFAULT; return -EFAULT;
break;
} }
ptr += sizeof(num_codecs); ptr += sizeof(num_codecs);
...@@ -1194,12 +1205,14 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname, ...@@ -1194,12 +1205,14 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname,
ptr += len; ptr += len;
} }
if (!err && put_user(buf_len, optlen))
err = -EFAULT;
hci_dev_unlock(hdev); hci_dev_unlock(hdev);
hci_dev_put(hdev); hci_dev_put(hdev);
lock_sock(sk);
if (!err && put_user(buf_len, optlen))
err = -EFAULT;
break; break;
default: default:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment