--- /dev/null
+From fde9607444ff7b540d0899f85e901c5ed52b35d5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 14 Nov 2025 16:07:51 +0800
+Subject: Bluetooth: MGMT: Fix possible UAFs
+
+From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+
+[ Upstream commit 302a1f674c00dd5581ab8e493ef44767c5101aab ]
+
+This attemps to fix possible UAFs caused by struct mgmt_pending being
+freed while still being processed like in the following trace, in order
+to fix mgmt_pending_valid is introduce and use to check if the
+mgmt_pending hasn't been removed from the pending list, on the complete
+callbacks it is used to check and in addtion remove the cmd from the list
+while holding mgmt_pending_lock to avoid TOCTOU problems since if the cmd
+is left on the list it can still be accessed and freed.
+
+BUG: KASAN: slab-use-after-free in mgmt_add_adv_patterns_monitor_sync+0x35/0x50 net/bluetooth/mgmt.c:5223
+Read of size 8 at addr ffff8880709d4dc0 by task kworker/u11:0/55
+
+CPU: 0 UID: 0 PID: 55 Comm: kworker/u11:0 Not tainted 6.16.4 #2 PREEMPT(full)
+Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.2-1ubuntu1 04/01/2014
+Workqueue: hci0 hci_cmd_sync_work
+Call Trace:
+ <TASK>
+ dump_stack_lvl+0x189/0x250 lib/dump_stack.c:120
+ print_address_description mm/kasan/report.c:378 [inline]
+ print_report+0xca/0x240 mm/kasan/report.c:482
+ kasan_report+0x118/0x150 mm/kasan/report.c:595
+ mgmt_add_adv_patterns_monitor_sync+0x35/0x50 net/bluetooth/mgmt.c:5223
+ hci_cmd_sync_work+0x210/0x3a0 net/bluetooth/hci_sync.c:332
+ process_one_work kernel/workqueue.c:3238 [inline]
+ process_scheduled_works+0xade/0x17b0 kernel/workqueue.c:3321
+ worker_thread+0x8a0/0xda0 kernel/workqueue.c:3402
+ kthread+0x711/0x8a0 kernel/kthread.c:464
+ ret_from_fork+0x3fc/0x770 arch/x86/kernel/process.c:148
+ ret_from_fork_asm+0x1a/0x30 home/kwqcheii/source/fuzzing/kernel/kasan/linux-6.16.4/arch/x86/entry/entry_64.S:245
+ </TASK>
+
+Allocated by task 12210:
+ kasan_save_stack mm/kasan/common.c:47 [inline]
+ kasan_save_track+0x3e/0x80 mm/kasan/common.c:68
+ poison_kmalloc_redzone mm/kasan/common.c:377 [inline]
+ __kasan_kmalloc+0x93/0xb0 mm/kasan/common.c:394
+ kasan_kmalloc include/linux/kasan.h:260 [inline]
+ __kmalloc_cache_noprof+0x230/0x3d0 mm/slub.c:4364
+ kmalloc_noprof include/linux/slab.h:905 [inline]
+ kzalloc_noprof include/linux/slab.h:1039 [inline]
+ mgmt_pending_new+0x65/0x1e0 net/bluetooth/mgmt_util.c:269
+ mgmt_pending_add+0x35/0x140 net/bluetooth/mgmt_util.c:296
+ __add_adv_patterns_monitor+0x130/0x200 net/bluetooth/mgmt.c:5247
+ add_adv_patterns_monitor+0x214/0x360 net/bluetooth/mgmt.c:5364
+ hci_mgmt_cmd+0x9c9/0xef0 net/bluetooth/hci_sock.c:1719
+ hci_sock_sendmsg+0x6ca/0xef0 net/bluetooth/hci_sock.c:1839
+ sock_sendmsg_nosec net/socket.c:714 [inline]
+ __sock_sendmsg+0x219/0x270 net/socket.c:729
+ sock_write_iter+0x258/0x330 net/socket.c:1133
+ new_sync_write fs/read_write.c:593 [inline]
+ vfs_write+0x5c9/0xb30 fs/read_write.c:686
+ ksys_write+0x145/0x250 fs/read_write.c:738
+ do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
+ do_syscall_64+0xfa/0x3b0 arch/x86/entry/syscall_64.c:94
+ entry_SYSCALL_64_after_hwframe+0x77/0x7f
+
+Freed by task 12221:
+ kasan_save_stack mm/kasan/common.c:47 [inline]
+ kasan_save_track+0x3e/0x80 mm/kasan/common.c:68
+ kasan_save_free_info+0x46/0x50 mm/kasan/generic.c:576
+ poison_slab_object mm/kasan/common.c:247 [inline]
+ __kasan_slab_free+0x62/0x70 mm/kasan/common.c:264
+ kasan_slab_free include/linux/kasan.h:233 [inline]
+ slab_free_hook mm/slub.c:2381 [inline]
+ slab_free mm/slub.c:4648 [inline]
+ kfree+0x18e/0x440 mm/slub.c:4847
+ mgmt_pending_free net/bluetooth/mgmt_util.c:311 [inline]
+ mgmt_pending_foreach+0x30d/0x380 net/bluetooth/mgmt_util.c:257
+ __mgmt_power_off+0x169/0x350 net/bluetooth/mgmt.c:9444
+ hci_dev_close_sync+0x754/0x1330 net/bluetooth/hci_sync.c:5290
+ hci_dev_do_close net/bluetooth/hci_core.c:501 [inline]
+ hci_dev_close+0x108/0x200 net/bluetooth/hci_core.c:526
+ sock_do_ioctl+0xd9/0x300 net/socket.c:1192
+ sock_ioctl+0x576/0x790 net/socket.c:1313
+ vfs_ioctl fs/ioctl.c:51 [inline]
+ __do_sys_ioctl fs/ioctl.c:907 [inline]
+ __se_sys_ioctl+0xf9/0x170 fs/ioctl.c:893
+ do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
+ do_syscall_64+0xfa/0x3b0 arch/x86/entry/syscall_64.c:94
+ entry_SYSCALL_64_after_hwframe+0x77/0x7f
+
+Fixes: cf75ad8b41d2 ("Bluetooth: hci_sync: Convert MGMT_SET_POWERED")
+Fixes: 2bd1b237616b ("Bluetooth: hci_sync: Convert MGMT_OP_SET_DISCOVERABLE to use cmd_sync")
+Fixes: f056a65783cc ("Bluetooth: hci_sync: Convert MGMT_OP_SET_CONNECTABLE to use cmd_sync")
+Fixes: 3244845c6307 ("Bluetooth: hci_sync: Convert MGMT_OP_SSP")
+Fixes: d81a494c43df ("Bluetooth: hci_sync: Convert MGMT_OP_SET_LE")
+Fixes: b338d91703fa ("Bluetooth: Implement support for Mesh")
+Fixes: 6f6ff38a1e14 ("Bluetooth: hci_sync: Convert MGMT_OP_SET_LOCAL_NAME")
+Fixes: 71efbb08b538 ("Bluetooth: hci_sync: Convert MGMT_OP_SET_PHY_CONFIGURATION")
+Fixes: b747a83690c8 ("Bluetooth: hci_sync: Refactor add Adv Monitor")
+Fixes: abfeea476c68 ("Bluetooth: hci_sync: Convert MGMT_OP_START_DISCOVERY")
+Fixes: 26ac4c56f03f ("Bluetooth: hci_sync: Convert MGMT_OP_SET_ADVERTISING")
+Reported-by: cen zhang <zzzccc427@gmail.com>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Chen Yu <xnguchen@sina.cn>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/mgmt.c | 259 ++++++++++++++++++++++++++------------
+ net/bluetooth/mgmt_util.c | 46 +++++++
+ net/bluetooth/mgmt_util.h | 3 +
+ 3 files changed, 231 insertions(+), 77 deletions(-)
+
+diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
+index c54cc701cdd48..83e33d9cfb33c 100644
+--- a/net/bluetooth/mgmt.c
++++ b/net/bluetooth/mgmt.c
+@@ -1318,8 +1318,7 @@ static void mgmt_set_powered_complete(struct hci_dev *hdev, void *data, int err)
+ struct mgmt_mode *cp;
+
+ /* Make sure cmd still outstanding. */
+- if (err == -ECANCELED ||
+- cmd != pending_find(MGMT_OP_SET_POWERED, hdev))
++ if (err == -ECANCELED || !mgmt_pending_valid(hdev, cmd))
+ return;
+
+ cp = cmd->param;
+@@ -1346,23 +1345,29 @@ static void mgmt_set_powered_complete(struct hci_dev *hdev, void *data, int err)
+ mgmt_status(err));
+ }
+
+- mgmt_pending_remove(cmd);
++ mgmt_pending_free(cmd);
+ }
+
+ static int set_powered_sync(struct hci_dev *hdev, void *data)
+ {
+ struct mgmt_pending_cmd *cmd = data;
+- struct mgmt_mode *cp;
++ struct mgmt_mode cp;
++
++ mutex_lock(&hdev->mgmt_pending_lock);
+
+ /* Make sure cmd still outstanding. */
+- if (cmd != pending_find(MGMT_OP_SET_POWERED, hdev))
++ if (!__mgmt_pending_listed(hdev, cmd)) {
++ mutex_unlock(&hdev->mgmt_pending_lock);
+ return -ECANCELED;
++ }
+
+- cp = cmd->param;
++ memcpy(&cp, cmd->param, sizeof(cp));
++
++ mutex_unlock(&hdev->mgmt_pending_lock);
+
+ BT_DBG("%s", hdev->name);
+
+- return hci_set_powered_sync(hdev, cp->val);
++ return hci_set_powered_sync(hdev, cp.val);
+ }
+
+ static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data,
+@@ -1511,8 +1516,7 @@ static void mgmt_set_discoverable_complete(struct hci_dev *hdev, void *data,
+ bt_dev_dbg(hdev, "err %d", err);
+
+ /* Make sure cmd still outstanding. */
+- if (err == -ECANCELED ||
+- cmd != pending_find(MGMT_OP_SET_DISCOVERABLE, hdev))
++ if (err == -ECANCELED || !mgmt_pending_valid(hdev, cmd))
+ return;
+
+ hci_dev_lock(hdev);
+@@ -1534,12 +1538,15 @@ static void mgmt_set_discoverable_complete(struct hci_dev *hdev, void *data,
+ new_settings(hdev, cmd->sk);
+
+ done:
+- mgmt_pending_remove(cmd);
++ mgmt_pending_free(cmd);
+ hci_dev_unlock(hdev);
+ }
+
+ static int set_discoverable_sync(struct hci_dev *hdev, void *data)
+ {
++ if (!mgmt_pending_listed(hdev, data))
++ return -ECANCELED;
++
+ BT_DBG("%s", hdev->name);
+
+ return hci_update_discoverable_sync(hdev);
+@@ -1686,8 +1693,7 @@ static void mgmt_set_connectable_complete(struct hci_dev *hdev, void *data,
+ bt_dev_dbg(hdev, "err %d", err);
+
+ /* Make sure cmd still outstanding. */
+- if (err == -ECANCELED ||
+- cmd != pending_find(MGMT_OP_SET_CONNECTABLE, hdev))
++ if (err == -ECANCELED || !mgmt_pending_valid(hdev, cmd))
+ return;
+
+ hci_dev_lock(hdev);
+@@ -1702,7 +1708,7 @@ static void mgmt_set_connectable_complete(struct hci_dev *hdev, void *data,
+ new_settings(hdev, cmd->sk);
+
+ done:
+- mgmt_pending_remove(cmd);
++ mgmt_pending_free(cmd);
+
+ hci_dev_unlock(hdev);
+ }
+@@ -1738,6 +1744,9 @@ static int set_connectable_update_settings(struct hci_dev *hdev,
+
+ static int set_connectable_sync(struct hci_dev *hdev, void *data)
+ {
++ if (!mgmt_pending_listed(hdev, data))
++ return -ECANCELED;
++
+ BT_DBG("%s", hdev->name);
+
+ return hci_update_connectable_sync(hdev);
+@@ -1914,14 +1923,17 @@ static void set_ssp_complete(struct hci_dev *hdev, void *data, int err)
+ {
+ struct cmd_lookup match = { NULL, hdev };
+ struct mgmt_pending_cmd *cmd = data;
+- struct mgmt_mode *cp = cmd->param;
+- u8 enable = cp->val;
++ struct mgmt_mode *cp;
++ u8 enable;
+ bool changed;
+
+ /* Make sure cmd still outstanding. */
+- if (err == -ECANCELED || cmd != pending_find(MGMT_OP_SET_SSP, hdev))
++ if (err == -ECANCELED || !mgmt_pending_valid(hdev, cmd))
+ return;
+
++ cp = cmd->param;
++ enable = cp->val;
++
+ if (err) {
+ u8 mgmt_err = mgmt_status(err);
+
+@@ -1930,8 +1942,7 @@ static void set_ssp_complete(struct hci_dev *hdev, void *data, int err)
+ new_settings(hdev, NULL);
+ }
+
+- mgmt_pending_foreach(MGMT_OP_SET_SSP, hdev, true,
+- cmd_status_rsp, &mgmt_err);
++ mgmt_cmd_status(cmd->sk, cmd->hdev->id, cmd->opcode, mgmt_err);
+ return;
+ }
+
+@@ -1941,7 +1952,7 @@ static void set_ssp_complete(struct hci_dev *hdev, void *data, int err)
+ changed = hci_dev_test_and_clear_flag(hdev, HCI_SSP_ENABLED);
+ }
+
+- mgmt_pending_foreach(MGMT_OP_SET_SSP, hdev, true, settings_rsp, &match);
++ settings_rsp(cmd, &match);
+
+ if (changed)
+ new_settings(hdev, match.sk);
+@@ -1955,14 +1966,25 @@ static void set_ssp_complete(struct hci_dev *hdev, void *data, int err)
+ static int set_ssp_sync(struct hci_dev *hdev, void *data)
+ {
+ struct mgmt_pending_cmd *cmd = data;
+- struct mgmt_mode *cp = cmd->param;
++ struct mgmt_mode cp;
+ bool changed = false;
+ int err;
+
+- if (cp->val)
++ mutex_lock(&hdev->mgmt_pending_lock);
++
++ if (!__mgmt_pending_listed(hdev, cmd)) {
++ mutex_unlock(&hdev->mgmt_pending_lock);
++ return -ECANCELED;
++ }
++
++ memcpy(&cp, cmd->param, sizeof(cp));
++
++ mutex_unlock(&hdev->mgmt_pending_lock);
++
++ if (cp.val)
+ changed = !hci_dev_test_and_set_flag(hdev, HCI_SSP_ENABLED);
+
+- err = hci_write_ssp_mode_sync(hdev, cp->val);
++ err = hci_write_ssp_mode_sync(hdev, cp.val);
+
+ if (!err && changed)
+ hci_dev_clear_flag(hdev, HCI_SSP_ENABLED);
+@@ -2055,32 +2077,50 @@ static int set_hs(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
+
+ static void set_le_complete(struct hci_dev *hdev, void *data, int err)
+ {
++ struct mgmt_pending_cmd *cmd = data;
+ struct cmd_lookup match = { NULL, hdev };
+ u8 status = mgmt_status(err);
+
+ bt_dev_dbg(hdev, "err %d", err);
+
+- if (status) {
+- mgmt_pending_foreach(MGMT_OP_SET_LE, hdev, true, cmd_status_rsp,
+- &status);
++ if (err == -ECANCELED || !mgmt_pending_valid(hdev, data))
+ return;
++
++ if (status) {
++ mgmt_cmd_status(cmd->sk, cmd->hdev->id, cmd->opcode, status);
++ goto done;
+ }
+
+- mgmt_pending_foreach(MGMT_OP_SET_LE, hdev, true, settings_rsp, &match);
++ settings_rsp(cmd, &match);
+
+ new_settings(hdev, match.sk);
+
+ if (match.sk)
+ sock_put(match.sk);
++
++done:
++ mgmt_pending_free(cmd);
+ }
+
+ static int set_le_sync(struct hci_dev *hdev, void *data)
+ {
+ struct mgmt_pending_cmd *cmd = data;
+- struct mgmt_mode *cp = cmd->param;
+- u8 val = !!cp->val;
++ struct mgmt_mode cp;
++ u8 val;
+ int err;
+
++ mutex_lock(&hdev->mgmt_pending_lock);
++
++ if (!__mgmt_pending_listed(hdev, cmd)) {
++ mutex_unlock(&hdev->mgmt_pending_lock);
++ return -ECANCELED;
++ }
++
++ memcpy(&cp, cmd->param, sizeof(cp));
++ val = !!cp.val;
++
++ mutex_unlock(&hdev->mgmt_pending_lock);
++
+ if (!val) {
+ hci_clear_adv_instance_sync(hdev, NULL, 0x00, true);
+
+@@ -2122,7 +2162,12 @@ static void set_mesh_complete(struct hci_dev *hdev, void *data, int err)
+ {
+ struct mgmt_pending_cmd *cmd = data;
+ u8 status = mgmt_status(err);
+- struct sock *sk = cmd->sk;
++ struct sock *sk;
++
++ if (err == -ECANCELED || !mgmt_pending_valid(hdev, cmd))
++ return;
++
++ sk = cmd->sk;
+
+ if (status) {
+ mgmt_pending_foreach(MGMT_OP_SET_MESH_RECEIVER, hdev, true,
+@@ -2137,24 +2182,37 @@ static void set_mesh_complete(struct hci_dev *hdev, void *data, int err)
+ static int set_mesh_sync(struct hci_dev *hdev, void *data)
+ {
+ struct mgmt_pending_cmd *cmd = data;
+- struct mgmt_cp_set_mesh *cp = cmd->param;
+- size_t len = cmd->param_len;
++ struct mgmt_cp_set_mesh cp;
++ size_t len;
++
++ mutex_lock(&hdev->mgmt_pending_lock);
++
++ if (!__mgmt_pending_listed(hdev, cmd)) {
++ mutex_unlock(&hdev->mgmt_pending_lock);
++ return -ECANCELED;
++ }
++
++ memcpy(&cp, cmd->param, sizeof(cp));
++
++ mutex_unlock(&hdev->mgmt_pending_lock);
++
++ len = cmd->param_len;
+
+ memset(hdev->mesh_ad_types, 0, sizeof(hdev->mesh_ad_types));
+
+- if (cp->enable)
++ if (cp.enable)
+ hci_dev_set_flag(hdev, HCI_MESH);
+ else
+ hci_dev_clear_flag(hdev, HCI_MESH);
+
+- hdev->le_scan_interval = __le16_to_cpu(cp->period);
+- hdev->le_scan_window = __le16_to_cpu(cp->window);
++ hdev->le_scan_interval = __le16_to_cpu(cp.period);
++ hdev->le_scan_window = __le16_to_cpu(cp.window);
+
+- len -= sizeof(*cp);
++ len -= sizeof(cp);
+
+ /* If filters don't fit, forward all adv pkts */
+ if (len <= sizeof(hdev->mesh_ad_types))
+- memcpy(hdev->mesh_ad_types, cp->ad_types, len);
++ memcpy(hdev->mesh_ad_types, cp.ad_types, len);
+
+ hci_update_passive_scan_sync(hdev);
+ return 0;
+@@ -3801,15 +3859,16 @@ static int name_changed_sync(struct hci_dev *hdev, void *data)
+ static void set_name_complete(struct hci_dev *hdev, void *data, int err)
+ {
+ struct mgmt_pending_cmd *cmd = data;
+- struct mgmt_cp_set_local_name *cp = cmd->param;
++ struct mgmt_cp_set_local_name *cp;
+ u8 status = mgmt_status(err);
+
+ bt_dev_dbg(hdev, "err %d", err);
+
+- if (err == -ECANCELED ||
+- cmd != pending_find(MGMT_OP_SET_LOCAL_NAME, hdev))
++ if (err == -ECANCELED || !mgmt_pending_valid(hdev, cmd))
+ return;
+
++ cp = cmd->param;
++
+ if (status) {
+ mgmt_cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME,
+ status);
+@@ -3821,16 +3880,27 @@ static void set_name_complete(struct hci_dev *hdev, void *data, int err)
+ hci_cmd_sync_queue(hdev, name_changed_sync, NULL, NULL);
+ }
+
+- mgmt_pending_remove(cmd);
++ mgmt_pending_free(cmd);
+ }
+
+ static int set_name_sync(struct hci_dev *hdev, void *data)
+ {
+ struct mgmt_pending_cmd *cmd = data;
+- struct mgmt_cp_set_local_name *cp = cmd->param;
++ struct mgmt_cp_set_local_name cp;
++
++ mutex_lock(&hdev->mgmt_pending_lock);
++
++ if (!__mgmt_pending_listed(hdev, cmd)) {
++ mutex_unlock(&hdev->mgmt_pending_lock);
++ return -ECANCELED;
++ }
++
++ memcpy(&cp, cmd->param, sizeof(cp));
++
++ mutex_unlock(&hdev->mgmt_pending_lock);
+
+ if (lmp_bredr_capable(hdev)) {
+- hci_update_name_sync(hdev, cp->name);
++ hci_update_name_sync(hdev, cp.name);
+ hci_update_eir_sync(hdev);
+ }
+
+@@ -3982,12 +4052,10 @@ int mgmt_phy_configuration_changed(struct hci_dev *hdev, struct sock *skip)
+ static void set_default_phy_complete(struct hci_dev *hdev, void *data, int err)
+ {
+ struct mgmt_pending_cmd *cmd = data;
+- struct sk_buff *skb = cmd->skb;
++ struct sk_buff *skb;
+ u8 status = mgmt_status(err);
+
+- if (err == -ECANCELED ||
+- cmd != pending_find(MGMT_OP_SET_PHY_CONFIGURATION, hdev))
+- return;
++ skb = cmd->skb;
+
+ if (!status) {
+ if (!skb)
+@@ -4014,7 +4082,7 @@ static void set_default_phy_complete(struct hci_dev *hdev, void *data, int err)
+ if (skb && !IS_ERR(skb))
+ kfree_skb(skb);
+
+- mgmt_pending_remove(cmd);
++ mgmt_pending_free(cmd);
+ }
+
+ static int set_default_phy_sync(struct hci_dev *hdev, void *data)
+@@ -4022,7 +4090,9 @@ static int set_default_phy_sync(struct hci_dev *hdev, void *data)
+ struct mgmt_pending_cmd *cmd = data;
+ struct mgmt_cp_set_phy_configuration *cp = cmd->param;
+ struct hci_cp_le_set_default_phy cp_phy;
+- u32 selected_phys = __le32_to_cpu(cp->selected_phys);
++ u32 selected_phys;
++
++ selected_phys = __le32_to_cpu(cp->selected_phys);
+
+ memset(&cp_phy, 0, sizeof(cp_phy));
+
+@@ -4162,7 +4232,7 @@ static int set_phy_configuration(struct sock *sk, struct hci_dev *hdev,
+ goto unlock;
+ }
+
+- cmd = mgmt_pending_add(sk, MGMT_OP_SET_PHY_CONFIGURATION, hdev, data,
++ cmd = mgmt_pending_new(sk, MGMT_OP_SET_PHY_CONFIGURATION, hdev, data,
+ len);
+ if (!cmd)
+ err = -ENOMEM;
+@@ -5252,7 +5322,17 @@ static void mgmt_add_adv_patterns_monitor_complete(struct hci_dev *hdev,
+ {
+ struct mgmt_rp_add_adv_patterns_monitor rp;
+ struct mgmt_pending_cmd *cmd = data;
+- struct adv_monitor *monitor = cmd->user_data;
++ struct adv_monitor *monitor;
++
++ /* This is likely the result of hdev being closed and mgmt_index_removed
++ * is attempting to clean up any pending command so
++ * hci_adv_monitors_clear is about to be called which will take care of
++ * freeing the adv_monitor instances.
++ */
++ if (status == -ECANCELED && !mgmt_pending_valid(hdev, cmd))
++ return;
++
++ monitor = cmd->user_data;
+
+ hci_dev_lock(hdev);
+
+@@ -5278,9 +5358,20 @@ static void mgmt_add_adv_patterns_monitor_complete(struct hci_dev *hdev,
+ static int mgmt_add_adv_patterns_monitor_sync(struct hci_dev *hdev, void *data)
+ {
+ struct mgmt_pending_cmd *cmd = data;
+- struct adv_monitor *monitor = cmd->user_data;
++ struct adv_monitor *mon;
++
++ mutex_lock(&hdev->mgmt_pending_lock);
++
++ if (!__mgmt_pending_listed(hdev, cmd)) {
++ mutex_unlock(&hdev->mgmt_pending_lock);
++ return -ECANCELED;
++ }
++
++ mon = cmd->user_data;
++
++ mutex_unlock(&hdev->mgmt_pending_lock);
+
+- return hci_add_adv_monitor(hdev, monitor);
++ return hci_add_adv_monitor(hdev, mon);
+ }
+
+ static int __add_adv_patterns_monitor(struct sock *sk, struct hci_dev *hdev,
+@@ -5547,7 +5638,8 @@ static int remove_adv_monitor(struct sock *sk, struct hci_dev *hdev,
+ status);
+ }
+
+-static void read_local_oob_data_complete(struct hci_dev *hdev, void *data, int err)
++static void read_local_oob_data_complete(struct hci_dev *hdev, void *data,
++ int err)
+ {
+ struct mgmt_rp_read_local_oob_data mgmt_rp;
+ size_t rp_size = sizeof(mgmt_rp);
+@@ -5567,7 +5659,8 @@ static void read_local_oob_data_complete(struct hci_dev *hdev, void *data, int e
+ bt_dev_dbg(hdev, "status %d", status);
+
+ if (status) {
+- mgmt_cmd_status(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA, status);
++ mgmt_cmd_status(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA,
++ status);
+ goto remove;
+ }
+
+@@ -5872,17 +5965,12 @@ static void start_discovery_complete(struct hci_dev *hdev, void *data, int err)
+
+ bt_dev_dbg(hdev, "err %d", err);
+
+- if (err == -ECANCELED)
+- return;
+-
+- if (cmd != pending_find(MGMT_OP_START_DISCOVERY, hdev) &&
+- cmd != pending_find(MGMT_OP_START_LIMITED_DISCOVERY, hdev) &&
+- cmd != pending_find(MGMT_OP_START_SERVICE_DISCOVERY, hdev))
++ if (err == -ECANCELED || !mgmt_pending_valid(hdev, cmd))
+ return;
+
+ mgmt_cmd_complete(cmd->sk, cmd->hdev->id, cmd->opcode, mgmt_status(err),
+ cmd->param, 1);
+- mgmt_pending_remove(cmd);
++ mgmt_pending_free(cmd);
+
+ hci_discovery_set_state(hdev, err ? DISCOVERY_STOPPED:
+ DISCOVERY_FINDING);
+@@ -5890,6 +5978,9 @@ static void start_discovery_complete(struct hci_dev *hdev, void *data, int err)
+
+ static int start_discovery_sync(struct hci_dev *hdev, void *data)
+ {
++ if (!mgmt_pending_listed(hdev, data))
++ return -ECANCELED;
++
+ return hci_start_discovery_sync(hdev);
+ }
+
+@@ -6112,15 +6203,14 @@ static void stop_discovery_complete(struct hci_dev *hdev, void *data, int err)
+ {
+ struct mgmt_pending_cmd *cmd = data;
+
+- if (err == -ECANCELED ||
+- cmd != pending_find(MGMT_OP_STOP_DISCOVERY, hdev))
++ if (err == -ECANCELED || !mgmt_pending_valid(hdev, cmd))
+ return;
+
+ bt_dev_dbg(hdev, "err %d", err);
+
+ mgmt_cmd_complete(cmd->sk, cmd->hdev->id, cmd->opcode, mgmt_status(err),
+ cmd->param, 1);
+- mgmt_pending_remove(cmd);
++ mgmt_pending_free(cmd);
+
+ if (!err)
+ hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
+@@ -6128,6 +6218,9 @@ static void stop_discovery_complete(struct hci_dev *hdev, void *data, int err)
+
+ static int stop_discovery_sync(struct hci_dev *hdev, void *data)
+ {
++ if (!mgmt_pending_listed(hdev, data))
++ return -ECANCELED;
++
+ return hci_stop_discovery_sync(hdev);
+ }
+
+@@ -6337,14 +6430,18 @@ static void enable_advertising_instance(struct hci_dev *hdev, int err)
+
+ static void set_advertising_complete(struct hci_dev *hdev, void *data, int err)
+ {
++ struct mgmt_pending_cmd *cmd = data;
+ struct cmd_lookup match = { NULL, hdev };
+ u8 instance;
+ struct adv_info *adv_instance;
+ u8 status = mgmt_status(err);
+
++ if (err == -ECANCELED || !mgmt_pending_valid(hdev, data))
++ return;
++
+ if (status) {
+- mgmt_pending_foreach(MGMT_OP_SET_ADVERTISING, hdev, true,
+- cmd_status_rsp, &status);
++ mgmt_cmd_status(cmd->sk, cmd->hdev->id, cmd->opcode, status);
++ mgmt_pending_free(cmd);
+ return;
+ }
+
+@@ -6353,8 +6450,7 @@ static void set_advertising_complete(struct hci_dev *hdev, void *data, int err)
+ else
+ hci_dev_clear_flag(hdev, HCI_ADVERTISING);
+
+- mgmt_pending_foreach(MGMT_OP_SET_ADVERTISING, hdev, true, settings_rsp,
+- &match);
++ settings_rsp(cmd, &match);
+
+ new_settings(hdev, match.sk);
+
+@@ -6386,10 +6482,23 @@ static void set_advertising_complete(struct hci_dev *hdev, void *data, int err)
+ static int set_adv_sync(struct hci_dev *hdev, void *data)
+ {
+ struct mgmt_pending_cmd *cmd = data;
+- struct mgmt_mode *cp = cmd->param;
+- u8 val = !!cp->val;
++ struct mgmt_mode cp;
++ u8 val;
+
+- if (cp->val == 0x02)
++ mutex_lock(&hdev->mgmt_pending_lock);
++
++ if (!__mgmt_pending_listed(hdev, cmd)) {
++ mutex_unlock(&hdev->mgmt_pending_lock);
++ return -ECANCELED;
++ }
++
++ memcpy(&cp, cmd->param, sizeof(cp));
++
++ mutex_unlock(&hdev->mgmt_pending_lock);
++
++ val = !!cp.val;
++
++ if (cp.val == 0x02)
+ hci_dev_set_flag(hdev, HCI_ADVERTISING_CONNECTABLE);
+ else
+ hci_dev_clear_flag(hdev, HCI_ADVERTISING_CONNECTABLE);
+@@ -8142,10 +8251,6 @@ static void read_local_oob_ext_data_complete(struct hci_dev *hdev, void *data,
+ u8 status = mgmt_status(err);
+ u16 eir_len;
+
+- if (err == -ECANCELED ||
+- cmd != pending_find(MGMT_OP_READ_LOCAL_OOB_EXT_DATA, hdev))
+- return;
+-
+ if (!status) {
+ if (!skb)
+ status = MGMT_STATUS_FAILED;
+@@ -8252,7 +8357,7 @@ static void read_local_oob_ext_data_complete(struct hci_dev *hdev, void *data,
+ kfree_skb(skb);
+
+ kfree(mgmt_rp);
+- mgmt_pending_remove(cmd);
++ mgmt_pending_free(cmd);
+ }
+
+ static int read_local_ssp_oob_req(struct hci_dev *hdev, struct sock *sk,
+@@ -8261,7 +8366,7 @@ static int read_local_ssp_oob_req(struct hci_dev *hdev, struct sock *sk,
+ struct mgmt_pending_cmd *cmd;
+ int err;
+
+- cmd = mgmt_pending_add(sk, MGMT_OP_READ_LOCAL_OOB_EXT_DATA, hdev,
++ cmd = mgmt_pending_new(sk, MGMT_OP_READ_LOCAL_OOB_EXT_DATA, hdev,
+ cp, sizeof(*cp));
+ if (!cmd)
+ return -ENOMEM;
+diff --git a/net/bluetooth/mgmt_util.c b/net/bluetooth/mgmt_util.c
+index a88a07da39473..aa7b5585cb268 100644
+--- a/net/bluetooth/mgmt_util.c
++++ b/net/bluetooth/mgmt_util.c
+@@ -320,6 +320,52 @@ void mgmt_pending_remove(struct mgmt_pending_cmd *cmd)
+ mgmt_pending_free(cmd);
+ }
+
++bool __mgmt_pending_listed(struct hci_dev *hdev, struct mgmt_pending_cmd *cmd)
++{
++ struct mgmt_pending_cmd *tmp;
++
++ lockdep_assert_held(&hdev->mgmt_pending_lock);
++
++ if (!cmd)
++ return false;
++
++ list_for_each_entry(tmp, &hdev->mgmt_pending, list) {
++ if (cmd == tmp)
++ return true;
++ }
++
++ return false;
++}
++
++bool mgmt_pending_listed(struct hci_dev *hdev, struct mgmt_pending_cmd *cmd)
++{
++ bool listed;
++
++ mutex_lock(&hdev->mgmt_pending_lock);
++ listed = __mgmt_pending_listed(hdev, cmd);
++ mutex_unlock(&hdev->mgmt_pending_lock);
++
++ return listed;
++}
++
++bool mgmt_pending_valid(struct hci_dev *hdev, struct mgmt_pending_cmd *cmd)
++{
++ bool listed;
++
++ if (!cmd)
++ return false;
++
++ mutex_lock(&hdev->mgmt_pending_lock);
++
++ listed = __mgmt_pending_listed(hdev, cmd);
++ if (listed)
++ list_del(&cmd->list);
++
++ mutex_unlock(&hdev->mgmt_pending_lock);
++
++ return listed;
++}
++
+ void mgmt_mesh_foreach(struct hci_dev *hdev,
+ void (*cb)(struct mgmt_mesh_tx *mesh_tx, void *data),
+ void *data, struct sock *sk)
+diff --git a/net/bluetooth/mgmt_util.h b/net/bluetooth/mgmt_util.h
+index 024e51dd69375..bcba8c9d89528 100644
+--- a/net/bluetooth/mgmt_util.h
++++ b/net/bluetooth/mgmt_util.h
+@@ -65,6 +65,9 @@ struct mgmt_pending_cmd *mgmt_pending_new(struct sock *sk, u16 opcode,
+ void *data, u16 len);
+ void mgmt_pending_free(struct mgmt_pending_cmd *cmd);
+ void mgmt_pending_remove(struct mgmt_pending_cmd *cmd);
++bool __mgmt_pending_listed(struct hci_dev *hdev, struct mgmt_pending_cmd *cmd);
++bool mgmt_pending_listed(struct hci_dev *hdev, struct mgmt_pending_cmd *cmd);
++bool mgmt_pending_valid(struct hci_dev *hdev, struct mgmt_pending_cmd *cmd);
+ void mgmt_mesh_foreach(struct hci_dev *hdev,
+ void (*cb)(struct mgmt_mesh_tx *mesh_tx, void *data),
+ void *data, struct sock *sk);
+--
+2.51.0
+
--- /dev/null
+From 203119c647e317ce65d00d86b818154bafaadcbe Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 13 Nov 2025 17:55:37 +0800
+Subject: ext4: fix out-of-bound read in ext4_xattr_inode_dec_ref_all()
+
+From: Ye Bin <yebin10@huawei.com>
+
+[ Upstream commit 5701875f9609b000d91351eaa6bfd97fe2f157f4 ]
+
+There's issue as follows:
+BUG: KASAN: use-after-free in ext4_xattr_inode_dec_ref_all+0x6ff/0x790
+Read of size 4 at addr ffff88807b003000 by task syz-executor.0/15172
+
+CPU: 3 PID: 15172 Comm: syz-executor.0
+Call Trace:
+ __dump_stack lib/dump_stack.c:82 [inline]
+ dump_stack+0xbe/0xfd lib/dump_stack.c:123
+ print_address_description.constprop.0+0x1e/0x280 mm/kasan/report.c:400
+ __kasan_report.cold+0x6c/0x84 mm/kasan/report.c:560
+ kasan_report+0x3a/0x50 mm/kasan/report.c:585
+ ext4_xattr_inode_dec_ref_all+0x6ff/0x790 fs/ext4/xattr.c:1137
+ ext4_xattr_delete_inode+0x4c7/0xda0 fs/ext4/xattr.c:2896
+ ext4_evict_inode+0xb3b/0x1670 fs/ext4/inode.c:323
+ evict+0x39f/0x880 fs/inode.c:622
+ iput_final fs/inode.c:1746 [inline]
+ iput fs/inode.c:1772 [inline]
+ iput+0x525/0x6c0 fs/inode.c:1758
+ ext4_orphan_cleanup fs/ext4/super.c:3298 [inline]
+ ext4_fill_super+0x8c57/0xba40 fs/ext4/super.c:5300
+ mount_bdev+0x355/0x410 fs/super.c:1446
+ legacy_get_tree+0xfe/0x220 fs/fs_context.c:611
+ vfs_get_tree+0x8d/0x2f0 fs/super.c:1576
+ do_new_mount fs/namespace.c:2983 [inline]
+ path_mount+0x119a/0x1ad0 fs/namespace.c:3316
+ do_mount+0xfc/0x110 fs/namespace.c:3329
+ __do_sys_mount fs/namespace.c:3540 [inline]
+ __se_sys_mount+0x219/0x2e0 fs/namespace.c:3514
+ do_syscall_64+0x33/0x40 arch/x86/entry/common.c:46
+ entry_SYSCALL_64_after_hwframe+0x67/0xd1
+
+Memory state around the buggy address:
+ ffff88807b002f00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ ffff88807b002f80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+>ffff88807b003000: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
+ ^
+ ffff88807b003080: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
+ ffff88807b003100: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
+
+Above issue happens as ext4_xattr_delete_inode() isn't check xattr
+is valid if xattr is in inode.
+To solve above issue call xattr_check_inode() check if xattr if valid
+in inode. In fact, we can directly verify in ext4_iget_extra_inode(),
+so that there is no divergent verification.
+
+Fixes: e50e5129f384 ("ext4: xattr-in-inode support")
+Signed-off-by: Ye Bin <yebin10@huawei.com>
+Reviewed-by: Jan Kara <jack@suse.cz>
+Link: https://patch.msgid.link/20250208063141.1539283-3-yebin@huaweicloud.com
+Signed-off-by: Theodore Ts'o <tytso@mit.edu>
+Signed-off-by: Rajani Kantha <681739313@139.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/ext4/inode.c | 5 +++++
+ fs/ext4/xattr.c | 26 +-------------------------
+ fs/ext4/xattr.h | 7 +++++++
+ 3 files changed, 13 insertions(+), 25 deletions(-)
+
+diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
+index 4ad34eba00a77..ae513b14fd084 100644
+--- a/fs/ext4/inode.c
++++ b/fs/ext4/inode.c
+@@ -4688,6 +4688,11 @@ static inline int ext4_iget_extra_inode(struct inode *inode,
+ *magic == cpu_to_le32(EXT4_XATTR_MAGIC)) {
+ int err;
+
++ err = xattr_check_inode(inode, IHDR(inode, raw_inode),
++ ITAIL(inode, raw_inode));
++ if (err)
++ return err;
++
+ ext4_set_inode_state(inode, EXT4_STATE_XATTR);
+ err = ext4_find_inline_data_nolock(inode);
+ if (!err && ext4_has_inline_data(inode))
+diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
+index 6946c1fc790ab..efaad43a7aab7 100644
+--- a/fs/ext4/xattr.c
++++ b/fs/ext4/xattr.c
+@@ -312,7 +312,7 @@ __ext4_xattr_check_block(struct inode *inode, struct buffer_head *bh,
+ __ext4_xattr_check_block((inode), (bh), __func__, __LINE__)
+
+
+-static inline int
++int
+ __xattr_check_inode(struct inode *inode, struct ext4_xattr_ibody_header *header,
+ void *end, const char *function, unsigned int line)
+ {
+@@ -320,9 +320,6 @@ __xattr_check_inode(struct inode *inode, struct ext4_xattr_ibody_header *header,
+ function, line);
+ }
+
+-#define xattr_check_inode(inode, header, end) \
+- __xattr_check_inode((inode), (header), (end), __func__, __LINE__)
+-
+ static int
+ xattr_find_entry(struct inode *inode, struct ext4_xattr_entry **pentry,
+ void *end, int name_index, const char *name, int sorted)
+@@ -654,9 +651,6 @@ ext4_xattr_ibody_get(struct inode *inode, int name_index, const char *name,
+ raw_inode = ext4_raw_inode(&iloc);
+ header = IHDR(inode, raw_inode);
+ end = ITAIL(inode, raw_inode);
+- error = xattr_check_inode(inode, header, end);
+- if (error)
+- goto cleanup;
+ entry = IFIRST(header);
+ error = xattr_find_entry(inode, &entry, end, name_index, name, 0);
+ if (error)
+@@ -787,7 +781,6 @@ ext4_xattr_ibody_list(struct dentry *dentry, char *buffer, size_t buffer_size)
+ struct ext4_xattr_ibody_header *header;
+ struct ext4_inode *raw_inode;
+ struct ext4_iloc iloc;
+- void *end;
+ int error;
+
+ if (!ext4_test_inode_state(inode, EXT4_STATE_XATTR))
+@@ -797,14 +790,9 @@ ext4_xattr_ibody_list(struct dentry *dentry, char *buffer, size_t buffer_size)
+ return error;
+ raw_inode = ext4_raw_inode(&iloc);
+ header = IHDR(inode, raw_inode);
+- end = ITAIL(inode, raw_inode);
+- error = xattr_check_inode(inode, header, end);
+- if (error)
+- goto cleanup;
+ error = ext4_xattr_list_entries(dentry, IFIRST(header),
+ buffer, buffer_size);
+
+-cleanup:
+ brelse(iloc.bh);
+ return error;
+ }
+@@ -872,7 +860,6 @@ int ext4_get_inode_usage(struct inode *inode, qsize_t *usage)
+ struct ext4_xattr_ibody_header *header;
+ struct ext4_xattr_entry *entry;
+ qsize_t ea_inode_refs = 0;
+- void *end;
+ int ret;
+
+ lockdep_assert_held_read(&EXT4_I(inode)->xattr_sem);
+@@ -883,10 +870,6 @@ int ext4_get_inode_usage(struct inode *inode, qsize_t *usage)
+ goto out;
+ raw_inode = ext4_raw_inode(&iloc);
+ header = IHDR(inode, raw_inode);
+- end = ITAIL(inode, raw_inode);
+- ret = xattr_check_inode(inode, header, end);
+- if (ret)
+- goto out;
+
+ for (entry = IFIRST(header); !IS_LAST_ENTRY(entry);
+ entry = EXT4_XATTR_NEXT(entry))
+@@ -2251,9 +2234,6 @@ int ext4_xattr_ibody_find(struct inode *inode, struct ext4_xattr_info *i,
+ is->s.here = is->s.first;
+ is->s.end = ITAIL(inode, raw_inode);
+ if (ext4_test_inode_state(inode, EXT4_STATE_XATTR)) {
+- error = xattr_check_inode(inode, header, is->s.end);
+- if (error)
+- return error;
+ /* Find the named attribute. */
+ error = xattr_find_entry(inode, &is->s.here, is->s.end,
+ i->name_index, i->name, 0);
+@@ -2804,10 +2784,6 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
+ min_offs = end - base;
+ total_ino = sizeof(struct ext4_xattr_ibody_header) + sizeof(u32);
+
+- error = xattr_check_inode(inode, header, end);
+- if (error)
+- goto cleanup;
+-
+ ifree = ext4_xattr_free_space(base, &min_offs, base, &total_ino);
+ if (ifree >= isize_diff)
+ goto shift;
+diff --git a/fs/ext4/xattr.h b/fs/ext4/xattr.h
+index 5197f17ffd9a2..1fedf44d4fb65 100644
+--- a/fs/ext4/xattr.h
++++ b/fs/ext4/xattr.h
+@@ -209,6 +209,13 @@ extern int ext4_xattr_ibody_set(handle_t *handle, struct inode *inode,
+ extern struct mb_cache *ext4_xattr_create_cache(void);
+ extern void ext4_xattr_destroy_cache(struct mb_cache *);
+
++extern int
++__xattr_check_inode(struct inode *inode, struct ext4_xattr_ibody_header *header,
++ void *end, const char *function, unsigned int line);
++
++#define xattr_check_inode(inode, header, end) \
++ __xattr_check_inode((inode), (header), (end), __func__, __LINE__)
++
+ #ifdef CONFIG_EXT4_FS_SECURITY
+ extern int ext4_init_security(handle_t *handle, struct inode *inode,
+ struct inode *dir, const struct qstr *qstr);
+--
+2.51.0
+