summaryrefslogtreecommitdiff
path: root/net/bluetooth
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-08-27 13:53:43 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-08-27 13:53:43 -0700
commit1b78070aaef63512688aebfbc82365ef9d6660f1 (patch)
tree691c0aeaa3d92278ceeb6ace56bc8cd56a7f2ae8 /net/bluetooth
parent3ba13f5e7180c034b0a1ef7e052fb780856b134e (diff)
parent4a9d62a8774f130a5b8de26ca9f415e6050a9d51 (diff)
downloadlinux-next-stable.tar.gz
linux-next-stable.zip
Merge tag 'net-7.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/netstable
Pull networking fixes from Jakub Kicinski: "Including fixes from Bluetooth, IPSec and Netfilter. Current release - fix to a fix: - netfilter: ipset: remove need to allocate memory on delete operations Current release - regressions: - macb: drop CONFIG_OF #if block, fix build Previous releases - always broken: - stream of fixes for SCTP continues - inet: frags: strip GSO state from fragments before reassembly - virtio-net: ensure that TCP packets don't overflow gso_segs - tcp-ao: fix use-after-free of current_key on reconnect to another peer - page_pool: remove zone/policy GFP flags when allocating XArray entries - Bluetooth: L2CAP: reject accept queue add unless BT_LISTEN - tls: device: fix out-of-bounds write in tls_append_frag() - eth: bnxt: - ring the doorbell when SW USO exits early, avoid packets stuck in Tx - gate TPH enablement behind BNXT_SUPPORTS_QUEUE_API check, avoid users of older NICs seeing non-actionable warning messages - eth: qede: fix NULL pointer dereference in TPA fragment processing" * tag 'net-7.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net: (216 commits) inet: frags: strip GSO state from fragments before reassembly net/sched: sch_htb: limit htb_classify inner-class filter hops selftests/net: packetdrill: add tcp_urg_ptr_retransmit tcp: fix corruption of urgent data on multi-segment retransmit usb: atm: usbatm: fix invalid ci_range initialization net: fec: only stop PTP if it was initialized slip: remove slip_hangup() to fix use-after-free in slip_receive_buf() net: bridge: mcast: fix use-after-free of a master VLAN's multicast context net/sched: bound qdisc_pkt_len to prevent qdisc soft lockup net: dsa: mxl862xx: enable assisted learning on CPU port net: stmmac: restore NET_IP_ALIGN in the RX DMA offset net: stmmac: drop gso_enabled_types and rely on netdev features net: stmmac: selftests: Don't test flow control for small rx fifos net: stmmac: selftests: Account for the UC filter list for filtering tests net: stmmac: dwxgmac: Account for the primary MAC address for UC filtering net: stmmac: dwmac4: Account for the primary MAC address for UC filtering net: stmmac: dwmac1000: Account for the primary MAC address for UC filtering net: stmmac: selftests: Check multiple MMC counters selftests: net: Fix slow configurations in big_tcp_tunnels.sh selftests: net: Lower threshold with csum offload off in big_tcp_tunnels.sh ...
Diffstat (limited to 'net/bluetooth')
-rw-r--r--net/bluetooth/eir.c3
-rw-r--r--net/bluetooth/hci_conn.c3
-rw-r--r--net/bluetooth/hci_core.c2
-rw-r--r--net/bluetooth/hci_event.c7
-rw-r--r--net/bluetooth/hci_sync.c16
-rw-r--r--net/bluetooth/iso.c8
-rw-r--r--net/bluetooth/l2cap_sock.c100
-rw-r--r--net/bluetooth/mgmt.c18
-rw-r--r--net/bluetooth/rfcomm/core.c14
9 files changed, 131 insertions, 40 deletions
diff --git a/net/bluetooth/eir.c b/net/bluetooth/eir.c
index 1de5f9df6eec..a55696820b22 100644
--- a/net/bluetooth/eir.c
+++ b/net/bluetooth/eir.c
@@ -369,6 +369,7 @@ u8 eir_create_scan_rsp(struct hci_dev *hdev, u8 instance, u8 *ptr)
void *eir_get_service_data(u8 *eir, size_t eir_len, u16 uuid, size_t *len)
{
+ const u8 *eir_end = eir + eir_len;
size_t dlen;
while ((eir = eir_get_data(eir, eir_len, EIR_SERVICE_DATA, &dlen))) {
@@ -381,7 +382,7 @@ void *eir_get_service_data(u8 *eir, size_t eir_len, u16 uuid, size_t *len)
}
eir += dlen;
- eir_len -= dlen;
+ eir_len = eir_end - eir;
}
return NULL;
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index 19b7629b1cc1..8de98af2fb58 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -1391,7 +1391,8 @@ static void hci_le_conn_failed(struct hci_conn *conn, u8 status)
/* Enable advertising in case this was a failed connection
* attempt as a peripheral.
*/
- hci_enable_advertising(hdev);
+ if (conn->role == HCI_ROLE_SLAVE)
+ hci_enable_advertising(hdev);
}
/* This function requires the caller holds hdev->lock */
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 509c820a693d..35a1be57e386 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -4093,7 +4093,7 @@ static int hci_send_cmd_sync(struct hci_dev *hdev, struct sk_buff *skb)
if (READ_ONCE(hdev->req_status) == HCI_REQ_PEND &&
!hci_dev_test_and_set_flag(hdev, HCI_CMD_PENDING)) {
kfree_skb(hdev->req_skb);
- hdev->req_skb = skb_clone(hdev->sent_cmd, GFP_KERNEL);
+ hdev->req_skb = skb_get(hdev->sent_cmd);
}
return err;
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 3eb1eaf6e6a0..2f5e21ff9752 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -5763,10 +5763,11 @@ static void le_conn_complete_evt(struct hci_dev *hdev, u8 status,
hci_dev_lock(hdev);
hci_store_wake_reason(hdev, bdaddr, bdaddr_type);
- /* All controllers implicitly stop advertising in the event of a
- * connection, so ensure that the state bit is cleared.
+ /* Advertising stops when a connection is created. On a failed
+ * connection it keeps running, so leave the state bit alone.
*/
- hci_dev_clear_flag(hdev, HCI_LE_ADV);
+ if (!status)
+ hci_dev_clear_flag(hdev, HCI_LE_ADV);
/* Check for existing connection:
*
diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
index b5897545d795..ffd7b37e7401 100644
--- a/net/bluetooth/hci_sync.c
+++ b/net/bluetooth/hci_sync.c
@@ -1287,6 +1287,7 @@ hci_set_ext_adv_params_sync(struct hci_dev *hdev, u8 instance,
}
static int hci_set_ext_adv_data_sync(struct hci_dev *hdev, u8 instance)
+ __context_unsafe(/* conditional locking */)
{
DEFINE_FLEX(struct hci_cp_le_set_ext_adv_data, pdu, data, length,
HCI_MAX_EXT_AD_LENGTH);
@@ -1375,6 +1376,7 @@ int hci_update_adv_data_sync(struct hci_dev *hdev, u8 instance)
}
int hci_setup_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance)
+ __context_unsafe(/* conditional locking */)
{
struct hci_cp_le_set_ext_adv_params cp;
struct hci_rp_le_set_ext_adv_params rp;
@@ -1535,6 +1537,7 @@ int hci_setup_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance)
}
static int hci_set_ext_scan_rsp_data_sync(struct hci_dev *hdev, u8 instance)
+ __context_unsafe(/* conditional locking */)
{
DEFINE_FLEX(struct hci_cp_le_set_ext_scan_rsp_data, pdu, data, length,
HCI_MAX_EXT_AD_LENGTH);
@@ -1588,6 +1591,7 @@ static int hci_set_ext_scan_rsp_data_sync(struct hci_dev *hdev, u8 instance)
}
static int __hci_set_scan_rsp_data_sync(struct hci_dev *hdev, u8 instance)
+ __context_unsafe(/* conditional locking */)
{
struct hci_cp_le_set_scan_rsp_data cp;
u8 len;
@@ -1729,6 +1733,7 @@ static int hci_set_per_adv_params_sync(struct hci_dev *hdev, u8 instance,
}
static int hci_set_per_adv_data_sync(struct hci_dev *hdev, u8 instance)
+ __context_unsafe(/* conditional locking */)
{
DEFINE_FLEX(struct hci_cp_le_set_per_adv_data, pdu, data, length,
HCI_MAX_PER_AD_LENGTH);
@@ -5448,6 +5453,7 @@ int hci_dev_open_sync(struct hci_dev *hdev)
if (hdev->req_skb) {
kfree_skb(hdev->req_skb);
hdev->req_skb = NULL;
+ hci_dev_clear_flag(hdev, HCI_CMD_PENDING);
}
clear_bit(HCI_RUNNING, &hdev->flags);
@@ -5632,6 +5638,7 @@ int hci_dev_close_sync(struct hci_dev *hdev)
if (hdev->req_skb) {
kfree_skb(hdev->req_skb);
hdev->req_skb = NULL;
+ hci_dev_clear_flag(hdev, HCI_CMD_PENDING);
}
clear_bit(HCI_RUNNING, &hdev->flags);
@@ -7282,8 +7289,13 @@ static void create_le_conn_complete(struct hci_dev *hdev, void *data, int err)
goto unlock;
}
- /* Check if connection is still pending */
- if (conn != hci_lookup_le_connect(hdev))
+ /* Check if this connection is still pending.
+ *
+ * hci_lookup_le_connect() returns only the first LE connection
+ * in BT_CONNECT, which is not necessarily this one when two are
+ * pending at once, so ask the connection itself.
+ */
+ if (conn->state != BT_CONNECT)
goto unlock;
/* Flush to make sure we send create conn cancel command if needed */
diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
index aa2ce78f56a2..75bfd5938b2e 100644
--- a/net/bluetooth/iso.c
+++ b/net/bluetooth/iso.c
@@ -2277,6 +2277,14 @@ static void iso_conn_ready(struct iso_conn *conn)
lock_sock(parent);
+ /* The listener may have been closed concurrently. */
+ if (parent->sk_state != BT_LISTEN ||
+ sock_flag(parent, SOCK_ZAPPED)) {
+ release_sock(parent);
+ sock_put(parent);
+ return;
+ }
+
sk = iso_sock_alloc(sock_net(parent), NULL,
BTPROTO_ISO, GFP_ATOMIC, 0);
if (!sk) {
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index 735167f73f31..1194c37e466f 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -436,11 +436,26 @@ static int l2cap_get_mode(struct l2cap_chan *chan)
return -EINVAL;
}
+static struct l2cap_conn *l2cap_chan_conn(struct l2cap_chan *chan)
+{
+ lockdep_assert_held(&chan->lock);
+
+ /* l2cap_conn_del() sets FLAG_DEL while holding chan->lock before
+ * conn->hcon is deleted. If not set and conn is non-NULL, conn->hcon
+ * remains alive during this chan->lock critical section.
+ */
+ if (test_bit(FLAG_DEL, &chan->flags))
+ return NULL;
+
+ return chan->conn;
+}
+
static int l2cap_sock_getsockopt_old(struct socket *sock, int optname,
sockopt_t *sopt)
{
struct sock *sk = sock->sk;
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
+ struct l2cap_conn *conn;
struct l2cap_options opts;
struct l2cap_conninfo cinfo;
int err = 0;
@@ -451,6 +466,7 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname,
len = sopt->optlen;
+ l2cap_chan_lock(chan);
lock_sock(sk);
switch (optname) {
@@ -537,9 +553,15 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname,
break;
}
+ conn = l2cap_chan_conn(chan);
+ if (!conn) {
+ err = -ENOTCONN;
+ break;
+ }
+
memset(&cinfo, 0, sizeof(cinfo));
- cinfo.hci_handle = chan->conn->hcon->handle;
- memcpy(cinfo.dev_class, chan->conn->hcon->dev_class, 3);
+ cinfo.hci_handle = conn->hcon->handle;
+ memcpy(cinfo.dev_class, conn->hcon->dev_class, 3);
len = min(len, sizeof(cinfo));
if (copy_to_iter(&cinfo, len, &sopt->iter_out) != len)
@@ -553,6 +575,8 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname,
}
release_sock(sk);
+ l2cap_chan_unlock(chan);
+
return err;
}
@@ -561,6 +585,7 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
{
struct sock *sk = sock->sk;
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
+ struct l2cap_conn *conn;
struct bt_security sec;
struct bt_power pwr;
int len, mode, err = 0;
@@ -578,6 +603,7 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
len = sopt->optlen;
+ l2cap_chan_lock(chan);
lock_sock(sk);
switch (optname) {
@@ -589,12 +615,14 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
break;
}
+ conn = l2cap_chan_conn(chan);
+
memset(&sec, 0, sizeof(sec));
- if (chan->conn) {
- sec.level = chan->conn->hcon->sec_level;
+ if (conn) {
+ sec.level = conn->hcon->sec_level;
if (sk->sk_state == BT_CONNECTED)
- sec.key_size = chan->conn->hcon->enc_key_size;
+ sec.key_size = conn->hcon->enc_key_size;
} else {
sec.level = chan->sec_level;
}
@@ -678,12 +706,14 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
break;
case BT_PHY:
- if (sk->sk_state != BT_CONNECTED) {
+ conn = l2cap_chan_conn(chan);
+
+ if (sk->sk_state != BT_CONNECTED || !conn) {
err = -ENOTCONN;
break;
}
- opt = hci_conn_get_phy(chan->conn->hcon);
+ opt = hci_conn_get_phy(conn->hcon);
if (copy_to_iter(&opt, sizeof(opt), &sopt->iter_out) !=
sizeof(opt))
@@ -719,6 +749,7 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
}
release_sock(sk);
+ l2cap_chan_unlock(chan);
return err;
}
@@ -749,6 +780,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname,
BT_DBG("sk %p", sk);
+ l2cap_chan_lock(chan);
lock_sock(sk);
switch (optname) {
@@ -850,6 +882,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname,
}
release_sock(sk);
+ l2cap_chan_unlock(chan);
return err;
}
@@ -913,6 +946,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
if (level != SOL_BLUETOOTH)
return -ENOPROTOOPT;
+ l2cap_chan_lock(chan);
lock_sock(sk);
switch (optname) {
@@ -938,11 +972,10 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
chan->sec_level = sec.level;
- if (!chan->conn)
+ conn = l2cap_chan_conn(chan);
+ if (!conn)
break;
- conn = chan->conn;
-
/* change security for LE channels */
if (chan->scid == L2CAP_CID_ATT) {
if (smp_conn_security(conn->hcon, sec.level)) {
@@ -997,7 +1030,8 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
}
if (opt == BT_FLUSHABLE_OFF) {
- conn = chan->conn;
+ conn = l2cap_chan_conn(chan);
+
/* proceed further only when we have l2cap_conn and
No Flush support in the LM */
if (!conn || !lmp_no_flush_capable(conn->hcon->hdev)) {
@@ -1083,7 +1117,8 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
break;
case BT_PHY:
- if (sk->sk_state != BT_CONNECTED) {
+ conn = l2cap_chan_conn(chan);
+ if (sk->sk_state != BT_CONNECTED || !conn) {
err = -ENOTCONN;
break;
}
@@ -1093,10 +1128,6 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
if (err)
break;
- if (!chan->conn)
- break;
-
- conn = chan->conn;
err = hci_conn_set_phy(conn->hcon, phys);
break;
@@ -1139,6 +1170,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
}
release_sock(sk);
+ l2cap_chan_unlock(chan);
return err;
}
@@ -1312,7 +1344,12 @@ static void l2cap_sock_kill(struct sock *sk)
BT_DBG("sk %p state %s", sk, state_to_string(sk->sk_state));
+ /* Take lock to synchronize against access without owning sk->sk_socket,
+ * eg. in l2cap_sock_cleanup_listen(). proto_ops etc. don't need lock.
+ */
+ lock_sock(sk);
l2cap_sock_put_chan(sk);
+ release_sock(sk);
/* Kill poor orphan */
sock_set_flag(sk, SOCK_DEAD);
@@ -1516,14 +1553,10 @@ static void l2cap_sock_cleanup_listen(struct sock *parent)
* establish sk_lock -> conn->lock and invert the established
* conn->lock -> chan->lock -> sk_lock order (lockdep deadlock).
*
- * Instead, briefly take the child sk lock to fetch and pin its chan.
- * l2cap_conn_del() reaches the chan free only via
- * l2cap_chan_del() -> l2cap_sock_teardown_cb(), which itself takes
- * the child sk lock; holding it across l2cap_chan_hold_unless_zero()
- * therefore guarantees the chan cannot be freed while we read and
- * pin it (hold_unless_zero() additionally skips a chan already past
- * its last reference). We then drop the sk lock before taking
- * chan->lock, so sk and chan locks are never held together.
+ * Instead, briefly take the child sk lock to synchronize vs.
+ * l2cap_sock_kill that puts l2cap_pi(sk)->chan. We then drop the sk
+ * lock before taking chan->lock, so sk and chan locks are never held
+ * together.
*
* Since we cannot call l2cap_chan_close() without conn->lock,
* schedule l2cap_chan_timeout to close the channel; it already
@@ -1533,10 +1566,12 @@ static void l2cap_sock_cleanup_listen(struct sock *parent)
struct l2cap_chan *chan;
lock_sock_nested(sk, L2CAP_NESTING_NORMAL);
- chan = l2cap_chan_hold_unless_zero(l2cap_pi(sk)->chan);
+ chan = l2cap_pi(sk)->chan;
+ if (chan)
+ l2cap_chan_hold(chan);
release_sock(sk);
if (!chan) {
- /* l2cap_conn_del() already tearing this child down */
+ /* Already torn down */
sock_put(sk);
continue;
}
@@ -1568,6 +1603,11 @@ static int l2cap_sock_new_connection_cb(struct l2cap_chan *chan,
lock_sock(parent);
+ if (parent->sk_state != BT_LISTEN) {
+ release_sock(parent);
+ return -EINVAL;
+ }
+
/* Check for backlog size */
if (sk_acceptq_is_full(parent)) {
BT_DBG("backlog full %d", parent->sk_ack_backlog);
@@ -1731,10 +1771,14 @@ static void l2cap_sock_state_change_cb(struct l2cap_chan *chan, int state,
if (!sk)
return;
+ lock_sock(sk);
+
sk->sk_state = state;
if (err)
sk->sk_err = err;
+
+ release_sock(sk);
}
static struct sk_buff *l2cap_sock_alloc_skb_cb(struct l2cap_chan *chan,
@@ -1810,6 +1854,8 @@ static void l2cap_sock_resume_cb(struct l2cap_chan *chan)
if (!sk)
return;
+ lock_sock(sk);
+
if (test_and_clear_bit(FLAG_PENDING_SECURITY, &chan->flags)) {
sk->sk_state = BT_CONNECTED;
chan->state = BT_CONNECTED;
@@ -1817,6 +1863,8 @@ static void l2cap_sock_resume_cb(struct l2cap_chan *chan)
clear_bit(BT_SK_SUSPEND, &bt_sk(sk)->flags);
sk->sk_state_change(sk);
+
+ release_sock(sk);
}
static void l2cap_sock_set_shutdown_cb(struct l2cap_chan *chan)
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 860c086011b7..ac4864e56ec7 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -6171,6 +6171,7 @@ static int start_service_discovery(struct sock *sk, struct hci_dev *hdev,
struct mgmt_pending_cmd *cmd;
const u16 max_uuid_count = ((U16_MAX - sizeof(*cp)) / 16);
u16 uuid_count, expected_len;
+ u8 (*uuids)[16] = NULL;
u8 status;
int err;
@@ -6247,12 +6248,10 @@ static int start_service_discovery(struct sock *sk, struct hci_dev *hdev,
hdev->discovery.result_filtering = true;
hdev->discovery.type = cp->type;
hdev->discovery.rssi = cp->rssi;
- hdev->discovery.uuid_count = uuid_count;
if (uuid_count > 0) {
- hdev->discovery.uuids = kmemdup(cp->uuids, uuid_count * 16,
- GFP_KERNEL);
- if (!hdev->discovery.uuids) {
+ uuids = kmemdup(cp->uuids, uuid_count * sizeof(*uuids), GFP_KERNEL);
+ if (!uuids) {
err = mgmt_cmd_complete(sk, hdev->id,
MGMT_OP_START_SERVICE_DISCOVERY,
MGMT_STATUS_FAILED,
@@ -6262,6 +6261,11 @@ static int start_service_discovery(struct sock *sk, struct hci_dev *hdev,
}
}
+ spin_lock(&hdev->discovery.lock);
+ hdev->discovery.uuids = uuids;
+ hdev->discovery.uuid_count = uuid_count;
+ spin_unlock(&hdev->discovery.lock);
+
err = hci_cmd_sync_queue(hdev, start_discovery_sync, cmd,
start_discovery_complete);
if (err < 0) {
@@ -10505,6 +10509,7 @@ static bool is_filter_match(struct hci_dev *hdev, s8 rssi, u8 *eir,
!hci_test_quirk(hdev, HCI_QUIRK_STRICT_DUPLICATE_FILTER))))
return false;
+ spin_lock(&hdev->discovery.lock);
if (hdev->discovery.uuid_count != 0) {
/* If a list of UUIDs is provided in filter, results with no
* matching UUID should be dropped.
@@ -10513,9 +10518,12 @@ static bool is_filter_match(struct hci_dev *hdev, s8 rssi, u8 *eir,
hdev->discovery.uuids) &&
!eir_has_uuids(scan_rsp, scan_rsp_len,
hdev->discovery.uuid_count,
- hdev->discovery.uuids))
+ hdev->discovery.uuids)) {
+ spin_unlock(&hdev->discovery.lock);
return false;
+ }
}
+ spin_unlock(&hdev->discovery.lock);
/* If duplicate filtering does not report RSSI changes, then restart
* scanning to ensure updated result with updated RSSI values.
diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c
index 9cdfea666a2c..f7463f092283 100644
--- a/net/bluetooth/rfcomm/core.c
+++ b/net/bluetooth/rfcomm/core.c
@@ -1455,6 +1455,10 @@ static int rfcomm_apply_pn(struct rfcomm_dlc *d, int cr, struct rfcomm_pn *pn)
d->mtu = __le16_to_cpu(pn->mtu);
+ /* MTU 0 causes an infinite loop when fragmenting in sendmsg */
+ if (!d->mtu)
+ d->mtu = RFCOMM_DEFAULT_MTU;
+
if (cr && d->mtu > s->mtu)
d->mtu = s->mtu;
@@ -2178,8 +2182,10 @@ static void rfcomm_kill_listener(void)
BT_DBG("");
+ rfcomm_lock();
list_for_each_entry_safe(s, n, &session_list, list)
rfcomm_session_del(s);
+ rfcomm_unlock();
}
static int rfcomm_run(void *unused)
@@ -2213,9 +2219,13 @@ static void rfcomm_security_cfm(struct hci_conn *conn, u8 status, u8 encrypt)
BT_DBG("conn %p status 0x%02x encrypt 0x%02x", conn, status, encrypt);
+ rfcomm_lock();
+
s = rfcomm_session_get(&conn->hdev->bdaddr, &conn->dst);
- if (!s)
+ if (!s) {
+ rfcomm_unlock();
return;
+ }
list_for_each_entry_safe(d, n, &s->dlcs, list) {
if (test_and_clear_bit(RFCOMM_SEC_PENDING, &d->flags)) {
@@ -2247,6 +2257,8 @@ static void rfcomm_security_cfm(struct hci_conn *conn, u8 status, u8 encrypt)
set_bit(RFCOMM_AUTH_REJECT, &d->flags);
}
+ rfcomm_unlock();
+
rfcomm_schedule();
}