mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-22 03:27:30 -04:00
Merge tag 'for-net-2026-06-03' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth
Luiz Augusto von Dentz says: ==================== bluetooth pull request for net: - hci_core: fix memory leak in error path of hci_alloc_dev() - hci_sync: reject oversized Broadcast Announcement prepend - MGMT: Fix backward compatibility with userspace - MGMT: validate advertising TLV before type checks - L2CAP: reject BR/EDR signaling packets over MTUsig - RFCOMM: validate skb length in MCC handlers - RFCOMM: hold listener socket in rfcomm_connect_ind() - ISO: Fix not releasing hdev reference on iso_conn_big_sync - ISO: Fix a use-after-free of the hci_conn pointer - ISO: Fix data-race on iso_pi fields in hci_get_route calls - SCO: Fix data-race on sco_pi fields in sco_connect - BNEP: reject short frames before parsing * tag 'for-net-2026-06-03' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth: Bluetooth: MGMT: Fix backward compatibility with userspace Bluetooth: SCO: Fix data-race on sco_pi fields in sco_connect Bluetooth: ISO: Fix data-race on iso_pi fields in hci_get_route calls Bluetooth: ISO: Fix a use-after-free of the hci_conn pointer Bluetooth: ISO: Fix not releasing hdev reference on iso_conn_big_sync Bluetooth: fix memory leak in error path of hci_alloc_dev() Bluetooth: bnep: reject short frames before parsing Bluetooth: hci_sync: reject oversized Broadcast Announcement prepend Bluetooth: L2CAP: reject BR/EDR signaling packets over MTUsig Bluetooth: RFCOMM: validate skb length in MCC handlers Bluetooth: MGMT: validate advertising TLV before type checks Bluetooth: RFCOMM: hold listener socket in rfcomm_connect_ind() ==================== Link: https://patch.msgid.link/20260603162714.342496-1-luiz.dentz@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
@@ -33,6 +33,7 @@
|
||||
/* L2CAP defaults */
|
||||
#define L2CAP_DEFAULT_MTU 672
|
||||
#define L2CAP_DEFAULT_MIN_MTU 48
|
||||
#define L2CAP_SIG_MTU 48 /* BR/EDR signaling MTU */
|
||||
#define L2CAP_DEFAULT_FLUSH_TO 0xFFFF
|
||||
#define L2CAP_EFS_DEFAULT_FLUSH_TO 0xFFFFFFFF
|
||||
#define L2CAP_DEFAULT_TX_WINDOW 63
|
||||
|
||||
@@ -206,14 +206,11 @@ static int bnep_ctrl_set_mcfilter(struct bnep_session *s, u8 *data, int len)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bnep_rx_control(struct bnep_session *s, void *data, int len)
|
||||
static int bnep_rx_control_cmd(struct bnep_session *s, u8 cmd, void *data,
|
||||
int len)
|
||||
{
|
||||
u8 cmd = *(u8 *)data;
|
||||
int err = 0;
|
||||
|
||||
data++;
|
||||
len--;
|
||||
|
||||
switch (cmd) {
|
||||
case BNEP_CMD_NOT_UNDERSTOOD:
|
||||
case BNEP_SETUP_CONN_RSP:
|
||||
@@ -254,6 +251,14 @@ static int bnep_rx_control(struct bnep_session *s, void *data, int len)
|
||||
return err;
|
||||
}
|
||||
|
||||
static int bnep_rx_control(struct bnep_session *s, void *data, int len)
|
||||
{
|
||||
if (len < 1)
|
||||
return -EILSEQ;
|
||||
|
||||
return bnep_rx_control_cmd(s, *(u8 *)data, data + 1, len - 1);
|
||||
}
|
||||
|
||||
static int bnep_rx_extension(struct bnep_session *s, struct sk_buff *skb)
|
||||
{
|
||||
struct bnep_ext_hdr *h;
|
||||
@@ -299,19 +304,26 @@ static int bnep_rx_frame(struct bnep_session *s, struct sk_buff *skb)
|
||||
{
|
||||
struct net_device *dev = s->dev;
|
||||
struct sk_buff *nskb;
|
||||
u8 *data;
|
||||
u8 type, ctrl_type;
|
||||
|
||||
dev->stats.rx_bytes += skb->len;
|
||||
|
||||
type = *(u8 *) skb->data;
|
||||
skb_pull(skb, 1);
|
||||
ctrl_type = *(u8 *)skb->data;
|
||||
data = skb_pull_data(skb, sizeof(type));
|
||||
if (!data)
|
||||
goto badframe;
|
||||
type = *data;
|
||||
|
||||
if ((type & BNEP_TYPE_MASK) >= sizeof(__bnep_rx_hlen))
|
||||
goto badframe;
|
||||
|
||||
if ((type & BNEP_TYPE_MASK) == BNEP_CONTROL) {
|
||||
if (bnep_rx_control(s, skb->data, skb->len) < 0) {
|
||||
data = skb_pull_data(skb, sizeof(ctrl_type));
|
||||
if (!data)
|
||||
goto badframe;
|
||||
ctrl_type = *data;
|
||||
|
||||
if (bnep_rx_control_cmd(s, ctrl_type, skb->data, skb->len) < 0) {
|
||||
dev->stats.tx_errors++;
|
||||
kfree_skb(skb);
|
||||
return 0;
|
||||
@@ -324,24 +336,27 @@ static int bnep_rx_frame(struct bnep_session *s, struct sk_buff *skb)
|
||||
|
||||
/* Verify and pull ctrl message since it's already processed */
|
||||
switch (ctrl_type) {
|
||||
case BNEP_SETUP_CONN_REQ:
|
||||
/* Pull: ctrl type (1 b), len (1 b), data (len bytes) */
|
||||
if (!skb_pull(skb, 2 + *(u8 *)(skb->data + 1) * 2))
|
||||
goto badframe;
|
||||
break;
|
||||
case BNEP_FILTER_MULTI_ADDR_SET:
|
||||
case BNEP_FILTER_NET_TYPE_SET: {
|
||||
u8 *hdr;
|
||||
case BNEP_SETUP_CONN_REQ: {
|
||||
u8 uuid_size;
|
||||
|
||||
/* Pull ctrl type (1 b) + len (2 b) */
|
||||
hdr = skb_pull_data(skb, 3);
|
||||
if (!hdr)
|
||||
/* Pull uuid_size and the dst/src service UUIDs. */
|
||||
data = skb_pull_data(skb, sizeof(uuid_size));
|
||||
if (!data)
|
||||
goto badframe;
|
||||
/* Pull data (len bytes); length is big-endian */
|
||||
if (!skb_pull(skb, get_unaligned_be16(&hdr[1])))
|
||||
uuid_size = *data;
|
||||
if (!skb_pull(skb, uuid_size + uuid_size))
|
||||
goto badframe;
|
||||
break;
|
||||
}
|
||||
case BNEP_FILTER_MULTI_ADDR_SET:
|
||||
case BNEP_FILTER_NET_TYPE_SET:
|
||||
/* Pull: len (2 b), data (len bytes) */
|
||||
data = skb_pull_data(skb, sizeof(u16));
|
||||
if (!data)
|
||||
goto badframe;
|
||||
if (!skb_pull(skb, get_unaligned_be16(data)))
|
||||
goto badframe;
|
||||
break;
|
||||
default:
|
||||
kfree_skb(skb);
|
||||
return 0;
|
||||
|
||||
@@ -1725,6 +1725,11 @@ static int hci_adv_bcast_annoucement(struct hci_dev *hdev, struct adv_info *adv)
|
||||
/* Generate Broadcast ID */
|
||||
get_random_bytes(bid, sizeof(bid));
|
||||
len = eir_append_service_data(ad, 0, 0x1852, bid, sizeof(bid));
|
||||
if (adv->adv_data_len > sizeof(ad) - len) {
|
||||
bt_dev_err(hdev, "No room for Broadcast Announcement");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memcpy(ad + len, adv->adv_data, adv->adv_data_len);
|
||||
hci_set_adv_instance_data(hdev, adv->instance, len + adv->adv_data_len,
|
||||
ad, 0, NULL);
|
||||
|
||||
@@ -83,10 +83,12 @@ static void bt_host_release(struct device *dev)
|
||||
{
|
||||
struct hci_dev *hdev = to_hci_dev(dev);
|
||||
|
||||
if (hci_dev_test_flag(hdev, HCI_UNREGISTER))
|
||||
if (hci_dev_test_flag(hdev, HCI_UNREGISTER)) {
|
||||
hci_release_dev(hdev);
|
||||
else
|
||||
} else {
|
||||
cleanup_srcu_struct(&hdev->srcu);
|
||||
kfree(hdev);
|
||||
}
|
||||
module_put(THIS_MODULE);
|
||||
}
|
||||
|
||||
|
||||
@@ -337,12 +337,20 @@ static int iso_connect_bis(struct sock *sk)
|
||||
struct iso_conn *conn;
|
||||
struct hci_conn *hcon;
|
||||
struct hci_dev *hdev;
|
||||
bdaddr_t src, dst;
|
||||
u8 src_type, bc_sid;
|
||||
int err;
|
||||
|
||||
BT_DBG("%pMR (SID 0x%2.2x)", &iso_pi(sk)->src, iso_pi(sk)->bc_sid);
|
||||
lock_sock(sk);
|
||||
bacpy(&src, &iso_pi(sk)->src);
|
||||
bacpy(&dst, &iso_pi(sk)->dst);
|
||||
src_type = iso_pi(sk)->src_type;
|
||||
bc_sid = iso_pi(sk)->bc_sid;
|
||||
release_sock(sk);
|
||||
|
||||
hdev = hci_get_route(&iso_pi(sk)->dst, &iso_pi(sk)->src,
|
||||
iso_pi(sk)->src_type);
|
||||
BT_DBG("%pMR (SID 0x%2.2x)", &src, bc_sid);
|
||||
|
||||
hdev = hci_get_route(&dst, &src, src_type);
|
||||
if (!hdev)
|
||||
return -EHOSTUNREACH;
|
||||
|
||||
@@ -430,12 +438,19 @@ static int iso_connect_cis(struct sock *sk)
|
||||
struct iso_conn *conn;
|
||||
struct hci_conn *hcon;
|
||||
struct hci_dev *hdev;
|
||||
bdaddr_t src, dst;
|
||||
u8 src_type;
|
||||
int err;
|
||||
|
||||
BT_DBG("%pMR -> %pMR", &iso_pi(sk)->src, &iso_pi(sk)->dst);
|
||||
lock_sock(sk);
|
||||
bacpy(&src, &iso_pi(sk)->src);
|
||||
bacpy(&dst, &iso_pi(sk)->dst);
|
||||
src_type = iso_pi(sk)->src_type;
|
||||
release_sock(sk);
|
||||
|
||||
hdev = hci_get_route(&iso_pi(sk)->dst, &iso_pi(sk)->src,
|
||||
iso_pi(sk)->src_type);
|
||||
BT_DBG("%pMR -> %pMR", &src, &dst);
|
||||
|
||||
hdev = hci_get_route(&dst, &src, src_type);
|
||||
if (!hdev)
|
||||
return -EHOSTUNREACH;
|
||||
|
||||
@@ -1082,7 +1097,7 @@ static int iso_sock_rebind_bc(struct sock *sk, struct sockaddr_iso *sa,
|
||||
* ordering.
|
||||
*/
|
||||
release_sock(sk);
|
||||
hci_dev_lock(bis->hdev);
|
||||
hci_dev_lock(hdev);
|
||||
lock_sock(sk);
|
||||
|
||||
if (!iso_pi(sk)->conn || iso_pi(sk)->conn->hcon != bis) {
|
||||
@@ -1212,18 +1227,25 @@ static int iso_sock_connect(struct socket *sock, struct sockaddr_unsized *addr,
|
||||
|
||||
static int iso_listen_bis(struct sock *sk)
|
||||
{
|
||||
struct hci_dev *hdev;
|
||||
int err = 0;
|
||||
struct iso_conn *conn;
|
||||
struct hci_conn *hcon;
|
||||
struct hci_dev *hdev;
|
||||
bdaddr_t src, dst;
|
||||
u8 src_type, bc_sid;
|
||||
int err = 0;
|
||||
|
||||
BT_DBG("%pMR -> %pMR (SID 0x%2.2x)", &iso_pi(sk)->src,
|
||||
&iso_pi(sk)->dst, iso_pi(sk)->bc_sid);
|
||||
lock_sock(sk);
|
||||
bacpy(&src, &iso_pi(sk)->src);
|
||||
bacpy(&dst, &iso_pi(sk)->dst);
|
||||
src_type = iso_pi(sk)->src_type;
|
||||
bc_sid = iso_pi(sk)->bc_sid;
|
||||
release_sock(sk);
|
||||
|
||||
BT_DBG("%pMR -> %pMR (SID 0x%2.2x)", &src, &dst, bc_sid);
|
||||
|
||||
write_lock(&iso_sk_list.lock);
|
||||
|
||||
if (__iso_get_sock_listen_by_sid(&iso_pi(sk)->src, &iso_pi(sk)->dst,
|
||||
iso_pi(sk)->bc_sid))
|
||||
if (__iso_get_sock_listen_by_sid(&src, &dst, bc_sid))
|
||||
err = -EADDRINUSE;
|
||||
|
||||
write_unlock(&iso_sk_list.lock);
|
||||
@@ -1231,8 +1253,7 @@ static int iso_listen_bis(struct sock *sk)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
hdev = hci_get_route(&iso_pi(sk)->dst, &iso_pi(sk)->src,
|
||||
iso_pi(sk)->src_type);
|
||||
hdev = hci_get_route(&dst, &src, src_type);
|
||||
if (!hdev)
|
||||
return -EHOSTUNREACH;
|
||||
|
||||
@@ -1568,9 +1589,16 @@ static void iso_conn_big_sync(struct sock *sk)
|
||||
{
|
||||
int err;
|
||||
struct hci_dev *hdev;
|
||||
bdaddr_t src, dst;
|
||||
u8 src_type;
|
||||
|
||||
hdev = hci_get_route(&iso_pi(sk)->dst, &iso_pi(sk)->src,
|
||||
iso_pi(sk)->src_type);
|
||||
lock_sock(sk);
|
||||
bacpy(&src, &iso_pi(sk)->src);
|
||||
bacpy(&dst, &iso_pi(sk)->dst);
|
||||
src_type = iso_pi(sk)->src_type;
|
||||
release_sock(sk);
|
||||
|
||||
hdev = hci_get_route(&dst, &src, src_type);
|
||||
|
||||
if (!hdev)
|
||||
return;
|
||||
@@ -1595,6 +1623,7 @@ static void iso_conn_big_sync(struct sock *sk)
|
||||
|
||||
release_sock(sk);
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_put(hdev);
|
||||
}
|
||||
|
||||
static int iso_sock_recvmsg(struct socket *sock, struct msghdr *msg,
|
||||
|
||||
@@ -5643,6 +5643,15 @@ static inline void l2cap_sig_send_rej(struct l2cap_conn *conn, u16 ident)
|
||||
l2cap_send_cmd(conn, ident, L2CAP_COMMAND_REJ, sizeof(rej), &rej);
|
||||
}
|
||||
|
||||
static inline void l2cap_sig_send_mtu_rej(struct l2cap_conn *conn, u8 ident)
|
||||
{
|
||||
struct l2cap_cmd_rej_mtu rej;
|
||||
|
||||
rej.reason = cpu_to_le16(L2CAP_REJ_MTU_EXCEEDED);
|
||||
rej.max_mtu = cpu_to_le16(L2CAP_SIG_MTU);
|
||||
l2cap_send_cmd(conn, ident, L2CAP_COMMAND_REJ, sizeof(rej), &rej);
|
||||
}
|
||||
|
||||
static inline void l2cap_sig_channel(struct l2cap_conn *conn,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
@@ -5655,6 +5664,43 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn,
|
||||
if (hcon->type != ACL_LINK)
|
||||
goto drop;
|
||||
|
||||
/*
|
||||
* Bluetooth Core v5.4, Vol 3, Part A, Section 4: the BR/EDR
|
||||
* signaling channel has a fixed signaling MTU (MTUsig) whose
|
||||
* minimum and default is 48 octets. Section 4.1 says that on
|
||||
* an MTUExceeded command reject the identifier "shall match
|
||||
* the first request command in the L2CAP packet" and that
|
||||
* packets containing only response commands "shall be
|
||||
* silently discarded".
|
||||
*
|
||||
* Linux intentionally deviates from that prescription:
|
||||
*
|
||||
* 1. Silently discarding desynchronizes the peer. The
|
||||
* remote stack never learns its responses were dropped,
|
||||
* so any state machine waiting on a paired response
|
||||
* stalls until its own timer fires.
|
||||
*
|
||||
* 2. Locating "the first request command" requires walking
|
||||
* command headers past MTUsig, i.e. processing bytes
|
||||
* from a packet we have already decided is too large to
|
||||
* process.
|
||||
*
|
||||
* Reject every over-MTUsig signaling packet with one
|
||||
* L2CAP_REJ_MTU_EXCEEDED command reject. The reject's
|
||||
* reason field is what tells the peer that the whole packet
|
||||
* was discarded; the identifier value is informational, so
|
||||
* we use the identifier from the first command header, a
|
||||
* single fixed-offset byte read.
|
||||
*/
|
||||
if (skb->len > L2CAP_SIG_MTU) {
|
||||
u8 ident = skb->data[1];
|
||||
|
||||
BT_DBG("signaling packet exceeds MTU: %u > %u",
|
||||
skb->len, L2CAP_SIG_MTU);
|
||||
l2cap_sig_send_mtu_rej(conn, ident);
|
||||
goto drop;
|
||||
}
|
||||
|
||||
while (skb->len >= L2CAP_CMD_HDR_SIZE) {
|
||||
u16 len;
|
||||
|
||||
|
||||
@@ -8638,6 +8638,12 @@ static bool tlv_data_is_valid(struct hci_dev *hdev, u32 adv_flags, u8 *data,
|
||||
if (!cur_len)
|
||||
continue;
|
||||
|
||||
/* If the current field length would exceed the total data
|
||||
* length, then it's invalid.
|
||||
*/
|
||||
if (i + cur_len >= len)
|
||||
return false;
|
||||
|
||||
if (data[i + 1] == EIR_FLAGS &&
|
||||
(!is_adv_data || flags_managed(adv_flags)))
|
||||
return false;
|
||||
@@ -8654,12 +8660,6 @@ static bool tlv_data_is_valid(struct hci_dev *hdev, u32 adv_flags, u8 *data,
|
||||
if (data[i + 1] == EIR_APPEARANCE &&
|
||||
appearance_managed(adv_flags))
|
||||
return false;
|
||||
|
||||
/* If the current field length would exceed the total data
|
||||
* length, then it's invalid.
|
||||
*/
|
||||
if (i + cur_len >= len)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -9114,8 +9114,9 @@ static int add_ext_adv_data(struct sock *sk, struct hci_dev *hdev, void *data,
|
||||
|
||||
BT_DBG("%s", hdev->name);
|
||||
|
||||
expected_len = struct_size(cp, data, cp->adv_data_len + cp->scan_rsp_len);
|
||||
if (expected_len != data_len)
|
||||
expected_len = struct_size(cp, data, cp->adv_data_len +
|
||||
cp->scan_rsp_len);
|
||||
if (expected_len > data_len)
|
||||
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_EXT_ADV_DATA,
|
||||
MGMT_STATUS_INVALID_PARAMS);
|
||||
|
||||
|
||||
@@ -1431,10 +1431,15 @@ static int rfcomm_apply_pn(struct rfcomm_dlc *d, int cr, struct rfcomm_pn *pn)
|
||||
|
||||
static int rfcomm_recv_pn(struct rfcomm_session *s, int cr, struct sk_buff *skb)
|
||||
{
|
||||
struct rfcomm_pn *pn = (void *) skb->data;
|
||||
struct rfcomm_pn *pn;
|
||||
struct rfcomm_dlc *d;
|
||||
u8 dlci = pn->dlci;
|
||||
u8 dlci;
|
||||
|
||||
pn = skb_pull_data(skb, sizeof(*pn));
|
||||
if (!pn)
|
||||
return -EILSEQ;
|
||||
|
||||
dlci = pn->dlci;
|
||||
BT_DBG("session %p state %ld dlci %d", s, s->state, dlci);
|
||||
|
||||
if (!dlci)
|
||||
@@ -1483,8 +1488,8 @@ static int rfcomm_recv_pn(struct rfcomm_session *s, int cr, struct sk_buff *skb)
|
||||
|
||||
static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_buff *skb)
|
||||
{
|
||||
struct rfcomm_rpn *rpn = (void *) skb->data;
|
||||
u8 dlci = __get_dlci(rpn->dlci);
|
||||
struct rfcomm_rpn *rpn;
|
||||
u8 dlci;
|
||||
|
||||
u8 bit_rate = 0;
|
||||
u8 data_bits = 0;
|
||||
@@ -1495,15 +1500,16 @@ static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_
|
||||
u8 xoff_char = 0;
|
||||
u16 rpn_mask = RFCOMM_RPN_PM_ALL;
|
||||
|
||||
BT_DBG("dlci %d cr %d len 0x%x bitr 0x%x line 0x%x flow 0x%x xonc 0x%x xoffc 0x%x pm 0x%x",
|
||||
dlci, cr, len, rpn->bit_rate, rpn->line_settings, rpn->flow_ctrl,
|
||||
rpn->xon_char, rpn->xoff_char, rpn->param_mask);
|
||||
|
||||
if (!cr)
|
||||
return 0;
|
||||
|
||||
if (len == 1) {
|
||||
/* This is a request, return default (according to ETSI TS 07.10) settings */
|
||||
rpn = skb_pull_data(skb, 1);
|
||||
if (!rpn)
|
||||
return -EILSEQ;
|
||||
|
||||
dlci = __get_dlci(rpn->dlci);
|
||||
|
||||
if (!cr)
|
||||
return 0;
|
||||
|
||||
bit_rate = RFCOMM_RPN_BR_9600;
|
||||
data_bits = RFCOMM_RPN_DATA_8;
|
||||
stop_bits = RFCOMM_RPN_STOP_1;
|
||||
@@ -1514,6 +1520,19 @@ static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_
|
||||
goto rpn_out;
|
||||
}
|
||||
|
||||
rpn = skb_pull_data(skb, sizeof(*rpn));
|
||||
if (!rpn)
|
||||
return -EILSEQ;
|
||||
|
||||
dlci = __get_dlci(rpn->dlci);
|
||||
|
||||
BT_DBG("dlci %d cr %d len 0x%x bitr 0x%x line 0x%x flow 0x%x xonc 0x%x xoffc 0x%x pm 0x%x",
|
||||
dlci, cr, len, rpn->bit_rate, rpn->line_settings, rpn->flow_ctrl,
|
||||
rpn->xon_char, rpn->xoff_char, rpn->param_mask);
|
||||
|
||||
if (!cr)
|
||||
return 0;
|
||||
|
||||
/* Check for sane values, ignore/accept bit_rate, 8 bits, 1 stop bit,
|
||||
* no parity, no flow control lines, normal XON/XOFF chars */
|
||||
|
||||
@@ -1589,9 +1608,14 @@ static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_
|
||||
|
||||
static int rfcomm_recv_rls(struct rfcomm_session *s, int cr, struct sk_buff *skb)
|
||||
{
|
||||
struct rfcomm_rls *rls = (void *) skb->data;
|
||||
u8 dlci = __get_dlci(rls->dlci);
|
||||
struct rfcomm_rls *rls;
|
||||
u8 dlci;
|
||||
|
||||
rls = skb_pull_data(skb, sizeof(*rls));
|
||||
if (!rls)
|
||||
return -EILSEQ;
|
||||
|
||||
dlci = __get_dlci(rls->dlci);
|
||||
BT_DBG("dlci %d cr %d status 0x%x", dlci, cr, rls->status);
|
||||
|
||||
if (!cr)
|
||||
@@ -1608,10 +1632,15 @@ static int rfcomm_recv_rls(struct rfcomm_session *s, int cr, struct sk_buff *skb
|
||||
|
||||
static int rfcomm_recv_msc(struct rfcomm_session *s, int cr, struct sk_buff *skb)
|
||||
{
|
||||
struct rfcomm_msc *msc = (void *) skb->data;
|
||||
struct rfcomm_msc *msc;
|
||||
struct rfcomm_dlc *d;
|
||||
u8 dlci = __get_dlci(msc->dlci);
|
||||
u8 dlci;
|
||||
|
||||
msc = skb_pull_data(skb, sizeof(*msc));
|
||||
if (!msc)
|
||||
return -EILSEQ;
|
||||
|
||||
dlci = __get_dlci(msc->dlci);
|
||||
BT_DBG("dlci %d cr %d v24 0x%x", dlci, cr, msc->v24_sig);
|
||||
|
||||
d = rfcomm_dlc_get(s, dlci);
|
||||
@@ -1644,17 +1673,19 @@ static int rfcomm_recv_msc(struct rfcomm_session *s, int cr, struct sk_buff *skb
|
||||
|
||||
static int rfcomm_recv_mcc(struct rfcomm_session *s, struct sk_buff *skb)
|
||||
{
|
||||
struct rfcomm_mcc *mcc = (void *) skb->data;
|
||||
struct rfcomm_mcc *mcc;
|
||||
u8 type, cr, len;
|
||||
|
||||
mcc = skb_pull_data(skb, sizeof(*mcc));
|
||||
if (!mcc)
|
||||
return -EILSEQ;
|
||||
|
||||
cr = __test_cr(mcc->type);
|
||||
type = __get_mcc_type(mcc->type);
|
||||
len = __get_mcc_len(mcc->len);
|
||||
|
||||
BT_DBG("%p type 0x%x cr %d", s, type, cr);
|
||||
|
||||
skb_pull(skb, 2);
|
||||
|
||||
switch (type) {
|
||||
case RFCOMM_PN:
|
||||
rfcomm_recv_pn(s, cr, skb);
|
||||
|
||||
@@ -122,7 +122,7 @@ static struct sock *__rfcomm_get_listen_sock_by_addr(u8 channel, bdaddr_t *src)
|
||||
}
|
||||
|
||||
/* Find socket with channel and source bdaddr.
|
||||
* Returns closest match.
|
||||
* Returns closest match with an extra reference held.
|
||||
*/
|
||||
static struct sock *rfcomm_get_sock_by_channel(int state, u8 channel, bdaddr_t *src)
|
||||
{
|
||||
@@ -136,15 +136,25 @@ static struct sock *rfcomm_get_sock_by_channel(int state, u8 channel, bdaddr_t *
|
||||
|
||||
if (rfcomm_pi(sk)->channel == channel) {
|
||||
/* Exact match. */
|
||||
if (!bacmp(&rfcomm_pi(sk)->src, src))
|
||||
if (!bacmp(&rfcomm_pi(sk)->src, src)) {
|
||||
sock_hold(sk);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Closest match */
|
||||
if (!bacmp(&rfcomm_pi(sk)->src, BDADDR_ANY))
|
||||
if (!bacmp(&rfcomm_pi(sk)->src, BDADDR_ANY)) {
|
||||
if (sk1)
|
||||
sock_put(sk1);
|
||||
|
||||
sk1 = sk;
|
||||
sock_hold(sk1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sk && sk1)
|
||||
sock_put(sk1);
|
||||
|
||||
read_unlock(&rfcomm_sk_list.lock);
|
||||
|
||||
return sk ? sk : sk1;
|
||||
@@ -941,6 +951,7 @@ int rfcomm_connect_ind(struct rfcomm_session *s, u8 channel, struct rfcomm_dlc *
|
||||
{
|
||||
struct sock *sk, *parent;
|
||||
bdaddr_t src, dst;
|
||||
bool defer_setup = false;
|
||||
int result = 0;
|
||||
|
||||
BT_DBG("session %p channel %d", s, channel);
|
||||
@@ -954,6 +965,11 @@ int rfcomm_connect_ind(struct rfcomm_session *s, u8 channel, struct rfcomm_dlc *
|
||||
|
||||
lock_sock(parent);
|
||||
|
||||
if (parent->sk_state != BT_LISTEN)
|
||||
goto done;
|
||||
|
||||
defer_setup = test_bit(BT_SK_DEFER_SETUP, &bt_sk(parent)->flags);
|
||||
|
||||
/* Check for backlog size */
|
||||
if (sk_acceptq_is_full(parent)) {
|
||||
BT_DBG("backlog full %d", parent->sk_ack_backlog);
|
||||
@@ -981,9 +997,11 @@ int rfcomm_connect_ind(struct rfcomm_session *s, u8 channel, struct rfcomm_dlc *
|
||||
done:
|
||||
release_sock(parent);
|
||||
|
||||
if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(parent)->flags))
|
||||
if (defer_setup)
|
||||
parent->sk_state_change(parent);
|
||||
|
||||
sock_put(parent);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -312,11 +312,21 @@ static int sco_connect(struct sock *sk)
|
||||
struct sco_conn *conn;
|
||||
struct hci_conn *hcon;
|
||||
struct hci_dev *hdev;
|
||||
bdaddr_t src, dst;
|
||||
struct bt_codec codec;
|
||||
__u16 setting;
|
||||
int err, type;
|
||||
|
||||
BT_DBG("%pMR -> %pMR", &sco_pi(sk)->src, &sco_pi(sk)->dst);
|
||||
lock_sock(sk);
|
||||
bacpy(&src, &sco_pi(sk)->src);
|
||||
bacpy(&dst, &sco_pi(sk)->dst);
|
||||
setting = sco_pi(sk)->setting;
|
||||
codec = sco_pi(sk)->codec;
|
||||
release_sock(sk);
|
||||
|
||||
hdev = hci_get_route(&sco_pi(sk)->dst, &sco_pi(sk)->src, BDADDR_BREDR);
|
||||
BT_DBG("%pMR -> %pMR", &src, &dst);
|
||||
|
||||
hdev = hci_get_route(&dst, &src, BDADDR_BREDR);
|
||||
if (!hdev)
|
||||
return -EHOSTUNREACH;
|
||||
|
||||
@@ -327,7 +337,7 @@ static int sco_connect(struct sock *sk)
|
||||
else
|
||||
type = SCO_LINK;
|
||||
|
||||
switch (sco_pi(sk)->setting & SCO_AIRMODE_MASK) {
|
||||
switch (setting & SCO_AIRMODE_MASK) {
|
||||
case SCO_AIRMODE_TRANSP:
|
||||
if (!lmp_transp_capable(hdev) || !lmp_esco_capable(hdev)) {
|
||||
err = -EOPNOTSUPP;
|
||||
@@ -336,8 +346,8 @@ static int sco_connect(struct sock *sk)
|
||||
break;
|
||||
}
|
||||
|
||||
hcon = hci_connect_sco(hdev, type, &sco_pi(sk)->dst,
|
||||
sco_pi(sk)->setting, &sco_pi(sk)->codec,
|
||||
hcon = hci_connect_sco(hdev, type, &dst,
|
||||
setting, &codec,
|
||||
READ_ONCE(sk->sk_sndtimeo));
|
||||
if (IS_ERR(hcon)) {
|
||||
err = PTR_ERR(hcon);
|
||||
|
||||
Reference in New Issue
Block a user