From: Greg Kroah-Hartman Date: Wed, 5 Aug 2026 09:39:40 +0000 (+0200) Subject: 6.18-stable patches X-Git-Url: http://git.ipfire.org/index.cgi?a=commitdiff_plain;h=b4fbbd2197f6b520c59739aa49617e7d1a4574db;p=thirdparty%2Fkernel%2Fstable-queue.git 6.18-stable patches added patches: audit-fix-potential-integer-overflow-in-audit_log_n_string.patch audit-fix-potential-use-after-free-in-audit_del_rule.patch bluetooth-btmtk-fix-short-read-errors-in-btmtk_usb_uhw_reg_read.patch bluetooth-btusb-fix-short-read-errors-in-btusb_qca_send_vendor_req.patch bluetooth-hci_sync-fix-advertising-data-uafs.patch bluetooth-hidp-reject-frames-without-a-transaction-header.patch bluetooth-hidp-validate-numbered-report-payloads.patch bluetooth-mgmt-fix-pending-command-uaf-in-eir-updates.patch bluetooth-mgmt-fix-uaf-in-pair-command-cancellation.patch bluetooth-sco-give-the-socket-its-own-sco_conn-reference.patch dibs-fix-use-after-free-of-dmb_node-in-loopback-attach-detach-unregister.patch fortify-disable-wstringop-overread-in-tests.patch fs-proc-task_mmu-fix-pagemap_scan-written-state-for-pmd-holes.patch kvm-s390-pci-fix-memory-accounting-for-pinned-unpinned-pages.patch kvm-s390-pci-fix-missing-error-codes-and-memory-unaccounting.patch kvm-s390-pci-fix-null-dereference-on-aibv-allocation-failure.patch kvm-s390-pci-reject-adapter-interrupt-forwarding-if-already-enabled.patch kvm-s390-pci-validate-aibv-and-aisb-before-pinning-guest-pages.patch kvm-svm-update-x2apic-msr-intercepts-if-avic-is-inhibited-while-l2-is-active.patch kvm-vmx-add-memory-clobber-to-asm-for-vmx-instructions.patch mm-hugetlb-fix-list-corruption-in-allocate_file_region_entries.patch mm-migrate_device-fix-pte_pfn-pte_dirty-called-on-non-present-pte.patch mm-percpu-km-fix-bitmap-overflow-and-accounting-in-pcpu_create_chunk.patch mm-util-don-t-read-__page_2-for-order-1-folios-in-snapshot_page.patch mm-vmstat-fold-stranded-per-cpu-node-stats-when-a-node-comes-online.patch sctp-validate-adaptation-indication-parameter-length.patch selftest-fix-headers-in-fclog.c.patch tracing-fprobe-roll-back-on-enable_trace_fprobe-failure.patch tracing-probes-reject-arg0-in-meta-argument-expansion.patch --- diff --git a/queue-6.18/audit-fix-potential-integer-overflow-in-audit_log_n_string.patch b/queue-6.18/audit-fix-potential-integer-overflow-in-audit_log_n_string.patch new file mode 100644 index 0000000000..3eb5a1ec5c --- /dev/null +++ b/queue-6.18/audit-fix-potential-integer-overflow-in-audit_log_n_string.patch @@ -0,0 +1,61 @@ +From f865c143629d4094866a811dba5f329250bad486 Mon Sep 17 00:00:00 2001 +From: Zhan Xusheng +Date: Sat, 18 Jul 2026 13:09:22 +0800 +Subject: audit: fix potential integer overflow in audit_log_n_string() + +From: Zhan Xusheng + +commit f865c143629d4094866a811dba5f329250bad486 upstream. + +audit_log_n_string() computes new_len as "slen + 3" (enclosing quotes +plus the NUL terminator) and stores it into an int, while slen is a +size_t. For a sufficiently large slen the addition can overflow and/or +the result be truncated when assigned to the int new_len, so the +"new_len > avail" check can be bypassed and the subsequent +memcpy(ptr, string, slen) can write past the skb tail. + +This is the same class of bug that was fixed for the hex sibling in +commit 65dfde57d1e2 ("audit: fix potential integer overflow in +audit_log_n_hex()"); both helpers are reached through +audit_log_n_untrustedstring() with the same length source. + +Make new_len a size_t and use check_add_overflow() to catch the +overflow, mirroring the audit_log_n_hex() fix. No functional change for +the in-tree callers, which all pass bounded lengths. + +Cc: stable@vger.kernel.org +Fixes: 168b7173959f ("AUDIT: Clean up logging of untrusted strings") +Signed-off-by: Zhan Xusheng +Signed-off-by: Paul Moore +Signed-off-by: Greg Kroah-Hartman +--- + kernel/audit.c | 11 +++++++++-- + 1 file changed, 9 insertions(+), 2 deletions(-) + +--- a/kernel/audit.c ++++ b/kernel/audit.c +@@ -2115,7 +2115,8 @@ void audit_log_n_hex(struct audit_buffer + void audit_log_n_string(struct audit_buffer *ab, const char *string, + size_t slen) + { +- int avail, new_len; ++ int avail; ++ size_t new_len; + unsigned char *ptr; + struct sk_buff *skb; + +@@ -2125,7 +2126,13 @@ void audit_log_n_string(struct audit_buf + BUG_ON(!ab->skb); + skb = ab->skb; + avail = skb_tailroom(skb); +- new_len = slen + 3; /* enclosing quotes + null terminator */ ++ ++ /* enclosing quotes + null terminator */ ++ if (check_add_overflow(slen, 3, &new_len)) { ++ audit_log_format(ab, "?"); ++ return; ++ } ++ + if (new_len > avail) { + avail = audit_expand(ab, new_len); + if (!avail) diff --git a/queue-6.18/audit-fix-potential-use-after-free-in-audit_del_rule.patch b/queue-6.18/audit-fix-potential-use-after-free-in-audit_del_rule.patch new file mode 100644 index 0000000000..14e10cfa3e --- /dev/null +++ b/queue-6.18/audit-fix-potential-use-after-free-in-audit_del_rule.patch @@ -0,0 +1,55 @@ +From 246df90b5f1a8a6e6abbd2f058b029558720adec Mon Sep 17 00:00:00 2001 +From: Luxiao Xu +Date: Tue, 21 Jul 2026 23:37:41 +0800 +Subject: audit: fix potential use-after-free in audit_del_rule() + +From: Luxiao Xu + +commit 246df90b5f1a8a6e6abbd2f058b029558720adec upstream. + +`audit_del_rule()` destroys `e->rule.exe` via `audit_remove_mark_rule()` +before unlinking the rule from RCU-visible filter lists and waiting for a +grace period. Concurrent readers in `audit_filter()` and +`audit_filter_rules()` still dereference `e->rule.exe`, while the fsnotify +mark can be freed on an independent lifetime path. This creates a +use-after-free window during rule deletion. + +Fix this by unlinking the rule from the RCU-visible lists and invoking +`synchronize_rcu()` before calling `audit_remove_mark_rule()` (and other +rule removal helpers). This ensures that all existing RCU readers have +exited the critical section before any underlying resources are destroyed. + +Cc: stable@vger.kernel.org +Fixes: 34d99af52ad4 ("audit: implement audit by executable") +Reported-by: Vega +Assisted-by: Codex:gpt-5.4 +Signed-off-by: Luxiao Xu +Signed-off-by: Ren Wei +Signed-off-by: Paul Moore +Signed-off-by: Greg Kroah-Hartman +--- + kernel/auditfilter.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +--- a/kernel/auditfilter.c ++++ b/kernel/auditfilter.c +@@ -1047,6 +1047,10 @@ int audit_del_rule(struct audit_entry *e + goto out; + } + ++ list_del_rcu(&e->list); ++ list_del(&e->rule.list); ++ synchronize_rcu(); ++ + if (e->rule.watch) + audit_remove_watch_rule(&e->rule); + +@@ -1064,8 +1068,6 @@ int audit_del_rule(struct audit_entry *e + audit_signals--; + #endif + +- list_del_rcu(&e->list); +- list_del(&e->rule.list); + call_rcu(&e->rcu, audit_free_rule_rcu); + + out: diff --git a/queue-6.18/bluetooth-btmtk-fix-short-read-errors-in-btmtk_usb_uhw_reg_read.patch b/queue-6.18/bluetooth-btmtk-fix-short-read-errors-in-btmtk_usb_uhw_reg_read.patch new file mode 100644 index 0000000000..a4606c478d --- /dev/null +++ b/queue-6.18/bluetooth-btmtk-fix-short-read-errors-in-btmtk_usb_uhw_reg_read.patch @@ -0,0 +1,167 @@ +From b186c18c4843dd58adc29443369bddc71cb626a3 Mon Sep 17 00:00:00 2001 +From: Greg Kroah-Hartman +Date: Mon, 27 Jul 2026 17:57:32 +0200 +Subject: Bluetooth: btmtk: Fix short read errors in btmtk_usb_uhw_reg_read() + +From: Greg Kroah-Hartman + +commit b186c18c4843dd58adc29443369bddc71cb626a3 upstream. + +If btmtk_usb_uhw_reg_read() gets a "short" read from a device, it will +accidentally treat that as a "real" read and populate the returned value +with some unknown and probably totally invalid data. + +Fix this logic error up by calling usb_control_msg_recv() which +guarantees a "full" read happens, and then simplify the error checking +for when btmtk_usb_uhw_reg_read() is called. + +Note, one caller of btmtk_usb_uhw_reg_read() does not check the return +value, but as we pre-initialize the return value as 0, an incorrect read +will not do anything wrong. + +Cc: stable +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Greg Kroah-Hartman +--- + drivers/bluetooth/btmtk.c | 50 ++++++++++++++++++++-------------------------- + 1 file changed, 22 insertions(+), 28 deletions(-) + +--- a/drivers/bluetooth/btmtk.c ++++ b/drivers/bluetooth/btmtk.c +@@ -763,30 +763,24 @@ static int btmtk_usb_uhw_reg_write(struc + static int btmtk_usb_uhw_reg_read(struct hci_dev *hdev, u32 reg, u32 *val) + { + struct btmtk_data *data = hci_get_priv(hdev); +- int pipe, err; +- void *buf; +- +- buf = kzalloc(4, GFP_KERNEL); +- if (!buf) +- return -ENOMEM; ++ u8 buf[sizeof(u32)]; ++ int err; + +- pipe = usb_rcvctrlpipe(data->udev, 0); +- err = usb_control_msg(data->udev, pipe, 0x01, +- 0xDE, +- reg >> 16, reg & 0xffff, +- buf, 4, USB_CTRL_GET_TIMEOUT); +- if (err < 0) { ++ *val = 0; ++ err = usb_control_msg_recv(data->udev, 0, 0x01, ++ 0xDE, ++ reg >> 16, reg & 0xffff, ++ buf, sizeof(buf), USB_CTRL_GET_TIMEOUT, ++ GFP_KERNEL); ++ if (err) { + bt_dev_err(hdev, "Failed to read uhw reg(%d)", err); +- goto err_free_buf; ++ return err; + } + + *val = get_unaligned_le32(buf); + bt_dev_dbg(hdev, "reg=%x, value=0x%08x", reg, *val); + +-err_free_buf: +- kfree(buf); +- +- return err; ++ return 0; + } + + static int btmtk_usb_reg_read(struct hci_dev *hdev, u32 reg, u32 *val) +@@ -836,7 +830,7 @@ int btmtk_usb_subsys_reset(struct hci_de + + if (dev_id == 0x7922) { + err = btmtk_usb_uhw_reg_read(hdev, MTK_BT_SUBSYS_RST, &val); +- if (err < 0) ++ if (err) + return err; + val |= 0x00002020; + err = btmtk_usb_uhw_reg_write(hdev, MTK_BT_SUBSYS_RST, val); +@@ -846,7 +840,7 @@ int btmtk_usb_subsys_reset(struct hci_de + if (err < 0) + return err; + err = btmtk_usb_uhw_reg_read(hdev, MTK_BT_SUBSYS_RST, &val); +- if (err < 0) ++ if (err) + return err; + val |= BIT(0); + err = btmtk_usb_uhw_reg_write(hdev, MTK_BT_SUBSYS_RST, val); +@@ -855,14 +849,14 @@ int btmtk_usb_subsys_reset(struct hci_de + msleep(100); + } else if (dev_id == 0x7925) { + err = btmtk_usb_uhw_reg_read(hdev, MTK_BT_RESET_REG_CONNV3, &val); +- if (err < 0) ++ if (err) + return err; + val |= (1 << 5); + err = btmtk_usb_uhw_reg_write(hdev, MTK_BT_RESET_REG_CONNV3, val); + if (err < 0) + return err; + err = btmtk_usb_uhw_reg_read(hdev, MTK_BT_RESET_REG_CONNV3, &val); +- if (err < 0) ++ if (err) + return err; + val &= 0xFFFF00FF; + val |= (1 << 13); +@@ -873,7 +867,7 @@ int btmtk_usb_subsys_reset(struct hci_de + if (err < 0) + return err; + err = btmtk_usb_uhw_reg_read(hdev, MTK_BT_RESET_REG_CONNV3, &val); +- if (err < 0) ++ if (err) + return err; + val |= (1 << 0); + err = btmtk_usb_uhw_reg_write(hdev, MTK_BT_RESET_REG_CONNV3, val); +@@ -883,13 +877,13 @@ int btmtk_usb_subsys_reset(struct hci_de + if (err < 0) + return err; + err = btmtk_usb_uhw_reg_read(hdev, MTK_UDMA_INT_STA_BT, &val); +- if (err < 0) ++ if (err) + return err; + err = btmtk_usb_uhw_reg_write(hdev, MTK_UDMA_INT_STA_BT1, 0x000000FF); + if (err < 0) + return err; + err = btmtk_usb_uhw_reg_read(hdev, MTK_UDMA_INT_STA_BT1, &val); +- if (err < 0) ++ if (err) + return err; + msleep(100); + } else { +@@ -899,7 +893,7 @@ int btmtk_usb_subsys_reset(struct hci_de + if (err < 0) + return err; + err = btmtk_usb_uhw_reg_read(hdev, MTK_BT_WDT_STATUS, &val); +- if (err < 0) ++ if (err) + return err; + /* Reset the bluetooth chip via USB interface. */ + err = btmtk_usb_uhw_reg_write(hdev, MTK_BT_SUBSYS_RST, 1); +@@ -909,13 +903,13 @@ int btmtk_usb_subsys_reset(struct hci_de + if (err < 0) + return err; + err = btmtk_usb_uhw_reg_read(hdev, MTK_UDMA_INT_STA_BT, &val); +- if (err < 0) ++ if (err) + return err; + err = btmtk_usb_uhw_reg_write(hdev, MTK_UDMA_INT_STA_BT1, 0x000000FF); + if (err < 0) + return err; + err = btmtk_usb_uhw_reg_read(hdev, MTK_UDMA_INT_STA_BT1, &val); +- if (err < 0) ++ if (err) + return err; + /* MT7921 need to delay 20ms between toggle reset bit */ + msleep(20); +@@ -923,7 +917,7 @@ int btmtk_usb_subsys_reset(struct hci_de + if (err < 0) + return err; + err = btmtk_usb_uhw_reg_read(hdev, MTK_BT_SUBSYS_RST, &val); +- if (err < 0) ++ if (err) + return err; + } + diff --git a/queue-6.18/bluetooth-btusb-fix-short-read-errors-in-btusb_qca_send_vendor_req.patch b/queue-6.18/bluetooth-btusb-fix-short-read-errors-in-btusb_qca_send_vendor_req.patch new file mode 100644 index 0000000000..940ace8dc5 --- /dev/null +++ b/queue-6.18/bluetooth-btusb-fix-short-read-errors-in-btusb_qca_send_vendor_req.patch @@ -0,0 +1,97 @@ +From cac43d360c928bc0cbbd18809632388265649761 Mon Sep 17 00:00:00 2001 +From: Greg Kroah-Hartman +Date: Mon, 27 Jul 2026 17:57:34 +0200 +Subject: Bluetooth: btusb: Fix short read errors in btusb_qca_send_vendor_req() + +From: Greg Kroah-Hartman + +commit cac43d360c928bc0cbbd18809632388265649761 upstream. + +If btusb_qca_send_vendor_req() gets a "short" read from a device, it +will accidentally treat that as a "real" read and populate the returned +value with some unknown and probably totally invalid data. + +Fix this logic error up by calling usb_control_msg_recv() which +guarantees a "full" read happens, and then simplify the error checking +for when btusb_qca_send_vendor_req() is called. + +Cc: stable +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Greg Kroah-Hartman +--- + drivers/bluetooth/btusb.c | 30 +++++++++--------------------- + 1 file changed, 9 insertions(+), 21 deletions(-) + +--- a/drivers/bluetooth/btusb.c ++++ b/drivers/bluetooth/btusb.c +@@ -3345,28 +3345,16 @@ static const char *qca_get_fw_subdirecto + static int btusb_qca_send_vendor_req(struct usb_device *udev, u8 request, + void *data, u16 size) + { +- int pipe, err; +- u8 *buf; +- +- buf = kmalloc(size, GFP_KERNEL); +- if (!buf) +- return -ENOMEM; ++ int err; + + /* Found some of USB hosts have IOT issues with ours so that we should + * not wait until HCI layer is ready. + */ +- pipe = usb_rcvctrlpipe(udev, 0); +- err = usb_control_msg(udev, pipe, request, USB_TYPE_VENDOR | USB_DIR_IN, +- 0, 0, buf, size, USB_CTRL_GET_TIMEOUT); +- if (err < 0) { ++ err = usb_control_msg_recv(udev, 0, request, USB_TYPE_VENDOR | USB_DIR_IN, ++ 0, 0, data, size, USB_CTRL_GET_TIMEOUT, ++ GFP_KERNEL); ++ if (err) + dev_err(&udev->dev, "Failed to access otp area (%d)", err); +- goto done; +- } +- +- memcpy(data, buf, size); +- +-done: +- kfree(buf); + + return err; + } +@@ -3573,7 +3561,7 @@ static bool btusb_qca_need_patch(struct + struct qca_version ver; + + if (btusb_qca_send_vendor_req(udev, QCA_GET_TARGET_VERSION, &ver, +- sizeof(ver)) < 0) ++ sizeof(ver))) + return false; + /* only low ROM versions need patches */ + return !(le32_to_cpu(ver.rom_version) & ~0xffffU); +@@ -3591,7 +3579,7 @@ static int btusb_setup_qca(struct hci_de + + err = btusb_qca_send_vendor_req(udev, QCA_GET_TARGET_VERSION, &ver, + sizeof(ver)); +- if (err < 0) ++ if (err) + return err; + + ver_rom = le32_to_cpu(ver.rom_version); +@@ -3614,7 +3602,7 @@ static int btusb_setup_qca(struct hci_de + + err = btusb_qca_send_vendor_req(udev, QCA_CHECK_STATUS, &status, + sizeof(status)); +- if (err < 0) ++ if (err) + return err; + + if (!(status & QCA_PATCH_UPDATED)) { +@@ -3625,7 +3613,7 @@ static int btusb_setup_qca(struct hci_de + + err = btusb_qca_send_vendor_req(udev, QCA_GET_TARGET_VERSION, &ver, + sizeof(ver)); +- if (err < 0) ++ if (err) + return err; + + btdata->qca_dump.fw_version = le32_to_cpu(ver.patch_version); diff --git a/queue-6.18/bluetooth-hci_sync-fix-advertising-data-uafs.patch b/queue-6.18/bluetooth-hci_sync-fix-advertising-data-uafs.patch new file mode 100644 index 0000000000..697de17520 --- /dev/null +++ b/queue-6.18/bluetooth-hci_sync-fix-advertising-data-uafs.patch @@ -0,0 +1,341 @@ +From cdc36db204ffd97b947d64374cf23a210dc74777 Mon Sep 17 00:00:00 2001 +From: Chengfeng Ye +Date: Thu, 23 Jul 2026 23:34:40 +0800 +Subject: Bluetooth: hci_sync: Fix advertising data UAFs + +From: Chengfeng Ye + +commit cdc36db204ffd97b947d64374cf23a210dc74777 upstream. + +hci_find_adv_instance() returns an adv_info pointer that is valid only +while hdev->lock is held. The advertising command-sync paths perform +instance lookups without that lock and, in some cases, retain the pointer +while waiting for a controller response. + +An advertising termination event can therefore interleave as follows: + + hci_cmd_sync_work hci_rx_work + hci_find_adv_instance() + __hci_cmd_sync_status() + wait for controller reply hci_dev_lock() + hci_remove_adv_instance() + kfree(adv) + adv->scan_rsp_changed = false + +KASAN reported: + + BUG: KASAN: slab-use-after-free in hci_set_ext_scan_rsp_data_sync+0x2e1/0x300 + Write of size 1 at addr ffff88810a45d21d by task kworker/u17:0/88 + Workqueue: hci0 hci_cmd_sync_work + Call Trace: + hci_set_ext_scan_rsp_data_sync+0x2e1/0x300 + hci_schedule_adv_instance_sync+0x390/0x4c0 + hci_cmd_sync_work+0x173/0x300 + Allocated by task 87: + hci_add_adv_instance+0x538/0xac0 + add_advertising+0x885/0x1160 + Freed by task 89: + kfree+0x131/0x3c0 + hci_remove_adv_instance+0x1d8/0x3b0 + hci_le_ext_adv_term_evt+0x17b/0x730 + +Protect the instance lookup and payload construction in the extended +advertising, scan response, and periodic advertising data paths. Snapshot +the advertising parameters under hdev->lock, but release the lock before +waiting for the controller. + +Clear advertising-data dirty bits before issuing their commands and +restore them after a failure using a fresh lookup. Likewise, update the +reported transmit power through a fresh lookup after the parameter command +completes. No adv_info pointer then survives an HCI command wait. + +Fixes: cba6b758711c ("Bluetooth: hci_sync: Make use of hci_cmd_sync_queue set 2") +Cc: stable@vger.kernel.org +Suggested-by: Luiz Augusto von Dentz +Signed-off-by: Chengfeng Ye +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Greg Kroah-Hartman +--- + net/bluetooth/hci_sync.c | 131 ++++++++++++++++++++++++++++++++++------------- + 1 file changed, 97 insertions(+), 34 deletions(-) + +--- a/net/bluetooth/hci_sync.c ++++ b/net/bluetooth/hci_sync.c +@@ -1233,10 +1233,11 @@ static int hci_set_adv_set_random_addr_s + } + + static int +-hci_set_ext_adv_params_sync(struct hci_dev *hdev, struct adv_info *adv, ++hci_set_ext_adv_params_sync(struct hci_dev *hdev, u8 instance, + const struct hci_cp_le_set_ext_adv_params *cp, + struct hci_rp_le_set_ext_adv_params *rp) + { ++ struct adv_info *adv; + struct sk_buff *skb; + + skb = __hci_cmd_sync(hdev, HCI_OP_LE_SET_EXT_ADV_PARAMS, sizeof(*cp), +@@ -1264,11 +1265,15 @@ hci_set_ext_adv_params_sync(struct hci_d + + if (!rp->status) { + hdev->adv_addr_type = cp->own_addr_type; +- if (!cp->handle) { ++ if (!instance) { + /* Store in hdev for instance 0 */ + hdev->adv_tx_power = rp->tx_power; +- } else if (adv) { +- adv->tx_power = rp->tx_power; ++ } else { ++ hci_dev_lock(hdev); ++ adv = hci_find_adv_instance(hdev, instance); ++ if (adv) ++ adv->tx_power = rp->tx_power; ++ hci_dev_unlock(hdev); + } + } + +@@ -1284,9 +1289,13 @@ static int hci_set_ext_adv_data_sync(str + int err; + + if (instance) { ++ hci_dev_lock(hdev); ++ + adv = hci_find_adv_instance(hdev, instance); +- if (!adv || !adv->adv_data_changed) ++ if (!adv || !adv->adv_data_changed) { ++ hci_dev_unlock(hdev); + return 0; ++ } + } + + len = eir_create_adv_data(hdev, instance, pdu->data, +@@ -1297,16 +1306,27 @@ static int hci_set_ext_adv_data_sync(str + pdu->operation = LE_SET_ADV_DATA_OP_COMPLETE; + pdu->frag_pref = LE_SET_ADV_DATA_NO_FRAG; + ++ if (adv) { ++ adv->adv_data_changed = false; ++ hci_dev_unlock(hdev); ++ } ++ + err = __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_EXT_ADV_DATA, + struct_size(pdu, data, len), pdu, + HCI_CMD_TIMEOUT); +- if (err) ++ if (err) { ++ if (instance) { ++ hci_dev_lock(hdev); ++ adv = hci_find_adv_instance(hdev, instance); ++ if (adv) ++ adv->adv_data_changed = true; ++ hci_dev_unlock(hdev); ++ } ++ + return err; ++ } + +- /* Update data if the command succeed */ +- if (adv) { +- adv->adv_data_changed = false; +- } else { ++ if (!instance) { + memcpy(hdev->adv_data, pdu->data, len); + hdev->adv_data_len = len; + } +@@ -1360,22 +1380,22 @@ int hci_setup_ext_adv_instance_sync(stru + struct adv_info *adv; + bool secondary_adv; + +- if (instance > 0) { +- adv = hci_find_adv_instance(hdev, instance); +- if (!adv) +- return -EINVAL; +- } else { +- adv = NULL; +- } +- + /* Updating parameters of an active instance will return a +- * Command Disallowed error, so we must first disable the +- * instance if it is active. ++ * Command Disallowed error, so disable it before taking a snapshot. + */ +- if (adv) { ++ if (instance > 0) { + err = hci_disable_ext_adv_instance_sync(hdev, instance); + if (err) + return err; ++ ++ hci_dev_lock(hdev); ++ adv = hci_find_adv_instance(hdev, instance); ++ if (!adv) { ++ hci_dev_unlock(hdev); ++ return -EINVAL; ++ } ++ } else { ++ adv = NULL; + } + + flags = hci_adv_instance_flags(hdev, instance); +@@ -1386,8 +1406,11 @@ int hci_setup_ext_adv_instance_sync(stru + connectable = (flags & MGMT_ADV_FLAG_CONNECTABLE) || + mgmt_get_connectable(hdev); + +- if (!is_advertising_allowed(hdev, connectable)) ++ if (!is_advertising_allowed(hdev, connectable)) { ++ if (instance) ++ hci_dev_unlock(hdev); + return -EPERM; ++ } + + /* Set require_privacy to true only when non-connectable + * advertising is used and it is not periodic. +@@ -1398,8 +1421,11 @@ int hci_setup_ext_adv_instance_sync(stru + err = hci_get_random_address(hdev, require_privacy, + adv_use_rpa(hdev, flags), adv, + &own_addr_type, &random_addr); +- if (err < 0) ++ if (err < 0) { ++ if (instance) ++ hci_dev_unlock(hdev); + return err; ++ } + + memset(&cp, 0, sizeof(cp)); + +@@ -1450,6 +1476,9 @@ int hci_setup_ext_adv_instance_sync(stru + cp.channel_map = hdev->le_adv_channel_map; + cp.handle = adv ? adv->handle : instance; + ++ if (instance) ++ hci_dev_unlock(hdev); ++ + if (flags & MGMT_ADV_FLAG_SEC_2M) { + cp.primary_phy = HCI_ADV_PHY_1M; + cp.secondary_phy = HCI_ADV_PHY_2M; +@@ -1462,12 +1491,12 @@ int hci_setup_ext_adv_instance_sync(stru + cp.secondary_phy = HCI_ADV_PHY_1M; + } + +- err = hci_set_ext_adv_params_sync(hdev, adv, &cp, &rp); ++ err = hci_set_ext_adv_params_sync(hdev, instance, &cp, &rp); + if (err) + return err; + + /* Update adv data as tx power is known now */ +- err = hci_set_ext_adv_data_sync(hdev, cp.handle); ++ err = hci_set_ext_adv_data_sync(hdev, instance); + if (err) + return err; + +@@ -1475,9 +1504,14 @@ int hci_setup_ext_adv_instance_sync(stru + own_addr_type == ADDR_LE_DEV_RANDOM_RESOLVED) && + bacmp(&random_addr, BDADDR_ANY)) { + /* Check if random address need to be updated */ +- if (adv) { +- if (!bacmp(&random_addr, &adv->random_addr)) ++ if (instance) { ++ hci_dev_lock(hdev); ++ adv = hci_find_adv_instance(hdev, instance); ++ if (!adv || !bacmp(&random_addr, &adv->random_addr)) { ++ hci_dev_unlock(hdev); + return 0; ++ } ++ hci_dev_unlock(hdev); + } else { + if (!bacmp(&random_addr, &hdev->random_addr)) + return 0; +@@ -1499,9 +1533,13 @@ static int hci_set_ext_scan_rsp_data_syn + int err; + + if (instance) { ++ hci_dev_lock(hdev); ++ + adv = hci_find_adv_instance(hdev, instance); +- if (!adv || !adv->scan_rsp_changed) ++ if (!adv || !adv->scan_rsp_changed) { ++ hci_dev_unlock(hdev); + return 0; ++ } + } + + len = eir_create_scan_rsp(hdev, instance, pdu->data); +@@ -1511,15 +1549,27 @@ static int hci_set_ext_scan_rsp_data_syn + pdu->operation = LE_SET_ADV_DATA_OP_COMPLETE; + pdu->frag_pref = LE_SET_ADV_DATA_NO_FRAG; + ++ if (adv) { ++ adv->scan_rsp_changed = false; ++ hci_dev_unlock(hdev); ++ } ++ + err = __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_EXT_SCAN_RSP_DATA, + struct_size(pdu, data, len), pdu, + HCI_CMD_TIMEOUT); +- if (err) ++ if (err) { ++ if (instance) { ++ hci_dev_lock(hdev); ++ adv = hci_find_adv_instance(hdev, instance); ++ if (adv) ++ adv->scan_rsp_changed = true; ++ hci_dev_unlock(hdev); ++ } ++ + return err; ++ } + +- if (adv) { +- adv->scan_rsp_changed = false; +- } else { ++ if (!instance) { + memcpy(hdev->scan_rsp_data, pdu->data, len); + hdev->scan_rsp_data_len = len; + } +@@ -1534,8 +1584,14 @@ static int __hci_set_scan_rsp_data_sync( + + memset(&cp, 0, sizeof(cp)); + ++ if (instance) ++ hci_dev_lock(hdev); ++ + len = eir_create_scan_rsp(hdev, instance, cp.data); + ++ if (instance) ++ hci_dev_unlock(hdev); ++ + if (hdev->scan_rsp_data_len == len && + !memcmp(cp.data, hdev->scan_rsp_data, len)) + return 0; +@@ -1670,9 +1726,13 @@ static int hci_set_per_adv_data_sync(str + struct adv_info *adv = NULL; + + if (instance) { ++ hci_dev_lock(hdev); ++ + adv = hci_find_adv_instance(hdev, instance); +- if (!adv || !adv->periodic) ++ if (!adv || !adv->periodic) { ++ hci_dev_unlock(hdev); + return 0; ++ } + } + + len = eir_create_per_adv_data(hdev, instance, pdu->data); +@@ -1681,6 +1741,9 @@ static int hci_set_per_adv_data_sync(str + pdu->handle = adv ? adv->handle : instance; + pdu->operation = LE_SET_ADV_DATA_OP_COMPLETE; + ++ if (adv) ++ hci_dev_unlock(hdev); ++ + return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_PER_ADV_DATA, + struct_size(pdu, data, len), pdu, + HCI_CMD_TIMEOUT); +@@ -6449,7 +6512,7 @@ static int hci_le_ext_directed_advertisi + if (err) + return err; + +- err = hci_set_ext_adv_params_sync(hdev, NULL, &cp, &rp); ++ err = hci_set_ext_adv_params_sync(hdev, 0, &cp, &rp); + if (err) + return err; + diff --git a/queue-6.18/bluetooth-hidp-reject-frames-without-a-transaction-header.patch b/queue-6.18/bluetooth-hidp-reject-frames-without-a-transaction-header.patch new file mode 100644 index 0000000000..bca8802abd --- /dev/null +++ b/queue-6.18/bluetooth-hidp-reject-frames-without-a-transaction-header.patch @@ -0,0 +1,105 @@ +From 47778d2c2087b5d192398f6fddf692d16a5431cf Mon Sep 17 00:00:00 2001 +From: Sangho Lee +Date: Thu, 23 Jul 2026 12:28:06 +0900 +Subject: Bluetooth: HIDP: reject frames without a transaction header + +From: Sangho Lee + +commit 47778d2c2087b5d192398f6fddf692d16a5431cf upstream. + +hidp_recv_ctrl_frame() and hidp_recv_intr_frame() read skb->data[0] +before checking that the L2CAP SDU contains a transaction header. A +connected HIDP peer can send an empty basic-mode SDU and make both paths +use an uninitialized byte from skb tailroom. + +KMSAN reports the use in hidp_session_run(), with the uninitialized value +originating in __alloc_skb() through vhci_write(). The control path +produces two reports and the interrupt path produces one. + +The byte can also be controlled by a malformed lower-layer packet. If an +HCI ACL packet contains an L2CAP PDU with a declared zero-length payload +followed by an extra 0x15 byte, l2cap_recv_acldata() reduces skb->len to +the declared PDU length before dispatch. The current HIDP path nevertheless +consumes the extra byte as HIDP_TRANS_HID_CONTROL | +HIDP_CTRL_VIRTUAL_CABLE_UNPLUG and terminates the HIDP session. With this +change, the same packet is discarded and a subsequent feature report +request succeeds. + +Pull the transaction header with skb_pull_data() and discard frames that +do not contain it. + +Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") +Cc: stable@vger.kernel.org +Signed-off-by: Sangho Lee +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Greg Kroah-Hartman +--- + net/bluetooth/hidp/core.c | 25 +++++++++++++++---------- + 1 file changed, 15 insertions(+), 10 deletions(-) + +--- a/net/bluetooth/hidp/core.c ++++ b/net/bluetooth/hidp/core.c +@@ -563,16 +563,18 @@ static int hidp_process_data(struct hidp + static void hidp_recv_ctrl_frame(struct hidp_session *session, + struct sk_buff *skb) + { +- unsigned char hdr, type, param; ++ unsigned char type, param; ++ u8 *hdr; + int free_skb = 1; + + BT_DBG("session %p skb %p len %u", session, skb, skb->len); + +- hdr = skb->data[0]; +- skb_pull(skb, 1); ++ hdr = skb_pull_data(skb, 1); ++ if (!hdr) ++ goto free; + +- type = hdr & HIDP_HEADER_TRANS_MASK; +- param = hdr & HIDP_HEADER_PARAM_MASK; ++ type = *hdr & HIDP_HEADER_TRANS_MASK; ++ param = *hdr & HIDP_HEADER_PARAM_MASK; + + switch (type) { + case HIDP_TRANS_HANDSHAKE: +@@ -593,6 +595,7 @@ static void hidp_recv_ctrl_frame(struct + break; + } + ++free: + if (free_skb) + kfree_skb(skb); + } +@@ -600,14 +603,15 @@ static void hidp_recv_ctrl_frame(struct + static void hidp_recv_intr_frame(struct hidp_session *session, + struct sk_buff *skb) + { +- unsigned char hdr; ++ u8 *hdr; + + BT_DBG("session %p skb %p len %u", session, skb, skb->len); + +- hdr = skb->data[0]; +- skb_pull(skb, 1); ++ hdr = skb_pull_data(skb, 1); ++ if (!hdr) ++ goto free; + +- if (hdr == (HIDP_TRANS_DATA | HIDP_DATA_RTYPE_INPUT)) { ++ if (*hdr == (HIDP_TRANS_DATA | HIDP_DATA_RTYPE_INPUT)) { + hidp_set_timer(session); + + if (session->input) +@@ -619,9 +623,10 @@ static void hidp_recv_intr_frame(struct + BT_DBG("report len %d", skb->len); + } + } else { +- BT_DBG("Unsupported protocol header 0x%02x", hdr); ++ BT_DBG("Unsupported protocol header 0x%02x", *hdr); + } + ++free: + kfree_skb(skb); + } + diff --git a/queue-6.18/bluetooth-hidp-validate-numbered-report-payloads.patch b/queue-6.18/bluetooth-hidp-validate-numbered-report-payloads.patch new file mode 100644 index 0000000000..afb4c57fcb --- /dev/null +++ b/queue-6.18/bluetooth-hidp-validate-numbered-report-payloads.patch @@ -0,0 +1,53 @@ +From 34f53d27b81a16a02828c8fdfa4e02badc326f17 Mon Sep 17 00:00:00 2001 +From: Sangho Lee +Date: Thu, 23 Jul 2026 12:28:07 +0900 +Subject: Bluetooth: HIDP: validate numbered report payloads + +From: Sangho Lee + +commit 34f53d27b81a16a02828c8fdfa4e02badc326f17 upstream. + +When hidp_get_raw_report() waits for a numbered report, +hidp_process_data() compares the expected report number with skb->data[0]. +A connected HIDP peer can reply with only a DATA transaction header, +leaving the skb empty after the header is removed. + +KMSAN reports an uninitialized-value use in hidp_session_run(), with the +value originating in __alloc_skb() through vhci_write(). The transaction +header checks remove the empty-frame reports, but this report remains until +the payload check is added. + +The comparison can also consume a peer-controlled byte beyond the declared +L2CAP PDU. A DATA | FEATURE response followed by an extra 0x01 byte made +the current code accept that byte as report ID 1 and complete +HIDIOCGFEATURE with a zero-byte result. With this change the malformed +response is rejected with -EIO, while a subsequent valid response still +succeeds. + +Require a payload byte before comparing a numbered report ID. Unnumbered +reports continue to accept an empty payload. + +Fixes: 0ff1731a1ae5 ("HID: bt: Add support for hidraw HIDIOCGFEATURE and HIDIOCSFEATURE") +Cc: stable@vger.kernel.org +Signed-off-by: Sangho Lee +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Greg Kroah-Hartman +--- + net/bluetooth/hidp/core.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +--- a/net/bluetooth/hidp/core.c ++++ b/net/bluetooth/hidp/core.c +@@ -546,9 +546,10 @@ static int hidp_process_data(struct hidp + } + + if (test_bit(HIDP_WAITING_FOR_RETURN, &session->flags) && +- param == session->waiting_report_type) { ++ param == session->waiting_report_type) { + if (session->waiting_report_number < 0 || +- session->waiting_report_number == skb->data[0]) { ++ (skb->len && ++ session->waiting_report_number == skb->data[0])) { + /* hidp_get_raw_report() is waiting on this report. */ + session->report_return = skb; + done_with_skb = 0; diff --git a/queue-6.18/bluetooth-mgmt-fix-pending-command-uaf-in-eir-updates.patch b/queue-6.18/bluetooth-mgmt-fix-pending-command-uaf-in-eir-updates.patch new file mode 100644 index 0000000000..f5d8bb0e68 --- /dev/null +++ b/queue-6.18/bluetooth-mgmt-fix-pending-command-uaf-in-eir-updates.patch @@ -0,0 +1,91 @@ +From 8f2f62855a41d1730fb9e8122912bd2c8d6bed5d Mon Sep 17 00:00:00 2001 +From: Zihan Xi +Date: Fri, 24 Jul 2026 00:43:46 +0800 +Subject: Bluetooth: mgmt: fix pending command UAF in EIR updates + +From: Zihan Xi + +commit 8f2f62855a41d1730fb9e8122912bd2c8d6bed5d upstream. + +MGMT_OP_SET_LOCAL_NAME is handled asynchronously on powered controllers +and can run set_name_sync(). When the controller is BR/EDR capable, +set_name_sync() updates the local name and then rebuilds EIR data through +eir_create(). The EIR builder walks hdev->uuids, but the UUID list can +be changed and entries can be freed by MGMT_OP_ADD_UUID and +MGMT_OP_REMOVE_UUID. + +pending_eir_or_class() is meant to serialize management commands that +can change EIR or the class of device, but it did not include +MGMT_OP_SET_LOCAL_NAME. In addition, it walked hdev->mgmt_pending +without hdev->mgmt_pending_lock even though pending commands are added +and removed under that mutex. A racing command completion can therefore +remove and free a pending command while pending_eir_or_class() is still +inspecting it, leading to a use-after-free in the pending-command list or +allowing a local name update to rebuild EIR while UUID entries are being +removed. + +Take hdev->mgmt_pending_lock while scanning hdev->mgmt_pending and treat +MGMT_OP_SET_LOCAL_NAME as an EIR/class-affecting pending command on the +powered asynchronous path. Check for a conflicting pending command before +copying the new short name so a rejected SET_LOCAL_NAME request does not +modify hdev->short_name. + +Fixes: 6fe26f694c82 ("Bluetooth: MGMT: Protect mgmt_pending list with its own lock") +Cc: stable@vger.kernel.org +Reported-by: Vega +Assisted-by: Codex:gpt-5.4 +Signed-off-by: Zihan Xi +Signed-off-by: Ren Wei +Reported-by: Vega +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Greg Kroah-Hartman +--- + net/bluetooth/mgmt.c | 20 ++++++++++++++++++-- + 1 file changed, 18 insertions(+), 2 deletions(-) + +--- a/net/bluetooth/mgmt.c ++++ b/net/bluetooth/mgmt.c +@@ -2699,18 +2699,28 @@ static int mgmt_hci_cmd_sync(struct sock + static bool pending_eir_or_class(struct hci_dev *hdev) + { + struct mgmt_pending_cmd *cmd; ++ bool pending = false; ++ ++ mutex_lock(&hdev->mgmt_pending_lock); + + list_for_each_entry(cmd, &hdev->mgmt_pending, list) { + switch (cmd->opcode) { + case MGMT_OP_ADD_UUID: + case MGMT_OP_REMOVE_UUID: + case MGMT_OP_SET_DEV_CLASS: ++ case MGMT_OP_SET_LOCAL_NAME: + case MGMT_OP_SET_POWERED: +- return true; ++ pending = true; ++ break; + } ++ ++ if (pending) ++ break; + } + +- return false; ++ mutex_unlock(&hdev->mgmt_pending_lock); ++ ++ return pending; + } + + static const u8 bluetooth_base_uuid[] = { +@@ -4046,6 +4056,12 @@ static int set_local_name(struct sock *s + goto failed; + } + ++ if (hdev_is_powered(hdev) && pending_eir_or_class(hdev)) { ++ err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, ++ MGMT_STATUS_BUSY); ++ goto failed; ++ } ++ + memcpy(hdev->short_name, cp->short_name, sizeof(hdev->short_name)); + + if (!hdev_is_powered(hdev)) { diff --git a/queue-6.18/bluetooth-mgmt-fix-uaf-in-pair-command-cancellation.patch b/queue-6.18/bluetooth-mgmt-fix-uaf-in-pair-command-cancellation.patch new file mode 100644 index 0000000000..60c2b01675 --- /dev/null +++ b/queue-6.18/bluetooth-mgmt-fix-uaf-in-pair-command-cancellation.patch @@ -0,0 +1,198 @@ +From d0a7b48ad0921bd88effaee10bf970ab1d5d0ddd Mon Sep 17 00:00:00 2001 +From: Zihan Xi +Date: Tue, 21 Jul 2026 22:36:07 +0800 +Subject: Bluetooth: mgmt: fix UAF in pair command cancellation + +From: Zihan Xi + +commit d0a7b48ad0921bd88effaee10bf970ab1d5d0ddd upstream. + +The pairing completion and authentication failure callbacks look up the +pending MGMT_OP_PAIR_DEVICE command by walking hdev->mgmt_pending. The +lookup returned a command that was still linked on the shared pending list, +without keeping mgmt_pending_lock held for the later dereference and +removal. + +A concurrent MGMT_OP_CANCEL_PAIR_DEVICE request can remove and free the +same pending command before the callback uses it. The reverse race is also +possible when cancel_pair_device() gets a command from pending_find() and a +callback removes it before the cancel path dereferences it. This can lead +to a use-after-free and a second list_del(). + +Make the pairing lookup helpers transfer ownership of the pending command +by removing it from hdev->mgmt_pending while holding mgmt_pending_lock. +The callbacks and cancel path then complete the command and free it +directly, so racing paths cannot find or free the same command again. Take +a temporary hci_conn reference in cancel_pair_device() because the command +completion drops the reference stored in the pending command. + +Fixes: e9a416b5ce0c ("Bluetooth: Add mgmt_pair_device command") +Cc: stable@vger.kernel.org +Reported-by: Vega +Assisted-by: Codex:gpt-5.4 +Signed-off-by: Zihan Xi +Reviewed-by: Ren Wei +Reported-by: Vega +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Greg Kroah-Hartman +--- + net/bluetooth/mgmt.c | 64 ++++++++++++++++++++++++++++++++++++--------------- + 1 file changed, 46 insertions(+), 18 deletions(-) + +--- a/net/bluetooth/mgmt.c ++++ b/net/bluetooth/mgmt.c +@@ -3527,11 +3527,13 @@ static int set_io_capability(struct sock + NULL, 0); + } + +-static struct mgmt_pending_cmd *find_pairing(struct hci_conn *conn) ++static struct mgmt_pending_cmd *remove_pairing(struct hci_conn *conn) + { + struct hci_dev *hdev = conn->hdev; + struct mgmt_pending_cmd *cmd; + ++ mutex_lock(&hdev->mgmt_pending_lock); ++ + list_for_each_entry(cmd, &hdev->mgmt_pending, list) { + if (cmd->opcode != MGMT_OP_PAIR_DEVICE) + continue; +@@ -3539,9 +3541,39 @@ static struct mgmt_pending_cmd *find_pai + if (cmd->user_data != conn) + continue; + ++ list_del(&cmd->list); ++ mutex_unlock(&hdev->mgmt_pending_lock); + return cmd; + } + ++ mutex_unlock(&hdev->mgmt_pending_lock); ++ ++ return NULL; ++} ++ ++static struct mgmt_pending_cmd *remove_pairing_by_addr(struct hci_dev *hdev, ++ bdaddr_t *bdaddr) ++{ ++ struct mgmt_pending_cmd *cmd; ++ struct hci_conn *conn; ++ ++ mutex_lock(&hdev->mgmt_pending_lock); ++ ++ list_for_each_entry(cmd, &hdev->mgmt_pending, list) { ++ if (cmd->opcode != MGMT_OP_PAIR_DEVICE) ++ continue; ++ ++ conn = cmd->user_data; ++ if (bacmp(bdaddr, &conn->dst) != 0) ++ continue; ++ ++ list_del(&cmd->list); ++ mutex_unlock(&hdev->mgmt_pending_lock); ++ return cmd; ++ } ++ ++ mutex_unlock(&hdev->mgmt_pending_lock); ++ + return NULL; + } + +@@ -3579,10 +3611,10 @@ void mgmt_smp_complete(struct hci_conn * + u8 status = complete ? MGMT_STATUS_SUCCESS : MGMT_STATUS_FAILED; + struct mgmt_pending_cmd *cmd; + +- cmd = find_pairing(conn); ++ cmd = remove_pairing(conn); + if (cmd) { + cmd->cmd_complete(cmd, status); +- mgmt_pending_remove(cmd); ++ mgmt_pending_free(cmd); + } + } + +@@ -3592,14 +3624,14 @@ static void pairing_complete_cb(struct h + + BT_DBG("status %u", status); + +- cmd = find_pairing(conn); ++ cmd = remove_pairing(conn); + if (!cmd) { + BT_DBG("Unable to find a pending command"); + return; + } + + cmd->cmd_complete(cmd, mgmt_status(status)); +- mgmt_pending_remove(cmd); ++ mgmt_pending_free(cmd); + } + + static void le_pairing_complete_cb(struct hci_conn *conn, u8 status) +@@ -3611,14 +3643,14 @@ static void le_pairing_complete_cb(struc + if (!status) + return; + +- cmd = find_pairing(conn); ++ cmd = remove_pairing(conn); + if (!cmd) { + BT_DBG("Unable to find a pending command"); + return; + } + + cmd->cmd_complete(cmd, mgmt_status(status)); +- mgmt_pending_remove(cmd); ++ mgmt_pending_free(cmd); + } + + static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data, +@@ -3775,23 +3807,17 @@ static int cancel_pair_device(struct soc + goto unlock; + } + +- cmd = pending_find(MGMT_OP_PAIR_DEVICE, hdev); ++ cmd = remove_pairing_by_addr(hdev, &addr->bdaddr); + if (!cmd) { + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE, + MGMT_STATUS_INVALID_PARAMS); + goto unlock; + } + +- conn = cmd->user_data; +- +- if (bacmp(&addr->bdaddr, &conn->dst) != 0) { +- err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE, +- MGMT_STATUS_INVALID_PARAMS); +- goto unlock; +- } ++ conn = hci_conn_get(cmd->user_data); + + cmd->cmd_complete(cmd, MGMT_STATUS_CANCELLED); +- mgmt_pending_remove(cmd); ++ mgmt_pending_free(cmd); + + err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE, 0, + addr, sizeof(*addr)); +@@ -3809,6 +3835,8 @@ static int cancel_pair_device(struct soc + if (conn->conn_reason == CONN_REASON_PAIR_DEVICE) + hci_abort_conn(conn, HCI_ERROR_REMOTE_USER_TERM); + ++ hci_conn_put(conn); ++ + unlock: + hci_dev_unlock(hdev); + return err; +@@ -10135,14 +10163,14 @@ void mgmt_auth_failed(struct hci_conn *c + ev.addr.type = link_to_bdaddr(conn->type, conn->dst_type); + ev.status = status; + +- cmd = find_pairing(conn); ++ cmd = remove_pairing(conn); + + mgmt_event(MGMT_EV_AUTH_FAILED, conn->hdev, &ev, sizeof(ev), + cmd ? cmd->sk : NULL); + + if (cmd) { + cmd->cmd_complete(cmd, status); +- mgmt_pending_remove(cmd); ++ mgmt_pending_free(cmd); + } + } + diff --git a/queue-6.18/bluetooth-sco-give-the-socket-its-own-sco_conn-reference.patch b/queue-6.18/bluetooth-sco-give-the-socket-its-own-sco_conn-reference.patch new file mode 100644 index 0000000000..d2689fa5cb --- /dev/null +++ b/queue-6.18/bluetooth-sco-give-the-socket-its-own-sco_conn-reference.patch @@ -0,0 +1,164 @@ +From abd93c85c8667add738ee82aeab95dd9fc8265a2 Mon Sep 17 00:00:00 2001 +From: Aldo Ariel Panzardo +Date: Sat, 25 Jul 2026 16:52:30 -0300 +Subject: Bluetooth: SCO: give the socket its own sco_conn reference + +From: Aldo Ariel Panzardo + +commit abd93c85c8667add738ee82aeab95dd9fc8265a2 upstream. + +sco_conn_del() drops a reference it does not own. It takes one transient +reference via sco_conn_hold_unless_zero() and releases it with the +sco_conn_put() that follows sco_sock_hold(); the additional put in the +!sk branch releases a second one: + + conn = sco_conn_hold_unless_zero(conn); + ... + sk = sco_sock_hold(conn); + sco_conn_unlock(conn); + sco_conn_put(conn); + + if (!sk) { + sco_conn_put(conn); + return; + } + +When close() races the controller's Disconnection Complete, sco_chan_del() +clears conn->sk and drops the socket's reference while sco_conn_del() is +running. sco_conn_del() then sees sk == NULL, its own put drops the count +to zero and frees the conn, and the second put writes to the freed kref: + + BUG: KASAN: slab-use-after-free in sco_conn_put.part.0+0x1a/0x190 + Write of size 4 at addr ffff8881099dec74 by task kworker/u17:3/413 + Workqueue: hci1 hci_rx_work + Call Trace: + sco_conn_put.part.0+0x1a/0x190 + hci_disconn_complete_evt+0x1ee/0x3e0 + hci_event_packet+0x54a/0x650 + hci_rx_work+0x321/0x3d0 + Allocated by task 413: + sco_conn_add+0x72/0x1a0 + sco_connect_cfm+0x88/0x670 + Freed by task 413: + sco_conn_del.isra.0+0x3f/0xf0 + hci_disconn_complete_evt+0x1ee/0x3e0 + refcount_t: underflow; use-after-free. + +The root cause is that the socket stores the connection without holding a +reference of its own. __sco_chan_add() does: + + sco_pi(sk)->conn = conn; + +so the socket borrows whatever reference its caller happened to hold, and +the callers paper over that with ad-hoc holds and puts. Give the socket a +counted reference instead: __sco_chan_add() takes one and it is released +together with the channel (sco_chan_del()) and in sco_sock_destruct(). +With the socket holding its own reference, sco_conn_del() no longer needs +the extra put and the redundant hold in sco_conn_ready() goes away. + +Making the socket own its reference means the connection is now actually +freed on the error paths of sco_connect() where it used to leak, which in +turn runs sco_conn_free() and its hci_conn_drop(conn->hcon). To keep the +hci_conn accounting balanced, make that ownership explicit as well: +sco_conn_add() consumes one hci_conn reference and the sco_conn owns it for +its lifetime. sco_connect() hands over the reference returned by +hci_connect_sco() and no longer drops it on the error paths; +sco_connect_cfm(), which is not given a reference, takes one with +hci_conn_hold() before handing it to sco_conn_add() (and drops it again if +the allocation fails); and the explicit hci_conn_hold() in sco_conn_ready() +is removed. Every reference then has a single, clear owner. + +Fixes: e6720779ae61 ("Bluetooth: SCO: Use kref to track lifetime of sco_conn") +Cc: stable@vger.kernel.org +Suggested-by: Pauli Virtanen +Signed-off-by: Aldo Ariel Panzardo +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Greg Kroah-Hartman +--- + net/bluetooth/sco.c | 22 +++++++++++++--------- + 1 file changed, 13 insertions(+), 9 deletions(-) + +--- a/net/bluetooth/sco.c ++++ b/net/bluetooth/sco.c +@@ -190,6 +190,9 @@ static void sco_sock_clear_timer(struct + } + + /* ---- SCO connections ---- */ ++/* Consumes a reference on @hcon, which the returned sco_conn owns until it is ++ * freed. On failure (NULL return) the reference is left for the caller to drop. ++ */ + static struct sco_conn *sco_conn_add(struct hci_conn *hcon) + { + struct sco_conn *conn = hcon->sco_data; +@@ -200,6 +203,9 @@ static struct sco_conn *sco_conn_add(str + sco_conn_lock(conn); + conn->hcon = hcon; + sco_conn_unlock(conn); ++ } else { ++ /* conn already owns a reference on hcon */ ++ hci_conn_drop(hcon); + } + return conn; + } +@@ -267,10 +273,8 @@ static void sco_conn_del(struct hci_conn + sco_conn_unlock(conn); + sco_conn_put(conn); + +- if (!sk) { +- sco_conn_put(conn); ++ if (!sk) + return; +- } + + /* Kill socket */ + lock_sock(sk); +@@ -285,7 +289,7 @@ static void __sco_chan_add(struct sco_co + { + BT_DBG("conn %p", conn); + +- sco_pi(sk)->conn = conn; ++ sco_pi(sk)->conn = sco_conn_hold(conn); + conn->sk = sk; + + if (parent) +@@ -368,15 +372,15 @@ static int sco_connect(struct sock *sk) + */ + if (sk->sk_state != BT_OPEN && sk->sk_state != BT_BOUND) { + release_sock(sk); +- hci_conn_drop(hcon); ++ sco_conn_put(conn); + err = -EBADFD; + goto unlock; + } + + err = sco_chan_add(conn, sk, NULL); ++ sco_conn_put(conn); + if (err) { + release_sock(sk); +- hci_conn_drop(hcon); + goto unlock; + } + +@@ -1452,8 +1456,6 @@ static void sco_conn_ready(struct sco_co + bacpy(&sco_pi(sk)->src, &conn->hcon->src); + bacpy(&sco_pi(sk)->dst, &conn->hcon->dst); + +- sco_conn_hold(conn); +- hci_conn_hold(conn->hcon); + __sco_chan_add(conn, sk, parent); + + if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(parent)->flags)) +@@ -1509,10 +1511,12 @@ static void sco_connect_cfm(struct hci_c + if (!status) { + struct sco_conn *conn; + +- conn = sco_conn_add(hcon); ++ conn = sco_conn_add(hci_conn_hold(hcon)); + if (conn) { + sco_conn_ready(conn); + sco_conn_put(conn); ++ } else { ++ hci_conn_drop(hcon); + } + } else + sco_conn_del(hcon, bt_to_errno(status)); diff --git a/queue-6.18/dibs-fix-use-after-free-of-dmb_node-in-loopback-attach-detach-unregister.patch b/queue-6.18/dibs-fix-use-after-free-of-dmb_node-in-loopback-attach-detach-unregister.patch new file mode 100644 index 0000000000..e83ed1d5ac --- /dev/null +++ b/queue-6.18/dibs-fix-use-after-free-of-dmb_node-in-loopback-attach-detach-unregister.patch @@ -0,0 +1,180 @@ +From a10ea943356b9d70c5616a0a06f6fa97cfdaccb1 Mon Sep 17 00:00:00 2001 +From: Hidayath Khan +Date: Mon, 27 Jul 2026 11:35:30 +0200 +Subject: dibs: fix use-after-free of dmb_node in loopback attach/detach/unregister + +From: Hidayath Khan + +commit a10ea943356b9d70c5616a0a06f6fa97cfdaccb1 upstream. + +dibs_lo_attach_dmb(), dibs_lo_detach_dmb() and dibs_lo_unregister_dmb() +look up the dmb_node under dmb_ht_lock, drop the lock and only then +operate on the node's refcount. Nothing keeps the node alive across +that window: __dibs_lo_unregister_dmb() removes the node from the hash +table under the write lock and immediately frees it. + +A concurrent final put can therefore free the node between the lookup +and the refcount operation: + +CPU0 (attach) CPU1 (owner unregisters) + +read_lock_bh(&dmb_ht_lock) +find dmb_node (refcnt == 1) +read_unlock_bh(&dmb_ht_lock) + refcount_dec_and_test() 1 -> 0 + write_lock_bh(&dmb_ht_lock) + hash_del(&dmb_node->list) + write_unlock_bh(&dmb_ht_lock) + kfree(dmb_node) +refcount_inc_not_zero(&dmb_node->refcnt) <-- use-after-free + +The same window exists for the refcount_dec_and_test() calls in the +detach and unregister paths. + +Close the race structurally by making hash table membership and the +refcount transitions atomic with respect to each other: + +- Perform the final refcount_dec_and_test() and hash_del() in a single + dmb_ht_lock write-side critical section, in both the unregister and + the detach path. Freeing the node still happens after the lock is + dropped, which is safe because a node whose refcount reached zero has + left the hash table and can no longer be found. + +- This establishes the invariant that any node found in the hash table + holds at least one reference, and that the final reference can only + be dropped under the write lock. dibs_lo_attach_dmb() can thus take + its reference with a plain refcount_inc() while still holding the + read lock; refcount_inc_not_zero() is no longer needed. + +__dibs_lo_unregister_dmb() no longer touches the hash table and is +renamed to dibs_lo_free_dmb() accordingly. + +Note: commit cc21191b584c ("dibs: Move data path to dibs layer") moved +the code to its current location; the race was introduced earlier by +commit c3a910f2380f ("net/smc: implement DMB-merged operations of +loopback-ism"). + +Tested SMC-D via ISM and dibs loopback. + +Cc: stable@vger.kernel.org +Fixes: c3a910f2380f ("net/smc: implement DMB-merged operations of loopback-ism") +Reported-by: Rahul Chandelkar +Signed-off-by: Hidayath Khan +Reviewed-by: Alexandra Winter +Link: https://patch.msgid.link/20260727093530.968834-1-hidayath@linux.ibm.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Greg Kroah-Hartman +--- + drivers/dibs/dibs_loopback.c | 47 +++++++++++++++++++++---------------------- + 1 file changed, 24 insertions(+), 23 deletions(-) + +--- a/drivers/dibs/dibs_loopback.c ++++ b/drivers/dibs/dibs_loopback.c +@@ -118,14 +118,9 @@ err_bit: + return rc; + } + +-static void __dibs_lo_unregister_dmb(struct dibs_lo_dev *ldev, +- struct dibs_lo_dmb_node *dmb_node) ++static void dibs_lo_free_dmb(struct dibs_lo_dev *ldev, ++ struct dibs_lo_dmb_node *dmb_node) + { +- /* remove dmb from hash table */ +- write_lock_bh(&ldev->dmb_ht_lock); +- hash_del(&dmb_node->list); +- write_unlock_bh(&ldev->dmb_ht_lock); +- + clear_bit(dmb_node->sba_idx, ldev->sba_idx_mask); + folio_put(virt_to_folio(dmb_node->cpu_addr)); + kfree(dmb_node); +@@ -139,27 +134,33 @@ static int dibs_lo_unregister_dmb(struct + struct dibs_lo_dmb_node *dmb_node = NULL, *tmp_node; + struct dibs_lo_dev *ldev; + unsigned long flags; ++ bool last; + + ldev = dibs->drv_priv; + + /* find dmb from hash table */ +- read_lock_bh(&ldev->dmb_ht_lock); ++ write_lock_bh(&ldev->dmb_ht_lock); + hash_for_each_possible(ldev->dmb_ht, tmp_node, list, dmb->dmb_tok) { + if (tmp_node->token == dmb->dmb_tok) { + dmb_node = tmp_node; + break; + } + } +- read_unlock_bh(&ldev->dmb_ht_lock); +- if (!dmb_node) ++ if (!dmb_node) { ++ write_unlock_bh(&ldev->dmb_ht_lock); + return -EINVAL; ++ } ++ last = refcount_dec_and_test(&dmb_node->refcnt); ++ if (last) ++ hash_del(&dmb_node->list); ++ write_unlock_bh(&ldev->dmb_ht_lock); + +- if (refcount_dec_and_test(&dmb_node->refcnt)) { ++ if (last) { + spin_lock_irqsave(&dibs->lock, flags); + dibs->dmb_clientid_arr[dmb_node->sba_idx] = NO_DIBS_CLIENT; + spin_unlock_irqrestore(&dibs->lock, flags); + +- __dibs_lo_unregister_dmb(ldev, dmb_node); ++ dibs_lo_free_dmb(ldev, dmb_node); + } + return 0; + } +@@ -188,14 +189,9 @@ static int dibs_lo_attach_dmb(struct dib + read_unlock_bh(&ldev->dmb_ht_lock); + return -EINVAL; + } ++ refcount_inc(&dmb_node->refcnt); + read_unlock_bh(&ldev->dmb_ht_lock); + +- if (!refcount_inc_not_zero(&dmb_node->refcnt)) +- /* the dmb is being unregistered, but has +- * not been removed from the hash table. +- */ +- return -EINVAL; +- + /* provide dmb information */ + dmb->idx = dmb_node->sba_idx; + dmb->dmb_tok = dmb_node->token; +@@ -209,11 +205,12 @@ static int dibs_lo_detach_dmb(struct dib + { + struct dibs_lo_dmb_node *dmb_node = NULL, *tmp_node; + struct dibs_lo_dev *ldev; ++ bool last; + + ldev = dibs->drv_priv; + + /* find dmb_node according to dmb->dmb_tok */ +- read_lock_bh(&ldev->dmb_ht_lock); ++ write_lock_bh(&ldev->dmb_ht_lock); + hash_for_each_possible(ldev->dmb_ht, tmp_node, list, token) { + if (tmp_node->token == token) { + dmb_node = tmp_node; +@@ -221,13 +218,17 @@ static int dibs_lo_detach_dmb(struct dib + } + } + if (!dmb_node) { +- read_unlock_bh(&ldev->dmb_ht_lock); ++ write_unlock_bh(&ldev->dmb_ht_lock); + return -EINVAL; + } +- read_unlock_bh(&ldev->dmb_ht_lock); ++ last = refcount_dec_and_test(&dmb_node->refcnt); ++ if (last) ++ hash_del(&dmb_node->list); ++ write_unlock_bh(&ldev->dmb_ht_lock); ++ ++ if (last) ++ dibs_lo_free_dmb(ldev, dmb_node); + +- if (refcount_dec_and_test(&dmb_node->refcnt)) +- __dibs_lo_unregister_dmb(ldev, dmb_node); + return 0; + } + diff --git a/queue-6.18/fortify-disable-wstringop-overread-in-tests.patch b/queue-6.18/fortify-disable-wstringop-overread-in-tests.patch new file mode 100644 index 0000000000..1eb405db45 --- /dev/null +++ b/queue-6.18/fortify-disable-wstringop-overread-in-tests.patch @@ -0,0 +1,55 @@ +From c1f3e770eec26d6f96dd6d2ea30555ba7c09a244 Mon Sep 17 00:00:00 2001 +From: Nathan Chancellor +Date: Tue, 23 Jun 2026 13:23:46 -0700 +Subject: fortify: Disable -Wstringop-overread in tests + +From: Nathan Chancellor + +commit c1f3e770eec26d6f96dd6d2ea30555ba7c09a244 upstream. + +clang recently added support for -Wstringop-overread [1], which is on by +default like -Wfortify-source. This breaks the usage of -Werror in the +fortify tests, resulting in the following false positive warnings in the +kernel build: + + warning: unsafe memcmp() usage lacked '__read_overflow2' warning in lib/test_fortify/read_overflow2-memcmp.c + warning: unsafe memcmp() usage lacked '__read_overflow' warning in lib/test_fortify/read_overflow-memcmp.c + warning: unsafe memchr() usage lacked '__read_overflow' warning in lib/test_fortify/read_overflow-memchr.c + +Examining the fortify test logs shows a warning like the following in +each of the failed logs: + + In file included from lib/test_fortify/read_overflow2-memcmp.c:5: + lib/test_fortify/test_fortify.h:34:2: error: 'memcmp' reading 17 bytes from a region of size 16 [-Werror,-Wstringop-overread] + 34 | TEST; + | ^ + lib/test_fortify/read_overflow2-memcmp.c:3:2: note: expanded from macro 'TEST' + 3 | memcmp(large, small, sizeof(small) + 1) + | ^ + 1 error generated. + +Disable -Wstringop-overread for the fortify tests, as it defeats the +purpose of testing the Linux specific implementation of fortify, like +-Wfortify-source. + +Cc: stable@vger.kernel.org +Closes: https://github.com/ClangBuiltLinux/linux/issues/2168 +Link: https://github.com/llvm/llvm-project/commit/86f2e71cb8d165b59ad31a442b2391e23826133e [1] +Signed-off-by: Nathan Chancellor +Link: https://patch.msgid.link/20260623-fix-test_fortify-for-clang-stringop-overread-v1-1-15ee8342a953@kernel.org +Signed-off-by: Kees Cook +Signed-off-by: Greg Kroah-Hartman +--- + lib/test_fortify/Makefile | 1 + + 1 file changed, 1 insertion(+) + +--- a/lib/test_fortify/Makefile ++++ b/lib/test_fortify/Makefile +@@ -1,6 +1,7 @@ + # SPDX-License-Identifier: GPL-2.0 + + ccflags-y := $(call cc-disable-warning,fortify-source) ++ccflags-y += $(call cc-disable-warning,stringop-overread) + + quiet_cmd_test_fortify = TEST $@ + cmd_test_fortify = $(CONFIG_SHELL) $(src)/test_fortify.sh \ diff --git a/queue-6.18/fs-proc-task_mmu-fix-pagemap_scan-written-state-for-pmd-holes.patch b/queue-6.18/fs-proc-task_mmu-fix-pagemap_scan-written-state-for-pmd-holes.patch new file mode 100644 index 0000000000..a46bfc1893 --- /dev/null +++ b/queue-6.18/fs-proc-task_mmu-fix-pagemap_scan-written-state-for-pmd-holes.patch @@ -0,0 +1,99 @@ +From 40de8160ca7f67d14619ee0351ce5d68fc4a237a Mon Sep 17 00:00:00 2001 +From: "Kiryl Shutsemau (Meta)" +Date: Wed, 15 Jul 2026 15:42:33 +0100 +Subject: fs/proc/task_mmu: fix PAGEMAP_SCAN written state for PMD holes + +From: Kiryl Shutsemau (Meta) + +commit 40de8160ca7f67d14619ee0351ce5d68fc4a237a upstream. + +PAGEMAP_SCAN reports an unpopulated PTE in a uffd-wp VMA as written, but a +range with no page table at all -- a PMD hole -- is skipped: +pagemap_scan_pte_hole() tests p->cur_vma_category, which never carries +PAGE_IS_WRITTEN, so the hole is neither reported nor (under +PM_SCAN_WP_MATCHING) armed. + +In a uffd-wp VMA, WP_UNPOPULATED installs uffd-wp markers when protecting +a range, allocating page tables as needed, so an unpopulated slot is +treated as written -- see the pte_none() handling in +pagemap_page_category(). A missing marker therefore means the range was +zapped, e.g. via MADV_DONTNEED. This applies to anon and shmem VMAs. + +An anonymous THP is write-protected in place as a huge PMD, so a full-PMD +MADV_DONTNEED clears it to pmd_none -- a hole with no page table -- and +pagemap_scan_pte_hole() misses it. For a MAP_PRIVATE|MAP_ANON mapping +MADV_DONTNEED has fill-with-zeros semantics, so a write-tracking +checkpoint/migration tool (e.g. CRIU) treats the range as unchanged and +keeps its previous contents; after restore or live migration the process +reads stale data instead of zeroes -- data corruption. + +Report a hole in a non-hugetlb uffd-wp VMA as written, matching the +pte_none handling in pagemap_page_category(); the existing +PM_SCAN_WP_MATCHING path then arms it via uffd_wp_range(). + +hugetlb is excluded: pagemap_hugetlb_category() reports an empty hugetlb +entry (huge_pte_none) as not-written, unlike pagemap_page_category(), +which reports pte_none as written. pagemap_scan_pte_hole() fires for a +hugetlb slot only when it has no page table; keeping that not-written +matches how an allocated-but-empty hugetlb entry reads, so the hole and +the empty-entry cases agree within the VMA. + +Link: https://lore.kernel.org/20260715144234.442721-2-kirill@shutemov.name +Fixes: 2bad466cc9d9 ("mm/uffd: UFFD_FEATURE_WP_UNPOPULATED") +Signed-off-by: Kiryl Shutsemau +Reported-by: Sashiko AI review +Closes: https://sashiko.dev/#/patchset/20260707151349.92143-1-kirill@shutemov.name +Tested-by: Muhammad Usama Anjum +Acked-by: David Hildenbrand (Arm) +Cc: Peter Xu +Cc: Jann Horn +Cc: Liam R. Howlett +Cc: Lorenzo Stoakes +Cc: Michal Hocko +Cc: Mike Rapoport +Cc: Pedro Falcato +Cc: Shuah Khan +Cc: Suren Baghdasaryan +Cc: Vlastimil Babka +Cc: Zenghui Yu +Assisted-by: Claude:claude-fable-5 +Cc: +Signed-off-by: Andrew Morton +Signed-off-by: Greg Kroah-Hartman +--- + fs/proc/task_mmu.c | 20 ++++++++++++++++++-- + 1 file changed, 18 insertions(+), 2 deletions(-) + +--- a/fs/proc/task_mmu.c ++++ b/fs/proc/task_mmu.c +@@ -2918,12 +2918,28 @@ static int pagemap_scan_pte_hole(unsigne + { + struct pagemap_scan_private *p = walk->private; + struct vm_area_struct *vma = walk->vma; ++ unsigned long categories; + int ret, err; + +- if (!vma || !pagemap_scan_is_interesting_page(p->cur_vma_category, p)) ++ if (!vma) + return 0; + +- ret = pagemap_scan_output(p->cur_vma_category, p, addr, &end); ++ /* ++ * In a uffd-wp VMA an unpopulated range is treated as written: ++ * uffd-wp registration populates page tables and installs markers ++ * with WP_UNPOPULATED, so a missing marker means the range was ++ * zapped. See the pte_none() handling in pagemap_page_category(). ++ * ++ * hugetlb differs, see pagemap_hugetlb_category(). ++ */ ++ categories = p->cur_vma_category; ++ if (userfaultfd_wp(vma) && !is_vm_hugetlb_page(vma)) ++ categories |= PAGE_IS_WRITTEN; ++ ++ if (!pagemap_scan_is_interesting_page(categories, p)) ++ return 0; ++ ++ ret = pagemap_scan_output(categories, p, addr, &end); + if (addr == end) + return ret; + diff --git a/queue-6.18/kvm-s390-pci-fix-memory-accounting-for-pinned-unpinned-pages.patch b/queue-6.18/kvm-s390-pci-fix-memory-accounting-for-pinned-unpinned-pages.patch new file mode 100644 index 0000000000..78817a972f --- /dev/null +++ b/queue-6.18/kvm-s390-pci-fix-memory-accounting-for-pinned-unpinned-pages.patch @@ -0,0 +1,129 @@ +From 36f6999ecde3976731a8bfc0b8e667da6f593069 Mon Sep 17 00:00:00 2001 +From: Farhan Ali +Date: Thu, 23 Jul 2026 15:14:05 -0700 +Subject: KVM: s390: pci: Fix memory accounting for pinned/unpinned pages + +From: Farhan Ali + +commit 36f6999ecde3976731a8bfc0b8e667da6f593069 upstream. + +The account_mem() and unaccount_mem() functions call get_uid() which +increments the reference count of struct user_struct on every invocation. +But we don't decrement the count by calling free_uid(). It also +accounted/unaccounted the pages against the current->mm. But its possible +the unaccount_mem() can be called from a different process context than the +one that originally pinned the pages. + +Let's fix this by storing the pinning process user_struct and mm_struct +when accounting for pinned pages, and subsequently free these resources +when the pages are unpinned. + +Fixes: 3c5a1b6f0a18 ("KVM: s390: pci: provide routines for enabling/disabling interrupt forwarding") +Cc: stable@vger.kernel.org +Reviewed-by: Christian Borntraeger +Reviewed-by: Matthew Rosato +Signed-off-by: Farhan Ali +Tested-by: Matthew Rosato +[borntraeger@linux.ibm.com: Fixed whitespace] +Signed-off-by: Christian Borntraeger +Signed-off-by: Greg Kroah-Hartman +--- + arch/s390/kvm/pci.c | 43 ++++++++++++++++++++++++++++++++----------- + arch/s390/kvm/pci.h | 2 ++ + 2 files changed, 34 insertions(+), 11 deletions(-) + +--- a/arch/s390/kvm/pci.c ++++ b/arch/s390/kvm/pci.c +@@ -191,33 +191,54 @@ static int kvm_zpci_clear_airq(struct zp + return cc ? -EIO : 0; + } + +-static inline void unaccount_mem(unsigned long nr_pages) ++static inline void unaccount_mem(struct kvm_zdev *kzdev, unsigned long nr_pages) + { +- struct user_struct *user = get_uid(current_user()); ++ struct user_struct *user = kzdev->user_account; ++ struct mm_struct *mm_account = kzdev->mm_account; + +- if (user) ++ if (user) { + atomic_long_sub(nr_pages, &user->locked_vm); +- if (current->mm) +- atomic64_sub(nr_pages, ¤t->mm->pinned_vm); ++ free_uid(user); ++ kzdev->user_account = NULL; ++ } ++ ++ if (mm_account) { ++ atomic64_sub(nr_pages, &mm_account->pinned_vm); ++ mmdrop(mm_account); ++ kzdev->mm_account = NULL; ++ } + } + +-static inline int account_mem(unsigned long nr_pages) ++static inline int account_mem(struct kvm_zdev *kzdev, unsigned long nr_pages) + { + struct user_struct *user = get_uid(current_user()); + unsigned long page_limit, cur_pages, new_pages; ++ int rc = 0; + + page_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; + + cur_pages = atomic_long_read(&user->locked_vm); + do { + new_pages = cur_pages + nr_pages; +- if (new_pages > page_limit) +- return -ENOMEM; ++ if (new_pages > page_limit) { ++ rc = -ENOMEM; ++ goto out; ++ } + } while (!atomic_long_try_cmpxchg(&user->locked_vm, &cur_pages, new_pages)); + +- atomic64_add(nr_pages, ¤t->mm->pinned_vm); ++ if (current->mm) { ++ mmgrab(current->mm); ++ atomic64_add(nr_pages, ¤t->mm->pinned_vm); ++ } ++ ++ kzdev->user_account = user; ++ kzdev->mm_account = current->mm; + + return 0; ++ ++out: ++ free_uid(user); ++ return rc; + } + + static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib, +@@ -280,7 +301,7 @@ static int kvm_s390_pci_aif_enable(struc + } + + /* Account for pinned pages, roll back on failure */ +- if (account_mem(pcount)) ++ if (account_mem(zdev->kzdev, pcount)) + goto unpin2; + + /* AISB must be allocated before we can fill in GAITE */ +@@ -401,7 +422,7 @@ static int kvm_s390_pci_aif_disable(stru + pcount++; + } + if (pcount > 0) +- unaccount_mem(pcount); ++ unaccount_mem(kzdev, pcount); + out: + mutex_unlock(&aift->aift_lock); + +--- a/arch/s390/kvm/pci.h ++++ b/arch/s390/kvm/pci.h +@@ -22,6 +22,8 @@ struct kvm_zdev { + struct kvm *kvm; + struct zpci_fib fib; + struct list_head entry; ++ struct user_struct *user_account; ++ struct mm_struct *mm_account; + }; + + struct zpci_gaite { diff --git a/queue-6.18/kvm-s390-pci-fix-missing-error-codes-and-memory-unaccounting.patch b/queue-6.18/kvm-s390-pci-fix-missing-error-codes-and-memory-unaccounting.patch new file mode 100644 index 0000000000..e31bbe396a --- /dev/null +++ b/queue-6.18/kvm-s390-pci-fix-missing-error-codes-and-memory-unaccounting.patch @@ -0,0 +1,57 @@ +From f86842e4d6c482300f4567f492d512c9ccf5bc4f Mon Sep 17 00:00:00 2001 +From: Farhan Ali +Date: Thu, 23 Jul 2026 15:14:06 -0700 +Subject: KVM: s390: pci: Fix missing error codes and memory unaccounting + +From: Farhan Ali + +commit f86842e4d6c482300f4567f492d512c9ccf5bc4f upstream. + +In kvm_s390_pci_aif_enable() two error paths failed to set an error code, +causing the function to return 0 on failure. It also failed to rollback +memory accounting on failure. Fix both by propagating an error code on +failure and calling unaccount_mem() in the cleanup path. + +Fixes: 3c5a1b6f0a18 ("KVM: s390: pci: provide routines for enabling/disabling interrupt forwarding") +Cc: stable@vger.kernel.org +Reviewed-by: Christian Borntraeger +Reviewed-by: Matthew Rosato +Signed-off-by: Farhan Ali +Tested-by: Matthew Rosato +Signed-off-by: Christian Borntraeger +Signed-off-by: Greg Kroah-Hartman +--- + arch/s390/kvm/pci.c | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +--- a/arch/s390/kvm/pci.c ++++ b/arch/s390/kvm/pci.c +@@ -301,14 +301,17 @@ static int kvm_s390_pci_aif_enable(struc + } + + /* Account for pinned pages, roll back on failure */ +- if (account_mem(zdev->kzdev, pcount)) ++ rc = account_mem(zdev->kzdev, pcount); ++ if (rc) + goto unpin2; + + /* AISB must be allocated before we can fill in GAITE */ + mutex_lock(&aift->aift_lock); + bit = airq_iv_alloc_bit(aift->sbv); +- if (bit == -1UL) ++ if (bit == -1UL) { ++ rc = -ENOMEM; + goto unlock; ++ } + zdev->aisb = bit; /* store the summary bit number */ + zdev->aibv = airq_iv_create(msi_vecs, AIRQ_IV_DATA | + AIRQ_IV_BITLOCK | +@@ -352,6 +355,8 @@ static int kvm_s390_pci_aif_enable(struc + return rc; + + unlock: ++ if (pcount > 0) ++ unaccount_mem(zdev->kzdev, pcount); + mutex_unlock(&aift->aift_lock); + unpin2: + if (fib->fmt0.sum == 1) diff --git a/queue-6.18/kvm-s390-pci-fix-null-dereference-on-aibv-allocation-failure.patch b/queue-6.18/kvm-s390-pci-fix-null-dereference-on-aibv-allocation-failure.patch new file mode 100644 index 0000000000..556bd78e80 --- /dev/null +++ b/queue-6.18/kvm-s390-pci-fix-null-dereference-on-aibv-allocation-failure.patch @@ -0,0 +1,50 @@ +From 8bf09b9b7d3232806df95f409581f8a9fd99a3fa Mon Sep 17 00:00:00 2001 +From: Farhan Ali +Date: Thu, 23 Jul 2026 15:14:07 -0700 +Subject: KVM: s390: pci: Fix NULL dereference on AIBV allocation failure + +From: Farhan Ali + +commit 8bf09b9b7d3232806df95f409581f8a9fd99a3fa upstream. + +The airq_iv_create() can return NULL on failure, but the return value was +never checked. If it fails, zdev->aibv will be NULL and fail when +dereferenced in kvm_zpci_set_airq(). Add a NULL check and free the +previously allocated AISB bit and zdev->aisb on failure. + +Fixes: 3c5a1b6f0a18 ("KVM: s390: pci: provide routines for enabling/disabling interrupt forwarding") +Cc: stable@vger.kernel.org +Reviewed-by: Christian Borntraeger +Reviewed-by: Matthew Rosato +Signed-off-by: Farhan Ali +Tested-by: Matthew Rosato +Signed-off-by: Christian Borntraeger +Signed-off-by: Greg Kroah-Hartman +--- + arch/s390/kvm/pci.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +--- a/arch/s390/kvm/pci.c ++++ b/arch/s390/kvm/pci.c +@@ -318,6 +318,11 @@ static int kvm_s390_pci_aif_enable(struc + AIRQ_IV_GUESTVEC, + phys_to_virt(fib->fmt0.aibv)); + ++ if (!zdev->aibv) { ++ rc = -ENOMEM; ++ goto free_aisb; ++ } ++ + spin_lock_irq(&aift->gait_lock); + gaite = aift->gait + zdev->aisb; + +@@ -354,6 +359,9 @@ static int kvm_s390_pci_aif_enable(struc + rc = kvm_zpci_set_airq(zdev); + return rc; + ++free_aisb: ++ airq_iv_free_bit(aift->sbv, zdev->aisb); ++ zdev->aisb = 0; + unlock: + if (pcount > 0) + unaccount_mem(zdev->kzdev, pcount); diff --git a/queue-6.18/kvm-s390-pci-reject-adapter-interrupt-forwarding-if-already-enabled.patch b/queue-6.18/kvm-s390-pci-reject-adapter-interrupt-forwarding-if-already-enabled.patch new file mode 100644 index 0000000000..64426aa71a --- /dev/null +++ b/queue-6.18/kvm-s390-pci-reject-adapter-interrupt-forwarding-if-already-enabled.patch @@ -0,0 +1,40 @@ +From 8fa01be5a6149404adb82c0979a78f6347edd3ef Mon Sep 17 00:00:00 2001 +From: Farhan Ali +Date: Thu, 23 Jul 2026 15:14:04 -0700 +Subject: KVM: s390: pci: Reject adapter interrupt forwarding if already enabled + +From: Farhan Ali + +commit 8fa01be5a6149404adb82c0979a78f6347edd3ef upstream. + +The MPCIFC instruction doesn't allow registering adapter interrupts without +first unregistering. So reject any request to enable interrupt forwarding +if its already enabled for the zPCI device. This also fixes overwriting and +thus leaking resources when the ioctl is called multiple times for the same +device. + +Fixes: 3c5a1b6f0a18 ("KVM: s390: pci: provide routines for enabling/disabling interrupt forwarding") +Cc: stable@vger.kernel.org +Reviewed-by: Christian Borntraeger +Reviewed-by: Matthew Rosato +Signed-off-by: Farhan Ali +Tested-by: Matthew Rosato +Signed-off-by: Christian Borntraeger +Signed-off-by: Greg Kroah-Hartman +--- + arch/s390/kvm/pci.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/arch/s390/kvm/pci.c ++++ b/arch/s390/kvm/pci.c +@@ -238,6 +238,10 @@ static int kvm_s390_pci_aif_enable(struc + if (zdev->gisa == 0) + return -EINVAL; + ++ /* AIF already enabled for the device */ ++ if (zdev->kzdev->fib.fmt0.aibv != 0) ++ return -EINVAL; ++ + kvm = zdev->kzdev->kvm; + msi_vecs = min_t(unsigned int, fib->fmt0.noi, zdev->max_msi); + diff --git a/queue-6.18/kvm-s390-pci-validate-aibv-and-aisb-before-pinning-guest-pages.patch b/queue-6.18/kvm-s390-pci-validate-aibv-and-aisb-before-pinning-guest-pages.patch new file mode 100644 index 0000000000..4aa9a123b7 --- /dev/null +++ b/queue-6.18/kvm-s390-pci-validate-aibv-and-aisb-before-pinning-guest-pages.patch @@ -0,0 +1,71 @@ +From 868d32ac72cba21c5c6d8a66a814b7c25a3a5c01 Mon Sep 17 00:00:00 2001 +From: Farhan Ali +Date: Thu, 23 Jul 2026 15:14:09 -0700 +Subject: KVM: s390: pci: Validate AIBV and AISB before pinning guest pages + +From: Farhan Ali + +commit 868d32ac72cba21c5c6d8a66a814b7c25a3a5c01 upstream. + +The AIBV holds one bit per MSI-X vector for a given function. The size of +the bit vector is derived from the NOI and the AIBVO. If the size of the +AIBV exceeds a single page boundary, then reject the request as we cannot +safely pin the guest AIBV. + +Similarly reject the request if the AISB address is not 8-byte aligned as +the architecture requires doubleword alignment for the summary bit address. +Since the AISBO can address up to 64 bits, the size of the AISB can only be +8 bytes for the function. This also ensures the AISB doesn't exceed a +single page boundary. + +Fixes: 3c5a1b6f0a18 ("KVM: s390: pci: provide routines for enabling/disabling interrupt forwarding") +Cc: stable@vger.kernel.org +Reviewed-by: Christian Borntraeger +Reviewed-by: Matthew Rosato +Signed-off-by: Farhan Ali +Tested-by: Matthew Rosato +Signed-off-by: Christian Borntraeger +Signed-off-by: Greg Kroah-Hartman +--- + arch/s390/kvm/pci.c | 16 +++++++++++++++- + 1 file changed, 15 insertions(+), 1 deletion(-) + +--- a/arch/s390/kvm/pci.c ++++ b/arch/s390/kvm/pci.c +@@ -245,7 +245,7 @@ static int kvm_s390_pci_aif_enable(struc + bool assist) + { + struct page *pages[1], *aibv_page, *aisb_page = NULL; +- unsigned int msi_vecs, idx; ++ unsigned int msi_vecs, idx, size; + struct zpci_gaite *gaite; + unsigned long hva, bit; + struct kvm *kvm; +@@ -272,6 +272,14 @@ static int kvm_s390_pci_aif_enable(struc + return gisc; + + /* Replace AIBV address */ ++ size = BITS_TO_LONGS(msi_vecs + fib->fmt0.aibvo) * sizeof(unsigned long); ++ npages = DIV_ROUND_UP((fib->fmt0.aibv & ~PAGE_MASK) + size, PAGE_SIZE); ++ /* AIBV cannot span more than 1 page */ ++ if (npages > 1) { ++ rc = -EINVAL; ++ goto out; ++ } ++ + idx = srcu_read_lock(&kvm->srcu); + hva = gfn_to_hva(kvm, gpa_to_gfn((gpa_t)fib->fmt0.aibv)); + npages = pin_user_pages_fast(hva, 1, FOLL_WRITE | FOLL_LONGTERM, pages); +@@ -287,6 +295,12 @@ static int kvm_s390_pci_aif_enable(struc + + /* Pin the guest AISB if one was specified */ + if (fib->fmt0.sum == 1) { ++ /* AISB must be dword aligned */ ++ if (fib->fmt0.aisb & 0x7) { ++ rc = -EINVAL; ++ goto unpin1; ++ } ++ + idx = srcu_read_lock(&kvm->srcu); + hva = gfn_to_hva(kvm, gpa_to_gfn((gpa_t)fib->fmt0.aisb)); + npages = pin_user_pages_fast(hva, 1, FOLL_WRITE | FOLL_LONGTERM, diff --git a/queue-6.18/kvm-svm-update-x2apic-msr-intercepts-if-avic-is-inhibited-while-l2-is-active.patch b/queue-6.18/kvm-svm-update-x2apic-msr-intercepts-if-avic-is-inhibited-while-l2-is-active.patch new file mode 100644 index 0000000000..8de7df97a2 --- /dev/null +++ b/queue-6.18/kvm-svm-update-x2apic-msr-intercepts-if-avic-is-inhibited-while-l2-is-active.patch @@ -0,0 +1,75 @@ +From 7d3aae206663c4e006b25a1c7a20a4029e67da76 Mon Sep 17 00:00:00 2001 +From: Sean Christopherson +Date: Fri, 10 Jul 2026 09:20:51 -0700 +Subject: KVM: SVM: Update x2APIC MSR intercepts if AVIC is inhibited while L2 is active + +From: Sean Christopherson + +commit 7d3aae206663c4e006b25a1c7a20a4029e67da76 upstream. + +Always update x2APIC MSR intercepts for L1 when AVIC is deactivated, even +if L2 is active and KVM is using a separate MSR bitmap to run L2. If AVIC +is fully enabled prior to running L2, and is then inhibited while L2 is +active (for a VM-scoped inhibit), then KVM will run L1 with AVIC disabled, +but with x2APIC MSR intercepts disabled, i.e. will allow L1 to read most of +the host's APIC state, send arbitrary interrupts, change task priority, and +ultimately trivially DoS the host. + +E.g. sending a self-IPI in L1 on HYPERV_REENLIGHTENMENT_VECTOR, 0xee, with +CONFIG_HYPERV=n in the host kernel as a "safe" PoC, yields: + + Spurious interrupt (vector 0xee) on CPU#425. Acked + +And hacking KVM to abuse kvm_set_posted_intr_wakeup_handler() to register a +handler and WARN on POSTED_INTR_WAKEUP_VECTOR yields: + + ------------[ cut here ]------------ + WARNING: arch/x86/kvm/svm/svm.c:5594 at pi_wakeup_handler+0x9/0x10 [kvm_amd], CPU#156: nested_x2apic_t/316940 + CPU: 156 UID: 0 PID: 316940 Comm: nested_x2apic_t Tainted: G S U + Tainted: [S]=CPU_OUT_OF_SPEC, [U]=USER + Hardware name: Google Astoria-Turin/astoria, BIOS 0.20260209.0-0 02/09/2026 + RIP: 0010:pi_wakeup_handler+0x9/0x10 [kvm_amd] + Call Trace: + + sysvec_kvm_posted_intr_wakeup_ipi+0x64/0x80 + + + asm_sysvec_kvm_posted_intr_wakeup_ipi+0x1a/0x20 + RIP: 0010:vcpu_run+0x1430/0x1e40 [kvm] + kvm_arch_vcpu_ioctl_run+0x2c1/0x600 [kvm] + kvm_vcpu_ioctl+0x580/0x6b0 [kvm] + __se_sys_ioctl+0x6d/0xb0 + do_syscall_64+0x10a/0x480 + entry_SYSCALL_64_after_hwframe+0x4b/0x53 + RIP: 0033:0x46ff4b + + ---[ end trace 0000000000000000 ]--- + +Fixes: 091abbf578f9 ("KVM: x86: nSVM: optimize svm_set_x2apic_msr_interception") +Cc: stable@vger.kernel.org +Cc: Yosry Ahmed +Signed-off-by: Sean Christopherson +Link: https://patch.msgid.link/20260729213558.639074-1-pbonzini@redhat.com/ +Signed-off-by: Paolo Bonzini +Signed-off-by: Greg Kroah-Hartman +--- + arch/x86/kvm/svm/avic.c | 8 -------- + 1 file changed, 8 deletions(-) + +--- a/arch/x86/kvm/svm/avic.c ++++ b/arch/x86/kvm/svm/avic.c +@@ -220,14 +220,6 @@ static void avic_deactivate_vmcb(struct + if (!sev_es_guest(svm->vcpu.kvm)) + svm_set_intercept(svm, INTERCEPT_CR8_WRITE); + +- /* +- * If running nested and the guest uses its own MSR bitmap, there +- * is no need to update L0's msr bitmap +- */ +- if (is_guest_mode(&svm->vcpu) && +- vmcb12_is_intercept(&svm->nested.ctl, INTERCEPT_MSR_PROT)) +- return; +- + /* Enabling MSR intercept for x2APIC registers */ + avic_set_x2apic_msr_interception(svm, true); + } diff --git a/queue-6.18/kvm-vmx-add-memory-clobber-to-asm-for-vmx-instructions.patch b/queue-6.18/kvm-vmx-add-memory-clobber-to-asm-for-vmx-instructions.patch new file mode 100644 index 0000000000..a5497c45e5 --- /dev/null +++ b/queue-6.18/kvm-vmx-add-memory-clobber-to-asm-for-vmx-instructions.patch @@ -0,0 +1,70 @@ +From 0e65cd9e5d41c34f86b7c347967bedac54926041 Mon Sep 17 00:00:00 2001 +From: Paolo Bonzini +Date: Tue, 21 Jul 2026 18:31:49 +0200 +Subject: KVM: VMX: add memory clobber to asm for VMX instructions + +From: Paolo Bonzini + +commit 0e65cd9e5d41c34f86b7c347967bedac54926041 upstream. + +VMCLEAR/VMREAD/VMWRITE/VMPTRLD access the internal VMCS cache, which +is not visible to the compiler; without a memory clobber, the compiler +can reorder them in troublesome ways because "asm volatile" and "asm goto" +only protect against removal of the asm. For example, placing a VMWRITE +before the corresponding VMCS pointer is loaded can lead to corruption. +While none of this has been observed, it is better to prevent than cure. + +Likewise, INVEPT and INVVPID access the TLB and, even though in their +case the effect is only visible to the next VMLAUNCH/VMRESUME, it is +technically correct to add the clobber there too. So avoid any urge to +special case them, and simply hardcode "memory" into the clobber list +of vmx_asm1() and vmx_asm2(). __vmcs_readl() open-codes its own asm, +so add the clobber there as well. + +Link: https://lore.kernel.org/kvm/CABgObfbL3t21yVeSwiLSjjOUER+rTYDPHYAH9YU4TWGRjx6XHg@mail.gmail.com/ +Cc: Sean Christopherson +Cc: stable@vger.kernel.org +Signed-off-by: Paolo Bonzini +Signed-off-by: Greg Kroah-Hartman +--- + arch/x86/kvm/vmx/vmx_ops.h | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +--- a/arch/x86/kvm/vmx/vmx_ops.h ++++ b/arch/x86/kvm/vmx/vmx_ops.h +@@ -101,7 +101,7 @@ static __always_inline unsigned long __v + + : [output] "=r" (value) + : [field] "r" (field) +- : "cc" ++ : "cc", "memory" + : do_fail, do_exception); + + return value; +@@ -146,7 +146,7 @@ do_exception: + + : ASM_CALL_CONSTRAINT, [output] "=&r" (value) + : [field] "r" (field) +- : "cc"); ++ : "cc", "memory"); + return value; + + #endif /* CONFIG_CC_HAS_ASM_GOTO_OUTPUT */ +@@ -194,7 +194,7 @@ do { \ + ".byte 0x2e\n\t" /* branch not taken hint */ \ + "jna %l[error]\n\t" \ + _ASM_EXTABLE(1b, %l[fault]) \ +- : : op1 : "cc" : error, fault); \ ++ : : op1 : "cc", "memory" : error, fault); \ + return; \ + error: \ + instrumentation_begin(); \ +@@ -211,7 +211,7 @@ do { \ + ".byte 0x2e\n\t" /* branch not taken hint */ \ + "jna %l[error]\n\t" \ + _ASM_EXTABLE(1b, %l[fault]) \ +- : : op1, op2 : "cc" : error, fault); \ ++ : : op1, op2 : "cc", "memory" : error, fault);\ + return; \ + error: \ + instrumentation_begin(); \ diff --git a/queue-6.18/mm-hugetlb-fix-list-corruption-in-allocate_file_region_entries.patch b/queue-6.18/mm-hugetlb-fix-list-corruption-in-allocate_file_region_entries.patch new file mode 100644 index 0000000000..5b13da76e6 --- /dev/null +++ b/queue-6.18/mm-hugetlb-fix-list-corruption-in-allocate_file_region_entries.patch @@ -0,0 +1,77 @@ +From dd9623f58ec702a07b2d67179d6fcea79c52231a Mon Sep 17 00:00:00 2001 +From: Xiangfeng Cai +Date: Tue, 14 Jul 2026 01:14:55 +0800 +Subject: mm/hugetlb: fix list corruption in allocate_file_region_entries() + +From: Xiangfeng Cai + +commit dd9623f58ec702a07b2d67179d6fcea79c52231a upstream. + +allocate_file_region_entries() tops up resv->region_cache with freshly +allocated file_region descriptors. The allocation uses GFP_KERNEL, so +resv->lock is dropped around it: the new entries are gathered on a +stack-local list head, allocated_regions, and spliced into +resv->region_cache once the lock is re-acquired. + +The splice used list_splice(), which moves the entries but does not +re-initialize the source head, so allocated_regions is left pointing at an +entry that now lives on resv->region_cache. The top-up runs in a while +loop that re-checks the cache deficit after re-acquiring the lock. For a +shared mapping the resv_map is shared by every mapper of the hugetlbfs +inode, so a concurrent region_chg()/region_add()/region_del() on the same +resv_map can consume cache entries during the unlocked window and force a +second iteration. That iteration calls list_add() on the stale head and +corrupts the list; with CONFIG_DEBUG_LIST the __list_add_valid() check +trips: + + list_add corruption. next->prev should be prev (ffffc900011ff7f8), + but was ffff88814c281460. (next=ffff88814c545640). + kernel BUG at lib/list_debug.c:31! + allocate_file_region_entries+0x191/0x420 + region_chg+0x267/0x300 + hugetlb_reserve_pages+0x387/0xc80 + hugetlbfs_file_mmap+0x2ce/0x3f0 + mmap_region+0x1348/0x1a80 + do_mmap+0x85e/0xb90 + vm_mmap_pgoff+0x18c/0x330 + ksys_mmap_pgoff+0x2a1/0x3e0 + do_syscall_64+0xd7/0x420 + +Without CONFIG_DEBUG_LIST the bad list_add() silently links a kernel-stack +address into resv->region_cache, leading to later use-after-free. + +This was observed as a real host panic on a dense KVM host where a QEMU +guest-RAM hugetlbfs file was mapped MAP_SHARED by both QEMU and a separate +SPDK/DPDK vhost-user target, generating concurrent region_* traffic on one +shared resv_map. + +Use list_splice_init() so the source head is re-initialized empty after +each splice, making the retry loop safe. + +Link: https://lore.kernel.org/20260713171456.300518-2-caixiangfeng@bytedance.com +Fixes: d3ec7b6e09e5 ("mm/hugetlb: use list_splice to merge two list at once") +Signed-off-by: Xiangfeng Cai +Reviewed-by: Muchun Song +Cc: Baoquan He +Cc: David Hildenbrand +Cc: Oscar Salvador +Cc: Shuah Khan +Cc: Wei Yang +Cc: +Signed-off-by: Andrew Morton +Signed-off-by: Greg Kroah-Hartman +--- + mm/hugetlb.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/mm/hugetlb.c ++++ b/mm/hugetlb.c +@@ -705,7 +705,7 @@ static int allocate_file_region_entries( + + spin_lock(&resv->lock); + +- list_splice(&allocated_regions, &resv->region_cache); ++ list_splice_init(&allocated_regions, &resv->region_cache); + resv->region_cache_count += to_allocate; + } + diff --git a/queue-6.18/mm-migrate_device-fix-pte_pfn-pte_dirty-called-on-non-present-pte.patch b/queue-6.18/mm-migrate_device-fix-pte_pfn-pte_dirty-called-on-non-present-pte.patch new file mode 100644 index 0000000000..5970970ec3 --- /dev/null +++ b/queue-6.18/mm-migrate_device-fix-pte_pfn-pte_dirty-called-on-non-present-pte.patch @@ -0,0 +1,59 @@ +From 63867c82d0c0c2d182016a32b1cc0103116b0ea5 Mon Sep 17 00:00:00 2001 +From: Kefeng Wang +Date: Mon, 6 Jul 2026 19:19:58 +0800 +Subject: mm: migrate_device: fix pte_pfn/pte_dirty called on non-present PTE + +From: Kefeng Wang + +commit 63867c82d0c0c2d182016a32b1cc0103116b0ea5 upstream. + +pte_pfn() and pte_dirty() have undefined behaviour when called on a +non-present PTE. In migrate_vma_collect_pmd(), these functions may be +invoked on non-present entries (e.g., device-private entries), leading +to potential crashes from pte_pfn() or incorrect dirty folio accounting +from pte_dirty(). Fix both by guarding with pte_present() checks. + +Link: https://lore.kernel.org/20260708003955.4024340-1-wangkefeng.wang@huawei.com +Link: https://lore.kernel.org/20260706111958.3649651-1-wangkefeng.wang@huawei.com +Fixes: fd35ca3d12cc ("mm/migrate_device.c: copy pte dirty bit to page") +Fixes: 6c287605fd56 ("mm: remember exclusively mapped anonymous pages with PG_anon_exclusive") +Signed-off-by: Kefeng Wang +Reviewed-by: Balbir Singh +Acked-by: Zi Yan +Cc: Alistair Popple +Cc: Byungchul Park +Cc: David Hildenbrand +Cc: Gregory Price +Cc: "Huang, Ying" +Cc: Joshua Hahn +Cc: Matthew Brost +Cc: Rakie Kim +Cc: Ying Huang +Cc: +Signed-off-by: Andrew Morton +Signed-off-by: Greg Kroah-Hartman +--- + mm/migrate_device.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +--- a/mm/migrate_device.c ++++ b/mm/migrate_device.c +@@ -209,7 +209,8 @@ again: + bool anon_exclusive; + pte_t swp_pte; + +- flush_cache_page(vma, addr, pte_pfn(pte)); ++ if (pte_present(pte)) ++ flush_cache_page(vma, addr, pte_pfn(pte)); + anon_exclusive = folio_test_anon(folio) && + PageAnonExclusive(page); + if (anon_exclusive) { +@@ -230,7 +231,7 @@ again: + migrate->cpages++; + + /* Set the dirty flag on the folio now the pte is gone. */ +- if (pte_dirty(pte)) ++ if (pte_present(pte) && pte_dirty(pte)) + folio_mark_dirty(folio); + + /* Setup special migration page table entry */ diff --git a/queue-6.18/mm-percpu-km-fix-bitmap-overflow-and-accounting-in-pcpu_create_chunk.patch b/queue-6.18/mm-percpu-km-fix-bitmap-overflow-and-accounting-in-pcpu_create_chunk.patch new file mode 100644 index 0000000000..74a170aaef --- /dev/null +++ b/queue-6.18/mm-percpu-km-fix-bitmap-overflow-and-accounting-in-pcpu_create_chunk.patch @@ -0,0 +1,53 @@ +From 89b1b79c308818a715e75f28744b70d8940a07c9 Mon Sep 17 00:00:00 2001 +From: Zi Yan +Date: Thu, 9 Jul 2026 15:12:01 -0400 +Subject: mm/percpu-km: fix bitmap overflow and accounting in pcpu_create_chunk() + +From: Zi Yan + +commit 89b1b79c308818a715e75f28744b70d8940a07c9 upstream. + +In pcpu_create_chunk(), nr_pages is the total contiguous backing +allocation, i.e., nr_units * pcpu_unit_pages, but pcpu_chunk_populated() +uses it to set chunk->populated, whose size is pcpu_unit_pages, bitmap. +Since bit N in chunk->populated means page offset N inside every unit is +backed. When nr_units > 1, the function writes beyond chunk->populated. +Fix it by using chunk->nr_pages. + +It also fixes the global pcpu_nr_empty_pop_pages accounting, since +pcpu_balance_free() only iterates up to chunk->nr_pages. + +Commit a63d4ac4ab609 ("percpu: make percpu-km set chunk->populated bitmap +properly") introduced the bitmap overflow issue. Later, commit +b539b87fed37f ("percpu: implmeent pcpu_nr_empty_pop_pages and +chunk->nr_populated") added pcpu_nr_empty_pop_pages and caused the +accounting issue. + +Link: https://lore.kernel.org/20260709-fix-pcpu_create_chunk-in-percpu-km-v1-1-1f64745a84cc@nvidia.com +Fixes: a63d4ac4ab609 ("percpu: make percpu-km set chunk->populated bitmap properly") +Reported-by: Sashiko +Closes: https://sashiko.dev/#/patchset/20260703-keep-subpage-private-zero-at-free-v2-0-2970fe777dd6%40nvidia.com?part=1 +Assisted-by: Codex:GPT-5 +Signed-off-by: Zi Yan +Acked-by: Dennis Zhou +Cc: Christoph Lameter +Cc: Tejun Heo +Cc: Zi Yan +Cc: +Signed-off-by: Andrew Morton +Signed-off-by: Greg Kroah-Hartman +--- + mm/percpu-km.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/mm/percpu-km.c ++++ b/mm/percpu-km.c +@@ -75,7 +75,7 @@ static struct pcpu_chunk *pcpu_create_ch + chunk->base_addr = page_address(pages); + + spin_lock_irqsave(&pcpu_lock, flags); +- pcpu_chunk_populated(chunk, 0, nr_pages); ++ pcpu_chunk_populated(chunk, 0, chunk->nr_pages); + spin_unlock_irqrestore(&pcpu_lock, flags); + + pcpu_stats_chunk_alloc(); diff --git a/queue-6.18/mm-util-don-t-read-__page_2-for-order-1-folios-in-snapshot_page.patch b/queue-6.18/mm-util-don-t-read-__page_2-for-order-1-folios-in-snapshot_page.patch new file mode 100644 index 0000000000..8c4d5294d5 --- /dev/null +++ b/queue-6.18/mm-util-don-t-read-__page_2-for-order-1-folios-in-snapshot_page.patch @@ -0,0 +1,54 @@ +From 7441d6348c70738e9ed307510db171c7a9b3f4bf Mon Sep 17 00:00:00 2001 +From: Aboorva Devarajan +Date: Thu, 9 Jul 2026 01:49:54 +0530 +Subject: mm/util: don't read __page_2 for order-1 folios in snapshot_page() + +From: Aboorva Devarajan + +commit 7441d6348c70738e9ed307510db171c7a9b3f4bf upstream. + +snapshot_page() currently reads __page_2 after checking nr_pages > 1, but +it should only do so when nr_pages > 2. + +If an order-1 folio is allocated at the end of a vmemmap section, +__page_2 will not exist and reading it will cause a fault. + +During DLPAR memory remove on a 22 TB ppc64le LPAR, snapshot_page() oopsed +on the page isolation path while reading an order-1 folio's __page_2 from +an adjacent absent section (unmapped vmemmap). + +Fix this to avoid reading memmap that doesn't exist (e.g., a vmemmap +hole). + +Link: https://lore.kernel.org/20260708201954.686111-1-aboorvad@linux.ibm.com +Fixes: 31a31da8a618 ("mm: move _pincount in folio to page[2] on 32bit") +Signed-off-by: Aboorva Devarajan +Reported-by: Sourabh Jain +Acked-by: David Hildenbrand (Arm) +Reviewed-by: Lorenzo Stoakes +Reviewed-by: Matthew Wilcox (Oracle) +Reviewed-by: Luiz Capitulino +Cc: Liam R. Howlett +Cc: Michal Hocko +Cc: Mike Rapoport +Cc: "Ritesh Harjani (IBM)" +Cc: Suren Baghdasaryan +Cc: Vlastimil Babka +Cc: # v6.15+ +Signed-off-by: Andrew Morton +Signed-off-by: Greg Kroah-Hartman +--- + mm/util.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/mm/util.c ++++ b/mm/util.c +@@ -1263,7 +1263,7 @@ again: + if (ps->idx < MAX_FOLIO_NR_PAGES) { + memcpy(&ps->folio_snapshot, foliop, 2 * sizeof(struct page)); + nr_pages = folio_nr_pages(&ps->folio_snapshot); +- if (nr_pages > 1) ++ if (nr_pages > 2) + memcpy(&ps->folio_snapshot.__page_2, &foliop->__page_2, + sizeof(struct page)); + set_ps_flags(ps, foliop, page); diff --git a/queue-6.18/mm-vmstat-fold-stranded-per-cpu-node-stats-when-a-node-comes-online.patch b/queue-6.18/mm-vmstat-fold-stranded-per-cpu-node-stats-when-a-node-comes-online.patch new file mode 100644 index 0000000000..b1c7323bd5 --- /dev/null +++ b/queue-6.18/mm-vmstat-fold-stranded-per-cpu-node-stats-when-a-node-comes-online.patch @@ -0,0 +1,74 @@ +From ea3034b2b00fa50c8d2518d0804c9d427bbafa86 Mon Sep 17 00:00:00 2001 +From: Gregory Price +Date: Sat, 27 Jun 2026 16:22:43 -0400 +Subject: mm/vmstat: fold stranded per-cpu node stats when a node comes online + +From: Gregory Price + +commit ea3034b2b00fa50c8d2518d0804c9d427bbafa86 upstream. + +A per-node vmstat counter is pgdat->vm_stat[] plus per-cpu deltas. A +balanced counter can sit split as global=+N / per-cpu=-N. + +The folds reconciling the split only walk online nodes, so when +try_offline_node() marks a node offline the per-cpu deltas are stranded. + +A subsequent online resets the per-cpu area but not pgdat->vm_stat[], +orphaning the +N permanently. All NR_VM_NODE_STAT_ITEMS are affected. + +The existing code zeroes the per-cpu counters and causes a permanent skew. +Fold the stranded deltas instead, before the node rejoins the online set. +The node is not online yet and the hotplug lock is held, so the remote +access to per-cpu values is safe. + +Discovered when node compaction hung for a nearly empty node, as the math +to determine throttling broke. Reproduced by repeated memory +hotplug/unplug cycles on a node under pressure: NR_ISOLATED_ANON ratchets +up and never returns to zero. + +Link: https://lore.kernel.org/20260627202243.758289-1-gourry@gourry.net +Fixes: 75ef71840539 ("mm, vmstat: add infrastructure for per-node vmstats") +Signed-off-by: Gregory Price +Cc: Johannes Weiner +Cc: Mel Gorman +Cc: Mike Rapoport +Cc: Vlastimil Babka +Cc: +Signed-off-by: Andrew Morton +Signed-off-by: Greg Kroah-Hartman +--- + mm/mm_init.c | 15 +++++++++++---- + 1 file changed, 11 insertions(+), 4 deletions(-) + +--- a/mm/mm_init.c ++++ b/mm/mm_init.c +@@ -1565,7 +1565,7 @@ void __ref free_area_init_core_hotplug(s + { + int nid = pgdat->node_id; + enum zone_type z; +- int cpu; ++ int cpu, i; + + pgdat_init_internals(pgdat); + +@@ -1583,10 +1583,17 @@ void __ref free_area_init_core_hotplug(s + pgdat->node_start_pfn = 0; + pgdat->node_present_pages = 0; + +- for_each_online_cpu(cpu) { +- struct per_cpu_nodestat *p; ++ /* ++ * Hot-unplug can leave per-cpu vmstat deltas unfolded (folders skip ++ * offline nodes) - reconcile this at online. Foreign access to counters ++ * is safe: the node is not online yet and we hold the hotplug lock. ++ */ ++ for_each_possible_cpu(cpu) { ++ struct per_cpu_nodestat *p = per_cpu_ptr(pgdat->per_cpu_nodestats, cpu); + +- p = per_cpu_ptr(pgdat->per_cpu_nodestats, cpu); ++ for (i = 0; i < NR_VM_NODE_STAT_ITEMS; i++) ++ if (p->vm_node_stat_diff[i]) ++ node_page_state_add(p->vm_node_stat_diff[i], pgdat, i); + memset(p, 0, sizeof(*p)); + } + diff --git a/queue-6.18/sctp-validate-adaptation-indication-parameter-length.patch b/queue-6.18/sctp-validate-adaptation-indication-parameter-length.patch new file mode 100644 index 0000000000..c480624fa1 --- /dev/null +++ b/queue-6.18/sctp-validate-adaptation-indication-parameter-length.patch @@ -0,0 +1,51 @@ +From 74b21f52c5c5a71a05c0ff70e513f4f04ff28b17 Mon Sep 17 00:00:00 2001 +From: Charles Vosburgh +Date: Mon, 27 Jul 2026 19:17:30 -0400 +Subject: sctp: validate Adaptation Indication parameter length + +From: Charles Vosburgh + +commit 74b21f52c5c5a71a05c0ff70e513f4f04ff28b17 upstream. + +The Adaptation Layer Indication parameter contains a fixed 32-bit +Adaptation Code Point after its parameter header. However, +sctp_verify_param() accepts a header-only parameter because the generic +parameter walker only requires the header to be present. + +sctp_process_param() then reads adaptation_ind beyond the declared +parameter. When the malformed parameter is last in an INIT, the read +starts at the receive skb tail, and the value is copied into the state +cookie returned in the INIT ACK. This may disclose four receive-buffer +tail bytes. + +Require the declared parameter length to match the fixed structure size +and abort the association through the existing invalid parameter length +path otherwise. + +Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") +Cc: stable@vger.kernel.org +Signed-off-by: Charles Vosburgh +Acked-by: Xin Long +Link: https://patch.msgid.link/20260727-sctp-adaptation-length-v1-1-0ab58b2810a5@gmail.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Greg Kroah-Hartman +--- + net/sctp/sm_make_chunk.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +--- a/net/sctp/sm_make_chunk.c ++++ b/net/sctp/sm_make_chunk.c +@@ -2168,7 +2168,13 @@ static enum sctp_ierror sctp_verify_para + case SCTP_PARAM_HEARTBEAT_INFO: + case SCTP_PARAM_UNRECOGNIZED_PARAMETERS: + case SCTP_PARAM_ECN_CAPABLE: ++ break; + case SCTP_PARAM_ADAPTATION_LAYER_IND: ++ if (ntohs(param.p->length) != sizeof(*param.aind)) { ++ sctp_process_inv_paramlength(asoc, param.p, ++ chunk, err_chunk); ++ retval = SCTP_IERROR_ABORT; ++ } + break; + + case SCTP_PARAM_SUPPORTED_EXT: diff --git a/queue-6.18/selftest-fix-headers-in-fclog.c.patch b/queue-6.18/selftest-fix-headers-in-fclog.c.patch new file mode 100644 index 0000000000..d97bba3fac --- /dev/null +++ b/queue-6.18/selftest-fix-headers-in-fclog.c.patch @@ -0,0 +1,42 @@ +From e3a0127eee04db8769e53c8102c4e76aa49be8c3 Mon Sep 17 00:00:00 2001 +From: Jori Koolstra +Date: Fri, 10 Jul 2026 19:17:35 +0200 +Subject: selftest: fix headers in fclog.c + +From: Jori Koolstra + +commit e3a0127eee04db8769e53c8102c4e76aa49be8c3 upstream. + +fclog.c does not compile because it is missing fcntl.h, needed for +O_RDONLY etc. + +There are also some redundant includes that are also in +kselftest_harness.h. + +Link: https://lore.kernel.org/20260710171741.837308-1-jkoolstra@xs4all.nl +Signed-off-by: Jori Koolstra +Cc: Aleksa Sarai +Cc: Shuah Khan +Cc: Wei Yang +Cc: Christian Brauner +Cc: +Signed-off-by: Andrew Morton +Signed-off-by: Greg Kroah-Hartman +--- + tools/testing/selftests/filesystems/fclog.c | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +--- a/tools/testing/selftests/filesystems/fclog.c ++++ b/tools/testing/selftests/filesystems/fclog.c +@@ -6,10 +6,8 @@ + + #include + #include ++#include + #include +-#include +-#include +-#include + #include + #include + diff --git a/queue-6.18/series b/queue-6.18/series index 6a17374f8b..06fb1ff305 100644 --- a/queue-6.18/series +++ b/queue-6.18/series @@ -165,3 +165,32 @@ pinctrl-microchip-sgpio-add-missing-select-regmap_mmio.patch pinctrl-devicetree-don-t-free-uninitialized-dev_name-on-error-path.patch erofs-cap-lzma-stream-pool-size.patch pinctrl-bm1880-add-missing-select-generic_pinconf.patch +fortify-disable-wstringop-overread-in-tests.patch +mm-migrate_device-fix-pte_pfn-pte_dirty-called-on-non-present-pte.patch +mm-util-don-t-read-__page_2-for-order-1-folios-in-snapshot_page.patch +selftest-fix-headers-in-fclog.c.patch +fs-proc-task_mmu-fix-pagemap_scan-written-state-for-pmd-holes.patch +mm-percpu-km-fix-bitmap-overflow-and-accounting-in-pcpu_create_chunk.patch +mm-hugetlb-fix-list-corruption-in-allocate_file_region_entries.patch +mm-vmstat-fold-stranded-per-cpu-node-stats-when-a-node-comes-online.patch +tracing-probes-reject-arg0-in-meta-argument-expansion.patch +tracing-fprobe-roll-back-on-enable_trace_fprobe-failure.patch +kvm-vmx-add-memory-clobber-to-asm-for-vmx-instructions.patch +kvm-svm-update-x2apic-msr-intercepts-if-avic-is-inhibited-while-l2-is-active.patch +kvm-s390-pci-reject-adapter-interrupt-forwarding-if-already-enabled.patch +kvm-s390-pci-fix-memory-accounting-for-pinned-unpinned-pages.patch +kvm-s390-pci-fix-missing-error-codes-and-memory-unaccounting.patch +kvm-s390-pci-fix-null-dereference-on-aibv-allocation-failure.patch +kvm-s390-pci-validate-aibv-and-aisb-before-pinning-guest-pages.patch +dibs-fix-use-after-free-of-dmb_node-in-loopback-attach-detach-unregister.patch +sctp-validate-adaptation-indication-parameter-length.patch +audit-fix-potential-integer-overflow-in-audit_log_n_string.patch +audit-fix-potential-use-after-free-in-audit_del_rule.patch +bluetooth-btusb-fix-short-read-errors-in-btusb_qca_send_vendor_req.patch +bluetooth-btmtk-fix-short-read-errors-in-btmtk_usb_uhw_reg_read.patch +bluetooth-mgmt-fix-pending-command-uaf-in-eir-updates.patch +bluetooth-sco-give-the-socket-its-own-sco_conn-reference.patch +bluetooth-mgmt-fix-uaf-in-pair-command-cancellation.patch +bluetooth-hci_sync-fix-advertising-data-uafs.patch +bluetooth-hidp-reject-frames-without-a-transaction-header.patch +bluetooth-hidp-validate-numbered-report-payloads.patch diff --git a/queue-6.18/tracing-fprobe-roll-back-on-enable_trace_fprobe-failure.patch b/queue-6.18/tracing-fprobe-roll-back-on-enable_trace_fprobe-failure.patch new file mode 100644 index 0000000000..439645e166 --- /dev/null +++ b/queue-6.18/tracing-fprobe-roll-back-on-enable_trace_fprobe-failure.patch @@ -0,0 +1,56 @@ +From aca0cd1bf16574327ef64f3178e5f5e37a61ef0b Mon Sep 17 00:00:00 2001 +From: Raushan Patel +Date: Fri, 24 Jul 2026 12:12:08 +0530 +Subject: tracing/fprobe: Roll back on enable_trace_fprobe() failure + +From: Raushan Patel + +commit aca0cd1bf16574327ef64f3178e5f5e37a61ef0b upstream. + +enable_trace_fprobe() sets the file link or the TP_FLAG_PROFILE flag and +then registers each trace_fprobe in the probe list. If +__register_trace_fprobe() fails partway through, the function returns +immediately without unregistering the trace_fprobes it already registered +or undoing the file link / flag it set, leaving the event half-enabled and +leaking the registered fprobe(s). + +enable_trace_kprobe() already handles this with a rollback path. Do the +same for fprobe: on failure, unregister all probes and clear the file link +or profile flag. + +Link: https://lore.kernel.org/all/20260724064208.480030-1-raushan.jhon@gmail.com/ + +Fixes: 334e5519c375 ("tracing/probes: Add fprobe events for tracing function entry and exit.") +Cc: stable@vger.kernel.org +Signed-off-by: Raushan Patel +Signed-off-by: Masami Hiramatsu (Google) +Signed-off-by: Greg Kroah-Hartman +--- + kernel/trace/trace_fprobe.c | 12 +++++++++++- + 1 file changed, 11 insertions(+), 1 deletion(-) + +--- a/kernel/trace/trace_fprobe.c ++++ b/kernel/trace/trace_fprobe.c +@@ -1481,11 +1481,21 @@ static int enable_trace_fprobe(struct tr + list_for_each_entry(tf, trace_probe_probe_list(tp), tp.list) { + ret = __register_trace_fprobe(tf); + if (ret < 0) +- return ret; ++ goto err; + } + } + + return 0; ++ ++err: ++ /* Failed to enable one of them. Roll back all */ ++ list_for_each_entry(tf, trace_probe_probe_list(tp), tp.list) ++ __unregister_trace_fprobe(tf); ++ if (file) ++ trace_probe_remove_file(tp, file); ++ else ++ trace_probe_clear_flag(tp, TP_FLAG_PROFILE); ++ return ret; + } + + /* diff --git a/queue-6.18/tracing-probes-reject-arg0-in-meta-argument-expansion.patch b/queue-6.18/tracing-probes-reject-arg0-in-meta-argument-expansion.patch new file mode 100644 index 0000000000..5d9630e491 --- /dev/null +++ b/queue-6.18/tracing-probes-reject-arg0-in-meta-argument-expansion.patch @@ -0,0 +1,48 @@ +From 00a8ce2a2a9fa17674e1feec4d9105c1a5d6a419 Mon Sep 17 00:00:00 2001 +From: Raushan Patel +Date: Fri, 24 Jul 2026 11:14:35 +0530 +Subject: tracing/probes: Reject $arg0 in meta argument expansion + +From: Raushan Patel + +commit 00a8ce2a2a9fa17674e1feec4d9105c1a5d6a419 upstream. + +traceprobe_expand_meta_args() parses $argN with simple_strtoul() and +calls sprint_nth_btf_arg(n - 1, ...). For $arg0, n is 0 so the index is +-1. Because ctx->nr_params is signed, the "idx >= nr_params" guard in +sprint_nth_btf_arg() does not catch the negative index, and +ctx->params[-1].name_off is read out of bounds. + +The normal per-argument path (parse_probe_vars()) already rejects +$arg0 via its argument-number check, but meta-argument expansion runs +before per-argument parsing and substitutes the value first, bypassing +that check. + +Reject $arg0 explicitly during expansion. + +Link: https://lore.kernel.org/all/20260724054435.146279-1-raushan.jhon@gmail.com/ + +Fixes: 18b1e870a496 ("tracing/probes: Add $arg* meta argument for all function args") +Cc: stable@vger.kernel.org +Signed-off-by: Raushan Patel +Signed-off-by: Masami Hiramatsu (Google) +Signed-off-by: Greg Kroah-Hartman +--- + kernel/trace/trace_probe.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +--- a/kernel/trace/trace_probe.c ++++ b/kernel/trace/trace_probe.c +@@ -1906,7 +1906,11 @@ const char **traceprobe_expand_meta_args + trace_probe_log_err(0, BAD_VAR); + return ERR_PTR(-ENOENT); + } +- /* Note: $argN starts from $arg1 */ ++ /* Note: $argN starts from $arg1, so $arg0 is invalid. */ ++ if (n == 0) { ++ trace_probe_log_err(0, BAD_ARG_NUM); ++ return ERR_PTR(-EINVAL); ++ } + ret = sprint_nth_btf_arg(n - 1, type, buf + used, + bufsize - used, ctx); + if (ret < 0)