From: Sasha Levin Date: Sun, 16 Nov 2025 17:36:33 +0000 (-0500) Subject: Fixes for all trees X-Git-Tag: v6.6.117~45 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=ab0d1457d584bb893a49dfa184ee274af41cb98b;p=thirdparty%2Fkernel%2Fstable-queue.git Fixes for all trees Signed-off-by: Sasha Levin --- diff --git a/queue-6.12/bluetooth-mgmt-fix-possible-uafs.patch b/queue-6.12/bluetooth-mgmt-fix-possible-uafs.patch new file mode 100644 index 0000000000..03abb3c7ec --- /dev/null +++ b/queue-6.12/bluetooth-mgmt-fix-possible-uafs.patch @@ -0,0 +1,776 @@ +From fde9607444ff7b540d0899f85e901c5ed52b35d5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 14 Nov 2025 16:07:51 +0800 +Subject: Bluetooth: MGMT: Fix possible UAFs + +From: Luiz Augusto von Dentz + +[ 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: + + 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 + + +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 +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Chen Yu +Signed-off-by: Sasha Levin +--- + 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 + diff --git a/queue-6.12/ext4-fix-out-of-bound-read-in-ext4_xattr_inode_dec_r.patch b/queue-6.12/ext4-fix-out-of-bound-read-in-ext4_xattr_inode_dec_r.patch new file mode 100644 index 0000000000..e35b36d33a --- /dev/null +++ b/queue-6.12/ext4-fix-out-of-bound-read-in-ext4_xattr_inode_dec_r.patch @@ -0,0 +1,200 @@ +From 203119c647e317ce65d00d86b818154bafaadcbe Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ 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 +Reviewed-by: Jan Kara +Link: https://patch.msgid.link/20250208063141.1539283-3-yebin@huaweicloud.com +Signed-off-by: Theodore Ts'o +Signed-off-by: Rajani Kantha <681739313@139.com> +Signed-off-by: Sasha Levin +--- + 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 + diff --git a/queue-6.12/ext4-introduce-itail-helper.patch b/queue-6.12/ext4-introduce-itail-helper.patch new file mode 100644 index 0000000000..15eaa866ea --- /dev/null +++ b/queue-6.12/ext4-introduce-itail-helper.patch @@ -0,0 +1,88 @@ +From 8eecdfb913a86ac0bf7ff124d5032d1a22b3e2e0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 13 Nov 2025 17:55:36 +0800 +Subject: ext4: introduce ITAIL helper + +From: Ye Bin + +[ Upstream commit 69f3a3039b0d0003de008659cafd5a1eaaa0a7a4 ] + +Introduce ITAIL helper to get the bound of xattr in inode. + +Signed-off-by: Ye Bin +Reviewed-by: Jan Kara +Link: https://patch.msgid.link/20250208063141.1539283-2-yebin@huaweicloud.com +Signed-off-by: Theodore Ts'o +Signed-off-by: Rajani Kantha <681739313@139.com> +Signed-off-by: Sasha Levin +--- + fs/ext4/xattr.c | 10 +++++----- + fs/ext4/xattr.h | 3 +++ + 2 files changed, 8 insertions(+), 5 deletions(-) + +diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c +index ce986312bf685..6946c1fc790ab 100644 +--- a/fs/ext4/xattr.c ++++ b/fs/ext4/xattr.c +@@ -653,7 +653,7 @@ ext4_xattr_ibody_get(struct inode *inode, int name_index, const char *name, + return error; + raw_inode = ext4_raw_inode(&iloc); + header = IHDR(inode, raw_inode); +- end = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size; ++ end = ITAIL(inode, raw_inode); + error = xattr_check_inode(inode, header, end); + if (error) + goto cleanup; +@@ -797,7 +797,7 @@ 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 = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size; ++ end = ITAIL(inode, raw_inode); + error = xattr_check_inode(inode, header, end); + if (error) + goto cleanup; +@@ -883,7 +883,7 @@ 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 = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size; ++ end = ITAIL(inode, raw_inode); + ret = xattr_check_inode(inode, header, end); + if (ret) + goto out; +@@ -2249,7 +2249,7 @@ int ext4_xattr_ibody_find(struct inode *inode, struct ext4_xattr_info *i, + header = IHDR(inode, raw_inode); + is->s.base = is->s.first = IFIRST(header); + is->s.here = is->s.first; +- is->s.end = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size; ++ 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) +@@ -2800,7 +2800,7 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize, + */ + + base = IFIRST(header); +- end = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size; ++ end = ITAIL(inode, raw_inode); + min_offs = end - base; + total_ino = sizeof(struct ext4_xattr_ibody_header) + sizeof(u32); + +diff --git a/fs/ext4/xattr.h b/fs/ext4/xattr.h +index b25c2d7b5f991..5197f17ffd9a2 100644 +--- a/fs/ext4/xattr.h ++++ b/fs/ext4/xattr.h +@@ -67,6 +67,9 @@ struct ext4_xattr_entry { + ((void *)raw_inode + \ + EXT4_GOOD_OLD_INODE_SIZE + \ + EXT4_I(inode)->i_extra_isize)) ++#define ITAIL(inode, raw_inode) \ ++ ((void *)(raw_inode) + \ ++ EXT4_SB((inode)->i_sb)->s_inode_size) + #define IFIRST(hdr) ((struct ext4_xattr_entry *)((hdr)+1)) + + /* +-- +2.51.0 + diff --git a/queue-6.12/f2fs-fix-to-avoid-overflow-while-left-shift-operatio.patch b/queue-6.12/f2fs-fix-to-avoid-overflow-while-left-shift-operatio.patch new file mode 100644 index 0000000000..971eead5c0 --- /dev/null +++ b/queue-6.12/f2fs-fix-to-avoid-overflow-while-left-shift-operatio.patch @@ -0,0 +1,40 @@ +From 74e909c0d3843c92fd0e5d84193a38eb3a8d8324 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 14 Nov 2025 14:47:40 +0800 +Subject: f2fs: fix to avoid overflow while left shift operation + +From: Chao Yu + +[ Upstream commit 0fe1c6bec54ea68ed8c987b3890f2296364e77bb ] + +Should cast type of folio->index from pgoff_t to loff_t to avoid overflow +while left shift operation. + +Fixes: 3265d3db1f16 ("f2fs: support partial truncation on compressed inode") +Signed-off-by: Chao Yu +Signed-off-by: Jaegeuk Kim +[ Modification: Using rpages[i]->index instead of folio->index due to +it was changed since commit:1cda5bc0b2fe ("f2fs: Use a folio in +f2fs_truncate_partial_cluster()") on 6.14 ] +Signed-off-by: Rajani Kantha <681739313@139.com> +Signed-off-by: Sasha Levin +--- + fs/f2fs/compress.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/fs/f2fs/compress.c b/fs/f2fs/compress.c +index b05bb7bfa14c5..fcd21bb060cd4 100644 +--- a/fs/f2fs/compress.c ++++ b/fs/f2fs/compress.c +@@ -1236,7 +1236,7 @@ int f2fs_truncate_partial_cluster(struct inode *inode, u64 from, bool lock) + int i; + + for (i = cluster_size - 1; i >= 0; i--) { +- loff_t start = rpages[i]->index << PAGE_SHIFT; ++ loff_t start = (loff_t)rpages[i]->index << PAGE_SHIFT; + + if (from <= start) { + zero_user_segment(rpages[i], 0, PAGE_SIZE); +-- +2.51.0 + diff --git a/queue-6.12/lib-crypto-arm-curve25519-disable-on-cpu_big_endian.patch b/queue-6.12/lib-crypto-arm-curve25519-disable-on-cpu_big_endian.patch new file mode 100644 index 0000000000..e4ec633822 --- /dev/null +++ b/queue-6.12/lib-crypto-arm-curve25519-disable-on-cpu_big_endian.patch @@ -0,0 +1,47 @@ +From 8a32791cb389933eadd18cb702d69c3598cc1af9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 11 Nov 2025 12:29:36 -0800 +Subject: lib/crypto: arm/curve25519: Disable on CPU_BIG_ENDIAN + +From: Eric Biggers + +commit 44e8241c51f762aafa50ed116da68fd6ecdcc954 upstream. + +On big endian arm kernels, the arm optimized Curve25519 code produces +incorrect outputs and fails the Curve25519 test. This has been true +ever since this code was added. + +It seems that hardly anyone (or even no one?) actually uses big endian +arm kernels. But as long as they're ostensibly supported, we should +disable this code on them so that it's not accidentally used. + +Note: for future-proofing, use !CPU_BIG_ENDIAN instead of +CPU_LITTLE_ENDIAN. Both of these are arch-specific options that could +get removed in the future if big endian support gets dropped. + +Fixes: d8f1308a025f ("crypto: arm/curve25519 - wire up NEON implementation") +Cc: stable@vger.kernel.org +Acked-by: Ard Biesheuvel +Link: https://lore.kernel.org/r/20251104054906.716914-1-ebiggers@kernel.org +Signed-off-by: Eric Biggers +Signed-off-by: Sasha Levin +--- + arch/arm/crypto/Kconfig | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/arm/crypto/Kconfig b/arch/arm/crypto/Kconfig +index f87e63b2212eb..df2ae5c6af953 100644 +--- a/arch/arm/crypto/Kconfig ++++ b/arch/arm/crypto/Kconfig +@@ -4,7 +4,7 @@ menu "Accelerated Cryptographic Algorithms for CPU (arm)" + + config CRYPTO_CURVE25519_NEON + tristate +- depends on KERNEL_MODE_NEON ++ depends on KERNEL_MODE_NEON && !CPU_BIG_ENDIAN + select CRYPTO_KPP + select CRYPTO_LIB_CURVE25519_GENERIC + select CRYPTO_ARCH_HAVE_LIB_CURVE25519 +-- +2.51.0 + diff --git a/queue-6.12/proc-fix-the-issue-of-proc_mem_open-returning-null.patch b/queue-6.12/proc-fix-the-issue-of-proc_mem_open-returning-null.patch new file mode 100644 index 0000000000..d04699da54 --- /dev/null +++ b/queue-6.12/proc-fix-the-issue-of-proc_mem_open-returning-null.patch @@ -0,0 +1,133 @@ +From 23923381ba6c321463cf1ed5f71276063c6870b6 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 11 Nov 2025 08:19:26 +0000 +Subject: proc: fix the issue of proc_mem_open returning NULL + +From: Penglei Jiang + +[ Upstream commit 65c66047259fad1b868d4454bc5af95b46a5f954 ] + +proc_mem_open() can return an errno, NULL, or mm_struct*. If it fails to +acquire mm, it returns NULL, but the caller does not check for the case +when the return value is NULL. + +The following conditions lead to failure in acquiring mm: + + - The task is a kernel thread (PF_KTHREAD) + - The task is exiting (PF_EXITING) + +Changes: + + - Add documentation comments for the return value of proc_mem_open(). + - Add checks in the caller to return -ESRCH when proc_mem_open() + returns NULL. + +Link: https://lkml.kernel.org/r/20250404063357.78891-1-superman.xpt@gmail.com +Reported-by: syzbot+f9238a0a31f9b5603fef@syzkaller.appspotmail.com +Closes: https://lore.kernel.org/all/000000000000f52642060d4e3750@google.com +Signed-off-by: Penglei Jiang +Cc: Al Viro +Cc: Adrian Ratiu +Cc: Christian Brauner +Cc: Felix Moessbauer +Cc: Jeff layton +Cc: Lorenzo Stoakes +Cc: Mateusz Guzik +Cc: Thomas Gleinxer +Cc: xu xin +Cc: Alexey Dobriyan +Signed-off-by: Andrew Morton +[ acsjakub: applied cleanly ] +Signed-off-by: Jakub Acs +Signed-off-by: Sasha Levin +--- + fs/proc/base.c | 12 +++++++++--- + fs/proc/task_mmu.c | 12 ++++++------ + fs/proc/task_nommu.c | 4 ++-- + 3 files changed, 17 insertions(+), 11 deletions(-) + +diff --git a/fs/proc/base.c b/fs/proc/base.c +index a2541f5204af0..d060af34a6e83 100644 +--- a/fs/proc/base.c ++++ b/fs/proc/base.c +@@ -828,7 +828,13 @@ static const struct file_operations proc_single_file_operations = { + .release = single_release, + }; + +- ++/* ++ * proc_mem_open() can return errno, NULL or mm_struct*. ++ * ++ * - Returns NULL if the task has no mm (PF_KTHREAD or PF_EXITING) ++ * - Returns mm_struct* on success ++ * - Returns error code on failure ++ */ + struct mm_struct *proc_mem_open(struct inode *inode, unsigned int mode) + { + struct task_struct *task = get_proc_task(inode); +@@ -853,8 +859,8 @@ static int __mem_open(struct inode *inode, struct file *file, unsigned int mode) + { + struct mm_struct *mm = proc_mem_open(inode, mode); + +- if (IS_ERR(mm)) +- return PTR_ERR(mm); ++ if (IS_ERR_OR_NULL(mm)) ++ return mm ? PTR_ERR(mm) : -ESRCH; + + file->private_data = mm; + return 0; +diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c +index 8f5ad591d7625..08a06fd37f0e1 100644 +--- a/fs/proc/task_mmu.c ++++ b/fs/proc/task_mmu.c +@@ -212,8 +212,8 @@ static int proc_maps_open(struct inode *inode, struct file *file, + + priv->inode = inode; + priv->mm = proc_mem_open(inode, PTRACE_MODE_READ); +- if (IS_ERR(priv->mm)) { +- int err = PTR_ERR(priv->mm); ++ if (IS_ERR_OR_NULL(priv->mm)) { ++ int err = priv->mm ? PTR_ERR(priv->mm) : -ESRCH; + + seq_release_private(inode, file); + return err; +@@ -1316,8 +1316,8 @@ static int smaps_rollup_open(struct inode *inode, struct file *file) + + priv->inode = inode; + priv->mm = proc_mem_open(inode, PTRACE_MODE_READ); +- if (IS_ERR(priv->mm)) { +- ret = PTR_ERR(priv->mm); ++ if (IS_ERR_OR_NULL(priv->mm)) { ++ ret = priv->mm ? PTR_ERR(priv->mm) : -ESRCH; + + single_release(inode, file); + goto out_free; +@@ -2049,8 +2049,8 @@ static int pagemap_open(struct inode *inode, struct file *file) + struct mm_struct *mm; + + mm = proc_mem_open(inode, PTRACE_MODE_READ); +- if (IS_ERR(mm)) +- return PTR_ERR(mm); ++ if (IS_ERR_OR_NULL(mm)) ++ return mm ? PTR_ERR(mm) : -ESRCH; + file->private_data = mm; + return 0; + } +diff --git a/fs/proc/task_nommu.c b/fs/proc/task_nommu.c +index bce6745330003..59bfd61d653aa 100644 +--- a/fs/proc/task_nommu.c ++++ b/fs/proc/task_nommu.c +@@ -260,8 +260,8 @@ static int maps_open(struct inode *inode, struct file *file, + + priv->inode = inode; + priv->mm = proc_mem_open(inode, PTRACE_MODE_READ); +- if (IS_ERR(priv->mm)) { +- int err = PTR_ERR(priv->mm); ++ if (IS_ERR_OR_NULL(priv->mm)) { ++ int err = priv->mm ? PTR_ERR(priv->mm) : -ESRCH; + + seq_release_private(inode, file); + return err; +-- +2.51.0 + diff --git a/queue-6.12/series b/queue-6.12/series index 2ce7c677e3..bba044cde7 100644 --- a/queue-6.12/series +++ b/queue-6.12/series @@ -82,3 +82,10 @@ acpi-hmat-fix-lockdep-warning-for-hmem_register_reso.patch bpf-add-bpf_prog_run_data_pointers.patch bpf-account-for-current-allocated-stack-depth-in-wid.patch irqchip-riscv-intc-add-missing-free-callback-in-risc.patch +wifi-ath11k-clear-affinity-hint-before-calling-ath11.patch +proc-fix-the-issue-of-proc_mem_open-returning-null.patch +ext4-introduce-itail-helper.patch +lib-crypto-arm-curve25519-disable-on-cpu_big_endian.patch +ext4-fix-out-of-bound-read-in-ext4_xattr_inode_dec_r.patch +bluetooth-mgmt-fix-possible-uafs.patch +f2fs-fix-to-avoid-overflow-while-left-shift-operatio.patch diff --git a/queue-6.12/wifi-ath11k-clear-affinity-hint-before-calling-ath11.patch b/queue-6.12/wifi-ath11k-clear-affinity-hint-before-calling-ath11.patch new file mode 100644 index 0000000000..47a526caaf --- /dev/null +++ b/queue-6.12/wifi-ath11k-clear-affinity-hint-before-calling-ath11.patch @@ -0,0 +1,61 @@ +From 85e567a1d460759567568a4ea3ad2702755440df Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 14 Nov 2025 15:05:09 +0800 +Subject: wifi: ath11k: Clear affinity hint before calling + ath11k_pcic_free_irq() in error path + +From: Manivannan Sadhasivam + +[ Upstream commit 68410c5bd381a81bcc92b808e7dc4e6b9ed25d11 ] + +If a shared IRQ is used by the driver due to platform limitation, then the +IRQ affinity hint is set right after the allocation of IRQ vectors in +ath11k_pci_alloc_msi(). This does no harm unless one of the functions +requesting the IRQ fails and attempt to free the IRQ. This results in the +below warning: + +WARNING: CPU: 7 PID: 349 at kernel/irq/manage.c:1929 free_irq+0x278/0x29c +Call trace: + free_irq+0x278/0x29c + ath11k_pcic_free_irq+0x70/0x10c [ath11k] + ath11k_pci_probe+0x800/0x820 [ath11k_pci] + local_pci_probe+0x40/0xbc + +The warning is due to not clearing the affinity hint before freeing the +IRQs. + +So to fix this issue, clear the IRQ affinity hint before calling +ath11k_pcic_free_irq() in the error path. The affinity will be cleared once +again further down the error path due to code organization, but that does +no harm. + +Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-05266-QCAHSTSWPLZ_V2_TO_X86-1 + +Cc: Baochen Qiang +Fixes: 39564b475ac5 ("wifi: ath11k: fix boot failure with one MSI vector") +Signed-off-by: Manivannan Sadhasivam +Reviewed-by: Baochen Qiang +Link: https://patch.msgid.link/20250225053447.16824-2-manivannan.sadhasivam@linaro.org +Signed-off-by: Jeff Johnson +Signed-off-by: Wenshan Lan +Signed-off-by: Sasha Levin +--- + drivers/net/wireless/ath/ath11k/pci.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/net/wireless/ath/ath11k/pci.c b/drivers/net/wireless/ath/ath11k/pci.c +index 6ebfa5d02e2e5..c1d576ff77faa 100644 +--- a/drivers/net/wireless/ath/ath11k/pci.c ++++ b/drivers/net/wireless/ath/ath11k/pci.c +@@ -936,6 +936,8 @@ static int ath11k_pci_probe(struct pci_dev *pdev, + return 0; + + err_free_irq: ++ /* __free_irq() expects the caller to have cleared the affinity hint */ ++ ath11k_pci_set_irq_affinity_hint(ab_pci, NULL); + ath11k_pcic_free_irq(ab); + + err_ce_free: +-- +2.51.0 + diff --git a/queue-6.17/lib-crypto-arm-curve25519-disable-on-cpu_big_endian.patch b/queue-6.17/lib-crypto-arm-curve25519-disable-on-cpu_big_endian.patch new file mode 100644 index 0000000000..92f455924c --- /dev/null +++ b/queue-6.17/lib-crypto-arm-curve25519-disable-on-cpu_big_endian.patch @@ -0,0 +1,47 @@ +From f05932b8294d173b24c11609b8ba4c6943cf5ba8 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 11 Nov 2025 12:29:23 -0800 +Subject: lib/crypto: arm/curve25519: Disable on CPU_BIG_ENDIAN + +From: Eric Biggers + +commit 44e8241c51f762aafa50ed116da68fd6ecdcc954 upstream. + +On big endian arm kernels, the arm optimized Curve25519 code produces +incorrect outputs and fails the Curve25519 test. This has been true +ever since this code was added. + +It seems that hardly anyone (or even no one?) actually uses big endian +arm kernels. But as long as they're ostensibly supported, we should +disable this code on them so that it's not accidentally used. + +Note: for future-proofing, use !CPU_BIG_ENDIAN instead of +CPU_LITTLE_ENDIAN. Both of these are arch-specific options that could +get removed in the future if big endian support gets dropped. + +Fixes: d8f1308a025f ("crypto: arm/curve25519 - wire up NEON implementation") +Cc: stable@vger.kernel.org +Acked-by: Ard Biesheuvel +Link: https://lore.kernel.org/r/20251104054906.716914-1-ebiggers@kernel.org +Signed-off-by: Eric Biggers +Signed-off-by: Sasha Levin +--- + arch/arm/crypto/Kconfig | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/arm/crypto/Kconfig b/arch/arm/crypto/Kconfig +index 1e5f3cdf691c4..a00ab9265280f 100644 +--- a/arch/arm/crypto/Kconfig ++++ b/arch/arm/crypto/Kconfig +@@ -4,7 +4,7 @@ menu "Accelerated Cryptographic Algorithms for CPU (arm)" + + config CRYPTO_CURVE25519_NEON + tristate +- depends on KERNEL_MODE_NEON ++ depends on KERNEL_MODE_NEON && !CPU_BIG_ENDIAN + select CRYPTO_KPP + select CRYPTO_LIB_CURVE25519_GENERIC + select CRYPTO_ARCH_HAVE_LIB_CURVE25519 +-- +2.51.0 + diff --git a/queue-6.17/series b/queue-6.17/series index 007e1a7b33..f302f61f93 100644 --- a/queue-6.17/series +++ b/queue-6.17/series @@ -119,3 +119,4 @@ bpf-add-bpf_prog_run_data_pointers.patch bpf-account-for-current-allocated-stack-depth-in-wid.patch irqchip-riscv-intc-add-missing-free-callback-in-risc.patch posix-timers-plug-potential-memory-leak-in-do_timer_.patch +lib-crypto-arm-curve25519-disable-on-cpu_big_endian.patch