mirror of
https://github.com/torvalds/linux.git
synced 2026-06-02 11:33:28 +02:00
bluetooth pull request for net:
- hci_core: Rework hci_dev_do_reset() to use hci_sync functions - hci_conn: Fix memory leak in hci_le_big_terminate() - hci_sync: Set HCI_CMD_DRAIN_WORKQUEUE during device close - hci_sync: Reset device counters in hci_dev_close_sync() - hci_sync: fix UAF in hci_le_create_cis_sync - L2CAP: Fix possible crash on l2cap_ecred_conn_rsp - L2CAP: fix chan ref leak in l2cap_chan_timeout() on !conn - L2CAP: use chan timer to close channels in cleanup_listen() - L2CAP: clear chan->ident on ECRED reconfiguration success - ISO: fix UAF in iso_recv_frame - ISO: serialize iso_sock_clear_timer with socket lock - HIDP: fix missing length checks in hidp_input_report() - 6lowpan: check skb_clone() return value in send_mcast_pkt() - btusb: Allow firmware re-download when version matches - hci_qca: Use 100 ms SSR delay for rampatch and NVM loading -----BEGIN PGP SIGNATURE----- iQJNBAABCgA3FiEE7E6oRXp8w05ovYr/9JCA4xAyCykFAmoYP6oZHGx1aXoudm9u LmRlbnR6QGludGVsLmNvbQAKCRD0kIDjEDILKX9SD/9+09g+LpkrBUyTsqaMiWii 4BNwbGG1kTzof/3pxmjxsJNbOi1u7wpA6gyGo9Opaaz3IjE5ZYnwblhSCjf7i0Qz Txl8p5DPmTBHBevZLDA0OrTtxAtNKlOzGQLs/nrWikHvEBy0F20e5defcv9IMaAy XAyv++EyZFznqwGU0iV2avXF4Gnfs6F1WL5f42jl0zwahB3pKZZik1HYOkg9JsIm 9/6qlBStf4VxATF+vN5OqY9K6xV2bPVC/htoBP4oMUbaDXbgZgDoTYScyvBOktBZ DenyFzWogGKRWrOxgWA9RYnQ0VU+Orx0C5gf+DLbUY2uMGMlEwVAOBUUbDUsgsz8 m97hfJamNcpVv9+40v8QfYc1gF+k5OYQx1UCAvRSdmnkdycukeZXfScwx/35jHqj BFExP7b5UtphUfjUiP/nEqZogiV4HXPoWF+X4Wnoe80dNhX0JGsjBjlBc6Da4ZOF zdn9l+Qc/OvaMJV2OZIHFu28BBpgBAp+ujOhMRtlvSgvi1XUkf1InFEWdIg+NpiE 8JOD63huNXeGzKLhwyMfIQ8zsMNwQpn+gnE4vqHal6GhYhAMhSBtJxLBAdc8aZQR 91SFDl96ghNrRXfQlWfN7g2TdTZKCUzD5ZmOl1s/iv1rqENYA6WCwLXHe/6I03mT hpx6ALMEjS8qud43JFfOOg== =7Nv9 -----END PGP SIGNATURE----- Merge tag 'for-net-2026-05-28' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth Luiz Augusto von Dentz says: ==================== bluetooth pull request for net: - hci_core: Rework hci_dev_do_reset() to use hci_sync functions - hci_conn: Fix memory leak in hci_le_big_terminate() - hci_sync: Set HCI_CMD_DRAIN_WORKQUEUE during device close - hci_sync: Reset device counters in hci_dev_close_sync() - hci_sync: fix UAF in hci_le_create_cis_sync - L2CAP: Fix possible crash on l2cap_ecred_conn_rsp - L2CAP: fix chan ref leak in l2cap_chan_timeout() on !conn - L2CAP: use chan timer to close channels in cleanup_listen() - L2CAP: clear chan->ident on ECRED reconfiguration success - ISO: fix UAF in iso_recv_frame - ISO: serialize iso_sock_clear_timer with socket lock - HIDP: fix missing length checks in hidp_input_report() - 6lowpan: check skb_clone() return value in send_mcast_pkt() - btusb: Allow firmware re-download when version matches - hci_qca: Use 100 ms SSR delay for rampatch and NVM loading * tag 'for-net-2026-05-28' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth: Bluetooth: hci_sync: Reset device counters in hci_dev_close_sync() Bluetooth: hci_sync: Set HCI_CMD_DRAIN_WORKQUEUE during device close Bluetooth: hci_core: Rework hci_dev_do_reset() to use hci_sync functions Bluetooth: ISO: serialize iso_sock_clear_timer with socket lock Bluetooth: ISO: fix UAF in iso_recv_frame Bluetooth: L2CAP: Fix possible crash on l2cap_ecred_conn_rsp Bluetooth: l2cap: clear chan->ident on ECRED reconfiguration success Bluetooth: hci_qca: Use 100 ms SSR delay for rampatch and NVM loading Bluetooth: hci_sync: fix UAF in hci_le_create_cis_sync Bluetooth: 6lowpan: check skb_clone() return value in send_mcast_pkt() Bluetooth: btusb: Allow firmware re-download when version matches Bluetooth: HIDP: fix missing length checks in hidp_input_report() Bluetooth: L2CAP: use chan timer to close channels in cleanup_listen() Bluetooth: L2CAP: fix chan ref leak in l2cap_chan_timeout() on !conn Bluetooth: hci_conn: Fix memory leak in hci_le_big_terminate() ==================== Link: https://patch.msgid.link/20260528131839.462344-1-luiz.dentz@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
2412591cfe
|
|
@ -3540,7 +3540,13 @@ static int btusb_setup_qca_load_rampatch(struct hci_dev *hdev,
|
|||
"firmware rome 0x%x build 0x%x",
|
||||
rver_rom, rver_patch, ver_rom, ver_patch);
|
||||
|
||||
if (rver_rom != ver_rom || rver_patch <= ver_patch) {
|
||||
/* Allow rampatch when the patch version equals the firmware version.
|
||||
* A firmware download may be aborted by a transient USB error (e.g.
|
||||
* disconnect) after the controller updates version info but before
|
||||
* completion.
|
||||
* Allowing equal versions enables re-flashing during recovery.
|
||||
*/
|
||||
if (rver_rom != ver_rom || rver_patch < ver_patch) {
|
||||
bt_dev_err(hdev, "rampatch file version did not match with firmware");
|
||||
err = -EINVAL;
|
||||
goto done;
|
||||
|
|
|
|||
|
|
@ -1680,8 +1680,8 @@ static void qca_hw_error(struct hci_dev *hdev, u8 code)
|
|||
mod_timer(&qca->tx_idle_timer, jiffies +
|
||||
msecs_to_jiffies(qca->tx_idle_delay));
|
||||
|
||||
/* Controller reset completion time is 50ms */
|
||||
msleep(50);
|
||||
/* Wait for the controller to load the rampatch and NVM. */
|
||||
msleep(100);
|
||||
|
||||
clear_bit(QCA_SSR_TRIGGERED, &qca->flags);
|
||||
clear_bit(QCA_IBS_DISABLED, &qca->flags);
|
||||
|
|
|
|||
|
|
@ -486,6 +486,8 @@ static int send_mcast_pkt(struct sk_buff *skb, struct net_device *netdev)
|
|||
int ret;
|
||||
|
||||
local_skb = skb_clone(skb, GFP_ATOMIC);
|
||||
if (!local_skb)
|
||||
continue;
|
||||
|
||||
BT_DBG("xmit %s to %pMR type %u IP %pI6c chan %p",
|
||||
netdev->name,
|
||||
|
|
|
|||
|
|
@ -870,8 +870,10 @@ static int hci_le_big_terminate(struct hci_dev *hdev, struct hci_conn *conn)
|
|||
d->big_sync_term = true;
|
||||
}
|
||||
|
||||
if (!d->pa_sync_term && !d->big_sync_term)
|
||||
if (!d->pa_sync_term && !d->big_sync_term) {
|
||||
kfree(d);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = hci_cmd_sync_queue(hdev, big_terminate_sync, d,
|
||||
terminate_big_destroy);
|
||||
|
|
|
|||
|
|
@ -539,46 +539,9 @@ static int hci_dev_do_reset(struct hci_dev *hdev)
|
|||
|
||||
hci_req_sync_lock(hdev);
|
||||
|
||||
/* Drop queues */
|
||||
skb_queue_purge(&hdev->rx_q);
|
||||
skb_queue_purge(&hdev->cmd_q);
|
||||
|
||||
/* Cancel these to avoid queueing non-chained pending work */
|
||||
hci_dev_set_flag(hdev, HCI_CMD_DRAIN_WORKQUEUE);
|
||||
/* Wait for
|
||||
*
|
||||
* if (!hci_dev_test_flag(hdev, HCI_CMD_DRAIN_WORKQUEUE))
|
||||
* queue_delayed_work(&hdev->{cmd,ncmd}_timer)
|
||||
*
|
||||
* inside RCU section to see the flag or complete scheduling.
|
||||
*/
|
||||
synchronize_rcu();
|
||||
/* Explicitly cancel works in case scheduled after setting the flag. */
|
||||
cancel_delayed_work(&hdev->cmd_timer);
|
||||
cancel_delayed_work(&hdev->ncmd_timer);
|
||||
|
||||
/* Avoid potential lockdep warnings from the *_flush() calls by
|
||||
* ensuring the workqueue is empty up front.
|
||||
*/
|
||||
drain_workqueue(hdev->workqueue);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
hci_inquiry_cache_flush(hdev);
|
||||
hci_conn_hash_flush(hdev);
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
if (hdev->flush)
|
||||
hdev->flush(hdev);
|
||||
|
||||
hci_dev_clear_flag(hdev, HCI_CMD_DRAIN_WORKQUEUE);
|
||||
|
||||
atomic_set(&hdev->cmd_cnt, 1);
|
||||
hdev->acl_cnt = 0;
|
||||
hdev->sco_cnt = 0;
|
||||
hdev->le_cnt = 0;
|
||||
hdev->iso_cnt = 0;
|
||||
|
||||
ret = hci_reset_sync(hdev);
|
||||
ret = hci_dev_close_sync(hdev);
|
||||
if (!ret)
|
||||
ret = hci_dev_open_sync(hdev);
|
||||
|
||||
hci_req_sync_unlock(hdev);
|
||||
return ret;
|
||||
|
|
|
|||
|
|
@ -5301,6 +5301,12 @@ int hci_dev_close_sync(struct hci_dev *hdev)
|
|||
|
||||
bt_dev_dbg(hdev, "");
|
||||
|
||||
/* Set HCI_DRAIN_WORKQUEUE flag to prevent queuing work during
|
||||
* reset/close. See hci_cmd_work() and handle_cmd_cnt_and_timer().
|
||||
*/
|
||||
hci_dev_set_flag(hdev, HCI_CMD_DRAIN_WORKQUEUE);
|
||||
synchronize_rcu();
|
||||
|
||||
if (hci_dev_test_flag(hdev, HCI_UNREGISTER)) {
|
||||
disable_delayed_work(&hdev->power_off);
|
||||
disable_delayed_work(&hdev->ncmd_timer);
|
||||
|
|
@ -5324,6 +5330,7 @@ int hci_dev_close_sync(struct hci_dev *hdev)
|
|||
|
||||
if (!test_and_clear_bit(HCI_UP, &hdev->flags)) {
|
||||
cancel_delayed_work_sync(&hdev->cmd_timer);
|
||||
hci_dev_clear_flag(hdev, HCI_CMD_DRAIN_WORKQUEUE);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
@ -5386,6 +5393,10 @@ int hci_dev_close_sync(struct hci_dev *hdev)
|
|||
/* Reset device */
|
||||
skb_queue_purge(&hdev->cmd_q);
|
||||
atomic_set(&hdev->cmd_cnt, 1);
|
||||
hdev->acl_cnt = 0;
|
||||
hdev->sco_cnt = 0;
|
||||
hdev->le_cnt = 0;
|
||||
hdev->iso_cnt = 0;
|
||||
if (hci_test_quirk(hdev, HCI_QUIRK_RESET_ON_CLOSE) &&
|
||||
!auto_off && !hci_dev_test_flag(hdev, HCI_UNCONFIGURED)) {
|
||||
set_bit(HCI_INIT, &hdev->flags);
|
||||
|
|
@ -5423,6 +5434,7 @@ int hci_dev_close_sync(struct hci_dev *hdev)
|
|||
/* Clear flags */
|
||||
hdev->flags &= BIT(HCI_RAW);
|
||||
hci_dev_clear_volatile_flags(hdev);
|
||||
hci_dev_clear_flag(hdev, HCI_CMD_DRAIN_WORKQUEUE);
|
||||
|
||||
memset(hdev->eir, 0, sizeof(hdev->eir));
|
||||
memset(hdev->dev_class, 0, sizeof(hdev->dev_class));
|
||||
|
|
@ -6699,6 +6711,7 @@ int hci_le_create_cis_sync(struct hci_dev *hdev)
|
|||
DEFINE_FLEX(struct hci_cp_le_create_cis, cmd, cis, num_cis, 0x1f);
|
||||
size_t aux_num_cis = 0;
|
||||
struct hci_conn *conn;
|
||||
u16 timeout = 0;
|
||||
u8 cig = BT_ISO_QOS_CIG_UNSET;
|
||||
|
||||
/* The spec allows only one pending LE Create CIS command at a time. If
|
||||
|
|
@ -6769,6 +6782,7 @@ int hci_le_create_cis_sync(struct hci_dev *hdev)
|
|||
set_bit(HCI_CONN_CREATE_CIS, &conn->flags);
|
||||
cis->acl_handle = cpu_to_le16(conn->parent->handle);
|
||||
cis->cis_handle = cpu_to_le16(conn->handle);
|
||||
timeout = conn->conn_timeout;
|
||||
aux_num_cis++;
|
||||
|
||||
if (aux_num_cis >= cmd->num_cis)
|
||||
|
|
@ -6788,7 +6802,7 @@ int hci_le_create_cis_sync(struct hci_dev *hdev)
|
|||
return __hci_cmd_sync_status_sk(hdev, HCI_OP_LE_CREATE_CIS,
|
||||
struct_size(cmd, cis, cmd->num_cis),
|
||||
cmd, HCI_EVT_LE_CIS_ESTABLISHED,
|
||||
conn->conn_timeout, NULL);
|
||||
timeout, NULL);
|
||||
}
|
||||
|
||||
int hci_le_remove_cig_sync(struct hci_dev *hdev, u8 handle)
|
||||
|
|
|
|||
|
|
@ -179,12 +179,21 @@ static void hidp_input_report(struct hidp_session *session, struct sk_buff *skb)
|
|||
{
|
||||
struct input_dev *dev = session->input;
|
||||
unsigned char *keys = session->keys;
|
||||
unsigned char *udata = skb->data + 1;
|
||||
signed char *sdata = skb->data + 1;
|
||||
int i, size = skb->len - 1;
|
||||
unsigned char *udata;
|
||||
signed char *sdata;
|
||||
u8 *hdr;
|
||||
int i;
|
||||
|
||||
switch (skb->data[0]) {
|
||||
hdr = skb_pull_data(skb, 1);
|
||||
if (!hdr)
|
||||
return;
|
||||
|
||||
switch (*hdr) {
|
||||
case 0x01: /* Keyboard report */
|
||||
udata = skb_pull_data(skb, 8);
|
||||
if (!udata)
|
||||
break;
|
||||
|
||||
for (i = 0; i < 8; i++)
|
||||
input_report_key(dev, hidp_keycode[i + 224], (udata[0] >> i) & 1);
|
||||
|
||||
|
|
@ -213,6 +222,10 @@ static void hidp_input_report(struct hidp_session *session, struct sk_buff *skb)
|
|||
break;
|
||||
|
||||
case 0x02: /* Mouse report */
|
||||
sdata = skb_pull_data(skb, 3);
|
||||
if (!sdata)
|
||||
break;
|
||||
|
||||
input_report_key(dev, BTN_LEFT, sdata[0] & 0x01);
|
||||
input_report_key(dev, BTN_RIGHT, sdata[0] & 0x02);
|
||||
input_report_key(dev, BTN_MIDDLE, sdata[0] & 0x04);
|
||||
|
|
@ -222,7 +235,7 @@ static void hidp_input_report(struct hidp_session *session, struct sk_buff *skb)
|
|||
input_report_rel(dev, REL_X, sdata[1]);
|
||||
input_report_rel(dev, REL_Y, sdata[2]);
|
||||
|
||||
if (size > 3)
|
||||
if (skb->len > 0)
|
||||
input_report_rel(dev, REL_WHEEL, sdata[3]);
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -564,7 +564,7 @@ static void iso_recv_frame(struct iso_conn *conn, struct sk_buff *skb)
|
|||
struct sock *sk;
|
||||
|
||||
iso_conn_lock(conn);
|
||||
sk = conn->sk;
|
||||
sk = iso_sock_hold(conn);
|
||||
iso_conn_unlock(conn);
|
||||
|
||||
if (!sk)
|
||||
|
|
@ -573,11 +573,15 @@ static void iso_recv_frame(struct iso_conn *conn, struct sk_buff *skb)
|
|||
BT_DBG("sk %p len %d", sk, skb->len);
|
||||
|
||||
if (sk->sk_state != BT_CONNECTED)
|
||||
goto drop;
|
||||
goto drop_put;
|
||||
|
||||
if (!sock_queue_rcv_skb(sk, skb))
|
||||
if (!sock_queue_rcv_skb(sk, skb)) {
|
||||
sock_put(sk);
|
||||
return;
|
||||
}
|
||||
|
||||
drop_put:
|
||||
sock_put(sk);
|
||||
drop:
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
|
@ -860,8 +864,8 @@ static void __iso_sock_close(struct sock *sk)
|
|||
/* Must be called on unlocked socket. */
|
||||
static void iso_sock_close(struct sock *sk)
|
||||
{
|
||||
iso_sock_clear_timer(sk);
|
||||
lock_sock(sk);
|
||||
iso_sock_clear_timer(sk);
|
||||
__iso_sock_close(sk);
|
||||
release_sock(sk);
|
||||
iso_sock_kill(sk);
|
||||
|
|
|
|||
|
|
@ -411,8 +411,10 @@ static void l2cap_chan_timeout(struct work_struct *work)
|
|||
|
||||
BT_DBG("chan %p state %s", chan, state_to_string(chan->state));
|
||||
|
||||
if (!conn)
|
||||
if (!conn) {
|
||||
l2cap_chan_put(chan);
|
||||
return;
|
||||
}
|
||||
|
||||
mutex_lock(&conn->lock);
|
||||
/* __set_chan_timer() calls l2cap_chan_hold(chan) while scheduling
|
||||
|
|
@ -5260,6 +5262,7 @@ static inline int l2cap_ecred_conn_rsp(struct l2cap_conn *conn,
|
|||
cmd_len -= sizeof(*rsp);
|
||||
|
||||
list_for_each_entry_safe(chan, tmp, &conn->chan_l, list) {
|
||||
struct l2cap_chan *orig;
|
||||
u16 dcid;
|
||||
|
||||
if (chan->ident != cmd->ident ||
|
||||
|
|
@ -5281,8 +5284,10 @@ static inline int l2cap_ecred_conn_rsp(struct l2cap_conn *conn,
|
|||
|
||||
BT_DBG("dcid[%d] 0x%4.4x", i, dcid);
|
||||
|
||||
orig = __l2cap_get_chan_by_dcid(conn, dcid);
|
||||
|
||||
/* Check if dcid is already in use */
|
||||
if (dcid && __l2cap_get_chan_by_dcid(conn, dcid)) {
|
||||
if (dcid && orig) {
|
||||
/* If a device receives a
|
||||
* L2CAP_CREDIT_BASED_CONNECTION_RSP packet with an
|
||||
* already-assigned Destination CID, then both the
|
||||
|
|
@ -5291,10 +5296,24 @@ static inline int l2cap_ecred_conn_rsp(struct l2cap_conn *conn,
|
|||
*/
|
||||
l2cap_chan_del(chan, ECONNREFUSED);
|
||||
l2cap_chan_unlock(chan);
|
||||
chan = __l2cap_get_chan_by_dcid(conn, dcid);
|
||||
l2cap_chan_lock(chan);
|
||||
l2cap_chan_del(chan, ECONNRESET);
|
||||
l2cap_chan_unlock(chan);
|
||||
|
||||
/* Check that the dcid channel mode is
|
||||
* L2CAP_MODE_EXT_FLOWCTL since this procedure is only
|
||||
* valid for that mode and shouldn't disconnect a dcid
|
||||
* in other modes.
|
||||
*/
|
||||
if (orig->mode == L2CAP_MODE_EXT_FLOWCTL) {
|
||||
l2cap_chan_lock(orig);
|
||||
/* Disconnect the original channel as it may be
|
||||
* considered connected since dcid has already
|
||||
* been assigned; don't call l2cap_chan_close
|
||||
* directly since that could lead to
|
||||
* l2cap_chan_del and then removing the channel
|
||||
* from the list while we're iterating over it.
|
||||
*/
|
||||
__set_chan_timer(orig, 0);
|
||||
l2cap_chan_unlock(orig);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -5458,14 +5477,20 @@ static inline int l2cap_ecred_reconf_rsp(struct l2cap_conn *conn,
|
|||
|
||||
BT_DBG("result 0x%4.4x", result);
|
||||
|
||||
if (!result)
|
||||
if (!result) {
|
||||
list_for_each_entry(chan, &conn->chan_l, list) {
|
||||
if (chan->ident == cmd->ident)
|
||||
chan->ident = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
list_for_each_entry_safe(chan, tmp, &conn->chan_l, list) {
|
||||
if (chan->ident != cmd->ident)
|
||||
continue;
|
||||
|
||||
l2cap_chan_hold(chan);
|
||||
if (!l2cap_chan_hold_unless_zero(chan))
|
||||
continue;
|
||||
l2cap_chan_lock(chan);
|
||||
|
||||
l2cap_chan_del(chan, ECONNRESET);
|
||||
|
|
|
|||
|
|
@ -1499,6 +1499,10 @@ static void l2cap_sock_cleanup_listen(struct sock *parent)
|
|||
* 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.
|
||||
*
|
||||
* Since we cannot call l2cap_chan_close() without conn->lock,
|
||||
* schedule l2cap_chan_timeout to close the channel; it already
|
||||
* acquires conn->lock -> chan->lock in the correct order.
|
||||
*/
|
||||
while ((sk = bt_accept_dequeue(parent, NULL))) {
|
||||
struct l2cap_chan *chan;
|
||||
|
|
@ -1516,14 +1520,12 @@ static void l2cap_sock_cleanup_listen(struct sock *parent)
|
|||
state_to_string(chan->state));
|
||||
|
||||
l2cap_chan_lock(chan);
|
||||
__clear_chan_timer(chan);
|
||||
l2cap_chan_close(chan, ECONNRESET);
|
||||
/* l2cap_conn_del() may already have killed this socket
|
||||
* (it sets SOCK_DEAD); skip the duplicate to avoid a
|
||||
* double sock_put()/l2cap_chan_put().
|
||||
/* Since we cannot call l2cap_chan_close() without
|
||||
* conn->lock, schedule its timer to trigger the close
|
||||
* and cleanup of this channel.
|
||||
*/
|
||||
if (!sock_flag(sk, SOCK_DEAD))
|
||||
l2cap_sock_kill(sk);
|
||||
if (chan->conn)
|
||||
__set_chan_timer(chan, 0);
|
||||
l2cap_chan_unlock(chan);
|
||||
|
||||
l2cap_chan_put(chan);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user