From 48fcc895403cc97aa6c776cb65e6aa11290c0b44 Mon Sep 17 00:00:00 2001 From: Wei Liu Date: Thu, 23 Apr 2026 17:26:26 +0000 Subject: [PATCH 001/257] mshv: add a missing padding field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit That was missed when importing the header. Reported-by: Doru Blânzeanu Reported-by: Magnus Kulke Fixes: e68bda71a2384 ("hyperv: Add new Hyper-V headers in include/hyperv") Cc: stable@kernel.org Reviewed-by: Easwar Hariharan Signed-off-by: Wei Liu --- include/hyperv/hvhdk.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/hyperv/hvhdk.h b/include/hyperv/hvhdk.h index 5e83d3714966..0c89c62c9706 100644 --- a/include/hyperv/hvhdk.h +++ b/include/hyperv/hvhdk.h @@ -79,6 +79,7 @@ struct hv_vp_register_page { u64 registers[18]; }; + u8 reserved[8]; /* Volatile XMM registers (HV_X64_REGISTER_CLASS_XMM) */ union { struct { From 3fcf923302a8f5c0dc3af3d2ca2657cb5fae4297 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Tue, 14 Apr 2026 13:10:08 +0200 Subject: [PATCH 002/257] hv: utils: handle and propagate errors in kvp_register Make kvp_register() return an error code instead of silently ignoring failures, and propagate the error from kvp_handle_handshake() instead of returning success. This propagates both kzalloc_obj() and hvutil_transport_send() failures to kvp_handle_handshake() and thus to kvp_on_msg(). Fixes: 245ba56a52a3 ("Staging: hv: Implement key/value pair (KVP)") Cc: stable@vger.kernel.org Signed-off-by: Thorsten Blum Reviewed-by: Long Li Signed-off-by: Wei Liu --- drivers/hv/hv_kvp.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c index 0d73daf745a7..6180ebe040ff 100644 --- a/drivers/hv/hv_kvp.c +++ b/drivers/hv/hv_kvp.c @@ -93,7 +93,7 @@ static void kvp_send_key(struct work_struct *dummy); static void kvp_respond_to_host(struct hv_kvp_msg *msg, int error); static void kvp_timeout_func(struct work_struct *dummy); static void kvp_host_handshake_func(struct work_struct *dummy); -static void kvp_register(int); +static int kvp_register(int); static DECLARE_DELAYED_WORK(kvp_timeout_work, kvp_timeout_func); static DECLARE_DELAYED_WORK(kvp_host_handshake_work, kvp_host_handshake_func); @@ -127,24 +127,26 @@ static void kvp_register_done(void) hv_poll_channel(kvp_transaction.recv_channel, kvp_poll_wrapper); } -static void +static int kvp_register(int reg_value) { struct hv_kvp_msg *kvp_msg; char *version; + int ret; kvp_msg = kzalloc_obj(*kvp_msg); + if (!kvp_msg) + return -ENOMEM; - if (kvp_msg) { - version = kvp_msg->body.kvp_register.version; - kvp_msg->kvp_hdr.operation = reg_value; - strcpy(version, HV_DRV_VERSION); + version = kvp_msg->body.kvp_register.version; + kvp_msg->kvp_hdr.operation = reg_value; + strcpy(version, HV_DRV_VERSION); - hvutil_transport_send(hvt, kvp_msg, sizeof(*kvp_msg), - kvp_register_done); - kfree(kvp_msg); - } + ret = hvutil_transport_send(hvt, kvp_msg, sizeof(*kvp_msg), + kvp_register_done); + kfree(kvp_msg); + return ret; } static void kvp_timeout_func(struct work_struct *dummy) @@ -186,9 +188,8 @@ static int kvp_handle_handshake(struct hv_kvp_msg *msg) */ pr_debug("KVP: userspace daemon ver. %d connected\n", msg->kvp_hdr.operation); - kvp_register(dm_reg_value); - return 0; + return kvp_register(dm_reg_value); } From 3f8c8497b4fc249e27cb335c627114d8412e584d Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Tue, 28 Apr 2026 19:11:05 +0200 Subject: [PATCH 003/257] hv: utils: replace deprecated strcpy with strscpy in kvp_register strcpy() has been deprecated [1] because it performs no bounds checking on the destination buffer, which can lead to buffer overflows. While the current code works correctly, replace strcpy() with the safer strscpy() to follow secure coding best practices. Use ->body.kvp_register.version directly as the destination buffer and remove the local variable. [1] https://www.kernel.org/doc/html/latest/process/deprecated.html#strcpy Signed-off-by: Thorsten Blum Signed-off-by: Wei Liu --- drivers/hv/hv_kvp.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c index 6180ebe040ff..336b278b2182 100644 --- a/drivers/hv/hv_kvp.c +++ b/drivers/hv/hv_kvp.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include "hyperv_vmbus.h" @@ -130,18 +131,15 @@ static void kvp_register_done(void) static int kvp_register(int reg_value) { - struct hv_kvp_msg *kvp_msg; - char *version; int ret; kvp_msg = kzalloc_obj(*kvp_msg); if (!kvp_msg) return -ENOMEM; - version = kvp_msg->body.kvp_register.version; kvp_msg->kvp_hdr.operation = reg_value; - strcpy(version, HV_DRV_VERSION); + strscpy(kvp_msg->body.kvp_register.version, HV_DRV_VERSION); ret = hvutil_transport_send(hvt, kvp_msg, sizeof(*kvp_msg), kvp_register_done); From f1a9e67c11388965802a61922c313bfc43272afe Mon Sep 17 00:00:00 2001 From: Jork Loeser Date: Mon, 27 Apr 2026 14:38:52 -0700 Subject: [PATCH 004/257] mshv: limit SynIC management to MSHV-owned resources The SynIC is shared between VMBus and MSHV. VMBus owns the message page (SIMP), event flags page (SIEFP), global enable (SCONTROL), and SINT2. MSHV adds SINT0, SINT5, and the event ring page (SIRBP). Currently mshv_synic_cpu_init() redundantly enables SIMP, SIEFP, and SCONTROL that VMBus already configured, and mshv_synic_cpu_exit() disables all of them. This is wrong because MSHV can be torn down while VMBus is still active. In particular, a kexec reboot notifier tears down MSHV first. Disabling SCONTROL, SIMP, and SIEFP out from under VMBus causes its later cleanup to write SynIC MSRs while SynIC is disabled, which the hypervisor does not tolerate. Restrict MSHV to managing only the resources it owns: - SINT0, SINT5: mask on cleanup, unmask on init - SIRBP: enable/disable as before - SIMP, SIEFP, SCONTROL: leave to VMBus when it is active (L1VH and nested root partition); on a non-nested root partition VMBus does not run, so MSHV must enable/disable them While here, fix the SIEFP and SIRBP memremap() and virt_to_phys() calls to use HV_HYP_PAGE_SHIFT/HV_HYP_PAGE_SIZE instead of PAGE_SHIFT/PAGE_SIZE. The hypervisor always uses 4K pages for SynIC register GPAs regardless of the kernel page size, so using PAGE_SHIFT produces wrong addresses on ARM64 with 64K pages. Note that initialization order matters - VMBUS first, MSHV second, and the reverse on de-init. Ideally, we would want a dedicated SYNIC driver that replaces the cross-dependencies with a clear API and dynamic tracking. Such refactor should go into its own dedicated series, outside of this kexec fix series. Signed-off-by: Jork Loeser Reviewed-by: Anirudh Rayabharam (Microsoft) Reviewed-by: Stanislav Kinsburskii Signed-off-by: Wei Liu --- drivers/hv/hv.c | 3 + drivers/hv/mshv_synic.c | 152 ++++++++++++++++++++++++++-------------- 2 files changed, 104 insertions(+), 51 deletions(-) diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index ae60fd542292..ef4b1b03395d 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -272,6 +272,9 @@ void hv_synic_free(void) /* * hv_hyp_synic_enable_regs - Initialize the Synthetic Interrupt Controller * with the hypervisor. + * + * Note: When MSHV is present, mshv_synic_cpu_init() intializes further + * registers later. */ void hv_hyp_synic_enable_regs(unsigned int cpu) { diff --git a/drivers/hv/mshv_synic.c b/drivers/hv/mshv_synic.c index e2288a726fec..2db3b0192eac 100644 --- a/drivers/hv/mshv_synic.c +++ b/drivers/hv/mshv_synic.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -456,46 +457,75 @@ static int mshv_synic_cpu_init(unsigned int cpu) union hv_synic_siefp siefp; union hv_synic_sirbp sirbp; union hv_synic_sint sint; - union hv_synic_scontrol sctrl; struct hv_synic_pages *spages = this_cpu_ptr(synic_pages); struct hv_message_page **msg_page = &spages->hyp_synic_message_page; struct hv_synic_event_flags_page **event_flags_page = &spages->synic_event_flags_page; struct hv_synic_event_ring_page **event_ring_page = &spages->synic_event_ring_page; + /* + * VMBus owns SIMP/SIEFP/SCONTROL when it is active. + * See hv_hyp_synic_enable_regs() for that initialization. + */ + bool vmbus_active = hv_vmbus_exists(); - /* Setup the Synic's message page */ + /* + * Map the SYNIC message page. When VMBus is not active the + * hypervisor pre-provisions the SIMP GPA but may not set + * simp_enabled — enable it here. + */ simp.as_uint64 = hv_get_non_nested_msr(HV_MSR_SIMP); - simp.simp_enabled = true; + if (!vmbus_active) { + simp.simp_enabled = true; + hv_set_non_nested_msr(HV_MSR_SIMP, simp.as_uint64); + } *msg_page = memremap(simp.base_simp_gpa << HV_HYP_PAGE_SHIFT, HV_HYP_PAGE_SIZE, MEMREMAP_WB); if (!(*msg_page)) - return -EFAULT; + goto cleanup_simp; - hv_set_non_nested_msr(HV_MSR_SIMP, simp.as_uint64); - - /* Setup the Synic's event flags page */ + /* + * Map the event flags page. Same as SIMP: enable when + * VMBus is not active, already enabled by VMBus otherwise. + */ siefp.as_uint64 = hv_get_non_nested_msr(HV_MSR_SIEFP); - siefp.siefp_enabled = true; - *event_flags_page = memremap(siefp.base_siefp_gpa << PAGE_SHIFT, - PAGE_SIZE, MEMREMAP_WB); + if (!vmbus_active) { + siefp.siefp_enabled = true; + hv_set_non_nested_msr(HV_MSR_SIEFP, siefp.as_uint64); + } + *event_flags_page = memremap(siefp.base_siefp_gpa << HV_HYP_PAGE_SHIFT, + HV_HYP_PAGE_SIZE, MEMREMAP_WB); if (!(*event_flags_page)) - goto cleanup; - - hv_set_non_nested_msr(HV_MSR_SIEFP, siefp.as_uint64); + goto cleanup_siefp; /* Setup the Synic's event ring page */ sirbp.as_uint64 = hv_get_non_nested_msr(HV_MSR_SIRBP); + + if (hv_root_partition()) { + *event_ring_page = memremap(sirbp.base_sirbp_gpa << HV_HYP_PAGE_SHIFT, + HV_HYP_PAGE_SIZE, MEMREMAP_WB); + + if (!(*event_ring_page)) + goto cleanup_siefp; + } else { + /* + * On L1VH the hypervisor does not provide a SIRBP page. + * Allocate one and program its GPA into the MSR. + */ + *event_ring_page = (struct hv_synic_event_ring_page *) + get_zeroed_page(GFP_KERNEL); + + if (!(*event_ring_page)) + goto cleanup_siefp; + + sirbp.base_sirbp_gpa = virt_to_phys(*event_ring_page) + >> HV_HYP_PAGE_SHIFT; + } + sirbp.sirbp_enabled = true; - *event_ring_page = memremap(sirbp.base_sirbp_gpa << PAGE_SHIFT, - PAGE_SIZE, MEMREMAP_WB); - - if (!(*event_ring_page)) - goto cleanup; - hv_set_non_nested_msr(HV_MSR_SIRBP, sirbp.as_uint64); if (mshv_sint_irq != -1) @@ -518,28 +548,30 @@ static int mshv_synic_cpu_init(unsigned int cpu) hv_set_non_nested_msr(HV_MSR_SINT0 + HV_SYNIC_DOORBELL_SINT_INDEX, sint.as_uint64); - /* Enable global synic bit */ - sctrl.as_uint64 = hv_get_non_nested_msr(HV_MSR_SCONTROL); - sctrl.enable = 1; - hv_set_non_nested_msr(HV_MSR_SCONTROL, sctrl.as_uint64); + /* When VMBus is active it already enabled SCONTROL. */ + if (!vmbus_active) { + union hv_synic_scontrol sctrl; + + sctrl.as_uint64 = hv_get_non_nested_msr(HV_MSR_SCONTROL); + sctrl.enable = 1; + hv_set_non_nested_msr(HV_MSR_SCONTROL, sctrl.as_uint64); + } return 0; -cleanup: - if (*event_ring_page) { - sirbp.sirbp_enabled = false; - hv_set_non_nested_msr(HV_MSR_SIRBP, sirbp.as_uint64); - memunmap(*event_ring_page); - } - if (*event_flags_page) { +cleanup_siefp: + if (*event_flags_page) + memunmap(*event_flags_page); + if (!vmbus_active) { siefp.siefp_enabled = false; hv_set_non_nested_msr(HV_MSR_SIEFP, siefp.as_uint64); - memunmap(*event_flags_page); } - if (*msg_page) { +cleanup_simp: + if (*msg_page) + memunmap(*msg_page); + if (!vmbus_active) { simp.simp_enabled = false; hv_set_non_nested_msr(HV_MSR_SIMP, simp.as_uint64); - memunmap(*msg_page); } return -EFAULT; @@ -548,16 +580,15 @@ static int mshv_synic_cpu_init(unsigned int cpu) static int mshv_synic_cpu_exit(unsigned int cpu) { union hv_synic_sint sint; - union hv_synic_simp simp; - union hv_synic_siefp siefp; union hv_synic_sirbp sirbp; - union hv_synic_scontrol sctrl; struct hv_synic_pages *spages = this_cpu_ptr(synic_pages); struct hv_message_page **msg_page = &spages->hyp_synic_message_page; struct hv_synic_event_flags_page **event_flags_page = &spages->synic_event_flags_page; struct hv_synic_event_ring_page **event_ring_page = &spages->synic_event_ring_page; + /* VMBus owns SIMP/SIEFP/SCONTROL when it is active */ + bool vmbus_active = hv_vmbus_exists(); /* Disable the interrupt */ sint.as_uint64 = hv_get_non_nested_msr(HV_MSR_SINT0 + HV_SYNIC_INTERCEPTION_SINT_INDEX); @@ -574,28 +605,47 @@ static int mshv_synic_cpu_exit(unsigned int cpu) if (mshv_sint_irq != -1) disable_percpu_irq(mshv_sint_irq); - /* Disable Synic's event ring page */ + /* Disable SYNIC event ring page owned by MSHV */ sirbp.as_uint64 = hv_get_non_nested_msr(HV_MSR_SIRBP); sirbp.sirbp_enabled = false; - hv_set_non_nested_msr(HV_MSR_SIRBP, sirbp.as_uint64); - memunmap(*event_ring_page); - /* Disable Synic's event flags page */ - siefp.as_uint64 = hv_get_non_nested_msr(HV_MSR_SIEFP); - siefp.siefp_enabled = false; - hv_set_non_nested_msr(HV_MSR_SIEFP, siefp.as_uint64); + if (hv_root_partition()) { + hv_set_non_nested_msr(HV_MSR_SIRBP, sirbp.as_uint64); + memunmap(*event_ring_page); + } else { + sirbp.base_sirbp_gpa = 0; + hv_set_non_nested_msr(HV_MSR_SIRBP, sirbp.as_uint64); + free_page((unsigned long)*event_ring_page); + } + + /* + * Release our mappings of the message and event flags pages. + * When VMBus is not active, we enabled SIMP/SIEFP — disable + * them. Otherwise VMBus owns the MSRs — leave them. + */ memunmap(*event_flags_page); + if (!vmbus_active) { + union hv_synic_simp simp; + union hv_synic_siefp siefp; - /* Disable Synic's message page */ - simp.as_uint64 = hv_get_non_nested_msr(HV_MSR_SIMP); - simp.simp_enabled = false; - hv_set_non_nested_msr(HV_MSR_SIMP, simp.as_uint64); + siefp.as_uint64 = hv_get_non_nested_msr(HV_MSR_SIEFP); + siefp.siefp_enabled = false; + hv_set_non_nested_msr(HV_MSR_SIEFP, siefp.as_uint64); + + simp.as_uint64 = hv_get_non_nested_msr(HV_MSR_SIMP); + simp.simp_enabled = false; + hv_set_non_nested_msr(HV_MSR_SIMP, simp.as_uint64); + } memunmap(*msg_page); - /* Disable global synic bit */ - sctrl.as_uint64 = hv_get_non_nested_msr(HV_MSR_SCONTROL); - sctrl.enable = 0; - hv_set_non_nested_msr(HV_MSR_SCONTROL, sctrl.as_uint64); + /* When VMBus is active it owns SCONTROL — leave it. */ + if (!vmbus_active) { + union hv_synic_scontrol sctrl; + + sctrl.as_uint64 = hv_get_non_nested_msr(HV_MSR_SCONTROL); + sctrl.enable = 0; + hv_set_non_nested_msr(HV_MSR_SCONTROL, sctrl.as_uint64); + } return 0; } From efe0fb8c3fe2b996522f7418fd311eeff43c1148 Mon Sep 17 00:00:00 2001 From: Jork Loeser Date: Mon, 27 Apr 2026 14:38:53 -0700 Subject: [PATCH 005/257] mshv: clean up SynIC state on kexec for L1VH The reboot notifier that tears down the SynIC cpuhp state guards the cleanup with hv_root_partition(), so on L1VH (where hv_root_partition() is false) SINT0, SINT5, and SIRBP are never cleaned up before kexec. The kexec'd kernel then inherits stale unmasked SINTs and an enabled SIRBP pointing to freed memory. Remove the hv_root_partition() guard so the cleanup runs for all parent partitions. Signed-off-by: Jork Loeser Reviewed-by: Stanislav Kinsburskii Reviewed-by: Anirudh Rayabharam (Microsoft) Signed-off-by: Wei Liu --- drivers/hv/mshv_synic.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/hv/mshv_synic.c b/drivers/hv/mshv_synic.c index 2db3b0192eac..978a1cace341 100644 --- a/drivers/hv/mshv_synic.c +++ b/drivers/hv/mshv_synic.c @@ -723,9 +723,6 @@ mshv_unregister_doorbell(u64 partition_id, int doorbell_portid) static int mshv_synic_reboot_notify(struct notifier_block *nb, unsigned long code, void *unused) { - if (!hv_root_partition()) - return 0; - cpuhp_remove_state(synic_cpuhp_online); return 0; } From 6e55a8d9f21bcb238596bdd8092c92e74c698a3d Mon Sep 17 00:00:00 2001 From: Jork Loeser Date: Mon, 27 Apr 2026 14:38:54 -0700 Subject: [PATCH 006/257] mshv: unmap debugfs stats pages on kexec On L1VH, debugfs stats pages are overlay pages: the kernel allocates them and registers the GPAs with the hypervisor via HVCALL_MAP_STATS_PAGE2. These overlay mappings persist in the hypervisor across kexec. If the kexec'd kernel reuses those physical pages, the hypervisor's overlay semantics cause a machine check exception. Fix this by calling mshv_debugfs_exit() from the reboot notifier, which issues HVCALL_UNMAP_STATS_PAGE for each mapped stats page before kexec. This releases the overlay bindings so the physical pages can be safely reused. Guard mshv_debugfs_exit() against being called when init failed. Signed-off-by: Jork Loeser Reviewed-by: Anirudh Rayabharam (Microsoft) Reviewed-by: Stanislav Kinsburskii Signed-off-by: Wei Liu --- drivers/hv/mshv_debugfs.c | 7 ++++++- drivers/hv/mshv_synic.c | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/hv/mshv_debugfs.c b/drivers/hv/mshv_debugfs.c index 418b6dc8f3c2..3c3e02237ae9 100644 --- a/drivers/hv/mshv_debugfs.c +++ b/drivers/hv/mshv_debugfs.c @@ -674,8 +674,10 @@ int __init mshv_debugfs_init(void) mshv_debugfs = debugfs_create_dir("mshv", NULL); if (IS_ERR(mshv_debugfs)) { + err = PTR_ERR(mshv_debugfs); + mshv_debugfs = NULL; pr_err("%s: failed to create debugfs directory\n", __func__); - return PTR_ERR(mshv_debugfs); + return err; } if (hv_root_partition()) { @@ -710,6 +712,9 @@ int __init mshv_debugfs_init(void) void mshv_debugfs_exit(void) { + if (!mshv_debugfs) + return; + mshv_debugfs_parent_partition_remove(); if (hv_root_partition()) { diff --git a/drivers/hv/mshv_synic.c b/drivers/hv/mshv_synic.c index 978a1cace341..88170ce6b83f 100644 --- a/drivers/hv/mshv_synic.c +++ b/drivers/hv/mshv_synic.c @@ -723,6 +723,7 @@ mshv_unregister_doorbell(u64 partition_id, int doorbell_portid) static int mshv_synic_reboot_notify(struct notifier_block *nb, unsigned long code, void *unused) { + mshv_debugfs_exit(); cpuhp_remove_state(synic_cpuhp_online); return 0; } From c5c3ef8d49e15d2fc1cec4ad7c91d81b99977440 Mon Sep 17 00:00:00 2001 From: Michael Kelley Date: Tue, 17 Feb 2026 10:23:34 -0800 Subject: [PATCH 007/257] Drivers: hv: vmbus: Provide option to skip VMBus unload on panic Currently, VMBus code initiates a VMBus unload in the panic path so that if a kdump kernel is loaded, it can start fresh in setting up its own VMBus connection. However, a driver for the VMBus virtual frame buffer may need to flush dirty portions of the frame buffer back to the Hyper-V host so that panic information is visible in the graphics console. To support such flushing, provide exported functions for the frame buffer driver to specify that the VMBus unload should not be done by the VMBus driver, and to initiate the VMBus unload itself. Together these allow a frame buffer driver to delay the VMBus unload until after it has completed the flush. Ideally, the VMBus driver could use its own panic-path callback to do the unload after all frame buffer drivers have finished. But DRM frame buffer drivers use the kmsg dump callback, and there are no callbacks after that in the panic path. Hence this somewhat messy approach to properly sequencing the frame buffer flush and the VMBus unload. Fixes: 3671f3777758 ("drm/hyperv: Add support for drm_panic") Signed-off-by: Michael Kelley Reviewed-by: Long Li Signed-off-by: Wei Liu --- drivers/hv/channel_mgmt.c | 1 + drivers/hv/hyperv_vmbus.h | 1 - drivers/hv/vmbus_drv.c | 25 ++++++++++++++++++------- include/linux/hyperv.h | 3 +++ 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index 84eb0a6a0b54..89d214dda360 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -952,6 +952,7 @@ void vmbus_initiate_unload(bool crash) else vmbus_wait_for_unload(); } +EXPORT_SYMBOL_GPL(vmbus_initiate_unload); static void vmbus_setup_channel_state(struct vmbus_channel *channel, struct vmbus_channel_offer_channel *offer) diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index 05a36854389a..eb8bdd8bb1f5 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -441,7 +441,6 @@ void hv_vss_deinit(void); int hv_vss_pre_suspend(void); int hv_vss_pre_resume(void); void hv_vss_onchannelcallback(void *context); -void vmbus_initiate_unload(bool crash); static inline void hv_poll_channel(struct vmbus_channel *channel, void (*cb)(void *)) diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index d28ff45d4cfd..c9eeb2ec365d 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -69,19 +69,29 @@ bool vmbus_is_confidential(void) } EXPORT_SYMBOL_GPL(vmbus_is_confidential); +static bool skip_vmbus_unload; + +/* + * Allow a VMBus framebuffer driver to specify that in the case of a panic, + * it will do the VMbus unload operation once it has flushed any dirty + * portions of the framebuffer to the Hyper-V host. + */ +void vmbus_set_skip_unload(bool skip) +{ + skip_vmbus_unload = skip; +} +EXPORT_SYMBOL_GPL(vmbus_set_skip_unload); + /* * The panic notifier below is responsible solely for unloading the * vmbus connection, which is necessary in a panic event. - * - * Notice an intrincate relation of this notifier with Hyper-V - * framebuffer panic notifier exists - we need vmbus connection alive - * there in order to succeed, so we need to order both with each other - * [see hvfb_on_panic()] - this is done using notifiers' priorities. */ static int hv_panic_vmbus_unload(struct notifier_block *nb, unsigned long val, void *args) { - vmbus_initiate_unload(true); + if (!skip_vmbus_unload) + vmbus_initiate_unload(true); + return NOTIFY_DONE; } static struct notifier_block hyperv_panic_vmbus_unload_block = { @@ -2897,7 +2907,8 @@ static void hv_crash_handler(struct pt_regs *regs) { int cpu; - vmbus_initiate_unload(true); + if (!skip_vmbus_unload) + vmbus_initiate_unload(true); /* * In crash handler we can't schedule synic cleanup for all CPUs, * doing the cleanup for current CPU only. This should be sufficient diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 964f1be8150c..41a3d82f0722 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -1336,6 +1336,9 @@ int vmbus_allocate_mmio(struct resource **new, struct hv_device *device_obj, bool fb_overlap_ok); void vmbus_free_mmio(resource_size_t start, resource_size_t size); +void vmbus_initiate_unload(bool crash); +void vmbus_set_skip_unload(bool skip); + /* * GUID definitions of various offer types - services offered to the guest. */ From 8b35874f56ded0cc1a90a25b87411249a86246cd Mon Sep 17 00:00:00 2001 From: Michael Kelley Date: Tue, 17 Feb 2026 10:23:35 -0800 Subject: [PATCH 008/257] drm/hyperv: During panic do VMBus unload after frame buffer is flushed In a VM, Linux panic information (reason for the panic, stack trace, etc.) may be written to a serial console and/or a virtual frame buffer for a graphics console. The latter may need to be flushed back to the host hypervisor for display. The current Hyper-V DRM driver for the frame buffer does the flushing *after* the VMBus connection has been unloaded, such that panic messages are not displayed on the graphics console. A user with a Hyper-V graphics console is left with just a hung empty screen after a panic. The enhanced control that DRM provides over the panic display in the graphics console is similarly non-functional. Commit 3671f3777758 ("drm/hyperv: Add support for drm_panic") added the Hyper-V DRM driver support to flush the virtual frame buffer. It provided necessary functionality but did not handle the sequencing problem with VMBus unload. Fix the full problem by using VMBus functions to suppress the VMBus unload that is normally done by the VMBus driver in the panic path. Then after the frame buffer has been flushed, do the VMBus unload so that a kdump kernel can start cleanly. As expected, CONFIG_DRM_PANIC must be selected for these changes to have effect. As a side benefit, the enhanced features of the DRM panic path are also functional. Fixes: 3671f3777758 ("drm/hyperv: Add support for drm_panic") Signed-off-by: Michael Kelley Reviewed-by: Jocelyn Falempe Signed-off-by: Wei Liu --- drivers/gpu/drm/hyperv/hyperv_drm_drv.c | 5 +++++ drivers/gpu/drm/hyperv/hyperv_drm_modeset.c | 15 ++++++++------- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/hyperv/hyperv_drm_drv.c b/drivers/gpu/drm/hyperv/hyperv_drm_drv.c index 06b5d96e6eaf..b6bf6412ae34 100644 --- a/drivers/gpu/drm/hyperv/hyperv_drm_drv.c +++ b/drivers/gpu/drm/hyperv/hyperv_drm_drv.c @@ -150,6 +150,10 @@ static int hyperv_vmbus_probe(struct hv_device *hdev, goto err_free_mmio; } + /* If DRM panic path is stubbed out VMBus code must do the unload */ + if (IS_ENABLED(CONFIG_DRM_PANIC)) + vmbus_set_skip_unload(true); + drm_client_setup(dev, NULL); return 0; @@ -169,6 +173,7 @@ static void hyperv_vmbus_remove(struct hv_device *hdev) struct drm_device *dev = hv_get_drvdata(hdev); struct hyperv_drm_device *hv = to_hv(dev); + vmbus_set_skip_unload(false); drm_dev_unplug(dev); drm_atomic_helper_shutdown(dev); vmbus_close(hdev->channel); diff --git a/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c b/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c index 7978f8c8108c..d48ca6c23b7c 100644 --- a/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c +++ b/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c @@ -212,15 +212,16 @@ static void hyperv_plane_panic_flush(struct drm_plane *plane) struct hyperv_drm_device *hv = to_hv(plane->dev); struct drm_rect rect; - if (!plane->state || !plane->state->fb) - return; + if (plane->state && plane->state->fb) { + rect.x1 = 0; + rect.y1 = 0; + rect.x2 = plane->state->fb->width; + rect.y2 = plane->state->fb->height; - rect.x1 = 0; - rect.y1 = 0; - rect.x2 = plane->state->fb->width; - rect.y2 = plane->state->fb->height; + hyperv_update_dirt(hv->hdev, &rect); + } - hyperv_update_dirt(hv->hdev, &rect); + vmbus_initiate_unload(true); } static const struct drm_plane_helper_funcs hyperv_plane_helper_funcs = { From 84a0f7caafc679f763d3868635837e22bb89651a Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Mon, 11 May 2026 17:02:44 +0900 Subject: [PATCH 009/257] ARM: Do not select HAVE_RUST when KASAN is enabled When KASAN is enabled, such as with allmodconfig, the build fails when building the Rust code with: error: kernel-address sanitizer is not supported for this target error: aborting due to 1 previous error make[4]: *** [rust/Makefile:654: rust/core.o] Error 1 The arm-unknown-linux-gnueabi target does not support KASAN, so avoid saying Rust is supported when it is enabled. Cc: stable@vger.kernel.org Fixes: ccb8ce526807 ("ARM: 9441/1: rust: Enable Rust support for ARMv7") Link: https://github.com/Rust-for-Linux/linux/issues/1234 Signed-off-by: Nathan Chancellor Reviewed-by: Christian Schrefl Link: https://patch.msgid.link/20260511-arm-avoid-rust-with-kasan-v1-1-24d55f4a900b@kernel.org Signed-off-by: Miguel Ojeda --- arch/arm/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 71fc5dd4123f..73e6647bea46 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -136,7 +136,7 @@ config ARM select MMU_GATHER_RCU_TABLE_FREE if SMP && ARM_LPAE select HAVE_REGS_AND_STACK_ACCESS_API select HAVE_RSEQ - select HAVE_RUST if CPU_LITTLE_ENDIAN && CPU_32v7 + select HAVE_RUST if CPU_LITTLE_ENDIAN && CPU_32v7 && !KASAN select HAVE_STACKPROTECTOR select HAVE_SYSCALL_TRACEPOINTS select HAVE_UID16 From 1711b6ed6953cee5940ca4c3a6e77f1b3798cee2 Mon Sep 17 00:00:00 2001 From: Nirmoy Das Date: Thu, 14 May 2026 07:42:57 -0700 Subject: [PATCH 010/257] ovl: keep err zero after successful ovl_cache_get() ovl_iterate_merged() stores PTR_ERR(cache) in err before checking IS_ERR(cache). On success err holds the truncated cache pointer and can be returned as a bogus non-zero error. The syzbot reproducer reaches this through overlay-on-overlay readdir: getdents64 iterate_dir(outer overlay file) ovl_iterate_merged() ovl_cache_get() ovl_dir_read_merged() ovl_dir_read() iterate_dir(inner overlay file) ovl_iterate_merged() Only compute PTR_ERR(cache) on the error path. Fixes: d25e4b739f83 ("ovl: refactor ovl_iterate() and port to cred guard") Reported-by: syzbot+a16fb0cce329a320661c@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=a16fb0cce329a320661c Cc: stable@vger.kernel.org Signed-off-by: Nirmoy Das Link: https://patch.msgid.link/20260514144258.3068715-1-nirmoyd@nvidia.com Signed-off-by: Christian Brauner --- fs/overlayfs/readdir.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c index 1dcc75b3a90f..e7fe29cb6028 100644 --- a/fs/overlayfs/readdir.c +++ b/fs/overlayfs/readdir.c @@ -838,15 +838,14 @@ static int ovl_iterate_merged(struct file *file, struct dir_context *ctx) struct ovl_dir_file *od = file->private_data; struct dentry *dentry = file->f_path.dentry; struct ovl_cache_entry *p; - int err = 0; + int err; if (!od->cache) { struct ovl_dir_cache *cache; cache = ovl_cache_get(dentry); - err = PTR_ERR(cache); if (IS_ERR(cache)) - return err; + return PTR_ERR(cache); od->cache = cache; ovl_seek_cursor(od, ctx->pos); @@ -869,7 +868,7 @@ static int ovl_iterate_merged(struct file *file, struct dir_context *ctx) od->cursor = p->l_node.next; ctx->pos++; } - return err; + return 0; } static bool ovl_need_adjust_d_ino(struct file *file) From 62c4d31d78294bd61cf3403626b789e854357177 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 18 May 2026 10:32:11 +0200 Subject: [PATCH 011/257] pidfd: refuse access to tasks that have started exiting harder The recent ptrace fix closed a hole where someone could rely on task->mm becoming NULL during do_exit() to bypass dumpability checks. This api here leans on on the very same check and so inherits the fix. But there is no good reason to let it succeed at all once the target has entered do_exit(). PF_EXITING is set by exit_signals() at the very top of do_exit(), before exit_mm() and exit_files() run. Once we observe it, the task is committed to dying and exit_files() will release the fdtable shortly. Fixes: 8649c322f75c ("pid: Implement pidfd_getfd syscall") Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260518-obgleich-petersilie-2d77ccccf9b9@brauner Signed-off-by: Christian Brauner (Amutable) --- kernel/pid.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/kernel/pid.c b/kernel/pid.c index fd5c2d4aa349..f55189a3d07d 100644 --- a/kernel/pid.c +++ b/kernel/pid.c @@ -885,10 +885,12 @@ static struct file *__pidfd_fget(struct task_struct *task, int fd) if (ret) return ERR_PTR(ret); - if (ptrace_may_access(task, PTRACE_MODE_ATTACH_REALCREDS)) - file = fget_task(task, fd); - else + if (!ptrace_may_access(task, PTRACE_MODE_ATTACH_REALCREDS)) file = ERR_PTR(-EPERM); + else if (task->flags & PF_EXITING) + file = ERR_PTR(-ESRCH); + else + file = fget_task(task, fd); up_read(&task->signal->exec_update_lock); From 0eb307d61317b42b120ab02099b597226318358a Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Mon, 20 Apr 2026 17:45:28 +0800 Subject: [PATCH 012/257] selftests: harness: fix pidfd leak in __wait_for_test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the pidfd leak in kselftest_harness.h's __wait_for_test() where childfd = syscall(__NR_pidfd_open, t->pid, 0) is never closed. Fixes: 73a3cde97677 ("selftests: harness: Implement test timeouts through pidfd") Signed-off-by: Geliang Tang Link: https://patch.msgid.link/a82e275ccfb2609a1984d90ab559fa3af78f1e81.1776678050.git.tanggeliang@kylinos.cn Reviewed-by: Thomas Weißschuh Signed-off-by: Christian Brauner (Amutable) --- tools/testing/selftests/kselftest_harness.h | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/testing/selftests/kselftest_harness.h b/tools/testing/selftests/kselftest_harness.h index cfdce9cd252e..261e4df94d9d 100644 --- a/tools/testing/selftests/kselftest_harness.h +++ b/tools/testing/selftests/kselftest_harness.h @@ -996,6 +996,7 @@ static void __wait_for_test(struct __test_metadata *t) poll_child.fd = childfd; poll_child.events = POLLIN; ret = poll(&poll_child, 1, t->timeout * 1000); + close(childfd); if (ret == -1) { t->exit_code = KSFT_FAIL; fprintf(TH_LOG_STREAM, From 4e3d1b2c48ca6c55f1e9ca7f8dccc76f120f276c Mon Sep 17 00:00:00 2001 From: Jann Horn Date: Tue, 19 May 2026 16:40:34 +0200 Subject: [PATCH 013/257] fuse: limit FUSE_NOTIFY_RETRIEVE to uptodate folios FUSE_NOTIFY_RETRIEVE must be limited to uptodate folios; !uptodate folios can contain uninitialized data. Since FUSE_NOTIFY_RETRIEVE is intended to only return data that is already in the page cache and not wait for data from the FUSE daemon, treat !uptodate folios as if they weren't present. This only has security impact on systems that don't enable automatic zero-initialization of all page allocations via CONFIG_INIT_ON_ALLOC_DEFAULT_ON or init_on_alloc=1. Cc: stable@kernel.org Fixes: 2d45ba381a74 ("fuse: add retrieve request") Signed-off-by: Jann Horn Link: https://patch.msgid.link/20260519-fuse-retrieve-uptodate-v1-1-a7a1912a37f9@google.com Acked-by: Miklos Szeredi Signed-off-by: Christian Brauner (Amutable) --- fs/fuse/dev.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 5dda7080f4a9..08d364ed7d6c 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -1912,6 +1912,10 @@ static int fuse_retrieve(struct fuse_mount *fm, struct inode *inode, folio = filemap_get_folio(mapping, index); if (IS_ERR(folio)) break; + if (!folio_test_uptodate(folio)) { + folio_put(folio); + break; + } folio_offset = offset_in_folio(folio, pos); nr_bytes = min(folio_size(folio) - folio_offset, num); From 9c954499d43aefac01c5dfb57a82b13d2dcf4b94 Mon Sep 17 00:00:00 2001 From: Jann Horn Date: Tue, 19 May 2026 16:29:38 +0200 Subject: [PATCH 014/257] fuse: reject fuse_notify() pagecache ops on directories The operations FUSE_NOTIFY_STORE and FUSE_NOTIFY_RETRIEVE allow the FUSE daemon to actively write/read pagecache contents. For directories with FOPEN_CACHE_DIR, the pagecache is used as kernel-internal cache storage, and userspace is not supposed to have direct access to this cache - in particular, fuse_parse_cache() will hit WARN_ON() if the cache contains bogus data. Reject FUSE_NOTIFY_STORE and FUSE_NOTIFY_RETRIEVE on anything other than regular files with -EINVAL. Fixes: 5d7bc7e8680c ("fuse: allow using readdir cache") Cc: stable@vger.kernel.org Signed-off-by: Jann Horn Link: https://patch.msgid.link/20260519-fuse-dir-pagecache-v2-1-5428fa48e175@google.com Acked-by: Miklos Szeredi Signed-off-by: Christian Brauner (Amutable) --- fs/fuse/dev.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 08d364ed7d6c..c105aaf9ff5d 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -1793,6 +1793,10 @@ static int fuse_notify_store(struct fuse_conn *fc, unsigned int size, inode = fuse_ilookup(fc, nodeid, NULL); if (!inode) goto out_up_killsb; + if (!S_ISREG(inode->i_mode)) { + err = -EINVAL; + goto out_iput; + } mapping = inode->i_mapping; file_size = i_size_read(inode); @@ -1970,7 +1974,10 @@ static int fuse_notify_retrieve(struct fuse_conn *fc, unsigned int size, inode = fuse_ilookup(fc, nodeid, &fm); if (inode) { - err = fuse_retrieve(fm, inode, &outarg); + if (!S_ISREG(inode->i_mode)) + err = -EINVAL; + else + err = fuse_retrieve(fm, inode, &outarg); iput(inode); } up_read(&fc->killsb); From 90918794a4e2c3b440f8fcf3847765a8b1d81b25 Mon Sep 17 00:00:00 2001 From: Aleksandr Nogikh Date: Thu, 21 May 2026 16:22:40 +0200 Subject: [PATCH 015/257] signal: clear JOBCTL_PENDING_MASK for caller in zap_other_threads() When a multi-threaded process receives a stop signal (e.g., SIGSTOP), do_signal_stop() sets JOBCTL_STOP_PENDING and JOBCTL_STOP_CONSUME on all threads and sets signal->group_stop_count to the number of threads. If one of the threads concurrently calls execve(), de_thread() invokes zap_other_threads() to kill all other threads. zap_other_threads() aborts the pending group stop by resetting signal->group_stop_count to 0 and clears the JOBCTL_PENDING_MASK for all other threads. However, it fails to clear the job control flags for the calling thread. When execve() completes, the calling thread returns to user mode and checks for pending signals. Seeing the stale JOBCTL_STOP_PENDING flag, it calls do_signal_stop(), which invokes task_participate_group_stop(). Since JOBCTL_STOP_CONSUME is still set, it attempts to decrement the already-zero signal->group_stop_count, triggering a warning: sig->group_stop_count == 0 WARNING: CPU: 1 PID: 6475 at kernel/signal.c:373 task_participate_group_stop+0x215/0x2d0 Call Trace: do_signal_stop+0x3be/0x5c0 kernel/signal.c:2619 get_signal+0xa8c/0x1330 kernel/signal.c:2884 arch_do_signal_or_restart+0xbc/0x840 arch/x86/kernel/signal.c:337 exit_to_user_mode_loop+0x8c/0x4d0 kernel/entry/common.c:98 do_syscall_64+0x33e/0xf80 arch/x86/entry/syscall_64.c:100 entry_SYSCALL_64_after_hwframe+0x77/0x7f Fix this race condition by clearing the JOBCTL_PENDING_MASK for the calling thread in zap_other_threads(), ensuring it does not retain any stale job control state after the thread group is destroyed. This aligns with other functions that tear down a thread group and abort group stops, such as zap_process() and complete_signal(), which correctly clear these flags for all threads including the current one. Fixes: 39efa3ef3a37 ("signal: Use GROUP_STOP_PENDING to stop once for a single group stop") Assisted-by: Gemini:gemini-3.1-pro-preview Gemini:gemini-3-flash-preview syzbot Reported-by: syzbot+b109633ea805cac54a61@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=b109633ea805cac54a61 Link: https://syzkaller.appspot.com/ai_job?id=d70208cc-862b-4fe3-bf02-3031e10cd0b3 Signed-off-by: Aleksandr Nogikh Link: https://patch.msgid.link/20260521142240.2973022-1-nogikh@google.com Signed-off-by: Christian Brauner (Amutable) --- kernel/signal.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/signal.c b/kernel/signal.c index 2d102e025883..9c2b32c4d755 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -1338,6 +1338,7 @@ int zap_other_threads(struct task_struct *p) int count = 0; p->signal->group_stop_count = 0; + task_clear_jobctl_pending(p, JOBCTL_PENDING_MASK); for_other_threads(p, t) { task_clear_jobctl_pending(t, JOBCTL_PENDING_MASK); From 4a44b17406cb5a93f90af3df9392b3a45eb336fb Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Thu, 7 May 2026 11:14:42 +0000 Subject: [PATCH 016/257] rust: kasan/kbuild: fix rustc-option when cross-compiling The Makefile version of rustc-option currently checks whether the option exists for the host target instead of the target actually being compiled for. It was done this way in commit 46e24a545cdb ("rust: kasan/kbuild: fix missing flags on first build") to avoid a circular dependency on target.json. However, because of this, rustc-option currently does not function when cross-compiling from x86_64 to aarch64 if CONFIG_SHADOW_CALL_STACK is enabled. This is because KBUILD_RUSTFLAGS contains -Zfixed-x18 under this configuration. Since that flag does not exist on the host target, rustc-option runs into a compilation failure every time, leading to all flags being rejected as unsupported. To fix this, update rustc-option to pass a --target parameter so that the host target is not used. For targets using target.json, use a built-in target that is as close as possible to the target created with target.json to avoid the circular dependency on target.json. One scenario where this causes a boot failure: * Cross-compiled from x86_64 to aarch64. * With CONFIG_SHADOW_CALL_STACK=y * With CONFIG_KASAN_SW_TAGS=y * With CONFIG_KASAN_INLINE=n Then the resulting kernel image will fail to boot when it first calls into Rust code with a crash along the lines of "Unable to handle kernel paging request at virtual address 0ffffffc08541796". This is because the call threshold is not specified, so rustc will inline kasan operations, but the kasan shadow offset is not specified, which leads to the inlined kasan instructions being incorrect. Note that the -Zsanitizer=kernel-hwaddress parameter itself does not lead to a rustc-option failure despite being aarch64-specific because RUSTFLAGS_KASAN has not yet been added to KBUILD_RUSTFLAGS when rustc-option is evaluated by the kasan Makefile. Cc: stable@vger.kernel.org Fixes: 46e24a545cdb ("rust: kasan/kbuild: fix missing flags on first build") Signed-off-by: Alice Ryhl Link: https://patch.msgid.link/20260507-rustc-option-cross-v2-1-2f650a49c2b5@google.com [ Edited slightly: - Reset variable to avoid using the environment. - Use a simply expanded variable flavor for simplicity. - Export variable so that behavior in sub-`make`s is consistent. This matches other variables. - Miguel ] Signed-off-by: Miguel Ojeda --- Makefile | 3 ++- arch/x86/Makefile | 4 ++++ arch/x86/Makefile.um | 8 ++++++++ scripts/Makefile.compiler | 2 +- 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index b7b80e84e1eb..1f205a004437 100644 --- a/Makefile +++ b/Makefile @@ -607,6 +607,7 @@ KBUILD_RUSTFLAGS := $(rust_common_flags) \ -Crelocation-model=static \ -Zfunction-sections=n \ -Wclippy::float_arithmetic +KBUILD_RUSTFLAGS_OPTION_CHKS := KBUILD_AFLAGS_KERNEL := KBUILD_CFLAGS_KERNEL := @@ -643,7 +644,7 @@ export KBUILD_USERCFLAGS KBUILD_USERLDFLAGS export KBUILD_CPPFLAGS NOSTDINC_FLAGS LINUXINCLUDE OBJCOPYFLAGS KBUILD_LDFLAGS export KBUILD_CFLAGS CFLAGS_KERNEL CFLAGS_MODULE -export KBUILD_RUSTFLAGS RUSTFLAGS_KERNEL RUSTFLAGS_MODULE +export KBUILD_RUSTFLAGS RUSTFLAGS_KERNEL RUSTFLAGS_MODULE KBUILD_RUSTFLAGS_OPTION_CHKS export KBUILD_AFLAGS AFLAGS_KERNEL AFLAGS_MODULE export KBUILD_AFLAGS_MODULE KBUILD_CFLAGS_MODULE KBUILD_RUSTFLAGS_MODULE KBUILD_LDFLAGS_MODULE export KBUILD_AFLAGS_KERNEL KBUILD_CFLAGS_KERNEL KBUILD_RUSTFLAGS_KERNEL diff --git a/arch/x86/Makefile b/arch/x86/Makefile index 46fec0b08487..1d526a5d2a83 100644 --- a/arch/x86/Makefile +++ b/arch/x86/Makefile @@ -77,6 +77,10 @@ KBUILD_CFLAGS += -mno-sse -mno-mmx -mno-sse2 -mno-3dnow -mno-avx -mno-sse4a KBUILD_RUSTFLAGS += --target=$(objtree)/scripts/target.json KBUILD_RUSTFLAGS += -Ctarget-feature=-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-avx,-avx2 +# The target.json file is not available when invoking rustc-option, so use the +# built-in target when checking whether flags are supported instead. +KBUILD_RUSTFLAGS_OPTION_CHKS += --target=x86_64-unknown-none + # # CFLAGS for compiling floating point code inside the kernel. # diff --git a/arch/x86/Makefile.um b/arch/x86/Makefile.um index 19c13afa474e..9adecd65639f 100644 --- a/arch/x86/Makefile.um +++ b/arch/x86/Makefile.um @@ -14,6 +14,14 @@ endif KBUILD_RUSTFLAGS += --target=$(objtree)/scripts/target.json +# The target.json file is not available when invoking rustc-option, so use the +# built-in target when checking whether flags are supported instead. +ifeq ($(CONFIG_X86_32),y) +KBUILD_RUSTFLAGS_OPTION_CHKS += --target=i686-unknown-linux-gnu +else +KBUILD_RUSTFLAGS_OPTION_CHKS += --target=x86_64-unknown-linux-gnu +endif + ifeq ($(CONFIG_X86_32),y) START := 0x8048000 diff --git a/scripts/Makefile.compiler b/scripts/Makefile.compiler index ef91910de265..06bbe29c846c 100644 --- a/scripts/Makefile.compiler +++ b/scripts/Makefile.compiler @@ -80,7 +80,7 @@ ld-option = $(call try-run, $(LD) $(KBUILD_LDFLAGS) $(1) -v,$(1),$(2),$(3)) # TODO: remove RUSTC_BOOTSTRAP=1 when we raise the minimum GNU Make version to 4.4 __rustc-option = $(call try-run,\ echo '$(pound)![allow(missing_docs)]$(pound)![feature(no_core)]$(pound)![no_core]' | RUSTC_BOOTSTRAP=1\ - $(1) --sysroot=/dev/null $(filter-out --sysroot=/dev/null --target=%,$(2)) $(3)\ + $(1) --sysroot=/dev/null $(KBUILD_RUSTFLAGS_OPTION_CHKS) $(filter-out --sysroot=/dev/null --target=%target.json,$(2)) $(3)\ --crate-type=rlib --out-dir=$(TMPOUT) --emit=obj=- - >/dev/null,$(3),$(4)) # rustc-option From 29d87434cb91b7689de2917830ca82acfd2770f5 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 26 May 2026 12:19:37 +0200 Subject: [PATCH 017/257] regulator: mt6363: select CONFIG_IRQ_DOMAIN When build-testing this driver without CONFIG_IRQ_DOMAIN causes a compile-time error: drivers/regulator/mt6363-regulator.c: In function 'mt6363_regulator_probe': drivers/regulator/mt6363-regulator.c:884:18: error: implicit declaration of function 'irq_find_host' [-Wimplicit-function-declaration] 884 | domain = irq_find_host(interrupt_parent); | ^~~~~~~~~~~~~ drivers/regulator/mt6363-regulator.c:884:16: error: assignment to 'struct irq_domain *' from 'int' makes pointer from integer without a cast [-Wint-conversion] 884 | domain = irq_find_host(interrupt_parent); | ^ drivers/regulator/mt6363-regulator.c:896:30: error: implicit declaration of function 'irq_create_fwspec_mapping'; did you mean 'irq_create_of_mapping'? [-Wimplicit-function-declaration] 896 | info->virq = irq_create_fwspec_mapping(&fwspec); | ^~~~~~~~~~~~~~~~~~~~~~~~~ | irq_create_of_mapping This is rather hard to trigger because so many other drivers enable IRQ_DOMAIN already, but I ran into this on an s390 randconfig build. Ensure this is always enabled using a Kconfig 'select IRQ_DOMAIN' entry, as we do for all other users of this. Fixes: 3c36965df808 ("regulator: Add support for MediaTek MT6363 SPMI PMIC Regulators") Signed-off-by: Arnd Bergmann Reviewed-by: AngeloGioacchino Del Regno Link: https://patch.msgid.link/20260526102003.2527570-1-arnd@kernel.org Signed-off-by: Mark Brown --- drivers/regulator/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 78076ac6eac4..87554ab92801 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -977,6 +977,7 @@ config REGULATOR_MT6363 tristate "MT6363 SPMI PMIC regulator driver" depends on SPMI select REGMAP_SPMI + select IRQ_DOMAIN help Say Y here to enable support for regulators found in the MediaTek MT6363 SPMI PMIC. From 3cd07ee35a66038fd1a643632bfc057645e07c9a Mon Sep 17 00:00:00 2001 From: Zhan Xusheng Date: Tue, 26 May 2026 10:21:31 +0800 Subject: [PATCH 018/257] cpufreq/amd-pstate: drop stale @epp_cached kdoc Commit 4e16c1175238 ("cpufreq/amd-pstate: Stop caching EPP") removed the epp_cached field from struct amd_cpudata in favour of always reading from cppc_req_cached, but the kdoc above the struct still documents @epp_cached. Drop the now-stale @epp_cached entry. Reviewed-by: Mario Limonciello (AMD) Fixes: 4e16c1175238 ("cpufreq/amd-pstate: Stop caching EPP") Signed-off-by: Zhan Xusheng Link: https://lore.kernel.org/r/20260526022131.1302373-1-zhanxusheng@xiaomi.com Signed-off-by: Mario Limonciello (AMD) --- drivers/cpufreq/amd-pstate.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/cpufreq/amd-pstate.h b/drivers/cpufreq/amd-pstate.h index e4722e54387b..23e8baa05849 100644 --- a/drivers/cpufreq/amd-pstate.h +++ b/drivers/cpufreq/amd-pstate.h @@ -84,7 +84,6 @@ struct amd_aperf_mperf { * @hw_prefcore: check whether HW supports preferred core featue. * Only when hw_prefcore and early prefcore param are true, * AMD P-State driver supports preferred core featue. - * @epp_cached: Cached CPPC energy-performance preference value * @policy: Cpufreq policy value * @suspended: If CPU core if offlined * @epp_default_ac: Default EPP value for AC power source From 880fa3a1e5c493d0deafe9153f8c2bed427b9428 Mon Sep 17 00:00:00 2001 From: Shivam Kalra Date: Sat, 23 May 2026 00:24:32 +0530 Subject: [PATCH 019/257] rust: helpers: add is_vmalloc_addr wrapper for NOMMU builds Commit 47ac2a4b5cd8 ("rust: kvec: implement shrink_to for KVVec") introduced a call to bindings::is_vmalloc_addr(). However, this fails to compile on architectures where CONFIG_MMU is disabled, resulting in the following build error: error[E0425]: cannot find function `is_vmalloc_addr` in crate `bindings` --> rust/kernel/alloc/kvec.rs:781:32 | 781 | if !unsafe { bindings::is_vmalloc_addr(self.ptr.as_ptr().cast()) } { | ^^^^^^^^^^^^^^^ not found in `bindings` When CONFIG_MMU is not set, is_vmalloc_addr() is defined as a static inline function in that unconditionally returns false. Because bindgen skips static inline functions when generating bindings, the symbol is completely missing from the Rust bindings crate. Fix this by providing a C helper wrapper, rust_helper_is_vmalloc_addr(), in rust/helpers/vmalloc.c. This ensures the function is reliably exposed to Rust regardless of the MMU configuration. On NOMMU builds, this allows KVVec::shrink_to() to successfully compile and correctly route all allocations through the kmalloc realloc path. Fixes: 47ac2a4b5cd8 ("rust: kvec: implement shrink_to for KVVec") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202605220811.LRplxeBR-lkp@intel.com/ Signed-off-by: Shivam Kalra Reviewed-by: Danilo Krummrich Reviewed-by: Alice Ryhl Link: https://patch.msgid.link/20260523-is-vmalloc-addr-build-fix-v1-1-73c919440c41@zohomail.in [ Pasted exact compiler output and expanded it. - Miguel ] Signed-off-by: Miguel Ojeda --- rust/helpers/vmalloc.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/rust/helpers/vmalloc.c b/rust/helpers/vmalloc.c index 326b030487a2..6aed13292313 100644 --- a/rust/helpers/vmalloc.c +++ b/rust/helpers/vmalloc.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 +#include #include __rust_helper void *__must_check __realloc_size(2) @@ -8,3 +9,8 @@ rust_helper_vrealloc_node_align(const void *p, size_t size, unsigned long align, { return vrealloc_node_align(p, size, align, flags, node); } + +__rust_helper bool rust_helper_is_vmalloc_addr(const void *x) +{ + return is_vmalloc_addr(x); +} From 83eb00f31eb1b10735d48e469df72cc2b0e06f6d Mon Sep 17 00:00:00 2001 From: Dexuan Cui Date: Wed, 27 May 2026 12:21:01 -0700 Subject: [PATCH 020/257] hyperv: Clean up and fix the guest ID comment in hvgdk.h Change the "64 bit" to "64-bit", and the "Os" to "OS". Remove the obsolete paragraph since the guideline has been published in the Hypervisor Top Level Functional Specification for many years. The "OS Type" is 0x1 for Linux, not 0x100. No functional change. Fixes: 83ba0c4f3f31 ("Drivers: hv: Cleanup the guest ID computation") Signed-off-by: Dexuan Cui Reviewed-by: Hamza Mahfooz Signed-off-by: Wei Liu --- include/hyperv/hvgdk.h | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/include/hyperv/hvgdk.h b/include/hyperv/hvgdk.h index 384c3f3ff4a5..f538144280ca 100644 --- a/include/hyperv/hvgdk.h +++ b/include/hyperv/hvgdk.h @@ -10,18 +10,12 @@ /* * The guest OS needs to register the guest ID with the hypervisor. - * The guest ID is a 64 bit entity and the structure of this ID is + * The guest ID is a 64-bit entity and the structure of this ID is * specified in the Hyper-V TLFS specification. * - * While the current guideline does not specify how Linux guest ID(s) - * need to be generated, our plan is to publish the guidelines for - * Linux and other guest operating systems that currently are hosted - * on Hyper-V. The implementation here conforms to this yet - * unpublished guidelines. - * * Bit(s) * 63 - Indicates if the OS is Open Source or not; 1 is Open Source - * 62:56 - Os Type; Linux is 0x100 + * 62:56 - OS Type; Linux is 0x1 * 55:48 - Distro specific identification * 47:16 - Linux kernel version number * 15:0 - Distro specific identification From 840b740a35bf969734e0f2e44c21289fdd03079e Mon Sep 17 00:00:00 2001 From: Michael Kelley Date: Tue, 26 May 2026 07:13:04 -0700 Subject: [PATCH 021/257] mshv: Add conditional VMBus dependency When the VMBus driver is not part of the kernel (CONFIG_HYPERV_VMBUS=n), the MSHV root driver fails to link: ERROR: modpost: "hv_vmbus_exists" [drivers/hv/mshv_root.ko] undefined! Fix this while meeting these requirements: * It must be possible to include the MSHV root driver without the VMBus driver. In such case, the MSHV root driver can be built-in to the kernel image, or it can be built as a separate module. * If both the MSHV root driver and the VMBus driver are present, the MSHV root driver and VMBus driver can both be built-in, or they can both be separate modules. Or the MSHV root driver can be a module while the VMBus driver can be built-in, but the reverse is disallowed. Regardless of the build choices, the VMBus driver must be loaded before the MSHV driver in order for the SynIC to be managed properly (see comments in the MSHV SynIC code). The fix has two parts: * Add a Kconfig entry for MSHV_ROOT to depend on HYPERV_VMBUS if HYPERV_VMBUS is present. The entry disallows MSHV_ROOT being built-in when HYPERV_VMBUS is a module, but without requiring that HYPERV_VMBUS be built. * Add a stub implementation of hv_vmbus_exists() for when the VMBus driver is not present so that the MSHV root driver has no module dependency on VMBus. When the VMBus driver *is* present, the module dependency ensures that the VMBus driver loads first when both are built as modules. Existing code ensures that the VMBus driver loads first if it is built-in. The VMBus driver uses subsys_initcall(), which is initcall level 4. The MSHV root driver uses module_init(), which becomes device_init() when built-in, and device_init() is initcall level 6. Reported-by: Arnd Bergmann Closes: https://lore.kernel.org/all/20260520074044.923728-1-arnd@kernel.org/ Signed-off-by: Michael Kelley Acked-by: Arnd Bergmann Reviewed-by: Jork Loeser Reviewed-by: Hardik Garg Signed-off-by: Wei Liu --- drivers/hv/Kconfig | 1 + include/linux/hyperv.h | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/drivers/hv/Kconfig b/drivers/hv/Kconfig index 2d0b3fcb0ff8..aa11bcefddf2 100644 --- a/drivers/hv/Kconfig +++ b/drivers/hv/Kconfig @@ -74,6 +74,7 @@ config MSHV_ROOT # e.g. When withdrawing memory, the hypervisor gives back 4k pages in # no particular order, making it impossible to reassemble larger pages depends on PAGE_SIZE_4KB + depends on HYPERV_VMBUS if HYPERV_VMBUS select EVENTFD select VIRT_XFER_TO_GUEST_WORK select HMM_MIRROR diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 41a3d82f0722..734b7ef98f4d 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -1304,7 +1304,11 @@ static inline void *hv_get_drvdata(struct hv_device *dev) struct device *hv_get_vmbus_root_device(void); +#if IS_ENABLED(CONFIG_HYPERV_VMBUS) bool hv_vmbus_exists(void); +#else +static inline bool hv_vmbus_exists(void) { return false; } +#endif struct hv_ring_buffer_debug_info { u32 current_interrupt_mask; From c53763aa2b9c96fbabee68ebe1e13074cb09bfb2 Mon Sep 17 00:00:00 2001 From: Can Peng Date: Wed, 20 May 2026 15:16:32 +0800 Subject: [PATCH 022/257] mshv: use kmalloc_array in mshv_root_scheduler_init Replace kmalloc() with kmalloc_array() to prevent potential overflow, as recommended in Documentation/process/deprecated.rst. No functional change. Signed-off-by: Can Peng Signed-off-by: Wei Liu --- drivers/hv/mshv_root_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c index bd1359eb58dd..146726cc4e9b 100644 --- a/drivers/hv/mshv_root_main.c +++ b/drivers/hv/mshv_root_main.c @@ -2241,7 +2241,7 @@ static int mshv_root_scheduler_init(unsigned int cpu) outputarg = (void **)this_cpu_ptr(root_scheduler_output); /* Allocate two consecutive pages. One for input, one for output. */ - p = kmalloc(2 * HV_HYP_PAGE_SIZE, GFP_KERNEL); + p = kmalloc_array(2, HV_HYP_PAGE_SIZE, GFP_KERNEL); if (!p) return -ENOMEM; From 016a25e4b0df4d77e7c258edee4aaf982e4ee809 Mon Sep 17 00:00:00 2001 From: Dexuan Cui Date: Thu, 7 May 2026 14:28:38 -0700 Subject: [PATCH 023/257] Drivers: hv: vmbus: Improve the logic of reserving fb_mmio on Gen2 VMs If vmbus_reserve_fb() in the kdump/kexec kernel fails to properly reserve the framebuffer MMIO range (which is below 4GB) due to a Gen2 VM's screen.lfb_base being zero [1], there is an MMIO conflict between the drivers hyperv-drm and pci-hyperv: when the driver pci-hyperv's hv_allocate_config_window() calls vmbus_allocate_mmio() to get an MMIO range, typically it gets a 32-bit MMIO range that overlaps with the framebuffer MMIO range, and later hv_pci_enter_d0() fails with an error message "PCI Pass-through VSP failed D0 Entry with status" since the host thinks that PCI devices must not use MMIO space that the host has assigned to the framebuffer. This is especially an issue if pci-hyperv is built-in and hyperv-drm is built as a module. Consequently, the kdump/kexec kernel fails to detect PCI devices via pci-hyperv, and may fail to mount the root file system, which may reside in a NVMe disk. The issue described here has existed for SR-IOV VF NICs since day one of the pci-hyperv driver, and has been worked around on x64 when possible. With the recent introduction of ARM64 VMs that boot from NVMe, there is no workaround, so we need a formal fix. On Gen2 VMs, if the screen.lfb_base is 0 in the kdump/kexec kernel [1], fall back to the low MMIO base, which should be equal to the framebuffer MMIO base [2] (the statement is true according to my testing on x64 Windows Server 2016, and on x64 and ARM64 Windows Server 2025 and on Azure. I checked with the Hyper-V team and they said the statement should continue to be true for Gen2 VMs). In the first kernel, screen.lfb_base is not 0; if the user specifies a very high resolution, it's not enough to only reserve 8MB: let's always reserve half of the space below 4GB, but cap the reservation to 128MB, which is the required framebuffer size of the highest resolution 7680*4320 supported by Hyper-V. While at it, fix the comparison "end > VTPM_BASE_ADDRESS" by changing the > to >=. Here the 'end' is an inclusive end (typically, it's 0xFFFF_FFFF for the low MMIO range). Note: vmbus_reserve_fb() now also reserves an MMIO range at the beginning of the low MMIO range on CVMs, which have no framebuffers (the 'screen.lfb_base' in vmbus_reserve_fb() is 0 for CVMs), just in case the host might treat the beginning of the low MMIO range specially [3]. BTW, the OpenHCL kernel is not affected by the change, because that kernel boots with DeviceTree rather than ACPI (so vmbus_reserve_fb() won't run there), and there is no framebuffer device for that kernel. Note: normally Gen1 VMs don't have the MMIO conflict issue because the framebuffer MMIO range (which is hardcoded to base=4GB-128MB and size=64MB for Gen1 VMs by the host) is always reported via the legacy PCI graphics device's BAR, so the kdump/kexec kernel can reserve the 64MB MMIO range; however, if the VM is configured to use a very high resolution and the required framebuffer size exceeds 64MB (AFAIK, in practice, this isn't a typical configuration by users), the hyperv-drm driver may need to allocate an MMIO range above 4GB and change the framebuffer MMIO location to the allocated MMIO range -- in this case, there can still be issues [4] which can't be easily fixed: any possible affected Gen1 users would have to use a resolution whose framebuffer size is <= 64MB, or switch to Gen2 VMs. [1] https://lore.kernel.org/all/SA1PR21MB692176C1BC53BFC9EAE5CF8EBF51A@SA1PR21MB6921.namprd21.prod.outlook.com/ [2] https://lore.kernel.org/all/SA1PR21MB69218F955B62DFF62E3E88D2BF222@SA1PR21MB6921.namprd21.prod.outlook.com/ [3] https://lore.kernel.org/all/SN6PR02MB415726B17D5A6027CD1717E8D4342@SN6PR02MB4157.namprd02.prod.outlook.com/ [4] https://lore.kernel.org/all/SA1PR21MB69213486F821CA5A2C793C81BF342@SA1PR21MB6921.namprd21.prod.outlook.com/ Fixes: 4daace0d8ce8 ("PCI: hv: Add paravirtual PCI front-end for Microsoft Hyper-V VMs") CC: stable@vger.kernel.org Reviewed-by: Michael Kelley Tested-by: Krister Johansen Tested-by: Matthew Ruffell Signed-off-by: Dexuan Cui Signed-off-by: Wei Liu --- drivers/hv/vmbus_drv.c | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index c9eeb2ec365d..b80a35c778ab 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -2327,8 +2327,8 @@ static acpi_status vmbus_walk_resources(struct acpi_resource *res, void *ctx) return AE_NO_MEMORY; /* If this range overlaps the virtual TPM, truncate it. */ - if (end > VTPM_BASE_ADDRESS && start < VTPM_BASE_ADDRESS) - end = VTPM_BASE_ADDRESS; + if (end >= VTPM_BASE_ADDRESS && start < VTPM_BASE_ADDRESS) + end = VTPM_BASE_ADDRESS - 1; new_res->name = "hyperv mmio"; new_res->flags = IORESOURCE_MEM; @@ -2395,6 +2395,7 @@ static void vmbus_mmio_remove(void) static void __maybe_unused vmbus_reserve_fb(void) { resource_size_t start = 0, size; + resource_size_t low_mmio_base; struct pci_dev *pdev; if (efi_enabled(EFI_BOOT)) { @@ -2402,6 +2403,24 @@ static void __maybe_unused vmbus_reserve_fb(void) if (IS_ENABLED(CONFIG_SYSFB)) { start = sysfb_primary_display.screen.lfb_base; size = max_t(__u32, sysfb_primary_display.screen.lfb_size, 0x800000); + + low_mmio_base = hyperv_mmio->start; + if (!low_mmio_base || upper_32_bits(low_mmio_base) || + (start && start < low_mmio_base)) { + pr_warn("Unexpected low mmio base %pa\n", &low_mmio_base); + } else { + /* + * If the kdump/kexec or CVM kernel's lfb_base + * is 0, fall back to the low mmio base. + */ + if (!start) + start = low_mmio_base; + /* + * Reserve half of the space below 4GB for high + * resolutions, but cap the reservation to 128MB. + */ + size = min((SZ_4G - start) / 2, SZ_128M); + } } } else { /* Gen1 VM: get FB base from PCI */ @@ -2422,8 +2441,10 @@ static void __maybe_unused vmbus_reserve_fb(void) pci_dev_put(pdev); } - if (!start) + if (!start) { + pr_warn("Unexpected framebuffer mmio base of zero\n"); return; + } /* * Make a claim for the frame buffer in the resource tree under the @@ -2433,6 +2454,8 @@ static void __maybe_unused vmbus_reserve_fb(void) */ for (; !fb_mmio && (size >= 0x100000); size >>= 1) fb_mmio = __request_region(hyperv_mmio, start, size, fb_mmio_name, 0); + + pr_info("hv_mmio=%pR,%pR fb=%pR\n", hyperv_mmio, hyperv_mmio->sibling, fb_mmio); } /** From 98e0fc32e53dd62cd38a0d67eaf5846ae20078cc Mon Sep 17 00:00:00 2001 From: "Anirudh Rayabharam (Microsoft)" Date: Wed, 13 May 2026 13:25:56 +0000 Subject: [PATCH 024/257] mshv: support 1G hugepages by passing them as 2M-aligned chunks The hypervisor's map GPA hypercall coalesces contiguous 2M-aligned chunks into 1G mappings when alignment permits, so the driver can support 1G hugepages by feeding them in as 2M chunks. Note that this is the only way to make 1G mappings; there is no way to directly map a 1G hugepage using the hypercall. Always emit a 2M (PMD_ORDER) stride for the huge-page case. The hypercall has no 1G stride, so 1G folios are processed as a sequence of 2M chunks. Folios whose order is less than PMD_ORDER (e.g. mTHP) fall back to single-page stride; mapping them as 2M would fail in the hypervisor anyway. Assisted-by: Copilot-CLI:claude-opus-4.7 Signed-off-by: Anirudh Rayabharam (Microsoft) Acked-by: Stanislav Kinsburskii Reviewed-by: Michael Kelley Signed-off-by: Wei Liu --- drivers/hv/mshv_regions.c | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/drivers/hv/mshv_regions.c b/drivers/hv/mshv_regions.c index fdffd4f002f6..6d65e5b42152 100644 --- a/drivers/hv/mshv_regions.c +++ b/drivers/hv/mshv_regions.c @@ -29,29 +29,27 @@ * Uses huge page stride if the backing page is huge and the guest mapping * is properly aligned; otherwise falls back to single page stride. * - * Return: Stride in pages, or -EINVAL if page order is unsupported. + * Return: Stride in pages. */ -static int mshv_chunk_stride(struct page *page, - u64 gfn, u64 page_count) +static unsigned int mshv_chunk_stride(struct page *page, u64 gfn, + u64 page_count) { - unsigned int page_order; + unsigned int page_order = folio_order(page_folio(page)); /* * Use single page stride by default. For huge page stride, the - * page must be compound and point to the head of the compound - * page, and both gfn and page_count must be huge-page aligned. + * folio order must be at least PMD_ORDER, the page's PFN must be + * 2M-aligned (so that a 2M-aligned tail page of a larger folio is + * acceptable), and both gfn and page_count must be 2M-aligned. */ - if (!PageCompound(page) || !PageHead(page) || + if (page_order < PMD_ORDER || + !IS_ALIGNED(page_to_pfn(page), PTRS_PER_PMD) || !IS_ALIGNED(gfn, PTRS_PER_PMD) || !IS_ALIGNED(page_count, PTRS_PER_PMD)) return 1; - page_order = folio_order(page_folio(page)); - /* The hypervisor only supports 2M huge page */ - if (page_order != PMD_ORDER) - return -EINVAL; - - return 1 << page_order; + /* Use 2M stride always i.e. process 1G folios as 2M chunks */ + return 1 << PMD_ORDER; } /** @@ -86,15 +84,14 @@ static long mshv_region_process_chunk(struct mshv_mem_region *region, u64 gfn = region->start_gfn + page_offset; u64 count; struct page *page; - int stride, ret; + unsigned int stride; + int ret; page = region->mreg_pages[page_offset]; if (!page) return -EINVAL; stride = mshv_chunk_stride(page, gfn, page_count); - if (stride < 0) - return stride; /* Start at stride since the first stride is validated */ for (count = stride; count < page_count; count += stride) { From e824bbd4d224cce4b5fb59cc9dcd3447fe0b7e44 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Mon, 25 May 2026 16:23:45 +1000 Subject: [PATCH 025/257] VFS: fix possible failure to unlock in nfsd4_create_file() atomic_create() in fs/namei.c drops the reference to the dentry when it returns an error. This behaviour was imported into dentry_create() so that it will drop the reference if an error is returned from atomic_create(), though not if vfs_create() returns an error (in the case where ->atomic_create is not supported). The caller - nfsd4_create_file() - is made aware of this by checking path->dentry, which will either be a counted reference to a dentry, or an error pointer. However the change to use start_creating()/end_creating() (which landed shortly before the dentry_create() change landed, though was likely developed around the same time) means that nfsd4_create_file() *needs* a valid dentry so that it can unlock the parent. The net result is that if NFSD exports a filesystem which uses ->atomic_create, and if a call to ->atomic_create returns an error, then nfsd4_create_file() will pass an error pointer to end_creating() and the parent will not be unlocked. Fix this by changing dentry_create() to make sure path->dentry is always a valid dentry, never an error-pointer. The actual error is already returned a different way. Note that if ->atomic_create() returns a different dentry (which may not be possible in practice) we are guaranteed (because it is only ever provided by d_spliace_alias()) that it will have the same d_parent and so it will have the same effect when passed to end_creating(). Fixes: 64a989dbd144 ("VFS/knfsd: Teach dentry_create() to use atomic_open()") Signed-off-by: NeilBrown Link: https://patch.msgid.link/177969022571.3379282.16448744624428323496@noble.neil.brown.name Reviewed-by: Jeff Layton Reviewed-by: Benjamin Coddington Reviewed-by: Jori Koolstra Signed-off-by: Christian Brauner (Amutable) --- fs/namei.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/fs/namei.c b/fs/namei.c index c7fac83c9a85..4787244ca4a7 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -5024,6 +5024,7 @@ struct file *dentry_create(struct path *path, int flags, umode_t mode, { struct file *file __free(fput) = NULL; struct dentry *dentry = path->dentry; + struct dentry *orig_dentry = dentry; struct dentry *dir = dentry->d_parent; struct inode *dir_inode = d_inode(dir); struct mnt_idmap *idmap; @@ -5043,9 +5044,18 @@ struct file *dentry_create(struct path *path, int flags, umode_t mode, if (create_error) flags &= ~O_CREAT; + /* atomic_open will dput(dentry) on error */ + dget(orig_dentry); dentry = atomic_open(path, dentry, file, flags, mode); error = PTR_ERR_OR_ZERO(dentry); + if (IS_ERR(dentry)) + /* keep the original */ + dentry = orig_dentry; + else + /* Drop the extra reference */ + dput(orig_dentry); + if (unlikely(create_error) && error == -ENOENT) error = create_error; From 89c4a1167f3a0a0efd2ec3e1801036d2eb65ae1a Mon Sep 17 00:00:00 2001 From: Arpith Kalaginanavoor Date: Tue, 26 May 2026 05:38:58 -0700 Subject: [PATCH 026/257] fs/qnx6: fix pointer arithmetic in directory iteration The conversion to qnx6_get_folio() in commit b2aa61556fcf ("qnx6: Convert qnx6_get_page() to qnx6_get_folio()") introduced a regression in directory iteration. The pointer 'de' and the 'limit' address were calculated using byte offsets from a char pointer without scaling by the size of a QNX6 directory entry. This causes the driver to read from incorrect memory offsets, leading to "invalid direntry size" errors and premature termination of directory scans. Fix this by casting 'kaddr' to 'struct qnx6_dir_entry *' before applying the offset and last_entry(...) increments. This allows the compiler to correctly scale the pointer arithmetic by the 32-byte stride of the directory entry structure. Fixes: b2aa61556fcf ("qnx6: Convert qnx6_get_page() to qnx6_get_folio()") Cc: stable@vger.kernel.org Signed-off-by: Arpith Kalaginanavoor Link: https://patch.msgid.link/20260526123858.1683035-1-arpithk@nvidia.com Signed-off-by: Christian Brauner (Amutable) --- fs/qnx6/dir.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/qnx6/dir.c b/fs/qnx6/dir.c index 135fb42f6936..56bbaffe4b44 100644 --- a/fs/qnx6/dir.c +++ b/fs/qnx6/dir.c @@ -132,16 +132,16 @@ static int qnx6_readdir(struct file *file, struct dir_context *ctx) struct qnx6_dir_entry *de; struct folio *folio; char *kaddr = qnx6_get_folio(inode, n, &folio); - char *limit; + struct qnx6_dir_entry *limit; if (IS_ERR(kaddr)) { pr_err("%s(): read failed\n", __func__); ctx->pos = (n + 1) << PAGE_SHIFT; return PTR_ERR(kaddr); } - de = (struct qnx6_dir_entry *)(kaddr + offset); - limit = kaddr + last_entry(inode, n); - for (; (char *)de < limit; de++, ctx->pos += QNX6_DIR_ENTRY_SIZE) { + de = (struct qnx6_dir_entry *)kaddr + offset; + limit = (struct qnx6_dir_entry *)kaddr + last_entry(inode, n); + for (; de < limit; de++, ctx->pos += QNX6_DIR_ENTRY_SIZE) { int size = de->de_size; u32 no_inode = fs32_to_cpu(sbi, de->de_inode); From 1aee05e814d292064bf5fa15733741040cdc48ba Mon Sep 17 00:00:00 2001 From: Gao Xiang Date: Fri, 22 May 2026 16:27:16 +0800 Subject: [PATCH 027/257] erofs: fix use-after-free on sbi->sync_decompress z_erofs_decompress_kickoff() can race with filesystem unmount, causing a use-after-free on sbi->sync_decompress. When I/O completes, z_erofs_endio() calls z_erofs_decompress_kickoff() to queue z_erofs_decompressqueue_work() asynchronously. Then, after all folios are unlocked, unmount workflow can proceed and sbi will be freed before accessing to sbi->sync_decompress. Thread (unmount) I/O completion kworker queue_work z_erofs_decompressqueue_work (all folios are unlocked) cleanup_mnt .. erofs_kill_sb erofs_sb_free kfree(sbi) access sbi->sync_decompress // UAF!! Fixes: 40452ffca3c1 ("erofs: add sysfs node to control sync decompression strategy") Reported-by: syzbot+52bae5c495dbe261a0bc@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=52bae5c495dbe261a0bc Reviewed-by: Chao Yu Reviewed-by: Jianan Huang Signed-off-by: Gao Xiang --- fs/erofs/zdata.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/erofs/zdata.c b/fs/erofs/zdata.c index 27ab7bd844ec..c6240dccbb0f 100644 --- a/fs/erofs/zdata.c +++ b/fs/erofs/zdata.c @@ -1455,6 +1455,9 @@ static void z_erofs_decompress_kickoff(struct z_erofs_decompressqueue *io, if (atomic_add_return(bios, &io->pending_bios)) return; if (z_erofs_in_atomic()) { + /* See `sync_decompress` in sysfs-fs-erofs for more details */ + if (sbi->sync_decompress == EROFS_SYNC_DECOMPRESS_AUTO) + sbi->sync_decompress = EROFS_SYNC_DECOMPRESS_FORCE_ON; #ifdef CONFIG_EROFS_FS_PCPU_KTHREAD struct kthread_worker *worker; @@ -1471,9 +1474,6 @@ static void z_erofs_decompress_kickoff(struct z_erofs_decompressqueue *io, #else queue_work(z_erofs_workqueue, &io->u.work); #endif - /* See `sync_decompress` in sysfs-fs-erofs for more details */ - if (sbi->sync_decompress == EROFS_SYNC_DECOMPRESS_AUTO) - sbi->sync_decompress = EROFS_SYNC_DECOMPRESS_FORCE_ON; return; } gfp_flag = memalloc_noio_save(); From ecf3edd349dfabee9bc8a46c5ff91c9ebd858d48 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 20 May 2026 09:38:44 +0200 Subject: [PATCH 028/257] crypto: s390 - add select CRYPTO_AEAD for aes The aes driver registers both skcipher and aead algorithms, but when aead is not enabled this causes a link failure: s390-linux-ld: arch/s390/crypto/aes_s390.o: in function `aes_s390_fini': arch/s390/crypto/aes_s390.c:969:(.text+0x115e): undefined reference to `crypto_unregister_aead' s390-linux-ld: arch/s390/crypto/aes_s390.o: in function `aes_s390_init': arch/s390/crypto/aes_s390.c:1028:(.init.text+0x294): undefined reference to `crypto_register_aead' Add the missing 'select' statement. Fixes: bf7fa038707c ("s390/crypto: add s390 platform specific aes gcm support.") Signed-off-by: Arnd Bergmann Reviewed-by: Harald Freudenberger Signed-off-by: Herbert Xu --- arch/s390/crypto/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/s390/crypto/Kconfig b/arch/s390/crypto/Kconfig index 00051d27db95..228570a1b233 100644 --- a/arch/s390/crypto/Kconfig +++ b/arch/s390/crypto/Kconfig @@ -4,6 +4,7 @@ menu "Accelerated Cryptographic Algorithms for CPU (s390)" config CRYPTO_AES_S390 tristate "Ciphers: AES, modes: ECB, CBC, CTR, XTS, GCM" + select CRYPTO_AEAD select CRYPTO_SKCIPHER help AEAD cipher: AES with GCM From bc95fa240a1b8ae64d3dabe87cbe103b912afc45 Mon Sep 17 00:00:00 2001 From: Hans Holmberg Date: Mon, 18 May 2026 08:52:24 +0200 Subject: [PATCH 029/257] xfs: handle racing deletions in xfs_zone_gc_iter_irec Under heavy garbage collection pressure from RocksDB workloads, filesystem shutdowns can occur in xfs_zone_gc_iter_irec when xfs_iget() returns -EINVAL for deleted files. Fix this by handling -EINVAL just like we handle -ENOENT, allowing zone GC to safely ignore stale mappings. Fixes: 080d01c41d44 ("xfs: implement zoned garbage collection") Signed-off-by: Hans Holmberg Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/xfs_zone_gc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/xfs/xfs_zone_gc.c b/fs/xfs/xfs_zone_gc.c index c8a1d5c0332c..f03211e4354a 100644 --- a/fs/xfs/xfs_zone_gc.c +++ b/fs/xfs/xfs_zone_gc.c @@ -400,7 +400,7 @@ xfs_zone_gc_iter_irec( /* * If the inode was already deleted, skip over it. */ - if (error == -ENOENT) { + if (error == -ENOENT || error == -EINVAL) { iter->rec_idx++; goto retry; } From 97bdbf2613eb89428b739ce09d1a6e1c8435a286 Mon Sep 17 00:00:00 2001 From: Dai Ngo Date: Tue, 19 May 2026 17:32:58 -0700 Subject: [PATCH 030/257] xfs: fix use of uninitialized imap in xfs_fs_map_blocks error path xfs_fs_map_blocks() acquires the data map lock and then calls xfs_bmapi_read(). If xfs_bmapi_read() fails, the function currently still falls through to xfs_bmbt_to_iomap(), which consumes an uninitialized imap record and may return invalid data to the caller. Fix this by releasing the data map lock and returning immediately when xfs_bmapi_read() reports an error. This prevents xfs_bmbt_to_iomap() from being called with an uninitialized xfs_bmbt_irec. Fixes: 527851124d10f ("xfs: implement pNFS export operations") Signed-off-by: Dai Ngo Reviewed-by: Carlos Maiolino Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/xfs_pnfs.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/fs/xfs/xfs_pnfs.c b/fs/xfs/xfs_pnfs.c index 221e55887a2a..b792e066b403 100644 --- a/fs/xfs/xfs_pnfs.c +++ b/fs/xfs/xfs_pnfs.c @@ -174,12 +174,15 @@ xfs_fs_map_blocks( lock_flags = xfs_ilock_data_map_shared(ip); error = xfs_bmapi_read(ip, offset_fsb, end_fsb - offset_fsb, &imap, &nimaps, bmapi_flags); + if (error) { + xfs_iunlock(ip, lock_flags); + goto out_unlock; + } seq = xfs_iomap_inode_sequence(ip, 0); ASSERT(!nimaps || imap.br_startblock != DELAYSTARTBLOCK); - if (!error && write && - (!nimaps || imap.br_startblock == HOLESTARTBLOCK)) { + if (write && (!nimaps || imap.br_startblock == HOLESTARTBLOCK)) { if (offset + length > XFS_ISIZE(ip)) end_fsb = xfs_iomap_eof_align_last_fsb(ip, end_fsb); else if (nimaps && imap.br_startblock == HOLESTARTBLOCK) From 36ca6f11424a5b6d92b88df37c40bf2fe825d5a0 Mon Sep 17 00:00:00 2001 From: Dai Ngo Date: Tue, 19 May 2026 17:32:59 -0700 Subject: [PATCH 031/257] xfs: fix overlapping extents returned for pNFS LAYOUTGET xfs_fs_map_blocks() currently passes XFS_BMAPI_ENTIRE to xfs_bmapi_read(), which causes the bmap code to expand the mapping to cover the entire extent rather than the requested range. A single LAYOUTGET request from the client can cause the server to issue multiple calls to xfs_fs_map_blocks() for different offsets within the same extent. Because the use of XFS_BMAPI_ENTIRE flag, these calls can produce overlapping mappings. As a result, the LAYOUTGET reply sent to the NFS client may contain overlapping extents. This creates ambiguity in extent selection for a given file range, which can lead to incorrect device selection, inconsistent handling of datastate, and ultimately data corruption or protocol violations on the client side. Problem discovered with xfstest generic/075 test using NFSv4.2 mount with SCSI layout. Fix this by replacing the XFS_BMAPI_ENTIRE flag with '0' so that xfs_bmapi_read() returns only the mapping for the requested range. Fixes: cc6c40e09d7b1 ("NFSD/blocklayout: Support multiple extents per LAYOUTGET"). Signed-off-by: Dai Ngo Reviewed-by: Carlos Maiolino Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/xfs_pnfs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/xfs/xfs_pnfs.c b/fs/xfs/xfs_pnfs.c index b792e066b403..d92993367ab6 100644 --- a/fs/xfs/xfs_pnfs.c +++ b/fs/xfs/xfs_pnfs.c @@ -118,7 +118,6 @@ xfs_fs_map_blocks( struct xfs_bmbt_irec imap; xfs_fileoff_t offset_fsb, end_fsb; loff_t limit; - int bmapi_flags = XFS_BMAPI_ENTIRE; int nimaps = 1; uint lock_flags; int error = 0; @@ -172,8 +171,9 @@ xfs_fs_map_blocks( offset_fsb = XFS_B_TO_FSBT(mp, offset); lock_flags = xfs_ilock_data_map_shared(ip); + /* request mappings for the specified range only */ error = xfs_bmapi_read(ip, offset_fsb, end_fsb - offset_fsb, - &imap, &nimaps, bmapi_flags); + &imap, &nimaps, 0); if (error) { xfs_iunlock(ip, lock_flags); goto out_unlock; From fcf4faba9f986b3bb528da11913c9ec5d6e8f689 Mon Sep 17 00:00:00 2001 From: Yingjie Gao Date: Wed, 27 May 2026 12:31:33 +0800 Subject: [PATCH 032/257] xfs: fix error returns in CoW fork repair xrep_cow_find_bad() returns success after the cleanup labels even if AG setup, btree queries, or bitmap updates failed. This can make repair continue with an incomplete bad-file-offset bitmap instead of stopping at the original error. The force-rebuild path has a related cleanup problem. If xrep_cow_mark_file_range() fails, the function returns directly and skips the scrub AG context and perag cleanup. Let the force-rebuild path fall through to the existing cleanup code and return the saved error after cleanup. Fixes: dbbdbd008632 ("xfs: repair problems in CoW forks") Cc: # v6.8 Signed-off-by: Yingjie Gao Reviewed-by: "Darrick J. Wong" Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/cow_repair.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/fs/xfs/scrub/cow_repair.c b/fs/xfs/scrub/cow_repair.c index bffc4666ce60..a6ff09ace43d 100644 --- a/fs/xfs/scrub/cow_repair.c +++ b/fs/xfs/scrub/cow_repair.c @@ -300,18 +300,15 @@ xrep_cow_find_bad( * on the debugging knob, replace everything in the CoW fork. */ if ((sc->sm->sm_flags & XFS_SCRUB_IFLAG_FORCE_REBUILD) || - XFS_TEST_ERROR(sc->mp, XFS_ERRTAG_FORCE_SCRUB_REPAIR)) { + XFS_TEST_ERROR(sc->mp, XFS_ERRTAG_FORCE_SCRUB_REPAIR)) error = xrep_cow_mark_file_range(xc, xc->irec.br_startblock, xc->irec.br_blockcount); - if (error) - return error; - } out_sa: xchk_ag_free(sc, &sc->sa); out_pag: xfs_perag_put(pag); - return 0; + return error; } /* From c3e073894379532c00cca7ba5762e18fafe29da8 Mon Sep 17 00:00:00 2001 From: Yingjie Gao Date: Wed, 27 May 2026 12:31:34 +0800 Subject: [PATCH 033/257] xfs: fix rtgroup cleanup in CoW fork repair xrep_cow_find_bad_rt() initializes scrub rtgroup state before the force-rebuild path calls xrep_cow_mark_file_range(). If that call fails, the code jumps directly to out_rtg, which skips the scrub rtgroup cleanup and only drops the local rtgroup reference. Remove the unnecessary jump so the function falls through to out_sr, ensuring the realtime cursors, lock state, and sr->rtg reference are released before returning. Fixes: fd97fe111208 ("xfs: fix CoW forks for realtime files") Cc: # v6.14 Signed-off-by: Yingjie Gao Reviewed-by: "Darrick J. Wong" Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/cow_repair.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/fs/xfs/scrub/cow_repair.c b/fs/xfs/scrub/cow_repair.c index a6ff09ace43d..c25716fc4fee 100644 --- a/fs/xfs/scrub/cow_repair.c +++ b/fs/xfs/scrub/cow_repair.c @@ -382,12 +382,9 @@ xrep_cow_find_bad_rt( * CoW fork and then scan for staging extents in the refcountbt. */ if ((sc->sm->sm_flags & XFS_SCRUB_IFLAG_FORCE_REBUILD) || - XFS_TEST_ERROR(sc->mp, XFS_ERRTAG_FORCE_SCRUB_REPAIR)) { + XFS_TEST_ERROR(sc->mp, XFS_ERRTAG_FORCE_SCRUB_REPAIR)) error = xrep_cow_mark_file_range(xc, xc->irec.br_startblock, xc->irec.br_blockcount); - if (error) - goto out_rtg; - } out_sr: xchk_rtgroup_btcur_free(&sc->sr); From c316bb56bf4dda9bd3456a163c3a3856ca1b9498 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 27 May 2026 14:39:28 +0200 Subject: [PATCH 034/257] xfs: drop the RTG reference later in xfs_ioc_rtgroup_geometry Keep the rtgroup reference until after reporting the write pointer, as that uses it. Right now this is not a major issue as we don't support shrinking file systems in a way that makes RTGs go away, but let's stick to the proper reference counting to prepare for that. Fixes: c6ce65cb17aa ("xfs: add write pointer to xfs_rtgroup_geometry") Signed-off-by: Christoph Hellwig Reviewed-by: Carlos Maiolino Reviewed-by: Wilfred Mallawa Reviewed-by: Damien Le Moal Signed-off-by: Carlos Maiolino --- fs/xfs/xfs_ioctl.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c index 46e234863644..737afcb5652a 100644 --- a/fs/xfs/xfs_ioctl.c +++ b/fs/xfs/xfs_ioctl.c @@ -433,9 +433,8 @@ xfs_ioc_rtgroup_geometry( return -EINVAL; error = xfs_rtgroup_get_geometry(rtg, &rgeo); - xfs_rtgroup_put(rtg); if (error) - return error; + goto out_put_rtg; if (xfs_has_zoned(mp)) { xfs_rtgroup_lock(rtg, XFS_RTGLOCK_RMAP); @@ -453,8 +452,10 @@ xfs_ioc_rtgroup_geometry( } if (copy_to_user(arg, &rgeo, sizeof(rgeo))) - return -EFAULT; - return 0; + error = -EFAULT; +out_put_rtg: + xfs_rtgroup_put(rtg); + return error; } /* From 2fa29c1a167739fdef0fda9742ddeb15d9a6c7e4 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 27 May 2026 14:39:29 +0200 Subject: [PATCH 035/257] xfs: factor rtgroup geom write pointer reporting into a helper Sticks out a bit better if we add a separate helper for it. Signed-off-by: Christoph Hellwig Reviewed-by: Carlos Maiolino Reviewed-by: Wilfred Mallawa Reviewed-by: Damien Le Moal Signed-off-by: Carlos Maiolino --- fs/xfs/xfs_ioctl.c | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c index 737afcb5652a..96af6b62ce39 100644 --- a/fs/xfs/xfs_ioctl.c +++ b/fs/xfs/xfs_ioctl.c @@ -409,6 +409,26 @@ xfs_ioc_ag_geometry( return 0; } +static void +xfs_rtgroup_report_write_pointer( + struct xfs_rtgroup *rtg, + struct xfs_rtgroup_geometry *rgeo) +{ + xfs_rtgroup_lock(rtg, XFS_RTGLOCK_RMAP); + if (rtg->rtg_open_zone) { + rgeo->rg_writepointer = rtg->rtg_open_zone->oz_allocated; + } else { + xfs_rgblock_t highest_rgbno = xfs_rtrmap_highest_rgbno(rtg); + + if (highest_rgbno == NULLRGBLOCK) + rgeo->rg_writepointer = 0; + else + rgeo->rg_writepointer = highest_rgbno + 1; + } + xfs_rtgroup_unlock(rtg, XFS_RTGLOCK_RMAP); + rgeo->rg_flags |= XFS_RTGROUP_GEOM_WRITEPOINTER; +} + STATIC int xfs_ioc_rtgroup_geometry( struct xfs_mount *mp, @@ -416,7 +436,6 @@ xfs_ioc_rtgroup_geometry( { struct xfs_rtgroup *rtg; struct xfs_rtgroup_geometry rgeo; - xfs_rgblock_t highest_rgbno; int error; if (copy_from_user(&rgeo, arg, sizeof(rgeo))) @@ -435,21 +454,8 @@ xfs_ioc_rtgroup_geometry( error = xfs_rtgroup_get_geometry(rtg, &rgeo); if (error) goto out_put_rtg; - - if (xfs_has_zoned(mp)) { - xfs_rtgroup_lock(rtg, XFS_RTGLOCK_RMAP); - if (rtg->rtg_open_zone) { - rgeo.rg_writepointer = rtg->rtg_open_zone->oz_allocated; - } else { - highest_rgbno = xfs_rtrmap_highest_rgbno(rtg); - if (highest_rgbno == NULLRGBLOCK) - rgeo.rg_writepointer = 0; - else - rgeo.rg_writepointer = highest_rgbno + 1; - } - xfs_rtgroup_unlock(rtg, XFS_RTGLOCK_RMAP); - rgeo.rg_flags |= XFS_RTGROUP_GEOM_WRITEPOINTER; - } + if (xfs_has_zoned(mp)) + xfs_rtgroup_report_write_pointer(rtg, &rgeo); if (copy_to_user(arg, &rgeo, sizeof(rgeo))) error = -EFAULT; From 8070a50b88971f9fada55a3ae3330f529bef9adc Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 27 May 2026 14:32:37 +0200 Subject: [PATCH 036/257] xfs: abort mount if xfs_fs_reserve_ag_blocks fails xfs_mountfs currently ignores all errors from xfs_fs_reserve_ag_blocks, which can lead to the mount path continuing on corruption errors. Fix the check to only ignore -ENOSPC as in other callers, and unwind for all other errors. Fixes: 81ed94751b15 ("xfs: fix log intent recovery ENOSPC shutdowns when inactivating inodes") Signed-off-by: Christoph Hellwig Reviewed-by: "Darrick J. Wong" Signed-off-by: Carlos Maiolino --- fs/xfs/xfs_mount.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c index b24195f570cd..7aa51826b1ca 100644 --- a/fs/xfs/xfs_mount.c +++ b/fs/xfs/xfs_mount.c @@ -1149,9 +1149,12 @@ xfs_mountfs( * blocks. */ error = xfs_fs_reserve_ag_blocks(mp); - if (error && error == -ENOSPC) + if (error) { + if (error != -ENOSPC) + goto out_rtunmount; xfs_warn(mp, - "ENOSPC reserving per-AG metadata pool, log recovery may fail."); +"ENOSPC reserving per-AG metadata pool, log recovery may fail."); + } error = xfs_log_mount_finish(mp); xfs_fs_unreserve_ag_blocks(mp); if (error) { From dabfaca8140f64535a79020f0f86ea56f5db5bb2 Mon Sep 17 00:00:00 2001 From: "Matthew Wilcox (Oracle)" Date: Tue, 26 May 2026 20:53:59 +0100 Subject: [PATCH 037/257] xfs: Remove mention of PageWriteback Update a comment to refer to folios instead of pages. Signed-off-by: Matthew Wilcox (Oracle) Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/xfs_reflink.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c index db23a0f231d6..251dec48f0e3 100644 --- a/fs/xfs/xfs_reflink.c +++ b/fs/xfs/xfs_reflink.c @@ -949,16 +949,16 @@ xfs_reflink_end_cow( * repeatedly cycles the ILOCK to allocate one transaction per remapped * extent. * - * If we're being called by writeback then the pages will still - * have PageWriteback set, which prevents races with reflink remapping - * and truncate. Reflink remapping prevents races with writeback by - * taking the iolock and mmaplock before flushing the pages and - * remapping, which means there won't be any further writeback or page - * cache dirtying until the reflink completes. + * If we're being called by writeback then the folios will still + * have the writeback flag set, which prevents races with reflink + * remapping and truncate. Reflink remapping prevents races with + * writeback by taking the iolock and mmaplock before flushing + * the folios and remapping, which means there won't be any further + * writeback or page cache dirtying until the reflink completes. * * We should never have two threads issuing writeback for the same file * region. There are also have post-eof checks in the writeback - * preparation code so that we don't bother writing out pages that are + * preparation code so that we don't bother writing out folios that are * about to be truncated. * * If we're being called as part of directio write completion, the dio From ac35b5580ace12e5d0a0b5e61e36d2c4e1ffa29c Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Wed, 27 May 2026 18:18:07 +0000 Subject: [PATCH 038/257] rust: arm64: set uwtable llvm module flag for CONFIG_UNWIND_TABLES Due to a rustc bug [1] the -Cforce-unwind-tables=y flag only emits the uwtable annotation for functions, but not for the module. This means that compiler-generated functions such as 'asan.module_ctor' do not receive the uwtable annotation. When CONFIG_UNWIND_PATCH_PAC_INTO_SCS is enabled, this leads to boot failures because the dwarf information emitted for the kasan constructors is wrong, which causes the SCS boot patching code to patch the constructor in an illegal manner. Specifically, the paciasp instruction is patched, but the autiasp instruction is not. This mismatch leads to a crash when the constructor is called during boot. ================================================================== BUG: KASAN: global-out-of-bounds in do_basic_setup+0x4c/0x90 Read of size 8 at addr ffffffe3cc7eb488 by task swapper/0/1 Specifically the faulting instruction is the (*fn)() to invoke the constructor in do_ctors() of the init/main.c file. Once the fix lands in rustc, this flag can be made conditional on the rustc version. Note that passing the flag on a rustc with the fix present has no effect. [ The fix [1] has landed for Rust 1.98.0 (expected release on 2026-08-20). Thus add a version check as discussed. - Miguel ] Fixes: d077242d68a3 ("rust: support for shadow call stack sanitizer") Cc: stable@kernel.org Link: https://github.com/rust-lang/rust/pull/156973 [1] Reported-by: Bo Ye Debugged-by: Isaac Manjarres Debugged-by: Sami Tolvanen Tested-by: Isaac Manjarres Signed-off-by: Alice Ryhl Link: https://patch.msgid.link/20260527-uwtable-module-flag-v1-1-caa41342be4b@google.com [ Adjusted link and comment. - Miguel ] Signed-off-by: Miguel Ojeda --- arch/arm64/Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile index 73a10f65ce8b..6b005c8fef70 100644 --- a/arch/arm64/Makefile +++ b/arch/arm64/Makefile @@ -63,6 +63,9 @@ else KBUILD_CFLAGS += -fasynchronous-unwind-tables KBUILD_AFLAGS += -fasynchronous-unwind-tables KBUILD_RUSTFLAGS += -Cforce-unwind-tables=y -Zuse-sync-unwind=n +# Work around rustc bug on compilers without +# https://github.com/rust-lang/rust/pull/156973. +KBUILD_RUSTFLAGS += $(if $(call rustc-min-version,109800),,-Zllvm_module_flag=uwtable:u32:2:max) endif ifeq ($(CONFIG_STACKPROTECTOR_PER_TASK),y) From 905b06d32a52afe32fcf5f30cf298c9ea6359f11 Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Sat, 30 May 2026 13:49:25 +0200 Subject: [PATCH 039/257] rust: x86: support Rust >= 1.98.0 target spec Starting with Rust 1.98.0 (expected 2026-08-20), the target spec will not support `x86-softfloat` anymore [1]. Instead, `softfloat` should be used, which is an alias. Otherwise, one gets: error: error loading target specification: rustc-abi: invalid rustc abi: 'x86-softfloat'. allowed values: 'x86-sse2', 'softfloat' at line 3 column 32 | = help: run `rustc --print target-list` for a list of built-in targets Thus conditionally use one or the other depending on the version. The alias has existed since Rust 1.95.0 (released 2026-04-16) [2], but use the newer version instead to avoid changing how the build works for existing compilers, at least until more testing takes place. Cc: Ralf Jung Cc: stable@vger.kernel.org # Needed in 6.12.y and later (Rust is pinned in older LTSs). Link: https://github.com/rust-lang/rust/pull/157151 [1] Link: https://github.com/rust-lang/rust/pull/151154 [2] Reviewed-by: Alice Ryhl Link: https://patch.msgid.link/20260530114925.260754-1-ojeda@kernel.org Signed-off-by: Miguel Ojeda --- scripts/generate_rust_target.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/scripts/generate_rust_target.rs b/scripts/generate_rust_target.rs index 38b3416bb979..16f7e855e012 100644 --- a/scripts/generate_rust_target.rs +++ b/scripts/generate_rust_target.rs @@ -196,7 +196,9 @@ fn main() { } } else if cfg.has("X86_64") { ts.push("arch", "x86_64"); - if cfg.rustc_version_atleast(1, 86, 0) { + if cfg.rustc_version_atleast(1, 98, 0) { + ts.push("rustc-abi", "softfloat"); + } else if cfg.rustc_version_atleast(1, 86, 0) { ts.push("rustc-abi", "x86-softfloat"); } ts.push( @@ -236,7 +238,9 @@ fn main() { panic!("32-bit x86 only works under UML"); } ts.push("arch", "x86"); - if cfg.rustc_version_atleast(1, 86, 0) { + if cfg.rustc_version_atleast(1, 98, 0) { + ts.push("rustc-abi", "softfloat"); + } else if cfg.rustc_version_atleast(1, 86, 0) { ts.push("rustc-abi", "x86-softfloat"); } ts.push( From 85e0f27dd1396307913ffc5745b0c05137e9beac Mon Sep 17 00:00:00 2001 From: "Masami Hiramatsu (Google)" Date: Mon, 25 May 2026 11:21:14 +0900 Subject: [PATCH 040/257] tracing/probes: Point the error offset correctly for eprobe argument error Fix to point the error offset correctly for eprobe argument error. In the cleanup commit 1b8b0cd754cd ("tracing/probes: Move event parameter fetching code to common parser"), due to incorrect backward compatibility aimed at conforming to the test specifications, the error location was set to 0 when a non-existent formal parameter was specified for Eprobe. However, this should be corrected in both the test and the implementation to point correct error position. Link: https://lore.kernel.org/all/177967567399.209006.1451571244515632097.stgit@devnote2/ Fixes: 1b8b0cd754cd ("tracing/probes: Move event parameter fetching code to common parser") Cc: stable@vger.kernel.org Signed-off-by: Masami Hiramatsu (Google) Reviewed-by: Steven Rostedt --- kernel/trace/trace_probe.c | 2 -- .../selftests/ftrace/test.d/dynevent/eprobes_syntax_errors.tc | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c index e0d3a0da26af..44c22d4e7881 100644 --- a/kernel/trace/trace_probe.c +++ b/kernel/trace/trace_probe.c @@ -962,8 +962,6 @@ static int parse_probe_vars(char *orig_arg, const struct fetch_type *t, code->op = FETCH_OP_COMM; return 0; } - /* backward compatibility */ - ctx->offset = 0; goto inval; } diff --git a/tools/testing/selftests/ftrace/test.d/dynevent/eprobes_syntax_errors.tc b/tools/testing/selftests/ftrace/test.d/dynevent/eprobes_syntax_errors.tc index 4f5e8c665156..2a680c086047 100644 --- a/tools/testing/selftests/ftrace/test.d/dynevent/eprobes_syntax_errors.tc +++ b/tools/testing/selftests/ftrace/test.d/dynevent/eprobes_syntax_errors.tc @@ -20,7 +20,7 @@ check_error 'e:foo/^123456789012345678901234567890123456789012345678901234567890 check_error 'e:foo/^bar.1 syscalls/sys_enter_openat' # BAD_EVENT_NAME check_error 'e:foo/bar syscalls/sys_enter_openat arg=^dfd' # BAD_FETCH_ARG -check_error 'e:foo/bar syscalls/sys_enter_openat ^arg=$foo' # BAD_ATTACH_ARG +check_error 'e:foo/bar syscalls/sys_enter_openat arg=^$foo' # BAD_ATTACH_ARG if grep -q '\..*\[if \]' README; then check_error 'e:foo/bar syscalls/sys_enter_openat if ^' # NO_EP_FILTER From aa2f4addab44407c7aa742321de5dc1914ab5762 Mon Sep 17 00:00:00 2001 From: Rong Zhang Date: Sun, 31 May 2026 03:52:49 +0800 Subject: [PATCH 041/257] ALSA: usb-audio: Set the value of potential sticky mixers to maximum It makes no sense to restore the saved value for a sticky mixer, since setting any value is a no-op. However, in some rare cases, SET_CUR is effective despite GET_CUR always returns a constant value. These mixers are not sticky, but there's no way to distinguish them. Without any additional information, the best thing we can do is to set the mixer value to the maximum before bailing out, so that a soft mixer can still reach the maximum hardware volume if the mixer turns out to be non-sticky. Meanwhile, all channels must be synchronized to prevent imbalance volume. Fixes: 86aa1ea1f15c ("ALSA: usb-audio: Do not expose sticky mixers") Signed-off-by: Rong Zhang Link: https://patch.msgid.link/20260531-uac-sticky-error-path-v1-1-12c2329d17ef@rong.moe Signed-off-by: Takashi Iwai --- sound/usb/mixer.c | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 5fba456eb4a9..fb37bb8ad9a9 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -1371,10 +1371,8 @@ static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval, goto no_checks; ret = check_sticky_volume_control(cval, minchn, saved); - if (ret < 0) { - snd_usb_set_cur_mix_value(cval, minchn, 0, saved); - return ret; - } + if (ret < 0) + goto sticky; if (cval->min + cval->res < cval->max) check_volume_control_res(cval, minchn, saved); @@ -1431,6 +1429,33 @@ static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval, } return 0; + +sticky: + /* + * It makes no sense to restore the saved value for a sticky mixer, + * since setting any value is a no-op. + * + * However, in some rare cases, SET_CUR is effective despite GET_CUR + * always returns a constant value. These mixers are not sticky, but + * there's no way to distinguish them. Without any additional + * information, the best thing we can do is to set the mixer value to + * the maximum before bailing out, so that a soft mixer can still reach + * the maximum hardware volume if the mixer turns out to be non-sticky. + * Meanwhile, all channels must be synchronized to prevent imbalance + * volume. + */ + if (!cval->cmask) { + snd_usb_set_cur_mix_value(cval, 0, 0, cval->max); + } else { + for (i = 0; i < MAX_CHANNELS; i++) { + idx = 0; + if (cval->cmask & BIT(i)) { + snd_usb_set_cur_mix_value(cval, i + 1, idx, cval->max); + idx++; + } + } + } + return ret; } #define get_min_max(cval, def) get_min_max_with_quirks(cval, def, NULL) From b003086d76968298f22e7cf62239833b5a3a06b1 Mon Sep 17 00:00:00 2001 From: Gil Portnoy Date: Thu, 28 May 2026 00:00:00 +0000 Subject: [PATCH 042/257] ksmbd: fix NULL-deref of opinfo->conn in oplock/lease break notifiers smb2_oplock_break_noti() and smb2_lease_break_noti() read opinfo->conn into a local with neither READ_ONCE() nor a NULL check. Both run from oplock_break() after opinfo_get_list() has dropped ci->m_lock, so a concurrent SMB2 LOGOFF (session_fd_check()) can set op->conn = NULL under ci->m_lock within that window. ksmbd_conn_r_count_inc(conn) then writes through NULL at offset 0xc4 -- a remotely triggerable oops. Guard both reads the way compare_guid_key() already does: read opinfo->conn with READ_ONCE() and return early if it is NULL, before allocating the work struct so nothing leaks. A NULL conn means the client is gone and the break is moot, so return 0; oplock_break() treats that as success and runs the normal teardown. Fixes: c8efcc786146 ("ksmbd: add support for durable handles v1/v2") Assisted-by: Henry (Claude):claude-opus-4 Signed-off-by: Gil Portnoy Acked-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/oplock.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c index 0f5c18520eff..b193dde4810d 100644 --- a/fs/smb/server/oplock.c +++ b/fs/smb/server/oplock.c @@ -711,11 +711,16 @@ static void __smb2_oplock_break_noti(struct work_struct *wk) */ static int smb2_oplock_break_noti(struct oplock_info *opinfo) { - struct ksmbd_conn *conn = opinfo->conn; + struct ksmbd_conn *conn; struct oplock_break_info *br_info; int ret = 0; - struct ksmbd_work *work = ksmbd_alloc_work_struct(); + struct ksmbd_work *work; + conn = READ_ONCE(opinfo->conn); + if (!conn) + return 0; + + work = ksmbd_alloc_work_struct(); if (!work) return -ENOMEM; @@ -815,11 +820,15 @@ static void __smb2_lease_break_noti(struct work_struct *wk) */ static int smb2_lease_break_noti(struct oplock_info *opinfo) { - struct ksmbd_conn *conn = opinfo->conn; + struct ksmbd_conn *conn; struct ksmbd_work *work; struct lease_break_info *br_info; struct lease *lease = opinfo->o_lease; + conn = READ_ONCE(opinfo->conn); + if (!conn) + return 0; + work = ksmbd_alloc_work_struct(); if (!work) return -ENOMEM; From 7ce4fc40018de07f05f3035241122d992610dbfb Mon Sep 17 00:00:00 2001 From: Gil Portnoy Date: Thu, 28 May 2026 00:00:00 +0000 Subject: [PATCH 043/257] ksmbd: fix durable reconnect double-bind race in ksmbd_reopen_durable_fd Two concurrent same-user DHnC reconnects can both observe fp->conn == NULL before either sets it. ksmbd_reopen_durable_fd() checks fp->conn to guard against a handle already being reconnected, but the check and the binding assignment are not atomic: both threads pass the guard, both call ksmbd_conn_get() on the same fp, and both eventually reach kfree(fp->owner.name) -- a double-free of the owner.name slab object. The double-bound ksmbd_file also causes a write-UAF on the 344-byte ksmbd_file_cache object when a concurrent smb2_close() spins on fp->f_lock after the object has been freed by the losing reconnect path. KASAN on 7.1-rc5 (48-thread concurrent reconnect, 3000 cycles): BUG: KASAN: double-free in ksmbd_reopen_durable_fd+0x268/0x308 BUG: KASAN: slab-use-after-free in _raw_spin_lock+0xac/0x150 Write of size 4 at offset 24 into freed ksmbd_file_cache object Five double-bind windows observed; 63 total KASAN reports triggered. Fix: validate and claim fp->conn under write_lock(&global_ft.lock) so the check-and-claim is atomic. ksmbd_lookup_durable_fd() already treats fp->conn != NULL as "in use" and skips such an fp; setting fp->conn before dropping the lock closes the race. ksmbd_conn_get() is a non-sleeping refcount increment, safe under the rwlock. The rollback path on __open_id() failure also clears fp->conn/tcon under the lock so concurrent readers see a consistent state. Fixes: b1f1e80620de ("ksmbd: centralize ksmbd_conn final release to plug transport leak") Assisted-by: Henry (Claude):claude-opus-4 Signed-off-by: Gil Portnoy Acked-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/vfs_cache.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c index 4d2d33df6231..ba3355a6057a 100644 --- a/fs/smb/server/vfs_cache.c +++ b/fs/smb/server/vfs_cache.c @@ -1390,19 +1390,19 @@ int ksmbd_reopen_durable_fd(struct ksmbd_work *work, struct ksmbd_file *fp) struct ksmbd_lock *smb_lock; unsigned int old_f_state; + write_lock(&global_ft.lock); if (!fp->is_durable || fp->conn || fp->tcon) { + write_unlock(&global_ft.lock); pr_err("Invalid durable fd [%p:%p]\n", fp->conn, fp->tcon); return -EBADF; } if (has_file_id(fp->volatile_id)) { + write_unlock(&global_ft.lock); pr_err("Still in use durable fd: %llu\n", fp->volatile_id); return -EBADF; } - old_f_state = fp->f_state; - fp->f_state = FP_NEW; - /* * Initialize fp's connection binding before publishing fp into the * session's file table. If __open_id() is ordered first, a @@ -1413,11 +1413,17 @@ int ksmbd_reopen_durable_fd(struct ksmbd_work *work, struct ksmbd_file *fp) */ fp->conn = ksmbd_conn_get(conn); fp->tcon = work->tcon; + write_unlock(&global_ft.lock); + + old_f_state = fp->f_state; + fp->f_state = FP_NEW; __open_id(&work->sess->file_table, fp, OPEN_ID_TYPE_VOLATILE_ID); if (!has_file_id(fp->volatile_id)) { + write_lock(&global_ft.lock); fp->conn = NULL; fp->tcon = NULL; + write_unlock(&global_ft.lock); ksmbd_conn_put(conn); fp->f_state = old_f_state; return -EBADF; From f580d27e8928828693df44ba2db0fffdbe11dfea Mon Sep 17 00:00:00 2001 From: Gil Portnoy Date: Mon, 1 Jun 2026 08:27:56 +0900 Subject: [PATCH 044/257] ksmbd: fix use-after-free of a deferred file_lock on double SMB2_CANCEL A deferred byte-range lock (an SMB2_LOCK that blocks) registers an async work on conn->async_requests via setup_async_work(), with cancel_fn = smb2_remove_blocked_lock and cancel_argv[0] pointing at the struct file_lock. When the request is cancelled, the worker frees the file_lock with locks_free_lock() and takes the cancelled early-exit, which "goto out"s and never reaches release_async_work() -- the only site that unlinks the work from conn->async_requests and clears cancel_fn/cancel_argv. The work therefore stays matchable on async_requests with a live cancel_fn pointing at the freed file_lock, until connection teardown finally runs release_async_work(). smb2_cancel() fires cancel_fn unconditionally with no state guard, so a second SMB2_CANCEL for the same AsyncId, arriving in that window, re-runs smb2_remove_blocked_lock() on the freed file_lock -- a slab use-after-free: BUG: KASAN: slab-use-after-free in __locks_delete_block __locks_delete_block locks_delete_block ksmbd_vfs_posix_lock_unblock smb2_remove_blocked_lock smb2_cancel <- 2nd SMB2_CANCEL fires cancel_fn handle_ksmbd_work Allocated by ...: locks_alloc_lock <- smb2_lock Freed by ...: locks_free_lock <- smb2_lock (cancelled branch) ... cache file_lock_cache of size 192 Reproduced on mainline with KASAN by an authenticated SMB client. Skip a work whose state is already KSMBD_WORK_CANCELLED so its cancel callback cannot be fired a second time. Cc: stable@vger.kernel.org Signed-off-by: Gil Portnoy Acked-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/smb2pdu.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 620bcfbbfd92..3eb3b1711acb 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -7322,6 +7322,17 @@ int smb2_cancel(struct ksmbd_work *work) le64_to_cpu(hdr->Id.AsyncId)) continue; + /* + * A cancelled deferred byte-range lock frees its + * file_lock and takes the smb2_lock() early-exit that + * skips release_async_work(), so the work stays on + * conn->async_requests with a live cancel_fn pointing + * at the freed file_lock. Re-firing it on a second + * SMB2_CANCEL is a use-after-free. + */ + if (iter->state == KSMBD_WORK_CANCELLED) + break; + ksmbd_debug(SMB, "smb2 with AsyncId %llu cancelled command = 0x%x\n", le64_to_cpu(hdr->Id.AsyncId), From e9096a5a170e7ecd6467bc2e08668ec39897cda7 Mon Sep 17 00:00:00 2001 From: Takao Sato Date: Tue, 26 May 2026 13:09:57 -0300 Subject: [PATCH 045/257] xfrm: iptfs: preserve shared-frag marker in iptfs_consume_frags() iptfs_consume_frags() transfers paged fragments from one socket buffer to another but fails to propagate the SKBFL_SHARED_FRAG flag. This is the same class of bug that was fixed in skb_try_coalesce() for CVE-2026-46300: when fragments backed by read-only page-cache pages are merged, the marker indicating their shared nature must be preserved so that ESP can decide correctly whether in-place encryption is safe. Apply the same two-line fix used in skb_try_coalesce() to iptfs_consume_frags(). Fixes: b96ba312e21c ("xfrm: iptfs: share page fragments of inner packets") Cc: stable@vger.kernel.org # 6.14+ Signed-off-by: Takao Sato Signed-off-by: Steffen Klassert --- net/xfrm/xfrm_iptfs.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/xfrm/xfrm_iptfs.c b/net/xfrm/xfrm_iptfs.c index 6c6bbc040517..62ba828632f1 100644 --- a/net/xfrm/xfrm_iptfs.c +++ b/net/xfrm/xfrm_iptfs.c @@ -2168,6 +2168,8 @@ static void iptfs_consume_frags(struct sk_buff *to, struct sk_buff *from) memcpy(&toi->frags[toi->nr_frags], fromi->frags, sizeof(fromi->frags[0]) * fromi->nr_frags); toi->nr_frags += fromi->nr_frags; + if (fromi->nr_frags) + toi->flags |= fromi->flags & SKBFL_SHARED_FRAG; fromi->nr_frags = 0; from->data_len = 0; from->len = 0; From fc12cf16df9af867e1455b011cdd9310474a612e Mon Sep 17 00:00:00 2001 From: Jackie Dong Date: Wed, 27 May 2026 18:20:05 +0800 Subject: [PATCH 046/257] ASoC: amd: acp: Add DMI quirk for Lenovo Yoga Pro 7 15ASH11 Lenovo Yoga Pro 7 15ASH11 with AMD RYZEN AI MAX+ 388 (Strix Halo, ACP 7.0) uses Realtek ALC287 series codec and no any DMIC connected by ACP. All DMICs directly connet with ALC codec. Without this quirk, Input Device of Gnome Sound settings shows Internal Stereo Microphone and Digital Microphone by default. In fact, Digital Microphone of ACP doesn't work due to no connecting with ALC287 codec, the Internal Stereo Microphone as analog device based on snd_hda_intel driver can work well. Add a DMI quirk to override the flag to 0, consistent with the existing entry for the HN7306EA. Signed-off-by: Jackie Dong Link: https://patch.msgid.link/20260527102005.58528-1-xy-jackie@139.com Signed-off-by: Mark Brown --- sound/soc/amd/acp-config.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/soc/amd/acp-config.c b/sound/soc/amd/acp-config.c index 309dc9ed6e0d..0d977f4f758d 100644 --- a/sound/soc/amd/acp-config.c +++ b/sound/soc/amd/acp-config.c @@ -37,6 +37,13 @@ static const struct dmi_system_id acp70_acpi_flag_override_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Zenbook S16 UM5606GA"), }, }, + { + /* Lenovo Yoga Pro 7 15ASH11 (Strix Halo, ACP 7.0) */ + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "83W5"), + }, + }, {} }; From c9c64820a48dcc739311fd7a9138511888539d02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Miguel?= Date: Sat, 23 May 2026 22:35:48 +0100 Subject: [PATCH 047/257] ASoC: amd: yc: Enable internal mic on MSI Bravo 17 C7VF MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The MSI Bravo 17 C7VF routes its internal digital microphone through the ACP6x. The machine driver only enables the DMIC for boards present in the DMI quirk table, so on this model the internal mic is never detected and no capture device is created. Add a quirk entry matching the board's DMI identifiers so the DMIC is enabled and the internal microphone works. Signed-off-by: João Miguel Link: https://patch.msgid.link/20260523213548.5219-1-jmiguel.ghp@gmail.com Signed-off-by: Mark Brown --- sound/soc/amd/yc/acp6x-mach.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c index 7a637d6b5576..a93a7fd4877e 100644 --- a/sound/soc/amd/yc/acp6x-mach.c +++ b/sound/soc/amd/yc/acp6x-mach.c @@ -794,6 +794,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "M7601RM"), } }, + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Micro-Star International Co., Ltd."), + DMI_MATCH(DMI_BOARD_NAME, "MS-17LN"), + } + }, {} }; From 42445de1765547f56f48d107c0b8f3482c98458e Mon Sep 17 00:00:00 2001 From: Daniele Ceraolo Spurio Date: Fri, 29 May 2026 12:36:02 -0700 Subject: [PATCH 048/257] Revert "drm/xe/nvls: Define GuC firmware for NVL-S" This reverts commit 4e88de313ff4d1c67b644b1f39f9fb4089711b71. The early GuC FW definition meant for our CI branch was accidentally merged to the drm-xe-next branch instead. This GuC FW will never be released to linux-firmware, so we do not want the definition to be available in the mainline Linux codebase. Fixes: 4e88de313ff4 ("drm/xe/nvls: Define GuC firmware for NVL-S") Signed-off-by: Daniele Ceraolo Spurio Cc: Julia Filipchuk Cc: Rodrigo Vivi Cc: Matt Roper Cc: stable@vger.kernel.org # v7.0+ Reviewed-by: Rodrigo Vivi Link: https://patch.msgid.link/20260529193558.185436-11-daniele.ceraolospurio@intel.com Signed-off-by: Rodrigo Vivi (cherry picked from commit 65b8e0ac86e48cfc9128c04dfc53ea3395d030dd) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/xe/xe_uc_fw.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpu/drm/xe/xe_uc_fw.c b/drivers/gpu/drm/xe/xe_uc_fw.c index 9cebb2490245..18ebefd444fe 100644 --- a/drivers/gpu/drm/xe/xe_uc_fw.c +++ b/drivers/gpu/drm/xe/xe_uc_fw.c @@ -115,7 +115,6 @@ struct fw_blobs_by_type { #define XE_GT_TYPE_ANY XE_GT_TYPE_UNINITIALIZED #define XE_GUC_FIRMWARE_DEFS(fw_def, mmp_ver, major_ver) \ - fw_def(NOVALAKE_S, GT_TYPE_ANY, mmp_ver(xe, guc, nvl, 70, 55, 4)) \ fw_def(PANTHERLAKE, GT_TYPE_ANY, major_ver(xe, guc, ptl, 70, 54, 0)) \ fw_def(BATTLEMAGE, GT_TYPE_ANY, major_ver(xe, guc, bmg, 70, 54, 0)) \ fw_def(LUNARLAKE, GT_TYPE_ANY, major_ver(xe, guc, lnl, 70, 53, 0)) \ From b5f53e6d3d32f2884d71500bb6d773c7bfe21f2e Mon Sep 17 00:00:00 2001 From: Pratik Vishwakarma Date: Sat, 30 May 2026 06:18:18 +0000 Subject: [PATCH 049/257] x86/CPU/AMD: Add more Zen6 models Family 0x1a, models 0xd0 - 0xef are Zen6, so add them to the range which sets X86_FEATURE_ZEN6. [ bp: Massage commit message. ] Signed-off-by: Pratik Vishwakarma Signed-off-by: Borislav Petkov (AMD) Link: https://patch.msgid.link/20260530061819.9721-1-Pratik.Vishwakarma@amd.com --- arch/x86/kernel/cpu/amd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c index 2f8e8ff2d000..31f01e9c7114 100644 --- a/arch/x86/kernel/cpu/amd.c +++ b/arch/x86/kernel/cpu/amd.c @@ -518,7 +518,7 @@ static void bsp_init_amd(struct cpuinfo_x86 *c) break; case 0x50 ... 0x5f: case 0x80 ... 0xaf: - case 0xc0 ... 0xcf: + case 0xc0 ... 0xef: setup_force_cpu_cap(X86_FEATURE_ZEN6); break; default: From 27f2d085bd72abe4235689d34d8654cfc876d568 Mon Sep 17 00:00:00 2001 From: Zhan Xusheng Date: Mon, 1 Jun 2026 16:51:36 +0800 Subject: [PATCH 050/257] erofs: fix EFSCORRUPTED on multi-algorithm images in z_erofs_map_sanity_check() Commit a5242d37c83a ("erofs: error out obviously illegal extents in advance") changed the per-extent algorithm presence check from "is the bit set" to "is the only bit set": - !(sbi->available_compr_algs & (1 << map->m_algorithmformat)) + (sbi->available_compr_algs ^ BIT(map->m_algorithmformat)) `available_compr_algs` is a bitmap of every compression algorithm available in the image (z_erofs_parse_cfgs() iterates it with for_each_set_bit()), so an image that enables more than one algorithm has multiple bits set. XOR is zero only when the bitmap is exactly BIT(map->m_algorithmformat); for any image with two or more algorithms the test is non-zero for every extent and the read fails with -EFSCORRUPTED ("inconsistent algorithmtype %u"). Reproducer (mkfs.erofs from erofs-utils 1.7.1): $ mkdir src $ yes A | head -c 100K > src/a $ head -c 64K /dev/zero > src/b $ mkfs.erofs -zlz4:deflate multi.erofs src $ mount -t erofs -o loop multi.erofs /mnt $ cat /mnt/a >/dev/null cat: /mnt/a: Structure needs cleaning $ dmesg | tail erofs (device loop0): inconsistent algorithmtype 0 for nid 46 erofs (device loop0): read error -117 @ 0 of nid 46 The erofs on-disk format (Z_EROFS_COMPRESSION_MAX = 4 with LZ4, LZMA, DEFLATE, ZSTD) and the kernel parser explicitly support multi-algorithm images, and erofs-utils 1.7.1 generates them via the "-z X:Y" syntax. Restore the original per-bit presence check. Fixes: a5242d37c83a ("erofs: error out obviously illegal extents in advance") Signed-off-by: Zhan Xusheng Reviewed-by: Gao Xiang Signed-off-by: Gao Xiang --- fs/erofs/zmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/erofs/zmap.c b/fs/erofs/zmap.c index a72db36096ca..e1a02a2c8406 100644 --- a/fs/erofs/zmap.c +++ b/fs/erofs/zmap.c @@ -716,7 +716,7 @@ static int z_erofs_map_sanity_check(struct inode *inode, } if (map->m_algorithmformat < Z_EROFS_COMPRESSION_MAX) { - if (sbi->available_compr_algs ^ BIT(map->m_algorithmformat)) { + if (!(sbi->available_compr_algs & BIT(map->m_algorithmformat))) { erofs_err(inode->i_sb, "inconsistent algorithmtype %u for nid %llu", map->m_algorithmformat, EROFS_I(inode)->nid); return -EFSCORRUPTED; From abf888b03a9805a3bc37948a0df443553b1c0910 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ADra=20Canal?= Date: Sat, 30 May 2026 15:37:42 -0300 Subject: [PATCH 051/257] drm/v3d: Wait for pending L2T flush before cleaning caches MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit v3d_clean_caches() starts the cache-clean sequence by writing V3D_L2TCACTL_TMUWCF to V3D_CTL_L2TCACTL and then polling for that bit to clear. It does not, however, check for an L2T flush (L2TFLS) that may still be in flight from a previous operation. On pre-V3D 7.1 hardware, kicking off the TMU write-combiner flush while an L2T flush is still pending can clobber bits in L2TCACTL and cause cache inconsistencies. Poll for L2TFLS to clear before writing L2TCACTL on V3D < 7.1, ensuring any pending flush has completed before a new clean is issued. Cc: stable@vger.kernel.org Fixes: d223f98f0209 ("drm/v3d: Add support for compute shader dispatch.") Link: https://patch.msgid.link/20260530-v3d-fix-rpi4-freezes-v1-1-c2c8307da6ce@igalia.com Signed-off-by: Maíra Canal Reviewed-by: Iago Toral Quiroga --- drivers/gpu/drm/v3d/v3d_gem.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/gpu/drm/v3d/v3d_gem.c b/drivers/gpu/drm/v3d/v3d_gem.c index 75d9eccd7966..dd7da419702f 100644 --- a/drivers/gpu/drm/v3d/v3d_gem.c +++ b/drivers/gpu/drm/v3d/v3d_gem.c @@ -213,6 +213,14 @@ v3d_clean_caches(struct v3d_dev *v3d) trace_v3d_cache_clean_begin(dev); + /* GFXH-1897: Ensure pending flushes complete before writing L2TCACTL */ + if (v3d->ver < V3D_GEN_71) { + if (wait_for(!(V3D_CORE_READ(core, V3D_CTL_L2TCACTL) & + V3D_L2TCACTL_L2TFLS), 100)) { + drm_err(dev, "Timeout waiting for L2T clean\n"); + } + } + V3D_CORE_WRITE(core, V3D_CTL_L2TCACTL, V3D_L2TCACTL_TMUWCF); if (wait_for(!(V3D_CORE_READ(core, V3D_CTL_L2TCACTL) & V3D_L2TCACTL_TMUWCF), 100)) { From 4a1b1ac2744694a2ecd66a84bdb1445f4ef24bee Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Tue, 26 May 2026 12:40:25 -0300 Subject: [PATCH 052/257] RDMA/core: Validate the passed in fops for ib_get_ucaps() Sashiko pointed out it is not safe to rely only on the devt because char/block alias so if the user finds a block device with the same dev_t it can masquerade as a ucap cdev fd. Test the f_ops to only accept authentic cdevs. Link: https://patch.msgid.link/r/0-v1-fd9482545e37+1e25-ib_ucaps_fd_ops_jgg@nvidia.com Cc: stable@vger.kernel.org Fixes: 61e51682816d ("RDMA/uverbs: Introduce UCAP (User CAPabilities) API") Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/ucaps.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/infiniband/core/ucaps.c b/drivers/infiniband/core/ucaps.c index 948093260dbd..5155ff0e538e 100644 --- a/drivers/infiniband/core/ucaps.c +++ b/drivers/infiniband/core/ucaps.c @@ -82,14 +82,12 @@ static int get_ucap_from_devt(dev_t devt, u64 *idx_mask) static int get_devt_from_fd(unsigned int fd, dev_t *ret_dev) { - struct file *file; + CLASS(fd, f)(fd); - file = fget(fd); - if (!file) + if (fd_empty(f) || fd_file(f)->f_op != &ucaps_cdev_fops) return -EBADF; - *ret_dev = file_inode(file)->i_rdev; - fput(file); + *ret_dev = file_inode(fd_file(f))->i_rdev; return 0; } From 1d0b597facdd3c0239c88e8797c1014e1ea0ef15 Mon Sep 17 00:00:00 2001 From: Andrzej Kacprowski Date: Fri, 29 May 2026 14:08:53 +0200 Subject: [PATCH 053/257] accel/ivpu: Add bounds check for firmware runtime memory Validate that the firmware runtime memory specified in the image header is properly aligned and sized to hold the firmware image. This prevents errors during memory allocation and image transfer. Fixes: 2007e210b6a1 ("accel/ivpu: Split FW runtime and global memory buffers") Cc: stable@vger.kernel.org # v7.0+ Signed-off-by: Andrzej Kacprowski Reviewed-by: Karol Wachowski Signed-off-by: Karol Wachowski Link: https://patch.msgid.link/20260529120853.135876-1-andrzej.kacprowski@linux.intel.com --- drivers/accel/ivpu/ivpu_fw.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/accel/ivpu/ivpu_fw.c b/drivers/accel/ivpu/ivpu_fw.c index 107f8ad31050..33c50779c06b 100644 --- a/drivers/accel/ivpu/ivpu_fw.c +++ b/drivers/accel/ivpu/ivpu_fw.c @@ -259,6 +259,22 @@ static int ivpu_fw_parse(struct ivpu_device *vdev) return -EINVAL; } + if (!PAGE_ALIGNED(runtime_addr)) { + ivpu_err(vdev, "Runtime address 0x%llx not page aligned\n", runtime_addr); + return -EINVAL; + } + + if (!PAGE_ALIGNED(runtime_size)) { + ivpu_err(vdev, "Runtime size %llu not page aligned\n", runtime_size); + return -EINVAL; + } + + if (runtime_size < image_size) { + ivpu_err(vdev, "Runtime size too small: %llu, image size: %llu\n", + runtime_size, image_size); + return -EINVAL; + } + if (!ivpu_is_within_range(image_load_addr, image_size, &vdev->hw->ranges.runtime)) { ivpu_err(vdev, "Invalid firmware load address: 0x%llx and size %llu\n", image_load_addr, image_size); From dd1311bcf0e62f0c515115f46a3813370f4a4bb1 Mon Sep 17 00:00:00 2001 From: Andrzej Kacprowski Date: Fri, 29 May 2026 13:58:42 +0200 Subject: [PATCH 054/257] accel/ivpu: Add bounds checks for firmware log indices Add validation that read and write indices in the firmware log buffer are within valid bounds (< data_size) before using them. If out-of-bounds indices are encountered (from firmware), clamp them to safe values instead of proceeding with invalid offsets. This prevents potential out-of-bounds buffer access when firmware supplies invalid log indices. Fixes: 1fc1251149a7 ("accel/ivpu: Refactor functions in ivpu_fw_log.c") Cc: stable@vger.kernel.org # v6.18+ Signed-off-by: Andrzej Kacprowski Reviewed-by: Karol Wachowski Signed-off-by: Karol Wachowski Link: https://patch.msgid.link/20260529115842.135378-1-andrzej.kacprowski@linux.intel.com --- drivers/accel/ivpu/ivpu_fw_log.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/accel/ivpu/ivpu_fw_log.c b/drivers/accel/ivpu/ivpu_fw_log.c index 337c906b0210..275baf844b56 100644 --- a/drivers/accel/ivpu/ivpu_fw_log.c +++ b/drivers/accel/ivpu/ivpu_fw_log.c @@ -98,6 +98,11 @@ static void fw_log_print_buffer(struct vpu_tracing_buffer_header *log, const cha u32 log_start = only_new_msgs ? READ_ONCE(log->read_index) : 0; u32 log_end = READ_ONCE(log->write_index); + if (log_start >= data_size) + log_start = 0; + if (log_end > data_size) + log_end = data_size; + if (log->wrap_count == log->read_wrap_count) { if (log_end <= log_start) { drm_printf(p, "==== %s \"%s\" log empty ====\n", prefix, log->name); From fb176425837693f50c5c9fc8db6fbb04af22bd0a Mon Sep 17 00:00:00 2001 From: Andrzej Kacprowski Date: Fri, 29 May 2026 14:08:41 +0200 Subject: [PATCH 055/257] accel/ivpu: Add buffer overflow check in MS get_info_ioctl Add validation that the info size returned from the metric stream info query is not exceeded when checked against the allocated buffer size. If the firmware returns a size larger than the buffer, reject the operation with -EOVERFLOW instead of proceeding with an incorrect buffer copy. Fixes: cdfad4db7756 ("accel/ivpu: Add NPU profiling support") Cc: stable@vger.kernel.org # v6.18+ Signed-off-by: Andrzej Kacprowski Reviewed-by: Karol Wachowski Signed-off-by: Karol Wachowski Link: https://patch.msgid.link/20260529120841.135852-1-andrzej.kacprowski@linux.intel.com --- drivers/accel/ivpu/ivpu_ms.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/accel/ivpu/ivpu_ms.c b/drivers/accel/ivpu/ivpu_ms.c index be43851f5f32..cd176e77b9a0 100644 --- a/drivers/accel/ivpu/ivpu_ms.c +++ b/drivers/accel/ivpu/ivpu_ms.c @@ -291,6 +291,13 @@ int ivpu_ms_get_info_ioctl(struct drm_device *dev, void *data, struct drm_file * if (ret) goto unlock; + if (info_size > ivpu_bo_size(bo)) { + ivpu_warn_ratelimited(vdev, "MS info overflow: %#llx > %#zx\n", + info_size, ivpu_bo_size(bo)); + ret = -EOVERFLOW; + goto unlock; + } + if (args->buffer_size < info_size) { ret = -ENOSPC; goto unlock; From ae0383e5a9a4b12d68c76c4769857def4665deff Mon Sep 17 00:00:00 2001 From: Yicong Hui Date: Mon, 6 Apr 2026 19:00:13 +0100 Subject: [PATCH 056/257] drm/imx: Fix three kernel-doc warnings in dcss-scaler.c Fix the following W=1 kerneldoc warnings by adding the missing parameter descriptions for @phase0_identity and @nn_interpolation in dcss_scaler_filter_design() and @phase0_identity in dcss_scaler_gaussian_filter() Warning: drivers/gpu/drm/imx/dcss/dcss-scaler.c:173 function parameter 'phase0_identity' not described in 'dcss_scaler_gaussian_filter' Warning: drivers/gpu/drm/imx/dcss/dcss-scaler.c:270 function parameter 'phase0_identity' not described in 'dcss_scaler_filter_design' Warning: drivers/gpu/drm/imx/dcss/dcss-scaler.c:270 function parameter 'nn_interpolation' not described in 'dcss_scaler_filter_design' Fixes: 9021c317b770 ("drm/imx: Add initial support for DCSS on iMX8MQ") Signed-off-by: Yicong Hui Reviewed-by: Laurentiu Palcu Link: https://patch.msgid.link/20260406180013.2442096-1-yiconghui@gmail.com Signed-off-by: Liu Ying --- drivers/gpu/drm/imx/dcss/dcss-scaler.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/imx/dcss/dcss-scaler.c b/drivers/gpu/drm/imx/dcss/dcss-scaler.c index 32c3f46b21da..5c7f8d952ec1 100644 --- a/drivers/gpu/drm/imx/dcss/dcss-scaler.c +++ b/drivers/gpu/drm/imx/dcss/dcss-scaler.c @@ -166,6 +166,7 @@ static int exp_approx_q(int x) * dcss_scaler_gaussian_filter() - Generate gaussian prototype filter. * @fc_q: fixed-point cutoff frequency normalized to range [0, 1] * @use_5_taps: indicates whether to use 5 taps or 7 taps + * @phase0_identity: whether to override phase 0 coefficients with identity filter * @coef: output filter coefficients */ static void dcss_scaler_gaussian_filter(int fc_q, bool use_5_taps, @@ -262,7 +263,9 @@ static void dcss_scaler_nearest_neighbor_filter(bool use_5_taps, * @src_length: length of input * @dst_length: length of output * @use_5_taps: 0 for 7 taps per phase, 1 for 5 taps + * @phase0_identity: whether to override phase 0 coefficients with identity filter * @coef: output coefficients + * @nn_interpolation: whether to use nearest neighbor instead of gaussian filter */ static void dcss_scaler_filter_design(int src_length, int dst_length, bool use_5_taps, bool phase0_identity, From 317d5146fb399ad1e87b310ee7d018fe648d40ba Mon Sep 17 00:00:00 2001 From: Dave Jones Date: Mon, 18 May 2026 14:46:28 -0400 Subject: [PATCH 057/257] NFS: write_completion: dereference loop-local req, not hdr->req 5d3869a41f36 ("NFS: fix writeback in presence of errors") introduced a dereference of hdr->req->wb_lock_context in nfs_write_completion's per-request loop. hdr->req is set once at nfs_pgheader_init() time and is not refcount-protected for the lifetime of the loop; when hdr aggregates requests from multiple page groups (common under heavy NFSv3 writeback), a parallel COMMIT on hdr->req's group can drop the last reference and free it while the outer loop is still iterating requests from other groups. KASAN catches this as an 8-byte read at offset +24 of a freed nfs_page slab object (wb_lock_context). All requests in a given pgio share the same open_context, so reading the loop-local req's wb_lock_context yields the same value and is safe -- req is still on hdr->pages and holds its writeback kref through the commit branch. Caught with kasan: BUG: KASAN: slab-use-after-free in nfs_write_completion+0x8f8/0xa50 [nfs] Read of size 8 at addr ffff888118af2058 by task kworker/u16:16/122062 CPU: 2 UID: 0 PID: 122062 Comm: kworker/u16:16 Kdump: loaded Not tainted 7.1.0-rc4+ #ge05a759574b2 PREEMPT Workqueue: nfsiod rpc_async_release Call Trace: dump_stack_lvl+0xaf/0x100 ? nfs_write_completion+0x8f8/0xa50 [nfs] print_report+0x157/0x4a1 ? __virt_addr_valid+0x1fb/0x400 ? nfs_write_completion+0x8f8/0xa50 [nfs] kasan_report+0xc2/0x190 ? nfs_write_completion+0x8f8/0xa50 [nfs] nfs_write_completion+0x8f8/0xa50 [nfs] ? nfs_commit_release_pages+0xbd0/0xbd0 [nfs] ? lock_acquire+0x182/0x2e0 ? process_one_work+0x937/0x1890 ? nfs_pgio_header_alloc+0xd0/0xd0 [nfs] rpc_free_task+0xee/0x160 rpc_async_release+0x5d/0xb0 process_one_work+0x9b0/0x1890 ? pwq_dec_nr_in_flight+0xed0/0xed0 ? rpc_final_put_task+0x140/0x140 worker_thread+0x75a/0x10a0 ? process_one_work+0x1890/0x1890 ? kthread+0x1af/0x4d0 ? process_one_work+0x1890/0x1890 kthread+0x3d3/0x4d0 ? kthread_affine_node+0x2c0/0x2c0 ret_from_fork+0x669/0xa50 ? native_tss_update_io_bitmap+0x660/0x660 ? __switch_to+0x9dd/0x1310 ? kthread_affine_node+0x2c0/0x2c0 ret_from_fork_asm+0x11/0x20 Allocated by task 121997 on cpu 3 at 31643.290294s: kasan_save_stack+0x1e/0x40 kasan_save_track+0x13/0x60 __kasan_slab_alloc+0x62/0x70 kmem_cache_alloc_noprof+0x1ab/0x4e0 nfs_page_create+0x152/0x460 [nfs] nfs_page_create_from_folio+0x7e/0x210 [nfs] nfs_update_folio+0x7a9/0x32a0 [nfs] nfs_write_end+0x290/0xc60 [nfs] generic_perform_write+0x4ce/0x990 nfs_file_write+0x6b3/0xce0 [nfs] vfs_write+0x63c/0xfa0 ksys_write+0x122/0x240 do_syscall_64+0xc3/0x13f0 entry_SYSCALL_64_after_hwframe+0x4b/0x53 Freed by task 122046 on cpu 0 at 31647.037964s: kasan_save_stack+0x1e/0x40 kasan_save_track+0x13/0x60 kasan_save_free_info+0x37/0x60 __kasan_slab_free+0x3b/0x60 kmem_cache_free+0x11b/0x5a0 nfs_page_group_destroy+0x13a/0x210 [nfs] nfs_unlock_and_release_request+0x64/0x90 [nfs] nfs_commit_release_pages+0x339/0xbd0 [nfs] nfs_commit_release+0x51/0xb0 [nfs] rpc_free_task+0xee/0x160 rpc_async_release+0x5d/0xb0 process_one_work+0x9b0/0x1890 worker_thread+0x75a/0x10a0 kthread+0x3d3/0x4d0 ret_from_fork+0x669/0xa50 ret_from_fork_asm+0x11/0x20 The buggy address belongs to the object at ffff888118af2040\x0a which belongs to the cache nfs_page of size 96 The buggy address is located 24 bytes inside of\x0a freed 96-byte region [ffff888118af2040, ffff888118af20a0) The buggy address belongs to the physical page: page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x118af2 head: order:1 mapcount:0 entire_mapcount:0 nr_pages_mapped:0 pincount:0 flags: 0x4000000000000040(head|zone=2) page_type: f5(slab) raw: 4000000000000040 ffff88818cf2c4c0 ffffea000e61b990 ffffea0004e7d110 raw: 0000000000000000 0000000800190019 00000000f5000000 0000000000000000 head: 4000000000000040 ffff88818cf2c4c0 ffffea000e61b990 ffffea0004e7d110 head: 0000000000000000 0000000800190019 00000000f5000000 0000000000000000 head: 4000000000000001 ffffffffffffff81 00000000ffffffff 00000000ffffffff head: ffffffffffffffff 0000000000000000 00000000ffffffff 0000000000000002 page dumped because: kasan: bad access detected page_owner tracks the page as allocated page last allocated via order 1, migratetype Unmovable, gfp_mask 0xd20c0(__GFP_IO|__GFP_FS|__GFP_NOWARN|__GFP_NORETRY|__GFP_COMP|__GFP_NOMEMALLOC), pid 121997, tgid 121997 (rsync), ts 31643290274577, free_ts 31642154777182 post_alloc_hook+0xd1/0x100 get_page_from_freelist+0xbad/0x2910 __alloc_frozen_pages_noprof+0x1c6/0x4a0 allocate_slab+0x330/0x620 ___slab_alloc+0xe9/0x930 kmem_cache_alloc_noprof+0x35b/0x4e0 nfs_page_create+0x152/0x460 [nfs] nfs_page_create_from_folio+0x7e/0x210 [nfs] nfs_update_folio+0x7a9/0x32a0 [nfs] nfs_write_end+0x290/0xc60 [nfs] generic_perform_write+0x4ce/0x990 nfs_file_write+0x6b3/0xce0 [nfs] vfs_write+0x63c/0xfa0 ksys_write+0x122/0x240 do_syscall_64+0xc3/0x13f0 entry_SYSCALL_64_after_hwframe+0x4b/0x53 page last free pid 122202 tgid 122202 stack trace: __free_frozen_pages+0x6da/0xf30 qlist_free_all+0x53/0x130 kasan_quarantine_reduce+0x198/0x1f0 __kasan_slab_alloc+0x46/0x70 kmem_cache_alloc_noprof+0x1ab/0x4e0 __alloc_object+0x2f/0x230 __create_object+0x22/0x80 kmem_cache_alloc_node_noprof+0x416/0x4d0 __alloc_skb+0x146/0x6e0 tcp_stream_alloc_skb+0x35/0x660 tcp_sendmsg_locked+0x1746/0x4260 tcp_sendmsg+0x2f/0x40 inet_sendmsg+0x9e/0xe0 __sock_sendmsg+0xd9/0x180 sock_sendmsg+0x122/0x200 xprt_sock_sendmsg+0x4ff/0x9a0 Memory state around the buggy address: ffff888118af1f00: 00 00 00 00 00 00 00 00 00 00 00 00 00 fc fc fc ffff888118af1f80: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc >ffff888118af2000: fc fc fc fc fc fc fc fc fa fb fb fb fb fb fb fb ^ ffff888118af2080: fb fb fb fb fc fc fc fc fc fc fc fc fc fc fc fc ffff888118af2100: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc ================================================================== Reviewed-by Jeff Layton Fixes: 5d3869a41f36 ("NFS: fix writeback in presence of errors") Cc: Olga Kornievskaia Cc: Trond Myklebust Cc: Anna Schumaker Cc: linux-nfs@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Dave Jones Signed-off-by: Trond Myklebust --- fs/nfs/write.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 3134bb17f3e3..d7c399763ad9 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -927,7 +927,7 @@ static void nfs_write_completion(struct nfs_pgio_header *hdr) } if (nfs_write_need_commit(hdr)) { struct nfs_open_context *ctx = - hdr->req->wb_lock_context->open_context; + req->wb_lock_context->open_context; /* Reset wb_nio, since the write was successful. */ req->wb_nio = 0; From eb48730bb827d1550401a5d391903f9d90b493c8 Mon Sep 17 00:00:00 2001 From: Zhenghang Xiao Date: Tue, 26 May 2026 18:53:28 +0800 Subject: [PATCH 058/257] xfrm: iptfs: fix use-after-free on first_skb in __input_process_payload MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit __input_process_payload() stores first_skb into xtfs->ra_newskb under drop_lock when starting partial reassembly, then unlocks and breaks out of the processing loop. The post-loop check reads xtfs->ra_newskb without the lock to decide whether first_skb is still owned: if (first_skb && first_iplen && !defer && first_skb != xtfs->ra_newskb) Between spin_unlock and this read, a concurrent CPU running iptfs_reassem_cont() (or the drop_timer hrtimer) can complete reassembly, NULL xtfs->ra_newskb, and free the skb. The check then evaluates first_skb != NULL as true, and pskb_trim/ip_summed/consume_skb operate on the freed skb — a use-after-free in skbuff_head_cache. Replace the unlocked read with a local bool that records whether first_skb was handed to the reassembly state in the current call. The flag is set after the existing spin_unlock, before the break, using the pointer equality that is stable at that point (first_skb == skb iff first_skb was stored in ra_newskb). Fixes: 3f3339885fb3 ("xfrm: iptfs: add reusing received skb for the tunnel egress packet") Signed-off-by: Zhenghang Xiao Signed-off-by: Steffen Klassert --- net/xfrm/xfrm_iptfs.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/xfrm/xfrm_iptfs.c b/net/xfrm/xfrm_iptfs.c index 62ba828632f1..aea63a000d1d 100644 --- a/net/xfrm/xfrm_iptfs.c +++ b/net/xfrm/xfrm_iptfs.c @@ -954,6 +954,7 @@ static bool __input_process_payload(struct xfrm_state *x, u32 data, u32 first_iplen, iphlen, iplen, remaining, tail; u32 capturelen; u64 seq; + bool first_skb_partial = false; xtfs = x->mode_data; net = xs_net(x); @@ -1161,6 +1162,7 @@ static bool __input_process_payload(struct xfrm_state *x, u32 data, spin_unlock(&xtfs->drop_lock); + first_skb_partial = (first_skb == skb); break; } @@ -1172,7 +1174,7 @@ static bool __input_process_payload(struct xfrm_state *x, u32 data, /* this should not happen from the above code */ XFRM_INC_STATS(net, LINUX_MIB_XFRMINIPTFSERROR); - if (first_skb && first_iplen && !defer && first_skb != xtfs->ra_newskb) { + if (first_skb && first_iplen && !defer && !first_skb_partial) { /* first_skb is queued b/c !defer and not partial */ if (pskb_trim(first_skb, first_iplen)) { /* error trimming */ From 6d99479799c69c3cb588fcda19c81d8f61d64ecd Mon Sep 17 00:00:00 2001 From: Qing Wang Date: Tue, 2 Jun 2026 11:08:54 +0800 Subject: [PATCH 059/257] rseq: Fix using an uninitialized stack variable in rseq_exit_user_update() There is an bug in which an uninitialized stack variable is used in rseq_exit_user_update() as reported by syzbot: BUG: KMSAN: kernel-infoleak in rseq_set_ids_get_csaddr include/linux/rseq_entry.h:502 [inline] The local variable: struct rseq_ids ids = { .cpu_id = task_cpu(t), .mm_cid = task_mm_cid(t), .node_id = cpu_to_node(ids.cpu_id), }; According to the C standard, the evaluation order of expressions in an initializer list is indeterminately sequenced. The compiler (Clang, in this KMSAN build) evaluates `cpu_to_node(ids.cpu_id)` *before* `ids.cpu_id` is initialized with `task_cpu(t)`. This is fixed by moving the assignment of ids.node_id outside the structure initialization. Fixes: 82f572449cfe ("rseq: Implement read only ABI enforcement for optimized RSEQ V2 mode") Closes: https://syzkaller.appspot.com/bug?extid=185a631927096f9da2fc Reported-by: syzbot+185a631927096f9da2fc@syzkaller.appspotmail.com Signed-off-by: Qing Wang Signed-off-by: Peter Zijlstra (Intel) Acked-by: Mark Rutland Link: https://patch.msgid.link/20260602030854.574038-1-wangqing7171@gmail.com --- include/linux/rseq_entry.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/linux/rseq_entry.h b/include/linux/rseq_entry.h index 63bc72086e75..ed9da6e41a2a 100644 --- a/include/linux/rseq_entry.h +++ b/include/linux/rseq_entry.h @@ -635,10 +635,11 @@ static __always_inline bool rseq_exit_user_update(struct pt_regs *regs, struct t return true; } + int cpu = task_cpu(t); struct rseq_ids ids = { - .cpu_id = task_cpu(t), + .cpu_id = cpu, .mm_cid = task_mm_cid(t), - .node_id = cpu_to_node(ids.cpu_id), + .node_id = cpu_to_node(cpu), }; return rseq_update_usr(t, regs, &ids); From 29e8751c1dd278262fb4cd234e8909287d4189d4 Mon Sep 17 00:00:00 2001 From: Claudio Imbrenda Date: Tue, 2 Jun 2026 16:23:47 +0200 Subject: [PATCH 060/257] KVM: s390: Fix _gmap_unmap_crste() In _gmap_unmap_crste(), the crste to be unmapped is zapped calling gmap_crstep_xchg_atomic() exactly once, and expecting it to succeed. This is a reasonable sanity check, since kvm->mmu_lock is being held in write mode, and thus no races should be possible. An upcoming patch will change the behaviour of gmap_crstep_xchg_atomic() to return false and clear the vsie_notif bit if the operation triggers an unshadow operation. With the new behaviour, an unmap operation that triggers an unshadow would cause the VM to be killed. Prepare for the change by checking if the vsie_notif bit was set in the old crste if gmap_crstep_xchg_atomic() fails the first time, and try a second time. The second time no failures are allowed. Fixes: b827ef02f409 ("KVM: s390: Remove non-atomic dat_crstep_xchg()") Fixes: a2c17f9270cc ("KVM: s390: New gmap code") Signed-off-by: Claudio Imbrenda Message-ID: <20260602142356.169458-2-imbrenda@linux.ibm.com> --- arch/s390/kvm/gmap.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/arch/s390/kvm/gmap.c b/arch/s390/kvm/gmap.c index 957126ab991c..52d55ddea8d4 100644 --- a/arch/s390/kvm/gmap.c +++ b/arch/s390/kvm/gmap.c @@ -395,15 +395,28 @@ static long _gmap_unmap_crste(union crste *crstep, gfn_t gfn, gfn_t next, struct struct gmap_unmap_priv *priv = walk->priv; struct folio *folio = NULL; union crste old = *crstep; + bool ok; if (!old.h.fc) return 0; if (old.s.fc1.pr && test_bit(GMAP_FLAG_EXPORT_ON_UNMAP, &priv->gmap->flags)) folio = phys_to_folio(crste_origin_large(old)); - /* No races should happen because kvm->mmu_lock is held in write mode */ - KVM_BUG_ON(!gmap_crstep_xchg_atomic(priv->gmap, crstep, old, _CRSTE_EMPTY(old.h.tt), gfn), - priv->gmap->kvm); + /* + * No races should happen because kvm->mmu_lock is held in write mode, + * but the unmap operation could have triggered an unshadow, which + * causes gmap_crstep_xchg_atomic() to return false and clear the + * vsie_notif bit. Allow the operation to fail once, if the old crste + * had the vsie_notif bit set. A second failure is not allowed, for + * the reasons above. + */ + ok = gmap_crstep_xchg_atomic(priv->gmap, crstep, old, _CRSTE_EMPTY(old.h.tt), gfn); + if (!ok) { + KVM_BUG_ON(!old.s.fc1.vsie_notif, priv->gmap->kvm); + old.s.fc1.vsie_notif = 0; + ok = gmap_crstep_xchg_atomic(priv->gmap, crstep, old, _CRSTE_EMPTY(old.h.tt), gfn); + KVM_BUG_ON(!ok, priv->gmap->kvm); + } if (folio) uv_convert_from_secure_folio(folio); From d1adc098ce08893c92fce3db63f7bb750fbb4c30 Mon Sep 17 00:00:00 2001 From: Claudio Imbrenda Date: Tue, 2 Jun 2026 16:23:48 +0200 Subject: [PATCH 061/257] KVM: s390: Fix _gmap_crstep_xchg_atomic() The previous incorrect behaviour cleared the vsie_notif bit without returning false, which allowed shadow crstes to be installed without the vsie_notif bit. Return false and do not perform the operation if an unshadow event has been triggered, but still attempt to clear the vsie_notif bit from the existing crste. This will prevent the installation of shadow crstes without vsie_notif bit and will also prevent the caller from looping forever if it was not checking for the sg->invalidated flag. Fixes: b827ef02f409 ("KVM: s390: Remove non-atomic dat_crstep_xchg()") Fixes: a2c17f9270cc ("KVM: s390: New gmap code") Signed-off-by: Claudio Imbrenda Message-ID: <20260602142356.169458-3-imbrenda@linux.ibm.com> --- arch/s390/kvm/gmap.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/s390/kvm/gmap.h b/arch/s390/kvm/gmap.h index 742e42a31744..5374f21aaf8d 100644 --- a/arch/s390/kvm/gmap.h +++ b/arch/s390/kvm/gmap.h @@ -273,11 +273,14 @@ static inline bool __must_check _gmap_crstep_xchg_atomic(struct gmap *gmap, unio gmap_unmap_prefix(gmap, gfn, gfn + align); } if (crste_leaf(oldcrste) && crste_needs_unshadow(oldcrste, newcrste)) { + newcrste = oldcrste; newcrste.s.fc1.vsie_notif = 0; if (needs_lock) gmap_handle_vsie_unshadow_event(gmap, gfn); else _gmap_handle_vsie_unshadow_event(gmap, gfn); + dat_crstep_xchg_atomic(crstep, oldcrste, newcrste, gfn, gmap->asce); + return false; } if (!oldcrste.s.fc1.d && newcrste.s.fc1.d && !newcrste.s.fc1.s) SetPageDirty(phys_to_page(crste_origin_large(newcrste))); From 89fa757931dc0bcd64ef22b28d1d5ad00c5d02f4 Mon Sep 17 00:00:00 2001 From: Claudio Imbrenda Date: Tue, 2 Jun 2026 16:23:49 +0200 Subject: [PATCH 062/257] KVM: s390: Avoid potentially sleeping while atomic when zapping pages Factor out try_get_locked_pte(), which behaves similarly to get_locked_pte(), but does not attempt to allocate missing tables and performs a spin_trylock() instead of blocking. The new function is also exported, since it will be used in other patches. If intermediate entries are missing, there can be no pte swap entry to free, so it's safe to ignore them. This avoids potentially sleeping while atomic. Fixes: e38c884df921 ("KVM: s390: Switch to new gmap") Signed-off-by: Claudio Imbrenda Message-ID: <20260602142356.169458-4-imbrenda@linux.ibm.com> --- arch/s390/include/asm/gmap_helpers.h | 1 + arch/s390/mm/gmap_helpers.c | 117 ++++++++++++++++----------- 2 files changed, 73 insertions(+), 45 deletions(-) diff --git a/arch/s390/include/asm/gmap_helpers.h b/arch/s390/include/asm/gmap_helpers.h index 2d3ae421077e..d2b616604a46 100644 --- a/arch/s390/include/asm/gmap_helpers.h +++ b/arch/s390/include/asm/gmap_helpers.h @@ -12,5 +12,6 @@ void gmap_helper_zap_one_page(struct mm_struct *mm, unsigned long vmaddr); void gmap_helper_discard(struct mm_struct *mm, unsigned long vmaddr, unsigned long end); int gmap_helper_disable_cow_sharing(void); void gmap_helper_try_set_pte_unused(struct mm_struct *mm, unsigned long vmaddr); +pte_t *try_get_locked_pte(struct mm_struct *mm, unsigned long addr, spinlock_t **ptl); #endif /* _ASM_S390_GMAP_HELPERS_H */ diff --git a/arch/s390/mm/gmap_helpers.c b/arch/s390/mm/gmap_helpers.c index f8789ffcc05c..396207163ca6 100644 --- a/arch/s390/mm/gmap_helpers.c +++ b/arch/s390/mm/gmap_helpers.c @@ -34,6 +34,70 @@ static void ptep_zap_softleaf_entry(struct mm_struct *mm, softleaf_t entry) swap_put_entries_direct(entry, 1); } +/** + * try_get_locked_pte() - like get_locked_pte(), but atomic and with trylock + * @mm: the mm + * @vmaddr: the userspace virtual address whose pte is to be found + * @ptl: will be set to the pointer to the lock used to lock the pte in case + * of success. + * + * This function returns the pointer to the pte corresponding to @addr in @mm, + * similarly to get_locked_pte(). Unlike get_locked_pte(), no attempt is made + * to allocate missing page tables. If a missing or large entry is found, the + * function will return NULL. If the ptl lock is contended, %-EAGAIN is + * returned. + * + * In case of success, *@ptl will point to the locked pte lock for the returned + * pte, like get_locked_pte() does. + * + * Context: mmap_lock or vma lock for read or for write needs to be held. + * Return: + * * %NULL if the pte cannot be reached. + * * %-EAGAIN if the pte can be reached, but cannot be locked. + * * the pointer to the pte corresponding to @addr in @mm, if it can be reached + * and locked. + */ +pte_t *try_get_locked_pte(struct mm_struct *mm, unsigned long vmaddr, spinlock_t **ptl) +{ + pmd_t *pmdp, pmd, pmdval; + pud_t *pudp, pud; + p4d_t *p4dp, p4d; + pgd_t *pgdp, pgd; + pte_t *ptep; + + pgdp = pgd_offset(mm, vmaddr); + pgd = pgdp_get(pgdp); + if (pgd_none(pgd) || !pgd_present(pgd)) + return NULL; + p4dp = p4d_offset(pgdp, vmaddr); + p4d = p4dp_get(p4dp); + if (p4d_none(p4d) || !p4d_present(p4d)) + return NULL; + pudp = pud_offset(p4dp, vmaddr); + pud = pudp_get(pudp); + if (pud_none(pud) || pud_leaf(pud) || !pud_present(pud)) + return NULL; + pmdp = pmd_offset(pudp, vmaddr); + pmd = pmdp_get_lockless(pmdp); + if (pmd_none(pmd) || pmd_leaf(pmd) || !pmd_present(pmd)) + return NULL; + ptep = pte_offset_map_rw_nolock(mm, pmdp, vmaddr, &pmdval, ptl); + if (!ptep) + return NULL; + + if (spin_trylock(*ptl)) { + if (unlikely(!pmd_same(pmdval, pmdp_get_lockless(pmdp)))) { + pte_unmap_unlock(ptep, *ptl); + return ERR_PTR(-EAGAIN); + } + return ptep; + } + + pte_unmap(ptep); + return ERR_PTR(-EAGAIN); +} +EXPORT_SYMBOL_GPL(try_get_locked_pte); + /** * gmap_helper_zap_one_page() - discard a page if it was swapped. * @mm: the mm @@ -46,7 +110,7 @@ static void ptep_zap_softleaf_entry(struct mm_struct *mm, softleaf_t entry) void gmap_helper_zap_one_page(struct mm_struct *mm, unsigned long vmaddr) { struct vm_area_struct *vma; - spinlock_t *ptl; + spinlock_t *ptl; /* Lock for the host (userspace) page table */ pte_t *ptep; mmap_assert_locked(mm); @@ -57,8 +121,8 @@ void gmap_helper_zap_one_page(struct mm_struct *mm, unsigned long vmaddr) return; /* Get pointer to the page table entry */ - ptep = get_locked_pte(mm, vmaddr, &ptl); - if (unlikely(!ptep)) + ptep = try_get_locked_pte(mm, vmaddr, &ptl); + if (IS_ERR_OR_NULL(ptep)) return; if (pte_swap(*ptep)) { ptep_zap_softleaf_entry(mm, softleaf_from_pte(*ptep)); @@ -113,37 +177,9 @@ EXPORT_SYMBOL_GPL(gmap_helper_discard); */ void gmap_helper_try_set_pte_unused(struct mm_struct *mm, unsigned long vmaddr) { - pmd_t *pmdp, pmd, pmdval; - pud_t *pudp, pud; - p4d_t *p4dp, p4d; - pgd_t *pgdp, pgd; spinlock_t *ptl; /* Lock for the host (userspace) page table */ pte_t *ptep; - pgdp = pgd_offset(mm, vmaddr); - pgd = pgdp_get(pgdp); - if (pgd_none(pgd) || !pgd_present(pgd)) - return; - - p4dp = p4d_offset(pgdp, vmaddr); - p4d = p4dp_get(p4dp); - if (p4d_none(p4d) || !p4d_present(p4d)) - return; - - pudp = pud_offset(p4dp, vmaddr); - pud = pudp_get(pudp); - if (pud_none(pud) || pud_leaf(pud) || !pud_present(pud)) - return; - - pmdp = pmd_offset(pudp, vmaddr); - pmd = pmdp_get_lockless(pmdp); - if (pmd_none(pmd) || pmd_leaf(pmd) || !pmd_present(pmd)) - return; - - ptep = pte_offset_map_rw_nolock(mm, pmdp, vmaddr, &pmdval, &ptl); - if (!ptep) - return; - /* * Several paths exists that takes the ptl lock and then call the * mmu_notifier, which takes the mmu_lock. The unmap path, instead, @@ -156,21 +192,12 @@ void gmap_helper_try_set_pte_unused(struct mm_struct *mm, unsigned long vmaddr) * If the lock is contended the bit is not set and the deadlock is * avoided. */ - if (spin_trylock(ptl)) { - /* - * Make sure the pte we are touching is still the correct - * one. In theory this check should not be needed, but - * better safe than sorry. - * Disabling interrupts or holding the mmap lock is enough to - * guarantee that no concurrent updates to the page tables - * are possible. - */ - if (likely(pmd_same(pmdval, pmdp_get_lockless(pmdp)))) - __atomic64_or(_PAGE_UNUSED, (long *)ptep); - spin_unlock(ptl); - } + ptep = try_get_locked_pte(mm, vmaddr, &ptl); + if (IS_ERR_OR_NULL(ptep)) + return; - pte_unmap(ptep); + __atomic64_or(_PAGE_UNUSED, (long *)ptep); + pte_unmap_unlock(ptep, ptl); } EXPORT_SYMBOL_GPL(gmap_helper_try_set_pte_unused); From ca42c16638d5570c44842ff68a772c62e6dd0124 Mon Sep 17 00:00:00 2001 From: Claudio Imbrenda Date: Tue, 2 Jun 2026 16:23:50 +0200 Subject: [PATCH 063/257] KVM: s390: Fix guest / virtual address confusion in _essa_clear_cbrl() Until now, gmap_helper_zap_one_page() was being called with the guest absolute address, but it expects a userspace virtual address. This meant that in the best case the requested pages were not being discarded, and in the worst case that the wrong pages were being discarded. Fix this by converting the guest absolute address to host virtual before passing it to gmap_helper_zap_one_page(). Fixes: e38c884df921 ("KVM: s390: Switch to new gmap") Signed-off-by: Claudio Imbrenda Message-ID: <20260602142356.169458-5-imbrenda@linux.ibm.com> --- arch/s390/kvm/priv.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/arch/s390/kvm/priv.c b/arch/s390/kvm/priv.c index cc0553da14cb..447ec7ed423d 100644 --- a/arch/s390/kvm/priv.c +++ b/arch/s390/kvm/priv.c @@ -1188,6 +1188,7 @@ static void _essa_clear_cbrl(struct kvm_vcpu *vcpu, unsigned long *cbrl, int len union crste *crstep; union pgste pgste; union pte *ptep; + hva_t hva; int i; lockdep_assert_held(&vcpu->kvm->mmu_lock); @@ -1199,8 +1200,11 @@ static void _essa_clear_cbrl(struct kvm_vcpu *vcpu, unsigned long *cbrl, int len if (!ptep || ptep->s.pr) continue; pgste = pgste_get_lock(ptep); - if (pgste.usage == PGSTE_GPS_USAGE_UNUSED || pgste.zero) - gmap_helper_zap_one_page(vcpu->kvm->mm, cbrl[i]); + if (pgste.usage == PGSTE_GPS_USAGE_UNUSED || pgste.zero) { + hva = gpa_to_hva(vcpu->kvm, cbrl[i]); + if (!kvm_is_error_hva(hva)) + gmap_helper_zap_one_page(vcpu->kvm->mm, hva); + } pgste_set_unlock(ptep, pgste); } } From 6af5563e827913d3e14dbf12eefd5af4aa592739 Mon Sep 17 00:00:00 2001 From: Claudio Imbrenda Date: Tue, 2 Jun 2026 16:23:51 +0200 Subject: [PATCH 064/257] KVM: s390: vsie: Fix rmap handling in _do_shadow_crste() Fix _do_shadow_crste() to also apply a mask on the reverse address, to prevent spurious entries from being created, like already done in gmap_protect_rmap(). Fixes: e38c884df921 ("KVM: s390: Switch to new gmap") Signed-off-by: Claudio Imbrenda Message-ID: <20260602142356.169458-6-imbrenda@linux.ibm.com> --- arch/s390/kvm/gaccess.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/arch/s390/kvm/gaccess.c b/arch/s390/kvm/gaccess.c index 4f8d5592c9a9..20e28b183c1a 100644 --- a/arch/s390/kvm/gaccess.c +++ b/arch/s390/kvm/gaccess.c @@ -1466,15 +1466,17 @@ static int _do_shadow_crste(struct gmap *sg, gpa_t raddr, union crste *host, uni struct guest_fault *f, bool p) { union crste newcrste, oldcrste; - gfn_t gfn; + unsigned long mask; + gfn_t r_gfn; int rc; lockdep_assert_held(&sg->kvm->mmu_lock); lockdep_assert_held(&sg->parent->children_lock); - gfn = f->gfn & (is_pmd(*table) ? _SEGMENT_FR_MASK : _REGION3_FR_MASK); + mask = is_pmd(*table) ? _SEGMENT_FR_MASK : _REGION3_FR_MASK; + r_gfn = gpa_to_gfn(raddr) & mask; scoped_guard(spinlock, &sg->host_to_rmap_lock) - rc = gmap_insert_rmap(sg, gfn, gpa_to_gfn(raddr), host->h.tt); + rc = gmap_insert_rmap(sg, f->gfn & mask, r_gfn, host->h.tt); if (rc) return rc; @@ -1497,8 +1499,7 @@ static int _do_shadow_crste(struct gmap *sg, gpa_t raddr, union crste *host, uni return -EAGAIN; newcrste = _crste_fc1(f->pfn, oldcrste.h.tt, 0, !p); - gfn = gpa_to_gfn(raddr); - while (!dat_crstep_xchg_atomic(table, READ_ONCE(*table), newcrste, gfn, sg->asce)) + while (!dat_crstep_xchg_atomic(table, READ_ONCE(*table), newcrste, r_gfn, sg->asce)) ; return 0; } From 9a1dfbbd3506c4f7159feab5ef27434e80256fa5 Mon Sep 17 00:00:00 2001 From: Claudio Imbrenda Date: Tue, 2 Jun 2026 16:23:52 +0200 Subject: [PATCH 065/257] KVM: s390: Fix fault-in code Fix the fault-in code so that it does not return success if a concurrent unmap event invalidated the fault-in process between the best-effort lockless check and the proper check with lock. The new behaviour is to retry, like the best-effort lockless check already did. This prevents the fault-in handler from returning success without having actually faulted in the requested page. Fixes: e907ae530133 ("KVM: s390: Add helper functions for fault handling") Reviewed-by: Steffen Eiden Signed-off-by: Claudio Imbrenda Message-ID: <20260602142356.169458-7-imbrenda@linux.ibm.com> --- arch/s390/kvm/faultin.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/arch/s390/kvm/faultin.c b/arch/s390/kvm/faultin.c index ddf0ca71f374..cf542b0a7e8e 100644 --- a/arch/s390/kvm/faultin.c +++ b/arch/s390/kvm/faultin.c @@ -36,7 +36,8 @@ int kvm_s390_faultin_gfn(struct kvm_vcpu *vcpu, struct kvm *kvm, struct guest_fa struct kvm_s390_mmu_cache *mc = NULL; struct kvm_memory_slot *slot; unsigned long inv_seq; - int foll, rc = 0; + int rc = -EAGAIN; + int foll; foll = f->write_attempt ? FOLL_WRITE : 0; foll |= f->attempt_pfault ? FOLL_NOWAIT : 0; @@ -53,7 +54,7 @@ int kvm_s390_faultin_gfn(struct kvm_vcpu *vcpu, struct kvm *kvm, struct guest_fa return 0; } - while (1) { + while (rc == -EAGAIN) { f->valid = false; inv_seq = kvm->mmu_invalidate_seq; /* Pairs with the smp_wmb() in kvm_mmu_invalidate_end(). */ @@ -110,20 +111,19 @@ int kvm_s390_faultin_gfn(struct kvm_vcpu *vcpu, struct kvm *kvm, struct guest_fa if (!mmu_invalidate_retry_gfn(kvm, inv_seq, f->gfn)) { f->valid = true; rc = gmap_link(mc, kvm->arch.gmap, f, slot); - kvm_release_faultin_page(kvm, f->page, !!rc, f->write_attempt); - f->page = NULL; } + kvm_release_faultin_page(kvm, f->page, !!rc, f->write_attempt); } - kvm_release_faultin_page(kvm, f->page, true, false); if (rc == -ENOMEM) { rc = kvm_s390_mmu_cache_topup(mc); if (rc) return rc; - } else if (rc != -EAGAIN) { - return rc; + rc = -EAGAIN; } } + + return rc; } int kvm_s390_get_guest_page(struct kvm *kvm, struct guest_fault *f, gfn_t gfn, bool w) From 42546fc642e929de07459ff839a9f43a653ffb4e Mon Sep 17 00:00:00 2001 From: Claudio Imbrenda Date: Tue, 2 Jun 2026 16:23:53 +0200 Subject: [PATCH 066/257] KVM: s390: Lock pte when making page secure Make sure _kvm_s390_pv_make_secure() takes the pte lock for the given address when attempting to make the page secure. One of the steps in making the page secure is freezing the folio using folio_ref_freeze(), which temporarily sets the reference count to 0. Any attempt to get such a folio while frozen will fail and cause a warning to be printed. Other users of folio_ref_freeze() make sure that the page is not mapped while it's being frozen, thus preventing gup functions from being able to access it. For _kvm_s390_pv_make_secure(), this is not possible, because the page needs to be mapped in order for the import to succeed. By taking the pte lock, gup functions will be blocked until the import operation is done, thus avoiding the race. In theory this does not completely solve the issue: if a page is mapped through multiple mappings, locking one pte does not protect from calling gup on it through the other mapping. In practice this does not happen and it is a decent stopgap solution until a more correct solution is available. Fixes: e38c884df921 ("KVM: s390: Switch to new gmap") Signed-off-by: Claudio Imbrenda Message-ID: <20260602142356.169458-8-imbrenda@linux.ibm.com> --- arch/s390/kvm/pv.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/arch/s390/kvm/pv.c b/arch/s390/kvm/pv.c index c2dafd812a3b..4b865e75351c 100644 --- a/arch/s390/kvm/pv.c +++ b/arch/s390/kvm/pv.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "kvm-s390.h" #include "dat.h" #include "gaccess.h" @@ -73,6 +74,7 @@ static bool should_export_before_import(struct uv_cb_header *uvcb, struct mm_str struct pv_make_secure { void *uvcb; struct folio *folio; + struct kvm *kvm; int rc; bool needs_export; }; @@ -103,9 +105,21 @@ static void _kvm_s390_pv_make_secure(struct guest_fault *f) { struct pv_make_secure *priv = f->priv; struct folio *folio; + spinlock_t *ptl; /* pte lock from try_get_locked_pte() */ + pte_t *ptep; folio = pfn_folio(f->pfn); priv->rc = -EAGAIN; + + if (!mmap_read_trylock(priv->kvm->mm)) + return; + + ptep = try_get_locked_pte(priv->kvm->mm, gfn_to_hva(priv->kvm, f->gfn), &ptl); + if (IS_ERR_VALUE(ptep)) { + priv->rc = PTR_ERR(ptep); + goto out; + } + if (folio_trylock(folio)) { priv->rc = __kvm_s390_pv_make_secure(f, folio); if (priv->rc == -E2BIG || priv->rc == -EBUSY) { @@ -114,6 +128,11 @@ static void _kvm_s390_pv_make_secure(struct guest_fault *f) } folio_unlock(folio); } + + if (ptep) + pte_unmap_unlock(ptep, ptl); +out: + mmap_read_unlock(priv->kvm->mm); } /** @@ -127,7 +146,7 @@ static void _kvm_s390_pv_make_secure(struct guest_fault *f) */ int kvm_s390_pv_make_secure(struct kvm *kvm, unsigned long gaddr, void *uvcb) { - struct pv_make_secure priv = { .uvcb = uvcb }; + struct pv_make_secure priv = { .uvcb = uvcb, .kvm = kvm, }; struct guest_fault f = { .write_attempt = true, .gfn = gpa_to_gfn(gaddr), From 7b53b4801884d48551f26a17f00cdc0ae4640d64 Mon Sep 17 00:00:00 2001 From: Claudio Imbrenda Date: Tue, 2 Jun 2026 16:23:54 +0200 Subject: [PATCH 067/257] KVM: s390: Prevent memslots outside the ASCE range With KVM_S390_VM_MEM_LIMIT_SIZE, userspace can set the highest address allowed for the VM. Creating a memslot that lies over the maximum address does not make sense and is only a potential source of bugs. Prevent creation of memslots over the maximum address, and prevent the maximum address from being reduced below the end of existing memslots. Fixes: e38c884df921 ("KVM: s390: Switch to new gmap") Signed-off-by: Claudio Imbrenda Message-ID: <20260602142356.169458-9-imbrenda@linux.ibm.com> --- arch/s390/kvm/kvm-s390.c | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index e09960c2e6ed..ffb20a64d328 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -999,7 +999,10 @@ static int kvm_s390_set_mem_control(struct kvm *kvm, struct kvm_device_attr *att break; } case KVM_S390_VM_MEM_LIMIT_SIZE: { + struct kvm_memslots *slots; + struct kvm_memory_slot *ms; unsigned long new_limit; + int bkt; if (kvm_is_ucontrol(kvm)) return -EINVAL; @@ -1007,6 +1010,9 @@ static int kvm_s390_set_mem_control(struct kvm *kvm, struct kvm_device_attr *att if (get_user(new_limit, (u64 __user *)attr->addr)) return -EFAULT; + guard(mutex)(&kvm->lock); + + new_limit = ALIGN(new_limit, HPAGE_SIZE); if (kvm->arch.mem_limit != KVM_S390_NO_MEM_LIMIT && new_limit > kvm->arch.mem_limit) return -E2BIG; @@ -1014,12 +1020,27 @@ static int kvm_s390_set_mem_control(struct kvm *kvm, struct kvm_device_attr *att if (!new_limit) return -EINVAL; - ret = -EBUSY; - if (!kvm->created_vcpus) - ret = gmap_set_limit(kvm->arch.gmap, gpa_to_gfn(new_limit)); + if (kvm->created_vcpus) + return -EBUSY; + + ret = 0; + scoped_guard(mutex, &kvm->slots_lock) { + slots = kvm_memslots(kvm); + if (slots && !kvm_memslots_empty(slots)) { + kvm_for_each_memslot(ms, bkt, slots) { + if (gpa_to_gfn(new_limit) < ms->base_gfn + ms->npages) { + ret = -EBUSY; + break; + } + } + } + if (!ret) + ret = gmap_set_limit(kvm->arch.gmap, gpa_to_gfn(new_limit)); + } + if (ret) + break; VM_EVENT(kvm, 3, "SET: max guest address: %lu", new_limit); - VM_EVENT(kvm, 3, "New guest asce: 0x%p", - (void *)kvm->arch.gmap->asce.val); + VM_EVENT(kvm, 3, "New guest asce: 0x%p", (void *)kvm->arch.gmap->asce.val); break; } default: @@ -5672,6 +5693,8 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm, return -EINVAL; if ((new->base_gfn + new->npages) * PAGE_SIZE > kvm->arch.mem_limit) return -EINVAL; + if (!asce_contains_gfn(kvm->arch.gmap->asce, new->base_gfn + new->npages - 1)) + return -EINVAL; } if (!kvm->arch.migration_mode) From 6ae67dcac742529210a23dac6c9a7de1acfb52a3 Mon Sep 17 00:00:00 2001 From: Claudio Imbrenda Date: Tue, 2 Jun 2026 16:23:55 +0200 Subject: [PATCH 068/257] KVM: s390: Fix possible reference leak in fault-in code If kvm_s390_new_mmu_cache() fails, kvm_s390_faultin_gfn() returns without releasing the faulted page. Fix this by moving the allocation of the memory cache outside of the loop. There is no reason to check at every iteration. Opportunistically fix a comment. Fixes: e907ae530133 ("KVM: s390: Add helper functions for fault handling") Signed-off-by: Claudio Imbrenda Message-ID: <20260602142356.169458-10-imbrenda@linux.ibm.com> --- arch/s390/kvm/faultin.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/arch/s390/kvm/faultin.c b/arch/s390/kvm/faultin.c index cf542b0a7e8e..fee80047bd94 100644 --- a/arch/s390/kvm/faultin.c +++ b/arch/s390/kvm/faultin.c @@ -54,6 +54,13 @@ int kvm_s390_faultin_gfn(struct kvm_vcpu *vcpu, struct kvm *kvm, struct guest_fa return 0; } + if (!mc) { + local_mc = kvm_s390_new_mmu_cache(); + if (!local_mc) + return -ENOMEM; + mc = local_mc; + } + while (rc == -EAGAIN) { f->valid = false; inv_seq = kvm->mmu_invalidate_seq; @@ -94,14 +101,7 @@ int kvm_s390_faultin_gfn(struct kvm_vcpu *vcpu, struct kvm *kvm, struct guest_fa if (is_error_pfn(f->pfn)) return -EFAULT; - if (!mc) { - local_mc = kvm_s390_new_mmu_cache(); - if (!local_mc) - return -ENOMEM; - mc = local_mc; - } - - /* Loop, will automatically release the faulted page. */ + /* Loop, release the faulted page. */ if (mmu_invalidate_retry_gfn_unsafe(kvm, inv_seq, f->gfn)) { kvm_release_faultin_page(kvm, f->page, true, false); continue; From c1edda54a0f713412f5914f9c9080856694bddca Mon Sep 17 00:00:00 2001 From: Claudio Imbrenda Date: Tue, 2 Jun 2026 16:23:56 +0200 Subject: [PATCH 069/257] KVM: s390: Remove ptep_zap_softleaf_entry() Migration entries do not need to be removed. The swap subsystem has been (and still is being) heavily reworked. The current implementation of ptep_zap_softleaf_entry() has been slowly modified and is now wrong, since it unconditionally calls swap_put_entries_direct() for both swap and migration entries. Remove ptep_zap_softleaf_entry() altogether, merge the path for proper swap entries directly in the only caller, and ignore migration entries. Fixes: 200197908dc4 ("KVM: s390: Refactor and split some gmap helpers") Signed-off-by: Claudio Imbrenda Message-ID: <20260602142356.169458-11-imbrenda@linux.ibm.com> --- arch/s390/mm/gmap_helpers.c | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/arch/s390/mm/gmap_helpers.c b/arch/s390/mm/gmap_helpers.c index 396207163ca6..1cfe4724fbe2 100644 --- a/arch/s390/mm/gmap_helpers.c +++ b/arch/s390/mm/gmap_helpers.c @@ -16,24 +16,6 @@ #include #include -/** - * ptep_zap_softleaf_entry() - discard a software leaf entry. - * @mm: the mm - * @entry: the software leaf entry that needs to be zapped - * - * Discards the given software leaf entry. If the leaf entry was an actual - * swap entry (and not a migration entry, for example), the actual swapped - * page is also discarded from swap. - */ -static void ptep_zap_softleaf_entry(struct mm_struct *mm, softleaf_t entry) -{ - if (softleaf_is_swap(entry)) - dec_mm_counter(mm, MM_SWAPENTS); - else if (softleaf_is_migration(entry)) - dec_mm_counter(mm, mm_counter(softleaf_to_folio(entry))); - swap_put_entries_direct(entry, 1); -} - /** * try_get_locked_pte() - like get_locked_pte(), but atomic and with trylock * @mm: the mm @@ -111,6 +93,7 @@ void gmap_helper_zap_one_page(struct mm_struct *mm, unsigned long vmaddr) { struct vm_area_struct *vma; spinlock_t *ptl; /* Lock for the host (userspace) page table */ + softleaf_t sl; pte_t *ptep; mmap_assert_locked(mm); @@ -124,8 +107,10 @@ void gmap_helper_zap_one_page(struct mm_struct *mm, unsigned long vmaddr) ptep = try_get_locked_pte(mm, vmaddr, &ptl); if (IS_ERR_OR_NULL(ptep)) return; - if (pte_swap(*ptep)) { - ptep_zap_softleaf_entry(mm, softleaf_from_pte(*ptep)); + sl = softleaf_from_pte(*ptep); + if (pte_swap(*ptep) && softleaf_is_swap(sl)) { + dec_mm_counter(mm, MM_SWAPENTS); + swap_put_entries_direct(sl, 1); pte_clear(mm, vmaddr, ptep); } pte_unmap_unlock(ptep, ptl); From 5629eec1a2829871d496f3042884cdc267612f6a Mon Sep 17 00:00:00 2001 From: "Mario Limonciello (AMD)" Date: Sat, 30 May 2026 17:04:34 +0200 Subject: [PATCH 070/257] cpufreq/amd-pstate: Fix setting EPP in performance mode EPP 0 is the only supported value in the performance policy. commit 798c47593cca ("cpufreq/amd-pstate: Add support for platform profile class") changed this while adding platform profile support to the dynamic EPP feature, but this actually wasn't necessary since platform profile writes disable manual EPP writes. Restore allowing writing EPP of 0 when in performance mode. Reviewed-by: Marco Scardovi Tested-by: Marco Scardovi Reported-by: Stuart Meckle Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221473 Closes: https://gitlab.freedesktop.org/upower/power-profiles-daemon/-/work_items/190 Fixes: 798c47593cca ("cpufreq/amd-pstate: Add support for platform profile class") Signed-off-by: Mario Limonciello (AMD) --- drivers/cpufreq/amd-pstate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c index 62b5d995281d..72df461e7b39 100644 --- a/drivers/cpufreq/amd-pstate.c +++ b/drivers/cpufreq/amd-pstate.c @@ -1428,7 +1428,7 @@ ssize_t store_energy_performance_preference(struct cpufreq_policy *policy, epp = cpudata->epp_default_dc; } - if (cpudata->policy == CPUFREQ_POLICY_PERFORMANCE) { + if (epp > 0 && cpudata->policy == CPUFREQ_POLICY_PERFORMANCE) { pr_debug("EPP cannot be set under performance policy\n"); return -EBUSY; } From b230b57bd6f242aaac3b8e62c7d18c69e1e30392 Mon Sep 17 00:00:00 2001 From: Yonatan Nachum Date: Tue, 26 May 2026 08:15:36 +0000 Subject: [PATCH 071/257] RDMA/efa: Validate SQ ring size against max LLQ size Validate the SQ ring size against the device's max LLQ size. This ensures that when using 128-byte WQEs, userspace cannot exceed the queue limits. On create QP, userspace provides the SQ ring size (depth x WQE size) which is validated against the max LLQ size. Fixes: 40909f664d27 ("RDMA/efa: Add EFA verbs implementation") Link: https://patch.msgid.link/r/20260526081536.1203553-1-ynachum@amazon.com Reviewed-by: Michael Margolin Signed-off-by: Yonatan Nachum Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/efa/efa_verbs.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/drivers/infiniband/hw/efa/efa_verbs.c b/drivers/infiniband/hw/efa/efa_verbs.c index 7bd0838ebc99..9b2b652800e4 100644 --- a/drivers/infiniband/hw/efa/efa_verbs.c +++ b/drivers/infiniband/hw/efa/efa_verbs.c @@ -613,7 +613,8 @@ static int qp_mmap_entries_setup(struct efa_qp *qp, } static int efa_qp_validate_cap(struct efa_dev *dev, - struct ib_qp_init_attr *init_attr) + struct ib_qp_init_attr *init_attr, + u32 sq_ring_size) { if (init_attr->cap.max_send_wr > dev->dev_attr.max_sq_depth) { ibdev_dbg(&dev->ibdev, @@ -622,6 +623,14 @@ static int efa_qp_validate_cap(struct efa_dev *dev, dev->dev_attr.max_sq_depth); return -EINVAL; } + + if (sq_ring_size > dev->dev_attr.max_llq_size) { + ibdev_dbg(&dev->ibdev, + "qp: requested sq ring size[%u] exceeds the max[%u]\n", + sq_ring_size, dev->dev_attr.max_llq_size); + return -EINVAL; + } + if (init_attr->cap.max_recv_wr > dev->dev_attr.max_rq_depth) { ibdev_dbg(&dev->ibdev, "qp: requested receive wr[%u] exceeds the max[%u]\n", @@ -691,14 +700,6 @@ int efa_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *init_attr, ucontext = rdma_udata_to_drv_context(udata, struct efa_ucontext, ibucontext); - err = efa_qp_validate_cap(dev, init_attr); - if (err) - goto err_out; - - err = efa_qp_validate_attr(dev, init_attr); - if (err) - goto err_out; - err = ib_copy_validate_udata_in_cm(udata, cmd, driver_qp_type, 0); if (err) goto err_out; @@ -720,6 +721,14 @@ int efa_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *init_attr, goto err_out; } + err = efa_qp_validate_cap(dev, init_attr, cmd.sq_ring_size); + if (err) + goto err_out; + + err = efa_qp_validate_attr(dev, init_attr); + if (err) + goto err_out; + create_qp_params.uarn = ucontext->uarn; create_qp_params.pd = to_epd(ibqp->pd)->pdn; From c1ca14ca227e92101c7ae597213275b60f4212c6 Mon Sep 17 00:00:00 2001 From: Naveen Kumar Chaudhary Date: Mon, 1 Jun 2026 09:27:46 +0530 Subject: [PATCH 072/257] clockevents: Fix duplicate type specifier in stub function parameter The stub for arch_inlined_clockevent_set_next_coupled() has 'u64 u64 cycles' in its parameter list. Since u64 is a typedef, the compiler parses the second 'u64' as the parameter name, making 'cycles' an unused token. Remove the duplicate so the parameter is correctly named. Fixes: 89f951a1e8ad ("clockevents: Provide support for clocksource coupled comparators") Signed-off-by: Naveen Kumar Chaudhary Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/7tostpvxzdn6tobmyow63a5rweatls5kux3scqp2vzhe7mv6uq@ecr746b4hyhf --- kernel/time/clockevents.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/time/clockevents.c b/kernel/time/clockevents.c index 5e22697b098d..0014d163f989 100644 --- a/kernel/time/clockevents.c +++ b/kernel/time/clockevents.c @@ -301,7 +301,7 @@ static int clockevents_program_min_delta(struct clock_event_device *dev) #include #else static __always_inline void -arch_inlined_clockevent_set_next_coupled(u64 u64 cycles, struct clock_event_device *dev) { } +arch_inlined_clockevent_set_next_coupled(u64 cycles, struct clock_event_device *dev) { } #endif static inline bool clockevent_set_next_coupled(struct clock_event_device *dev, ktime_t expires) From ce4abda5e12622f33450159e76c8f56d28d7f03d Mon Sep 17 00:00:00 2001 From: Naveen Kumar Chaudhary Date: Tue, 2 Jun 2026 23:37:37 +0530 Subject: [PATCH 073/257] time: Fix off-by-one in settimeofday() usec validation The validation check uses '>' instead of '>=' when comparing tv_usec against USEC_PER_SEC, allowing the value 1000000 through. After conversion to nanoseconds (*= 1000), this produces tv_nsec == NSEC_PER_SEC, violating the timespec invariant that tv_nsec must be less than NSEC_PER_SEC. Use '>=' to reject tv_usec values that are not in the valid range of 0 to 999999. Fixes: 5e0fb1b57bea ("y2038: time: avoid timespec usage in settimeofday()") Signed-off-by: Naveen Kumar Chaudhary Signed-off-by: Thomas Gleixner Acked-by: John Stultz Link: https://patch.msgid.link/4rikk44zew3s6577dugmx4jyblz7o5c57niuap6ct3td5yfm6w@gh7pcumg7qor --- kernel/time/time.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/time/time.c b/kernel/time/time.c index 0d832317d576..771cef87ad3b 100644 --- a/kernel/time/time.c +++ b/kernel/time/time.c @@ -207,7 +207,7 @@ SYSCALL_DEFINE2(settimeofday, struct __kernel_old_timeval __user *, tv, get_user(new_ts.tv_nsec, &tv->tv_usec)) return -EFAULT; - if (new_ts.tv_nsec > USEC_PER_SEC || new_ts.tv_nsec < 0) + if (new_ts.tv_nsec >= USEC_PER_SEC || new_ts.tv_nsec < 0) return -EINVAL; new_ts.tv_nsec *= NSEC_PER_USEC; From 3f7fbde4cdd4a25c199a60849897459a63907ca5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Wed, 13 May 2026 08:32:46 +0200 Subject: [PATCH 074/257] vdso/datastore: Mark vdso_k_*_data pointers as __ro_after_init MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These pointers are only modified once in vdso_setup_data_pages(), during the init phase. Make them read-only after that. Drop __refdata as that would conflict with __ro_after_init. Modpost does accept the reference from a __ro_after_init symbol to an __init one. Fixes: 05988dba1179 ("vdso/datastore: Allocate data pages dynamically") Signed-off-by: Thomas Weißschuh Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260513-vdso-ro-after-init-v1-1-4b51f74015a4@linutronix.de --- lib/vdso/datastore.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/vdso/datastore.c b/lib/vdso/datastore.c index cf5d784a4a5a..17d37b82ebc6 100644 --- a/lib/vdso/datastore.c +++ b/lib/vdso/datastore.c @@ -11,21 +11,21 @@ static u8 vdso_initdata[VDSO_NR_PAGES * PAGE_SIZE] __aligned(PAGE_SIZE) __initdata = {}; #ifdef CONFIG_GENERIC_GETTIMEOFDAY -struct vdso_time_data *vdso_k_time_data __refdata = +struct vdso_time_data *vdso_k_time_data __ro_after_init = (void *)&vdso_initdata[VDSO_TIME_PAGE_OFFSET * PAGE_SIZE]; static_assert(sizeof(struct vdso_time_data) <= PAGE_SIZE); #endif /* CONFIG_GENERIC_GETTIMEOFDAY */ #ifdef CONFIG_VDSO_GETRANDOM -struct vdso_rng_data *vdso_k_rng_data __refdata = +struct vdso_rng_data *vdso_k_rng_data __ro_after_init = (void *)&vdso_initdata[VDSO_RNG_PAGE_OFFSET * PAGE_SIZE]; static_assert(sizeof(struct vdso_rng_data) <= PAGE_SIZE); #endif /* CONFIG_VDSO_GETRANDOM */ #ifdef CONFIG_ARCH_HAS_VDSO_ARCH_DATA -struct vdso_arch_data *vdso_k_arch_data __refdata = +struct vdso_arch_data *vdso_k_arch_data __ro_after_init = (void *)&vdso_initdata[VDSO_ARCH_PAGES_START * PAGE_SIZE]; #endif /* CONFIG_ARCH_HAS_VDSO_ARCH_DATA */ From 74e144274af39935b0f410c0ee4d2b91c3730414 Mon Sep 17 00:00:00 2001 From: Ji'an Zhou Date: Tue, 2 Jun 2026 09:12:04 +0000 Subject: [PATCH 075/257] futex/requeue: Prevent NULL pointer dereference in remove_waiter() on self-deadlock When FUTEX_CMP_REQUEUE_PI requeues a non-top waiter that already owns the target PI futex, task_blocks_on_rt_mutex() returns -EDEADLK before setting waiter->task. The subsequent remove_waiter() in rt_mutex_start_proxy_lock() dereferences the NULL waiter->task, causing a kernel crash. Add a self-deadlock check for non-top waiters before calling rt_mutex_start_proxy_lock(), analogous to the top-waiter check in futex_lock_pi_atomic(). Fixes: 3bfdc63936dd4773109b7b8c280c0f3b5ae7d349 ("rtmutex: Use waiter::task instead of current in remove_waiter()") Signed-off-by: Ji'an Zhou Signed-off-by: Thomas Gleixner Cc: stable@vger.kernel.org --- kernel/futex/requeue.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/kernel/futex/requeue.c b/kernel/futex/requeue.c index b597cb3d17fc..1d99a84dc9ad 100644 --- a/kernel/futex/requeue.c +++ b/kernel/futex/requeue.c @@ -643,6 +643,12 @@ int futex_requeue(u32 __user *uaddr1, unsigned int flags1, continue; } + /* Self-deadlock: non-top waiter already owns the PI futex. */ + if (rt_mutex_owner(&pi_state->pi_mutex) == this->task) { + ret = -EDEADLK; + break; + } + ret = rt_mutex_start_proxy_lock(&pi_state->pi_mutex, this->rt_waiter, this->task); From ad0979fe053e9f2db82da82188256ef6eb41095a Mon Sep 17 00:00:00 2001 From: Zeyu WANG Date: Wed, 3 Jun 2026 01:09:09 +0800 Subject: [PATCH 076/257] Input: atkbd - add DMI quirk for Lenovo Yoga Air 14 (83QK) The Lenovo Yoga Air 14 (83QK) laptop keyboard becomes unresponsive after the standard atkbd init sequence. Controlled testing on the actual hardware shows the F5 (ATKBD_CMD_RESET_DIS / deactivate) command specifically corrupts the EC state, causing zero IRQ1 interrupts after init. Skipping only the deactivate command (while keeping F4 ENABLE) resolves the issue completely: both keystroke input and CapsLock LED toggle work correctly. The reverse test - skipping only F4 while keeping F5 - makes the problem worse (zero keystroke interrupts), confirming F5 is the sole culprit. Add a DMI quirk entry for LENOVO/83QK using the existing atkbd_deactivate_fixup callback, consistent with the existing entries for LG Electronics and HONOR FMB-P that address the same EC F5 deactivate issue. Signed-off-by: Zeyu WANG Link: https://patch.msgid.link/20260602170909.14725-1-zeyu.thomas.wang@gmail.com Cc: stable@vger.kernel.org Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/atkbd.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c index c8ad55f26ea8..217e66ee36a1 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c @@ -1923,6 +1923,14 @@ static const struct dmi_system_id atkbd_dmi_quirk_table[] __initconst = { }, .callback = atkbd_deactivate_fixup, }, + { + /* Lenovo Yoga Air 14 (83QK) */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "83QK"), + }, + .callback = atkbd_deactivate_fixup, + }, { } }; From 26eb7c0a7ab09d83eec833db6a5a2bc60b9d4d9a Mon Sep 17 00:00:00 2001 From: Chaitanya Kumar Borah Date: Mon, 1 Jun 2026 13:59:53 +0530 Subject: [PATCH 077/257] drm/i915: Fix color blob reference handling in intel_plane_state Take proper references for hw color blobs (degamma_lut, gamma_lut, ctm, lut_3d) in intel_plane_duplicate_state() and drop them in intel_plane_destroy_state(). v2: - handle blobs in hw state clear Cc: #v6.19+ Fixes: 3b7476e786c2 ("drm/i915/color: Add framework to program PRE/POST CSC LUT") Fixes: a78f1b6baf4d ("drm/i915/color: Add framework to program CSC") Fixes: 65db7a1f9cf7 ("drm/i915/color: Add 3D LUT to color pipeline") Reviewed-by: Pranay Samala #v1 Reviewed-by: Uma Shankar Signed-off-by: Chaitanya Kumar Borah Signed-off-by: Uma Shankar Link: https://patch.msgid.link/20260601082953.128539-4-chaitanya.kumar.borah@intel.com (cherry picked from commit c6eea1925154b6697fe22b217faab9bb30635e6b) Signed-off-by: Tvrtko Ursulin --- drivers/gpu/drm/i915/display/intel_plane.c | 27 ++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_plane.c b/drivers/gpu/drm/i915/display/intel_plane.c index 82f445c83158..07eae4176dad 100644 --- a/drivers/gpu/drm/i915/display/intel_plane.c +++ b/drivers/gpu/drm/i915/display/intel_plane.c @@ -144,6 +144,15 @@ intel_plane_duplicate_state(struct drm_plane *plane) if (intel_state->hw.fb) drm_framebuffer_get(intel_state->hw.fb); + if (intel_state->hw.degamma_lut) + drm_property_blob_get(intel_state->hw.degamma_lut); + if (intel_state->hw.gamma_lut) + drm_property_blob_get(intel_state->hw.gamma_lut); + if (intel_state->hw.ctm) + drm_property_blob_get(intel_state->hw.ctm); + if (intel_state->hw.lut_3d) + drm_property_blob_get(intel_state->hw.lut_3d); + return &intel_state->uapi; } @@ -167,6 +176,16 @@ intel_plane_destroy_state(struct drm_plane *plane, __drm_atomic_helper_plane_destroy_state(&plane_state->uapi); if (plane_state->hw.fb) drm_framebuffer_put(plane_state->hw.fb); + + if (plane_state->hw.degamma_lut) + drm_property_blob_put(plane_state->hw.degamma_lut); + if (plane_state->hw.gamma_lut) + drm_property_blob_put(plane_state->hw.gamma_lut); + if (plane_state->hw.ctm) + drm_property_blob_put(plane_state->hw.ctm); + if (plane_state->hw.lut_3d) + drm_property_blob_put(plane_state->hw.lut_3d); + kfree(plane_state); } @@ -317,6 +336,14 @@ static void intel_plane_clear_hw_state(struct intel_plane_state *plane_state) { if (plane_state->hw.fb) drm_framebuffer_put(plane_state->hw.fb); + if (plane_state->hw.degamma_lut) + drm_property_blob_put(plane_state->hw.degamma_lut); + if (plane_state->hw.gamma_lut) + drm_property_blob_put(plane_state->hw.gamma_lut); + if (plane_state->hw.ctm) + drm_property_blob_put(plane_state->hw.ctm); + if (plane_state->hw.lut_3d) + drm_property_blob_put(plane_state->hw.lut_3d); memset(&plane_state->hw, 0, sizeof(plane_state->hw)); } From 4db207599acfc9d676340daa2dc6b52bfca17db4 Mon Sep 17 00:00:00 2001 From: Kendall Willis Date: Wed, 6 May 2026 22:16:45 -0500 Subject: [PATCH 078/257] pmdomain: ti_sci: add wakeup constraint to parent devices of wakeup source Set wakeup constraint for any device in a wakeup path. All parent devices of a wakeup device should not be turned off during suspend. This ensures the wakeup device is kept on while the system is suspended. Cc: stable@vger.kernel.org Fixes: 9d8aa0dd3be4 ("pmdomain: ti_sci: add wakeup constraint management") Reported-by: Vitor Soares Closes: https://lore.kernel.org/linux-pm/c0fe43a2339c802e9ce5900092cd530a2ba17a6b.camel@gmail.com/ Signed-off-by: Kendall Willis Reviewed-by: Sebin Francis Signed-off-by: Ulf Hansson --- drivers/pmdomain/ti/ti_sci_pm_domains.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pmdomain/ti/ti_sci_pm_domains.c b/drivers/pmdomain/ti/ti_sci_pm_domains.c index 18d33bc35dee..949e4115f930 100644 --- a/drivers/pmdomain/ti/ti_sci_pm_domains.c +++ b/drivers/pmdomain/ti/ti_sci_pm_domains.c @@ -86,7 +86,7 @@ static inline void ti_sci_pd_set_wkup_constraint(struct device *dev) const struct ti_sci_handle *ti_sci = pd->parent->ti_sci; int ret; - if (device_may_wakeup(dev)) { + if (device_may_wakeup(dev) || device_wakeup_path(dev)) { /* * If device can wakeup using IO daisy chain wakeups, * we do not want to set a constraint. From fba0510cd62666951dcc0221527edc0c47ae6599 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 21 May 2026 10:36:27 +0200 Subject: [PATCH 079/257] pmdomain: imx: fix OF node refcount for_each_child_of_node_scoped() decrements the reference count of the nod after each iteration. Assigning it without incrementing the refcount to a dynamically allocated platform device will result in a double put in platform_device_release(). Add the missing call to of_node_get(). Cc: stable@vger.kernel.org Fixes: 3e4d109ee8fc ("pmdomain: imx: gpc: Simplify with scoped for each OF child loop") Signed-off-by: Bartosz Golaszewski Signed-off-by: Ulf Hansson --- drivers/pmdomain/imx/gpc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pmdomain/imx/gpc.c b/drivers/pmdomain/imx/gpc.c index de695f1944ab..42e50c9b4fb9 100644 --- a/drivers/pmdomain/imx/gpc.c +++ b/drivers/pmdomain/imx/gpc.c @@ -487,7 +487,7 @@ static int imx_gpc_probe(struct platform_device *pdev) domain->ipg_rate_mhz = ipg_rate_mhz; pd_pdev->dev.parent = &pdev->dev; - pd_pdev->dev.of_node = np; + pd_pdev->dev.of_node = of_node_get(np); pd_pdev->dev.fwnode = of_fwnode_handle(np); ret = platform_device_add(pd_pdev); From 4793e8a6e20e4c17ed4e755ea40e5912cc3539af Mon Sep 17 00:00:00 2001 From: Gabriele Monaco Date: Mon, 1 Jun 2026 17:38:28 +0200 Subject: [PATCH 080/257] rv: Fix __user specifier usage in extract_params() The attributes variables extracted from syscalls in the helper are both defined with the __user specifier although only the actual pointer to user data should be marked. Remove the __user specifier from attr. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202604150820.Ny143u6X-lkp@intel.com Fixes: b133207deb72 ("rv: Add nomiss deadline monitor") Reviewed-by: Wen Yang Reviewed-by: Nam Cao Link: https://lore.kernel.org/r/20260601153840.124372-2-gmonaco@redhat.com Signed-off-by: Gabriele Monaco --- kernel/trace/rv/monitors/deadline/deadline.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/trace/rv/monitors/deadline/deadline.h b/kernel/trace/rv/monitors/deadline/deadline.h index 0bbfd2543329..78fca873d61e 100644 --- a/kernel/trace/rv/monitors/deadline/deadline.h +++ b/kernel/trace/rv/monitors/deadline/deadline.h @@ -95,7 +95,8 @@ static inline u8 get_server_type(struct task_struct *tsk) static inline int extract_params(struct pt_regs *regs, long id, pid_t *pid_out) { size_t size = offsetofend(struct sched_attr, sched_flags); - struct sched_attr __user *uattr, attr; + struct sched_attr __user *uattr; + struct sched_attr attr; int new_policy = -1, ret; unsigned long args[6]; From e32d7f404d7d9dac307c1cd9a1fe132fa62ab6d6 Mon Sep 17 00:00:00 2001 From: Gabriele Monaco Date: Mon, 1 Jun 2026 17:38:29 +0200 Subject: [PATCH 081/257] rv: Reset per-task DA monitors before releasing the slot Per-task monitors use task_mon_slot to determine which slot in the array to use for the monitor. During destruction, this slot is returned but this is done before resetting the monitor. As a result, the monitor's reset is in fact resetting a slot that is outside of the array (RV_PER_TASK_MONITOR_INIT). Release the slot only after the reset to avoid out-of-bound memory access. Fixes: f5587d1b6ec93 ("rv: Add Hybrid Automata monitor type") Cc: stable@vger.kernel.org Suggested-by: Wen Yang Reviewed-by: Wen Yang Reviewed-by: Nam Cao Link: https://lore.kernel.org/r/20260601153840.124372-3-gmonaco@redhat.com Signed-off-by: Gabriele Monaco --- include/rv/da_monitor.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/rv/da_monitor.h b/include/rv/da_monitor.h index 39765ff6f098..1459fb3dfee6 100644 --- a/include/rv/da_monitor.h +++ b/include/rv/da_monitor.h @@ -309,10 +309,11 @@ static inline void da_monitor_destroy(void) WARN_ONCE(1, "Disabling a disabled monitor: " __stringify(MONITOR_NAME)); return; } - rv_put_task_monitor_slot(task_mon_slot); - task_mon_slot = RV_PER_TASK_MONITOR_INIT; da_monitor_reset_all(); + + rv_put_task_monitor_slot(task_mon_slot); + task_mon_slot = RV_PER_TASK_MONITOR_INIT; } #elif RV_MON_TYPE == RV_MON_PER_OBJ From c3d016ea823a9941ab8cbcef01a500821ff0cf16 Mon Sep 17 00:00:00 2001 From: Gabriele Monaco Date: Mon, 1 Jun 2026 17:38:30 +0200 Subject: [PATCH 082/257] rv: Prevent in-flight per-task handlers from using invalid slots Per-task monitors use a slot in the task_struct->rv[] array and store that locally (e.g. task_mon_slot), this slot is returned during the destruction process but currently hanlers can be running while that slot is returning and this race may lead to accessing an invalid slot. Synchronise with all in-flight tracepoint handlers using tracepoint_synchronize_unregister() before returning the slot. Fixes: f5587d1b6ec9 ("rv: Add Hybrid Automata monitor type") Fixes: a9769a5b9878 ("rv: Add support for LTL monitors") Suggested-by: Wen Yang Reviewed-by: Nam Cao Link: https://lore.kernel.org/r/20260601153840.124372-4-gmonaco@redhat.com Signed-off-by: Gabriele Monaco --- include/rv/da_monitor.h | 4 ++++ include/rv/ltl_monitor.h | 1 + 2 files changed, 5 insertions(+) diff --git a/include/rv/da_monitor.h b/include/rv/da_monitor.h index 1459fb3dfee6..cc97cc5dfbfd 100644 --- a/include/rv/da_monitor.h +++ b/include/rv/da_monitor.h @@ -302,6 +302,9 @@ static int da_monitor_init(void) /* * da_monitor_destroy - return the allocated slot + * + * Wait for all in-flight handlers before returning the slot to avoid + * out-of-bound accesses. */ static inline void da_monitor_destroy(void) { @@ -310,6 +313,7 @@ static inline void da_monitor_destroy(void) return; } + tracepoint_synchronize_unregister(); da_monitor_reset_all(); rv_put_task_monitor_slot(task_mon_slot); diff --git a/include/rv/ltl_monitor.h b/include/rv/ltl_monitor.h index eff60cd61106..38e792401f76 100644 --- a/include/rv/ltl_monitor.h +++ b/include/rv/ltl_monitor.h @@ -77,6 +77,7 @@ static void ltl_monitor_destroy(void) { rv_detach_trace_probe(name, task_newtask, handle_task_newtask); + tracepoint_synchronize_unregister(); rv_put_task_monitor_slot(ltl_monitor_slot); ltl_monitor_slot = RV_PER_TASK_MONITOR_INIT; } From 32171d828ab964dc1f05f2056a81477522a3361b Mon Sep 17 00:00:00 2001 From: Gabriele Monaco Date: Mon, 1 Jun 2026 17:38:31 +0200 Subject: [PATCH 083/257] rv: Ensure all pending probes terminate on per-obj monitor destroy The monitor disable/destroy sequence detaches all probes and resets the monitor's data, however it doesn't wait for pending probes. This is an issue with per-object monitors, which free the monitor storage. Call tracepoint_synchronize_unregister() to make sure to wait for all pending probes before destroying the monitor storage. Fixes: 4a24127bd6cb ("rv: Add support for per-object monitors in DA/HA") Reviewed-by: Wen Yang Reviewed-by: Nam Cao Link: https://lore.kernel.org/r/20260601153840.124372-5-gmonaco@redhat.com Signed-off-by: Gabriele Monaco --- include/rv/da_monitor.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/rv/da_monitor.h b/include/rv/da_monitor.h index cc97cc5dfbfd..a7e103654406 100644 --- a/include/rv/da_monitor.h +++ b/include/rv/da_monitor.h @@ -511,9 +511,10 @@ static inline void da_monitor_destroy(void) struct hlist_node *tmp; int bkt; + tracepoint_synchronize_unregister(); /* - * This function is called after all probes are disabled, we need only - * worry about concurrency against old events. + * This function is called after all probes are disabled and no longer + * pending, we can safely assume no concurrent user. */ synchronize_rcu(); hash_for_each_safe(da_monitor_ht, bkt, tmp, mon_storage, node) { From 2b9a8c27417eeef01967c6297def426a44776c04 Mon Sep 17 00:00:00 2001 From: Wen Yang Date: Mon, 1 Jun 2026 17:38:32 +0200 Subject: [PATCH 084/257] rv: Fix monitor start ordering and memory ordering for monitoring flag da_monitor_start() set monitoring=1 before calling da_monitor_init_hook(), may racing with the sched_switch handler: da_monitor_start() sched_switch handler ------------------------- --------------------------------- da_mon->monitoring = 1; if (da_monitoring(da_mon)) /* true */ ha_start_timer_ns(...); /* hrtimer->base == NULL, crash */ da_monitor_init_hook(da_mon); /* hrtimer_setup() sets base */ Fix the ordering and pair with release/acquire semantics: da_monitor_init_hook(da_mon); smp_store_release(&da_mon->monitoring, 1); /* da_monitor_start() */ return smp_load_acquire(&da_mon->monitoring); /* da_monitoring() */ On ARM64 a plain STR + LDR does not form a release-acquire pair, so the load can observe monitoring=1 while hrtimer->base is still NULL. The plain accesses are also data races under KCSAN. Use WRITE_ONCE for the monitoring=0 store in da_monitor_reset() to cover the reset path. Fixes: 792575348ff7 ("rv/include: Add deterministic automata monitor definition via C macros") Signed-off-by: Wen Yang Reviewed-by: Gabriele Monaco Reviewed-by: Nam Cao Link: https://lore.kernel.org/r/20260601153840.124372-6-gmonaco@redhat.com Signed-off-by: Gabriele Monaco --- include/rv/da_monitor.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/include/rv/da_monitor.h b/include/rv/da_monitor.h index a7e103654406..60dc39f2620a 100644 --- a/include/rv/da_monitor.h +++ b/include/rv/da_monitor.h @@ -82,7 +82,7 @@ static void react(enum states curr_state, enum events event) static inline void da_monitor_reset(struct da_monitor *da_mon) { da_monitor_reset_hook(da_mon); - da_mon->monitoring = 0; + WRITE_ONCE(da_mon->monitoring, 0); da_mon->curr_state = model_get_initial_state(); } @@ -95,8 +95,9 @@ static inline void da_monitor_reset(struct da_monitor *da_mon) static inline void da_monitor_start(struct da_monitor *da_mon) { da_mon->curr_state = model_get_initial_state(); - da_mon->monitoring = 1; da_monitor_init_hook(da_mon); + /* Pairs with smp_load_acquire in da_monitoring(). */ + smp_store_release(&da_mon->monitoring, 1); } /* @@ -104,7 +105,8 @@ static inline void da_monitor_start(struct da_monitor *da_mon) */ static inline bool da_monitoring(struct da_monitor *da_mon) { - return da_mon->monitoring; + /* Pairs with smp_store_release in da_monitor_start(). */ + return smp_load_acquire(&da_mon->monitoring); } /* From 713c9e1ea7623846f8e0bb657f1b9d211d1a61e6 Mon Sep 17 00:00:00 2001 From: Gabriele Monaco Date: Mon, 1 Jun 2026 17:38:33 +0200 Subject: [PATCH 085/257] rv: Do not rely on clean monitor when initialising HA Hybrid Automata monitors hook into the DA implementation when doing da_monitor_reset(). This function is called both on initialisation and teardown, HA monitors try to cancel a timer only when it's initialised relying on the da_mon->monitoring flag. This flag could however be corrupted during initialisation. This happens for instance on per-task monitors that share the same storage with different type of monitors like LTL or in case of races during a previous teardown. Stop relying on the monitoring flag during initialisation, assume that can have any value, so use a separate da_reset_state() skiping timer cancellation. New monitors (e.g. new tasks) are always zero-initialised so it is safe to rely on the monitoring flag for those. Reported-by: Wen Yang Closes: https://lore.kernel.org/lkml/d02c656aada7d071f083460a5c9a454363669b61.1778522945.git.wen.yang@linux.dev Suggested-by: Nam Cao Fixes: f5587d1b6ec9 ("rv: Add Hybrid Automata monitor type") Reviewed-by: Wen Yang Reviewed-by: Nam Cao Link: https://lore.kernel.org/r/20260601153840.124372-7-gmonaco@redhat.com Signed-off-by: Gabriele Monaco --- include/rv/da_monitor.h | 91 +++++++++++++++++++++++++++++++++-------- include/rv/ha_monitor.h | 2 +- 2 files changed, 76 insertions(+), 17 deletions(-) diff --git a/include/rv/da_monitor.h b/include/rv/da_monitor.h index 60dc39f2620a..ec9bc88bd4c4 100644 --- a/include/rv/da_monitor.h +++ b/include/rv/da_monitor.h @@ -76,14 +76,22 @@ static void react(enum states curr_state, enum events event) model_get_state_name(curr_state)); } +/* + * da_monitor_reset_state - reset a monitor and setting it to init state + */ +static inline void da_monitor_reset_state(struct da_monitor *da_mon) +{ + WRITE_ONCE(da_mon->monitoring, 0); + da_mon->curr_state = model_get_initial_state(); +} + /* * da_monitor_reset - reset a monitor and setting it to init state */ static inline void da_monitor_reset(struct da_monitor *da_mon) { da_monitor_reset_hook(da_mon); - WRITE_ONCE(da_mon->monitoring, 0); - da_mon->curr_state = model_get_initial_state(); + da_monitor_reset_state(da_mon); } /* @@ -158,12 +166,28 @@ static struct da_monitor *da_get_monitor(void) return &DA_MON_NAME; } +/* + * __da_monitor_reset_all - reset the single monitor + */ +static void __da_monitor_reset_all(void (*reset)(struct da_monitor *)) +{ + reset(da_get_monitor()); +} + /* * da_monitor_reset_all - reset the single monitor */ static void da_monitor_reset_all(void) { - da_monitor_reset(da_get_monitor()); + __da_monitor_reset_all(da_monitor_reset); +} + +/* + * da_monitor_reset_state_all - reset the single monitor + */ +static inline void da_monitor_reset_state_all(void) +{ + __da_monitor_reset_all(da_monitor_reset_state); } /* @@ -171,7 +195,7 @@ static void da_monitor_reset_all(void) */ static inline int da_monitor_init(void) { - da_monitor_reset_all(); + da_monitor_reset_state_all(); return 0; } @@ -202,25 +226,41 @@ static struct da_monitor *da_get_monitor(void) } /* - * da_monitor_reset_all - reset all CPUs' monitor + * __da_monitor_reset_all - reset all CPUs' monitor */ -static void da_monitor_reset_all(void) +static void __da_monitor_reset_all(void (*reset)(struct da_monitor *)) { struct da_monitor *da_mon; int cpu; for_each_cpu(cpu, cpu_online_mask) { da_mon = per_cpu_ptr(&DA_MON_NAME, cpu); - da_monitor_reset(da_mon); + reset(da_mon); } } +/* + * da_monitor_reset_all - reset all CPUs' monitor + */ +static void da_monitor_reset_all(void) +{ + __da_monitor_reset_all(da_monitor_reset); +} + +/* + * da_monitor_reset_state_all - reset all CPUs' monitor + */ +static inline void da_monitor_reset_state_all(void) +{ + __da_monitor_reset_all(da_monitor_reset_state); +} + /* * da_monitor_init - initialize all CPUs' monitor */ static inline int da_monitor_init(void) { - da_monitor_reset_all(); + da_monitor_reset_state_all(); return 0; } @@ -269,19 +309,29 @@ static inline da_id_type da_get_id(struct da_monitor *da_mon) return da_get_target(da_mon)->pid; } -static void da_monitor_reset_all(void) +static void __da_monitor_reset_all(void (*reset)(struct da_monitor *)) { struct task_struct *g, *p; int cpu; read_lock(&tasklist_lock); for_each_process_thread(g, p) - da_monitor_reset(da_get_monitor(p)); + reset(da_get_monitor(p)); for_each_present_cpu(cpu) - da_monitor_reset(da_get_monitor(idle_task(cpu))); + reset(da_get_monitor(idle_task(cpu))); read_unlock(&tasklist_lock); } +static void da_monitor_reset_all(void) +{ + __da_monitor_reset_all(da_monitor_reset); +} + +static inline void da_monitor_reset_state_all(void) +{ + __da_monitor_reset_all(da_monitor_reset_state); +} + /* * da_monitor_init - initialize the per-task monitor * @@ -298,7 +348,7 @@ static int da_monitor_init(void) task_mon_slot = slot; - da_monitor_reset_all(); + da_monitor_reset_state_all(); return 0; } @@ -490,15 +540,24 @@ static inline void da_destroy_storage(da_id_type id) kfree_rcu(mon_storage, rcu); } -static void da_monitor_reset_all(void) +static void __da_monitor_reset_all(void (*reset)(struct da_monitor *)) { struct da_monitor_storage *mon_storage; int bkt; - rcu_read_lock(); + guard(rcu)(); hash_for_each_rcu(da_monitor_ht, bkt, mon_storage, node) - da_monitor_reset(&mon_storage->rv.da_mon); - rcu_read_unlock(); + reset(&mon_storage->rv.da_mon); +} + +static void da_monitor_reset_all(void) +{ + __da_monitor_reset_all(da_monitor_reset); +} + +static inline void da_monitor_reset_state_all(void) +{ + __da_monitor_reset_all(da_monitor_reset_state); } static inline int da_monitor_init(void) diff --git a/include/rv/ha_monitor.h b/include/rv/ha_monitor.h index d59507e8cb30..bd87055567cc 100644 --- a/include/rv/ha_monitor.h +++ b/include/rv/ha_monitor.h @@ -153,12 +153,12 @@ static inline void ha_monitor_init_env(struct da_monitor *da_mon) * Called from a hook in the DA reset functions, it supplies the da_mon * corresponding to the current ha_mon. * Not all hybrid automata require the timer, still clear it for simplicity. + * Monitors that never started have their timer uninitialized, do not stop those. */ static inline void ha_monitor_reset_env(struct da_monitor *da_mon) { struct ha_monitor *ha_mon = to_ha_monitor(da_mon); - /* Initialisation resets the monitor before initialising the timer */ if (likely(da_monitoring(da_mon))) ha_cancel_timer(ha_mon); } From 700782ec8f6589c5792b323efd6e004dd183328b Mon Sep 17 00:00:00 2001 From: Gabriele Monaco Date: Mon, 1 Jun 2026 17:38:34 +0200 Subject: [PATCH 086/257] rv: Add automatic cleanup handlers for per-task HA monitors Hybrid automata monitors may start timers, depending on the model, these may remain active on an exiting task and cause false positives or even access freed memory. Add an enable/disable hook in the HA code, currently only populated by the per-task handler for registration and deregistration. This hooks to the sched_process_exit event and ensures the timer is stopped for every exiting task. The handler is enabled automatically but may be disabled, for instance if the monitor uses the event for another purpose (but should still manually ensure timers are stopped). Fixes: f5587d1b6ec9 ("rv: Add Hybrid Automata monitor type") Reviewed-by: Nam Cao Link: https://lore.kernel.org/r/20260601153840.124372-8-gmonaco@redhat.com Signed-off-by: Gabriele Monaco --- include/rv/ha_monitor.h | 60 +++++++++++++++++++ kernel/trace/rv/monitors/nomiss/nomiss.c | 4 +- kernel/trace/rv/monitors/opid/opid.c | 4 +- kernel/trace/rv/monitors/stall/stall.c | 4 +- .../rvgen/rvgen/templates/dot2k/main.c | 4 +- 5 files changed, 68 insertions(+), 8 deletions(-) diff --git a/include/rv/ha_monitor.h b/include/rv/ha_monitor.h index bd87055567cc..4002b5247c46 100644 --- a/include/rv/ha_monitor.h +++ b/include/rv/ha_monitor.h @@ -28,6 +28,7 @@ static inline void ha_monitor_init_env(struct da_monitor *da_mon); static inline void ha_monitor_reset_env(struct da_monitor *da_mon); static inline void ha_setup_timer(struct ha_monitor *ha_mon); static inline bool ha_cancel_timer(struct ha_monitor *ha_mon); +static inline void ha_cancel_timer_sync(struct ha_monitor *ha_mon); static bool ha_monitor_handle_constraint(struct da_monitor *da_mon, enum states curr_state, enum events event, @@ -37,6 +38,26 @@ static bool ha_monitor_handle_constraint(struct da_monitor *da_mon, #define da_monitor_init_hook ha_monitor_init_env #define da_monitor_reset_hook ha_monitor_reset_env +#if !defined(HA_SKIP_AUTO_CLEANUP) && RV_MON_TYPE == RV_MON_PER_TASK +/* + * Automatic cleanup handlers for per-task HA monitors, only skip if you know + * what you are doing (e.g. you want to implement cleanup manually in another + * handler doing more things). + */ +static void ha_handle_sched_process_exit(void *data, struct task_struct *p, + bool group_dead); + +#define ha_monitor_enable_hook() \ + rv_attach_trace_probe(__stringify(MONITOR_NAME), sched_process_exit, \ + ha_handle_sched_process_exit) +#define ha_monitor_disable_hook() \ + rv_detach_trace_probe(__stringify(MONITOR_NAME), sched_process_exit, \ + ha_handle_sched_process_exit) +#else +#define ha_monitor_enable_hook() ((void)0) +#define ha_monitor_disable_hook() ((void)0) +#endif + #include #include @@ -115,6 +136,22 @@ static enum hrtimer_restart ha_monitor_timer_callback(struct hrtimer *hrtimer); #define ha_get_ns() 0 #endif /* HA_CLK_NS */ +static int ha_monitor_init(void) +{ + int ret; + + ret = da_monitor_init(); + if (ret == 0) + ha_monitor_enable_hook(); + return ret; +} + +static void ha_monitor_destroy(void) +{ + ha_monitor_disable_hook(); + da_monitor_destroy(); +} + /* Should be supplied by the monitor */ static u64 ha_get_env(struct ha_monitor *ha_mon, enum envs env, u64 time_ns); static bool ha_verify_constraint(struct ha_monitor *ha_mon, @@ -200,6 +237,20 @@ static inline void ha_trace_error_env(struct ha_monitor *ha_mon, { CONCATENATE(trace_error_env_, MONITOR_NAME)(id, curr_state, event, env); } + +#if !defined(HA_SKIP_AUTO_CLEANUP) && RV_MON_TYPE == RV_MON_PER_TASK +static void ha_handle_sched_process_exit(void *data, struct task_struct *p, + bool group_dead) +{ + struct da_monitor *da_mon = da_get_monitor(p); + + if (likely(da_monitoring(da_mon))) { + da_monitor_reset(da_mon); + ha_cancel_timer_sync(to_ha_monitor(da_mon)); + } +} +#endif + #endif /* RV_MON_TYPE */ /* @@ -412,6 +463,10 @@ static inline bool ha_cancel_timer(struct ha_monitor *ha_mon) { return timer_delete(&ha_mon->timer); } +static inline void ha_cancel_timer_sync(struct ha_monitor *ha_mon) +{ + timer_delete_sync(&ha_mon->timer); +} #elif HA_TIMER_TYPE == HA_TIMER_HRTIMER /* * Helper functions to handle the monitor timer. @@ -463,6 +518,10 @@ static inline bool ha_cancel_timer(struct ha_monitor *ha_mon) { return hrtimer_try_to_cancel(&ha_mon->hrtimer) == 1; } +static inline void ha_cancel_timer_sync(struct ha_monitor *ha_mon) +{ + hrtimer_cancel(&ha_mon->hrtimer); +} #else /* HA_TIMER_NONE */ /* * Start function is intentionally not defined, monitors using timers must @@ -473,6 +532,7 @@ static inline bool ha_cancel_timer(struct ha_monitor *ha_mon) { return false; } +static inline void ha_cancel_timer_sync(struct ha_monitor *ha_mon) { } #endif #endif diff --git a/kernel/trace/rv/monitors/nomiss/nomiss.c b/kernel/trace/rv/monitors/nomiss/nomiss.c index 31f90f3638d8..8ead8783c29f 100644 --- a/kernel/trace/rv/monitors/nomiss/nomiss.c +++ b/kernel/trace/rv/monitors/nomiss/nomiss.c @@ -227,7 +227,7 @@ static int enable_nomiss(void) { int retval; - retval = da_monitor_init(); + retval = ha_monitor_init(); if (retval) return retval; @@ -263,7 +263,7 @@ static void disable_nomiss(void) rv_detach_trace_probe("nomiss", sched_switch, handle_sched_switch); rv_detach_trace_probe("nomiss", sched_wakeup, handle_sched_wakeup); - da_monitor_destroy(); + ha_monitor_destroy(); } static struct rv_monitor rv_this = { diff --git a/kernel/trace/rv/monitors/opid/opid.c b/kernel/trace/rv/monitors/opid/opid.c index 4594c7c46601..2922318c6112 100644 --- a/kernel/trace/rv/monitors/opid/opid.c +++ b/kernel/trace/rv/monitors/opid/opid.c @@ -73,7 +73,7 @@ static int enable_opid(void) { int retval; - retval = da_monitor_init(); + retval = ha_monitor_init(); if (retval) return retval; @@ -90,7 +90,7 @@ static void disable_opid(void) rv_detach_trace_probe("opid", sched_set_need_resched_tp, handle_sched_need_resched); rv_detach_trace_probe("opid", sched_waking, handle_sched_waking); - da_monitor_destroy(); + ha_monitor_destroy(); } /* diff --git a/kernel/trace/rv/monitors/stall/stall.c b/kernel/trace/rv/monitors/stall/stall.c index 9ccfda6b0e73..3c38fb1a0159 100644 --- a/kernel/trace/rv/monitors/stall/stall.c +++ b/kernel/trace/rv/monitors/stall/stall.c @@ -103,7 +103,7 @@ static int enable_stall(void) { int retval; - retval = da_monitor_init(); + retval = ha_monitor_init(); if (retval) return retval; @@ -120,7 +120,7 @@ static void disable_stall(void) rv_detach_trace_probe("stall", sched_switch, handle_sched_switch); rv_detach_trace_probe("stall", sched_wakeup, handle_sched_wakeup); - da_monitor_destroy(); + ha_monitor_destroy(); } static struct rv_monitor rv_this = { diff --git a/tools/verification/rvgen/rvgen/templates/dot2k/main.c b/tools/verification/rvgen/rvgen/templates/dot2k/main.c index bf0999f6657a..889446760e3c 100644 --- a/tools/verification/rvgen/rvgen/templates/dot2k/main.c +++ b/tools/verification/rvgen/rvgen/templates/dot2k/main.c @@ -35,7 +35,7 @@ static int enable_%%MODEL_NAME%%(void) { int retval; - retval = da_monitor_init(); + retval = %%MONITOR_CLASS%%_monitor_init(); if (retval) return retval; @@ -50,7 +50,7 @@ static void disable_%%MODEL_NAME%%(void) %%TRACEPOINT_DETACH%% - da_monitor_destroy(); + %%MONITOR_CLASS%%_monitor_destroy(); } /* From 74e17bd6fc8ed7e30363bb78d4c50b38cfd71efe Mon Sep 17 00:00:00 2001 From: Gabriele Monaco Date: Mon, 1 Jun 2026 17:38:35 +0200 Subject: [PATCH 087/257] rv: Ensure synchronous cleanup for HA monitors HA monitors may start timers, all cleanup functions currently stop the timers asynchronously to avoid sleeping in the wrong context. Nothing makes sure running callbacks terminate on cleanup. Run the entire HA timer callback in an RCU read-side critical section, this way we can simply synchronize_rcu() with any pending timer and are sure any cleanup using kfree_rcu() runs after callbacks terminated. Additionally make sure any unlikely callback running late won't run any code if the monitor is marked as disabled or if destruction started. Use memory barriers to serialise with racing resets. Fixes: f5587d1b6ec9 ("rv: Add Hybrid Automata monitor type") Fixes: 4a24127bd6cb ("rv: Add support for per-object monitors in DA/HA") Reviewed-by: Nam Cao Link: https://lore.kernel.org/r/20260601153840.124372-9-gmonaco@redhat.com Signed-off-by: Gabriele Monaco --- include/rv/da_monitor.h | 19 ++++++++++++++++--- include/rv/ha_monitor.h | 27 +++++++++++++++++++++++++-- 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/include/rv/da_monitor.h b/include/rv/da_monitor.h index ec9bc88bd4c4..1f440c7818e6 100644 --- a/include/rv/da_monitor.h +++ b/include/rv/da_monitor.h @@ -57,6 +57,15 @@ static struct rv_monitor rv_this; #define da_monitor_reset_hook(da_mon) #endif +/* + * Hook to allow the implementation of hybrid automata: define it with a + * function that waits for the termination of all monitors background + * activities (e.g. all timers). This hook can sleep. + */ +#ifndef da_monitor_sync_hook +#define da_monitor_sync_hook() +#endif + /* * Type for the target id, default to int but can be overridden. * A long type can work as hash table key (PER_OBJ) but will be downgraded to @@ -82,7 +91,8 @@ static void react(enum states curr_state, enum events event) static inline void da_monitor_reset_state(struct da_monitor *da_mon) { WRITE_ONCE(da_mon->monitoring, 0); - da_mon->curr_state = model_get_initial_state(); + /* Pair with load in __ha_monitor_timer_callback */ + smp_store_release(&da_mon->curr_state, model_get_initial_state()); } /* @@ -205,6 +215,7 @@ static inline int da_monitor_init(void) static inline void da_monitor_destroy(void) { da_monitor_reset_all(); + da_monitor_sync_hook(); } #elif RV_MON_TYPE == RV_MON_PER_CPU @@ -270,6 +281,7 @@ static inline int da_monitor_init(void) static inline void da_monitor_destroy(void) { da_monitor_reset_all(); + da_monitor_sync_hook(); } #elif RV_MON_TYPE == RV_MON_PER_TASK @@ -367,6 +379,7 @@ static inline void da_monitor_destroy(void) tracepoint_synchronize_unregister(); da_monitor_reset_all(); + da_monitor_sync_hook(); rv_put_task_monitor_slot(task_mon_slot); task_mon_slot = RV_PER_TASK_MONITOR_INIT; @@ -573,13 +586,13 @@ static inline void da_monitor_destroy(void) int bkt; tracepoint_synchronize_unregister(); + da_monitor_reset_all(); + da_monitor_sync_hook(); /* * This function is called after all probes are disabled and no longer * pending, we can safely assume no concurrent user. */ - synchronize_rcu(); hash_for_each_safe(da_monitor_ht, bkt, tmp, mon_storage, node) { - da_monitor_reset_hook(&mon_storage->rv.da_mon); hash_del_rcu(&mon_storage->node); kfree(mon_storage); } diff --git a/include/rv/ha_monitor.h b/include/rv/ha_monitor.h index 4002b5247c46..28d3c74cabfc 100644 --- a/include/rv/ha_monitor.h +++ b/include/rv/ha_monitor.h @@ -37,6 +37,7 @@ static bool ha_monitor_handle_constraint(struct da_monitor *da_mon, #define da_monitor_event_hook ha_monitor_handle_constraint #define da_monitor_init_hook ha_monitor_init_env #define da_monitor_reset_hook ha_monitor_reset_env +#define da_monitor_sync_hook() synchronize_rcu() #if !defined(HA_SKIP_AUTO_CLEANUP) && RV_MON_TYPE == RV_MON_PER_TASK /* @@ -136,10 +137,13 @@ static enum hrtimer_restart ha_monitor_timer_callback(struct hrtimer *hrtimer); #define ha_get_ns() 0 #endif /* HA_CLK_NS */ +static bool ha_mon_destroying; + static int ha_monitor_init(void) { int ret; + WRITE_ONCE(ha_mon_destroying, false); ret = da_monitor_init(); if (ret == 0) ha_monitor_enable_hook(); @@ -148,6 +152,7 @@ static int ha_monitor_init(void) static void ha_monitor_destroy(void) { + WRITE_ONCE(ha_mon_destroying, true); ha_monitor_disable_hook(); da_monitor_destroy(); } @@ -288,12 +293,30 @@ static bool ha_monitor_handle_constraint(struct da_monitor *da_mon, return false; } +/* + * __ha_monitor_timer_callback - generic callback representation + * + * This callback runs in an RCU read-side critical section to allow the + * destruction sequence to easily synchronize_rcu() with all pending timers + * after asynchronously disabling them. The ha_mon_destroying check ensures + * any callback entering the RCU section after synchronize_rcu() completes + * will see the flag and bail out immediately. + */ static inline void __ha_monitor_timer_callback(struct ha_monitor *ha_mon) { - enum states curr_state = READ_ONCE(ha_mon->da_mon.curr_state); DECLARE_SEQ_BUF(env_string, ENV_BUFFER_SIZE); - u64 time_ns = ha_get_ns(); + enum states curr_state; + u64 time_ns; + guard(rcu)(); + if (unlikely(READ_ONCE(ha_mon_destroying))) + return; + /* Ensure consistent curr_state if we race with da_monitor_reset */ + curr_state = smp_load_acquire(&ha_mon->da_mon.curr_state); + if (unlikely(!da_monitor_handling_event(&ha_mon->da_mon))) + return; + + time_ns = ha_get_ns(); ha_get_env_string(&env_string, ha_mon, time_ns); ha_react(curr_state, EVENT_NONE, env_string.buffer); ha_trace_error_env(ha_mon, model_get_state_name(curr_state), From 7c147fae71f3b3542ba3292d2099ef7237cfc0da Mon Sep 17 00:00:00 2001 From: Gabriele Monaco Date: Mon, 1 Jun 2026 17:38:36 +0200 Subject: [PATCH 088/257] rv: Prevent task migration while handling per-CPU events Tracepoint handlers are fully preemptible after a46023d5616 ("tracing: Guard __DECLARE_TRACE() use of __DO_TRACE_CALL() with SRCU-fast"). When a per-CPU monitor handles an event, it retrieves the monitor state using a per-CPU pointer. If the event itself doesn't disable preemption, the task can migrate to a different CPU and we risk updating the wrong monitor. Mitigate this by explicitly disabling task migration before acquiring the monitor pointer. This cannot guarantee the monitor runs on the correct CPU but reduces the race condition window and prevents warnings. Reviewed-by: Wen Yang Reviewed-by: Nam Cao Link: https://lore.kernel.org/r/20260601153840.124372-10-gmonaco@redhat.com Signed-off-by: Gabriele Monaco --- include/rv/da_monitor.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/include/rv/da_monitor.h b/include/rv/da_monitor.h index 1f440c7818e6..34b8fba9ecd4 100644 --- a/include/rv/da_monitor.h +++ b/include/rv/da_monitor.h @@ -218,6 +218,10 @@ static inline void da_monitor_destroy(void) da_monitor_sync_hook(); } +#ifndef da_implicit_guard +#define da_implicit_guard() +#endif + #elif RV_MON_TYPE == RV_MON_PER_CPU /* * Functions to define, init and get a per-cpu monitor. @@ -284,6 +288,10 @@ static inline void da_monitor_destroy(void) da_monitor_sync_hook(); } +#ifndef da_implicit_guard +#define da_implicit_guard() guard(migrate)() +#endif + #elif RV_MON_TYPE == RV_MON_PER_TASK /* * Functions to define, init and get a per-task monitor. @@ -756,6 +764,7 @@ static inline bool __da_handle_start_run_event(struct da_monitor *da_mon, */ static inline void da_handle_event(enum events event) { + da_implicit_guard(); __da_handle_event(da_get_monitor(), event, 0); } @@ -771,6 +780,7 @@ static inline void da_handle_event(enum events event) */ static inline bool da_handle_start_event(enum events event) { + da_implicit_guard(); return __da_handle_start_event(da_get_monitor(), event, 0); } @@ -782,6 +792,7 @@ static inline bool da_handle_start_event(enum events event) */ static inline bool da_handle_start_run_event(enum events event) { + da_implicit_guard(); return __da_handle_start_run_event(da_get_monitor(), event, 0); } From d9022172c1abea9e220d13ee453813dafec5b9e4 Mon Sep 17 00:00:00 2001 From: Gabriele Monaco Date: Mon, 1 Jun 2026 17:38:37 +0200 Subject: [PATCH 089/257] rv: Use 0 to check preemption enabled in opid Tracepoint handlers no longer run with preemption disabled by default since a46023d5616 ("tracing: Guard __DECLARE_TRACE() use of __DO_TRACE_CALL() with SRCU-fast"), the opid monitor should now count 1 in the preemption count as preemption disabled. Change the rule for preempt_off to preempt > 0. Reviewed-by: Nam Cao Link: https://lore.kernel.org/r/20260601153840.124372-11-gmonaco@redhat.com Signed-off-by: Gabriele Monaco --- kernel/trace/rv/monitors/opid/opid.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/kernel/trace/rv/monitors/opid/opid.c b/kernel/trace/rv/monitors/opid/opid.c index 2922318c6112..3b6a85e815b8 100644 --- a/kernel/trace/rv/monitors/opid/opid.c +++ b/kernel/trace/rv/monitors/opid/opid.c @@ -22,14 +22,8 @@ static u64 ha_get_env(struct ha_monitor *ha_mon, enum envs_opid env, u64 time_ns if (env == irq_off_opid) return irqs_disabled(); else if (env == preempt_off_opid) { - /* - * If CONFIG_PREEMPTION is enabled, then the tracepoint itself disables - * preemption (adding one to the preempt_count). Since we are - * interested in the preempt_count at the time the tracepoint was - * hit, we consider 1 as still enabled. - */ if (IS_ENABLED(CONFIG_PREEMPTION)) - return (preempt_count() & PREEMPT_MASK) > 1; + return (preempt_count() & PREEMPT_MASK) > 0; return true; } return ENV_INVALID_VALUE; From ae7676952790f421c40918e2586a2c9f12a682b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ADra=20Canal?= Date: Tue, 2 Jun 2026 14:50:14 -0300 Subject: [PATCH 090/257] drm/v3d: Fix vaddr leak when indirect CSD has zeroed workgroups MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit v3d_rewrite_csd_job_wg_counts_from_indirect() maps both the indirect buffer and the workgroup buffer and is expected to release them before returning. When any of the workgroup counts read from the buffer is zero, the function bailed out early and skipped the cleanup, leaking the vaddr mappings of both BOs. Jump to the cleanup path instead of returning directly, so the mappings are always dropped. Cc: stable@vger.kernel.org Fixes: 18b8413b25b7 ("drm/v3d: Create a CPU job extension for a indirect CSD job") Suggested-by: Jose Maria Casanova Crespo Reviewed-by: Iago Toral Quiroga Link: https://patch.msgid.link/20260602-v3d-fix-indirect-csd-v4-1-654309e32bc0@igalia.com Signed-off-by: Maíra Canal --- drivers/gpu/drm/v3d/v3d_sched.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/v3d/v3d_sched.c b/drivers/gpu/drm/v3d/v3d_sched.c index 94bf628dc91c..47f83936cd73 100644 --- a/drivers/gpu/drm/v3d/v3d_sched.c +++ b/drivers/gpu/drm/v3d/v3d_sched.c @@ -403,7 +403,7 @@ v3d_rewrite_csd_job_wg_counts_from_indirect(struct v3d_cpu_job *job) wg_counts = (uint32_t *)(bo->vaddr + indirect_csd->offset); if (wg_counts[0] == 0 || wg_counts[1] == 0 || wg_counts[2] == 0) - return; + goto unmap_bo; args->cfg[0] = wg_counts[0] << V3D_CSD_CFG012_WG_COUNT_SHIFT; args->cfg[1] = wg_counts[1] << V3D_CSD_CFG012_WG_COUNT_SHIFT; @@ -428,6 +428,7 @@ v3d_rewrite_csd_job_wg_counts_from_indirect(struct v3d_cpu_job *job) } } +unmap_bo: v3d_put_bo_vaddr(indirect); v3d_put_bo_vaddr(bo); } From 7f93fad5ea0affc9e1505dd0f7596c0fdb496213 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ADra=20Canal?= Date: Tue, 2 Jun 2026 14:50:15 -0300 Subject: [PATCH 091/257] drm/v3d: Skip CSD when it has zeroed workgroups MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A compute shader dispatch encodes its workgroup counts in the CFG0..CFG2 registers. Kicking off a dispatch with a zero count in any of the three dimensions is invalid. First, the hardware will process 0 as 65536, while the user-space driver exposes a maximum of 65535. Over that, a submission with a zeroed workgroup dimension should be a no-op. These zeroed counts can reach the dispatch path through an indirect CSD job, whose workgroup counts are only known once the indirect buffer is read and may legitimately be zero, but such scenario should only result in a no-op. Overwrite the indirect CSD job workgroup counts with the indirect BO ones, even if they are zeroed, and don't submit the job to the hardware when any of the workgroup counts is zero, so the job completes immediately instead of running the shader. Cc: stable@vger.kernel.org Fixes: d223f98f0209 ("drm/v3d: Add support for compute shader dispatch.") Suggested-by: Jose Maria Casanova Crespo Reviewed-by: Iago Toral Quiroga Link: https://patch.msgid.link/20260602-v3d-fix-indirect-csd-v4-2-654309e32bc0@igalia.com Signed-off-by: Maíra Canal --- drivers/gpu/drm/v3d/v3d_sched.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/v3d/v3d_sched.c b/drivers/gpu/drm/v3d/v3d_sched.c index 47f83936cd73..8a635a9ec046 100644 --- a/drivers/gpu/drm/v3d/v3d_sched.c +++ b/drivers/gpu/drm/v3d/v3d_sched.c @@ -352,6 +352,16 @@ v3d_csd_job_run(struct drm_sched_job *sched_job) return NULL; } + /* The HW interprets a workgroup size of 0 as 65536; however, the + * user-space driver exposes a maximum of 65535. Therefore, a 0 in + * any dimension means that we have no workgroups and the compute + * shader should not be dispatched. + */ + if (!V3D_GET_FIELD(job->args.cfg[0], V3D_CSD_QUEUED_CFG0_NUM_WGS_X) || + !V3D_GET_FIELD(job->args.cfg[1], V3D_CSD_QUEUED_CFG1_NUM_WGS_Y) || + !V3D_GET_FIELD(job->args.cfg[2], V3D_CSD_QUEUED_CFG2_NUM_WGS_Z)) + return NULL; + v3d->queue[V3D_CSD].active_job = &job->base; v3d_invalidate_caches(v3d); @@ -402,13 +412,13 @@ v3d_rewrite_csd_job_wg_counts_from_indirect(struct v3d_cpu_job *job) wg_counts = (uint32_t *)(bo->vaddr + indirect_csd->offset); - if (wg_counts[0] == 0 || wg_counts[1] == 0 || wg_counts[2] == 0) - goto unmap_bo; - args->cfg[0] = wg_counts[0] << V3D_CSD_CFG012_WG_COUNT_SHIFT; args->cfg[1] = wg_counts[1] << V3D_CSD_CFG012_WG_COUNT_SHIFT; args->cfg[2] = wg_counts[2] << V3D_CSD_CFG012_WG_COUNT_SHIFT; + if (wg_counts[0] == 0 || wg_counts[1] == 0 || wg_counts[2] == 0) + goto unmap_bo; + num_batches = DIV_ROUND_UP(indirect_csd->wg_size, 16) * (wg_counts[0] * wg_counts[1] * wg_counts[2]); From 15fe76e23615f502d051ef0768f86babaf08746c Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Mon, 1 Jun 2026 13:52:31 -0300 Subject: [PATCH 092/257] RDMA/umem: Fix truncation for block sizes >= 4G When the iommu is used the linearization of the mapping can give a single block that is very large split across multiple SG entries. When __rdma_block_iter_next() reassembles the split SG entries it is overflowing the 32 bit stack values and computed the wrong DMA addresses for blocks after the truncation. Use the right types to hold DMA addresses. Link: https://patch.msgid.link/r/1-v1-88303e9e509f+f7-ib_umem_types_jgg@nvidia.com Cc: stable@vger.kernel.org Fixes: a808273a495c ("RDMA/verbs: Add a DMA iterator to return aligned contiguous memory blocks") Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/iter.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/core/iter.c b/drivers/infiniband/core/iter.c index 8e543d100657..3ed351e8fcf6 100644 --- a/drivers/infiniband/core/iter.c +++ b/drivers/infiniband/core/iter.c @@ -19,8 +19,8 @@ EXPORT_SYMBOL(__rdma_block_iter_start); bool __rdma_block_iter_next(struct ib_block_iter *biter) { - unsigned int block_offset; - unsigned int delta; + dma_addr_t block_offset; + dma_addr_t delta; if (!biter->__sg_nents || !biter->__sg) return false; From 17d62e0b157f262dd8c7586cb882ece72a5b10c2 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 29 May 2026 20:35:36 +0200 Subject: [PATCH 093/257] KVM: SEV: Make it more obvious when KVM is writing back the current PSC index Increment the guest-visible "cur_entry" index outside of the for-loop when processing Page State Change entries, and add a comment to make it more obvious which code is operating on trusted data, and which code is touching guest-accessible data. No functional change intended. Reviewed-by: Tom Lendacky Reviewed-by: Michael Roth Signed-off-by: Sean Christopherson Message-ID: <20260501202250.2115252-12-seanjc@google.com> Signed-off-by: Paolo Bonzini Message-ID: <20260529183549.1104619-12-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- arch/x86/kvm/svm/sev.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index 62b5befe0eed..1982d13e71d9 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -3947,7 +3947,7 @@ static int snp_begin_psc(struct vcpu_svm *svm) } /* Find the start of the next range which needs processing. */ - for (idx = idx_start; idx <= idx_end; idx++, hdr->cur_entry++) { + for (idx = idx_start; idx <= idx_end; idx++) { entry_start = READ_ONCE(entries[idx]); gfn = entry_start.gfn; @@ -3974,6 +3974,14 @@ static int snp_begin_psc(struct vcpu_svm *svm) if (npages) break; + + /* + * Increment the guest-visible index to communicate the current + * entry back to the guest, e.g. in case of failure. No need + * for READ_ONCE() as KVM doesn't consume the field, i.e. a + * misbehaving guest can only break itself. + */ + hdr->cur_entry++; } if (idx > idx_end) { From 00b9744a3688758c5c492756d08a1cd599a76965 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 29 May 2026 20:35:37 +0200 Subject: [PATCH 094/257] KVM: SEV: Add an anonymous "psc" struct to track current PSC metadata Add a "psc" struct to vcpu_sev_es_state to avoid having to prefix all of the fields with "psc_". Take advantage of the code churn to opportunistically rename local variables to "guest_psc" to make it more obvious that the buffer is guest data, and more importantly, guest accessible! Opportunistically rename inflight => batch_size as well, because there can really only be one operation in-flight (per-vCPU), i.e. "inflight" _looks_ like a boolean, but in actuality is an integer tracking how many pages are being handled by the current operation. No functional change intended. Reviewed-by: Tom Lendacky Signed-off-by: Sean Christopherson Message-ID: <20260501202250.2115252-13-seanjc@google.com> Signed-off-by: Paolo Bonzini Message-ID: <20260529183549.1104619-13-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- arch/x86/kvm/svm/sev.c | 43 +++++++++++++++++++----------------------- arch/x86/kvm/svm/svm.h | 8 +++++--- 2 files changed, 24 insertions(+), 27 deletions(-) diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index 1982d13e71d9..9f6543cebedf 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -3845,9 +3845,7 @@ static int snp_begin_psc(struct vcpu_svm *svm); static void snp_complete_psc(struct vcpu_svm *svm, u64 psc_ret) { - svm->sev_es.psc_inflight = 0; - svm->sev_es.psc_idx = 0; - svm->sev_es.psc_2m = false; + memset(&svm->sev_es.psc, 0, sizeof(svm->sev_es.psc)); /* * PSC requests always get a "no action" response in SW_EXITINFO1, with @@ -3860,9 +3858,8 @@ static void snp_complete_psc(struct vcpu_svm *svm, u64 psc_ret) static void __snp_complete_one_psc(struct vcpu_svm *svm) { - struct psc_buffer *psc = svm->sev_es.ghcb_sa; - struct psc_entry *entries = psc->entries; - struct psc_hdr *hdr = &psc->hdr; + struct vcpu_sev_es_state *sev_es = &svm->sev_es; + struct psc_buffer *guest_psc = sev_es->ghcb_sa; __u16 idx; /* @@ -3870,14 +3867,14 @@ static void __snp_complete_one_psc(struct vcpu_svm *svm) * corresponding entries in the guest's PSC buffer and zero out the * count of in-flight PSC entries. */ - for (idx = svm->sev_es.psc_idx; svm->sev_es.psc_inflight; - svm->sev_es.psc_inflight--, idx++) { - struct psc_entry entry = READ_ONCE(entries[idx]); + for (idx = sev_es->psc.cur_idx; sev_es->psc.batch_size; + sev_es->psc.batch_size--, idx++) { + struct psc_entry entry = READ_ONCE(guest_psc->entries[idx]); - entries[idx].cur_page = entry.pagesize ? 512 : 1; + guest_psc->entries[idx].cur_page = entry.pagesize ? 512 : 1; } - hdr->cur_entry = idx; + guest_psc->hdr.cur_entry = idx; } static int snp_complete_one_psc(struct kvm_vcpu *vcpu) @@ -3898,10 +3895,8 @@ static int snp_complete_one_psc(struct kvm_vcpu *vcpu) static int snp_begin_psc(struct vcpu_svm *svm) { struct vcpu_sev_es_state *sev_es = &svm->sev_es; - struct psc_buffer *psc = sev_es->ghcb_sa; - struct psc_entry *entries = psc->entries; + struct psc_buffer *guest_psc = sev_es->ghcb_sa; struct kvm_vcpu *vcpu = &svm->vcpu; - struct psc_hdr *hdr = &psc->hdr; struct psc_entry entry_start; u16 idx, idx_start, idx_end, max_nr_entries; int npages; @@ -3928,7 +3923,7 @@ static int snp_begin_psc(struct vcpu_svm *svm) next_range: /* There should be no other PSCs in-flight at this point. */ - if (WARN_ON_ONCE(svm->sev_es.psc_inflight)) { + if (WARN_ON_ONCE(svm->sev_es.psc.batch_size)) { snp_complete_psc(svm, VMGEXIT_PSC_ERROR_GENERIC); return 1; } @@ -3938,8 +3933,8 @@ static int snp_begin_psc(struct vcpu_svm *svm) * validation, so take care to only use validated copies of values used * for things like array indexing. */ - idx_start = READ_ONCE(hdr->cur_entry); - idx_end = READ_ONCE(hdr->end_entry); + idx_start = READ_ONCE(guest_psc->hdr.cur_entry); + idx_end = READ_ONCE(guest_psc->hdr.end_entry); if (idx_end >= max_nr_entries) { snp_complete_psc(svm, VMGEXIT_PSC_ERROR_INVALID_HDR); @@ -3948,7 +3943,7 @@ static int snp_begin_psc(struct vcpu_svm *svm) /* Find the start of the next range which needs processing. */ for (idx = idx_start; idx <= idx_end; idx++) { - entry_start = READ_ONCE(entries[idx]); + entry_start = READ_ONCE(guest_psc->entries[idx]); gfn = entry_start.gfn; huge = entry_start.pagesize; @@ -3981,7 +3976,7 @@ static int snp_begin_psc(struct vcpu_svm *svm) * for READ_ONCE() as KVM doesn't consume the field, i.e. a * misbehaving guest can only break itself. */ - hdr->cur_entry++; + guest_psc->hdr.cur_entry++; } if (idx > idx_end) { @@ -3990,9 +3985,9 @@ static int snp_begin_psc(struct vcpu_svm *svm) return 1; } - svm->sev_es.psc_2m = huge; - svm->sev_es.psc_idx = idx; - svm->sev_es.psc_inflight = 1; + sev_es->psc.is_2m = huge; + sev_es->psc.cur_idx = idx; + sev_es->psc.batch_size = 1; /* * Find all subsequent PSC entries that contain adjacent GPA @@ -4000,14 +3995,14 @@ static int snp_begin_psc(struct vcpu_svm *svm) * KVM_HC_MAP_GPA_RANGE exit. */ while (++idx <= idx_end) { - struct psc_entry entry = READ_ONCE(entries[idx]); + struct psc_entry entry = READ_ONCE(guest_psc->entries[idx]); if (entry.operation != entry_start.operation || entry.gfn != entry_start.gfn + npages || entry.cur_page || !!entry.pagesize != huge) break; - svm->sev_es.psc_inflight++; + sev_es->psc.batch_size++; npages += huge ? 512 : 1; } diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h index a10668d17a16..06192bc9c107 100644 --- a/arch/x86/kvm/svm/svm.h +++ b/arch/x86/kvm/svm/svm.h @@ -257,9 +257,11 @@ struct vcpu_sev_es_state { bool ghcb_sa_free; /* SNP Page-State-Change buffer entries currently being processed */ - u16 psc_idx; - u16 psc_inflight; - bool psc_2m; + struct { + u16 cur_idx; + u16 batch_size; + bool is_2m; + } psc; u64 ghcb_registered_gpa; From ce6ea7b33e0075335b1eb3b227a21a98e3196e41 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 29 May 2026 20:35:38 +0200 Subject: [PATCH 095/257] KVM: SEV: Read start/end indices of PSC requests exactly once per #VMGEXIT Rework Page State Change (PSC) handling to read the guest-provided start and end indices exactly once, at the beginning of the request. Re-reading the indices is "fine", _if_ the guest is well-behaved. KVM _should_ be safe against concurrent guest modification of the indices, but there is zero reason to introduce unnecessary risk. Reviewed-by: Tom Lendacky Reviewed-by: Michael Roth Signed-off-by: Sean Christopherson Message-ID: <20260501202250.2115252-14-seanjc@google.com> Signed-off-by: Paolo Bonzini Message-ID: <20260529183549.1104619-14-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- arch/x86/kvm/svm/sev.c | 86 +++++++++++++++++++++++------------------- arch/x86/kvm/svm/svm.h | 1 + 2 files changed, 49 insertions(+), 38 deletions(-) diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index 9f6543cebedf..4ebe0d449789 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -3841,7 +3841,7 @@ struct psc_buffer { struct psc_entry entries[]; } __packed; -static int snp_begin_psc(struct vcpu_svm *svm); +static int snp_do_psc(struct vcpu_svm *svm); static void snp_complete_psc(struct vcpu_svm *svm, u64 psc_ret) { @@ -3874,6 +3874,7 @@ static void __snp_complete_one_psc(struct vcpu_svm *svm) guest_psc->entries[idx].cur_page = entry.pagesize ? 512 : 1; } + sev_es->psc.cur_idx = idx; guest_psc->hdr.cur_entry = idx; } @@ -3889,37 +3890,19 @@ static int snp_complete_one_psc(struct kvm_vcpu *vcpu) __snp_complete_one_psc(svm); /* Handle the next range (if any). */ - return snp_begin_psc(svm); + return snp_do_psc(svm); } -static int snp_begin_psc(struct vcpu_svm *svm) +static int snp_do_psc(struct vcpu_svm *svm) { struct vcpu_sev_es_state *sev_es = &svm->sev_es; struct psc_buffer *guest_psc = sev_es->ghcb_sa; struct kvm_vcpu *vcpu = &svm->vcpu; struct psc_entry entry_start; - u16 idx, idx_start, idx_end, max_nr_entries; int npages; bool huge; u64 gfn; - - if (!user_exit_on_hypercall(vcpu->kvm, KVM_HC_MAP_GPA_RANGE)) { - snp_complete_psc(svm, VMGEXIT_PSC_ERROR_GENERIC); - return 1; - } - - /* - * GHCB v2 requires the scratch area to reside within the GHCB itself, - * and PSC requests are only supported for GHCB v2+. Thus it should be - * impossible to exceed the max PSC entry count (which is derived from - * the size of the shared GHCB buffer). - */ - max_nr_entries = (sev_es->ghcb_sa_len - sizeof(struct psc_hdr)) / - sizeof(struct psc_entry); - if (WARN_ON_ONCE(max_nr_entries > VMGEXIT_PSC_MAX_COUNT)) { - snp_complete_psc(svm, VMGEXIT_PSC_ERROR_GENERIC); - return 1; - } + u16 idx; next_range: /* There should be no other PSCs in-flight at this point. */ @@ -3928,21 +3911,8 @@ static int snp_begin_psc(struct vcpu_svm *svm) return 1; } - /* - * The PSC descriptor buffer can be modified by a misbehaved guest after - * validation, so take care to only use validated copies of values used - * for things like array indexing. - */ - idx_start = READ_ONCE(guest_psc->hdr.cur_entry); - idx_end = READ_ONCE(guest_psc->hdr.end_entry); - - if (idx_end >= max_nr_entries) { - snp_complete_psc(svm, VMGEXIT_PSC_ERROR_INVALID_HDR); - return 1; - } - /* Find the start of the next range which needs processing. */ - for (idx = idx_start; idx <= idx_end; idx++) { + for (idx = sev_es->psc.cur_idx; idx <= sev_es->psc.end_idx; idx++) { entry_start = READ_ONCE(guest_psc->entries[idx]); gfn = entry_start.gfn; @@ -3979,7 +3949,7 @@ static int snp_begin_psc(struct vcpu_svm *svm) guest_psc->hdr.cur_entry++; } - if (idx > idx_end) { + if (idx > sev_es->psc.end_idx) { /* Nothing more to process. */ snp_complete_psc(svm, 0); return 1; @@ -3994,7 +3964,7 @@ static int snp_begin_psc(struct vcpu_svm *svm) * ranges/operations and can be combined into a single * KVM_HC_MAP_GPA_RANGE exit. */ - while (++idx <= idx_end) { + while (++idx <= sev_es->psc.end_idx) { struct psc_entry entry = READ_ONCE(guest_psc->entries[idx]); if (entry.operation != entry_start.operation || @@ -4044,6 +4014,46 @@ static int snp_begin_psc(struct vcpu_svm *svm) BUG(); } +static int snp_begin_psc(struct vcpu_svm *svm) +{ + struct vcpu_sev_es_state *sev_es = &svm->sev_es; + struct psc_buffer *guest_psc = sev_es->ghcb_sa; + u16 max_nr_entries; + + if (!user_exit_on_hypercall(svm->vcpu.kvm, KVM_HC_MAP_GPA_RANGE)) { + snp_complete_psc(svm, VMGEXIT_PSC_ERROR_GENERIC); + return 1; + } + + /* + * GHCB v2 requires the scratch area to reside within the GHCB itself, + * and PSC requests are only supported for GHCB v2+. Thus it should be + * impossible to exceed the max PSC entry count (which is derived from + * the size of the shared GHCB buffer). + */ + max_nr_entries = (sev_es->ghcb_sa_len - sizeof(struct psc_hdr)) / + sizeof(struct psc_entry); + if (WARN_ON_ONCE(max_nr_entries > VMGEXIT_PSC_MAX_COUNT)) { + snp_complete_psc(svm, VMGEXIT_PSC_ERROR_GENERIC); + return 1; + } + + /* + * The PSC descriptor buffer can be modified by a misbehaved guest after + * validation, so take care to only use validated copies of values used + * for things like array indexing. + */ + sev_es->psc.cur_idx = READ_ONCE(guest_psc->hdr.cur_entry); + sev_es->psc.end_idx = READ_ONCE(guest_psc->hdr.end_entry); + + if (sev_es->psc.end_idx >= max_nr_entries) { + snp_complete_psc(svm, VMGEXIT_PSC_ERROR_INVALID_HDR); + return 1; + } + + return snp_do_psc(svm); +} + /* * Invoked as part of svm_vcpu_reset() processing of an init event. */ diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h index 06192bc9c107..5137416be593 100644 --- a/arch/x86/kvm/svm/svm.h +++ b/arch/x86/kvm/svm/svm.h @@ -259,6 +259,7 @@ struct vcpu_sev_es_state { /* SNP Page-State-Change buffer entries currently being processed */ struct { u16 cur_idx; + u16 end_idx; u16 batch_size; bool is_2m; } psc; From 8618004d3e897c0f1b71d9a9ab860461289bb89a Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 29 May 2026 20:35:39 +0200 Subject: [PATCH 096/257] KVM: Don't WARN if memory is dirtied without a vCPU when the VM is dying When marking a page dirty, complain about not having a running/loaded vCPU if and only if the VM is still alive, i.e. its refcount is non-zero. This will allow fixing a memory leak for x86 SEV-ES guests without hitting what is effectively a false positive on the WARN. For some SEV-ES VM-Exits, KVM keeps a writable mapping of a guest page across an exit to userspace, and typically unmaps the page on the next KVM_RUN. But if userspace never calls KVM_RUN after such an exit, then KVM needs to unmap the page when the vCPU is destroyed, which in turn triggers the WARN about not having a running vCPU. Alternatively, SEV-ES could temporarily load the vCPU to suppress the WARN, as is done in nested_vmx_free_vcpu() (but for completely unrelated reasons; suppressing WARN from nested_put_vmcs12_pages() is pure happenstance). But loading a vCPU during destruction is gross (ideally nVMX code would be cleaned up), risks complicating the SEV-ES code (KVM would need to ensure the temporarily load()+put() only runs when the vCPU isn't already loaded), and is ultimately pointless. The motivation for the WARN is to guard against KVM dirtying guest memory without pushing the corresponding GFN to the active vCPU's dirty ring, e.g. to ensure userspace doesn't miss a dirty page. But for the VM's refcount to reach zero, there can't be _any_ userspace mappings to the dirty ring, as mapping the dirty ring requires doing mmap() on the vCPU FD. I.e. if userspace had a valid mapping for the dirty ring, then the vCPU file and thus the owning VM would still be alive. And so since userspace can't possibly reach the dirty ring, whether or not KVM technically "misses" a push to the dirty ring is irrelevant. Reported-by: Michael Roth Cc: stable@vger.kernel.org Reviewed-by: Michael Roth Signed-off-by: Sean Christopherson Message-ID: <20260501202250.2115252-15-seanjc@google.com> Signed-off-by: Paolo Bonzini Message-ID: <20260529183549.1104619-15-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- virt/kvm/kvm_main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 89489996fbc1..881f92d7a469 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -3520,7 +3520,8 @@ void mark_page_dirty_in_slot(struct kvm *kvm, if (WARN_ON_ONCE(vcpu && vcpu->kvm != kvm)) return; - WARN_ON_ONCE(!vcpu && !kvm_arch_allow_write_without_running_vcpu(kvm)); + WARN_ON_ONCE(!vcpu && refcount_read(&kvm->users_count) && + !kvm_arch_allow_write_without_running_vcpu(kvm)); #endif if (memslot && kvm_slot_dirty_track_enabled(memslot)) { From 08385c5e1814edee829ffe475d559ed730354335 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 29 May 2026 20:35:40 +0200 Subject: [PATCH 097/257] KVM: SEV: Move sev_free_vcpu() down below sev_es_unmap_ghcb() Relocate sev_free_vcpu() down in sev.c so that it's definition comes after sev_es_unmap_ghcb(). This will allow sharing unmap functionality between the two functions without needing a forward declaration (or weird placement of the common code). No functional change intended. Cc: stable@vger.kernel.org Reviewed-by: Tom Lendacky Reviewed-by: Michael Roth Signed-off-by: Sean Christopherson Message-ID: <20260501202250.2115252-16-seanjc@google.com> Signed-off-by: Paolo Bonzini Message-ID: <20260529183549.1104619-16-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- arch/x86/kvm/svm/sev.c | 62 +++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index 4ebe0d449789..437282f0ea94 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -3313,37 +3313,6 @@ void sev_guest_memory_reclaimed(struct kvm *kvm) sev_writeback_caches(kvm); } -void sev_free_vcpu(struct kvm_vcpu *vcpu) -{ - struct vcpu_svm *svm; - - if (!is_sev_es_guest(vcpu)) - return; - - svm = to_svm(vcpu); - - /* - * If it's an SNP guest, then the VMSA was marked in the RMP table as - * a guest-owned page. Transition the page to hypervisor state before - * releasing it back to the system. - */ - if (is_sev_snp_guest(vcpu)) { - u64 pfn = __pa(svm->sev_es.vmsa) >> PAGE_SHIFT; - - if (kvm_rmp_make_shared(vcpu->kvm, pfn, PG_LEVEL_4K)) - goto skip_vmsa_free; - } - - if (vcpu->arch.guest_state_protected) - sev_flush_encrypted_page(vcpu, svm->sev_es.vmsa); - - __free_page(virt_to_page(svm->sev_es.vmsa)); - -skip_vmsa_free: - if (svm->sev_es.ghcb_sa_free) - kvfree(svm->sev_es.ghcb_sa); -} - static void dump_ghcb(struct vcpu_svm *svm) { struct vmcb_control_area *control = &svm->vmcb->control; @@ -3618,6 +3587,37 @@ void sev_es_unmap_ghcb(struct vcpu_svm *svm) svm->sev_es.ghcb = NULL; } +void sev_free_vcpu(struct kvm_vcpu *vcpu) +{ + struct vcpu_svm *svm; + + if (!is_sev_es_guest(vcpu)) + return; + + svm = to_svm(vcpu); + + /* + * If it's an SNP guest, then the VMSA was marked in the RMP table as + * a guest-owned page. Transition the page to hypervisor state before + * releasing it back to the system. + */ + if (is_sev_snp_guest(vcpu)) { + u64 pfn = __pa(svm->sev_es.vmsa) >> PAGE_SHIFT; + + if (kvm_rmp_make_shared(vcpu->kvm, pfn, PG_LEVEL_4K)) + goto skip_vmsa_free; + } + + if (vcpu->arch.guest_state_protected) + sev_flush_encrypted_page(vcpu, svm->sev_es.vmsa); + + __free_page(virt_to_page(svm->sev_es.vmsa)); + +skip_vmsa_free: + if (svm->sev_es.ghcb_sa_free) + kvfree(svm->sev_es.ghcb_sa); +} + int pre_sev_run(struct vcpu_svm *svm, int cpu) { struct svm_cpu_data *sd = per_cpu_ptr(&svm_data, cpu); From f041dc80de4abbdd0909d871bf64f3f87d2350ff Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 29 May 2026 20:35:41 +0200 Subject: [PATCH 098/257] KVM: SEV: Decouple the need to sync the GHCB SA from the need to free the SA Decouple synchronizing the GHCB SA from freeing/unpinning the SA, so that the free/unpin path can be reused when freeing a vCPU. Opportunistically add a WARN to harden KVM against stomping over (and thus leaking) an already-allocated scratch area. Cc: stable@vger.kernel.org Reviewed-by: Tom Lendacky Reviewed-by: Michael Roth Signed-off-by: Sean Christopherson Message-ID: <20260501202250.2115252-17-seanjc@google.com> Signed-off-by: Paolo Bonzini Message-ID: <20260529183549.1104619-17-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- arch/x86/kvm/svm/sev.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index 437282f0ea94..11d46600cbdc 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -3560,20 +3560,17 @@ void sev_es_unmap_ghcb(struct vcpu_svm *svm) if (!svm->sev_es.ghcb) return; - if (svm->sev_es.ghcb_sa_free) { - /* - * The scratch area lives outside the GHCB, so there is a - * buffer that, depending on the operation performed, may - * need to be synced, then freed. - */ - if (svm->sev_es.ghcb_sa_sync) { - kvm_write_guest(svm->vcpu.kvm, - svm->sev_es.sw_scratch, - svm->sev_es.ghcb_sa, - svm->sev_es.ghcb_sa_len); - svm->sev_es.ghcb_sa_sync = false; - } + /* + * If the scratch area lives outside the GHCB, there's a buffer that, + * depending on the operation performed, may need to be synced. + */ + if (svm->sev_es.ghcb_sa_sync) { + kvm_write_guest(svm->vcpu.kvm, svm->sev_es.sw_scratch, + svm->sev_es.ghcb_sa, svm->sev_es.ghcb_sa_len); + svm->sev_es.ghcb_sa_sync = false; + } + if (svm->sev_es.ghcb_sa_free) { kvfree(svm->sev_es.ghcb_sa); svm->sev_es.ghcb_sa = NULL; svm->sev_es.ghcb_sa_free = false; @@ -3685,6 +3682,8 @@ static int setup_vmgexit_scratch(struct vcpu_svm *svm, bool sync, u64 min_len) goto e_scratch; } + WARN_ON_ONCE(svm->sev_es.ghcb_sa_sync || svm->sev_es.ghcb_sa_free); + if ((scratch_gpa_beg & PAGE_MASK) == control->ghcb_gpa) { /* Scratch area begins within GHCB */ ghcb_scratch_beg = control->ghcb_gpa + @@ -3706,6 +3705,8 @@ static int setup_vmgexit_scratch(struct vcpu_svm *svm, bool sync, u64 min_len) scratch_va = (void *)svm->sev_es.ghcb; scratch_va += (scratch_gpa_beg - control->ghcb_gpa); + svm->sev_es.ghcb_sa_sync = false; + svm->sev_es.ghcb_sa_free = false; svm->sev_es.ghcb_sa_len = ghcb_scratch_end - scratch_gpa_beg; } else { /* GHCB v2 requires the scratch area to be within the GHCB. */ From db38bcb3311053954f62b865cd2d86e164b04351 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 29 May 2026 20:35:42 +0200 Subject: [PATCH 099/257] KVM: SEV: Unmap and unpin the GHCB as needed on vCPU free Unmap and unpin the GHCB as needed when freeing a vCPU. If the VM is destroyed after mapping+pinning the GHCB on #VMGEXIT, without re-running the vCPU, KVM will effectively leak the GHCB and any mappings created for the GHCB. Fixes: 291bd20d5d88 ("KVM: SVM: Add initial support for a VMGEXIT VMEXIT") Cc: stable@vger.kernel.org Tested-by: Michael Roth Reviewed-by: Tom Lendacky Reviewed-by: Michael Roth Signed-off-by: Sean Christopherson Message-ID: <20260501202250.2115252-18-seanjc@google.com> Signed-off-by: Paolo Bonzini Message-ID: <20260529183549.1104619-18-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- arch/x86/kvm/svm/sev.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index 11d46600cbdc..6c6a6d663e29 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -3552,6 +3552,20 @@ static int sev_es_validate_vmgexit(struct vcpu_svm *svm) return 1; } +static void __sev_es_unmap_ghcb(struct vcpu_svm *svm) +{ + if (svm->sev_es.ghcb_sa_free) { + kvfree(svm->sev_es.ghcb_sa); + svm->sev_es.ghcb_sa = NULL; + svm->sev_es.ghcb_sa_free = false; + } + + if (svm->sev_es.ghcb) { + kvm_vcpu_unmap(&svm->vcpu, &svm->sev_es.ghcb_map); + svm->sev_es.ghcb = NULL; + } +} + void sev_es_unmap_ghcb(struct vcpu_svm *svm) { /* Clear any indication that the vCPU is in a type of AP Reset Hold */ @@ -3570,18 +3584,11 @@ void sev_es_unmap_ghcb(struct vcpu_svm *svm) svm->sev_es.ghcb_sa_sync = false; } - if (svm->sev_es.ghcb_sa_free) { - kvfree(svm->sev_es.ghcb_sa); - svm->sev_es.ghcb_sa = NULL; - svm->sev_es.ghcb_sa_free = false; - } - trace_kvm_vmgexit_exit(svm->vcpu.vcpu_id, svm->sev_es.ghcb); sev_es_sync_to_ghcb(svm); - kvm_vcpu_unmap(&svm->vcpu, &svm->sev_es.ghcb_map); - svm->sev_es.ghcb = NULL; + __sev_es_unmap_ghcb(svm); } void sev_free_vcpu(struct kvm_vcpu *vcpu) @@ -3611,8 +3618,7 @@ void sev_free_vcpu(struct kvm_vcpu *vcpu) __free_page(virt_to_page(svm->sev_es.vmsa)); skip_vmsa_free: - if (svm->sev_es.ghcb_sa_free) - kvfree(svm->sev_es.ghcb_sa); + __sev_es_unmap_ghcb(svm); } int pre_sev_run(struct vcpu_svm *svm, int cpu) From 30252e6f71ba974ecf9cd8ce395b73b9900bc378 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Tue, 2 Jun 2026 13:24:01 +0200 Subject: [PATCH 100/257] drm/dumb-buffer: Drop buffer-size limits for now The size limits break some of the CI tests. So drop them for now. Keep the other overflow tests from commit 5ab62dd3687b ("drm: prevent integer overflows in dumb buffer creation helpers") in place. There is still a pre-existing overflow check for 32-bit type limits in drm_mode_create_dumb() that will catch the really absurd size requests. Drivers that still do not use drm_mode_size_dumb() should be updated. The helper calculates dumb-buffer geometry with overflow checks. Signed-off-by: Thomas Zimmermann Fixes: 5ab62dd3687b ("drm: prevent integer overflows in dumb buffer creation helpers") Reported-by: Jani Nikula Closes: https://lore.kernel.org/dri-devel/ddf0233e50044059c85279f928661563ef6a55bf@intel.com/ Cc: Rajat Gupta Cc: Thomas Zimmermann Cc: Maarten Lankhorst Cc: Maxime Ripard Acked-by: Jani Nikula Link: https://patch.msgid.link/20260602112842.252279-1-tzimmermann@suse.de --- drivers/gpu/drm/drm_dumb_buffers.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/gpu/drm/drm_dumb_buffers.c b/drivers/gpu/drm/drm_dumb_buffers.c index cc99681a9ed0..2156dbe601c9 100644 --- a/drivers/gpu/drm/drm_dumb_buffers.c +++ b/drivers/gpu/drm/drm_dumb_buffers.c @@ -202,13 +202,6 @@ int drm_mode_create_dumb(struct drm_device *dev, if (!args->width || !args->height || !args->bpp) return -EINVAL; - /* Reject unreasonable inputs early. Dumb buffers are for software - * rendering; nothing legitimate needs more than 8192x8192 at 32bpp. - * This prevents overflows in downstream alignment helpers. - */ - if (args->width >= 8192 || args->height >= 8192 || args->bpp > 32) - return -EINVAL; - /* overflow checks for 32bit size calculations */ if (args->bpp > U32_MAX - 8) return -EINVAL; From 323c98a4ff06aa28114f2bf658fb43eb3b536bbc Mon Sep 17 00:00:00 2001 From: Yishai Hadas Date: Mon, 25 May 2026 17:21:36 +0300 Subject: [PATCH 101/257] RDMA/core: Validate cpu_id against nr_cpu_ids in DMAH alloc The cpu_id attribute supplied by user space through UVERBS_ATTR_ALLOC_DMAH_CPU_ID is passed directly to cpumask_test_cpu() without first verifying that the value is within the valid CPU range. Passing such untrusted data to cpumask_test_cpu() may lead to an out-of-bounds read of the underlying cpumask bitmap: the helper expands to a test_bit() that indexes the bitmap by cpu_id / BITS_PER_LONG with no bound check. In addition, on kernels built with CONFIG_DEBUG_PER_CPU_MAPS it trips the WARN_ON_ONCE() in cpumask_check(); combined with panic_on_warn this turns a bad user input into a machine reboot. Reject any cpu_id that is not smaller than nr_cpu_ids with -EINVAL before it is used. Reported by Smatch. Fixes: d83edab562a4 ("RDMA/core: Introduce a DMAH object and its alloc/free APIs") Link: https://patch.msgid.link/r/20260525142136.28165-1-yishaih@nvidia.com Cc: stable@vger.kernel.org Reported-by: Dan Carpenter Closes: https://lore.kernel.org/r/ag68qoAW3P04J7pT@stanley.mountain/ Signed-off-by: Yishai Hadas Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/uverbs_std_types_dmah.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/infiniband/core/uverbs_std_types_dmah.c b/drivers/infiniband/core/uverbs_std_types_dmah.c index 453ce656c6f2..97101e093826 100644 --- a/drivers/infiniband/core/uverbs_std_types_dmah.c +++ b/drivers/infiniband/core/uverbs_std_types_dmah.c @@ -47,6 +47,11 @@ static int UVERBS_HANDLER(UVERBS_METHOD_DMAH_ALLOC)( if (ret) goto err; + if (dmah->cpu_id >= nr_cpu_ids) { + ret = -EINVAL; + goto err; + } + if (!cpumask_test_cpu(dmah->cpu_id, current->cpus_ptr)) { ret = -EPERM; goto err; From 6590fe323ce2807f5d9454e7fccf3fab875d4352 Mon Sep 17 00:00:00 2001 From: Leorize Date: Wed, 27 May 2026 23:58:54 -0700 Subject: [PATCH 102/257] drm/amd/display: add missing CSC entries for BT.2020 for DCE IPs DCE-based hardware does not have the CSC matrices for BT.2020, which causes the driver to fallback to the GPU built-in matrices. This does not appear to cause any issues for RGB sinks, but causes major color artifacts for YCbCr ones (e.g. black becomes green). This commit adds the missing CSC matrices (taken from DC common) to DCE CSC tables, resolving the issue. Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/3358 Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/5333 Assisted-by: oh-my-pi:GPT-5.5 Signed-off-by: Leorize Reviewed-by: Alex Hung Signed-off-by: Alex Deucher (cherry picked from commit 51e6668ab4baf55b082c376318d51ef965757196) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/display/dc/dce/dce_transform.c | 10 +++++++++- .../gpu/drm/amd/display/dc/dce110/dce110_opp_csc_v.c | 10 +++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_transform.c b/drivers/gpu/drm/amd/display/dc/dce/dce_transform.c index c1448ae47366..0d312b40bcfa 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_transform.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_transform.c @@ -110,7 +110,15 @@ static const struct out_csc_color_matrix global_color_matrix[] = { { COLOR_SPACE_YCBCR601_LIMITED, { 0xE00, 0xF447, 0xFDB9, 0x1000, 0x991, 0x12C9, 0x3A6, 0x200, 0xFB47, 0xF6B9, 0xE00, 0x1000} }, { COLOR_SPACE_YCBCR709_LIMITED, { 0xE00, 0xF349, 0xFEB7, 0x1000, 0x6CE, 0x16E3, - 0x24F, 0x200, 0xFCCB, 0xF535, 0xE00, 0x1000} } + 0x24F, 0x200, 0xFCCB, 0xF535, 0xE00, 0x1000} }, +{ COLOR_SPACE_2020_RGB_FULLRANGE, + { 0x2000, 0, 0, 0, 0, 0x2000, 0, 0, 0, 0, 0x2000, 0} }, +{ COLOR_SPACE_2020_RGB_LIMITEDRANGE, + { 0x1B67, 0, 0, 0x201, 0, 0x1B67, 0, 0x201, 0, 0, 0x1B67, 0x201} }, +{ COLOR_SPACE_2020_YCBCR_LIMITED, { 0x1000, 0xF149, 0xFEB7, 0x1004, 0x0868, + 0x15B2, 0x01E6, 0x201, 0xFB88, 0xF478, 0x1000, 0x1004} }, +{ COLOR_SPACE_2020_YCBCR_FULL, { 0x1000, 0xF149, 0xFEB7, 0x1004, 0x0868, 0x15B2, + 0x01E6, 0x201, 0xFB88, 0xF478, 0x1000, 0x1004} } }; static bool setup_scaling_configuration( diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_csc_v.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_csc_v.c index cf63fac82832..1ed018aaa4bb 100644 --- a/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_csc_v.c +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_csc_v.c @@ -88,7 +88,15 @@ static const struct out_csc_color_matrix global_color_matrix[] = { { COLOR_SPACE_YCBCR601_LIMITED, { 0xE00, 0xF447, 0xFDB9, 0x1000, 0x991, 0x12C9, 0x3A6, 0x200, 0xFB47, 0xF6B9, 0xE00, 0x1000} }, { COLOR_SPACE_YCBCR709_LIMITED, { 0xE00, 0xF349, 0xFEB7, 0x1000, 0x6CE, 0x16E3, - 0x24F, 0x200, 0xFCCB, 0xF535, 0xE00, 0x1000} } + 0x24F, 0x200, 0xFCCB, 0xF535, 0xE00, 0x1000} }, +{ COLOR_SPACE_2020_RGB_FULLRANGE, + { 0x2000, 0, 0, 0, 0, 0x2000, 0, 0, 0, 0, 0x2000, 0} }, +{ COLOR_SPACE_2020_RGB_LIMITEDRANGE, + { 0x1B67, 0, 0, 0x201, 0, 0x1B67, 0, 0x201, 0, 0, 0x1B67, 0x201} }, +{ COLOR_SPACE_2020_YCBCR_LIMITED, { 0x1000, 0xF149, 0xFEB7, 0x1004, 0x0868, + 0x15B2, 0x01E6, 0x201, 0xFB88, 0xF478, 0x1000, 0x1004} }, +{ COLOR_SPACE_2020_YCBCR_FULL, { 0x1000, 0xF149, 0xFEB7, 0x1004, 0x0868, 0x15B2, + 0x01E6, 0x201, 0xFB88, 0xF478, 0x1000, 0x1004} } }; enum csc_color_mode { From e8b4d37eba05141ee01794fc6b7f2da808cee83b Mon Sep 17 00:00:00 2001 From: Harry Wentland Date: Tue, 5 May 2026 11:44:15 -0400 Subject: [PATCH 103/257] drm/amd/display: Fix out-of-bounds read in dp_get_eq_aux_rd_interval() [Why & How] The aux_rd_interval array in struct dc_lttpr_caps is declared with MAX_REPEATER_CNT - 1 (7) elements, indexed 0..6. However, the offset parameter passed to dp_get_eq_aux_rd_interval() can be as large as MAX_REPEATER_CNT (8) when a sink reports 8 LTTPR repeaters via DPCD. This leads to an out-of-bounds read of aux_rd_interval[7] when offset is 8. Fix this by growing aux_rd_interval to MAX_REPEATER_CNT elements to accommodate the full range of valid repeater counts defined by the DP spec. Assisted-by: GitHub Copilot:Claude claude-4-opus Signed-off-by: Harry Wentland Signed-off-by: Ray Wu Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher (cherry picked from commit a55a458a8df37a65ffda5cf721d554a8f74f6b04) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/display/dc/dc_dp_types.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/dc/dc_dp_types.h b/drivers/gpu/drm/amd/display/dc/dc_dp_types.h index 7fa336bf1115..7dd73eaaf940 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_dp_types.h +++ b/drivers/gpu/drm/amd/display/dc/dc_dp_types.h @@ -1217,7 +1217,7 @@ struct dc_lttpr_caps { union dp_main_link_channel_coding_lttpr_cap main_link_channel_coding; union dp_128b_132b_supported_lttpr_link_rates supported_128b_132b_rates; union dp_alpm_lttpr_cap alpm; - uint8_t aux_rd_interval[MAX_REPEATER_CNT - 1]; + uint8_t aux_rd_interval[MAX_REPEATER_CNT]; uint8_t lttpr_ieee_oui[3]; // Always read from closest LTTPR to host uint8_t lttpr_device_id[6]; // Always read from closest LTTPR to host }; From fb0707ce00eef4e2d60c3020e1c0432739703e4a Mon Sep 17 00:00:00 2001 From: Harry Wentland Date: Mon, 4 May 2026 15:51:13 -0400 Subject: [PATCH 104/257] drm/amd/display: Clamp VBIOS HDMI retimer register count to array size [Why & How] The VBIOS integrated info tables (v1_11 and v2_1) contain HdmiRegNum and Hdmi6GRegNum fields that are used as loop bounds when copying retimer I2C register settings into fixed-size arrays (dp*_ext_hdmi_reg_settings[9] and dp*_ext_hdmi_6g_reg_settings[3]). These u8 fields are not validated before use, so a malformed VBIOS can specify values up to 255, causing an out-of-bounds heap write during driver probe. Clamp each register count to the destination array size using min_t() before the copy loops, in both get_integrated_info_v11() and get_integrated_info_v2_1(). Assisted-by: GitHub Copilot:claude-opus-4.6 Reviewed-by: Alex Hung Signed-off-by: Harry Wentland Signed-off-by: Ray Wu Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher (cherry picked from commit 5a7f0ef90195940c54b0f5bb85b87da55f038c69) Cc: stable@vger.kernel.org --- .../drm/amd/display/dc/bios/bios_parser2.c | 48 ++++++++++++------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c b/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c index c51c4b2c6fae..e8d8947c552e 100644 --- a/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c +++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c @@ -2600,14 +2600,16 @@ static enum bp_result get_integrated_info_v11( info_v11->extdispconninfo.checksum; info->dp0_ext_hdmi_slv_addr = info_v11->dp0_retimer_set.HdmiSlvAddr; - info->dp0_ext_hdmi_reg_num = info_v11->dp0_retimer_set.HdmiRegNum; + info->dp0_ext_hdmi_reg_num = min_t(u8, info_v11->dp0_retimer_set.HdmiRegNum, + ARRAY_SIZE(info->dp0_ext_hdmi_reg_settings)); for (i = 0; i < info->dp0_ext_hdmi_reg_num; i++) { info->dp0_ext_hdmi_reg_settings[i].i2c_reg_index = info_v11->dp0_retimer_set.HdmiRegSetting[i].ucI2cRegIndex; info->dp0_ext_hdmi_reg_settings[i].i2c_reg_val = info_v11->dp0_retimer_set.HdmiRegSetting[i].ucI2cRegVal; } - info->dp0_ext_hdmi_6g_reg_num = info_v11->dp0_retimer_set.Hdmi6GRegNum; + info->dp0_ext_hdmi_6g_reg_num = min_t(u8, info_v11->dp0_retimer_set.Hdmi6GRegNum, + ARRAY_SIZE(info->dp0_ext_hdmi_6g_reg_settings)); for (i = 0; i < info->dp0_ext_hdmi_6g_reg_num; i++) { info->dp0_ext_hdmi_6g_reg_settings[i].i2c_reg_index = info_v11->dp0_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegIndex; @@ -2616,14 +2618,16 @@ static enum bp_result get_integrated_info_v11( } info->dp1_ext_hdmi_slv_addr = info_v11->dp1_retimer_set.HdmiSlvAddr; - info->dp1_ext_hdmi_reg_num = info_v11->dp1_retimer_set.HdmiRegNum; + info->dp1_ext_hdmi_reg_num = min_t(u8, info_v11->dp1_retimer_set.HdmiRegNum, + ARRAY_SIZE(info->dp1_ext_hdmi_reg_settings)); for (i = 0; i < info->dp1_ext_hdmi_reg_num; i++) { info->dp1_ext_hdmi_reg_settings[i].i2c_reg_index = info_v11->dp1_retimer_set.HdmiRegSetting[i].ucI2cRegIndex; info->dp1_ext_hdmi_reg_settings[i].i2c_reg_val = info_v11->dp1_retimer_set.HdmiRegSetting[i].ucI2cRegVal; } - info->dp1_ext_hdmi_6g_reg_num = info_v11->dp1_retimer_set.Hdmi6GRegNum; + info->dp1_ext_hdmi_6g_reg_num = min_t(u8, info_v11->dp1_retimer_set.Hdmi6GRegNum, + ARRAY_SIZE(info->dp1_ext_hdmi_6g_reg_settings)); for (i = 0; i < info->dp1_ext_hdmi_6g_reg_num; i++) { info->dp1_ext_hdmi_6g_reg_settings[i].i2c_reg_index = info_v11->dp1_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegIndex; @@ -2632,14 +2636,16 @@ static enum bp_result get_integrated_info_v11( } info->dp2_ext_hdmi_slv_addr = info_v11->dp2_retimer_set.HdmiSlvAddr; - info->dp2_ext_hdmi_reg_num = info_v11->dp2_retimer_set.HdmiRegNum; + info->dp2_ext_hdmi_reg_num = min_t(u8, info_v11->dp2_retimer_set.HdmiRegNum, + ARRAY_SIZE(info->dp2_ext_hdmi_reg_settings)); for (i = 0; i < info->dp2_ext_hdmi_reg_num; i++) { info->dp2_ext_hdmi_reg_settings[i].i2c_reg_index = info_v11->dp2_retimer_set.HdmiRegSetting[i].ucI2cRegIndex; info->dp2_ext_hdmi_reg_settings[i].i2c_reg_val = info_v11->dp2_retimer_set.HdmiRegSetting[i].ucI2cRegVal; } - info->dp2_ext_hdmi_6g_reg_num = info_v11->dp2_retimer_set.Hdmi6GRegNum; + info->dp2_ext_hdmi_6g_reg_num = min_t(u8, info_v11->dp2_retimer_set.Hdmi6GRegNum, + ARRAY_SIZE(info->dp2_ext_hdmi_6g_reg_settings)); for (i = 0; i < info->dp2_ext_hdmi_6g_reg_num; i++) { info->dp2_ext_hdmi_6g_reg_settings[i].i2c_reg_index = info_v11->dp2_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegIndex; @@ -2648,14 +2654,16 @@ static enum bp_result get_integrated_info_v11( } info->dp3_ext_hdmi_slv_addr = info_v11->dp3_retimer_set.HdmiSlvAddr; - info->dp3_ext_hdmi_reg_num = info_v11->dp3_retimer_set.HdmiRegNum; + info->dp3_ext_hdmi_reg_num = min_t(u8, info_v11->dp3_retimer_set.HdmiRegNum, + ARRAY_SIZE(info->dp3_ext_hdmi_reg_settings)); for (i = 0; i < info->dp3_ext_hdmi_reg_num; i++) { info->dp3_ext_hdmi_reg_settings[i].i2c_reg_index = info_v11->dp3_retimer_set.HdmiRegSetting[i].ucI2cRegIndex; info->dp3_ext_hdmi_reg_settings[i].i2c_reg_val = info_v11->dp3_retimer_set.HdmiRegSetting[i].ucI2cRegVal; } - info->dp3_ext_hdmi_6g_reg_num = info_v11->dp3_retimer_set.Hdmi6GRegNum; + info->dp3_ext_hdmi_6g_reg_num = min_t(u8, info_v11->dp3_retimer_set.Hdmi6GRegNum, + ARRAY_SIZE(info->dp3_ext_hdmi_6g_reg_settings)); for (i = 0; i < info->dp3_ext_hdmi_6g_reg_num; i++) { info->dp3_ext_hdmi_6g_reg_settings[i].i2c_reg_index = info_v11->dp3_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegIndex; @@ -2805,14 +2813,16 @@ static enum bp_result get_integrated_info_v2_1( info->ext_disp_conn_info.checksum = info_v2_1->extdispconninfo.checksum; info->dp0_ext_hdmi_slv_addr = info_v2_1->dp0_retimer_set.HdmiSlvAddr; - info->dp0_ext_hdmi_reg_num = info_v2_1->dp0_retimer_set.HdmiRegNum; + info->dp0_ext_hdmi_reg_num = min_t(u8, info_v2_1->dp0_retimer_set.HdmiRegNum, + ARRAY_SIZE(info->dp0_ext_hdmi_reg_settings)); for (i = 0; i < info->dp0_ext_hdmi_reg_num; i++) { info->dp0_ext_hdmi_reg_settings[i].i2c_reg_index = info_v2_1->dp0_retimer_set.HdmiRegSetting[i].ucI2cRegIndex; info->dp0_ext_hdmi_reg_settings[i].i2c_reg_val = info_v2_1->dp0_retimer_set.HdmiRegSetting[i].ucI2cRegVal; } - info->dp0_ext_hdmi_6g_reg_num = info_v2_1->dp0_retimer_set.Hdmi6GRegNum; + info->dp0_ext_hdmi_6g_reg_num = min_t(u8, info_v2_1->dp0_retimer_set.Hdmi6GRegNum, + ARRAY_SIZE(info->dp0_ext_hdmi_6g_reg_settings)); for (i = 0; i < info->dp0_ext_hdmi_6g_reg_num; i++) { info->dp0_ext_hdmi_6g_reg_settings[i].i2c_reg_index = info_v2_1->dp0_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegIndex; @@ -2820,14 +2830,16 @@ static enum bp_result get_integrated_info_v2_1( info_v2_1->dp0_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegVal; } info->dp1_ext_hdmi_slv_addr = info_v2_1->dp1_retimer_set.HdmiSlvAddr; - info->dp1_ext_hdmi_reg_num = info_v2_1->dp1_retimer_set.HdmiRegNum; + info->dp1_ext_hdmi_reg_num = min_t(u8, info_v2_1->dp1_retimer_set.HdmiRegNum, + ARRAY_SIZE(info->dp1_ext_hdmi_reg_settings)); for (i = 0; i < info->dp1_ext_hdmi_reg_num; i++) { info->dp1_ext_hdmi_reg_settings[i].i2c_reg_index = info_v2_1->dp1_retimer_set.HdmiRegSetting[i].ucI2cRegIndex; info->dp1_ext_hdmi_reg_settings[i].i2c_reg_val = info_v2_1->dp1_retimer_set.HdmiRegSetting[i].ucI2cRegVal; } - info->dp1_ext_hdmi_6g_reg_num = info_v2_1->dp1_retimer_set.Hdmi6GRegNum; + info->dp1_ext_hdmi_6g_reg_num = min_t(u8, info_v2_1->dp1_retimer_set.Hdmi6GRegNum, + ARRAY_SIZE(info->dp1_ext_hdmi_6g_reg_settings)); for (i = 0; i < info->dp1_ext_hdmi_6g_reg_num; i++) { info->dp1_ext_hdmi_6g_reg_settings[i].i2c_reg_index = info_v2_1->dp1_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegIndex; @@ -2835,14 +2847,16 @@ static enum bp_result get_integrated_info_v2_1( info_v2_1->dp1_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegVal; } info->dp2_ext_hdmi_slv_addr = info_v2_1->dp2_retimer_set.HdmiSlvAddr; - info->dp2_ext_hdmi_reg_num = info_v2_1->dp2_retimer_set.HdmiRegNum; + info->dp2_ext_hdmi_reg_num = min_t(u8, info_v2_1->dp2_retimer_set.HdmiRegNum, + ARRAY_SIZE(info->dp2_ext_hdmi_reg_settings)); for (i = 0; i < info->dp2_ext_hdmi_reg_num; i++) { info->dp2_ext_hdmi_reg_settings[i].i2c_reg_index = info_v2_1->dp2_retimer_set.HdmiRegSetting[i].ucI2cRegIndex; info->dp2_ext_hdmi_reg_settings[i].i2c_reg_val = info_v2_1->dp2_retimer_set.HdmiRegSetting[i].ucI2cRegVal; } - info->dp2_ext_hdmi_6g_reg_num = info_v2_1->dp2_retimer_set.Hdmi6GRegNum; + info->dp2_ext_hdmi_6g_reg_num = min_t(u8, info_v2_1->dp2_retimer_set.Hdmi6GRegNum, + ARRAY_SIZE(info->dp2_ext_hdmi_6g_reg_settings)); for (i = 0; i < info->dp2_ext_hdmi_6g_reg_num; i++) { info->dp2_ext_hdmi_6g_reg_settings[i].i2c_reg_index = info_v2_1->dp2_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegIndex; @@ -2850,14 +2864,16 @@ static enum bp_result get_integrated_info_v2_1( info_v2_1->dp2_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegVal; } info->dp3_ext_hdmi_slv_addr = info_v2_1->dp3_retimer_set.HdmiSlvAddr; - info->dp3_ext_hdmi_reg_num = info_v2_1->dp3_retimer_set.HdmiRegNum; + info->dp3_ext_hdmi_reg_num = min_t(u8, info_v2_1->dp3_retimer_set.HdmiRegNum, + ARRAY_SIZE(info->dp3_ext_hdmi_reg_settings)); for (i = 0; i < info->dp3_ext_hdmi_reg_num; i++) { info->dp3_ext_hdmi_reg_settings[i].i2c_reg_index = info_v2_1->dp3_retimer_set.HdmiRegSetting[i].ucI2cRegIndex; info->dp3_ext_hdmi_reg_settings[i].i2c_reg_val = info_v2_1->dp3_retimer_set.HdmiRegSetting[i].ucI2cRegVal; } - info->dp3_ext_hdmi_6g_reg_num = info_v2_1->dp3_retimer_set.Hdmi6GRegNum; + info->dp3_ext_hdmi_6g_reg_num = min_t(u8, info_v2_1->dp3_retimer_set.Hdmi6GRegNum, + ARRAY_SIZE(info->dp3_ext_hdmi_6g_reg_settings)); for (i = 0; i < info->dp3_ext_hdmi_6g_reg_num; i++) { info->dp3_ext_hdmi_6g_reg_settings[i].i2c_reg_index = info_v2_1->dp3_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegIndex; From adf67034b1f61f7119295208085bfd43f85f56af Mon Sep 17 00:00:00 2001 From: Harry Wentland Date: Mon, 11 May 2026 16:46:25 -0400 Subject: [PATCH 105/257] drm/amd/display: Fix NULL deref and buffer over-read in SDP debugfs [Why & How] dp_sdp_message_debugfs_write() dereferences connector->base.state->crtc without checking for NULL. A connector can be connected but not bound to any CRTC (e.g. after hot-plug before the next atomic commit), causing a kernel crash when writing to the sdp_message debugfs node. The function also ignores the user-provided size argument and always passes 36 bytes to copy_from_user(), reading past the user buffer when size < 36. Fix both issues by: - Returning -ENODEV when connector->base.state or state->crtc is NULL - Clamping write_size to min(size, sizeof(data)) Fixes: c7ba3653e977 ("drm/amd/display: Generic SDP message access in amdgpu") Assisted-by: Copilot:claude-opus-4.6 Reviewed-by: Alex Hung Signed-off-by: Harry Wentland Signed-off-by: Ray Wu Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher (cherry picked from commit 6ab4c36a522842ff70474a1c0af2e40e50fc8300) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c index 2409ac72b166..3a3d01ce0d42 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c @@ -1344,8 +1344,13 @@ static ssize_t dp_sdp_message_debugfs_write(struct file *f, const char __user *b if (size == 0) return 0; + if (!connector->base.state || !connector->base.state->crtc) + return -ENODEV; + acrtc_state = to_dm_crtc_state(connector->base.state->crtc->state); + write_size = min_t(size_t, size, sizeof(data)); + r = copy_from_user(data, buf, write_size); write_size -= r; From da48bc4461b8a5ebfb9264c9b191a701d8e99009 Mon Sep 17 00:00:00 2001 From: Harry Wentland Date: Tue, 5 May 2026 11:52:15 -0400 Subject: [PATCH 106/257] drm/amd/display: Use krealloc_array() in dal_vector_reserve() [Why & How] dal_vector_reserve() computes the allocation size as "capacity * vector->struct_size" using uint32_t arithmetic, which can silently wrap to a small value on overflow. This would cause krealloc to return a smaller buffer than expected, leading to heap overflows on subsequent vector appends. Replace krealloc() with krealloc_array() which performs an internal overflow check and returns NULL on wrap, preventing the issue. Fixes: 2004f45ef83f ("drm/amd/display: Use kernel alloc/free") Assisted-by: Copilot:claude-opus-4.6 Reviewed-by: Alex Hung Signed-off-by: Harry Wentland Signed-off-by: Ray Wu Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher (cherry picked from commit 37668568641ccc4cc1dbca4923d0a16609dd5707) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/display/dc/basics/vector.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/basics/vector.c b/drivers/gpu/drm/amd/display/dc/basics/vector.c index e8736c134b8d..60bd9ead928a 100644 --- a/drivers/gpu/drm/amd/display/dc/basics/vector.c +++ b/drivers/gpu/drm/amd/display/dc/basics/vector.c @@ -289,8 +289,8 @@ bool dal_vector_reserve(struct vector *vector, uint32_t capacity) if (capacity <= vector->capacity) return true; - new_container = krealloc(vector->container, - capacity * vector->struct_size, GFP_KERNEL); + new_container = krealloc_array(vector->container, + capacity, vector->struct_size, GFP_KERNEL); if (new_container) { vector->container = new_container; From 49c3da65961fe9857c831d47fa1989084e87514a Mon Sep 17 00:00:00 2001 From: Harry Wentland Date: Tue, 5 May 2026 11:50:07 -0400 Subject: [PATCH 107/257] drm/amd/display: Reject gpio_bitshift >= 32 in bios_parser_get_gpio_pin_info() [Why & How] gpio_bitshift is a uint8_t read directly from the VBIOS GPIO pin table. If the value is >= 32, the expression "1 << gpio_bitshift" triggers undefined behaviour in C (shift count exceeds type width). On x86 the shift is silently masked to 5 bits, producing an incorrect GPIO mask that may cause wrong MMIO register bits to be toggled. Validate gpio_bitshift before use and return BP_RESULT_BADBIOSTABLE for out-of-range values. Fixes: ae79c310b1a6 ("drm/amd/display: Add DCE12 bios parser support") Assisted-by: Copilot:claude-opus-4.6 Reviewed-by: Alex Hung Signed-off-by: Harry Wentland Signed-off-by: Ray Wu Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher (cherry picked from commit eadf438ab8d370b9d19acee9359918c85afeb80d) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c b/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c index e8d8947c552e..4f213ea865b8 100644 --- a/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c +++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c @@ -701,8 +701,10 @@ static enum bp_result bios_parser_get_gpio_pin_info( info->offset_en = info->offset + 1; info->offset_mask = info->offset - 1; - info->mask = (uint32_t) (1 << - header->gpio_pin[i].gpio_bitshift); + if (header->gpio_pin[i].gpio_bitshift >= 32) + return BP_RESULT_BADBIOSTABLE; + + info->mask = 1u << header->gpio_pin[i].gpio_bitshift; info->mask_y = info->mask + 2; info->mask_en = info->mask + 1; info->mask_mask = info->mask - 1; From f0f3981c43b32cadfe373d636d9e9ca522bb3702 Mon Sep 17 00:00:00 2001 From: Harry Wentland Date: Thu, 7 May 2026 15:38:37 -0400 Subject: [PATCH 108/257] drm/amd/display: Clamp HDMI HDCP2 rx_id_list read to buffer size [Why & How] During HDCP 2.x repeater authentication over HDMI, the driver reads the sink's RxStatus register and extracts a 10-bit message size field (max value 1023). This value is used as the read length for the ReceiverID list without being clamped to the size of the destination buffer rx_id_list[177]. A malicious HDMI repeater could advertise a message size larger than the buffer, causing an out-of-bounds write during the I2C read. Clamp the read length in mod_hdcp_read_rx_id_list() to the size of the rx_id_list buffer, matching the approach already used in the DP branch. Fixes: eff682f83c9c ("drm/amd/display: Add DDC handles for HDCP2.2") Assisted-by: Copilot:claude-opus-4.6 Reviewed-by: Alex Hung Signed-off-by: Harry Wentland Signed-off-by: Ray Wu Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher (cherry picked from commit 229212219e4247d9486f8ba41ef087358490be09) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/display/modules/hdcp/hdcp_ddc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_ddc.c b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_ddc.c index 0ca39873f807..324413a090bf 100644 --- a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_ddc.c +++ b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_ddc.c @@ -529,7 +529,8 @@ enum mod_hdcp_status mod_hdcp_read_rx_id_list(struct mod_hdcp *hdcp) } else { status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_REPEATER_AUTH_SEND_RECEIVERID_LIST, hdcp->auth.msg.hdcp2.rx_id_list, - hdcp->auth.msg.hdcp2.rx_id_list_size); + MIN(hdcp->auth.msg.hdcp2.rx_id_list_size, + sizeof(hdcp->auth.msg.hdcp2.rx_id_list))); } return status; } From ff287df16a1a58aca78b08d1f3ee09fc44da0351 Mon Sep 17 00:00:00 2001 From: Harry Wentland Date: Tue, 12 May 2026 15:24:22 -0400 Subject: [PATCH 109/257] drm/amd/display: Bound VBIOS record-chain walk loops [Why & How] All record-chain walk loops in bios_parser.c and bios_parser2.c use for(;;) and only terminate on a 0xFF record_type sentinel or zero record_size. A malformed VBIOS image missing the terminator record causes unbounded iteration at probe time, potentially hundreds of thousands of iterations with record_size=1. In the final iterations near the BIOS image boundary, struct casts beyond the 2-byte header validated by GET_IMAGE can also read out of bounds. Cap all 14 record-chain walk loops to BIOS_MAX_NUM_RECORD (256) iterations. The atombios.h defines up to 22 distinct record types and atomfirmware.h has 13. Assuming an average of less than 10 records per type (which is reasonable since most are connector- based) 256 is a generous upper bound. Fixes: 4562236b3bc0 ("drm/amd/dc: Add dc display driver (v2)") Assisted-by: Copilot:claude-opus-4.6 Mythos Reviewed-by: Alex Hung Signed-off-by: Harry Wentland Signed-off-by: Ray Wu Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher (cherry picked from commit 95700a3d660287ed657d6892f7be9ffc0e294a93) Cc: stable@vger.kernel.org --- .../gpu/drm/amd/display/dc/bios/bios_parser.c | 15 +++++++---- .../drm/amd/display/dc/bios/bios_parser2.c | 27 ++++++++++++------- .../amd/display/dc/bios/bios_parser_helper.h | 5 ++++ 3 files changed, 33 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c b/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c index c307f42fe0b9..507b628abdb5 100644 --- a/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c +++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c @@ -222,6 +222,7 @@ static enum bp_result bios_parser_get_i2c_info(struct dc_bios *dcb, ATOM_COMMON_RECORD_HEADER *header; ATOM_I2C_RECORD *record; struct bios_parser *bp = BP_FROM_DCB(dcb); + int i; if (!info) return BP_RESULT_BADINPUT; @@ -234,7 +235,7 @@ static enum bp_result bios_parser_get_i2c_info(struct dc_bios *dcb, offset = le16_to_cpu(object->usRecordOffset) + bp->object_info_tbl_offset; - for (;;) { + for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); if (!header) @@ -293,11 +294,12 @@ static enum bp_result bios_parser_get_device_tag_record( { ATOM_COMMON_RECORD_HEADER *header; uint32_t offset; + int i; offset = le16_to_cpu(object->usRecordOffset) + bp->object_info_tbl_offset; - for (;;) { + for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); if (!header) @@ -966,6 +968,7 @@ static ATOM_HPD_INT_RECORD *get_hpd_record(struct bios_parser *bp, { ATOM_COMMON_RECORD_HEADER *header; uint32_t offset; + int i; if (!object) { BREAK_TO_DEBUGGER(); /* Invalid object */ @@ -975,7 +978,7 @@ static ATOM_HPD_INT_RECORD *get_hpd_record(struct bios_parser *bp, offset = le16_to_cpu(object->usRecordOffset) + bp->object_info_tbl_offset; - for (;;) { + for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); if (!header) @@ -1670,6 +1673,7 @@ static ATOM_ENCODER_CAP_RECORD_V2 *get_encoder_cap_record( { ATOM_COMMON_RECORD_HEADER *header; uint32_t offset; + int i; if (!object) { BREAK_TO_DEBUGGER(); /* Invalid object */ @@ -1679,7 +1683,7 @@ static ATOM_ENCODER_CAP_RECORD_V2 *get_encoder_cap_record( offset = le16_to_cpu(object->usRecordOffset) + bp->object_info_tbl_offset; - for (;;) { + for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); if (!header) @@ -2769,6 +2773,7 @@ static enum bp_result update_slot_layout_info(struct dc_bios *dcb, { (void)i; unsigned int j; + unsigned int n; struct bios_parser *bp; ATOM_BRACKET_LAYOUT_RECORD *record; ATOM_COMMON_RECORD_HEADER *record_header; @@ -2778,7 +2783,7 @@ static enum bp_result update_slot_layout_info(struct dc_bios *dcb, record = NULL; record_header = NULL; - for (;;) { + for (n = 0; n < BIOS_MAX_NUM_RECORD; n++) { record_header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, record_offset); if (record_header == NULL) { diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c b/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c index 4f213ea865b8..0e1f973326ed 100644 --- a/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c +++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c @@ -396,6 +396,7 @@ static enum bp_result bios_parser_get_i2c_info(struct dc_bios *dcb, struct atom_i2c_record *record; struct atom_i2c_record dummy_record = {0}; struct bios_parser *bp = BP_FROM_DCB(dcb); + int i; if (!info) return BP_RESULT_BADINPUT; @@ -429,7 +430,7 @@ static enum bp_result bios_parser_get_i2c_info(struct dc_bios *dcb, break; } - for (;;) { + for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { header = GET_IMAGE(struct atom_common_record_header, offset); if (!header) @@ -534,6 +535,7 @@ static struct atom_hpd_int_record *get_hpd_record_for_path_v3(struct bios_parser { struct atom_common_record_header *header; uint32_t offset; + int i; if (!object) { BREAK_TO_DEBUGGER(); /* Invalid object */ @@ -542,7 +544,7 @@ static struct atom_hpd_int_record *get_hpd_record_for_path_v3(struct bios_parser offset = object->disp_recordoffset + bp->object_info_tbl_offset; - for (;;) { + for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { header = GET_IMAGE(struct atom_common_record_header, offset); if (!header) @@ -611,6 +613,7 @@ static struct atom_hpd_int_record *get_hpd_record( { struct atom_common_record_header *header; uint32_t offset; + int i; if (!object) { BREAK_TO_DEBUGGER(); /* Invalid object */ @@ -620,7 +623,7 @@ static struct atom_hpd_int_record *get_hpd_record( offset = le16_to_cpu(object->disp_recordoffset) + bp->object_info_tbl_offset; - for (;;) { + for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { header = GET_IMAGE(struct atom_common_record_header, offset); if (!header) @@ -2195,6 +2198,7 @@ static struct atom_encoder_caps_record *get_encoder_cap_record( { struct atom_common_record_header *header; uint32_t offset; + int i; if (!object) { BREAK_TO_DEBUGGER(); /* Invalid object */ @@ -2203,7 +2207,7 @@ static struct atom_encoder_caps_record *get_encoder_cap_record( offset = object->encoder_recordoffset + bp->object_info_tbl_offset; - for (;;) { + for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { header = GET_IMAGE(struct atom_common_record_header, offset); if (!header) @@ -2232,6 +2236,7 @@ static struct atom_disp_connector_caps_record *get_disp_connector_caps_record( { struct atom_common_record_header *header; uint32_t offset; + int i; if (!object) { BREAK_TO_DEBUGGER(); /* Invalid object */ @@ -2240,7 +2245,7 @@ static struct atom_disp_connector_caps_record *get_disp_connector_caps_record( offset = object->disp_recordoffset + bp->object_info_tbl_offset; - for (;;) { + for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { header = GET_IMAGE(struct atom_common_record_header, offset); if (!header) @@ -2268,6 +2273,7 @@ static struct atom_connector_caps_record *get_connector_caps_record(struct bios_ { struct atom_common_record_header *header; uint32_t offset; + int i; if (!object) { BREAK_TO_DEBUGGER(); /* Invalid object */ @@ -2276,7 +2282,7 @@ static struct atom_connector_caps_record *get_connector_caps_record(struct bios_ offset = object->disp_recordoffset + bp->object_info_tbl_offset; - for (;;) { + for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { header = GET_IMAGE(struct atom_common_record_header, offset); if (!header) @@ -2354,6 +2360,7 @@ static struct atom_connector_speed_record *get_connector_speed_cap_record(struct { struct atom_common_record_header *header; uint32_t offset; + int i; if (!object) { BREAK_TO_DEBUGGER(); /* Invalid object */ @@ -2362,7 +2369,7 @@ static struct atom_connector_speed_record *get_connector_speed_cap_record(struct offset = object->disp_recordoffset + bp->object_info_tbl_offset; - for (;;) { + for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { header = GET_IMAGE(struct atom_common_record_header, offset); if (!header) @@ -3263,6 +3270,7 @@ static enum bp_result update_slot_layout_info( { unsigned int record_offset; unsigned int j; + unsigned int n; struct atom_display_object_path_v2 *object; struct atom_bracket_layout_record *record; struct atom_common_record_header *record_header; @@ -3284,7 +3292,7 @@ static enum bp_result update_slot_layout_info( (object->disp_recordoffset) + (unsigned int)(bp->object_info_tbl_offset); - for (;;) { + for (n = 0; n < BIOS_MAX_NUM_RECORD; n++) { record_header = (struct atom_common_record_header *) GET_IMAGE(struct atom_common_record_header, @@ -3378,6 +3386,7 @@ static enum bp_result update_slot_layout_info_v2( struct slot_layout_info *slot_layout_info) { unsigned int record_offset; + unsigned int n; struct atom_display_object_path_v3 *object; struct atom_bracket_layout_record_v2 *record; struct atom_common_record_header *record_header; @@ -3400,7 +3409,7 @@ static enum bp_result update_slot_layout_info_v2( (object->disp_recordoffset) + (unsigned int)(bp->object_info_tbl_offset); - for (;;) { + for (n = 0; n < BIOS_MAX_NUM_RECORD; n++) { record_header = (struct atom_common_record_header *) GET_IMAGE(struct atom_common_record_header, diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser_helper.h b/drivers/gpu/drm/amd/display/dc/bios/bios_parser_helper.h index ab162f2fe577..19fd7aea18f1 100644 --- a/drivers/gpu/drm/amd/display/dc/bios/bios_parser_helper.h +++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser_helper.h @@ -37,4 +37,9 @@ void bios_set_scratch_critical_state(struct dc_bios *bios, bool state); #define GET_IMAGE(type, offset) ((type *) bios_get_image(&bp->base, offset, sizeof(type))) +/* Upper bound on the number of records in a VBIOS record chain. Prevents + * unbounded looping if the VBIOS image is malformed and lacks a terminator. + */ +#define BIOS_MAX_NUM_RECORD 256 + #endif From 181eda5549c5d9fad3fdb88b050fbf0844d884f8 Mon Sep 17 00:00:00 2001 From: Alysa Liu Date: Wed, 27 May 2026 11:31:35 -0400 Subject: [PATCH 110/257] drm/amdkfd: fix UAF race in destroy_queue_cpsch wait_on_destroy_queue() drops locks to wait for queue resume, allowing a concurrent destroy to free the queue. Use is_being_destroyed flag to serialize destruction. Reviewed-by: Amir Shetaia Signed-off-by: Alysa Liu Signed-off-by: Alex Deucher (cherry picked from commit ac081deaf16a639ea7dff2f285fe421a33c1ade0) --- drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c index 0d7296c739ed..0a408f95baac 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c @@ -2502,6 +2502,9 @@ static int wait_on_destroy_queue(struct device_queue_manager *dqm, if (pdd->qpd.is_debug) return ret; + if (q->properties.is_being_destroyed) + return -EBUSY; + q->properties.is_being_destroyed = true; if (pdd->process->debug_trap_enabled && q->properties.is_suspended) { @@ -2514,6 +2517,9 @@ static int wait_on_destroy_queue(struct device_queue_manager *dqm, dqm_lock(dqm); } + if (ret) + q->properties.is_being_destroyed = false; + return ret; } @@ -2607,7 +2613,7 @@ static int destroy_queue_cpsch(struct device_queue_manager *dqm, return retval; failed_try_destroy_debugged_queue: - + q->properties.is_being_destroyed = false; dqm_unlock(dqm); return retval; } From 1a4a55f181a9584386701f9b4183d9ecc5271b21 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Wed, 27 May 2026 15:41:59 -0400 Subject: [PATCH 111/257] drm/amdgpu/sdma7.1: fix support for disable_kq Set the flag in the ring structure. Fixes: 80d4d3a45b86 ("drm/amdgpu/sdma7.1: add support for disable_kq") Reviewed-by: Kent Russell Signed-off-by: Alex Deucher (cherry picked from commit e0a3aa8a6750e8cf067fe2146dc618ffd296d5ef) --- drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c b/drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c index 061934a2e93a..9c9bbe043a47 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c @@ -1316,6 +1316,7 @@ static int sdma_v7_1_sw_init(struct amdgpu_ip_block *ip_block) ring->ring_obj = NULL; ring->use_doorbell = true; ring->me = i; + ring->no_user_submission = adev->sdma.no_user_submission; for (xcc_id = 0; xcc_id < fls(adev->gfx.xcc_mask); xcc_id++) { if (adev->sdma.instance[i].xcc_id == GET_INST(GC, xcc_id)) From e0153b94a1d104cf4545878c83302672573de65e Mon Sep 17 00:00:00 2001 From: Prike Liang Date: Thu, 14 May 2026 17:21:09 +0800 Subject: [PATCH 112/257] drm/amdgpu: unmap userq for evicting user queue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the driver only preempts queues, there can still be inflight waves, pending dispatch state, or resume/redispatch possibility tied to the same queue. Then the VM/TTM side may proceed to move/unmap queue related BOs during evicting userq objects while shader TCP clients still need to access them. So for eviction, unmap is safer because it makes the queue nonrunnable before memory backing is invalidated. Meanwhile, for a idle queue it's more sutiable for unmapping it rather preempt and unmapping also can save more processing time than preempt. Signed-off-by: Prike Liang Reviewed-by: Christian König Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit d87c9d86727a0bcc95c3009a213a1b27a11b691e) --- drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c index cf192500800f..e937099de3e7 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c @@ -887,7 +887,7 @@ amdgpu_userq_restore_all(struct amdgpu_userq_mgr *uq_mgr) continue; } - r = amdgpu_userq_restore_helper(queue); + r = amdgpu_userq_map_helper(queue); if (r) ret = r; @@ -1124,7 +1124,7 @@ amdgpu_userq_evict_all(struct amdgpu_userq_mgr *uq_mgr) /* Try to unmap all the queues in this process ctx */ xa_for_each(&uq_mgr->userq_xa, queue_id, queue) { - r = amdgpu_userq_preempt_helper(queue); + r = amdgpu_userq_unmap_helper(queue); if (r) ret = r; } From a40412285ec17a69ed728675a56b7ad479c86e36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timur=20Krist=C3=B3f?= Date: Mon, 25 May 2026 13:22:04 +0200 Subject: [PATCH 113/257] drm/amdgpu: Align amdgpu_gtt_mgr entries to TLB size on all SI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It seems that Pitcairn has the same issues as Tahiti with regards to the TLB size. This commit fixes a VCE1 FW validation timeout on suspend/resume on Pitcairn. Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/5336 Signed-off-by: Timur Kristóf Signed-off-by: Alex Deucher (cherry picked from commit 629279e2e798cd161cf74f40aaebfeb16d45eb01) --- drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c index a5d26b943f6d..d23a91d029aa 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c @@ -203,7 +203,7 @@ int amdgpu_gtt_mgr_alloc_entries(struct amdgpu_gtt_mgr *mgr, int r; /* Align to TLB L2 cache entry size to work around "V bit HW bug" */ - if (adev->asic_type == CHIP_TAHITI) { + if (adev->family == AMDGPU_FAMILY_SI) { alignment = 32 * 1024 / AMDGPU_GPU_PAGE_SIZE; num_pages = ALIGN(num_pages, alignment); } From 2493d87bb4c31ec9ca7f0ef7257e33b8b175f913 Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Thu, 21 May 2026 22:36:37 +0800 Subject: [PATCH 114/257] drm/amd/pm: apply SMU 13.0.10 workaround during MP1 unload On SMU v13.0.10, sending PrepareMp1ForUnload with the default parameter may leave the device in an inaccessible state. This can affect runtime power management and partial PnP flows. e.g: kexec, driver unload, boco/d3cold. Pass the required workaround parameter 0x55, when preparing MP1 for unload on SMU v13.0.10, keep the existing behavior for other SMU versions. Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/5133 Signed-off-by: Yang Wang Reviewed-by: Kenneth Feng Signed-off-by: Alex Deucher (cherry picked from commit 4e8ee1afeedb8d24dd22cdd5ae9f98a6d76ebe4b) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c index 0a7f5fa3c1d3..fa861ec4d700 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c @@ -2801,11 +2801,19 @@ static void smu_v13_0_0_i2c_control_fini(struct smu_context *smu) static int smu_v13_0_0_set_mp1_state(struct smu_context *smu, enum pp_mp1_state mp1_state) { + uint32_t param; int ret; switch (mp1_state) { case PP_MP1_STATE_UNLOAD: - ret = smu_cmn_set_mp1_state(smu, mp1_state); + /* + * NOTE: Param 0x55 comes from PMFW 80.31.0, ignored in older versions. + * No PMFW version check required. + */ + param = amdgpu_ip_version(smu->adev, MP1_HWIP, 0) == IP_VERSION(13, 0, 10) ? + 0x55 : 0x00; + ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_PrepareMp1ForUnload, + param, NULL); break; default: /* Ignore others */ From bb204f19e4a115f094a6a3c4d82fcf48862d0766 Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Tue, 19 May 2026 11:18:12 +0800 Subject: [PATCH 115/257] drm/amd/pm: fix smu13 power limit default/cap calculation smu_v13_0_0_get_power_limit() and smu_v13_0_7_get_power_limit() mix runtime power_limit with PP table limits when reporting default/min/max. When current power limit query succeeds, default_power_limit was set to the runtime value instead of the PP table default, and min/max could be derived from inconsistent bases (MsgLimits/runtime), leading to incorrect cap info. Use SocketPowerLimitAc/Dc as the PP default base (pp_limit), keep current_power_limit as runtime value, and derive min/max from pp_limit with OD percentages. Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/5227 Signed-off-by: Yang Wang Reviewed-by: Kenneth Feng Reviewed-by: Lijo Lazar Signed-off-by: Alex Deucher (cherry picked from commit 1eaf26db95901ca70737503a89b831dd763c8453) Cc: stable@vger.kernel.org --- .../drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c | 32 ++++++++++--------- .../drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c | 32 +++++++++++-------- 2 files changed, 35 insertions(+), 29 deletions(-) diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c index fa861ec4d700..7f8d4bb47d02 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c @@ -2390,28 +2390,30 @@ static int smu_v13_0_0_enable_mgpu_fan_boost(struct smu_context *smu) } static int smu_v13_0_0_get_power_limit(struct smu_context *smu, - uint32_t *current_power_limit, - uint32_t *default_power_limit, - uint32_t *max_power_limit, - uint32_t *min_power_limit) + uint32_t *current_power_limit, + uint32_t *default_power_limit, + uint32_t *max_power_limit, + uint32_t *min_power_limit) { struct smu_table_context *table_context = &smu->smu_table; struct smu_13_0_0_powerplay_table *powerplay_table = (struct smu_13_0_0_powerplay_table *)table_context->power_play_table; PPTable_t *pptable = table_context->driver_pptable; SkuTable_t *skutable = &pptable->SkuTable; - uint32_t power_limit, od_percent_upper = 0, od_percent_lower = 0; - uint32_t msg_limit = skutable->MsgLimits.Power[PPT_THROTTLER_PPT0][POWER_SOURCE_AC]; - - if (smu_v13_0_get_current_power_limit(smu, &power_limit)) - power_limit = smu->adev->pm.ac_power ? + uint32_t pp_limit = smu->adev->pm.ac_power ? skutable->SocketPowerLimitAc[PPT_THROTTLER_PPT0] : skutable->SocketPowerLimitDc[PPT_THROTTLER_PPT0]; + uint32_t power_limit = 0, od_percent_upper = 0, od_percent_lower = 0; + int ret; + + if (current_power_limit) { + ret = smu_v13_0_get_current_power_limit(smu, &power_limit); + if (ret) + *current_power_limit = pp_limit; + } - if (current_power_limit) - *current_power_limit = power_limit; if (default_power_limit) - *default_power_limit = power_limit; + *default_power_limit = pp_limit; if (powerplay_table) { if (smu->od_enabled && @@ -2425,15 +2427,15 @@ static int smu_v13_0_0_get_power_limit(struct smu_context *smu, } dev_dbg(smu->adev->dev, "od percent upper:%d, od percent lower:%d (default power: %d)\n", - od_percent_upper, od_percent_lower, power_limit); + od_percent_upper, od_percent_lower, pp_limit); if (max_power_limit) { - *max_power_limit = msg_limit * (100 + od_percent_upper); + *max_power_limit = pp_limit * (100 + od_percent_upper); *max_power_limit /= 100; } if (min_power_limit) { - *min_power_limit = power_limit * (100 - od_percent_lower); + *min_power_limit = pp_limit * (100 - od_percent_lower); *min_power_limit /= 100; } diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c index 5abf2b0703c6..0f774b0920ce 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c @@ -2372,28 +2372,32 @@ static int smu_v13_0_7_enable_mgpu_fan_boost(struct smu_context *smu) } static int smu_v13_0_7_get_power_limit(struct smu_context *smu, - uint32_t *current_power_limit, - uint32_t *default_power_limit, - uint32_t *max_power_limit, - uint32_t *min_power_limit) + uint32_t *current_power_limit, + uint32_t *default_power_limit, + uint32_t *max_power_limit, + uint32_t *min_power_limit) { struct smu_table_context *table_context = &smu->smu_table; struct smu_13_0_7_powerplay_table *powerplay_table = (struct smu_13_0_7_powerplay_table *)table_context->power_play_table; PPTable_t *pptable = table_context->driver_pptable; SkuTable_t *skutable = &pptable->SkuTable; - uint32_t power_limit, od_percent_upper = 0, od_percent_lower = 0; - uint32_t msg_limit = skutable->MsgLimits.Power[PPT_THROTTLER_PPT0][POWER_SOURCE_AC]; - - if (smu_v13_0_get_current_power_limit(smu, &power_limit)) - power_limit = smu->adev->pm.ac_power ? + uint32_t pp_limit = smu->adev->pm.ac_power ? skutable->SocketPowerLimitAc[PPT_THROTTLER_PPT0] : skutable->SocketPowerLimitDc[PPT_THROTTLER_PPT0]; + uint32_t power_limit = 0, od_percent_upper = 0, od_percent_lower = 0; + int ret; + + if (current_power_limit) { + ret = smu_v13_0_get_current_power_limit(smu, &power_limit); + if (ret) + power_limit = pp_limit; - if (current_power_limit) *current_power_limit = power_limit; + } + if (default_power_limit) - *default_power_limit = power_limit; + *default_power_limit = pp_limit; if (powerplay_table) { if (smu->od_enabled && @@ -2407,15 +2411,15 @@ static int smu_v13_0_7_get_power_limit(struct smu_context *smu, } dev_dbg(smu->adev->dev, "od percent upper:%d, od percent lower:%d (default power: %d)\n", - od_percent_upper, od_percent_lower, power_limit); + od_percent_upper, od_percent_lower, pp_limit); if (max_power_limit) { - *max_power_limit = msg_limit * (100 + od_percent_upper); + *max_power_limit = pp_limit * (100 + od_percent_upper); *max_power_limit /= 100; } if (min_power_limit) { - *min_power_limit = power_limit * (100 - od_percent_lower); + *min_power_limit = pp_limit * (100 - od_percent_lower); *min_power_limit /= 100; } From ee193c5bbd5e2b56bbeb54ef554414b43a6fc896 Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Fri, 29 May 2026 11:47:31 +0800 Subject: [PATCH 116/257] drm/amd/pm: mark metrics.energy_accumulator is invalid for smu 14.0.2 EnergyAccumulator is unsupported on SMU 14.0.2, mark it invalid. Signed-off-by: Yang Wang Reviewed-by: Asad Kamal Signed-off-by: Alex Deucher (cherry picked from commit 646b05043eeed04b51c14aad22a400a8250af4b7) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c index 5ce4e982ca33..fdc1456b885c 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c @@ -2152,7 +2152,6 @@ static ssize_t smu_v14_0_2_get_gpu_metrics(struct smu_context *smu, metrics->Vcn1ActivityPercentage); gpu_metrics->average_socket_power = metrics->AverageSocketPower; - gpu_metrics->energy_accumulator = metrics->EnergyAccumulator; if (metrics->AverageGfxActivity <= SMU_14_0_2_BUSY_THRESHOLD) gpu_metrics->average_gfxclk_frequency = metrics->AverageGfxclkFrequencyPostDs; From a169b326bab4a3617a9b1d34beddaaa6f798aa20 Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Mon, 11 May 2026 16:33:37 +0800 Subject: [PATCH 117/257] drm/amd/pm: zero unused SMU argument registers SMU messages may use fewer arguments than the available argument registers, the previous code only wrote used registers and left the rest unchanged, so stale values from a prior message could persist. Write all argument registers for each message and zero the unused tail to keep command arguments deterministic and avoid unintended carry-over. Signed-off-by: Yang Wang Reviewed-by: Asad Kamal Signed-off-by: Alex Deucher (cherry picked from commit e03b635f61f77ebd5107ef82f48e3221cb695856) --- drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c b/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c index 90c7127beabf..fe97fda8bfe9 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c @@ -272,11 +272,15 @@ static void __smu_msg_v1_send(struct smu_msg_ctl *ctl, u16 index, { struct amdgpu_device *adev = ctl->smu->adev; struct smu_msg_config *cfg = &ctl->config; + u32 arg; int i; WREG32(cfg->resp_reg, 0); - for (i = 0; i < args->num_args; i++) - WREG32(cfg->arg_regs[i], args->args[i]); + for (i = 0; i < cfg->num_arg_regs; i++) { + /* NOTE: Clear unused argument registers to avoid stale values. */ + arg = i < args->num_args ? args->args[i] : 0; + WREG32(cfg->arg_regs[i], arg); + } WREG32(cfg->msg_reg, index); } From ae4e30f24d67075dc975002effa68d424c7ff7e3 Mon Sep 17 00:00:00 2001 From: Harish Kasiviswanathan Date: Tue, 28 Apr 2026 17:45:06 -0400 Subject: [PATCH 118/257] drm/amdgpu: Use asic specific pte_addr_mask For PTE creation use asic specific physical page base address mask v2: Change variable name from pa_mask to pte_addr_mask Signed-off-by: Harish Kasiviswanathan Reviewed-by: Lijo Lazar Signed-off-by: Alex Deucher (cherry picked from commit 2ea989885941a6e5607ef86dbe309e90b7191f21) --- drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c | 2 +- drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.h | 1 + drivers/gpu/drm/amd/amdgpu/gmc_v10_0.c | 1 + drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c | 1 + drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c | 4 ++++ drivers/gpu/drm/amd/amdgpu/gmc_v6_0.c | 1 + drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c | 1 + drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c | 1 + drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c | 1 + 9 files changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c index 3d9497d121ca..13a5acdf8da3 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c @@ -170,7 +170,7 @@ int amdgpu_gmc_set_pte_pde(struct amdgpu_device *adev, void *cpu_pt_addr, /* * The following is for PTE only. GART does not have PDEs. */ - value = addr & 0x0000FFFFFFFFF000ULL; + value = addr & adev->gmc.pte_addr_mask; value |= flags; writeq(value, ptr + (gpu_page_idx * 8)); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.h index 6ab4c1e297fc..d03536b969b5 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.h @@ -280,6 +280,7 @@ struct amdgpu_gmc { u64 real_vram_size; int vram_mtrr; u64 mc_mask; + uint64_t pte_addr_mask; const struct firmware *fw; /* MC firmware */ uint32_t fw_version; struct amdgpu_irq_src vm_fault; diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v10_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v10_0.c index e1ace7d44ffd..f5bdfea54afa 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v10_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v10_0.c @@ -847,6 +847,7 @@ static int gmc_v10_0_sw_init(struct amdgpu_ip_block *ip_block) * internal address space. */ adev->gmc.mc_mask = 0xffffffffffffULL; /* 48 bit MC */ + adev->gmc.pte_addr_mask = 0x0000FFFFFFFFF000ULL; /* 48 bit PA */ r = dma_set_mask_and_coherent(adev->dev, DMA_BIT_MASK(44)); if (r) { diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c index 94d6631ce0bc..807bd180b9d4 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c @@ -821,6 +821,7 @@ static int gmc_v11_0_sw_init(struct amdgpu_ip_block *ip_block) * internal address space. */ adev->gmc.mc_mask = 0xffffffffffffULL; /* 48 bit MC */ + adev->gmc.pte_addr_mask = 0x0000FFFFFFFFF000ULL; /* 48 bit PA */ r = dma_set_mask_and_coherent(adev->dev, DMA_BIT_MASK(44)); if (r) { diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c index e10ac9788d13..52c161c2df0a 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c @@ -814,6 +814,7 @@ static int gmc_v12_0_sw_init(struct amdgpu_ip_block *ip_block) { int r, vram_width = 0, vram_type = 0, vram_vendor = 0; struct amdgpu_device *adev = ip_block->adev; + uint64_t pte_addr_mask = 0; int i; adev->mmhub.funcs->init(adev); @@ -843,6 +844,7 @@ static int gmc_v12_0_sw_init(struct amdgpu_ip_block *ip_block) * block size 512 (9bit) */ amdgpu_vm_adjust_size(adev, 256 * 1024, 9, 3, 48); + pte_addr_mask = 0x0000FFFFFFFFF000ULL; /* 48 bit PA */ break; case IP_VERSION(12, 1, 0): bitmap_set(adev->vmhubs_mask, AMDGPU_GFXHUB(0), @@ -855,6 +857,7 @@ static int gmc_v12_0_sw_init(struct amdgpu_ip_block *ip_block) * block size 512 (9bit) */ amdgpu_vm_adjust_size(adev, 128 * 1024 * 1024, 9, 4, 57); + pte_addr_mask = 0x000FFFFFFFFFF000ULL; /* 52 bit PA */ break; default: break; @@ -911,6 +914,7 @@ static int gmc_v12_0_sw_init(struct amdgpu_ip_block *ip_block) * internal address space. */ adev->gmc.mc_mask = AMDGPU_GMC_HOLE_MASK; + adev->gmc.pte_addr_mask = pte_addr_mask; r = dma_set_mask_and_coherent(adev->dev, DMA_BIT_MASK(44)); if (r) { diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v6_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v6_0.c index cc272a96fcef..6aa581b1c148 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v6_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v6_0.c @@ -836,6 +836,7 @@ static int gmc_v6_0_sw_init(struct amdgpu_ip_block *ip_block) amdgpu_vm_adjust_size(adev, 64, 9, 1, 40); adev->gmc.mc_mask = 0xffffffffffULL; + adev->gmc.pte_addr_mask = 0x000000FFFFFFF000ULL; r = dma_set_mask_and_coherent(adev->dev, DMA_BIT_MASK(40)); if (r) { diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c index bb16ba2ef6fd..2b0362c4d9eb 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c @@ -1016,6 +1016,7 @@ static int gmc_v7_0_sw_init(struct amdgpu_ip_block *ip_block) * internal address space. */ adev->gmc.mc_mask = 0xffffffffffULL; /* 40 bit MC */ + adev->gmc.pte_addr_mask = 0x000000FFFFFFF000ULL; /* 40 bit PA */ r = dma_set_mask_and_coherent(adev->dev, DMA_BIT_MASK(40)); if (r) { diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c index a59174f6bcc1..fbccfcb3d7cf 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c @@ -1131,6 +1131,7 @@ static int gmc_v8_0_sw_init(struct amdgpu_ip_block *ip_block) * internal address space. */ adev->gmc.mc_mask = 0xffffffffffULL; /* 40 bit MC */ + adev->gmc.pte_addr_mask = 0x000000FFFFFFF000ULL; /* 40 bit PA */ r = dma_set_mask_and_coherent(adev->dev, DMA_BIT_MASK(40)); if (r) { diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c index e7b78027002b..c6dbe25f2bd9 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c @@ -1994,6 +1994,7 @@ static int gmc_v9_0_sw_init(struct amdgpu_ip_block *ip_block) * internal address space. */ adev->gmc.mc_mask = 0xffffffffffffULL; /* 48 bit MC */ + adev->gmc.pte_addr_mask = 0x0000FFFFFFFFF000ULL; /* 48 bit PA */ dma_addr_bits = amdgpu_ip_version(adev, GC_HWIP, 0) >= IP_VERSION(9, 4, 2) ? From e3fa02872e223d24a925eb876b31c00d833bce7c Mon Sep 17 00:00:00 2001 From: Harish Kasiviswanathan Date: Tue, 12 May 2026 10:57:49 -0400 Subject: [PATCH 119/257] drm/amdgpu: drm/amdgpu: Set correct DMA mask for gfx12.1 Set correct DMA mask for gfx12 Signed-off-by: Harish Kasiviswanathan Reviewed-by: Lijo Lazar Signed-off-by: Alex Deucher (cherry picked from commit a2ef14ee2593b48242b8d90f229f71c1710529da) --- drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c index 52c161c2df0a..8dc9c053897b 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c @@ -812,7 +812,7 @@ static int gmc_v12_0_gart_init(struct amdgpu_device *adev) static int gmc_v12_0_sw_init(struct amdgpu_ip_block *ip_block) { - int r, vram_width = 0, vram_type = 0, vram_vendor = 0; + int r, vram_width = 0, vram_type = 0, vram_vendor = 0, dma_addr_bits; struct amdgpu_device *adev = ip_block->adev; uint64_t pte_addr_mask = 0; int i; @@ -845,6 +845,7 @@ static int gmc_v12_0_sw_init(struct amdgpu_ip_block *ip_block) */ amdgpu_vm_adjust_size(adev, 256 * 1024, 9, 3, 48); pte_addr_mask = 0x0000FFFFFFFFF000ULL; /* 48 bit PA */ + dma_addr_bits = 44; break; case IP_VERSION(12, 1, 0): bitmap_set(adev->vmhubs_mask, AMDGPU_GFXHUB(0), @@ -858,9 +859,12 @@ static int gmc_v12_0_sw_init(struct amdgpu_ip_block *ip_block) */ amdgpu_vm_adjust_size(adev, 128 * 1024 * 1024, 9, 4, 57); pte_addr_mask = 0x000FFFFFFFFFF000ULL; /* 52 bit PA */ + dma_addr_bits = 52; break; default: - break; + dev_warn(adev->dev, "Unrecognized GC IP version: 0x%08x\n", + amdgpu_ip_version(adev, GC_HWIP, 0)); + return -EINVAL; } /* This interrupt is VMC page fault.*/ @@ -916,13 +920,13 @@ static int gmc_v12_0_sw_init(struct amdgpu_ip_block *ip_block) adev->gmc.mc_mask = AMDGPU_GMC_HOLE_MASK; adev->gmc.pte_addr_mask = pte_addr_mask; - r = dma_set_mask_and_coherent(adev->dev, DMA_BIT_MASK(44)); + r = dma_set_mask_and_coherent(adev->dev, DMA_BIT_MASK(dma_addr_bits)); if (r) { drm_warn(adev_to_drm(adev), "No suitable DMA available.\n"); return r; } - adev->need_swiotlb = drm_need_swiotlb(44); + adev->need_swiotlb = drm_need_swiotlb(dma_addr_bits); r = gmc_v12_0_mc_init(adev); if (r) From 58bafc666c484b21839a2d27e923ae1b2727a1df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20K=C3=B6nig?= Date: Wed, 18 Feb 2026 13:05:46 +0100 Subject: [PATCH 120/257] drm/amdgpu: fix waiting for all submissions for userptrs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wait for all submissions when userptrs need to be invalidated by the MMU notifier, not just the one the userptr was involved into. Signed-off-by: Christian König Reviewed-by: Vitaly Prosyak Tested-by: Vitaly Prosyak Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit 91250893cbaa25c86872deca95a540d08de1f91e) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c index 5bfa5a84b09c..e452444b33b0 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c @@ -67,6 +67,7 @@ static bool amdgpu_hmm_invalidate_gfx(struct mmu_interval_notifier *mni, { struct amdgpu_bo *bo = container_of(mni, struct amdgpu_bo, notifier); struct amdgpu_device *adev = amdgpu_ttm_adev(bo->tbo.bdev); + struct amdgpu_bo *vm_root = bo->vm_bo->vm->root.bo; long r; if (!mmu_notifier_range_blockable(range)) @@ -77,8 +78,9 @@ static bool amdgpu_hmm_invalidate_gfx(struct mmu_interval_notifier *mni, mmu_interval_set_seq(mni, cur_seq); amdgpu_vm_bo_invalidate(bo, false); - r = dma_resv_wait_timeout(bo->tbo.base.resv, DMA_RESV_USAGE_BOOKKEEP, - false, MAX_SCHEDULE_TIMEOUT); + r = dma_resv_wait_timeout(vm_root->tbo.base.resv, + DMA_RESV_USAGE_BOOKKEEP, false, + MAX_SCHEDULE_TIMEOUT); mutex_unlock(&adev->notifier_lock); if (r <= 0) DRM_ERROR("(%ld) failed to wait for user bo\n", r); From 40bab7c606702e8ea3e95c6f30c99cc8295826af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timur=20Krist=C3=B3f?= Date: Mon, 25 May 2026 13:45:02 +0200 Subject: [PATCH 121/257] drm/amdgpu/gfxhub: Program CRASH_ON_*_FAULT bits to 0 as needed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the fault stop mode isn't AMDGPU_VM_FAULT_STOP_ALWAYS, these bits should be programmed to 0. Program CRASH_ON_NO_RETRY_FAULT and CRASH_ON_RETRY_FAULT always, to make sure to clear the bits when we don't want to crash. Signed-off-by: Timur Kristóf Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit d0cd99e73090700b7a942b98a3327ec966597d0a) --- drivers/gpu/drm/amd/amdgpu/gfxhub_v11_5_0.c | 10 ++++------ drivers/gpu/drm/amd/amdgpu/gfxhub_v12_0.c | 10 ++++------ drivers/gpu/drm/amd/amdgpu/gfxhub_v12_1.c | 14 ++++++-------- drivers/gpu/drm/amd/amdgpu/gfxhub_v1_0.c | 10 ++++------ drivers/gpu/drm/amd/amdgpu/gfxhub_v1_2.c | 10 ++++------ drivers/gpu/drm/amd/amdgpu/gfxhub_v2_0.c | 10 ++++------ drivers/gpu/drm/amd/amdgpu/gfxhub_v2_1.c | 10 ++++------ drivers/gpu/drm/amd/amdgpu/gfxhub_v3_0.c | 10 ++++------ drivers/gpu/drm/amd/amdgpu/gfxhub_v3_0_3.c | 10 ++++------ 9 files changed, 38 insertions(+), 56 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/gfxhub_v11_5_0.c b/drivers/gpu/drm/amd/amdgpu/gfxhub_v11_5_0.c index f9949fedfbb9..f2fe6f5bc7f7 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfxhub_v11_5_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfxhub_v11_5_0.c @@ -449,12 +449,10 @@ static void gfxhub_v11_5_0_set_fault_enable_default(struct amdgpu_device *adev, WRITE_PROTECTION_FAULT_ENABLE_DEFAULT, value); tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, EXECUTE_PROTECTION_FAULT_ENABLE_DEFAULT, value); - if (!value) { - tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, - CRASH_ON_NO_RETRY_FAULT, 1); - tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, - CRASH_ON_RETRY_FAULT, 1); - } + tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, + CRASH_ON_NO_RETRY_FAULT, !value); + tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, + CRASH_ON_RETRY_FAULT, !value); WREG32_SOC15(GC, 0, regGCVM_L2_PROTECTION_FAULT_CNTL, tmp); } diff --git a/drivers/gpu/drm/amd/amdgpu/gfxhub_v12_0.c b/drivers/gpu/drm/amd/amdgpu/gfxhub_v12_0.c index 7609b9cecae8..efcaca70c27a 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfxhub_v12_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfxhub_v12_0.c @@ -454,12 +454,10 @@ static void gfxhub_v12_0_set_fault_enable_default(struct amdgpu_device *adev, WRITE_PROTECTION_FAULT_ENABLE_DEFAULT, value); tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, EXECUTE_PROTECTION_FAULT_ENABLE_DEFAULT, value); - if (!value) { - tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, - CRASH_ON_NO_RETRY_FAULT, 1); - tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, - CRASH_ON_RETRY_FAULT, 1); - } + tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, + CRASH_ON_NO_RETRY_FAULT, !value); + tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, + CRASH_ON_RETRY_FAULT, !value); WREG32_SOC15(GC, 0, regGCVM_L2_PROTECTION_FAULT_CNTL, tmp); } diff --git a/drivers/gpu/drm/amd/amdgpu/gfxhub_v12_1.c b/drivers/gpu/drm/amd/amdgpu/gfxhub_v12_1.c index 3544eb42dca6..4c2fd1e6616e 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfxhub_v12_1.c +++ b/drivers/gpu/drm/amd/amdgpu/gfxhub_v12_1.c @@ -633,19 +633,17 @@ static void gfxhub_v12_1_xcc_set_fault_enable_default(struct amdgpu_device *adev tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL_LO32, OTHER_CLIENT_ID_NO_RETRY_FAULT_INTERRUPT, value); - if (!value) - tmp = REG_SET_FIELD(tmp, - GCVM_L2_PROTECTION_FAULT_CNTL_LO32, - CRASH_ON_NO_RETRY_FAULT, 1); + tmp = REG_SET_FIELD(tmp, + GCVM_L2_PROTECTION_FAULT_CNTL_LO32, + CRASH_ON_NO_RETRY_FAULT, !value); WREG32_SOC15(GC, GET_INST(GC, i), regGCVM_L2_PROTECTION_FAULT_CNTL_LO32, tmp); tmp = RREG32_SOC15(GC, GET_INST(GC, i), regGCVM_L2_PROTECTION_FAULT_CNTL_HI32); - if (!value) - tmp = REG_SET_FIELD(tmp, - GCVM_L2_PROTECTION_FAULT_CNTL_HI32, - CRASH_ON_RETRY_FAULT, 1); + tmp = REG_SET_FIELD(tmp, + GCVM_L2_PROTECTION_FAULT_CNTL_HI32, + CRASH_ON_RETRY_FAULT, !value); WREG32_SOC15(GC, GET_INST(GC, i), regGCVM_L2_PROTECTION_FAULT_CNTL_HI32, tmp); } diff --git a/drivers/gpu/drm/amd/amdgpu/gfxhub_v1_0.c b/drivers/gpu/drm/amd/amdgpu/gfxhub_v1_0.c index a7bfc9f41d0e..bfe247b1a333 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfxhub_v1_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfxhub_v1_0.c @@ -403,12 +403,10 @@ static void gfxhub_v1_0_set_fault_enable_default(struct amdgpu_device *adev, WRITE_PROTECTION_FAULT_ENABLE_DEFAULT, value); tmp = REG_SET_FIELD(tmp, VM_L2_PROTECTION_FAULT_CNTL, EXECUTE_PROTECTION_FAULT_ENABLE_DEFAULT, value); - if (!value) { - tmp = REG_SET_FIELD(tmp, VM_L2_PROTECTION_FAULT_CNTL, - CRASH_ON_NO_RETRY_FAULT, 1); - tmp = REG_SET_FIELD(tmp, VM_L2_PROTECTION_FAULT_CNTL, - CRASH_ON_RETRY_FAULT, 1); - } + tmp = REG_SET_FIELD(tmp, VM_L2_PROTECTION_FAULT_CNTL, + CRASH_ON_NO_RETRY_FAULT, !value); + tmp = REG_SET_FIELD(tmp, VM_L2_PROTECTION_FAULT_CNTL, + CRASH_ON_RETRY_FAULT, !value); WREG32_SOC15(GC, 0, mmVM_L2_PROTECTION_FAULT_CNTL, tmp); } diff --git a/drivers/gpu/drm/amd/amdgpu/gfxhub_v1_2.c b/drivers/gpu/drm/amd/amdgpu/gfxhub_v1_2.c index 6c03bf9f1ae8..fbdf46070b38 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfxhub_v1_2.c +++ b/drivers/gpu/drm/amd/amdgpu/gfxhub_v1_2.c @@ -516,12 +516,10 @@ static void gfxhub_v1_2_xcc_set_fault_enable_default(struct amdgpu_device *adev, WRITE_PROTECTION_FAULT_ENABLE_DEFAULT, value); tmp = REG_SET_FIELD(tmp, VM_L2_PROTECTION_FAULT_CNTL, EXECUTE_PROTECTION_FAULT_ENABLE_DEFAULT, value); - if (!value) { - tmp = REG_SET_FIELD(tmp, VM_L2_PROTECTION_FAULT_CNTL, - CRASH_ON_NO_RETRY_FAULT, 1); - tmp = REG_SET_FIELD(tmp, VM_L2_PROTECTION_FAULT_CNTL, - CRASH_ON_RETRY_FAULT, 1); - } + tmp = REG_SET_FIELD(tmp, VM_L2_PROTECTION_FAULT_CNTL, + CRASH_ON_NO_RETRY_FAULT, !value); + tmp = REG_SET_FIELD(tmp, VM_L2_PROTECTION_FAULT_CNTL, + CRASH_ON_RETRY_FAULT, !value); WREG32_SOC15(GC, GET_INST(GC, i), regVM_L2_PROTECTION_FAULT_CNTL, tmp); } } diff --git a/drivers/gpu/drm/amd/amdgpu/gfxhub_v2_0.c b/drivers/gpu/drm/amd/amdgpu/gfxhub_v2_0.c index 793faf62cb07..9ea593e2c719 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfxhub_v2_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfxhub_v2_0.c @@ -418,12 +418,10 @@ static void gfxhub_v2_0_set_fault_enable_default(struct amdgpu_device *adev, WRITE_PROTECTION_FAULT_ENABLE_DEFAULT, value); tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, EXECUTE_PROTECTION_FAULT_ENABLE_DEFAULT, value); - if (!value) { - tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, - CRASH_ON_NO_RETRY_FAULT, 1); - tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, - CRASH_ON_RETRY_FAULT, 1); - } + tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, + CRASH_ON_NO_RETRY_FAULT, !value); + tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, + CRASH_ON_RETRY_FAULT, !value); WREG32_SOC15(GC, 0, mmGCVM_L2_PROTECTION_FAULT_CNTL, tmp); } diff --git a/drivers/gpu/drm/amd/amdgpu/gfxhub_v2_1.c b/drivers/gpu/drm/amd/amdgpu/gfxhub_v2_1.c index aceb8447feac..30b90d35abd0 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfxhub_v2_1.c +++ b/drivers/gpu/drm/amd/amdgpu/gfxhub_v2_1.c @@ -449,12 +449,10 @@ static void gfxhub_v2_1_set_fault_enable_default(struct amdgpu_device *adev, WRITE_PROTECTION_FAULT_ENABLE_DEFAULT, value); tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, EXECUTE_PROTECTION_FAULT_ENABLE_DEFAULT, value); - if (!value) { - tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, - CRASH_ON_NO_RETRY_FAULT, 1); - tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, - CRASH_ON_RETRY_FAULT, 1); - } + tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, + CRASH_ON_NO_RETRY_FAULT, !value); + tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, + CRASH_ON_RETRY_FAULT, !value); WREG32_SOC15(GC, 0, mmGCVM_L2_PROTECTION_FAULT_CNTL, tmp); } diff --git a/drivers/gpu/drm/amd/amdgpu/gfxhub_v3_0.c b/drivers/gpu/drm/amd/amdgpu/gfxhub_v3_0.c index abe30c8bd2ba..f089f70571aa 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfxhub_v3_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfxhub_v3_0.c @@ -446,12 +446,10 @@ static void gfxhub_v3_0_set_fault_enable_default(struct amdgpu_device *adev, WRITE_PROTECTION_FAULT_ENABLE_DEFAULT, value); tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, EXECUTE_PROTECTION_FAULT_ENABLE_DEFAULT, value); - if (!value) { - tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, - CRASH_ON_NO_RETRY_FAULT, 1); - tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, - CRASH_ON_RETRY_FAULT, 1); - } + tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, + CRASH_ON_NO_RETRY_FAULT, !value); + tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, + CRASH_ON_RETRY_FAULT, !value); WREG32_SOC15(GC, 0, regGCVM_L2_PROTECTION_FAULT_CNTL, tmp); } diff --git a/drivers/gpu/drm/amd/amdgpu/gfxhub_v3_0_3.c b/drivers/gpu/drm/amd/amdgpu/gfxhub_v3_0_3.c index b3ef6e71811f..128115a2cb45 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfxhub_v3_0_3.c +++ b/drivers/gpu/drm/amd/amdgpu/gfxhub_v3_0_3.c @@ -434,12 +434,10 @@ static void gfxhub_v3_0_3_set_fault_enable_default(struct amdgpu_device *adev, WRITE_PROTECTION_FAULT_ENABLE_DEFAULT, value); tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, EXECUTE_PROTECTION_FAULT_ENABLE_DEFAULT, value); - if (!value) { - tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, - CRASH_ON_NO_RETRY_FAULT, 1); - tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, - CRASH_ON_RETRY_FAULT, 1); - } + tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, + CRASH_ON_NO_RETRY_FAULT, !value); + tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, + CRASH_ON_RETRY_FAULT, !value); WREG32_SOC15(GC, 0, regGCVM_L2_PROTECTION_FAULT_CNTL, tmp); } From e47b0056a08dc70430ffc44bbf62197e7d1ff8ea Mon Sep 17 00:00:00 2001 From: Vitaly Prosyak Date: Fri, 29 May 2026 13:50:38 -0400 Subject: [PATCH 122/257] drm/amdgpu: set noretry=1 as default for GFX 10.1.x (Navi10/12/14) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: While developing the amd_close_race IGT test (which intentionally triggers execute permission faults by removing VM_PAGE_EXECUTABLE from GPU page table entries), we discovered that on Navi10 (GFX 10.1.x) these faults produce zero diagnostic output. The GPU simply hangs silently for ~10s until the scheduler timeout fires. There is no way to distinguish an execute permission fault from any other type of GPU hang. Root cause: GFX 10.1.x defaults to noretry=0, which sets RETRY_PERMISSION_OR_INVALID_PAGE_FAULT=1 in the GFXHUB UTCL2 registers (gfxhub_v2_0.c line 313). With this bit set, permission faults (valid PTE, wrong R/W/X bits) are handled entirely within the UTCL1/UTCL2 hardware loop: UTCL2 returns an XNACK to UTCL1, and UTCL1 re-requests the translation indefinitely, expecting software to eventually fix the permission bits (as happens in SVM/HMM recovery). No interrupt of any kind reaches the IH ring. This is different from invalid-page faults (V=0) which DO generate a retry fault interrupt that the driver can escalate to a no-retry fault. Permission faults with valid PTEs loop silently forever in hardware. GFX 10.3+ already defaults to noretry=1, which makes permission faults generate immediate L2 protection fault interrupts. GFX 10.1.x was inadvertently left out of this default. Fix: Change the noretry=1 threshold from IP_VERSION(10, 3, 0) to IP_VERSION(10, 1, 0) in amdgpu_gmc_noretry_set(). This is a one-line change that aligns GFX 10.1.x behavior with GFX 10.3+ and all newer generations. With noretry=1, the existing non-retry fault handler (gmc_v10_0_process_interrupt) already decodes and prints the full GCVM_L2_PROTECTION_FAULT_STATUS register including PERMISSION_FAULTS, faulting address, VMID, PASID, and process name. No additional logging code is needed — the fix is purely routing permission faults to the existing, fully-capable non-retry interrupt handler. v2: Dropped GFX10-specific logging from gmc_v10_0.c and kfd_int_process_v10.c (Felix Kuehling). v1 added logging in the retry fault handler, but with noretry=1 permission faults take the non-retry path — the v1 retry handler code was dead and would never execute. Tested on Navi10 (GFX 10.1.10): - Execute permission faults now produce immediate, clear output: [gfxhub] page fault (src_id:0 ring:64 vmid:4 pasid:592) Process amd_close_race pid 13380 thread amd_close_race pid 13384 in page at address 0x40001000 from client 0x1b (UTCL2) GCVM_L2_PROTECTION_FAULT_STATUS:0x00700881 PERMISSION_FAULTS: 0x8 - No regressions with properly-mapped GPU workloads Cc: Christian Koenig Cc: Alex Deucher Cc: Felix Kuehling Signed-off-by: Vitaly Prosyak Acked-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit eb21edd24c40d81066753f8ac6f23bce15745395) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c index 13a5acdf8da3..c076c5f06e77 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c @@ -1003,7 +1003,7 @@ void amdgpu_gmc_noretry_set(struct amdgpu_device *adev) gc_ver == IP_VERSION(9, 4, 3) || gc_ver == IP_VERSION(9, 4, 4) || gc_ver == IP_VERSION(9, 5, 0) || - gc_ver >= IP_VERSION(10, 3, 0)); + gc_ver >= IP_VERSION(10, 1, 0)); if (!amdgpu_sriov_xnack_support(adev)) gmc->noretry = 1; From 2bd550b547deabef98bd3b017ff743b7c34d3a6d Mon Sep 17 00:00:00 2001 From: Muhammad Bilal Date: Sat, 23 May 2026 16:56:46 +0000 Subject: [PATCH 123/257] drm/amdkfd: fix NULL dereference in get_queue_ids() When usr_queue_id_array is NULL and num_queues is non-zero, get_queue_ids() returns NULL. The callers check only IS_ERR() on the return value; since IS_ERR(NULL) == false the check passes, and suspend_queues() calls q_array_invalidate() which immediately dereferences NULL while iterating num_queues times. Userspace can trigger this via kfd_ioctl_set_debug_trap() by supplying num_queues > 0 with a zero queue_array_ptr, causing a kernel panic. A NULL usr_queue_id_array with num_queues == 0 is a legitimate no-op (q_array_invalidate never executes, and resume_queues already guards all queue_ids dereferences behind a NULL check). Return ERR_PTR(-EINVAL) only when num_queues is non-zero and the pointer is absent; both callers already propagate IS_ERR() returns correctly to userspace. Fixes: a70a93fa568b ("drm/amdkfd: add debug suspend and resume process queues operation") Signed-off-by: Muhammad Bilal Signed-off-by: Alex Deucher (cherry picked from commit f165a82cdf503884bb1797771c61b2fcc72113d4) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c index 0a408f95baac..31187ddbb79e 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c @@ -3315,7 +3315,7 @@ static void copy_context_work_handler(struct work_struct *work) static uint32_t *get_queue_ids(uint32_t num_queues, uint32_t *usr_queue_id_array) { if (!usr_queue_id_array) - return NULL; + return num_queues ? ERR_PTR(-EINVAL) : NULL; if (num_queues > KFD_MAX_NUM_OF_QUEUES_PER_PROCESS) return ERR_PTR(-EINVAL); From 352ea59028ea48a6fff77f19ae28f98f71946a80 Mon Sep 17 00:00:00 2001 From: Andrew Martin Date: Thu, 28 May 2026 12:54:39 -0400 Subject: [PATCH 124/257] drm/amdkfd: Fix buffer overflow in SDMA queue checkpoint/restore on GFX11 The v11 MQD manager incorrectly assigned the CP-compute variants of checkpoint_mqd/restore_mqd for KFD_MQD_TYPE_SDMA queues. These functions use sizeof(struct v11_compute_mqd) (2048 bytes) instead of sizeof(struct v11_sdma_mqd) (512 bytes), causing a 1536-byte overflow. During CRIU checkpoint of an SDMA queue on Navi3x: - checkpoint_mqd() reads 2048 bytes from a 512-byte SDMA MQD buffer, leaking 1536 bytes of adjacent GTT memory to userspace During CRIU restore: - restore_mqd() writes 2048 bytes into a 512-byte SDMA MQD buffer, corrupting 1536 bytes of adjacent GTT memory (often the ring buffer or neighboring MQDs) This is a copy-paste regression unique to v11. All other ASIC backends (cik, vi, v9, v10, v12) correctly use the SDMA-specific variants. Add checkpoint_mqd_sdma() and restore_mqd_sdma() functions that properly handle the smaller v11_sdma_mqd structure, matching the pattern used in other MQD managers. Fixes: cc009e613de6 ("drm/amdkfd: Add KFD support for soc21 v3") Assisted-by: Claude:Sonnet 4-5 Signed-off-by: Andrew Martin Acked-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit 6fa41db7ffdec97d62433adf03b7b9b759af8c2c) Cc: stable@vger.kernel.org --- .../gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c | 49 ++++++++++++++++--- 1 file changed, 41 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c index a1e3cf2384dd..527c531676e4 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c @@ -320,8 +320,7 @@ static void checkpoint_mqd(struct mqd_manager *mm, void *mqd, void *mqd_dst, voi static void restore_mqd(struct mqd_manager *mm, void **mqd, struct kfd_mem_obj *mqd_mem_obj, uint64_t *gart_addr, - struct queue_properties *qp, - const void *mqd_src, + struct queue_properties *qp, const void *mqd_src, const void *ctl_stack_src, const u32 ctl_stack_size) { uint64_t addr; @@ -337,14 +336,48 @@ static void restore_mqd(struct mqd_manager *mm, void **mqd, *gart_addr = addr; m->cp_hqd_pq_doorbell_control = - qp->doorbell_off << - CP_HQD_PQ_DOORBELL_CONTROL__DOORBELL_OFFSET__SHIFT; - pr_debug("cp_hqd_pq_doorbell_control 0x%x\n", - m->cp_hqd_pq_doorbell_control); + qp->doorbell_off << CP_HQD_PQ_DOORBELL_CONTROL__DOORBELL_OFFSET__SHIFT; + pr_debug("cp_hqd_pq_doorbell_control 0x%x\n", m->cp_hqd_pq_doorbell_control); qp->is_active = 0; } +static void checkpoint_mqd_sdma(struct mqd_manager *mm, + void *mqd, + void *mqd_dst, + void *ctl_stack_dst) +{ + struct v11_sdma_mqd *m; + + m = get_sdma_mqd(mqd); + + memcpy(mqd_dst, m, sizeof(struct v11_sdma_mqd)); +} + +static void restore_mqd_sdma(struct mqd_manager *mm, void **mqd, + struct kfd_mem_obj *mqd_mem_obj, uint64_t *gart_addr, + struct queue_properties *qp, + const void *mqd_src, + const void *ctl_stack_src, + const u32 ctl_stack_size) +{ + uint64_t addr; + struct v11_sdma_mqd *m; + + m = (struct v11_sdma_mqd *) mqd_mem_obj->cpu_ptr; + addr = mqd_mem_obj->gpu_addr; + + memcpy(m, mqd_src, sizeof(*m)); + + m->sdmax_rlcx_doorbell_offset = + qp->doorbell_off << SDMA0_QUEUE0_DOORBELL_OFFSET__OFFSET__SHIFT; + + *mqd = m; + if (gart_addr) + *gart_addr = addr; + + qp->is_active = 0; +} static void init_mqd_hiq(struct mqd_manager *mm, void **mqd, struct kfd_mem_obj *mqd_mem_obj, uint64_t *gart_addr, @@ -529,8 +562,8 @@ struct mqd_manager *mqd_manager_init_v11(enum KFD_MQD_TYPE type, mqd->update_mqd = update_mqd_sdma; mqd->destroy_mqd = kfd_destroy_mqd_sdma; mqd->is_occupied = kfd_is_occupied_sdma; - mqd->checkpoint_mqd = checkpoint_mqd; - mqd->restore_mqd = restore_mqd; + mqd->checkpoint_mqd = checkpoint_mqd_sdma; + mqd->restore_mqd = restore_mqd_sdma; mqd->mqd_size = sizeof(struct v11_sdma_mqd); mqd->mqd_stride = kfd_mqd_stride; #if defined(CONFIG_DEBUG_FS) From 2a07f3fa4998e2ef248e1f55cd3776348758aec0 Mon Sep 17 00:00:00 2001 From: Sunil Khatri Date: Mon, 25 May 2026 13:18:00 +0530 Subject: [PATCH 125/257] drm/amdgpu/userq: remove the vital queue unmap logging MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mesa userqueues free does not wait for the free to complete and go ahead in unmapping the vital bos while kernel is still in queue free and corresponding cleanup. So ideally we don't need the logging for that and hence remove the warn message as this is expected behaviour and functionally, we are making sure to wait for the required fences before unmap. Signed-off-by: Sunil Khatri Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit 758a868043dcb07eca923bc451c16da3e73dc47c) --- drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 10 +++------- drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h | 3 +-- drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c | 2 +- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c index e937099de3e7..986ef5ca0087 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c @@ -1344,8 +1344,7 @@ int amdgpu_userq_start_sched_for_enforce_isolation(struct amdgpu_device *adev, } void amdgpu_userq_gem_va_unmap_validate(struct amdgpu_device *adev, - struct amdgpu_bo_va_mapping *mapping, - uint64_t saddr) + struct amdgpu_bo_va_mapping *mapping) { u32 ip_mask = amdgpu_userq_get_supported_ip_mask(adev); struct amdgpu_bo_va *bo_va = mapping->bo_va; @@ -1354,12 +1353,9 @@ void amdgpu_userq_gem_va_unmap_validate(struct amdgpu_device *adev, if (!ip_mask) return; - dev_warn_once(adev->dev, "now unmapping a vital queue va:%llx\n", saddr); /** - * The userq VA mapping reservation should include the eviction fence, - * if the eviction fence can't signal successfully during unmapping, - * then driver will warn to flag this improper unmap of the userq VA. - * Note: The eviction fence may be attached to different BOs, and this + * The userq VA mapping reservation should include the eviction fence. + * Note: The eviction fence may be attached to different BOs and this * unmap is only for one kind of userq VAs, so at this point suppose * the eviction fence is always unsignaled. */ diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h index 28cfc6682333..d1751febaefe 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h @@ -182,6 +182,5 @@ int amdgpu_userq_input_va_validate(struct amdgpu_device *adev, u64 addr, u64 expected_size, u64 *va_out); void amdgpu_userq_gem_va_unmap_validate(struct amdgpu_device *adev, - struct amdgpu_bo_va_mapping *mapping, - uint64_t saddr); + struct amdgpu_bo_va_mapping *mapping); #endif diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c index c9f88ecce1a7..381901bc539f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c @@ -2006,7 +2006,7 @@ int amdgpu_vm_bo_unmap(struct amdgpu_device *adev, * from user space. */ if (unlikely(bo_va->userq_va_mapped)) - amdgpu_userq_gem_va_unmap_validate(adev, mapping, saddr); + amdgpu_userq_gem_va_unmap_validate(adev, mapping); list_del(&mapping->list); amdgpu_vm_it_remove(mapping, &vm->va); From 5af28a22ce834544bb22efbba30ffb837098b2b8 Mon Sep 17 00:00:00 2001 From: Prike Liang Date: Tue, 26 May 2026 10:25:26 +0800 Subject: [PATCH 126/257] drm/amdgpu: improve the userq seq BO free bit lookup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use find_next_zero_bit() to locate the next free seq slot bit instead of the current walk, for more efficient bitmap scanning. Signed-off-by: Prike Liang Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit ff905a9b6228de9eedd0db71ecb1bdde91fb898d) --- drivers/gpu/drm/amd/amdgpu/amdgpu_seq64.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_seq64.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_seq64.c index f4be19223588..21a225b0116a 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_seq64.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_seq64.c @@ -173,16 +173,17 @@ void amdgpu_seq64_unmap(struct amdgpu_device *adev, struct amdgpu_fpriv *fpriv) int amdgpu_seq64_alloc(struct amdgpu_device *adev, u64 *va, u64 *gpu_addr, u64 **cpu_addr) { - unsigned long bit_pos; + unsigned long bit_pos = 0; - for (;;) { - bit_pos = find_first_zero_bit(adev->seq64.used, adev->seq64.num_sem); + do { + bit_pos = find_next_zero_bit(adev->seq64.used, + adev->seq64.num_sem, bit_pos); if (bit_pos >= adev->seq64.num_sem) return -ENOSPC; - if (!test_and_set_bit(bit_pos, adev->seq64.used)) break; - } + bit_pos++; + } while (1); *va = bit_pos * sizeof(u64) + amdgpu_seq64_get_va_base(adev); From 14ad7e1e6e2cd44f866e2dbb3f6a2b2f4a39b96d Mon Sep 17 00:00:00 2001 From: Sunil Khatri Date: Mon, 25 May 2026 09:56:23 +0530 Subject: [PATCH 127/257] drm/amdgpu/userq: move wptr_obj cleanup in mqd_destroy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In case when queue_create fails and mqd has already been allocated and hence wptr_obj is not cleaned up. So moving that cleanup part to mqd_destroy so it takes care of all the cases of clean up and during tear down of the queue. Signed-off-by: Sunil Khatri Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit 43355f62cd2ef5386c2693df537c232ea0f2ce6c) --- drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 4 ---- drivers/gpu/drm/amd/amdgpu/mes_userqueue.c | 5 +++++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c index 986ef5ca0087..59ffaa7b61c2 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c @@ -532,10 +532,6 @@ amdgpu_userq_destroy(struct amdgpu_userq_mgr *uq_mgr, struct amdgpu_usermode_que amdgpu_bo_unreserve(queue->db_obj.obj); amdgpu_bo_unref(&queue->db_obj.obj); - amdgpu_bo_reserve(queue->wptr_obj.obj, true); - amdgpu_bo_unpin(queue->wptr_obj.obj); - amdgpu_bo_unreserve(queue->wptr_obj.obj); - amdgpu_bo_unref(&queue->wptr_obj.obj); kfree(queue); pm_runtime_put_autosuspend(adev_to_drm(adev)->dev); diff --git a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c index 98aa00eeb2f4..4cbd46f53e85 100644 --- a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c +++ b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c @@ -467,6 +467,11 @@ static void mes_userq_mqd_destroy(struct amdgpu_usermode_queue *queue) kfree(queue->userq_prop); amdgpu_bo_free_kernel(&queue->mqd.obj, &queue->mqd.gpu_addr, &queue->mqd.cpu_ptr); + + amdgpu_bo_reserve(queue->wptr_obj.obj, true); + amdgpu_bo_unpin(queue->wptr_obj.obj); + amdgpu_bo_unreserve(queue->wptr_obj.obj); + amdgpu_bo_unref(&queue->wptr_obj.obj); } static int mes_userq_preempt(struct amdgpu_usermode_queue *queue) From ec4c462e2d8161b32038e21e7187f4a15fe1661d Mon Sep 17 00:00:00 2001 From: Donet Tom Date: Wed, 27 May 2026 18:49:31 +0530 Subject: [PATCH 128/257] drm/amdgpu: Fix incorrect VRAM GART mappings on non-4K page size systems MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When mapping VRAM pages into the GART page table, amdgpu_gart_map_vram_range() assumes that the system page size is the same as the GPU page size. On systems with non-4K page sizes, multiple GPU pages can exist within a single CPU page. As a result, the mappings are created incorrectly because fewer page table entries are programmed than required. Fix this by programming the mappings correctly for non-4K page size systems. Fixes: 237d623ae659 ("drm/amdgpu/gart: Add helper to bind VRAM pages (v2)") Reviewed-by: Timur Kristóf Reviewed-by: Christian König Signed-off-by: Donet Tom Signed-off-by: Alex Deucher (cherry picked from commit a8f0bc22388f74e0cf4ed8b7d1846c580eaf44cc) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c index b6f849d51c2e..c4c21dbbbdbf 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c @@ -394,7 +394,8 @@ void amdgpu_gart_map_vram_range(struct amdgpu_device *adev, uint64_t pa, uint64_t start_page, uint64_t num_pages, uint64_t flags, void *dst) { - u32 i, idx; + u32 i, j, t, idx; + u64 page_base; /* The SYSTEM flag indicates the pages aren't in VRAM. */ WARN_ON_ONCE(flags & AMDGPU_PTE_SYSTEM); @@ -402,9 +403,12 @@ void amdgpu_gart_map_vram_range(struct amdgpu_device *adev, uint64_t pa, if (!drm_dev_enter(adev_to_drm(adev), &idx)) return; - for (i = 0; i < num_pages; ++i) { - amdgpu_gmc_set_pte_pde(adev, dst, - start_page + i, pa + AMDGPU_GPU_PAGE_SIZE * i, flags); + page_base = pa; + for (i = 0, t = 0; i < num_pages; i++) { + for (j = 0; j < AMDGPU_GPU_PAGES_IN_CPU_PAGE; j++, t++) { + amdgpu_gmc_set_pte_pde(adev, dst, start_page + t, page_base, flags); + page_base += AMDGPU_GPU_PAGE_SIZE; + } } drm_dev_exit(idx); From 03b70e0d8aa26bab89a0f1394c1c80a871925e42 Mon Sep 17 00:00:00 2001 From: Priya Hosur Date: Thu, 7 May 2026 13:31:37 +0530 Subject: [PATCH 129/257] drm/amd/pm: smu_v14_0_0: use SoftMin for gfxclk in set_soft_freq_limited_range In smu_v14_0_0_set_soft_freq_limited_range(), the gfxclk floor is programmed via SetHardMinGfxClk together with SetSoftMaxGfxClk. Under power_dpm_force_performance_level=high this pins HardMin to peak gfxclk. In PMFW arbitration HardMin has higher priority than SoftMax, so the firmware thermal/PPT throttler cannot clamp gfxclk via SoftMax once HardMin is set to peak. Replace SetHardMinGfxClk with SetSoftMinGfxclk so the driver still requests peak performance but the firmware throttler retains the ability to clamp gfxclk under thermal/PPT pressure. SoftMax handling is unchanged and no other clock domains are affected. Signed-off-by: Priya Hosur Acked-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit 3ea273267fd29cbf6d83ee72329f59eb5042605b) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c index a28624d4847a..75719c47a41e 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c @@ -1231,7 +1231,8 @@ static int smu_v14_0_0_set_soft_freq_limited_range(struct smu_context *smu, switch (clk_type) { case SMU_GFXCLK: case SMU_SCLK: - msg_set_min = SMU_MSG_SetHardMinGfxClk; + /* SoftMin lets PMFW throttle gfxclk; HardMin would override SoftMax. */ + msg_set_min = SMU_MSG_SetSoftMinGfxclk; msg_set_max = SMU_MSG_SetSoftMaxGfxClk; break; case SMU_FCLK: From 40a25d59e85b3c8709ac2424d44f65610467871e Mon Sep 17 00:00:00 2001 From: Davidlohr Bueso Date: Thu, 7 May 2026 04:29:13 -0700 Subject: [PATCH 130/257] locking/rtmutex: Skip remove_waiter() when waiter is not enqueued syzbot triggered the following splat in remove_waiter() via FUTEX_CMP_REQUEUE_PI: KASAN: null-ptr-deref in range [0x0000000000000a88-0x0000000000000a8f] class_raw_spinlock_constructor remove_waiter+0x159/0x1200 kernel/locking/rtmutex.c:1561 rt_mutex_start_proxy_lock+0x103/0x120 futex_requeue+0x10e4/0x20d0 __x64_sys_futex+0x34f/0x4d0 task_blocks_on_rt_mutex() does not arm the waiter upon deadlock detection, leaving waiter->task nil, where 3bfdc63936dd ("rtmutex: Use waiter::task instead of current in remove_waiter()") made this fatal. Furthermore, rt_mutex_start_proxy_lock() should not be calling into remove_waiter() upon a successfully grabbing the rtmutex. 1a1fb985f2e2 ("futex: Handle early deadlock return correctly"), moved the remove_waiter() out of __rt_mutex_start_proxy_lock() (where 'ret' was only ever 0 or < 0) into the wrapper. Tighten this check to account for try_to_take_rt_mutex(). Fixes: 3bfdc63936dd ("rtmutex: Use waiter::task instead of current in remove_waiter()") Reported-by: syzbot+78147abe6c524f183ee9@syzkaller.appspotmail.com Signed-off-by: Davidlohr Bueso Signed-off-by: Thomas Gleixner Cc: stable@vger.kernel.org Closes: https://lore.kernel.org/all/69f114ac.050a0220.ac8b.0003.GAE@google.com/ Link: https://patch.msgid.link/20260507112913.1019537-1-dave@stgolabs.net --- kernel/locking/rtmutex.c | 3 +++ kernel/locking/rtmutex_api.c | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/kernel/locking/rtmutex.c b/kernel/locking/rtmutex.c index 4f386ea6c792..daeeeef973e2 100644 --- a/kernel/locking/rtmutex.c +++ b/kernel/locking/rtmutex.c @@ -1558,6 +1558,9 @@ static void __sched remove_waiter(struct rt_mutex_base *lock, lockdep_assert_held(&lock->wait_lock); + if (!waiter_task) /* never enqueued */ + return; + scoped_guard(raw_spinlock, &waiter_task->pi_lock) { rt_mutex_dequeue(lock, waiter); waiter_task->pi_blocked_on = NULL; diff --git a/kernel/locking/rtmutex_api.c b/kernel/locking/rtmutex_api.c index 124219aea46e..514fce7a4e0a 100644 --- a/kernel/locking/rtmutex_api.c +++ b/kernel/locking/rtmutex_api.c @@ -365,7 +365,7 @@ int __sched rt_mutex_start_proxy_lock(struct rt_mutex_base *lock, raw_spin_lock_irq(&lock->wait_lock); ret = __rt_mutex_start_proxy_lock(lock, waiter, task, &wake_q); - if (unlikely(ret)) + if (unlikely(ret < 0)) remove_waiter(lock, waiter); preempt_disable(); raw_spin_unlock_irq(&lock->wait_lock); From 2e5c6f4fd4001562781e99bbfc7f1f0127187542 Mon Sep 17 00:00:00 2001 From: Yilin Zhu Date: Thu, 30 Apr 2026 13:21:34 +0800 Subject: [PATCH 131/257] ipc/shm: serialize orphan cleanup with shm_nattch updates shm_destroy_orphaned() walks the shm idr under shm_ids(ns).rwsem, but that does not serialize all fields tested by shm_may_destroy(). In particular, shm_nattch is updated while holding shm_perm.lock, and attach paths can do that without holding the rwsem. Do not decide that an orphaned segment is unused before taking the object lock. Move the shm_may_destroy() check under shm_perm.lock, matching the other destroy paths, and unlock the segment when it no longer qualifies for removal. Link: https://lore.kernel.org/9d97cc1031de2d0bace0edf3a668818aa2f4eca6.1777410234.git.zylzyl2333@gmail.com Fixes: 4c677e2eefdb ("shm: optimize locking and ipc_namespace getting") Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Xin Liu Signed-off-by: Yilin Zhu Signed-off-by: Ren Wei Cc: Christian Brauner Cc: Jeongjun Park Cc: Kees Cook Cc: Liam Howlett Cc: Lorenzo Stoakes Cc: Serge Hallyn Cc: Vasiliy Kulikov Cc: Davidlohr Bueso Cc: Oleg Nesterov Cc: Serge Hallyn Cc: Signed-off-by: Andrew Morton --- ipc/shm.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/ipc/shm.c b/ipc/shm.c index a95dae447707..b3e8a58e177d 100644 --- a/ipc/shm.c +++ b/ipc/shm.c @@ -418,15 +418,17 @@ static int shm_try_destroy_orphaned(int id, void *p, void *data) * We want to destroy segments without users and with already * exit'ed originating process. * - * As shp->* are changed under rwsem, it's safe to skip shp locking. + * shm_nattch can be changed under shm_perm.lock without holding the + * rwsem, so take the object lock before checking shm_may_destroy(). */ if (!list_empty(&shp->shm_clist)) return 0; - if (shm_may_destroy(shp)) { - shm_lock_by_ptr(shp); + shm_lock_by_ptr(shp); + if (shm_may_destroy(shp)) shm_destroy(ns, shp); - } + else + shm_unlock(shp); return 0; } From 0ade3a8d83a8f5bc274e1ace776c1a26aa7a0308 Mon Sep 17 00:00:00 2001 From: Kaitao Cheng Date: Fri, 22 May 2026 21:14:34 +0800 Subject: [PATCH 132/257] mm/cma_sysfs: skip inactive CMA areas in sysfs cma_activate_area() can fail after a CMA area has already been added to cma_areas[]. In that case the area is left in the global array, but it does not reach the point where CMA_ACTIVATED is set. cma_sysfs_init() currently walks all cma_area_count entries and creates sysfs files for every area, including ones that failed activation. These areas are not usable CMA areas and should not be exposed to userspace as valid CMA regions. If such an inactive area is exposed, userspace sees a CMA directory whose read-only accounting files report zeros. total_pages and available_pages report zero because the failed activation path clears cma->count and cma->available_count, while the allocation and release counters also stay at zero because the area cannot service CMA allocations. This makes the failed area look like a valid but empty CMA region and can mislead tests, monitoring, and diagnostics. Skip CMA areas that did not reach CMA_ACTIVATED when creating the sysfs objects. Since inactive entries can now be skipped, make the error unwind tolerate entries that never had cma_kobj initialized. Link: https://lore.kernel.org/20260524140420.61864-1-kaitao.cheng@linux.dev Link: https://lore.kernel.org/20260522131434.78532-1-kaitao.cheng@linux.dev Fixes: 43ca106fa8ec ("mm: cma: support sysfs") Signed-off-by: Kaitao Cheng Reported-by: David Hildenbrand (Arm) Suggested-by: David Hildenbrand (Arm) Suggested-by: Muchun Song Reported-by: Muchun Song Closes: https://lore.kernel.org/linux-mm/55481a8b-dcfc-4bef-ba59-aa0b43dca88b@kernel.org/ Acked-by: Muchun Song Cc: David Hildenbrand Cc: Dmitry Osipenko Cc: Greg Kroah-Hartman Cc: John Hubbard Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Minchan Kim Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- mm/cma_sysfs.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/mm/cma_sysfs.c b/mm/cma_sysfs.c index f52b696bc46d..d5bf792c6245 100644 --- a/mm/cma_sysfs.c +++ b/mm/cma_sysfs.c @@ -117,13 +117,16 @@ static int __init cma_sysfs_init(void) return -ENOMEM; for (i = 0; i < cma_area_count; i++) { + cma = &cma_areas[i]; + if (!test_bit(CMA_ACTIVATED, &cma->flags)) + continue; + cma_kobj = kzalloc_obj(*cma_kobj); if (!cma_kobj) { err = -ENOMEM; goto out; } - cma = &cma_areas[i]; cma->cma_kobj = cma_kobj; cma_kobj->cma = cma; err = kobject_init_and_add(&cma_kobj->kobj, &cma_ktype, @@ -138,7 +141,8 @@ static int __init cma_sysfs_init(void) out: while (--i >= 0) { cma = &cma_areas[i]; - kobject_put(&cma->cma_kobj->kobj); + if (cma->cma_kobj) + kobject_put(&cma->cma_kobj->kobj); } kobject_put(cma_kobj_root); From 5921629dd625a69546692a92e1ca7ae987f03964 Mon Sep 17 00:00:00 2001 From: Wang Yaxin Date: Wed, 27 May 2026 21:35:58 +0800 Subject: [PATCH 133/257] tools headers UAPI: sync linux/taskstats.h for procacct.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After commit 9b93f7e32774 ("tools/getdelays: use the static UAPI headers from tools/include/uapi"), the Makefile was changed to use -I../include/uapi/ instead of -I../../usr/include to ensure tools always use the up-to-date UAPI headers. However, only linux/taskstats.h was added to tools/include/uapi/ in commit e5bbb35a07b3 ("tools headers UAPI: sync linux/taskstats.h"), but linux/acct.h was missing. This causes procacct.c to fail to compile with: procacct.c:234:37: error: 'AGROUP' undeclared (first use in this function) gcc -I../include/uapi/ getdelays.c -o getdelays gcc -I../include/uapi/ procacct.c -o procacct procacct.c: In function `print_procacct': procacct.c:234:37: error: `AGROUP' undeclared (first use in this function) did you mean `NOGROUP'? 234 | , t->version >= 12 ? (t->ac_flag & AGROUP ? 'P' : 'T') : '?' | ^~~~~~ | NOGROUP procacct.c:234:37: note: each undeclared ident because procacct.c uses the AGROUP macro defined in linux/acct.h. Add the missing linux/acct.h to complete the static UAPI header set. Link: https://lore.kernel.org/20260527213558929EhiHHy9EDTMjmg3uuDOMi@zte.com.cn Fixes: 9b93f7e32774 ("tools/getdelays: use the static UAPI headers from tools/include/uapi") Signed-off-by: Wang Yaxin Reviewed-by: Thomas Weißschuh Cc: Fan Yu Cc: Jonathan Corbet Cc: xu xin Cc: Yang Yang Signed-off-by: Andrew Morton --- tools/include/uapi/linux/acct.h | 128 ++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 tools/include/uapi/linux/acct.h diff --git a/tools/include/uapi/linux/acct.h b/tools/include/uapi/linux/acct.h new file mode 100644 index 000000000000..1e2382ed4fd5 --- /dev/null +++ b/tools/include/uapi/linux/acct.h @@ -0,0 +1,128 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * BSD Process Accounting for Linux - Definitions + * + * Author: Marco van Wieringen (mvw@planets.elm.net) + * + * This header file contains the definitions needed to implement + * BSD-style process accounting. The kernel accounting code and all + * user-level programs that try to do something useful with the + * process accounting log must include this file. + * + * Copyright (C) 1995 - 1997 Marco van Wieringen - ELM Consultancy B.V. + * + */ + +#ifndef _UAPI_LINUX_ACCT_H +#define _UAPI_LINUX_ACCT_H + +#include + +#include +#include + +/* + * comp_t is a 16-bit "floating" point number with a 3-bit base 8 + * exponent and a 13-bit fraction. + * comp2_t is 24-bit with 5-bit base 2 exponent and 20 bit fraction + * (leading 1 not stored). + * See linux/kernel/acct.c for the specific encoding systems used. + */ + +typedef __u16 comp_t; +typedef __u32 comp2_t; + +/* + * accounting file record + * + * This structure contains all of the information written out to the + * process accounting file whenever a process exits. + */ + +#define ACCT_COMM 16 + +struct acct +{ + char ac_flag; /* Flags */ + char ac_version; /* Always set to ACCT_VERSION */ + /* for binary compatibility back until 2.0 */ + __u16 ac_uid16; /* LSB of Real User ID */ + __u16 ac_gid16; /* LSB of Real Group ID */ + __u16 ac_tty; /* Control Terminal */ + /* __u32 range means times from 1970 to 2106 */ + __u32 ac_btime; /* Process Creation Time */ + comp_t ac_utime; /* User Time */ + comp_t ac_stime; /* System Time */ + comp_t ac_etime; /* Elapsed Time */ + comp_t ac_mem; /* Average Memory Usage */ + comp_t ac_io; /* Chars Transferred */ + comp_t ac_rw; /* Blocks Read or Written */ + comp_t ac_minflt; /* Minor Pagefaults */ + comp_t ac_majflt; /* Major Pagefaults */ + comp_t ac_swaps; /* Number of Swaps */ +/* m68k had no padding here. */ +#if !defined(CONFIG_M68K) || !defined(__KERNEL__) + __u16 ac_ahz; /* AHZ */ +#endif + __u32 ac_exitcode; /* Exitcode */ + char ac_comm[ACCT_COMM + 1]; /* Command Name */ + __u8 ac_etime_hi; /* Elapsed Time MSB */ + __u16 ac_etime_lo; /* Elapsed Time LSB */ + __u32 ac_uid; /* Real User ID */ + __u32 ac_gid; /* Real Group ID */ +}; + +struct acct_v3 +{ + char ac_flag; /* Flags */ + char ac_version; /* Always set to ACCT_VERSION */ + __u16 ac_tty; /* Control Terminal */ + __u32 ac_exitcode; /* Exitcode */ + __u32 ac_uid; /* Real User ID */ + __u32 ac_gid; /* Real Group ID */ + __u32 ac_pid; /* Process ID */ + __u32 ac_ppid; /* Parent Process ID */ + /* __u32 range means times from 1970 to 2106 */ + __u32 ac_btime; /* Process Creation Time */ +#ifdef __KERNEL__ + __u32 ac_etime; /* Elapsed Time */ +#else + float ac_etime; /* Elapsed Time */ +#endif + comp_t ac_utime; /* User Time */ + comp_t ac_stime; /* System Time */ + comp_t ac_mem; /* Average Memory Usage */ + comp_t ac_io; /* Chars Transferred */ + comp_t ac_rw; /* Blocks Read or Written */ + comp_t ac_minflt; /* Minor Pagefaults */ + comp_t ac_majflt; /* Major Pagefaults */ + comp_t ac_swaps; /* Number of Swaps */ + char ac_comm[ACCT_COMM]; /* Command Name */ +}; + +/* + * accounting flags + */ + /* bit set when the process/task ... */ +#define AFORK 0x01 /* ... executed fork, but did not exec */ +#define ASU 0x02 /* ... used super-user privileges */ +#define ACOMPAT 0x04 /* ... used compatibility mode (VAX only not used) */ +#define ACORE 0x08 /* ... dumped core */ +#define AXSIG 0x10 /* ... was killed by a signal */ +#define AGROUP 0x20 /* ... was the last task of the process (task group) */ + +#if defined(__BYTE_ORDER) ? __BYTE_ORDER == __BIG_ENDIAN : defined(__BIG_ENDIAN) +#define ACCT_BYTEORDER 0x80 /* accounting file is big endian */ +#elif defined(__BYTE_ORDER) ? __BYTE_ORDER == __LITTLE_ENDIAN : defined(__LITTLE_ENDIAN) +#define ACCT_BYTEORDER 0x00 /* accounting file is little endian */ +#else +#error unspecified endianness +#endif + +#ifndef __KERNEL__ +#define ACCT_VERSION 2 +#define AHZ (HZ) +#endif /* __KERNEL */ + + +#endif /* _UAPI_LINUX_ACCT_H */ From 8ad974459aef3349ec5cb3cc54e2bfc1b7aebf34 Mon Sep 17 00:00:00 2001 From: Baoquan He Date: Thu, 28 May 2026 21:14:54 +0800 Subject: [PATCH 134/257] MAINTAINERS: update Baoquan He's email address I will switch to use @linux.dev mailbox, update all entries in MAINTAINERS. And map the address in .mailmap. Link: https://lore.kernel.org/20260528131454.1996752-1-baoquan.he@linux.dev Signed-off-by: Baoquan He Signed-off-by: Andrew Morton --- .mailmap | 1 + MAINTAINERS | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.mailmap b/.mailmap index a009f73d7ea5..0b9298a55d2d 100644 --- a/.mailmap +++ b/.mailmap @@ -126,6 +126,7 @@ Baolin Wang Baolin Wang Baolin Wang Baolin Wang +Baoquan He Barry Song <21cnbao@gmail.com> Barry Song Barry Song diff --git a/MAINTAINERS b/MAINTAINERS index 461a3eed6129..fcc46165038c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13864,7 +13864,7 @@ F: scripts/Makefile.kcsan KDUMP M: Andrew Morton -M: Baoquan He +M: Baoquan He M: Mike Rapoport M: Pasha Tatashin M: Pratyush Yadav @@ -14183,7 +14183,7 @@ F: include/linux/kernfs.h KEXEC M: Andrew Morton -M: Baoquan He +M: Baoquan He M: Mike Rapoport M: Pasha Tatashin M: Pratyush Yadav @@ -17036,7 +17036,7 @@ M: Chris Li M: Kairui Song R: Kemeng Shi R: Nhat Pham -R: Baoquan He +R: Baoquan He R: Barry Song R: Youngjun Park L: linux-mm@kvack.org From 732fd9f0b9c1cdc6dfd77162ded60df005182cc0 Mon Sep 17 00:00:00 2001 From: Cunlong Li Date: Thu, 28 May 2026 10:48:44 +0800 Subject: [PATCH 135/257] zram: fix use-after-free in zram_bvec_write_partial() zram_read_page() picks the sync or async backing device read path based on whether the parent bio is NULL. zram_bvec_write_partial() passes its parent bio down, so for ZRAM_WB slots the read is dispatched asynchronously and zram_read_page() returns 0 while the bio is still in flight. The caller then runs memcpy_from_bvec(), zram_write_page() and __free_page() on the buffer, leaving the async read to write into a freed page. zram_bvec_read_partial() was switched to NULL in commit 4e3c87b9421d ("zram: fix synchronous reads") for the same reason; the write_partial counterpart was missed. Link: https://lore.kernel.org/20260528-zram-v3-1-cab86eef8764@gmail.com Fixes: 8e654f8fbff5 ("zram: read page from backing device") Reviewed-by: Christoph Hellwig Reviewed-by: Sergey Senozhatsky Signed-off-by: Cunlong Li Cc: Jens Axboe Cc: Minchan Kim Cc: Yisheng Xie Cc: Signed-off-by: Andrew Morton --- drivers/block/zram/zram_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index 07111455eecf..e11ee1ed3832 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -2337,7 +2337,7 @@ static int zram_bvec_write_partial(struct zram *zram, struct bio_vec *bvec, if (!page) return -ENOMEM; - ret = zram_read_page(zram, page, index, bio); + ret = zram_read_page(zram, page, index, NULL); if (!ret) { memcpy_from_bvec(page_address(page) + offset, bvec); ret = zram_write_page(zram, page, index); From 7e2ed8a29427af534bf2cb9b8bc51762b8b6e654 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Thu, 28 May 2026 17:01:02 -0700 Subject: [PATCH 136/257] mm/damon/reclaim: handle ctx allocation failure Patch series "mm/damon/{reclaim,lru_sort}: handle ctx allocation failures". DAMON_RECLAIM and DAMON_LRU_SORT could dereference NULL pointers if their damon_ctx object allocations fail. The bugs are expected to happen infrequently because the allocations are arguably too small to fail on common setups. But theoretically they are possible and the consequences are bad. Fix those. The issues were discovered [1] by Sashiko. This patch (of 2): DAMON_RECLAIM allocates the damon_ctx object for its kdamond in its init function. damon_reclaim_enabled_store() wrongly assumes the allocation will always succeed once tried. If the damon_ctx allocation was failed, therefore, code execution reaches to damon_commit_ctx() while 'ctx' is NULL. As a result, it dereferences the NULL 'ctx' pointer. Avoid the NULL dereference by returning -ENOMEM if 'ctx' is NULL. Link: https://lore.kernel.org/20260529000104.7006-2-sj@kernel.org Link: https://lore.kernel.org/20260419014800.877-1-sj@kernel.org [1] Fixes: 3f7a914ab9a5 ("mm/damon/reclaim: use damon_initialized()") Signed-off-by: SeongJae Park Cc: # 6.18.x Signed-off-by: Andrew Morton --- mm/damon/reclaim.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mm/damon/reclaim.c b/mm/damon/reclaim.c index fe7fce26cf6c..96f6dfc28eae 100644 --- a/mm/damon/reclaim.c +++ b/mm/damon/reclaim.c @@ -339,6 +339,10 @@ static int damon_reclaim_enabled_store(const char *val, if (!damon_initialized()) return 0; + /* damon_modules_new_paddr_ctx_target() in the init function failed. */ + if (!ctx) + return -ENOMEM; + return damon_reclaim_turn(enabled); } From ab04340b5ae5d52c1d46b750538febcde9d889e7 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Thu, 28 May 2026 17:01:03 -0700 Subject: [PATCH 137/257] mm/damon/lru_sort: handle ctx allocation failure DAMON_LRU_SORT allocates the damon_ctx object for its kdamond in its init function. damon_lru_sort_enabled_store() wrongly assumes the allocation will always succeed once tried. If the damon_ctx allocation was failed, therefore, code execution reaches to damon_commit_ctx() while 'ctx' is NULL. As a result, it dereferences the NULL 'ctx' pointer. Avoid the NULL dereference by returning -ENOMEM if 'ctx' is NULL. Link: https://lore.kernel.org/20260529000104.7006-3-sj@kernel.org Fixes: c4a8e662c839 ("mm/damon/lru_sort: use damon_initialized()") Signed-off-by: SeongJae Park Cc: # 6.18.x Signed-off-by: Andrew Morton --- mm/damon/lru_sort.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mm/damon/lru_sort.c b/mm/damon/lru_sort.c index 8494040b1ee4..8cfe7bd3dc1d 100644 --- a/mm/damon/lru_sort.c +++ b/mm/damon/lru_sort.c @@ -437,6 +437,10 @@ static int damon_lru_sort_enabled_store(const char *val, if (!damon_initialized()) return 0; + /* damon_modules_new_paddr_ctx_target() in the init function failed. */ + if (!ctx) + return -ENOMEM; + return damon_lru_sort_turn(enabled); } From 43e7f189769c512c843184a8a5892ac779a6bd90 Mon Sep 17 00:00:00 2001 From: Lorenzo Stoakes Date: Mon, 1 Jun 2026 09:30:44 +0100 Subject: [PATCH 138/257] mm/huge_memory: use correct flags for device private PMD entry Commit 65edfda6f3f2 ("mm/rmap: extend rmap and migration support device-private entries") updated set_pmd_migration_entry() to use pmdp_huge_get_and_clear() in the softleaf case, but made no further adjustments to the function itself. Therefore this function continues to incorrectly use pmd_write(), pmd_soft_dirty() and pmd_uffd_wp() to determine whether the installed migration entry should be marked writable, softdirty or uffd-wp respectively. Whilst all are incorrect, the most problematic of these is pmd_write(), as this can lead to corrupted rmap state. On x86-64 _PAGE_SWP_SOFT_DIRTY is aliased to _PAGE_RW. So calling pmd_write() on a softleaf will return the softdirty state encoded in the entry, assuming CONFIG_MEM_SOFT_DIRTY was enabled. This was observed when running the hmm.hmm_device_private.anon_write_child selftest: 1. The test faults in a range then migrates it such that a device-private THP range is established. 2. The parent then migrates it to a device-private writable PMD entry whose folio is entirely AnonExclusive with entire_mapcount=1, softdirty set (accidentally correct write state). 3. The parent forks and the PMD entries are set to device-private read only entries, entire_mapcount=2, softdirty still set. 4. [BUG] The child writes to the range then migrates to RAM - intending to install non-writable migration entries - but replacing parent and child PMD mappings with WRITABLE entries due to misinterpreting the softdirty bit. 5. In remove_migration_pmd(), if !softleaf_is_migration_read(entry) we set the RMAP_EXCLUSIVE flag when calling folio_add_anon_rmap_pmd() for both parent and child, which are therefore AnonExclusive. 6. [SPLAT] Child sets migrated folio entire_mapcount=1, parent sets entire_mapcount=2 and we end up with an AnonExclusive folio with entire_mapcount=2! Assert fires in __folio_add_anon_rmap(): VM_WARN_ON_FOLIO(folio_test_large(folio) && folio_entire_mapcount(folio) > 1 && PageAnonExclusive(cur_page), folio) This patch fixes the issue by correctly referencing the softleaf entry fields for writable, softdirty and uffd-wp in set_pmd_migration_entry(). It also only updates A/D flags if the entry is present as these are otherwise not meaningful for a softleaf entry. This patch also flips the if (!present) { ... } else { ... } logic in set_pmd_migration_entry() so it is easier to understand, and adds some comments to make things clearer. I was able to bisect this to commit 775465fd26a3 ("lib/test_hmm: add zone device private THP test infrastructure") which first exposes this bug as it was the commit that permitted test_hmm to generate the test. However commit 65edfda6f3f2 ("mm/rmap: extend rmap and migration support device-private entries") is the commit that actually enabled this behaviour. Link: https://lore.kernel.org/20260601083044.57132-1-ljs@kernel.org Fixes: 65edfda6f3f2 ("mm/rmap: extend rmap and migration support device-private entries") Signed-off-by: Lorenzo Stoakes Acked-by: David Hildenbrand (Arm) Reviewed-by: Dev Jain Reviewed-by: Balbir Singh Reviewed-by: Baolin Wang Reviewed-by: Oscar Salvador (SUSE) Reviewed-by: Barry Song Reviewed-by: Lance Yang Reviewed-by: Zi Yan Cc: Baolin Wang Cc: Liam R. Howlett Cc: Nico Pache Cc: Ryan Roberts Cc: SeongJae Park Cc: Wei Yang Cc: Signed-off-by: Andrew Morton --- mm/huge_memory.c | 45 +++++++++++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 653f2dc03403..b118bcd392cb 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -4983,7 +4983,7 @@ int set_pmd_migration_entry(struct page_vma_mapped_walk *pvmw, struct vm_area_struct *vma = pvmw->vma; struct mm_struct *mm = vma->vm_mm; unsigned long address = pvmw->address; - bool anon_exclusive; + bool anon_exclusive, present, writable, softdirty, uffd_wp; pmd_t pmdval; swp_entry_t entry; pmd_t pmdswp; @@ -4991,12 +4991,26 @@ int set_pmd_migration_entry(struct page_vma_mapped_walk *pvmw, if (!(pvmw->pmd && !pvmw->pte)) return 0; - flush_cache_range(vma, address, address + HPAGE_PMD_SIZE); - if (unlikely(!pmd_present(*pvmw->pmd))) - pmdval = pmdp_huge_get_and_clear(vma->vm_mm, address, pvmw->pmd); - else + present = pmd_present(*pvmw->pmd); + if (likely(present)) { + flush_cache_range(vma, address, address + HPAGE_PMD_SIZE); + pmdval = pmdp_invalidate(vma, address, pvmw->pmd); + writable = pmd_write(pmdval); + softdirty = pmd_soft_dirty(pmdval); + uffd_wp = pmd_uffd_wp(pmdval); + } else { + softleaf_t old_entry; + + pmdval = pmdp_huge_get_and_clear(vma->vm_mm, address, pvmw->pmd); + old_entry = softleaf_from_pmd(pmdval); + + writable = softleaf_is_device_private_write(old_entry); + softdirty = pmd_swp_soft_dirty(pmdval); + uffd_wp = pmd_swp_uffd_wp(pmdval); + } + /* See folio_try_share_anon_rmap_pmd(): invalidate PMD first. */ anon_exclusive = folio_test_anon(folio) && PageAnonExclusive(page); if (anon_exclusive && folio_try_share_anon_rmap_pmd(folio, page)) { @@ -5004,24 +5018,31 @@ int set_pmd_migration_entry(struct page_vma_mapped_walk *pvmw, return -EBUSY; } - if (pmd_dirty(pmdval)) - folio_mark_dirty(folio); - if (pmd_write(pmdval)) + /* Determine type of migration entry. */ + if (writable) entry = make_writable_migration_entry(page_to_pfn(page)); else if (anon_exclusive) entry = make_readable_exclusive_migration_entry(page_to_pfn(page)); else entry = make_readable_migration_entry(page_to_pfn(page)); - if (pmd_young(pmdval)) + + /* Set A/D bits as necessary. */ + if (present && pmd_young(pmdval)) entry = make_migration_entry_young(entry); - if (pmd_dirty(pmdval)) + if (present && pmd_dirty(pmdval)) { + folio_mark_dirty(folio); entry = make_migration_entry_dirty(entry); + } + + /* Set PMD. */ pmdswp = swp_entry_to_pmd(entry); - if (pmd_soft_dirty(pmdval)) + if (softdirty) pmdswp = pmd_swp_mksoft_dirty(pmdswp); - if (pmd_uffd_wp(pmdval)) + if (uffd_wp) pmdswp = pmd_swp_mkuffd_wp(pmdswp); set_pmd_at(mm, address, pvmw->pmd, pmdswp); + + /* Migration entry installed: cleanup rmap, folio. */ folio_remove_rmap_pmd(folio, page, vma); folio_put(folio); trace_set_migration_pmd(address, pmd_val(pmdswp)); From 98733f3f0becb1ae0701d021c1748e974e5fa55c Mon Sep 17 00:00:00 2001 From: Shakeel Butt Date: Mon, 1 Jun 2026 09:15:01 -0700 Subject: [PATCH 139/257] mm/list_lru: drain before clearing xarray entry on reparent memcg_reparent_list_lrus() clears the dying memcg's xarray entry with xas_store(&xas, NULL) before reparenting its per-node lists into the parent. This opens a window where a concurrent list_lru_del() arriving for the dying memcg sees xa_load() == NULL, walks to the parent in lock_list_lru_of_memcg(), takes the parent's per-node lock, and calls list_del_init() on an item still physically linked on the dying memcg's list. If another in-flight thread holds the dying memcg's per-node lock at the same moment (another list_lru_del, or a list_lru_walk_one running an isolate callback), both threads modify ->next/->prev pointers on the same physical list under different locks. Adjacent items can corrupt each other's links. Fix it by reversing the order: reparent each per-node list and mark the child's list lru dead and then clear the xarray entry. Any concurrent list_lru op that finds the still-set xarray entry either takes the dying memcg's per-node lock (synchronizing with the drain) or sees LONG_MIN and walks to the parent, where the items now live. Link: https://lore.kernel.org/20260601161501.1444829-1-shakeel.butt@linux.dev Fixes: fb56fdf8b9a2 ("mm/list_lru: split the lock to per-cgroup scope") Signed-off-by: Shakeel Butt Reported-by: Chris Mason Reviewed-by: Kairui Song Acked-by: Muchun Song Cc: Dave Chinner Cc: Johannes Weiner Cc: Roman Gushchin Cc: Signed-off-by: Andrew Morton --- mm/list_lru.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/mm/list_lru.c b/mm/list_lru.c index dd29bcf8eb5f..9bf7f524796b 100644 --- a/mm/list_lru.c +++ b/mm/list_lru.c @@ -473,26 +473,29 @@ void memcg_reparent_list_lrus(struct mem_cgroup *memcg, struct mem_cgroup *paren mutex_lock(&list_lrus_mutex); list_for_each_entry(lru, &memcg_list_lrus, list) { struct list_lru_memcg *mlru; - XA_STATE(xas, &lru->xa, memcg->kmemcg_id); /* - * Lock the Xarray to ensure no on going list_lru_memcg - * allocation and further allocation will see css_is_dying(). + * css_is_dying() check in memcg_list_lru_alloc() avoids + * allocating a new mlru since CSS_DYING is already set for this + * memcg a rcu grace period ago. */ - xas_lock_irq(&xas); - mlru = xas_store(&xas, NULL); - xas_unlock_irq(&xas); + mlru = xa_load(&lru->xa, memcg->kmemcg_id); if (!mlru) continue; /* - * With Xarray value set to NULL, holding the lru lock below - * prevents list_lru_{add,del,isolate} from touching the lru, - * safe to reparent. + * Reparent each per-node list and mark the child dead + * (LONG_MIN) before clearing xarray entry otherwise a + * concurrent list_lru_del() may corrupt the list if it arrives + * after xarray clear but before reparenting as + * lock_list_lru_of_memcg will acquire parent's lock while the + * item is still on child's list. */ for_each_node(i) memcg_reparent_list_lru_one(lru, i, &mlru->node[i], parent); + xa_erase_irq(&lru->xa, memcg->kmemcg_id); + /* * Here all list_lrus corresponding to the cgroup are guaranteed * to remain empty, we can safely free this lru, any further From c594b83457ccdee76d458416fb3bc9348a37592f Mon Sep 17 00:00:00 2001 From: Alistair Popple Date: Thu, 21 May 2026 13:27:30 +1000 Subject: [PATCH 140/257] arm64: mm: call pagetable dtor when freeing hot-removed page tables Since 5e8eb9aeeda3 ("arm64: mm: always call PTE/PMD ctor in __create_pgd_mapping()") page-table allocation on ARM64 always calls pagetable_{pte,pmd,pud,p4d}_ctor(). This sets the page_type to PGTY_table, increments NR_PAGETABLE and possible allocates a PTL. However the matching pagetable_dtor() calls were never added. With DEBUG_VM enabled on kernel versions prior to v6.17 without 2dfcd1608f3a9 ("mm/page_alloc: let page freeing clear any set page type") this leads to the following warning when freeing these pages due to page->page_type sharing page->_mapcount: BUG: Bad page state in process ... pfn:284fbb page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x284fbb flags: 0x17fffc000000000(node=0|zone=2|lastcpupid=0x1ffff) page_type: f2(table) page dumped because: nonzero mapcount Call trace: bad_page+0x13c/0x160 __free_frozen_pages+0x6cc/0x860 ___free_pages+0xf4/0x180 free_pages+0x54/0x80 free_hotplug_page_range.part.0+0x58/0x90 free_empty_tables+0x438/0x500 __remove_pgd_mapping.constprop.0+0x60/0xa8 arch_remove_memory+0x48/0x80 try_remove_memory+0x158/0x1d8 offline_and_remove_memory+0x138/0x180 It can also lead to leaking the ptl allocation if ALLOC_SPLIT_PTLOCKS is defined and incorrect NR_PAGETABLE stats. Fix this by calling pagetable_dtor() in free_hotplug_pgtable_page() prior to freeing the page to undo the effects of calling pagetable_*_ctor(). Link: https://lore.kernel.org/20260521032730.2104017-1-apopple@nvidia.com Fixes: 5e8eb9aeeda3 ("arm64: mm: always call PTE/PMD ctor in __create_pgd_mapping()") Signed-off-by: Alistair Popple Cc: Catalin Marinas Cc: David Hildenbrand Cc: Will Deacon Cc: Signed-off-by: Andrew Morton --- arch/arm64/mm/mmu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c index dd85e093ffdb..8242f93f05e4 100644 --- a/arch/arm64/mm/mmu.c +++ b/arch/arm64/mm/mmu.c @@ -1441,6 +1441,7 @@ static void free_hotplug_page_range(struct page *page, size_t size, static void free_hotplug_pgtable_page(struct page *page) { + pagetable_dtor(page_ptdesc(page)); free_hotplug_page_range(page, PAGE_SIZE, NULL); } From 0c25b8734367574e21aeb8468c2e522713134da7 Mon Sep 17 00:00:00 2001 From: Usama Arif Date: Tue, 2 Jun 2026 10:22:47 -0700 Subject: [PATCH 141/257] mm/mincore: handle non-swap entries before !CONFIG_SWAP guard mincore_swap() also fields migration/hwpoison entries (and shmem swapin-error entries), which can exist on !CONFIG_SWAP builds when CONFIG_MIGRATION or CONFIG_MEMORY_FAILURE is enabled. The !IS_ENABLED(CONFIG_SWAP) guard ran before the non-swap-entry early return, so mincore_pte_range() can spuriously WARN and report these pages nonresident on !CONFIG_SWAP kernels. Move the guard below the non-swap-entry check so only true swap entries trip the WARN, and migration/hwpoison entries take the existing "uptodate / non-shmem" path. Link: https://lore.kernel.org/20260602172247.279421-1-usama.arif@linux.dev Fixes: 1f2052755c15 ("mm/mincore: use a helper for checking the swap cache") Signed-off-by: Usama Arif Reviewed-by: Pedro Falcato Reviewed-by: Kairui Song Reviewed-by: Lorenzo Stoakes Acked-by: Johannes Weiner Cc: Baoquan He Cc: Chris Li Cc: Jann Horn Cc: Liam R. Howlett Cc: Rik van Riel Cc: Shakeel Butt Cc: Vlastimil Babka Cc: Signed-off-by: Andrew Morton --- mm/mincore.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/mm/mincore.c b/mm/mincore.c index e5d13eea9234..296f2e3922b5 100644 --- a/mm/mincore.c +++ b/mm/mincore.c @@ -64,11 +64,6 @@ static unsigned char mincore_swap(swp_entry_t entry, bool shmem) struct folio *folio = NULL; unsigned char present = 0; - if (!IS_ENABLED(CONFIG_SWAP)) { - WARN_ON(1); - return 0; - } - /* * Shmem mapping may contain swapin error entries, which are * absent. Page table may contain migration or hwpoison @@ -77,6 +72,11 @@ static unsigned char mincore_swap(swp_entry_t entry, bool shmem) if (!softleaf_is_swap(entry)) return !shmem; + if (!IS_ENABLED(CONFIG_SWAP)) { + WARN_ON(1); + return 0; + } + /* * Shmem mapping lookup is lockless, so we need to grab the swap * device. mincore page table walk locks the PTL, and the swap From 40ab6644b99685755f740b872c00ef40d9aa870e Mon Sep 17 00:00:00 2001 From: Jann Horn Date: Wed, 3 Jun 2026 21:31:57 +0200 Subject: [PATCH 142/257] fhandle: fix UAF due to unlocked ->mnt_ns read in may_decode_fh() may_decode_fh() accesses mount::mnt_ns without holding any locks; that means the mount can concurrently be unmounted, and the mnt_namespace can concurrently be freed after an RCU grace period. This race can happens as follows, assuming that the mount point was created by open_tree(..., OPEN_TREE_CLONE): thread 1 thread 2 RCU __do_sys_open_by_handle_at do_handle_open handle_to_path may_decode_fh is_mounted [mount::mnt_ns access] [mount::mnt_ns access] __do_sys_close fput_close_sync __fput dissolve_on_fput umount_tree class_namespace_excl_destructor namespace_unlock free_mnt_ns mnt_ns_tree_remove call_rcu(mnt_ns_release_rcu) mnt_ns_release_rcu mnt_ns_release kfree [mnt_namespace::user_ns access] **UAF** Fix it by taking rcu_read_lock() around the mount::mnt_ns access, like in __prepend_path(). Additionally, document the semantics of mount::mnt_ns, and use WRITE_ONCE() for writers that can race with lockless readers. This bug is unreachable unless one of the following is set: - CONFIG_PREEMPTION - CONFIG_RCU_STRICT_GRACE_PERIOD because it requires an RCU grace period to happen during a syscall without an explicit preemption. This doesn't seem to have interesting security impact; worst-case, it could leak the result of an integer comparison to userspace (from the level check in cap_capable()), cause an endless loop, or crash the kernel by dereferencing an invalid address. Fixes: 620c266f3949 ("fhandle: relax open_by_handle_at() permission checks") Cc: stable@vger.kernel.org Signed-off-by: Jann Horn Link: https://patch.msgid.link/20260603-vfs-fhandle-uaf-fix-v2-1-d05db76a5084@google.com Signed-off-by: Christian Brauner (Amutable) --- fs/fhandle.c | 16 ++++++++++++++-- fs/mount.h | 10 +++++++++- fs/namespace.c | 6 +++--- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/fs/fhandle.c b/fs/fhandle.c index 642e3d569497..1ca7eb3a6cb5 100644 --- a/fs/fhandle.c +++ b/fs/fhandle.c @@ -285,6 +285,19 @@ static int do_handle_to_path(struct file_handle *handle, struct path *path, return 0; } +static bool capable_wrt_mount(struct mount *mount) +{ + struct mnt_namespace *mnt_ns; + + /* + * For ->mnt_ns access. + * The following READ_ONCE() is semantically rcu_dereference(). + */ + guard(rcu)(); + mnt_ns = READ_ONCE(mount->mnt_ns); + return ns_capable(mnt_ns->user_ns, CAP_SYS_ADMIN); +} + static inline int may_decode_fh(struct handle_to_path_ctx *ctx, unsigned int o_flags) { @@ -320,8 +333,7 @@ static inline int may_decode_fh(struct handle_to_path_ctx *ctx, if (ns_capable(root->mnt->mnt_sb->s_user_ns, CAP_SYS_ADMIN)) ctx->flags = HANDLE_CHECK_PERMS; else if (is_mounted(root->mnt) && - ns_capable(real_mount(root->mnt)->mnt_ns->user_ns, - CAP_SYS_ADMIN) && + capable_wrt_mount(real_mount(root->mnt)) && !has_locked_children(real_mount(root->mnt), root->dentry)) ctx->flags = HANDLE_CHECK_PERMS | HANDLE_CHECK_SUBTREE; else diff --git a/fs/mount.h b/fs/mount.h index e0816c11a198..5c120f8361bd 100644 --- a/fs/mount.h +++ b/fs/mount.h @@ -71,7 +71,15 @@ struct mount { struct hlist_head mnt_slave_list;/* list of slave mounts */ struct hlist_node mnt_slave; /* slave list entry */ struct mount *mnt_master; /* slave is on master->mnt_slave_list */ - struct mnt_namespace *mnt_ns; /* containing namespace */ + /* + * Containing namespace (active or deactivating, non-refcounted). + * Normally protected by namespace_sem. + * Can also be accessed locklessly under RCU. RCU readers can't rely on + * the namespace still being active, but implicitly hold a passive + * reference (because an RCU delay happens between a namespace being + * deactivated and the corresponding passive refcount drop). + */ + struct mnt_namespace *mnt_ns; struct mountpoint *mnt_mp; /* where is it mounted */ union { struct hlist_node mnt_mp_list; /* list mounts with the same mountpoint */ diff --git a/fs/namespace.c b/fs/namespace.c index fe919abd2f01..f5905f4ec560 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -1079,7 +1079,7 @@ static void mnt_add_to_ns(struct mnt_namespace *ns, struct mount *mnt) bool mnt_first_node = true, mnt_last_node = true; WARN_ON(mnt_ns_attached(mnt)); - mnt->mnt_ns = ns; + WRITE_ONCE(mnt->mnt_ns, ns); while (*link) { parent = *link; if (mnt->mnt_id_unique < node_to_mount(parent)->mnt_id_unique) { @@ -1434,7 +1434,7 @@ EXPORT_SYMBOL(mntget); void mnt_make_shortterm(struct vfsmount *mnt) { if (mnt) - real_mount(mnt)->mnt_ns = NULL; + WRITE_ONCE(real_mount(mnt)->mnt_ns, NULL); } /** @@ -1806,7 +1806,7 @@ static void umount_tree(struct mount *mnt, enum umount_tree_flags how) ns->nr_mounts--; __touch_mnt_namespace(ns); } - p->mnt_ns = NULL; + WRITE_ONCE(p->mnt_ns, NULL); if (how & UMOUNT_SYNC) p->mnt.mnt_flags |= MNT_SYNC_UMOUNT; From 2eea7f44b9c8b42fd7d3a1a87c06a7cd1b99c327 Mon Sep 17 00:00:00 2001 From: Joanne Koong Date: Wed, 3 Jun 2026 18:18:58 -0700 Subject: [PATCH 143/257] iomap: avoid potential null folio->mapping deref during error reporting When a buffered read fails, iomap_finish_folio_read() reports the error with fserror_report_io(folio->mapping->host, ...). This is called after ifs->read_bytes_pending has been decremented by the bytes attempted to be read. For a folio split across multiple read completions, the folio is only guaranteed to stay locked while read_bytes_pending > 0. Once iomap_finish_folio_read() decrements read_bytes_pending, another in-flight read can complete and end the read on the folio, which unlocks it. This allows truncate logic to run and detach the folio (set folio->mapping to NULL). The error reporting path then can dereference a NULL folio->mapping. As reported by Sam Sun, this is the race that can occur: CPU0: failed completion CPU1: final completion CPU2: truncate ----------------------- ---------------------- -------------- read_bytes_pending -= len finished = false /* preempted before fserror_report_io() */ read_bytes_pending -= len finished = true folio_end_read() truncate clears folio->mapping fserror_report_io( folio->mapping->host, ...) ^ NULL deref Fix this by reporting the error first before decrementing ifs->read_bytes_pending. Fixes: a9d573ee88af ("iomap: report file I/O errors to the VFS") Cc: stable@vger.kernel.org Reported-by: Sam Sun Closes: https://lore.kernel.org/linux-fsdevel/CAEkJfYPhWdd59RKmuNLJg-bkypHz7xiOwaWyNVu3A8CUqQCnvg@mail.gmail.com/ Signed-off-by: Joanne Koong Link: https://patch.msgid.link/20260604011858.2297561-1-joannelkoong@gmail.com Reviewed-by: "Darrick J. Wong" Signed-off-by: Christian Brauner (Amutable) --- fs/iomap/buffered-io.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/fs/iomap/buffered-io.c b/fs/iomap/buffered-io.c index d7b648421a70..d55b936e6986 100644 --- a/fs/iomap/buffered-io.c +++ b/fs/iomap/buffered-io.c @@ -400,6 +400,11 @@ void iomap_finish_folio_read(struct folio *folio, size_t off, size_t len, bool uptodate = !error; bool finished = true; + if (error) + fserror_report_io(folio->mapping->host, FSERR_BUFFERED_READ, + folio_pos(folio) + off, len, error, + GFP_ATOMIC); + if (ifs) { unsigned long flags; @@ -411,11 +416,6 @@ void iomap_finish_folio_read(struct folio *folio, size_t off, size_t len, spin_unlock_irqrestore(&ifs->state_lock, flags); } - if (error) - fserror_report_io(folio->mapping->host, FSERR_BUFFERED_READ, - folio_pos(folio) + off, len, error, - GFP_ATOMIC); - if (finished) folio_end_read(folio, uptodate); } From e9e41d3035032ed6053d8bad7b7077e1cb3a6540 Mon Sep 17 00:00:00 2001 From: Tomas Glozar Date: Tue, 2 Jun 2026 14:55:06 +0200 Subject: [PATCH 144/257] rtla: Fix parsing of multi-character short options A bug was reported where the parsing of multi-character short options, be it a short option with an argument specified without space (e.g. "-p100") or multiple short options in one argument (e.g. -un), ignores options specific to individual tools. Furthermore, if the rest of the option is supposed to be an argument, it gets reinterpreted as a string of options. For example, -p100 gets interpreted as -100, which is due to hackish implementation read as --no-thread --no-irq --no-irq with timerlat hist, causing rtla to error out: $ rtla timerlat hist -p100 no-irq and no-thread set, there is nothing to do here This behavior is caused by getopt_long() being called twice on each argument, once in common_parse_options(), once in [tool]_parse_args(): - common_parse_options() calls getopt_long() with an array of options common for all rtla tools, while suppressing errors (opterr = 0). - If the option fails to parse, common_parse_options() returns 0. - If 0 is returned from common_parse_options(), [tool]_parse_args() calls getopt_long() again, with its own set of options. * [tool] means one of {osnoise,timerlat}_{top,hist} At least in glibc, getopt_long() increments its internal nextchar variable even if the option is not recognized. That means that in the case of "-p100", common_parse_options() sets nextchar pointing to '1', and timerlat_hist_parse_args() sees '1', not 'p'; the same then repeats for the first and second '0'. As there is no way to restore the correct internal state of getopt_long() reliably, fix the issue by merging the common options back to the longopt array and option string of the [tool]_parse_args() functions using a macro; only the switch part is left in the original function, which is renamed to set_common_option(). Fixes: 850cd24cb6d6 ("tools/rtla: Add common_parse_options()") Reported-by: John Kacur Tested-by: John Kacur Link: https://lore.kernel.org/r/20260602125506.3325345-1-tglozar@redhat.com Signed-off-by: Tomas Glozar --- tools/tracing/rtla/src/common.c | 28 +++++--------------------- tools/tracing/rtla/src/common.h | 12 ++++++++++- tools/tracing/rtla/src/osnoise_hist.c | 7 ++++--- tools/tracing/rtla/src/osnoise_top.c | 7 ++++--- tools/tracing/rtla/src/timerlat_hist.c | 7 ++++--- tools/tracing/rtla/src/timerlat_top.c | 7 ++++--- 6 files changed, 32 insertions(+), 36 deletions(-) diff --git a/tools/tracing/rtla/src/common.c b/tools/tracing/rtla/src/common.c index 35e3d3aa922e..bc9d01ddd102 100644 --- a/tools/tracing/rtla/src/common.c +++ b/tools/tracing/rtla/src/common.c @@ -84,37 +84,20 @@ int getopt_auto(int argc, char **argv, const struct option *long_opts) } /* - * common_parse_options - parse common command line options + * set_common_option - set common options * + * @c: option character * @argc: argument count * @argv: argument vector * @common: common parameters structure * * Parse command line options that are common to all rtla tools. * - * Returns: non zero if a common option was parsed, or 0 - * if the option should be handled by tool-specific parsing. + * Returns: 1 if the option was set, 0 otherwise. */ -int common_parse_options(int argc, char **argv, struct common_params *common) +int set_common_option(int c, int argc, char **argv, struct common_params *common) { struct trace_events *tevent; - int saved_state = optind; - int c; - - static struct option long_options[] = { - {"cpus", required_argument, 0, 'c'}, - {"cgroup", optional_argument, 0, 'C'}, - {"debug", no_argument, 0, 'D'}, - {"duration", required_argument, 0, 'd'}, - {"event", required_argument, 0, 'e'}, - {"house-keeping", required_argument, 0, 'H'}, - {"priority", required_argument, 0, 'P'}, - {0, 0, 0, 0} - }; - - opterr = 0; - c = getopt_auto(argc, argv, long_options); - opterr = 1; switch (c) { case 'c': @@ -154,11 +137,10 @@ int common_parse_options(int argc, char **argv, struct common_params *common) common->set_sched = 1; break; default: - optind = saved_state; return 0; } - return c; + return 1; } /* diff --git a/tools/tracing/rtla/src/common.h b/tools/tracing/rtla/src/common.h index 51665db4ffce..8921807bda98 100644 --- a/tools/tracing/rtla/src/common.h +++ b/tools/tracing/rtla/src/common.h @@ -178,7 +178,17 @@ int osnoise_set_stop_total_us(struct osnoise_context *context, long long stop_total_us); int getopt_auto(int argc, char **argv, const struct option *long_opts); -int common_parse_options(int argc, char **argv, struct common_params *common); + +#define COMMON_OPTIONS \ + {"cpus", required_argument, 0, 'c'},\ + {"cgroup", optional_argument, 0, 'C'},\ + {"debug", no_argument, 0, 'D'},\ + {"duration", required_argument, 0, 'd'},\ + {"event", required_argument, 0, 'e'},\ + {"house-keeping", required_argument, 0, 'H'},\ + {"priority", required_argument, 0, 'P'} +int set_common_option(int c, int argc, char **argv, struct common_params *common); + int common_apply_config(struct osnoise_tool *tool, struct common_params *params); int top_main_loop(struct osnoise_tool *tool); int hist_main_loop(struct osnoise_tool *tool); diff --git a/tools/tracing/rtla/src/osnoise_hist.c b/tools/tracing/rtla/src/osnoise_hist.c index 8ad816b80265..cb4ce58c5987 100644 --- a/tools/tracing/rtla/src/osnoise_hist.c +++ b/tools/tracing/rtla/src/osnoise_hist.c @@ -475,6 +475,7 @@ static struct common_params while (1) { static struct option long_options[] = { + COMMON_OPTIONS, {"auto", required_argument, 0, 'a'}, {"bucket-size", required_argument, 0, 'b'}, {"entries", required_argument, 0, 'E'}, @@ -498,15 +499,15 @@ static struct common_params {0, 0, 0, 0} }; - if (common_parse_options(argc, argv, ¶ms->common)) - continue; - c = getopt_auto(argc, argv, long_options); /* detect the end of the options. */ if (c == -1) break; + if (set_common_option(c, argc, argv, ¶ms->common)) + continue; + switch (c) { case 'a': /* set sample stop to auto_thresh */ diff --git a/tools/tracing/rtla/src/osnoise_top.c b/tools/tracing/rtla/src/osnoise_top.c index 244bdce022ad..e65312ec26c4 100644 --- a/tools/tracing/rtla/src/osnoise_top.c +++ b/tools/tracing/rtla/src/osnoise_top.c @@ -328,6 +328,7 @@ struct common_params *osnoise_top_parse_args(int argc, char **argv) while (1) { static struct option long_options[] = { + COMMON_OPTIONS, {"auto", required_argument, 0, 'a'}, {"help", no_argument, 0, 'h'}, {"period", required_argument, 0, 'p'}, @@ -346,15 +347,15 @@ struct common_params *osnoise_top_parse_args(int argc, char **argv) {0, 0, 0, 0} }; - if (common_parse_options(argc, argv, ¶ms->common)) - continue; - c = getopt_auto(argc, argv, long_options); /* Detect the end of the options. */ if (c == -1) break; + if (set_common_option(c, argc, argv, ¶ms->common)) + continue; + switch (c) { case 'a': /* set sample stop to auto_thresh */ diff --git a/tools/tracing/rtla/src/timerlat_hist.c b/tools/tracing/rtla/src/timerlat_hist.c index 79142af4f566..4b6708e333b8 100644 --- a/tools/tracing/rtla/src/timerlat_hist.c +++ b/tools/tracing/rtla/src/timerlat_hist.c @@ -785,6 +785,7 @@ static struct common_params while (1) { static struct option long_options[] = { + COMMON_OPTIONS, {"auto", required_argument, 0, 'a'}, {"bucket-size", required_argument, 0, 'b'}, {"entries", required_argument, 0, 'E'}, @@ -819,11 +820,11 @@ static struct common_params {0, 0, 0, 0} }; - if (common_parse_options(argc, argv, ¶ms->common)) - continue; - c = getopt_auto(argc, argv, long_options); + if (set_common_option(c, argc, argv, ¶ms->common)) + continue; + /* detect the end of the options. */ if (c == -1) break; diff --git a/tools/tracing/rtla/src/timerlat_top.c b/tools/tracing/rtla/src/timerlat_top.c index 64cbdcc878b0..91f88bbebad9 100644 --- a/tools/tracing/rtla/src/timerlat_top.c +++ b/tools/tracing/rtla/src/timerlat_top.c @@ -549,6 +549,7 @@ static struct common_params while (1) { static struct option long_options[] = { + COMMON_OPTIONS, {"auto", required_argument, 0, 'a'}, {"help", no_argument, 0, 'h'}, {"irq", required_argument, 0, 'i'}, @@ -577,11 +578,11 @@ static struct common_params {0, 0, 0, 0} }; - if (common_parse_options(argc, argv, ¶ms->common)) - continue; - c = getopt_auto(argc, argv, long_options); + if (set_common_option(c, argc, argv, ¶ms->common)) + continue; + /* detect the end of the options. */ if (c == -1) break; From 7f2d76c9c03257c0782afef9d95321fa04096f60 Mon Sep 17 00:00:00 2001 From: Sanghyun Park Date: Tue, 2 Jun 2026 18:49:05 +0900 Subject: [PATCH 145/257] xfrm: policy: fix use-after-free on inexact bin in xfrm_policy_bysel_ctx() Fix the race by pruning the bin while still holding xfrm_policy_lock, before dropping it. Use __xfrm_policy_inexact_prune_bin() directly since the lock is already held. The wrapper xfrm_policy_inexact_prune_bin() becomes unused and is removed. Race: CPU0 (XFRM_MSG_DELPOLICY) CPU1 (XFRM_MSG_NEWSPDINFO) ========================== ========================== xfrm_policy_bysel_ctx(): spin_lock_bh(xfrm_policy_lock) bin = xfrm_policy_inexact_lookup() __xfrm_policy_unlink(pol) spin_unlock_bh(xfrm_policy_lock) xfrm_policy_kill(ret) // wide window, lock not held xfrm_hash_rebuild(): spin_lock_bh(xfrm_policy_lock) __xfrm_policy_inexact_flush(): kfree_rcu(bin) // bin freed spin_unlock_bh(xfrm_policy_lock) xfrm_policy_inexact_prune_bin(bin) // UAF: bin is freed Fixes: 6be3b0db6db8 ("xfrm: policy: add inexact policy search tree infrastructure") Signed-off-by: Sanghyun Park Signed-off-by: Steffen Klassert --- net/xfrm/xfrm_policy.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index dd09d2063da2..959544425692 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -1156,15 +1156,6 @@ static void __xfrm_policy_inexact_prune_bin(struct xfrm_pol_inexact_bin *b, bool } } -static void xfrm_policy_inexact_prune_bin(struct xfrm_pol_inexact_bin *b) -{ - struct net *net = read_pnet(&b->k.net); - - spin_lock_bh(&net->xfrm.xfrm_policy_lock); - __xfrm_policy_inexact_prune_bin(b, false); - spin_unlock_bh(&net->xfrm.xfrm_policy_lock); -} - static void __xfrm_policy_inexact_flush(struct net *net) { struct xfrm_pol_inexact_bin *bin, *t; @@ -1707,12 +1698,12 @@ xfrm_policy_bysel_ctx(struct net *net, const struct xfrm_mark *mark, u32 if_id, } ret = pol; } + if (bin && delete) + __xfrm_policy_inexact_prune_bin(bin, false); spin_unlock_bh(&net->xfrm.xfrm_policy_lock); if (ret && delete) xfrm_policy_kill(ret); - if (bin && delete) - xfrm_policy_inexact_prune_bin(bin); return ret; } EXPORT_SYMBOL(xfrm_policy_bysel_ctx); From 4790af1cc2e8871fb31f28c66e42b9a949a23992 Mon Sep 17 00:00:00 2001 From: Chancel Liu Date: Mon, 1 Jun 2026 17:33:27 +0900 Subject: [PATCH 146/257] ASoC: fsl_sai: Fix 32 slots TDM broken by integer shift UB in xMR write When configuring 32 slots TDM (channels == slots == 32), the xMR (Mask Register) write used: ~0UL - ((1 << min(channels, slots)) - 1) The literal "1" is a signed 32-bit int. Shifting it by 32 positions is undefined behaviour which may set this register to 0xFFFFFFFF, masking all 32 slots. Use GENMASK_U32() macro instead. For 32 slots this produces a zero mask: ~GENMASK_U32(31, 0) = ~0xFFFFFFFF = 0x00000000 Behaviour for fewer than 32 slots is unchanged. Fixes: 770f58d7d2c5 ("ASoC: fsl_sai: Support multiple data channel enable bits") Cc: stable@vger.kernel.org Signed-off-by: Chancel Liu Reviewed-by: Shengjiu Wang Link: https://patch.msgid.link/20260601083327.1535185-1-chancel.liu@oss.nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_sai.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index e364552c1f47..78e953cd3e9f 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -793,7 +793,7 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, FSL_SAI_CR4_FSD_MSTR, FSL_SAI_CR4_FSD_MSTR); regmap_write(sai->regmap, FSL_SAI_xMR(tx), - ~0UL - ((1 << min(channels, slots)) - 1)); + ~GENMASK_U32(min(channels, slots) - 1, 0)); return 0; } From cad530a8371124da988608f11df30b7dd82391ef Mon Sep 17 00:00:00 2001 From: David Glushkov Date: Sun, 31 May 2026 23:45:12 +0200 Subject: [PATCH 147/257] ASoC: amd: yc: Add MSI Raider A18 HX A9WJG to quirk table The MSI Raider A18 HX A9WJG has an internal digital microphone connected through AMD ACP6x, but this machine does not expose the AcpDmicConnected ACPI property, so acp_yc_mach does not bind. Add a DMI quirk for this model. This was tested on an MSI Raider A18 HX A9WJG with board MS-182L, BIOS E182LAMS.31A, AMD ACP6x rev 0x62, and Realtek ALC274. After applying the quirk, the internal microphone appears as an acp6x DMIC capture device and records correctly. Signed-off-by: David Glushkov Link: https://patch.msgid.link/20260531214512.170716-1-david.glushkov@sntiq.com Signed-off-by: Mark Brown --- sound/soc/amd/yc/acp6x-mach.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c index a93a7fd4877e..b426cda529a9 100644 --- a/sound/soc/amd/yc/acp6x-mach.c +++ b/sound/soc/amd/yc/acp6x-mach.c @@ -521,6 +521,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Vector A16 HX A8WHG"), } }, + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Micro-Star International Co., Ltd."), + DMI_MATCH(DMI_PRODUCT_NAME, "Raider A18 HX A9WJG"), + } + }, { .driver_data = &acp6x_card, .matches = { From d2dcd85f9e09fe3566d4cdcd357856a42ac73f93 Mon Sep 17 00:00:00 2001 From: Aleksander Pshenitsyn Date: Sun, 31 May 2026 13:11:59 +0300 Subject: [PATCH 148/257] ASoC: amd: acp70: add standalone RT721 SoundWire machine The ASUS Vivobook 18 M1807GA (AMD ACP7.x, PCI 1022:15e2, subsystem 1043:3531) exposes a single Realtek RT721 SDCA codec on SoundWire link 1. The BIOS reports the ACP audio config flag as 0 (SoundWire mode), so snd_pci_ps claims the device, brings up the SoundWire managers and enumerates the RT721 peripheral (sdw:0:1:025d:0721:01); the rt721-sdca codec driver binds successfully. No sound card is created, however: acp63_sdw_machine_select() walks snd_soc_acpi_amd_acp70_sdw_machines[] and finds no entry whose declared SoundWire peripherals are all present on the bus. The only existing RT721 entry, acp70_rt721_l1u0_tas2783x2_l1u8b, additionally requires two TAS2783 amplifiers and deliberately exposes the RT721 as jack + DMIC only. This M1807GA variant has no external amplifiers - the RT721's internal AIF2 amplifier path drives the speakers - so that entry never matches and no machine device is registered. Add a standalone RT721 machine entry for link 1 exposing all three RT721 endpoints (jack/AIF1, speaker amplifier/AIF2, DMIC/AIF3), mirroring the standalone RT722 configuration. Place it after the TAS2783 combo entry so platforms that do have the external amplifiers continue to match the more specific entry first. ACPI _ADR of the codec: 0x000130025D072101 (link_id=1 version=3 mfg_id=0x025d Realtek part_id=0x0721 class=0x01). Verified on the hardware: with the entry present the amd_sdw machine binds, an "amd-soundwire" card is registered exposing the rt721-sdca AIF1 (SimpleJack) and AIF2 (SmartAmp) PCM devices, and audio plays out of the built-in speakers. Link: https://bugzilla.kernel.org/show_bug.cgi?id=221282 Signed-off-by: Aleksander Pshenitsyn Link: https://patch.msgid.link/20260531101159.14241-1-brains.fatman@gmail.com Signed-off-by: Mark Brown --- sound/soc/amd/acp/amd-acp70-acpi-match.c | 44 ++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/sound/soc/amd/acp/amd-acp70-acpi-match.c b/sound/soc/amd/acp/amd-acp70-acpi-match.c index 1ae43df5da6c..18f2918d4ada 100644 --- a/sound/soc/amd/acp/amd-acp70-acpi-match.c +++ b/sound/soc/amd/acp/amd-acp70-acpi-match.c @@ -619,6 +619,45 @@ static const struct snd_soc_acpi_link_adr acp70_rt721_l1u0_tas2783x2_l1u8b[] = { {} }; +static const struct snd_soc_acpi_endpoint rt721_endpoints[] = { + { /* Jack Playback/Capture Endpoint (AIF1) */ + .num = 0, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, + { /* Speaker Amplifier Endpoint (AIF2, internal amp) */ + .num = 1, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, + { /* DMIC Capture Endpoint (AIF3) */ + .num = 2, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, +}; + +static const struct snd_soc_acpi_adr_device rt721_1_single_adr[] = { + { + .adr = 0x000130025D072101ull, + .num_endpoints = ARRAY_SIZE(rt721_endpoints), + .endpoints = rt721_endpoints, + .name_prefix = "rt721" + } +}; + +static const struct snd_soc_acpi_link_adr acp70_rt721_only[] = { + { + .mask = BIT(1), + .num_adr = ARRAY_SIZE(rt721_1_single_adr), + .adr_d = rt721_1_single_adr, + }, + {} +}; + struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sdw_machines[] = { { .link_mask = BIT(0) | BIT(1), @@ -711,6 +750,11 @@ struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sdw_machines[] = { .links = acp70_rt721_l1u0_tas2783x2_l1u8b, .drv_name = "amd_sdw", }, + { + .link_mask = BIT(1), + .links = acp70_rt721_only, + .drv_name = "amd_sdw", + }, {}, }; EXPORT_SYMBOL(snd_soc_acpi_amd_acp70_sdw_machines); From d486b4934a8e504376b85cdb3766f306d57aff5b Mon Sep 17 00:00:00 2001 From: Amit Matityahu Date: Wed, 3 Jun 2026 17:01:39 +0000 Subject: [PATCH 149/257] timers/migration: Fix livelock in tmigr_handle_remote_up() tmigr_handle_remote_cpu() skips timer_expire_remote() when cpu == smp_processor_id(), assuming the local softirq path already handled this CPU's timers. This assumption is wrong because jiffies can advance after the handling of the CPU's global timers in run_timer_base(BASE_GLOBAL) and before tmigr_handle_remote() evaluates the expiry times. As a consequence a timer which expires after the CPU local timer wheel advanced and becomes expired in the remote handling is ignored and the callback is never invoked and removed from the timer wheel. What's worse is that fetch_next_timer_interrupt_remote() keeps reporting it as expired, and the event is re-queued with expires == now on each iteration. The goto-again loop spins indefinitely. Fix this by calling timer_expire_remote() unconditionally. That's minimal overhead for the common case as __run_timer_base() returns immediately if there is nothing to expire in the local wheel. [ tglx: Amend change log and add a comment ] Fixes: 7ee988770326 ("timers: Implement the hierarchical pull model") Reported-by: Alon Kariv Signed-off-by: Amit Matityahu Signed-off-by: Thomas Gleixner Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260603170139.33628-1-amitmat@amazon.com --- kernel/time/timer_migration.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/kernel/time/timer_migration.c b/kernel/time/timer_migration.c index 1d0d3a4058d5..52c15affdbff 100644 --- a/kernel/time/timer_migration.c +++ b/kernel/time/timer_migration.c @@ -978,8 +978,12 @@ static void tmigr_handle_remote_cpu(unsigned int cpu, u64 now, /* Drop the lock to allow the remote CPU to exit idle */ raw_spin_unlock_irq(&tmc->lock); - if (cpu != smp_processor_id()) - timer_expire_remote(cpu); + /* + * This can't exclude the local CPU because jiffies might have advanced + * after the timer softirq invoked run_timer_base(BASE_GLOBAL) and the + * point where the jiffies snapshot @jif was taken in tmigr_handle_remote(). + */ + timer_expire_remote(cpu); /* * Lock ordering needs to be preserved - timer_base locks before tmigr From fa7c84726dc217ce0c183926ef9411636c7a2213 Mon Sep 17 00:00:00 2001 From: Tangudu Tilak Tirumalesh Date: Wed, 3 Jun 2026 12:22:15 +0530 Subject: [PATCH 150/257] Revert "drm/xe: Skip exec queue schedule toggle if queue is idle during suspend" This reverts commit 8533051ce92015e9cc6f75e0d52119b9d91610b6. The idle-skip optimization bypasses GuC suspend, so the GPU may not perform the context switch that flushes TLB entries for invalidated userptr VMAs. In LR/preempt-fence VM mode, this can lead to missed TLB invalidation and page faults during userptr invalidation tests. Restore unconditional schedule toggling on suspend so the context-switch TLB flush is always performed. This optimization will be reintroduced with a fix that does not skip suspend in LR/preempt-fence VM mode. Fixes: 8533051ce920 ("drm/xe: Skip exec queue schedule toggle if queue is idle during suspend") Cc: stable@vger.kernel.org # v7.0+ Suggested-by: Thomas Hellstrom Signed-off-by: Tangudu Tilak Tirumalesh Reviewed-by: Thomas Hellstrom Signed-off-by: Daniele Ceraolo Spurio Link: https://patch.msgid.link/20260603065217.3131066-2-tilak.tirumalesh.tangudu@intel.com (cherry picked from commit 6a1e7934d9a6cf46aecae00a99c2603d1295e170) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/xe/xe_exec_queue.h | 17 -------- drivers/gpu/drm/xe/xe_guc_submit.c | 55 ++----------------------- drivers/gpu/drm/xe/xe_hw_engine_group.c | 10 +---- 3 files changed, 5 insertions(+), 77 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_exec_queue.h b/drivers/gpu/drm/xe/xe_exec_queue.h index a82d99bd77bc..0225426c57b0 100644 --- a/drivers/gpu/drm/xe/xe_exec_queue.h +++ b/drivers/gpu/drm/xe/xe_exec_queue.h @@ -162,21 +162,4 @@ int xe_exec_queue_contexts_hwsp_rebase(struct xe_exec_queue *q, void *scratch); struct xe_lrc *xe_exec_queue_lrc(struct xe_exec_queue *q); struct xe_lrc *xe_exec_queue_get_lrc(struct xe_exec_queue *q, u16 idx); -/** - * xe_exec_queue_idle_skip_suspend() - Can exec queue skip suspend - * @q: The exec_queue - * - * If an exec queue is not parallel and is idle, the suspend steps can be - * skipped in the submission backend immediatley signaling the suspend fence. - * Parallel queues cannot skip this step due to limitations in the submission - * backend. - * - * Return: True if exec queue is idle and can skip suspend steps, False - * otherwise - */ -static inline bool xe_exec_queue_idle_skip_suspend(struct xe_exec_queue *q) -{ - return !xe_exec_queue_is_parallel(q) && xe_exec_queue_is_idle(q); -} - #endif diff --git a/drivers/gpu/drm/xe/xe_guc_submit.c b/drivers/gpu/drm/xe/xe_guc_submit.c index 912182dc7704..3db627b56e11 100644 --- a/drivers/gpu/drm/xe/xe_guc_submit.c +++ b/drivers/gpu/drm/xe/xe_guc_submit.c @@ -71,7 +71,6 @@ exec_queue_to_guc(struct xe_exec_queue *q) #define EXEC_QUEUE_STATE_WEDGED (1 << 8) #define EXEC_QUEUE_STATE_BANNED (1 << 9) #define EXEC_QUEUE_STATE_PENDING_RESUME (1 << 10) -#define EXEC_QUEUE_STATE_IDLE_SKIP_SUSPEND (1 << 11) static bool exec_queue_registered(struct xe_exec_queue *q) { @@ -218,21 +217,6 @@ static void clear_exec_queue_pending_resume(struct xe_exec_queue *q) atomic_and(~EXEC_QUEUE_STATE_PENDING_RESUME, &q->guc->state); } -static bool exec_queue_idle_skip_suspend(struct xe_exec_queue *q) -{ - return atomic_read(&q->guc->state) & EXEC_QUEUE_STATE_IDLE_SKIP_SUSPEND; -} - -static void set_exec_queue_idle_skip_suspend(struct xe_exec_queue *q) -{ - atomic_or(EXEC_QUEUE_STATE_IDLE_SKIP_SUSPEND, &q->guc->state); -} - -static void clear_exec_queue_idle_skip_suspend(struct xe_exec_queue *q) -{ - atomic_and(~EXEC_QUEUE_STATE_IDLE_SKIP_SUSPEND, &q->guc->state); -} - static bool exec_queue_killed_or_banned_or_wedged(struct xe_exec_queue *q) { return (atomic_read(&q->guc->state) & @@ -1153,7 +1137,7 @@ static void submit_exec_queue(struct xe_exec_queue *q, struct xe_sched_job *job) if (!job->restore_replay || job->last_replay) { if (xe_exec_queue_is_parallel(q)) wq_item_append(q); - else if (!exec_queue_idle_skip_suspend(q)) + else xe_lrc_set_ring_tail(lrc, lrc->ring.tail); job->last_replay = false; } @@ -1810,10 +1794,9 @@ static void __guc_exec_queue_process_msg_suspend(struct xe_sched_msg *msg) { struct xe_exec_queue *q = msg->private_data; struct xe_guc *guc = exec_queue_to_guc(q); - bool idle_skip_suspend = xe_exec_queue_idle_skip_suspend(q); - if (!idle_skip_suspend && guc_exec_queue_allowed_to_change_state(q) && - !exec_queue_suspended(q) && exec_queue_enabled(q)) { + if (guc_exec_queue_allowed_to_change_state(q) && !exec_queue_suspended(q) && + exec_queue_enabled(q)) { wait_event(guc->ct.wq, vf_recovery(guc) || ((q->guc->resume_time != RESUME_PENDING || xe_guc_read_stopped(guc)) && !exec_queue_pending_disable(q))); @@ -1832,33 +1815,11 @@ static void __guc_exec_queue_process_msg_suspend(struct xe_sched_msg *msg) disable_scheduling(q, false); } } else if (q->guc->suspend_pending) { - if (idle_skip_suspend) - set_exec_queue_idle_skip_suspend(q); set_exec_queue_suspended(q); suspend_fence_signal(q); } } -static void sched_context(struct xe_exec_queue *q) -{ - struct xe_guc *guc = exec_queue_to_guc(q); - struct xe_lrc *lrc = q->lrc[0]; - u32 action[] = { - XE_GUC_ACTION_SCHED_CONTEXT, - q->guc->id, - }; - - xe_gt_assert(guc_to_gt(guc), !xe_exec_queue_is_parallel(q)); - xe_gt_assert(guc_to_gt(guc), !exec_queue_destroyed(q)); - xe_gt_assert(guc_to_gt(guc), exec_queue_registered(q)); - xe_gt_assert(guc_to_gt(guc), !exec_queue_pending_disable(q)); - - trace_xe_exec_queue_submit(q); - - xe_lrc_set_ring_tail(lrc, lrc->ring.tail); - xe_guc_ct_send(&guc->ct, action, ARRAY_SIZE(action), 0, 0); -} - static void __guc_exec_queue_process_msg_resume(struct xe_sched_msg *msg) { struct xe_exec_queue *q = msg->private_data; @@ -1866,22 +1827,12 @@ static void __guc_exec_queue_process_msg_resume(struct xe_sched_msg *msg) if (guc_exec_queue_allowed_to_change_state(q)) { clear_exec_queue_suspended(q); if (!exec_queue_enabled(q)) { - if (exec_queue_idle_skip_suspend(q)) { - struct xe_lrc *lrc = q->lrc[0]; - - clear_exec_queue_idle_skip_suspend(q); - xe_lrc_set_ring_tail(lrc, lrc->ring.tail); - } q->guc->resume_time = RESUME_PENDING; set_exec_queue_pending_resume(q); enable_scheduling(q); - } else if (exec_queue_idle_skip_suspend(q)) { - clear_exec_queue_idle_skip_suspend(q); - sched_context(q); } } else { clear_exec_queue_suspended(q); - clear_exec_queue_idle_skip_suspend(q); } } diff --git a/drivers/gpu/drm/xe/xe_hw_engine_group.c b/drivers/gpu/drm/xe/xe_hw_engine_group.c index 4c2b113364d3..02cf32ae5aa9 100644 --- a/drivers/gpu/drm/xe/xe_hw_engine_group.c +++ b/drivers/gpu/drm/xe/xe_hw_engine_group.c @@ -208,21 +208,15 @@ static int xe_hw_engine_group_suspend_faulting_lr_jobs(struct xe_hw_engine_group lockdep_assert_held_write(&group->mode_sem); list_for_each_entry(q, &group->exec_queue_list, hw_engine_group_link) { - bool idle_skip_suspend; if (!xe_vm_in_fault_mode(q->vm)) continue; - idle_skip_suspend = xe_exec_queue_idle_skip_suspend(q); - if (!idle_skip_suspend && has_deps) + if (has_deps) return -EAGAIN; xe_gt_stats_incr(q->gt, XE_GT_STATS_ID_HW_ENGINE_GROUP_SUSPEND_LR_QUEUE_COUNT, 1); - if (idle_skip_suspend) - xe_gt_stats_incr(q->gt, - XE_GT_STATS_ID_HW_ENGINE_GROUP_SKIP_LR_QUEUE_COUNT, 1); - - need_resume |= !idle_skip_suspend; + need_resume = true; q->ops->suspend(q); gt = q->gt; } From 54f2a0442a30fe7a0f6bc8345e81f8b2db8effbd Mon Sep 17 00:00:00 2001 From: Tangudu Tilak Tirumalesh Date: Wed, 3 Jun 2026 12:22:16 +0530 Subject: [PATCH 151/257] drm/xe: Clear pending_disable before signaling suspend fence In the schedule-disable done path for suspend, we signal the suspend fence before clearing pending_disable. That wakeup can let suspend_wait complete and resume be queued immediately. The resume path may then reach enable_scheduling() while pending_disable is still set and hit the !exec_queue_pending_disable(q) assertion. Fix this by clearing pending_disable before signaling the suspend fence, so any resumed transition observes a consistent state. Fixes: 87651f31ae4e ("drm/xe/guc_submit: fix race around suspend_pending") Cc: stable@vger.kernel.org # v7.0+ Signed-off-by: Tangudu Tilak Tirumalesh Reviewed-by: Thomas Hellstrom Signed-off-by: Daniele Ceraolo Spurio Link: https://patch.msgid.link/20260603065217.3131066-3-tilak.tirumalesh.tangudu@intel.com (cherry picked from commit 4b1ae138b0e103d753773956a84eebc2edbf62c4) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/xe/xe_guc_submit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/xe/xe_guc_submit.c b/drivers/gpu/drm/xe/xe_guc_submit.c index 3db627b56e11..3493dd533d6c 100644 --- a/drivers/gpu/drm/xe/xe_guc_submit.c +++ b/drivers/gpu/drm/xe/xe_guc_submit.c @@ -2804,8 +2804,8 @@ static void handle_sched_done(struct xe_guc *guc, struct xe_exec_queue *q, xe_gt_assert(guc_to_gt(guc), exec_queue_pending_disable(q)); if (q->guc->suspend_pending) { - suspend_fence_signal(q); clear_exec_queue_pending_disable(q); + suspend_fence_signal(q); } else { if (exec_queue_banned(q)) { smp_wmb(); From ec4cbdd163f9bb2a2bd44eb93ecf4a2fa0e912a9 Mon Sep 17 00:00:00 2001 From: Niranjana Vishwanathapura Date: Wed, 3 Jun 2026 16:39:47 -0700 Subject: [PATCH 152/257] drm/xe/multi_queue: skip submit when primary queue is suspended Return early in submit path when the multi-queue primary exec queue is suspended to avoid submitting while suspended. v2: Remove idle_skip_suspend fix as that feature is being reverted here https://patchwork.freedesktop.org/series/167262/ Fixes: bc5775c59258 ("drm/xe/multi_queue: Add GuC interface for multi queue support") Cc: stable@vger.kernel.org # v7.0+ Assisted-by: GitHub-Copilot:claude-sonnet-4.6 Reviewed-by: Daniele Ceraolo Spurio Signed-off-by: Niranjana Vishwanathapura Link: https://patch.msgid.link/20260603233946.863663-2-niranjana.vishwanathapura@intel.com (cherry picked from commit b7fb55cc3364ca128cfff9d50649ffd4327cd01e) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/xe/xe_guc_submit.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/xe/xe_guc_submit.c b/drivers/gpu/drm/xe/xe_guc_submit.c index 3493dd533d6c..a4a8f0d41fe8 100644 --- a/drivers/gpu/drm/xe/xe_guc_submit.c +++ b/drivers/gpu/drm/xe/xe_guc_submit.c @@ -1147,9 +1147,12 @@ static void submit_exec_queue(struct xe_exec_queue *q, struct xe_sched_job *job) /* * All queues in a multi-queue group will use the primary queue - * of the group to interface with GuC. + * of the group to interface with GuC. If primay is suspended, + * just return. Jobs will get scheduled once primary is resumed. */ q = xe_exec_queue_multi_queue_primary(q); + if (exec_queue_suspended(q)) + return; if (!exec_queue_enabled(q) && !exec_queue_suspended(q)) { action[len++] = XE_GUC_ACTION_SCHED_CONTEXT_MODE_SET; From 6bf7e2affc6e62da7add393d7f352d4040f5bc27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ADra=20Canal?= Date: Sun, 31 May 2026 17:18:55 -0300 Subject: [PATCH 153/257] drm/v3d: Fix global performance monitor reference counting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the SET_GLOBAL ioctl, v3d_perfmon_find() bumps the reference count on the perfmon it returns, but v3d_perfmon_set_global_ioctl() and v3d_perfmon_delete() fail to release that reference on several paths: 1. v3d_perfmon_set_global_ioctl() leaks the reference on its error paths. 2. CLEAR_GLOBAL leaks both the find reference and the reference previously stashed in v3d->global_perfmon by the SET_GLOBAL ioctl that configured it. 3. Destroying a perfmon that is the current global perfmon leaks the reference stashed by the SET_GLOBAL ioctl. Release each of these references explicitly. Cc: stable@vger.kernel.org Fixes: c6eabbab359c ("drm/v3d: Add DRM_IOCTL_V3D_PERFMON_SET_GLOBAL") Reviewed-by: Iago Toral Quiroga Link: https://patch.msgid.link/20260531-v3d-perfmon-lifetime-v2-1-60ed4485a203@igalia.com Signed-off-by: Maíra Canal --- drivers/gpu/drm/v3d/v3d_perfmon.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/v3d/v3d_perfmon.c b/drivers/gpu/drm/v3d/v3d_perfmon.c index 8e0249580bba..ecfd446ff75f 100644 --- a/drivers/gpu/drm/v3d/v3d_perfmon.c +++ b/drivers/gpu/drm/v3d/v3d_perfmon.c @@ -309,8 +309,11 @@ static void v3d_perfmon_delete(struct v3d_file_priv *v3d_priv, if (perfmon == v3d->active_perfmon) v3d_perfmon_stop(v3d, perfmon, false); - /* If the global perfmon is being destroyed, set it to NULL */ - cmpxchg(&v3d->global_perfmon, perfmon, NULL); + /* If the global perfmon is being destroyed, clean it and release + * the reference stashed in v3d_perfmon_set_global_ioctl(). + */ + if (cmpxchg(&v3d->global_perfmon, perfmon, NULL) == perfmon) + v3d_perfmon_put(perfmon); v3d_perfmon_put(perfmon); } @@ -461,16 +464,27 @@ int v3d_perfmon_set_global_ioctl(struct drm_device *dev, void *data, /* If the request is to clear the global performance monitor */ if (req->flags & DRM_V3D_PERFMON_CLEAR_GLOBAL) { - if (!v3d->global_perfmon) + struct v3d_perfmon *old; + + /* DRM_V3D_PERFMON_CLEAR_GLOBAL doesn't check if + * v3d->global_perfmon == perfmon. Therefore, there + * is no need to keep perfmon's reference. + */ + v3d_perfmon_put(perfmon); + + old = xchg(&v3d->global_perfmon, NULL); + if (!old) return -EINVAL; - xchg(&v3d->global_perfmon, NULL); + v3d_perfmon_put(old); return 0; } - if (cmpxchg(&v3d->global_perfmon, NULL, perfmon)) + if (cmpxchg(&v3d->global_perfmon, NULL, perfmon)) { + v3d_perfmon_put(perfmon); return -EBUSY; + } return 0; } From 08904765bb941f98306ae6841c33cfd299343faf Mon Sep 17 00:00:00 2001 From: Gabriele Monaco Date: Thu, 4 Jun 2026 14:09:45 +0200 Subject: [PATCH 154/257] tools/rv: Ensure monitor name and desc are NUL-terminated ikm_fill_monitor_definition() copies monitor name and description with strncpy(), but does not guarantee NUL termination when source strings are equal to or longer than the destination buffers. Clamp copies to sizeof(dst) - 1 and explicitly append '\0' for both fields to keep them safe for later string operations. Suggested-by: unknownbbqrx Fixes: 6d60f89691fc9 ("tools/rv: Add in-kernel monitor interface") Link: https://lore.kernel.org/r/20260604120946.90302-2-gmonaco@redhat.com Signed-off-by: Gabriele Monaco --- tools/verification/rv/src/in_kernel.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tools/verification/rv/src/in_kernel.c b/tools/verification/rv/src/in_kernel.c index 4bb746ea6e17..d324538249d3 100644 --- a/tools/verification/rv/src/in_kernel.c +++ b/tools/verification/rv/src/in_kernel.c @@ -215,10 +215,11 @@ static int ikm_fill_monitor_definition(char *name, struct monitor *ikm, char *co return -1; } - strncpy(ikm->name, nested_name, MAX_DA_NAME_LEN); + strncpy(ikm->name, nested_name, sizeof(ikm->name) - 1); + ikm->name[sizeof(ikm->name) - 1] = '\0'; ikm->enabled = enabled; - strncpy(ikm->desc, desc, MAX_DESCRIPTION); - + strncpy(ikm->desc, desc, sizeof(ikm->desc) - 1); + ikm->desc[sizeof(ikm->desc) - 1] = '\0'; free(desc); return 0; From a963fbf3166f2e178ac38b6c3c186a0c98092fb9 Mon Sep 17 00:00:00 2001 From: Gabriele Monaco Date: Thu, 14 May 2026 17:20:42 +0200 Subject: [PATCH 155/257] tools/rv: Fix substring match bug in monitor name search __ikm_find_monitor_name() relies on strstr() to find a monitor by name, which fails if the target monitor is a substring of a previously listed monitor. Fix it by tokenizing the available_monitors file and matching full tokens instead. Fixes: eba321a16fc6 ("tools/rv: Add support for nested monitors") Reviewed-by: Nam Cao Link: https://lore.kernel.org/r/20260514152055.229162-2-gmonaco@redhat.com Signed-off-by: Gabriele Monaco --- tools/verification/rv/src/in_kernel.c | 50 ++++++++++++++------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/tools/verification/rv/src/in_kernel.c b/tools/verification/rv/src/in_kernel.c index d324538249d3..95eac9ab1484 100644 --- a/tools/verification/rv/src/in_kernel.c +++ b/tools/verification/rv/src/in_kernel.c @@ -58,38 +58,40 @@ static int __ikm_read_enable(char *monitor_name) */ static int __ikm_find_monitor_name(char *monitor_name, char *out_name) { - char *available_monitors, container[MAX_DA_NAME_LEN+1], *cursor, *end; - int retval = 1; + char *available_monitors, *cursor, *line; + int len = strlen(monitor_name); + int found = 0; available_monitors = tracefs_instance_file_read(NULL, "rv/available_monitors", NULL); if (!available_monitors) return -1; - cursor = strstr(available_monitors, monitor_name); - if (!cursor) { - retval = 0; - goto out_free; + config_is_container = 0; + cursor = available_monitors; + while ((line = strsep(&cursor, "\n"))) { + char *colon = strchr(line, ':'); + + if (strcmp(line, monitor_name) && (!colon || strcmp(colon + 1, monitor_name))) + continue; + + strncpy(out_name, line, 2 * MAX_DA_NAME_LEN); + out_name[2 * MAX_DA_NAME_LEN - 1] = '\0'; + + if (colon) { + out_name[colon - line] = '/'; + } else { + /* If there are children, they are on the next line. */ + line = strsep(&cursor, "\n"); + if (line && !strncmp(line, monitor_name, len) && line[len] == ':') + config_is_container = 1; + } + + found = 1; + break; } - for (; cursor > available_monitors; cursor--) - if (*(cursor-1) == '\n') - break; - end = strstr(cursor, "\n"); - memcpy(out_name, cursor, end-cursor); - out_name[end-cursor] = '\0'; - - cursor = strstr(out_name, ":"); - if (cursor) - *cursor = '/'; - else { - sprintf(container, "%s:", monitor_name); - if (strstr(available_monitors, container)) - config_is_container = 1; - } - -out_free: free(available_monitors); - return retval; + return found; } /* From ba0247c5aa3fcb2890a92a97a88c70fe5ce704a6 Mon Sep 17 00:00:00 2001 From: Gabriele Monaco Date: Thu, 14 May 2026 17:20:43 +0200 Subject: [PATCH 156/257] tools/rv: Fix substring match when listing container monitors When listing monitors within a specific container (rv list ), the tool incorrectly matched monitors if the requested container name was only a prefix of the actual container (e.g., 'rv list sche' would incorrectly list monitors from 'sched:'). Fix this by ensuring the container name is an exact match and is immediately followed by the ':' separator. Fixes: eba321a16fc6 ("tools/rv: Add support for nested monitors") Reviewed-by: Nam Cao Link: https://lore.kernel.org/r/20260514152055.229162-3-gmonaco@redhat.com Signed-off-by: Gabriele Monaco --- tools/verification/rv/src/in_kernel.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tools/verification/rv/src/in_kernel.c b/tools/verification/rv/src/in_kernel.c index 95eac9ab1484..e4f35940374f 100644 --- a/tools/verification/rv/src/in_kernel.c +++ b/tools/verification/rv/src/in_kernel.c @@ -193,8 +193,12 @@ static int ikm_fill_monitor_definition(char *name, struct monitor *ikm, char *co nested_name = strstr(name, ":"); if (nested_name) { /* it belongs in container if it starts with "container:" */ - if (container && strstr(name, container) != name) - return 1; + if (container) { + int len = strlen(container); + + if (strncmp(name, container, len) || name[len] != ':') + return 1; + } *nested_name = '/'; ++nested_name; ikm->nested = 1; From 33ec2269a4155cad7e9e42c92327dcaa9aee59a7 Mon Sep 17 00:00:00 2001 From: Gabriele Monaco Date: Thu, 14 May 2026 17:20:45 +0200 Subject: [PATCH 157/257] tools/rv: Fix cleanup after failed trace setup Currently if ikm_setup_trace_instance() fails, the tool returns without any cleanup, if rv was called with both -t and -r, this means the reactor is not going to be cleared. Jump to the cleanup label to restore the reactor if necessary. Fixes: 6d60f89691fc9 ("tools/rv: Add in-kernel monitor interface") Reviewed-by: Nam Cao Link: https://lore.kernel.org/r/20260514152055.229162-5-gmonaco@redhat.com Signed-off-by: Gabriele Monaco --- tools/verification/rv/src/in_kernel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/verification/rv/src/in_kernel.c b/tools/verification/rv/src/in_kernel.c index e4f35940374f..e6dea4040f8f 100644 --- a/tools/verification/rv/src/in_kernel.c +++ b/tools/verification/rv/src/in_kernel.c @@ -810,7 +810,7 @@ int ikm_run_monitor(char *monitor_name, int argc, char **argv) if (config_trace) { inst = ikm_setup_trace_instance(nested_name); if (!inst) - return -1; + goto out_free_instance; } retval = ikm_enable(full_name); From 85339442de941e4d7ff5d53f51ae1413905e45ec Mon Sep 17 00:00:00 2001 From: Gabriele Monaco Date: Mon, 1 Jun 2026 17:38:38 +0200 Subject: [PATCH 158/257] verification/rvgen: Fix suffix strip in dot2k __start_to_invariant_check() and __get_constraint_env() parse the environment variable's name from sources that have it padded with the monitor name. This is removed using rstrip(), which is not meant to strip a substring but rather a set of characters. Use removesuffix() to actually get rid of the trailing _. Fixes: a82adadb16894 ("verification/rvgen: Add support for Hybrid Automata") Reviewed-by: Nam Cao Link: https://lore.kernel.org/r/20260601153840.124372-12-gmonaco@redhat.com Signed-off-by: Gabriele Monaco --- tools/verification/rvgen/rvgen/dot2k.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/verification/rvgen/rvgen/dot2k.py b/tools/verification/rvgen/rvgen/dot2k.py index e6f476b903b0..110cfd69e53a 100644 --- a/tools/verification/rvgen/rvgen/dot2k.py +++ b/tools/verification/rvgen/rvgen/dot2k.py @@ -215,14 +215,14 @@ class ha2k(dot2k): def __get_constraint_env(self, constr: str) -> str: """Extract the second argument from an ha_ function""" env = constr.split("(")[1].split()[1].rstrip(")").rstrip(",") - assert env.rstrip(f"_{self.name}") in self.envs + assert env.removesuffix(f"_{self.name}") in self.envs return env def __start_to_invariant_check(self, constr: str) -> str: # by default assume the timer has ns expiration env = self.__get_constraint_env(constr) clock_type = "ns" - if self.env_types.get(env.rstrip(f"_{self.name}")) == "j": + if self.env_types.get(env.removesuffix(f"_{self.name}")) == "j": clock_type = "jiffy" return f"return ha_check_invariant_{clock_type}(ha_mon, {env}, time_ns)" From 5f845ad706c0b394ae274e9a930044f78bef782e Mon Sep 17 00:00:00 2001 From: Gabriele Monaco Date: Thu, 14 May 2026 17:20:47 +0200 Subject: [PATCH 159/257] verification/rvgen: Fix options shared among commands After rvgen was refactored to use subparsers, the common options (-a and -D) were left in the main parser. This meant that they needed to be called /before/ the subcommand and using them without subcommand was allowed. This is not the original intent. rvgen -D "some description" container -n name Define the options as parent in the subparsers to allow them to be used from both subcommands together with other options. rvgen container -n name -D "some description" Fixes: 5270a0e3041c ("verification/dot2k: Replace is_container() hack with subparsers") Reviewed-by: Nam Cao Link: https://lore.kernel.org/r/20260514152055.229162-7-gmonaco@redhat.com Signed-off-by: Gabriele Monaco --- tools/verification/rvgen/__main__.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tools/verification/rvgen/__main__.py b/tools/verification/rvgen/__main__.py index 3be7f85fe37b..5c923dc10d0f 100644 --- a/tools/verification/rvgen/__main__.py +++ b/tools/verification/rvgen/__main__.py @@ -18,14 +18,16 @@ if __name__ == '__main__': import sys parser = argparse.ArgumentParser(description='Generate kernel rv monitor') - parser.add_argument("-D", "--description", dest="description", required=False) - parser.add_argument("-a", "--auto_patch", dest="auto_patch", + + parent_parser = argparse.ArgumentParser(add_help=False) + parent_parser.add_argument("-D", "--description", dest="description", required=False) + parent_parser.add_argument("-a", "--auto_patch", dest="auto_patch", action="store_true", required=False, help="Patch the kernel in place") subparsers = parser.add_subparsers(dest="subcmd", required=True) - monitor_parser = subparsers.add_parser("monitor") + monitor_parser = subparsers.add_parser("monitor", parents=[parent_parser]) monitor_parser.add_argument('-n', "--model_name", dest="model_name") monitor_parser.add_argument("-p", "--parent", dest="parent", required=False, help="Create a monitor nested to parent") @@ -36,7 +38,7 @@ if __name__ == '__main__': monitor_parser.add_argument('-t', "--monitor_type", dest="monitor_type", required=True, help=f"Available options: {', '.join(Monitor.monitor_types.keys())}") - container_parser = subparsers.add_parser("container") + container_parser = subparsers.add_parser("container", parents=[parent_parser]) container_parser.add_argument('-n', "--model_name", dest="model_name", required=True) params = parser.parse_args() From df996599cc69a9b74ff437c67751cf8a61f62e39 Mon Sep 17 00:00:00 2001 From: Gabriele Monaco Date: Thu, 14 May 2026 17:20:48 +0200 Subject: [PATCH 160/257] verification/rvgen: Fix ltl2k writing True as a literal The rvgen parser for LTL stores literal true values in the python representation (capitalised True), this doesn't build in C. The Literal class should already handle this case but ASTNode skips its strigification method and converts the value (true/false) directly. Fix by delegating ASTNode stringification to the Literal and Variable classes instead of bypassing them. Fixes: 97ffa4ce6ab32 ("verification/rvgen: Add support for linear temporal logic") Reviewed-by: Nam Cao Link: https://lore.kernel.org/r/20260514152055.229162-8-gmonaco@redhat.com Signed-off-by: Gabriele Monaco --- tools/verification/rvgen/rvgen/ltl2ba.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tools/verification/rvgen/rvgen/ltl2ba.py b/tools/verification/rvgen/rvgen/ltl2ba.py index 7f538598a868..016e7cf93bbb 100644 --- a/tools/verification/rvgen/rvgen/ltl2ba.py +++ b/tools/verification/rvgen/rvgen/ltl2ba.py @@ -122,10 +122,8 @@ class ASTNode: return self.op.expand(self, node, node_set) def __str__(self): - if isinstance(self.op, Literal): - return str(self.op.value) - if isinstance(self.op, Variable): - return self.op.name.lower() + if isinstance(self.op, (Literal, Variable)): + return str(self.op) return "val" + str(self.id) def normalize(self): @@ -382,6 +380,9 @@ class Variable: def __iter__(self): yield from () + def __str__(self): + return self.name.lower() + def negate(self): new = ASTNode(self) return NotOp(new) From 88fe2e3658726cb21ff2dcf9770bf672f9b9d31b Mon Sep 17 00:00:00 2001 From: Ji'an Zhou Date: Thu, 4 Jun 2026 14:25:59 +0000 Subject: [PATCH 161/257] ALSA: PCM: Fix wait queue list corruption in snd_pcm_drain() on linked streams snd_pcm_drain() uses init_waitqueue_entry which does not clear entry.prev/next, and add_wait_queue with a conditional remove_wait_queue that is skipped when to_check is no longer in the group after concurrent UNLINK. The orphaned wait entry remains on the unlinked substream sleep queue. On the next drain iteration, add_wait_queue adds the entry to a new queue while still linked on the old one, corrupting both lists. A subsequent wake_up dereferences NULL at the func pointer (mapped from the spinlock at offset 0 of the misinterpreted wait_queue_head_t), causing a kernel panic. Replace init_waitqueue_entry/add_wait_queue/conditional remove_wait_queue with init_wait_entry/prepare_to_wait/ finish_wait. init_wait_entry clears prev/next via INIT_LIST_HEAD on each iteration and sets autoremove_wake_function which auto-removes the entry on wake-up. finish_wait safely handles both the already-removed and still-queued cases. Fixes: 9b1dbd69ba6f ("ALSA: pcm: fix use-after-free on linked stream runtime in snd_pcm_drain") Signed-off-by: Ji'an Zhou Link: https://patch.msgid.link/20260604142559.3840881-1-eilaimemedsnaimel@gmail.com Signed-off-by: Takashi Iwai --- sound/core/pcm_native.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index a541bb235cfa..302643c1c192 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -2199,9 +2199,8 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream, drain_no_period_wakeup = to_check->no_period_wakeup; drain_rate = to_check->rate; drain_bufsz = to_check->buffer_size; - init_waitqueue_entry(&wait, current); - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&to_check->sleep, &wait); + init_wait_entry(&wait, 0); + prepare_to_wait(&to_check->sleep, &wait, TASK_INTERRUPTIBLE); snd_pcm_stream_unlock_irq(substream); if (drain_no_period_wakeup) tout = MAX_SCHEDULE_TIMEOUT; @@ -2219,7 +2218,7 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream, group = snd_pcm_stream_group_ref(substream); snd_pcm_group_for_each_entry(s, substream) { if (s->runtime == to_check) { - remove_wait_queue(&to_check->sleep, &wait); + finish_wait(&to_check->sleep, &wait); break; } } From d76b56b06b2734c6e0ff519599126919fa4f154b Mon Sep 17 00:00:00 2001 From: Lianqin Hu Date: Thu, 4 Jun 2026 12:40:28 +0000 Subject: [PATCH 162/257] ALSA: usb-audio: Add iface reset and delay quirk for AB13X USB Audio Setting up the interface when suspended/resumeing fail on this card. Adding a reset and delay quirk will eliminate this problem. usb 1-1: new full-speed USB device number 2 using xhci-hcd usb 1-1: New USB device found, idVendor=3c20, idProduct=3d21 usb 1-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3 usb 1-1: Product: AB13X USB Audio usb 1-1: Manufacturer: Generic usb 1-1: SerialNumber: 20210726905926 Signed-off-by: Lianqin Hu Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/TYUPR06MB62174610061C213260E1A992D2102@TYUPR06MB6217.apcprd06.prod.outlook.com --- sound/usb/quirks.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 3d1b3523b020..f4e885f96e83 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -2487,6 +2487,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE), DEVICE_FLG(0x3443, 0x930d, /* NexiGo N930W 60fps Webcam */ QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_MIC_RES_16), + DEVICE_FLG(0x3c20, 0x3d21, /* AB13X USB Audio */ + QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY), DEVICE_FLG(0x413c, 0xa506, /* Dell AE515 sound bar */ QUIRK_FLAG_GET_SAMPLE_RATE), DEVICE_FLG(0x534d, 0x0021, /* MacroSilicon MS2100/MS2106 */ From 00f547e0dfecf83014fb32bcba587c6b684c1362 Mon Sep 17 00:00:00 2001 From: Muhammad Bilal Date: Sat, 23 May 2026 19:51:59 +0000 Subject: [PATCH 163/257] accel/ethosu: fix IFM region index out-of-bounds in command stream parser NPU_SET_IFM_REGION extracts the region index with param & 0x7f, giving a maximum value of 127. However region_size[] and output_region[] in struct ethosu_validated_cmdstream_info are both sized to NPU_BASEP_REGION_MAX (8), giving valid indices [0..7]. Every other region assignment in the same switch uses param & 0x7: NPU_SET_OFM_REGION: st.ofm.region = param & 0x7; NPU_SET_IFM2_REGION: st.ifm2.region = param & 0x7; NPU_SET_WEIGHT_REGION: st.weight[0].region = param & 0x7; NPU_SET_SCALE_REGION: st.scale[0].region = param & 0x7; The 0x7f mask on IFM is inconsistent and appears to be a typo. feat_matrix_length() and calc_sizes() use the region index directly as an array subscript into the kzalloc'd info struct: info->region_size[fm->region] = max(...); A userspace caller supplying NPU_SET_IFM_REGION with param > 7 causes a write up to 127*8 = 1016 bytes past the start of region_size[], corrupting adjacent kernel heap data. Fix by applying the same & 0x7 mask used by all other region assignments. Fixes: 5a5e9c0228e6 ("accel: Add Arm Ethos-U NPU driver") Cc: stable@vger.kernel.org Signed-off-by: Muhammad Bilal Link: https://patch.msgid.link/20260523195159.55801-1-meatuni001@gmail.com Signed-off-by: Rob Herring (Arm) --- drivers/accel/ethosu/ethosu_gem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/accel/ethosu/ethosu_gem.c b/drivers/accel/ethosu/ethosu_gem.c index 7994e7073903..ced99cf9cdfc 100644 --- a/drivers/accel/ethosu/ethosu_gem.c +++ b/drivers/accel/ethosu/ethosu_gem.c @@ -464,7 +464,7 @@ static int ethosu_gem_cmdstream_copy_and_validate(struct drm_device *ddev, st.ifm.broadcast = param; break; case NPU_SET_IFM_REGION: - st.ifm.region = param & 0x7f; + st.ifm.region = param & 0x7; break; case NPU_SET_IFM_WIDTH0_M1: st.ifm.width0 = param; From ef911805d86a05363d3ec2fa9835a41def83bb7e Mon Sep 17 00:00:00 2001 From: Muhammad Bilal Date: Sat, 23 May 2026 21:07:52 +0000 Subject: [PATCH 164/257] accel/ethosu: reject NPU_OP_RESIZE commands from userspace NPU_OP_RESIZE is a U85-only command that the driver does not yet implement. The existing WARN_ON(1) placeholder fires unconditionally whenever userspace submits this command via DRM_IOCTL_ETHOSU_GEM_CREATE, causing unbounded kernel log spam. If panic_on_warn is set the kernel panics, giving any unprivileged user with access to the DRM device a trivial denial-of-service primitive. Replace the WARN_ON(1) with an explicit -EINVAL return so the ioctl rejects the command before it reaches hardware. Fixes: 5a5e9c0228e6 ("accel: Add Arm Ethos-U NPU driver") Cc: stable@vger.kernel.org Signed-off-by: Muhammad Bilal Link: https://patch.msgid.link/20260523210840.92039-2-meatuni001@gmail.com Signed-off-by: Rob Herring (Arm) --- drivers/accel/ethosu/ethosu_gem.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/accel/ethosu/ethosu_gem.c b/drivers/accel/ethosu/ethosu_gem.c index ced99cf9cdfc..863cdadb137a 100644 --- a/drivers/accel/ethosu/ethosu_gem.c +++ b/drivers/accel/ethosu/ethosu_gem.c @@ -431,8 +431,7 @@ static int ethosu_gem_cmdstream_copy_and_validate(struct drm_device *ddev, return ret; break; case NPU_OP_RESIZE: // U85 only - WARN_ON(1); // TODO - break; + return -EINVAL; case NPU_SET_KERNEL_WIDTH_M1: st.ifm.width = param; break; From e703843f242b28e35ac79408de571ae110c740b5 Mon Sep 17 00:00:00 2001 From: Muhammad Bilal Date: Sat, 23 May 2026 21:07:53 +0000 Subject: [PATCH 165/257] accel/ethosu: fix wrong weight index in NPU_SET_SCALE1_LENGTH on U85 On non-U65 hardware (e.g. U85), opcode 0x4093 is NPU_SET_WEIGHT2_LENGTH. The BASE handler for the same opcode correctly assigns to st.weight[2].base, but the LENGTH handler mistakenly assigns cmds[1] to st.weight[1].length instead of st.weight[2].length. This leaves weight[2].length at its initialised sentinel value of 0xffffffff and corrupts weight[1].length with the user-supplied value, breaking the software bounds-check state for both weight buffers on U85. Fix the index to match the BASE handler. Fixes: 5a5e9c0228e6 ("accel: Add Arm Ethos-U NPU driver") Cc: stable@vger.kernel.org Signed-off-by: Muhammad Bilal Link: https://patch.msgid.link/20260523210840.92039-3-meatuni001@gmail.com Signed-off-by: Rob Herring (Arm) --- drivers/accel/ethosu/ethosu_gem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/accel/ethosu/ethosu_gem.c b/drivers/accel/ethosu/ethosu_gem.c index 863cdadb137a..52b6a8752c75 100644 --- a/drivers/accel/ethosu/ethosu_gem.c +++ b/drivers/accel/ethosu/ethosu_gem.c @@ -598,7 +598,7 @@ static int ethosu_gem_cmdstream_copy_and_validate(struct drm_device *ddev, if (ethosu_is_u65(edev)) st.scale[1].length = cmds[1]; else - st.weight[1].length = cmds[1]; + st.weight[2].length = cmds[1]; break; case NPU_SET_WEIGHT3_BASE: st.weight[3].base = addr; From ee6d9b6e51626f259c6f0e38d94f91be4fd14754 Mon Sep 17 00:00:00 2001 From: Muhammad Bilal Date: Sun, 24 May 2026 10:37:10 +0000 Subject: [PATCH 166/257] accel/ethosu: fix arithmetic issues in dma_length() dma_length() derives DMA region usage from command stream values and updates region_size[]: len = ((len + stride[0]) * size0 + stride[1]) * size1 region_size[region] = max(..., len + dma->offset) Several arithmetic issues can corrupt the derived region size: - signed stride values may underflow when added to len - intermediate multiplications may overflow - len + dma->offset may overflow during region_size updates - dma_length() error returns were not validated by the caller region_size[] is later used by ethosu_job.c to validate command stream accesses against GEM buffer sizes. Arithmetic wraparound can therefore under-report region usage and bypass the bounds validation. Fix by validating signed additions, using overflow helpers for multiplications and offset updates, and propagating dma_length() failures to the caller. Fixes: 5a5e9c0228e6 ("accel: Add Arm Ethos-U NPU driver") Cc: stable@vger.kernel.org Signed-off-by: Muhammad Bilal Link: https://patch.msgid.link/20260524103710.47397-1-meatuni001@gmail.com Signed-off-by: Rob Herring (Arm) --- drivers/accel/ethosu/ethosu_gem.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/drivers/accel/ethosu/ethosu_gem.c b/drivers/accel/ethosu/ethosu_gem.c index 52b6a8752c75..27c5fdbe9a5d 100644 --- a/drivers/accel/ethosu/ethosu_gem.c +++ b/drivers/accel/ethosu/ethosu_gem.c @@ -2,6 +2,7 @@ /* Copyright 2025 Arm, Ltd. */ #include +#include #include #include @@ -164,16 +165,26 @@ static u64 dma_length(struct ethosu_validated_cmdstream_info *info, u64 len = dma->len; if (mode >= 1) { + if (dma->stride[0] < 0 && (u64)(-dma->stride[0]) > len) + return U64_MAX; len += dma->stride[0]; - len *= dma_st->size0; + if (check_mul_overflow(len, (u64)dma_st->size0, &len)) + return U64_MAX; } if (mode == 2) { + if (dma->stride[1] < 0 && (u64)(-dma->stride[1]) > len) + return U64_MAX; len += dma->stride[1]; - len *= dma_st->size1; + if (check_mul_overflow(len, (u64)dma_st->size1, &len)) + return U64_MAX; + } + if (dma->region >= 0) { + u64 end; + + if (check_add_overflow(len, dma->offset, &end)) + return U64_MAX; + info->region_size[dma->region] = max(info->region_size[dma->region], end); } - if (dma->region >= 0) - info->region_size[dma->region] = max(info->region_size[dma->region], - len + dma->offset); return len; } @@ -395,6 +406,8 @@ static int ethosu_gem_cmdstream_copy_and_validate(struct drm_device *ddev, case NPU_OP_DMA_START: srclen = dma_length(info, &st.dma, &st.dma.src); dstlen = dma_length(info, &st.dma, &st.dma.dst); + if (srclen == U64_MAX || dstlen == U64_MAX) + return -EINVAL; if (st.dma.dst.region >= 0) info->output_region[st.dma.dst.region] = true; From d9d021218162b6c4fe0bdf42b2b340f1aae23a12 Mon Sep 17 00:00:00 2001 From: Muhammad Bilal Date: Sun, 24 May 2026 13:03:19 +0000 Subject: [PATCH 167/257] accel/ethosu: reject DMA commands with uninitialized length cmd_state_init() initializes the command state with memset(0xff), leaving dma->len at U64_MAX to signal missing setup. The only setter is NPU_SET_DMA0_LEN; if userspace omits this command and issues NPU_OP_DMA_START, dma->len remains U64_MAX. In dma_length(), a positive stride added to U64_MAX wraps to a small value. With size0 == 1, check_mul_overflow() does not trigger and dma_length() returns 0 instead of U64_MAX. The caller's U64_MAX check then passes, region_size[] stays 0, and the bounds check in ethosu_job.c is bypassed, allowing hardware to execute DMA with stale physical addresses. Fix by checking for U64_MAX at the start of dma_length() before any arithmetic, consistent with the sentinel value used throughout the driver to detect uninitialized fields. Fixes: 5a5e9c0228e6 ("accel: Add Arm Ethos-U NPU driver") Cc: stable@vger.kernel.org Signed-off-by: Muhammad Bilal Link: https://patch.msgid.link/20260524130319.12747-1-meatuni001@gmail.com Signed-off-by: Rob Herring (Arm) --- drivers/accel/ethosu/ethosu_gem.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/accel/ethosu/ethosu_gem.c b/drivers/accel/ethosu/ethosu_gem.c index 27c5fdbe9a5d..2cb7964ddfa5 100644 --- a/drivers/accel/ethosu/ethosu_gem.c +++ b/drivers/accel/ethosu/ethosu_gem.c @@ -164,6 +164,9 @@ static u64 dma_length(struct ethosu_validated_cmdstream_info *info, s8 mode = dma_st->mode; u64 len = dma->len; + if (len == U64_MAX) + return U64_MAX; + if (mode >= 1) { if (dma->stride[0] < 0 && (u64)(-dma->stride[0]) > len) return U64_MAX; From 32594b09854970d7ba83eb2dc8c69a2edd158c8e Mon Sep 17 00:00:00 2001 From: Hyunwoo Kim Date: Tue, 2 Jun 2026 19:21:05 +0900 Subject: [PATCH 168/257] inet: frags: fix use-after-free caused by the fqdir_pre_exit() flush On netns teardown, fqdir_pre_exit() walks the fqdir rhashtable and flushes every fragment queue that is not yet complete using inet_frag_queue_flush(). That helper frees all the skbs queued on the fragment queue but does not set INET_FRAG_COMPLETE, and leaves q->fragments_tail and q->last_run_head pointing at the freed skbs. The queue itself stays in the rhashtable. fqdir_pre_exit() first lowers high_thresh to 0 to stop new queue lookups, but it cannot stop a fragment that already obtained the queue through inet_frag_find() earlier and stalled just before taking the queue lock. Once that fragment resumes after the flush and takes the queue lock, it passes the INET_FRAG_COMPLETE check and then dereferences the freed fragments_tail. inet_frag_queue_insert() reads FRAG_CB() and ->len of that pointer and, on the append path, writes ->next_frag, causing a slab use-after-free. IPv6, nf_conntrack_reasm6 and 6lowpan reassembly share the same flush path and are affected as well. Reset rb_fragments, fragments_tail and last_run_head in inet_frag_queue_flush() so a flushed queue no longer points at the freed skbs. A fragment that resumes after the flush and takes the queue lock then finds an empty queue and starts a new run instead of dereferencing the freed fragments_tail. ip_frag_reinit() already performed this reset after its own flush, so drop the now duplicate code there. Cc: stable@vger.kernel.org Fixes: 006a5035b495 ("inet: frags: flush pending skbs in fqdir_pre_exit()") Suggested-by: Eric Dumazet Signed-off-by: Hyunwoo Kim Link: https://patch.msgid.link/ah6ukYq5G98LshdA@v4bel Signed-off-by: Jakub Kicinski --- net/ipv4/inet_fragment.c | 3 +++ net/ipv4/ip_fragment.c | 3 --- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c index 393770920abd..1127519b8416 100644 --- a/net/ipv4/inet_fragment.c +++ b/net/ipv4/inet_fragment.c @@ -328,6 +328,9 @@ void inet_frag_queue_flush(struct inet_frag_queue *q, reason = reason ?: SKB_DROP_REASON_FRAG_REASM_TIMEOUT; sum = inet_frag_rbtree_purge(&q->rb_fragments, reason); sub_frag_mem_limit(q->fqdir, sum); + q->rb_fragments = RB_ROOT; + q->fragments_tail = NULL; + q->last_run_head = NULL; } EXPORT_SYMBOL(inet_frag_queue_flush); diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index 56b0f738d2f2..c790d2f49487 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -250,9 +250,6 @@ static int ip_frag_reinit(struct ipq *qp) qp->q.flags = 0; qp->q.len = 0; qp->q.meat = 0; - qp->q.rb_fragments = RB_ROOT; - qp->q.fragments_tail = NULL; - qp->q.last_run_head = NULL; qp->iif = 0; qp->ecn = 0; From 4aacf509e537a711fa71bca9f234e5eb6968850e Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 2 Jun 2026 09:34:14 +0200 Subject: [PATCH 169/257] net: mv643xx: fix OF node refcount Platform devices created with platform_device_alloc() call platform_device_release() when the last reference to the device's kobject is dropped. This function calls of_node_put() unconditionally. This works fine for devices created with platform_device_register_full() but users of the split approach (platform_device_alloc() + platform_device_add()) must bump the reference of the of_node they assign manually. Add the missing call to of_node_get(). Cc: stable@vger.kernel.org Fixes: 76723bca2802 ("net: mv643xx_eth: add DT parsing support") Signed-off-by: Bartosz Golaszewski Link: https://patch.msgid.link/20260602073414.22500-1-bartosz.golaszewski@oss.qualcomm.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/marvell/mv643xx_eth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c index f9055b3d6fb1..1881583be5ce 100644 --- a/drivers/net/ethernet/marvell/mv643xx_eth.c +++ b/drivers/net/ethernet/marvell/mv643xx_eth.c @@ -2780,7 +2780,7 @@ static int mv643xx_eth_shared_of_add_port(struct platform_device *pdev, goto put_err; } ppdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); - ppdev->dev.of_node = pnp; + ppdev->dev.of_node = of_node_get(pnp); ret = platform_device_add_resources(ppdev, &res, 1); if (ret) From c0837b9cf6eabbad8b8cbddaff1a46a6d0a2e29d Mon Sep 17 00:00:00 2001 From: Muhammad Bilal Date: Sat, 23 May 2026 19:08:43 +0000 Subject: [PATCH 170/257] accel/ethosu: fix OOB write in ethosu_gem_cmdstream_copy_and_validate() The command stream parsing loop increments the index variable a second time when a 64-bit command word is encountered (bit 14 set), but does not re-check the loop bound before writing the second word: for (i = 0; i < size / 4; i++) { bocmds[i] = cmds[0]; if (cmd & 0x4000) { i++; bocmds[i] = cmds[1]; /* unchecked */ } } The buffer bocmds is backed by a DMA allocation of exactly size bytes from drm_gem_dma_create(ddev, size), giving valid indices [0, size/4-1]. When i == size/4 - 1 on entry to an iteration and bit 14 of cmds[0] is set, bocmds[size/4-1] is written in bounds, i is then incremented to size/4, and bocmds[size/4] writes four bytes past the end of the allocation. Userspace controls both the buffer contents and the size argument via the ioctl, making this a userspace-triggerable heap out-of-bounds write. Fix by checking the incremented index against the buffer bound before the second write and returning -EINVAL if the buffer is too small to contain the extended command. Fixes: 5a5e9c0228e6 ("accel: Add Arm Ethos-U NPU driver") Cc: stable@vger.kernel.org Signed-off-by: Muhammad Bilal Link: https://patch.msgid.link/20260523190843.33977-1-meatuni001@gmail.com Signed-off-by: Rob Herring (Arm) --- drivers/accel/ethosu/ethosu_gem.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/accel/ethosu/ethosu_gem.c b/drivers/accel/ethosu/ethosu_gem.c index 2cb7964ddfa5..3401883e207f 100644 --- a/drivers/accel/ethosu/ethosu_gem.c +++ b/drivers/accel/ethosu/ethosu_gem.c @@ -401,6 +401,8 @@ static int ethosu_gem_cmdstream_copy_and_validate(struct drm_device *ddev, return -EFAULT; i++; + if (i >= size / 4) + return -EINVAL; bocmds[i] = cmds[1]; addr = cmd_to_addr(cmds); } From 2b5ff4db5d7aa5b981d966df02e687f79ad7b311 Mon Sep 17 00:00:00 2001 From: Kyle Zeng Date: Fri, 5 Jun 2026 01:02:04 -0700 Subject: [PATCH 171/257] ALSA: seq: dummy: fix UMP event stack overread The dummy sequencer port forwards events by copying an incoming struct snd_seq_event into a stack temporary, rewriting source and destination, and dispatching the temporary to subscribers. That legacy event storage is smaller than struct snd_seq_ump_event. When a UMP event reaches the dummy client, the copy leaves the UMP flag set but only provides legacy-sized stack storage. The subscriber delivery path then uses snd_seq_event_packet_size() and copies a UMP-sized packet from that stack object, reading past the end of the temporary. Use the existing union __snd_seq_event storage and copy the packet size reported for the incoming event before rewriting the common routing fields. This preserves the full UMP packet for UMP events while keeping legacy event handling unchanged. Fixes: 32cb23a0f911 ("ALSA: seq: dummy: Allow UMP conversion") Signed-off-by: Kyle Zeng Link: https://patch.msgid.link/20260605080204.32045-1-kylebot@openai.com Signed-off-by: Takashi Iwai --- sound/core/seq/seq_dummy.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/sound/core/seq/seq_dummy.c b/sound/core/seq/seq_dummy.c index af45f328ae99..8abe80985dad 100644 --- a/sound/core/seq/seq_dummy.c +++ b/sound/core/seq/seq_dummy.c @@ -9,6 +9,7 @@ #include #include #include "seq_clientmgr.h" +#include "seq_memory.h" #include #include @@ -81,19 +82,21 @@ dummy_input(struct snd_seq_event *ev, int direct, void *private_data, int atomic, int hop) { struct snd_seq_dummy_port *p; - struct snd_seq_event tmpev; + union __snd_seq_event tmpev; + size_t size; p = private_data; if (ev->source.client == SNDRV_SEQ_CLIENT_SYSTEM || ev->type == SNDRV_SEQ_EVENT_KERNEL_ERROR) return 0; /* ignore system messages */ - tmpev = *ev; + size = snd_seq_event_packet_size(ev); + memcpy(&tmpev, ev, size); if (p->duplex) - tmpev.source.port = p->connect; + tmpev.legacy.source.port = p->connect; else - tmpev.source.port = p->port; - tmpev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS; - return snd_seq_kernel_client_dispatch(p->client, &tmpev, atomic, hop); + tmpev.legacy.source.port = p->port; + tmpev.legacy.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS; + return snd_seq_kernel_client_dispatch(p->client, &tmpev.legacy, atomic, hop); } /* From 6f6947b2387e94e405f80d472f8a189bfbf2bd6c Mon Sep 17 00:00:00 2001 From: Tony Luck Date: Thu, 4 Jun 2026 21:46:49 -0700 Subject: [PATCH 172/257] x86/resctrl: Only check Intel systems for SNC topology_num_nodes_per_package() reports values greater than one on certain AMD systems resulting in resctrl's Intel model specific SNC detection printing the confusing message: "CoD enabled system? Resctrl not supported" Add a check for Intel systems before looking at the topology. [ reinette: Add Closes tag, fix tag typos, rework changelog ] Fixes: 59674fc9d0bf ("x86/resctrl: Fix SNC detection") Reported-by: Babu Moger Signed-off-by: Tony Luck Signed-off-by: Reinette Chatre Signed-off-by: Ingo Molnar Tested-by: Babu Moger Link: https://patch.msgid.link/9849330f45ac86344cc5ac54df2d313906d70bc4.1780634584.git.reinette.chatre@intel.com Closes: https://lore.kernel.org/lkml/37ac0376-43a3-4283-a3d5-4d57b3bec578@amd.com/ --- arch/x86/kernel/cpu/resctrl/monitor.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/arch/x86/kernel/cpu/resctrl/monitor.c b/arch/x86/kernel/cpu/resctrl/monitor.c index 9bd87bae4983..59215fef3924 100644 --- a/arch/x86/kernel/cpu/resctrl/monitor.c +++ b/arch/x86/kernel/cpu/resctrl/monitor.c @@ -377,7 +377,12 @@ static const struct x86_cpu_id snc_cpu_ids[] __initconst = { static __init int snc_get_config(void) { - int ret = topology_num_nodes_per_package(); + int ret; + + if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL) + return 1; + + ret = topology_num_nodes_per_package(); if (ret > 1 && !x86_match_cpu(snc_cpu_ids)) { pr_warn("CoD enabled system? Resctrl not supported\n"); From 13031fb6b8357fbbcded2a7f4cba73e4781ee594 Mon Sep 17 00:00:00 2001 From: Hyunwoo Kim Date: Mon, 1 Jun 2026 23:53:26 +0900 Subject: [PATCH 173/257] KVM: arm64: vgic-its: Drop the translation cache reference only for the erased entry vgic_its_invalidate_cache() walks the per-ITS translation cache with xa_for_each() and drops the cache's reference on each entry with vgic_put_irq(). It puts the iterated pointer, though, rather than the value returned by xa_erase(). The function is called from contexts that do not exclude one another: the ITS command handlers hold its_lock, the GITS_CTLR write path holds cmd_lock, and the path that clears EnableLPIs in a redistributor's GICR_CTLR holds neither. Two or more of them can drain the same cache concurrently, and if each one observes the same entry, erases it and then puts it, the single reference the cache holds on that entry is dropped more than once. The entry can then be freed while an ITE still maps it. xa_erase() is atomic and returns the previous entry, so put only the entry that this context actually removed. The cache reference is then dropped exactly once per entry even when the invalidations run concurrently, and the behavior is unchanged when only one context runs. Fixes: 8201d1028caa ("KVM: arm64: vgic-its: Maintain a translation cache per ITS") Signed-off-by: Hyunwoo Kim Reviewed-by: Oliver Upton Link: https://patch.msgid.link/ah2c5lu4JbUg7dj-@v4bel Signed-off-by: Marc Zyngier Cc: stable@vger.kernel.org --- arch/arm64/kvm/vgic/vgic-its.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/arm64/kvm/vgic/vgic-its.c b/arch/arm64/kvm/vgic/vgic-its.c index 1d7e5d560af4..1e3706ac3b8e 100644 --- a/arch/arm64/kvm/vgic/vgic-its.c +++ b/arch/arm64/kvm/vgic/vgic-its.c @@ -597,8 +597,10 @@ static void vgic_its_invalidate_cache(struct vgic_its *its) unsigned long idx; xa_for_each(&its->translation_cache, idx, irq) { - xa_erase(&its->translation_cache, idx); - vgic_put_irq(kvm, irq); + /* Only the context that erases the entry drops its cache ref. */ + irq = xa_erase(&its->translation_cache, idx); + if (irq) + vgic_put_irq(kvm, irq); } } From f2ca45b50d4216c9cc7ffabf50d9ad1932209251 Mon Sep 17 00:00:00 2001 From: Hyunwoo Kim Date: Wed, 3 Jun 2026 21:09:33 +0900 Subject: [PATCH 174/257] KVM: arm64: Take the SRCU lock for page table walks in fault injection and AT emulation walk_s1() and kvm_walk_nested_s2() expect to be called while holding kvm->srcu to guard against memslot changes. While this is generally the case, __kvm_at_s12() and __kvm_find_s1_desc_level() call into the respective walkers without taking kvm->srcu. Fix by acquiring kvm->srcu prior to the table walk in both instances. Cc: stable@vger.kernel.org Fixes: 50f77dc87f13 ("KVM: arm64: Populate level on S1PTW SEA injection") Fixes: be04cebf3e78 ("KVM: arm64: nv: Add emulation of AT S12E{0,1}{R,W}") Suggested-by: Oliver Upton Signed-off-by: Hyunwoo Kim Reviewed-by: Oliver Upton Link: https://patch.msgid.link/aiAZfdeyanIvP8SD@v4bel Signed-off-by: Marc Zyngier --- arch/arm64/kvm/at.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/arm64/kvm/at.c b/arch/arm64/kvm/at.c index 9f8f0ae8e86e..889c2c15d7bd 100644 --- a/arch/arm64/kvm/at.c +++ b/arch/arm64/kvm/at.c @@ -1569,7 +1569,8 @@ int __kvm_at_s12(struct kvm_vcpu *vcpu, u32 op, u64 vaddr) /* Do the stage-2 translation */ ipa = (par & GENMASK_ULL(47, 12)) | (vaddr & GENMASK_ULL(11, 0)); out.esr = 0; - ret = kvm_walk_nested_s2(vcpu, ipa, &out); + scoped_guard(srcu, &vcpu->kvm->srcu) + ret = kvm_walk_nested_s2(vcpu, ipa, &out); if (ret < 0) return ret; @@ -1665,7 +1666,8 @@ int __kvm_find_s1_desc_level(struct kvm_vcpu *vcpu, u64 va, u64 ipa, int *level) } /* Walk the guest's PT, looking for a match along the way */ - ret = walk_s1(vcpu, &wi, &wr, va); + scoped_guard(srcu, &vcpu->kvm->srcu) + ret = walk_s1(vcpu, &wi, &wr, va); switch (ret) { case -EINTR: /* We interrupted the walk on a match, return the level */ From 2914709c914101eb704e01bed2351070d4161ccf Mon Sep 17 00:00:00 2001 From: Suraj Kandpal Date: Sun, 17 May 2026 08:17:09 +0530 Subject: [PATCH 175/257] Revert "drm/i915/backlight: Remove try_vesa_interface" This reverts commit 40d2f5820951dee818d05c14677277048bd85f9f. Removing the try_vesa_interface gate caused a backlight regression on panels whose VBT correctly reports INTEL_BACKLIGHT_DISPLAY_DDI and whose PWM path is the actual backlight control, but whose DPCD optimistically advertises DP_EDP_BACKLIGHT_AUX_ENABLE_CAP / _BRIGHTNESS_AUX_SET_CAP. After the commit such panels silently bind to the VESA AUX backlight funcs; AUX writes complete but the panel ignores them, leaving brightness stuck (no-op backlight). Observed on at least KBL and TGL eDP setups. Signed-off-by: Suraj Kandpal Reviewed-by: Ankit Nautiyal Link: https://patch.msgid.link/20260517024709.1016121-1-suraj.kandpal@intel.com (cherry picked from commit f30fddb4402313aa5301a74d721638d343395269) Signed-off-by: Tvrtko Ursulin --- .../drm/i915/display/intel_dp_aux_backlight.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c index a8d56ebf06a2..7a6c07f6aaeb 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c +++ b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c @@ -691,10 +691,9 @@ int intel_dp_aux_init_backlight_funcs(struct intel_connector *connector) struct intel_dp *intel_dp = intel_attached_dp(connector); struct drm_device *dev = connector->base.dev; struct intel_panel *panel = &connector->panel; - bool try_intel_interface = false; + bool try_intel_interface = false, try_vesa_interface = false; - /* - * Check the VBT and user's module parameters to figure out which + /* Check the VBT and user's module parameters to figure out which * interfaces to probe */ switch (display->params.enable_dpcd_backlight) { @@ -703,6 +702,7 @@ int intel_dp_aux_init_backlight_funcs(struct intel_connector *connector) case INTEL_DP_AUX_BACKLIGHT_AUTO: switch (panel->vbt.backlight.type) { case INTEL_BACKLIGHT_VESA_EDP_AUX_INTERFACE: + try_vesa_interface = true; break; case INTEL_BACKLIGHT_DISPLAY_DDI: try_intel_interface = true; @@ -715,12 +715,20 @@ int intel_dp_aux_init_backlight_funcs(struct intel_connector *connector) if (panel->vbt.backlight.type != INTEL_BACKLIGHT_VESA_EDP_AUX_INTERFACE) try_intel_interface = true; + try_vesa_interface = true; + break; + case INTEL_DP_AUX_BACKLIGHT_FORCE_VESA: + try_vesa_interface = true; break; case INTEL_DP_AUX_BACKLIGHT_FORCE_INTEL: try_intel_interface = true; break; } + /* For eDP 1.5 and above we are supposed to use VESA interface for brightness control */ + if (intel_dp->edp_dpcd[0] >= DP_EDP_15) + try_vesa_interface = true; + /* * Since Intel has their own backlight control interface, the majority of machines out there * using DPCD backlight controls with Intel GPUs will be using this interface as opposed to @@ -733,9 +741,6 @@ int intel_dp_aux_init_backlight_funcs(struct intel_connector *connector) * panel with Intel's OUI - which is also required for us to be able to detect Intel's * backlight interface at all. This means that the only sensible way for us to detect both * interfaces is to probe for Intel's first, and VESA's second. - * - * Also there is a chance some VBTs may advertise false Intel backlight support even if the - * TCON DPCD says otherwise. This means we keep VESA interface as fallback in that case. */ if (try_intel_interface && intel_dp->edp_dpcd[0] <= DP_EDP_14b && intel_dp_aux_supports_hdr_backlight(connector)) { @@ -745,7 +750,7 @@ int intel_dp_aux_init_backlight_funcs(struct intel_connector *connector) return 0; } - if (intel_dp_aux_supports_vesa_backlight(connector)) { + if (try_vesa_interface && intel_dp_aux_supports_vesa_backlight(connector)) { drm_dbg_kms(dev, "[CONNECTOR:%d:%s] Using VESA eDP backlight controls\n", connector->base.base.id, connector->base.name); panel->backlight.funcs = &intel_dp_vesa_bl_funcs; From cbaffe843a942c0d3102e0f9bce0e72b029b2594 Mon Sep 17 00:00:00 2001 From: Joey Gouly Date: Thu, 4 Jun 2026 11:54:34 +0100 Subject: [PATCH 176/257] KVM: arm64: Restore POR_EL0 access to host EL0 CPTR_EL2.E0POE was being cleared in __deactivate_cptr_traps_vhe(), which meant that any accesses to POR_EL0 from host EL0 would trap and be reported to userspace as an Illegal instruction. This would happen after running any VM, regardless if it used POE or not. Signed-off-by: Joey Gouly Link: https://sashiko.dev/#/patchset/20260602155430.2088142-1-maz@kernel.org?part=1 Link: https://patch.msgid.link/20260604105434.2297268-1-joey.gouly@arm.com Signed-off-by: Marc Zyngier Cc: stable@vger,kernel.org --- arch/arm64/kvm/hyp/include/hyp/switch.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm64/kvm/hyp/include/hyp/switch.h b/arch/arm64/kvm/hyp/include/hyp/switch.h index 320cd45d49c5..e9b36a3b27bb 100644 --- a/arch/arm64/kvm/hyp/include/hyp/switch.h +++ b/arch/arm64/kvm/hyp/include/hyp/switch.h @@ -181,6 +181,8 @@ static inline void __deactivate_cptr_traps_vhe(struct kvm_vcpu *vcpu) val |= CPACR_EL1_ZEN; if (cpus_have_final_cap(ARM64_SME)) val |= CPACR_EL1_SMEN; + if (cpus_have_final_cap(ARM64_HAS_S1POE)) + val |= CPACR_EL1_E0POE; write_sysreg(val, cpacr_el1); } From 70543358fa08e0f7cebc3447c3b70fe97ad7aaa8 Mon Sep 17 00:00:00 2001 From: Hyunwoo Kim Date: Fri, 5 Jun 2026 17:27:01 +0900 Subject: [PATCH 177/257] KVM: arm64: Reassign nested_mmus array behind mmu_lock kvm->arch.nested_mmus[] is walked under kvm->mmu_lock, including from the MMU notifier path (kvm_unmap_gfn_range() -> kvm_nested_s2_unmap()), which can run at any time. kvm_vcpu_init_nested() reallocates the array and frees the old buffer while holding only kvm->arch.config_lock, so such a walker can reference the freed array. Allocate the new array outside of mmu_lock, as the allocation can sleep. Under the lock, copy the existing entries, fix up the back pointers and reassign the array. Free the old buffer after dropping the lock, as kvfree() can sleep as well. Fixes: 4f128f8e1aaac ("KVM: arm64: nv: Support multiple nested Stage-2 mmu structures") Signed-off-by: Hyunwoo Kim Reviewed-by: Oliver Upton Link: https://patch.msgid.link/aiKIVVeIr1aAB1yp@v4bel Signed-off-by: Marc Zyngier Cc: stable@vger,kernel.org --- arch/arm64/kvm/nested.c | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c index 38f672e94087..6f7bc9a9992e 100644 --- a/arch/arm64/kvm/nested.c +++ b/arch/arm64/kvm/nested.c @@ -89,21 +89,28 @@ int kvm_vcpu_init_nested(struct kvm_vcpu *vcpu) * again, and there is no reason to affect the whole VM for this. */ num_mmus = atomic_read(&kvm->online_vcpus) * S2_MMU_PER_VCPU; - tmp = kvrealloc(kvm->arch.nested_mmus, - size_mul(sizeof(*kvm->arch.nested_mmus), num_mmus), - GFP_KERNEL_ACCOUNT | __GFP_ZERO); - if (!tmp) - return -ENOMEM; - swap(kvm->arch.nested_mmus, tmp); + if (num_mmus > kvm->arch.nested_mmus_size) { + tmp = kvcalloc(num_mmus, sizeof(*tmp), GFP_KERNEL_ACCOUNT); + if (!tmp) + return -ENOMEM; - /* - * If we went through a realocation, adjust the MMU back-pointers in - * the previously initialised kvm_pgtable structures. - */ - if (kvm->arch.nested_mmus != tmp) - for (int i = 0; i < kvm->arch.nested_mmus_size; i++) - kvm->arch.nested_mmus[i].pgt->mmu = &kvm->arch.nested_mmus[i]; + write_lock(&kvm->mmu_lock); + + if (kvm->arch.nested_mmus_size) { + memcpy(tmp, kvm->arch.nested_mmus, + size_mul(sizeof(*tmp), kvm->arch.nested_mmus_size)); + + for (int i = 0; i < kvm->arch.nested_mmus_size; i++) + tmp[i].pgt->mmu = &tmp[i]; + } + + swap(kvm->arch.nested_mmus, tmp); + + write_unlock(&kvm->mmu_lock); + + kvfree(tmp); + } for (int i = kvm->arch.nested_mmus_size; !ret && i < num_mmus; i++) ret = init_nested_s2_mmu(kvm, &kvm->arch.nested_mmus[i]); From c8a8a75b733467b00c08b91a38dbaf207a08ed6e Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Tue, 2 Jun 2026 17:16:41 +0000 Subject: [PATCH 178/257] xfrm: iptfs: fix ABBA deadlock in iptfs_destroy_state() iptfs_destroy_state() calls hrtimer_cancel() while holding a spinlock that the timer callback also acquires, leading to an ABBA deadlock on SMP systems. For the output timer (iptfs_timer): - iptfs_destroy_state() holds x->lock, calls hrtimer_cancel() - iptfs_delay_timer() callback takes x->lock For the drop timer (drop_timer): - iptfs_destroy_state() holds drop_lock, calls hrtimer_cancel() - iptfs_drop_timer() callback takes drop_lock Both timers use HRTIMER_MODE_REL_SOFT, so their callbacks run in softirq context. When hrtimer_cancel() is called for a soft timer that is currently executing on another CPU, hrtimer_cancel_wait_running() spins on softirq_expiry_lock -- the same lock held by the softirq running the callback. If the callback is blocked waiting for the spinlock held by the caller of hrtimer_cancel(), a circular dependency forms: CPU 0: holds lock_A -> waits for softirq_expiry_lock CPU 1: holds softirq_expiry_lock -> waits for lock_A Fix by calling hrtimer_cancel() before acquiring the respective locks. hrtimer_cancel() is safe to call without holding any lock and will wait for any in-progress callback to complete. For the output timer, the lock is still acquired afterwards to drain the packet queue. For the drop timer, the lock/unlock pair is removed entirely since it only existed to serialize with the timer callback, which hrtimer_cancel() already guarantees. Found by source code audit. Fixes: 4b3faf610cc6 ("xfrm: iptfs: add new iptfs xfrm mode impl") Cc: Christian Hopps Cc: Steffen Klassert Cc: stable@vger.kernel.org Signed-off-by: Tristan Madani Signed-off-by: Steffen Klassert --- net/xfrm/xfrm_iptfs.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/net/xfrm/xfrm_iptfs.c b/net/xfrm/xfrm_iptfs.c index aea63a000d1d..ad810d1f97c0 100644 --- a/net/xfrm/xfrm_iptfs.c +++ b/net/xfrm/xfrm_iptfs.c @@ -2730,8 +2730,9 @@ static void iptfs_destroy_state(struct xfrm_state *x) if (!xtfs) return; - spin_lock_bh(&xtfs->x->lock); hrtimer_cancel(&xtfs->iptfs_timer); + + spin_lock_bh(&xtfs->x->lock); __skb_queue_head_init(&list); skb_queue_splice_init(&xtfs->queue, &list); spin_unlock_bh(&xtfs->x->lock); @@ -2739,9 +2740,7 @@ static void iptfs_destroy_state(struct xfrm_state *x) while ((skb = __skb_dequeue(&list))) kfree_skb(skb); - spin_lock_bh(&xtfs->drop_lock); hrtimer_cancel(&xtfs->drop_timer); - spin_unlock_bh(&xtfs->drop_lock); if (xtfs->ra_newskb) kfree_skb(xtfs->ra_newskb); From c381039ade2e161ab08c0eda73c4f8b9a7115928 Mon Sep 17 00:00:00 2001 From: Wyatt Feng Date: Wed, 3 Jun 2026 00:46:27 +0800 Subject: [PATCH 179/257] xfrm: espintcp: do not reuse an in-progress partial send espintcp keeps a single in-flight transmit in ctx->partial. Before building a new sk_msg, espintcp_sendmsg() first tries to flush that state through espintcp_push_msgs(). For blocking callers, espintcp_push_msgs() may return success even when the previous partial send is still pending. espintcp_sendmsg() would then reinitialize emsg->skmsg and reuse ctx->partial while the old transfer still owns that state. Do not rebuild the send message when ctx->partial is still in progress. If espintcp_push_msgs() returns with emsg->len still set, fail the new send instead of overwriting the live partial state. This is a memory-safety fix: reusing the live partial-send state can leave a stale offset attached to a new sk_msg and lead to an out-of- bounds read in the send path. tcp_sendmsg_locked() already handles waiting for send buffer memory, so the fix here is just to preserve espintcp's one-message-at-a-time transmit state. Fixes: e27cca96cd68 ("xfrm: add espintcp (RFC 8229)") Cc: stable@kernel.org Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Zhengchuan Liang Reported-by: Xin Liu Assisted-by: Codex:GPT-5.4 Signed-off-by: Wyatt Feng Signed-off-by: Ren Wei Signed-off-by: Steffen Klassert --- net/xfrm/espintcp.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/xfrm/espintcp.c b/net/xfrm/espintcp.c index a2756186e13a..d9035546375e 100644 --- a/net/xfrm/espintcp.c +++ b/net/xfrm/espintcp.c @@ -346,6 +346,10 @@ static int espintcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t size) err = -ENOBUFS; goto unlock; } + if (emsg->len) { + err = -ENOBUFS; + goto unlock; + } sk_msg_init(&emsg->skmsg); while (1) { From ed46f39c47eb5530a9c161481a2080d3a869cfaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20L=C3=A9ger?= Date: Thu, 4 Jun 2026 09:07:13 -0700 Subject: [PATCH 180/257] io_uring/net: inherit IORING_CQE_F_BUF_MORE across bundle recv retries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a bundle recv retries inside io_recv_finish(), the merge logic OR the saved cflags from the previous iteration with the cflags returned by the new iteration: cflags = req->cqe.flags | (cflags & CQE_F_MASK); Bits listed in CQE_F_MASK are inherited from the new iteration, and all other bits (notably IORING_CQE_F_BUFFER and the buffer ID) come from the saved cflags. Before this change CQE_F_MASK covered only IORING_CQE_F_SOCK_NONEMPTY and IORING_CQE_F_MORE. When using provided buffer rings (IOU_PBUF_RING_INC) with incremental mode, and bundle recv, io_kbuf_inc_commit() can leave the head ring entry partially consumed, __io_put_kbufs() then sets IORING_CQE_F_BUF_MORE on the returned cflags so userspace knows the buffer ID will be reused for subsequent completions. Because IORING_CQE_F_BUF_MORE was not in CQE_F_MASK, the merge above silently dropped it whenever the final retry iteration partially consumed the buffer, and the subsequent req->cqe.flags = cflags & ~CQE_F_MASK save would have left a stale IORING_CQE_F_BUF_MORE in the carried-over cflags had one been present. Userspace would then wrongfully advance it ring head past an entry the kernel still uses. Add IORING_CQE_F_BUF_MORE to CQE_F_MASK so it is both inherited from the new iteration into the user-visible CQE and stripped from the saved cflags between iterations. Cc: stable@vger.kernel.org Signed-off-by: Clément Léger Assisted-by: Claude:claude-opus-4.6 Fixes: ae98dbf43d75 ("io_uring/kbuf: add support for incremental buffer consumption") Link: https://patch.msgid.link/20260604160715.2482972-1-cleger@meta.com Signed-off-by: Jens Axboe --- io_uring/net.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/io_uring/net.c b/io_uring/net.c index 8df15b639358..ee848eb65ec9 100644 --- a/io_uring/net.c +++ b/io_uring/net.c @@ -842,7 +842,8 @@ int io_recvmsg_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) } /* bits to clear in old and inherit in new cflags on bundle retry */ -#define CQE_F_MASK (IORING_CQE_F_SOCK_NONEMPTY|IORING_CQE_F_MORE) +#define CQE_F_MASK (IORING_CQE_F_SOCK_NONEMPTY|IORING_CQE_F_MORE|\ + IORING_CQE_F_BUF_MORE) /* * Finishes io_recv and io_recvmsg. From 49b32ddb87a3a109afecea89e55d70f73956b8bc Mon Sep 17 00:00:00 2001 From: Oliver Upton Date: Tue, 2 Jun 2026 09:59:00 -0700 Subject: [PATCH 181/257] KVM: arm64: nv: Fix handling of XN[0] when !FEAT_XNX XN has already been extracted from its bitfield position so using FIELD_PREP() on the mask that clears XN[0] is completely broken, having the effect of unconditionally granting execute permissions... Fix the obvious mistake by manipulating the right bit. Cc: stable@vger.kernel.org Fixes: d93febe2ed2e ("KVM: arm64: nv: Forward FEAT_XNX permissions to the shadow stage-2") Reviewed-by: Wei-Lin Chang Signed-off-by: Oliver Upton Link: https://patch.msgid.link/20260602165901.52800-2-oupton@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/include/asm/kvm_nested.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm64/include/asm/kvm_nested.h b/arch/arm64/include/asm/kvm_nested.h index dc2957662ff2..cdf3e8422ea1 100644 --- a/arch/arm64/include/asm/kvm_nested.h +++ b/arch/arm64/include/asm/kvm_nested.h @@ -132,7 +132,7 @@ static inline bool kvm_s2_trans_exec_el0(struct kvm *kvm, struct kvm_s2_trans *t u8 xn = FIELD_GET(KVM_PTE_LEAF_ATTR_HI_S2_XN, trans->desc); if (!kvm_has_xnx(kvm)) - xn &= FIELD_PREP(KVM_PTE_LEAF_ATTR_HI_S2_XN, 0b10); + xn &= 0b10; switch (xn) { case 0b00: @@ -148,7 +148,7 @@ static inline bool kvm_s2_trans_exec_el1(struct kvm *kvm, struct kvm_s2_trans *t u8 xn = FIELD_GET(KVM_PTE_LEAF_ATTR_HI_S2_XN, trans->desc); if (!kvm_has_xnx(kvm)) - xn &= FIELD_PREP(KVM_PTE_LEAF_ATTR_HI_S2_XN, 0b10); + xn &= 0b10; switch (xn) { case 0b00: From 17f073f78fc43280891ecde8f8ec3f84f98bb37c Mon Sep 17 00:00:00 2001 From: Oliver Upton Date: Tue, 2 Jun 2026 09:59:01 -0700 Subject: [PATCH 182/257] KVM: arm64: Correctly identify executable PTEs at stage-2 KVM invalidates the I-cache before installing an executable PTE on implementations without DIC. Unfortunately, support for FEAT_XNX broke this check as KVM_PTE_LEAF_ATTR_HI_S2_XN was expanded to a bitfield. Fix it by reusing kvm_pgtable_stage2_pte_prot() and testing the abstract permission bits instead. Fixes: 2608563b466b ("KVM: arm64: Add support for FEAT_XNX stage-2 permissions") Reported-by: Sashiko (gemini/gemini-3.1-pro-preview) Signed-off-by: Oliver Upton Reviewed-by: Wei-Lin Chang Link: https://patch.msgid.link/20260602165901.52800-3-oupton@kernel.org Signed-off-by: Marc Zyngier Cc: stable@vger.kernel.org --- arch/arm64/kvm/hyp/pgtable.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/arm64/kvm/hyp/pgtable.c b/arch/arm64/kvm/hyp/pgtable.c index 0c1defa5fb0f..91a7dfad6686 100644 --- a/arch/arm64/kvm/hyp/pgtable.c +++ b/arch/arm64/kvm/hyp/pgtable.c @@ -925,7 +925,9 @@ static bool stage2_pte_cacheable(struct kvm_pgtable *pgt, kvm_pte_t pte) static bool stage2_pte_executable(kvm_pte_t pte) { - return kvm_pte_valid(pte) && !(pte & KVM_PTE_LEAF_ATTR_HI_S2_XN); + enum kvm_pgtable_prot prot = kvm_pgtable_stage2_pte_prot(pte); + + return prot & (KVM_PGTABLE_PROT_UX | KVM_PGTABLE_PROT_PX); } static u64 stage2_map_walker_phys_addr(const struct kvm_pgtable_visit_ctx *ctx, From 65b09bfa8aa7ebe087093b591525385efb2d58b0 Mon Sep 17 00:00:00 2001 From: Zhou Yuhang Date: Wed, 20 May 2026 15:08:00 +0800 Subject: [PATCH 183/257] kconfig: Fix repeated include selftest expectation The err_repeated_inc test was added with an expected stderr fixture that does not match the diagnostic printed by kconfig. Running "make testconfig" currently fails in that test even though the parser reports the duplicated include correctly: [stderr] Kconfig.inc1:4: error: repeated inclusion of Kconfig.inc3 Kconfig.inc2:3: note: location of first inclusion of Kconfig.inc3 The fixture expects "Repeated" and "Location" with capital letters, but the diagnostic emitted by scripts/kconfig/util.c uses lowercase words. Update the fixture to match the real message. Fixes: 102d712ded3e ("kconfig: Error out on duplicated kconfig inclusion") Signed-off-by: Zhou Yuhang Tested-by: Nicolas Schier Reviewed-by: Nathan Chancellor Link: https://patch.msgid.link/20260520070800.2265479-1-zhouyuhang1010@163.com Signed-off-by: Nicolas Schier --- scripts/kconfig/tests/err_repeated_inc/expected_stderr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/kconfig/tests/err_repeated_inc/expected_stderr b/scripts/kconfig/tests/err_repeated_inc/expected_stderr index 95d90d6a93c5..53071430ea7d 100644 --- a/scripts/kconfig/tests/err_repeated_inc/expected_stderr +++ b/scripts/kconfig/tests/err_repeated_inc/expected_stderr @@ -1,2 +1,2 @@ -Kconfig.inc1:4: error: Repeated inclusion of Kconfig.inc3 -Kconfig.inc2:3: note: Location of first inclusion of Kconfig.inc3 +Kconfig.inc1:4: error: repeated inclusion of Kconfig.inc3 +Kconfig.inc2:3: note: location of first inclusion of Kconfig.inc3 From 1a4f03d22fb655e5f192244fb2c87d8066fcfca2 Mon Sep 17 00:00:00 2001 From: Simona Vetter Date: Thu, 4 Jun 2026 21:44:37 +0200 Subject: [PATCH 184/257] drm/gem: Try to fix change_handle ioctl, attempt 4 [airlied: just added some comments on how to reenable] On-list because the cat is out of the bag and we're clearly not good enough to figure this out in private. The story thus far: 5e28b7b94408 ("drm: Set old handle to NULL before prime swap in change_handle") tried to fix a race condition between the gem_close and gem_change_handle ioctls, but got a few things wrong: - There's a confusion with the local variable handle, which is actually the new handle, and so the two-stage trick was actually applied to the wrong idr slot. 7164d78559b0 ("drm/gem: fix race between change_handle and handle_delete") tried to fix that by adding yet another code block, but forgot to add the error handling. Which meant we now have two paths, both kinda wrong. - dc366607c41c ("drm: Replace old pointer to new idr") tried to apply another fix, but inconsistently, again because of the handle confusion - this would be the right fix (kinda, somewhat, it's a mess) if we'd do the two-stage approach for the new handle. Except that wasn't the intent of the original fix. We also didn't have an igt merged for the original ioctl, which is a big no-go. This was attempted to address off-list in the original bugfix, and amd QA people claimed the bug was fixed now. Very clearly that's not the case. Here's my attempt to sort this out: - Rename the local variable to new_handle, the old aliasing with args->handle is just too dangerously confusing. - Merge the gem obj lookup with the two-stage idr_replace so that we avoid getting ourselves confused there. - This means we don't have a surplus temporary reference anymore, only an inherited from the idr. A concurrent gem_close on the new_handle could steal that. Fix that with the same two-stage approach create_tail uses. This is a bit overkill as documented in the comment, but I also don't trust my ability to understand this all correctly, so go with the established pattern we have from other ioctls instead for maximum paranoia. - Adjust error paths. I've tried to make the error and success paths common, because they are identical except for which handle is removed and on which we call idr_replace to (re)install the object again. But that made things messier to read, so I've left it at the more verbose version, which unfortunately hides the symmetry in the entire code flow a bit. - While at it, also replace the 7 space indent with 1 tab. And finally, because I flat out don't trust my abilities here at all anymore: - Disable the ioctl until we have the igt situation and everything else sorted out on-list and with full consensus. v2: Sashiko noticed that I didn't handle the error path for idr_replace correctly, it must be checked with IS_ERR_OR_NULL like in gem_handle_delete. So yeah, definitely should just the existing paths 1:1 because this is endless amounts of tricky. Also add the Fixes: line for the original ioctl, I forgot that too. Reported-by: DARKNAVY (@DarkNavyOrg) Signed-off-by: Simona Vetter Fixes: dc366607c41c ("drm: Replace old pointer to new idr") Cc: syzbot+d7c9eed171647e421013@syzkaller.appspotmail.com Cc: stable@vger.kernel.org Cc: Edward Adam Davis Cc: Dave Airlie Cc: Maarten Lankhorst Cc: Maxime Ripard Cc: Thomas Zimmermann Fixes: 5e28b7b94408 ("drm: Set old handle to NULL before prime swap in change_handle") Cc: David Francis Cc: Puttimet Thammasaeng Cc: Christian Koenig Fixes: 7164d78559b0 ("drm/gem: fix race between change_handle and handle_delete") Cc: Zhenghang Xiao Fixes: 5e28b7b94408 ("drm: Set old handle to NULL before prime swap in change_handle") Reviewed-by: David Francis Signed-off-by: Dave Airlie Link: https://patch.msgid.link/20260604194437.1725314-1-simona.vetter@ffwll.ch --- drivers/gpu/drm/drm_gem.c | 73 +++++++++++++++++-------------------- drivers/gpu/drm/drm_ioctl.c | 3 +- 2 files changed, 36 insertions(+), 40 deletions(-) diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index e12cdf91f4dc..3b2448a3a9de 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -1015,12 +1015,25 @@ drm_gem_open_ioctl(struct drm_device *dev, void *data, return ret; } +/* + * This ioctl is disabled for security reasons but also it failed + * to follow process in terms of adding testing in igt and verifying + * all the corner cases which made fixing security bugs in it even + * harder than necessary. + * + * To re-enable this ioctl + * 1. land working IGT tests in igt-gpu-tools that cover + * all corner cases and race conditions. + * 2. handle idr_preload + * 3. handle == 0 + * 4. handle == new_handle semantics definition. + */ int drm_gem_change_handle_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_gem_change_handle *args = data; - struct drm_gem_object *obj, *idrobj; - int handle, ret; + struct drm_gem_object *obj; + int new_handle, ret; if (!drm_core_check_feature(dev, DRIVER_GEM)) return -EOPNOTSUPP; @@ -1028,52 +1041,36 @@ int drm_gem_change_handle_ioctl(struct drm_device *dev, void *data, /* idr_alloc() limitation. */ if (args->new_handle > INT_MAX) return -EINVAL; - handle = args->new_handle; + new_handle = args->new_handle; - obj = drm_gem_object_lookup(file_priv, args->handle); - if (!obj) - return -ENOENT; - - if (args->handle == handle) { - ret = 0; - goto out; - } + if (args->handle == new_handle) + return 0; mutex_lock(&file_priv->prime.lock); - spin_lock(&file_priv->table_lock); - - /* When create_tail allocs an obj idr, it needs to first alloc as NULL, - * then later replace with the correct object. This is not necessary - * here, because the only operations that could race are drm_prime - * bookkeeping, and we hold the prime lock. - */ - ret = idr_alloc(&file_priv->object_idr, obj, handle, handle + 1, + ret = idr_alloc(&file_priv->object_idr, NULL, new_handle, new_handle + 1, GFP_NOWAIT); - if (ret < 0) { - spin_unlock(&file_priv->table_lock); - goto out_unlock; - } + if (ret < 0) { + spin_unlock(&file_priv->table_lock); + goto out_unlock; + } - idrobj = idr_replace(&file_priv->object_idr, NULL, handle); - if (idrobj != obj) { - idr_replace(&file_priv->object_idr, idrobj, handle); - idr_remove(&file_priv->object_idr, args->new_handle); - spin_unlock(&file_priv->table_lock); - ret = -ENOENT; - goto out_unlock; - } - - idr_replace(&file_priv->object_idr, NULL, args->handle); + obj = idr_replace(&file_priv->object_idr, NULL, args->handle); + if (IS_ERR_OR_NULL(obj)) { + idr_remove(&file_priv->object_idr, new_handle); + spin_unlock(&file_priv->table_lock); + ret = -ENOENT; + goto out_unlock; + } spin_unlock(&file_priv->table_lock); if (obj->dma_buf) { ret = drm_prime_add_buf_handle(&file_priv->prime, obj->dma_buf, - handle); + new_handle); if (ret < 0) { spin_lock(&file_priv->table_lock); - idr_remove(&file_priv->object_idr, handle); + idr_remove(&file_priv->object_idr, new_handle); idr_replace(&file_priv->object_idr, obj, args->handle); spin_unlock(&file_priv->table_lock); goto out_unlock; @@ -1086,14 +1083,12 @@ int drm_gem_change_handle_ioctl(struct drm_device *dev, void *data, spin_lock(&file_priv->table_lock); idr_remove(&file_priv->object_idr, args->handle); - idrobj = idr_replace(&file_priv->object_idr, obj, handle); + obj = idr_replace(&file_priv->object_idr, obj, new_handle); spin_unlock(&file_priv->table_lock); - WARN_ON(idrobj != NULL); + WARN_ON(obj != NULL); out_unlock: mutex_unlock(&file_priv->prime.lock); -out: - drm_gem_object_put(obj); return ret; } diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c index ff193155129e..e2df4becce62 100644 --- a/drivers/gpu/drm/drm_ioctl.c +++ b/drivers/gpu/drm/drm_ioctl.c @@ -660,7 +660,8 @@ static const struct drm_ioctl_desc drm_ioctls[] = { DRM_IOCTL_DEF(DRM_IOCTL_GEM_CLOSE, drm_gem_close_ioctl, DRM_RENDER_ALLOW), DRM_IOCTL_DEF(DRM_IOCTL_GEM_FLINK, drm_gem_flink_ioctl, DRM_AUTH), DRM_IOCTL_DEF(DRM_IOCTL_GEM_OPEN, drm_gem_open_ioctl, DRM_AUTH), - DRM_IOCTL_DEF(DRM_IOCTL_GEM_CHANGE_HANDLE, drm_gem_change_handle_ioctl, DRM_RENDER_ALLOW), + /* see drm_gem.c:drm_gem_change_handle_ioctl for why this is invalid */ + DRM_IOCTL_DEF(DRM_IOCTL_GEM_CHANGE_HANDLE, drm_invalid_op, DRM_RENDER_ALLOW), DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETRESOURCES, drm_mode_getresources, 0), From f1fa677e428e8873486938086bd934dc18169b47 Mon Sep 17 00:00:00 2001 From: Petr Oros Date: Tue, 2 Jun 2026 15:55:10 -0700 Subject: [PATCH 185/257] ice: fix missing priority callbacks for U.FL DPLL pins The U.FL2 input pin advertises DPLL_PIN_CAPABILITIES_PRIORITY_CAN_CHANGE in its capability mask, but ice_dpll_pin_ufl_ops does not provide .prio_get and .prio_set callbacks. As a result the DPLL subsystem cannot report or accept priority for U.FL pins: pin-get omits the prio field on U.FL2 and pin-set with prio is rejected as invalid, even though the capability is present. This prevents user space from using priority to select or disable U.FL2 as a DPLL input source. Reproducer with iproute2 (dpll command): # dpll pin show board-label U.FL2 pin id 16: module-name ice board-label U.FL2 type ext capabilities priority-can-change|state-can-change parent-device: id 0 direction input state selectable phase-offset 0 /* note: no "prio" between "direction" and "state", even though priority-can-change is advertised */ # dpll pin set id 16 parent-device 0 prio 5 RTNETLINK answers: Operation not supported After the fix the prio field is reported by pin show and pin set with prio is accepted on U.FL2. Add the missing .prio_get and .prio_set callbacks to ice_dpll_pin_ufl_ops, reusing ice_dpll_sw_input_prio_{get,set}. The same ops struct is shared by U.FL1 and U.FL2: U.FL2 (input) delegates to the backing hardware input pin, while U.FL1 (output) does not advertise DPLL_PIN_CAPABILITIES_PRIORITY_CAN_CHANGE so the dpll core capability gate never invokes prio_set for it, and prio_get reports the OUTPUT sentinel (ICE_DPLL_PIN_PRIO_OUTPUT) on the output side exactly like the SMA path does today. Fixes: 2dd5d03c77e2 ("ice: redesign dpll sma/u.fl pins control") Reviewed-by: Aleksandr Loktionov Reviewed-by: Paul Menzel Signed-off-by: Petr Oros Tested-by: Rinitha S (A Contingent worker at Intel) Signed-off-by: Tony Nguyen Link: https://patch.msgid.link/20260602225513.393338-3-anthony.l.nguyen@intel.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/intel/ice/ice_dpll.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.c b/drivers/net/ethernet/intel/ice/ice_dpll.c index 892bc7c2e28b..0704e92ab043 100644 --- a/drivers/net/ethernet/intel/ice/ice_dpll.c +++ b/drivers/net/ethernet/intel/ice/ice_dpll.c @@ -2633,6 +2633,8 @@ static const struct dpll_pin_ops ice_dpll_pin_ufl_ops = { .state_on_dpll_set = ice_dpll_ufl_pin_state_set, .state_on_dpll_get = ice_dpll_sw_pin_state_get, .direction_get = ice_dpll_pin_sw_direction_get, + .prio_get = ice_dpll_sw_input_prio_get, + .prio_set = ice_dpll_sw_input_prio_set, .frequency_get = ice_dpll_sw_pin_frequency_get, .frequency_set = ice_dpll_sw_pin_frequency_set, .esync_set = ice_dpll_sw_esync_set, From 85b0cbc1f38bc1e38956a9e6d7b04d309b435697 Mon Sep 17 00:00:00 2001 From: Alok Tiwari Date: Tue, 2 Jun 2026 15:55:11 -0700 Subject: [PATCH 186/257] idpf: fix mailbox capability for set device clock time The current code incorrectly uses VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME for both direct and mailbox capabilities, causing mailbox-only support to be ignored and potentially reporting IDPF_PTP_NONE. Fixes: d5dba8f7206da ("idpf: add PTP clock configuration") Signed-off-by: Alok Tiwari Tested-by: Samuel Salin Reviewed-by: Aleksandr Loktionov Signed-off-by: Tony Nguyen Link: https://patch.msgid.link/20260602225513.393338-4-anthony.l.nguyen@intel.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/intel/idpf/idpf_ptp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/idpf/idpf_ptp.c b/drivers/net/ethernet/intel/idpf/idpf_ptp.c index 4a51d2727547..71fe8b2a8b4e 100644 --- a/drivers/net/ethernet/intel/idpf/idpf_ptp.c +++ b/drivers/net/ethernet/intel/idpf/idpf_ptp.c @@ -51,7 +51,7 @@ void idpf_ptp_get_features_access(const struct idpf_adapter *adapter) /* Set the device clock time */ direct = VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME; - mailbox = VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME; + mailbox = VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME_MB; ptp->set_dev_clk_time_access = idpf_ptp_get_access(adapter, direct, mailbox); From f9f25118faa4dd2b6e3d14a03d123bbdbd59925d Mon Sep 17 00:00:00 2001 From: ZhaoJinming Date: Thu, 4 Jun 2026 15:03:52 +0800 Subject: [PATCH 187/257] net: airoha: Add NULL check for of_reserved_mem_lookup() in airoha_qdma_init_hfwd_queues() of_reserved_mem_lookup() may return NULL if the reserved memory region referenced by the "memory-region" phandle is not found in the reserved memory table (e.g. due to a misconfigured DTS or a removed memory-region node). The current code dereferences the returned pointer without checking for NULL, leading to a kernel NULL pointer dereference at the following lines: dma_addr = rmem->base; // line 1156 num_desc = div_u64(rmem->size, buf_size); // line 1160 Add a NULL check after of_reserved_mem_lookup() and return -ENODEV if the lookup fails, which is consistent with the existing error handling for of_parse_phandle() failure in the same code block. Fixes: 3a1ce9e3d01b ("net: airoha: Add the capability to allocate hwfd buffers via reserved-memory") Cc: stable@vger.kernel.org Signed-off-by: ZhaoJinming Acked-by: Lorenzo Bianconi Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/airoha/airoha_eth.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c index eab6a98d62b9..31cdb11cd78d 100644 --- a/drivers/net/ethernet/airoha/airoha_eth.c +++ b/drivers/net/ethernet/airoha/airoha_eth.c @@ -1153,6 +1153,9 @@ static int airoha_qdma_init_hfwd_queues(struct airoha_qdma *qdma) rmem = of_reserved_mem_lookup(np); of_node_put(np); + if (!rmem) + return -ENODEV; + dma_addr = rmem->base; /* Compute the number of hw descriptors according to the * reserved memory size and the payload buffer size From 954981dbbfbd78f21d2fbac1ac0742dbf38b4e69 Mon Sep 17 00:00:00 2001 From: Arthur Kiyanovski Date: Thu, 4 Jun 2026 08:07:04 +0000 Subject: [PATCH 188/257] net: ena: PHC: Add missing barrier Add dma_rmb() barrier after req_id completion check in ena_com_phc_get_timestamp(). On weakly-ordered architectures, payload fields may be read before req_id is observed as updated. Fixes: e0ea34158ee8 ("net: ena: Add PHC support in the ENA driver") Closes: https://sashiko.dev/#/patchset/20260430032507.11586-1-akiyano%40amazon.com Signed-off-by: Arthur Kiyanovski Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/amazon/ena/ena_com.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/ethernet/amazon/ena/ena_com.c b/drivers/net/ethernet/amazon/ena/ena_com.c index 8c86789d867a..297fb36ab8c1 100644 --- a/drivers/net/ethernet/amazon/ena/ena_com.c +++ b/drivers/net/ethernet/amazon/ena/ena_com.c @@ -1880,6 +1880,11 @@ int ena_com_phc_get_timestamp(struct ena_com_dev *ena_dev, u64 *timestamp) continue; } + /* Ensure PHC payload (timestamp, error_flags) is read + * after req_id update is observed + */ + dma_rmb(); + /* req_id was updated by the device which indicates that * PHC timestamp and error_flags are updated too, * checking errors before retrieving timestamp From 3847d94783c0b893c27ff0b26a3325796d9444c6 Mon Sep 17 00:00:00 2001 From: Vikas Gupta Date: Thu, 4 Jun 2026 22:07:09 +0530 Subject: [PATCH 189/257] bnge: fix context mem iteration The firmware advertises context memory (backing store) types through a linked list, with BNGE_CTX_INV serving as the end-of-list sentinel. However, the driver incorrectly assumes that the list is strictly ordered and prematurely terminates traversal when it encounters an unrecognized type (>=BNGE_CTX_V2_MAX). As a result, any valid context types that appear later in the chain are silently skipped, leading to incomplete memory configuration and eventual driver load failure. Fix this by traversing the entire list until the BNGE_CTX_INV sentinel is reached, while safely ignoring only those context types that fall outside the supported range. Fixes: 29c5b358f385 ("bng_en: Add backing store support") Signed-off-by: Vikas Gupta Reviewed-by: Dharmender Garg Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c b/drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c index eb11800f5573..1c9cfec1b633 100644 --- a/drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c +++ b/drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c @@ -277,7 +277,7 @@ int bnge_hwrm_func_backing_store_qcaps(struct bnge_dev *bd) struct hwrm_func_backing_store_qcaps_v2_output *resp; struct hwrm_func_backing_store_qcaps_v2_input *req; struct bnge_ctx_mem_info *ctx; - u16 type; + u16 type, next_type; int rc; if (bd->ctx) @@ -294,8 +294,8 @@ int bnge_hwrm_func_backing_store_qcaps(struct bnge_dev *bd) resp = bnge_hwrm_req_hold(bd, req); - for (type = 0; type < BNGE_CTX_V2_MAX; ) { - struct bnge_ctx_mem_type *ctxm = &ctx->ctx_arr[type]; + for (type = 0; type < BNGE_CTX_INV; type = next_type) { + struct bnge_ctx_mem_type *ctxm; u8 init_val, init_off, i; __le32 *p; u32 flags; @@ -304,8 +304,14 @@ int bnge_hwrm_func_backing_store_qcaps(struct bnge_dev *bd) rc = bnge_hwrm_req_send(bd, req); if (rc) goto ctx_done; + + next_type = le16_to_cpu(resp->next_valid_type); + if (type >= BNGE_CTX_V2_MAX) + continue; + + ctxm = &ctx->ctx_arr[type]; flags = le32_to_cpu(resp->flags); - type = le16_to_cpu(resp->next_valid_type); + if (!(flags & FUNC_BACKING_STORE_QCAPS_V2_RESP_FLAGS_TYPE_VALID)) continue; From 9772589b57e44aedc240211c5c3f7a684a034d3a Mon Sep 17 00:00:00 2001 From: Chenguang Zhao Date: Wed, 3 Jun 2026 09:13:53 +0800 Subject: [PATCH 190/257] netlabel: validate unlabeled address and mask attribute lengths netlbl_unlabel_addrinfo_get() used the address attribute length to determine whether the attribute data could be read as an IPv4 or IPv6 address, but did not independently validate the corresponding mask attribute length. A crafted Generic Netlink request could therefore provide a valid IPv4/IPv6 address attribute with a shorter mask attribute, which would later be read as a full struct in_addr or struct in6_addr. NLA_BINARY policy lengths are maximum lengths by default, so use NLA_POLICY_EXACT_LEN() for the unlabeled IPv4/IPv6 address and mask attributes. This rejects short attributes during policy validation and also exposes the exact length requirements through policy introspection. Fixes: 8cc44579d1bd ("NetLabel: Introduce static network labels for unlabeled connections") Signed-off-by: Chenguang Zhao Signed-off-by: Jakub Kicinski --- net/netlabel/netlabel_unlabeled.c | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/net/netlabel/netlabel_unlabeled.c b/net/netlabel/netlabel_unlabeled.c index ca7a9e2a3de7..870e7699326a 100644 --- a/net/netlabel/netlabel_unlabeled.c +++ b/net/netlabel/netlabel_unlabeled.c @@ -114,14 +114,14 @@ static struct genl_family netlbl_unlabel_gnl_family; /* NetLabel Netlink attribute policy */ static const struct nla_policy netlbl_unlabel_genl_policy[NLBL_UNLABEL_A_MAX + 1] = { [NLBL_UNLABEL_A_ACPTFLG] = { .type = NLA_U8 }, - [NLBL_UNLABEL_A_IPV6ADDR] = { .type = NLA_BINARY, - .len = sizeof(struct in6_addr) }, - [NLBL_UNLABEL_A_IPV6MASK] = { .type = NLA_BINARY, - .len = sizeof(struct in6_addr) }, - [NLBL_UNLABEL_A_IPV4ADDR] = { .type = NLA_BINARY, - .len = sizeof(struct in_addr) }, - [NLBL_UNLABEL_A_IPV4MASK] = { .type = NLA_BINARY, - .len = sizeof(struct in_addr) }, + [NLBL_UNLABEL_A_IPV6ADDR] = + NLA_POLICY_EXACT_LEN(sizeof(struct in6_addr)), + [NLBL_UNLABEL_A_IPV6MASK] = + NLA_POLICY_EXACT_LEN(sizeof(struct in6_addr)), + [NLBL_UNLABEL_A_IPV4ADDR] = + NLA_POLICY_EXACT_LEN(sizeof(struct in_addr)), + [NLBL_UNLABEL_A_IPV4MASK] = + NLA_POLICY_EXACT_LEN(sizeof(struct in_addr)), [NLBL_UNLABEL_A_IFACE] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ - 1 }, [NLBL_UNLABEL_A_SECCTX] = { .type = NLA_BINARY } @@ -757,24 +757,14 @@ static int netlbl_unlabel_addrinfo_get(struct genl_info *info, void **mask, u32 *len) { - u32 addr_len; - if (info->attrs[NLBL_UNLABEL_A_IPV4ADDR] && info->attrs[NLBL_UNLABEL_A_IPV4MASK]) { - addr_len = nla_len(info->attrs[NLBL_UNLABEL_A_IPV4ADDR]); - if (addr_len != sizeof(struct in_addr) && - addr_len != nla_len(info->attrs[NLBL_UNLABEL_A_IPV4MASK])) - return -EINVAL; - *len = addr_len; + *len = sizeof(struct in_addr); *addr = nla_data(info->attrs[NLBL_UNLABEL_A_IPV4ADDR]); *mask = nla_data(info->attrs[NLBL_UNLABEL_A_IPV4MASK]); return 0; } else if (info->attrs[NLBL_UNLABEL_A_IPV6ADDR]) { - addr_len = nla_len(info->attrs[NLBL_UNLABEL_A_IPV6ADDR]); - if (addr_len != sizeof(struct in6_addr) && - addr_len != nla_len(info->attrs[NLBL_UNLABEL_A_IPV6MASK])) - return -EINVAL; - *len = addr_len; + *len = sizeof(struct in6_addr); *addr = nla_data(info->attrs[NLBL_UNLABEL_A_IPV6ADDR]); *mask = nla_data(info->attrs[NLBL_UNLABEL_A_IPV6MASK]); return 0; From fb402386af4cdce108ff991a796386de55439735 Mon Sep 17 00:00:00 2001 From: Cryolitia PukNgae Date: Fri, 5 Jun 2026 15:27:21 +0800 Subject: [PATCH 191/257] Input: atkbd - skip deactivate for HONOR BCC-N's internal keyboard After commit 9cf6e24c9fbf17e52de9fff07f12be7565ea6d61 ("Input: atkbd - do not skip atkbd_deactivate() when skipping ATKBD_CMD_GETID"), HONOR BCC-N, aka HONOR MagicBook 14 2026's internal keyboard stops working. Adding the atkbd_deactivate_fixup quirk fixes it. DMI: HONOR BCC-N/BCC-N-PCB, BIOS 1.04 04/07/2026 Fixes: 9cf6e24c9fbf17e52de9fff07f12be7565ea6d61 ("Input: atkbd - do not skip atkbd_deactivate() when skipping ATKBD_CMD_GETID") Reported-by: Hongfei Ren Link: https://github.com/colorcube/Linux-on-Honor-Magicbook-14-Pro/issues/1#issuecomment-4562679891 Tested-by: Hongfei Ren Cc: stable@kernel.org Signed-off-by: Cryolitia PukNgae Link: https://patch.msgid.link/20260605-honor-v1-1-78e05e491193@linux.dev Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/atkbd.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c index 217e66ee36a1..8cb4dc6fb165 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c @@ -1931,6 +1931,13 @@ static const struct dmi_system_id atkbd_dmi_quirk_table[] __initconst = { }, .callback = atkbd_deactivate_fixup, }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "HONOR"), + DMI_MATCH(DMI_PRODUCT_NAME, "BCC-N"), + }, + .callback = atkbd_deactivate_fixup, + }, { } }; From 979c294509f9248fe1e7c358d582fb37dd5ca12d Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Thu, 4 Jun 2026 17:33:21 -0700 Subject: [PATCH 192/257] cfi: Include uaccess.h for get_kernel_nofault() After commit 0652a3daa787 ("tracing: Fix CFI violation in probestub being called by tprobes"), there are many build errors when building ARCH=arm multi_v7_defconfig + CONFIG_CFI=y like: In file included from drivers/base/devres.c:17: In file included from drivers/base/trace.h:16: In file included from include/linux/tracepoint.h:23: include/linux/cfi.h:44:6: error: call to undeclared function 'get_kernel_nofault'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration] 44 | if (get_kernel_nofault(hash, func - cfi_get_offset())) | ^ 1 error generated. get_kernel_nofault() is called in the generic version of cfi_get_func_hash() but nothing ensures uaccess.h is always included for a proper expansion and prototype. Include uaccess.h in cfi.h to clear up the errors. Cc: stable@vger.kernel.org Fixes: 0652a3daa787 ("tracing: Fix CFI violation in probestub being called by tprobes") Signed-off-by: Nathan Chancellor Acked-by: Masami Hiramatsu (Google) Reviewed-by: Sami Tolvanen Signed-off-by: Linus Torvalds --- include/linux/cfi.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/cfi.h b/include/linux/cfi.h index 1fd22ea6eba4..0f220d29225c 100644 --- a/include/linux/cfi.h +++ b/include/linux/cfi.h @@ -9,6 +9,7 @@ #include #include +#include #include #ifdef CONFIG_CFI From e3573f739e3dadab57ec80488d07e05c8f6e82d3 Mon Sep 17 00:00:00 2001 From: Andreas Schwab Date: Fri, 22 May 2026 00:34:30 +0200 Subject: [PATCH 193/257] riscv/ptrace: Use USER_REGSET_NOTE_TYPE for REGSET_CFI Fixes a warning while dumping core: [54983.546369][ C7] WARNING: [!note_name] fs/binfmt_elf.c:1771 at elf_core_dump+0x910/0xf68, CPU#7: abort01/31982 Fixes: 2af7c9cf021c ("riscv/ptrace: expose riscv CFI status and state via ptrace and in core files") Signed-off-by: Andreas Schwab Link: https://patch.msgid.link/87y0hcxuh5.fsf@igel.home Signed-off-by: Paul Walmsley --- arch/riscv/kernel/ptrace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/kernel/ptrace.c b/arch/riscv/kernel/ptrace.c index 793bcee46182..f336a183667e 100644 --- a/arch/riscv/kernel/ptrace.c +++ b/arch/riscv/kernel/ptrace.c @@ -413,7 +413,7 @@ static struct user_regset riscv_user_regset[] __ro_after_init = { #endif #ifdef CONFIG_RISCV_USER_CFI [REGSET_CFI] = { - .core_note_type = NT_RISCV_USER_CFI, + USER_REGSET_NOTE_TYPE(RISCV_USER_CFI), .align = sizeof(__u64), .n = sizeof(struct user_cfi_state) / sizeof(__u64), .size = sizeof(__u64), From aad60bdd0b5a6dfa40454ab03ce658408eaf4615 Mon Sep 17 00:00:00 2001 From: Nam Cao Date: Tue, 7 Apr 2026 14:06:39 +0200 Subject: [PATCH 194/257] riscv: Fix fast_unaligned_access_speed_key not getting initialized The static key fast_unaligned_access_speed_key is supposed to be initialized after check_unaligned_access_all_cpus() has been completed. However, check_unaligned_access_all_cpus() has been moved to late_initcall while setting fast_unaligned_access_speed_key still happens at arch_initcall_sync, thus the static key does not get properly initialized. fast_unaligned_access_speed_key can still be initialized in CPU hotplug events, but that cannot be relied on. Move fast_unaligned_access_speed_key's initialization into check_unaligned_access_all_cpus() to fix this issue. This also prevent someone from moving one initcall while forgetting the other in the future. Fixes: 6455c6c11827 ("riscv: Clean up & optimize unaligned scalar access probe") Reported-by: Michael Neuling Closes: https://lore.kernel.org/linux-riscv/CAEjGV6y0=bSLp_wrS0uHFj1S2TCRtz4GKzaU5O-L1VV-EL7Nnw@mail.gmail.com/ Signed-off-by: Nam Cao Link: https://patch.msgid.link/20260407120639.4006031-1-namcao@linutronix.de Signed-off-by: Paul Walmsley --- arch/riscv/kernel/unaligned_access_speed.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/arch/riscv/kernel/unaligned_access_speed.c b/arch/riscv/kernel/unaligned_access_speed.c index 485ab1d105d3..11c781a4de73 100644 --- a/arch/riscv/kernel/unaligned_access_speed.c +++ b/arch/riscv/kernel/unaligned_access_speed.c @@ -235,17 +235,6 @@ static void set_unaligned_access_static_branches(void) modify_unaligned_access_branches(&fast_and_online, num_online_cpus()); } -static int __init lock_and_set_unaligned_access_static_branch(void) -{ - cpus_read_lock(); - set_unaligned_access_static_branches(); - cpus_read_unlock(); - - return 0; -} - -arch_initcall_sync(lock_and_set_unaligned_access_static_branch); - static int riscv_online_cpu(unsigned int cpu) { int ret = cpu_online_unaligned_access_init(cpu); @@ -440,6 +429,10 @@ static int __init check_unaligned_access_all_cpus(void) cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN, "riscv:online", riscv_online_cpu_vec, NULL); + cpus_read_lock(); + set_unaligned_access_static_branches(); + cpus_read_unlock(); + return 0; } From 15b4155138505669d3d43d7692459ee8ea2a86e7 Mon Sep 17 00:00:00 2001 From: Richard Patel Date: Mon, 18 May 2026 18:39:18 +0000 Subject: [PATCH 195/257] riscv: cfi: reject unknown flags in PR_SET_CFI prctl(PR_SET_CFI,PR_CFI_BRANCH_LANDING_PADS) silently ignored unknown control values. Only PR_CFI_{ENABLE,DISABLE,LOCK} should be permitted. This changes the behavior of the uABI (fails previously accepted bits with EINVAL). Fixes: 08ee1559052b ("prctl: cfi: change the branch landing pad prctl()s to be more descriptive") Signed-off-by: Richard Patel Link: https://patch.msgid.link/20260518183918.322545-1-ripatel@wii.dev [pjw@kernel.org: change the patch description to note that although this is a uABI change, it does not break the uABI] Signed-off-by: Paul Walmsley --- arch/riscv/include/asm/usercfi.h | 1 + arch/riscv/kernel/usercfi.c | 3 +++ tools/testing/selftests/riscv/cfi/cfitests.c | 6 ++++++ 3 files changed, 10 insertions(+) diff --git a/arch/riscv/include/asm/usercfi.h b/arch/riscv/include/asm/usercfi.h index f56966edbf5c..61ee02cee297 100644 --- a/arch/riscv/include/asm/usercfi.h +++ b/arch/riscv/include/asm/usercfi.h @@ -50,6 +50,7 @@ void set_indir_lp_status(struct task_struct *task, bool enable); void set_indir_lp_lock(struct task_struct *task, bool lock); #define PR_SHADOW_STACK_SUPPORTED_STATUS_MASK (PR_SHADOW_STACK_ENABLE) +#define PR_CFI_SUPPORTED_STATUS_MASK (PR_CFI_ENABLE | PR_CFI_DISABLE | PR_CFI_LOCK) #else diff --git a/arch/riscv/kernel/usercfi.c b/arch/riscv/kernel/usercfi.c index cbfb4e495e9f..5a7113d69bad 100644 --- a/arch/riscv/kernel/usercfi.c +++ b/arch/riscv/kernel/usercfi.c @@ -467,6 +467,9 @@ int arch_prctl_set_branch_landing_pad_state(struct task_struct *t, unsigned long if (!is_user_lpad_enabled()) return -EINVAL; + if (state & ~PR_CFI_SUPPORTED_STATUS_MASK) + return -EINVAL; + /* indirect branch tracking is locked and further can't be modified by user */ if (is_indir_lp_locked(t)) return -EINVAL; diff --git a/tools/testing/selftests/riscv/cfi/cfitests.c b/tools/testing/selftests/riscv/cfi/cfitests.c index 39d097b6881f..0e3943461e7d 100644 --- a/tools/testing/selftests/riscv/cfi/cfitests.c +++ b/tools/testing/selftests/riscv/cfi/cfitests.c @@ -141,6 +141,12 @@ int main(int argc, char *argv[]) ksft_print_msg("Starting risc-v tests\n"); + /* Test unknown PR_CFI bits */ + ret = my_syscall5(__NR_prctl, PR_SET_CFI, PR_CFI_BRANCH_LANDING_PADS, + PR_CFI_ENABLE | 0xffff0, 0, 0); + if (!ret) + ksft_exit_fail_msg("PR_SET_CFI accepted reserved branch landing pad bits\n"); + /* * Landing pad test. Not a lot of kernel changes to support landing * pads for user mode except lighting up a bit in senvcfg via a prctl. From 4549871118cf616eecdd2d939f78e3b9e1dddc48 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 7 Jun 2026 15:37:58 -0700 Subject: [PATCH 196/257] Linux 7.1-rc7 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index e3aabcda1ff7..e156e2696efe 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ VERSION = 7 PATCHLEVEL = 1 SUBLEVEL = 0 -EXTRAVERSION = -rc6 +EXTRAVERSION = -rc7 NAME = Baby Opossum Posse # *DOCUMENTATION* From b9ad50d7505ebd48282ec3630258dc820fc85c81 Mon Sep 17 00:00:00 2001 From: Yun Zhou Date: Mon, 8 Jun 2026 16:43:34 +0800 Subject: [PATCH 197/257] gpio: mvebu: fix NULL pointer dereference in suspend/resume mvebu_pwm_suspend() and mvebu_pwm_resume() are called for all GPIO banks during suspend/resume, but not all banks have PWM functionality. GPIO banks without PWM have mvchip->mvpwm set to NULL. Calling mvebu_pwm_suspend() with mvpwm == NULL causes a NULL pointer dereference when it tries to access mvpwm->blink_select. Unable to handle kernel NULL pointer dereference at virtual address 00000020 when write [00000020] *pgd=00000000 Internal error: Oops: 815 [#1] PREEMPT ARM Modules linked in: CPU: 0 UID: 0 PID: 406 Comm: sh Not tainted 6.12.74-rt12-yocto-standard-g4e96f98fb7db-dirty #353 Hardware name: Marvell Armada 370/XP (Device Tree) PC is at regmap_mmio_read+0x38/0x54 LR is at regmap_mmio_read+0x38/0x54 pc : [] lr : [] psr: 200f0013 sp : f0c11d10 ip : 00000000 fp : c100d2f0 r10: c14fb854 r9 : 00000000 r8 : 00000000 r7 : c1799c00 r6 : 00000020 r5 : 00000020 r4 : c179c7c0 r3 : f0a231a0 r2 : 00000020 r1 : 00000020 r0 : 00000000 Flags: nzCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment none Control: 10c5387d Table: 135ec059 DAC: 00000051 Call trace: regmap_mmio_read from _regmap_bus_reg_read+0x78/0xac _regmap_bus_reg_read from _regmap_read+0x60/0x154 _regmap_read from regmap_read+0x3c/0x60 regmap_read from mvebu_gpio_suspend+0xa4/0x14c mvebu_gpio_suspend from dpm_run_callback+0x54/0x180 dpm_run_callback from device_suspend+0x124/0x630 device_suspend from dpm_suspend+0x124/0x270 dpm_suspend from dpm_suspend_start+0x64/0x6c dpm_suspend_start from suspend_devices_and_enter+0x140/0x8e8 suspend_devices_and_enter from pm_suspend+0x2fc/0x308 pm_suspend from state_store+0x6c/0xc8 state_store from kernfs_fop_write_iter+0x10c/0x1f8 kernfs_fop_write_iter from vfs_write+0x270/0x468 vfs_write from ksys_write+0x70/0xf0 ksys_write from ret_fast_syscall+0x0/0x54 Add a NULL check for mvchip->mvpwm before calling the PWM suspend/resume functions. Fixes: 757642f9a584 ("gpio: mvebu: Add limited PWM support") Signed-off-by: Yun Zhou Link: https://patch.msgid.link/20260608084334.2960803-1-yun.zhou@windriver.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-mvebu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c index 22c36b79e249..c030d1f00abc 100644 --- a/drivers/gpio/gpio-mvebu.c +++ b/drivers/gpio/gpio-mvebu.c @@ -996,7 +996,7 @@ static int mvebu_gpio_suspend(struct platform_device *pdev, pm_message_t state) BUG(); } - if (IS_REACHABLE(CONFIG_PWM)) + if (IS_REACHABLE(CONFIG_PWM) && mvchip->mvpwm) mvebu_pwm_suspend(mvchip); return 0; @@ -1048,7 +1048,7 @@ static int mvebu_gpio_resume(struct platform_device *pdev) BUG(); } - if (IS_REACHABLE(CONFIG_PWM)) + if (IS_REACHABLE(CONFIG_PWM) && mvchip->mvpwm) mvebu_pwm_resume(mvchip); return 0; From badad6fad60def1b9805559dd81dbab3d97b82aa Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Thu, 4 Jun 2026 15:03:13 -0300 Subject: [PATCH 198/257] RDMA: During rereg_mr ensure that REREG_ACCESS is compatible If IB_MR_REREG_ACCESS changes from RO to RW then the umem has to be re-evaluated to ensure it is properly pinned as RW. Since the umem is hidden inside each driver's mr struct add a ib_umem_check_rereg() function that each driver has to call before processing IB_MR_REREG_ACCESS. mlx4 has to retain its duplicate ib_access_writable check because it implements IB_MR_REREG_ACCESS | IB_MR_REREG_TRANS by changing both items in place sequentially while the MR is live, so it will continue to not support this combination. Cc: stable@vger.kernel.org Fixes: b40656aa7d55 ("RDMA/umem: remove FOLL_FORCE usage") Link: https://patch.msgid.link/r/0-v1-06fb1a2d6cf5+107-rereg_access_jgg@nvidia.com Reported-by: Philip Tsukerman Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/umem.c | 16 ++++++++++++++++ drivers/infiniband/hw/hns/hns_roce_mr.c | 4 ++++ drivers/infiniband/hw/irdma/verbs.c | 4 ++++ drivers/infiniband/hw/mlx4/mr.c | 4 ++++ drivers/infiniband/hw/mlx5/mr.c | 4 ++++ drivers/infiniband/sw/rxe/rxe_verbs.c | 5 +++++ include/rdma/ib_umem.h | 8 ++++++++ 7 files changed, 45 insertions(+) diff --git a/drivers/infiniband/core/umem.c b/drivers/infiniband/core/umem.c index 786fa1aa8e55..4b055712b0d0 100644 --- a/drivers/infiniband/core/umem.c +++ b/drivers/infiniband/core/umem.c @@ -332,3 +332,19 @@ int ib_umem_copy_from(void *dst, struct ib_umem *umem, size_t offset, return 0; } EXPORT_SYMBOL(ib_umem_copy_from); + +/* + * Called during rereg mr if the driver is able to re-use a umem for + * IB_MR_REREG_ACCESS. + */ +int ib_umem_check_rereg(struct ib_umem *umem, int flags, int new_access_flags) +{ + if (!umem) + return 0; + + if ((flags & IB_MR_REREG_ACCESS) && !(flags & IB_MR_REREG_TRANS)) + if (ib_access_writable(new_access_flags) && !umem->writable) + return -EACCES; + return 0; +} +EXPORT_SYMBOL(ib_umem_check_rereg); diff --git a/drivers/infiniband/hw/hns/hns_roce_mr.c b/drivers/infiniband/hw/hns/hns_roce_mr.c index 896af1828a38..25bfd3970f5b 100644 --- a/drivers/infiniband/hw/hns/hns_roce_mr.c +++ b/drivers/infiniband/hw/hns/hns_roce_mr.c @@ -300,6 +300,10 @@ struct ib_mr *hns_roce_rereg_user_mr(struct ib_mr *ibmr, int flags, u64 start, goto err_out; } + ret = ib_umem_check_rereg(mr->pbl_mtr.umem, flags, mr_access_flags); + if (ret) + goto err_out; + mailbox = hns_roce_alloc_cmd_mailbox(hr_dev); ret = PTR_ERR_OR_ZERO(mailbox); if (ret) diff --git a/drivers/infiniband/hw/irdma/verbs.c b/drivers/infiniband/hw/irdma/verbs.c index 17086048d2d7..8cd427532805 100644 --- a/drivers/infiniband/hw/irdma/verbs.c +++ b/drivers/infiniband/hw/irdma/verbs.c @@ -3803,6 +3803,10 @@ static struct ib_mr *irdma_rereg_user_mr(struct ib_mr *ib_mr, int flags, if (flags & ~(IB_MR_REREG_TRANS | IB_MR_REREG_PD | IB_MR_REREG_ACCESS)) return ERR_PTR(-EOPNOTSUPP); + ret = ib_umem_check_rereg(iwmr->region, flags, new_access); + if (ret) + return ERR_PTR(ret); + if (dmabuf_revocable) { umem_dmabuf = to_ib_umem_dmabuf(iwmr->region); diff --git a/drivers/infiniband/hw/mlx4/mr.c b/drivers/infiniband/hw/mlx4/mr.c index 650b4a9121ff..6747bca30677 100644 --- a/drivers/infiniband/hw/mlx4/mr.c +++ b/drivers/infiniband/hw/mlx4/mr.c @@ -209,6 +209,10 @@ struct ib_mr *mlx4_ib_rereg_user_mr(struct ib_mr *mr, int flags, u64 start, struct mlx4_mpt_entry **pmpt_entry = &mpt_entry; int err; + err = ib_umem_check_rereg(mmr->umem, flags, mr_access_flags); + if (err) + return ERR_PTR(err); + /* Since we synchronize this call and mlx4_ib_dereg_mr via uverbs, * we assume that the calls can't run concurrently. Otherwise, a * race exists. diff --git a/drivers/infiniband/hw/mlx5/mr.c b/drivers/infiniband/hw/mlx5/mr.c index 3b6da45061a5..fb40b44496f4 100644 --- a/drivers/infiniband/hw/mlx5/mr.c +++ b/drivers/infiniband/hw/mlx5/mr.c @@ -1179,6 +1179,10 @@ struct ib_mr *mlx5_ib_rereg_user_mr(struct ib_mr *ib_mr, int flags, u64 start, if (flags & ~(IB_MR_REREG_TRANS | IB_MR_REREG_PD | IB_MR_REREG_ACCESS)) return ERR_PTR(-EOPNOTSUPP); + err = ib_umem_check_rereg(mr->umem, flags, new_access_flags); + if (err) + return ERR_PTR(err); + if (!(flags & IB_MR_REREG_ACCESS)) new_access_flags = mr->access_flags; if (!(flags & IB_MR_REREG_PD)) diff --git a/drivers/infiniband/sw/rxe/rxe_verbs.c b/drivers/infiniband/sw/rxe/rxe_verbs.c index 4d4891dc2884..4cf04a44189c 100644 --- a/drivers/infiniband/sw/rxe/rxe_verbs.c +++ b/drivers/infiniband/sw/rxe/rxe_verbs.c @@ -1319,6 +1319,7 @@ static struct ib_mr *rxe_rereg_user_mr(struct ib_mr *ibmr, int flags, struct rxe_mr *mr = to_rmr(ibmr); struct rxe_pd *old_pd = to_rpd(ibmr->pd); struct rxe_pd *pd = to_rpd(ibpd); + int err; /* for now only support the two easy cases: * rereg_pd and rereg_access @@ -1328,6 +1329,10 @@ static struct ib_mr *rxe_rereg_user_mr(struct ib_mr *ibmr, int flags, return ERR_PTR(-EOPNOTSUPP); } + err = ib_umem_check_rereg(mr->umem, flags, access); + if (err) + return ERR_PTR(err); + if (flags & IB_MR_REREG_PD) { rxe_put(old_pd); rxe_get(pd); diff --git a/include/rdma/ib_umem.h b/include/rdma/ib_umem.h index 2ad52cc1d52b..49172098a8de 100644 --- a/include/rdma/ib_umem.h +++ b/include/rdma/ib_umem.h @@ -156,6 +156,8 @@ void ib_umem_dmabuf_revoke_lock(struct ib_umem_dmabuf *umem_dmabuf); void ib_umem_dmabuf_revoke_unlock(struct ib_umem_dmabuf *umem_dmabuf); void ib_umem_dmabuf_revoke(struct ib_umem_dmabuf *umem_dmabuf); +int ib_umem_check_rereg(struct ib_umem *umem, int flags, int new_access_flags); + #else /* CONFIG_INFINIBAND_USER_MEM */ #include @@ -230,5 +232,11 @@ static inline void ib_umem_dmabuf_revoke_lock(struct ib_umem_dmabuf *umem_dmabuf static inline void ib_umem_dmabuf_revoke_unlock(struct ib_umem_dmabuf *umem_dmabuf) {} static inline void ib_umem_dmabuf_revoke(struct ib_umem_dmabuf *umem_dmabuf) {} +static inline int ib_umem_check_rereg(struct ib_umem *umem, int flags, + int new_access_flags) +{ + return -EOPNOTSUPP; +} + #endif /* CONFIG_INFINIBAND_USER_MEM */ #endif /* IB_UMEM_H */ From 29e7b925ae6df64894e82ab6419994dc25580a8a Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Tue, 2 Jun 2026 15:46:42 -0400 Subject: [PATCH 199/257] IB/isert: Reject login PDUs shorter than ISER_HEADERS_LEN In drivers/infiniband/ulp/isert/ib_isert.c, isert_login_recv_done() computes the login request payload length as wc->byte_len minus ISER_HEADERS_LEN with no lower bound, and login_req_len is a signed int. A remote iSER initiator can post a login Send work request carrying fewer than ISER_HEADERS_LEN (76) bytes, so the subtraction underflows and login_req_len becomes negative. isert_rx_login_req() then reads that negative length back into a signed int, takes size = min(rx_buflen, MAX_KEY_VALUE_PAIRS), and because the min() is signed it keeps the negative value; the value is then passed as the memcpy() length and sign-extended to a multi-gigabyte size_t. The copy into the 8192-byte login->req_buf runs far out of bounds and faults, crashing the target node. The login phase precedes iSCSI authentication, so no credentials are required to reach this path. Reject any login PDU shorter than ISER_HEADERS_LEN before the subtraction, mirroring the existing early return on a failed work completion, so login_req_len can never go negative. The upper bound was already safe: a posted login buffer cannot deliver more than ISER_RX_PAYLOAD_SIZE, so the difference stays at or below MAX_KEY_VALUE_PAIRS and the existing min() clamps it; only the missing lower bound needs to be added. Fixes: b8d26b3be8b3 ("iser-target: Add iSCSI Extensions for RDMA (iSER) target driver") Link: https://patch.msgid.link/r/20260602194642.2273217-1-michael.bommarito@gmail.com Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Michael Bommarito Signed-off-by: Jason Gunthorpe --- drivers/infiniband/ulp/isert/ib_isert.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c index 348005e71891..1015a51f750a 100644 --- a/drivers/infiniband/ulp/isert/ib_isert.c +++ b/drivers/infiniband/ulp/isert/ib_isert.c @@ -1383,6 +1383,12 @@ isert_login_recv_done(struct ib_cq *cq, struct ib_wc *wc) ib_dma_sync_single_for_cpu(ib_dev, isert_conn->login_desc->dma_addr, ISER_RX_SIZE, DMA_FROM_DEVICE); + if (unlikely(wc->byte_len < ISER_HEADERS_LEN)) { + isert_dbg("login request length %u is too short\n", + wc->byte_len); + return; + } + isert_conn->login_req_len = wc->byte_len - ISER_HEADERS_LEN; if (isert_conn->conn) { From 13e91fd076306f5d0cdfa14f53d69e37274723c4 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Tue, 2 Jun 2026 18:04:57 -0400 Subject: [PATCH 200/257] RDMA/srp: bound SRP_RSP sense copy by the received length srp_process_rsp() copies sense data from rsp->data + resp_data_len, where resp_data_len is the full 32-bit value supplied by the SRP target and is never checked against the number of bytes actually received (wc->byte_len). The copy length is bounded to SCSI_SENSE_BUFFERSIZE, so at most 96 bytes are copied, but the source offset is not bounded. A malicious or compromised SRP target on the InfiniBand/RoCE fabric that the initiator has logged into can return an SRP_RSP with SRP_RSP_FLAG_SNSVALID set and a large resp_data_len. The receive buffer is allocated at the target-chosen max_ti_iu_len, so the source of the sense copy lands past the bytes actually received; with resp_data_len near 0xFFFFFFFF it is gigabytes past the buffer and the read faults. Copy the sense data only if it has not been truncated, that is, only if the response header, the response data, and the sense region fit within the bytes actually received; otherwise drop the sense and log. The in-tree iSER and NVMe-RDMA receive paths already bound their parse by wc->byte_len; this brings ib_srp into line with them. Fixes: aef9ec39c47f ("IB: Add SCSI RDMA Protocol (SRP) initiator") Link: https://patch.msgid.link/r/20260602220457.2542840-1-michael.bommarito@gmail.com Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Michael Bommarito Reviewed-by: Bart Van Assche Signed-off-by: Jason Gunthorpe --- drivers/infiniband/ulp/srp/ib_srp.c | 30 +++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c index b58868e1cf11..acbd787de265 100644 --- a/drivers/infiniband/ulp/srp/ib_srp.c +++ b/drivers/infiniband/ulp/srp/ib_srp.c @@ -1932,7 +1932,8 @@ static int srp_post_recv(struct srp_rdma_ch *ch, struct srp_iu *iu) return ib_post_recv(ch->qp, &wr, NULL); } -static void srp_process_rsp(struct srp_rdma_ch *ch, struct srp_rsp *rsp) +static void srp_process_rsp(struct srp_rdma_ch *ch, struct srp_rsp *rsp, + u32 byte_len) { struct srp_target_port *target = ch->target; struct srp_request *req; @@ -1973,10 +1974,27 @@ static void srp_process_rsp(struct srp_rdma_ch *ch, struct srp_rsp *rsp) scmnd->result = rsp->status; if (rsp->flags & SRP_RSP_FLAG_SNSVALID) { - memcpy(scmnd->sense_buffer, rsp->data + - be32_to_cpu(rsp->resp_data_len), - min_t(int, be32_to_cpu(rsp->sense_data_len), - SCSI_SENSE_BUFFERSIZE)); + u32 resp_len = be32_to_cpu(rsp->resp_data_len); + u32 sense_len = be32_to_cpu(rsp->sense_data_len); + + /* + * The sense data starts resp_data_len bytes past the + * response data area; both lengths come from the + * target-controlled response. Copy the sense data + * only if it has not been truncated, that is, only if + * the full sense region fits within the bytes actually + * received. Otherwise the copy source would run past + * the receive buffer (sized to the target-chosen + * max_ti_iu_len), reading out of bounds. + */ + if (sizeof(*rsp) + (u64)resp_len + sense_len <= byte_len) + memcpy(scmnd->sense_buffer, + rsp->data + resp_len, + min(sense_len, SCSI_SENSE_BUFFERSIZE)); + else + shost_printk(KERN_ERR, target->scsi_host, + "dropping truncated sense data (resp_data_len %u sense_data_len %u, %u bytes received)\n", + resp_len, sense_len, byte_len); } if (unlikely(rsp->flags & SRP_RSP_FLAG_DIUNDER)) @@ -2086,7 +2104,7 @@ static void srp_recv_done(struct ib_cq *cq, struct ib_wc *wc) switch (opcode) { case SRP_RSP: - srp_process_rsp(ch, iu->buf); + srp_process_rsp(ch, iu->buf, wc->byte_len); break; case SRP_CRED_REQ: From 5d39580f68e6ddeedd15e587282207489dfb3da2 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 5 Jun 2026 11:21:34 +0000 Subject: [PATCH 201/257] tcp: restrict SO_ATTACH_FILTER to priv users This patch restricts the use of SO_ATTACH_FILTER (cBPF) on TCP sockets to users with CAP_NET_ADMIN capability. This blocks potential side-channel attack where an unprivileged application attaches a filter to leak TCP sequence/acknowledgment numbers. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Eric Dumazet Reported-by: Tamir Shahar Reported-by: Amit Klein Cc: Willem de Bruijn Cc: Alexei Starovoitov Cc: Daniel Borkmann Cc: Andrii Nakryiko Cc: Martin KaFai Lau Cc: Eduard Zingerman Cc: Kumar Kartikeya Dwivedi Cc: Song Liu Cc: Yonghong Song Cc: Jiri Olsa Cc: John Fastabend Cc: Stanislav Fomichev Acked-by: Daniel Borkmann Reviewed-by: Willem de Bruijn Signed-off-by: Jakub Kicinski --- net/core/sock.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net/core/sock.c b/net/core/sock.c index d097025c116a..cab041b57d28 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -1465,6 +1465,11 @@ int sk_setsockopt(struct sock *sk, int level, int optname, case SO_ATTACH_FILTER: { struct sock_fprog fprog; + if (sk_is_tcp(sk) && + !sockopt_ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN)) { + ret = -EPERM; + break; + } ret = copy_bpf_fprog_from_user(&fprog, optval, optlen); if (!ret) ret = sk_attach_filter(&fprog, sk); From f2bb3434544454099a5b6dec213567267b05d79d Mon Sep 17 00:00:00 2001 From: HanQuan Date: Thu, 4 Jun 2026 14:46:25 +0000 Subject: [PATCH 202/257] net: add pskb_may_pull() to skb_gro_receive_list() skb_gro_receive_list() calls skb_pull(skb, skb_gro_offset(skb)) without first ensuring the data is in the linear area via pskb_may_pull(). When the skb arrives via napi_gro_frags(), skb_headlen can be 0 (all data in page fragments) while skb_gro_offset is non-zero (after IP+TCP header parsing). The skb_pull() then decrements skb->len by skb_gro_offset but skb->data_len stays unchanged, hitting BUG_ON(skb->len < skb->data_len) in __skb_pull(). The UDP fraglist GRO path already contains this guard at udp_offload.c:749. Adding it to skb_gro_receive_list() itself provides centralized protection for all callers (TCP, UDP, and any future protocols), and ensures the precondition of skb_pull() is satisfied before it is called. On pskb_may_pull() failure, set NAPI_GRO_CB(skb)->flush = 1 so the skb is not held as a new GRO head and is instead delivered through the normal receive path, matching the UDP handling. Fixes: 8d95dc474f85 ("net: add code for TCP fraglist GRO") Reported-by: HanQuan Reported-by: MingXuan Signed-off-by: HanQuan Reviewed-by: Eric Dumazet Signed-off-by: Jakub Kicinski --- net/core/gro.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net/core/gro.c b/net/core/gro.c index a84753983467..35f2f708f010 100644 --- a/net/core/gro.c +++ b/net/core/gro.c @@ -232,6 +232,11 @@ int skb_gro_receive_list(struct sk_buff *p, struct sk_buff *skb) if (unlikely(p->len + skb->len >= 65536)) return -E2BIG; + if (!pskb_may_pull(skb, skb_gro_offset(skb))) { + NAPI_GRO_CB(skb)->flush = 1; + return -ENOMEM; + } + if (NAPI_GRO_CB(p)->last == p) skb_shinfo(p)->frag_list = skb; else From 2365343f4aad3e1b1e7a2e87e98cf66d5e590589 Mon Sep 17 00:00:00 2001 From: Yao Sang Date: Wed, 3 Jun 2026 14:10:44 +0800 Subject: [PATCH 203/257] net/mlx4: avoid GCC 10 __bad_copy_from() false positive mlx4_init_user_cqes() fills a scratch buffer with the CQE initialization pattern and then copies from that buffer to userspace. In the single-copy path, the copy length is array_size(entries, cqe_size), but the scratch buffer is allocated with PAGE_SIZE. GCC 10 does not carry the branch invariant strongly enough through the object size checks and falsely triggers __bad_copy_from(). Size the scratch buffer to the actual copy length for the active path, keep array_size() for the single-copy case, and retain a WARN_ON_ONCE() guard for the PAGE_SIZE invariant before allocating the buffer. Fixes: f69bf5dee7ef ("net/mlx4: Use array_size() helper in copy_to_user()") Signed-off-by: Yao Sang Reviewed-by: Jacob Keller Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mellanox/mlx4/cq.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx4/cq.c b/drivers/net/ethernet/mellanox/mlx4/cq.c index e130e7259275..5c55971abbf0 100644 --- a/drivers/net/ethernet/mellanox/mlx4/cq.c +++ b/drivers/net/ethernet/mellanox/mlx4/cq.c @@ -290,6 +290,7 @@ static void mlx4_cq_free_icm(struct mlx4_dev *dev, int cqn) static int mlx4_init_user_cqes(void *buf, int entries, int cqe_size) { int entries_per_copy = PAGE_SIZE / cqe_size; + size_t copy_bytes; void *init_ents; int err = 0; int i; @@ -314,8 +315,14 @@ static int mlx4_init_user_cqes(void *buf, int entries, int cqe_size) buf += PAGE_SIZE; } } else { + copy_bytes = array_size(entries, cqe_size); + if (WARN_ON_ONCE(copy_bytes > PAGE_SIZE)) { + err = -EINVAL; + goto out; + } + err = copy_to_user((void __user *)buf, init_ents, - array_size(entries, cqe_size)) ? + copy_bytes) ? -EFAULT : 0; } From a0130d682222ae21afc395aead7cd2d87e1a8358 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Wed, 3 Jun 2026 15:12:17 -0700 Subject: [PATCH 204/257] net: ibm: emac: Fix use-after-free during device removal The driver was using devm_register_netdev() which causes unregister_netdev() to be deferred until the devres cleanup phase, which runs after emac_remove() returns. This creates a use-after-free window where: 1. emac_remove() is called, which tears down hardware (cancels work, detaches modules, unregisters from MAL) 2. emac_remove() returns 3. devres cleanup runs and finally calls unregister_netdev() During step 3, the network stack might still process packets, triggering emac_irq(), emac_poll(), or other handlers that access now-freed hardware resources (dev->emacp, dev->mal, etc.). Fix this by replacing devm_register_netdev() with manual register_netdev() and calling unregister_netdev() at the beginning of emac_remove(), before any hardware teardown. This ensures the network device is fully stopped and unregistered before hardware resources are released. The change is safe because: - dev->ndev is assigned very early in probe (before any error paths that could bypass emac_remove) - platform_set_drvdata() is only called after successful registration, so emac_remove() only runs for fully registered devices - unregister_netdev() is idempotent and safe to call on any registered device Fixes: a4dd8535a527 ("net: ibm: emac: use devm for register_netdev") Signed-off-by: Rosen Penev Reviewed-by: Jacob Keller Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/ibm/emac/core.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/ibm/emac/core.c b/drivers/net/ethernet/ibm/emac/core.c index 417dfa18daae..4e503b3d0d2d 100644 --- a/drivers/net/ethernet/ibm/emac/core.c +++ b/drivers/net/ethernet/ibm/emac/core.c @@ -3144,7 +3144,7 @@ static int emac_probe(struct platform_device *ofdev) netif_carrier_off(ndev); - err = devm_register_netdev(&ofdev->dev, ndev); + err = register_netdev(ndev); if (err) { printk(KERN_ERR "%pOF: failed to register net device (%d)!\n", np, err); @@ -3197,6 +3197,13 @@ static void emac_remove(struct platform_device *ofdev) DBG(dev, "remove" NL); + /* Unregister network device before tearing down hardware + * to prevent use-after-free during deferred cleanup. This ensures + * the network stack stops all operations before hardware resources + * are released. + */ + unregister_netdev(dev->ndev); + cancel_work_sync(&dev->reset_work); if (emac_has_feature(dev, EMAC_FTR_HAS_TAH)) From 71de0177b28da751f407581a4515cf4d762f6296 Mon Sep 17 00:00:00 2001 From: Santosh Kalluri Date: Wed, 3 Jun 2026 17:08:43 -0700 Subject: [PATCH 205/257] net: phonet: free phonet_device after RCU grace period MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit phonet_device_destroy() removes a phonet_device from the per-net device list with list_del_rcu(), but frees it immediately. RCU readers walking the same list can still hold a pointer to the object after it has been removed, leading to a slab-use-after-free. Use kfree_rcu(), matching the lifetime rule already used by phonet_address_del() for the same object type. Fixes: eeb74a9d45f7 ("Phonet: convert devices list to RCU") Cc: stable@vger.kernel.org Signed-off-by: Santosh Kalluri Acked-by: Rémi Denis-Courmont Reviewed-by: Simon Horman Signed-off-by: Jakub Kicinski --- net/phonet/pn_dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/phonet/pn_dev.c b/net/phonet/pn_dev.c index 86325b7fc1b6..ad44831d6745 100644 --- a/net/phonet/pn_dev.c +++ b/net/phonet/pn_dev.c @@ -108,7 +108,7 @@ static void phonet_device_destroy(struct net_device *dev) for_each_set_bit(addr, pnd->addrs, 64) phonet_address_notify(net, RTM_DELADDR, ifindex, addr); - kfree(pnd); + kfree_rcu(pnd, rcu); } } From c849de7d8757a7af801fc4a4058f71d481d367f2 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 5 Jun 2026 18:21:24 -0700 Subject: [PATCH 206/257] netdev: fix double-free in netdev_nl_bind_rx_doit() Sashiko flags that genlmsg_reply() always consumes the skb. The error path calls nlmsg_free(rsp) so we can't jump directly to it. Let's not unbind, just propagate the error to the user. This is the typical way of handling genlmsg_reply() failures. They shouldn't happen unless user does something silly like calling the kernel with an already-full rcvbuf. Reported-by: Sashiko Fixes: 170aafe35cb9 ("netdev: support binding dma-buf to netdevice") Reviewed-by: Bobby Eshleman Acked-by: Daniel Borkmann Reviewed-by: Nikolay Aleksandrov Signed-off-by: Jakub Kicinski --- net/core/netdev-genl.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/net/core/netdev-genl.c b/net/core/netdev-genl.c index b8f6076d8007..119eaa6501d5 100644 --- a/net/core/netdev-genl.c +++ b/net/core/netdev-genl.c @@ -1095,8 +1095,6 @@ int netdev_nl_bind_rx_doit(struct sk_buff *skb, struct genl_info *info) genlmsg_end(rsp, hdr); err = genlmsg_reply(rsp, info); - if (err) - goto err_unbind; bitmap_free(rxq_bitmap); @@ -1104,7 +1102,7 @@ int netdev_nl_bind_rx_doit(struct sk_buff *skb, struct genl_info *info) mutex_unlock(&priv->lock); - return 0; + return err < 0 ? err : 0; err_unbind: net_devmem_unbind_dmabuf(binding); From 48774e87bbaa0056819d4b52301e4692e50e3252 Mon Sep 17 00:00:00 2001 From: Maxime Chevallier Date: Thu, 4 Jun 2026 11:28:15 +0200 Subject: [PATCH 207/257] net: phy: clean the sfp upstream if phy probing fails Sashiko reported that we don't call sfp_bus_del_upstream() in the probe failure path, so let's add it, otherwise the sfp-bus is left with a dangling 'upstream' field, that may be used later on during SFP events. This issue existed before the generic phylib sfp support, back when drivers were calling phy_sfp_probe themselves. Reviewed-by: Nicolai Buchwitz Fixes: 298e54fa810e ("net: phy: add core phylib sfp support") Signed-off-by: Maxime Chevallier Link: https://patch.msgid.link/20260604092819.723505-2-maxime.chevallier@bootlin.com Signed-off-by: Jakub Kicinski --- drivers/net/phy/phy_device.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 3370eb822017..6ccbfacf7d1d 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -1718,6 +1718,9 @@ static int phy_sfp_probe(struct phy_device *phydev) ret = sfp_bus_add_upstream(bus, phydev, &sfp_phydev_ops); sfp_bus_put(bus); + + if (ret) + phydev->sfp_bus = NULL; } if (!ret && phydev->sfp_bus) @@ -3775,6 +3778,9 @@ static int phy_probe(struct device *dev) return 0; out: + sfp_bus_del_upstream(phydev->sfp_bus); + phydev->sfp_bus = NULL; + if (!phydev->is_on_sfp_module) phy_led_triggers_unregister(phydev); From b1e780bb37c641d8291c51d7b4bde33450d18fb4 Mon Sep 17 00:00:00 2001 From: Maxime Chevallier Date: Thu, 4 Jun 2026 11:28:16 +0200 Subject: [PATCH 208/257] net: phy: remove phy ports upon probe failure When phy_probe fails, let's clean the phy_ports that were successfully added already. Suggested-by: Nicolai Buchwitz Reviewed-by: Nicolai Buchwitz Fixes: 589e934d2735 ("net: phy: Introduce PHY ports representation") Signed-off-by: Maxime Chevallier Link: https://patch.msgid.link/20260604092819.723505-3-maxime.chevallier@bootlin.com Signed-off-by: Jakub Kicinski --- drivers/net/phy/phy_device.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 6ccbfacf7d1d..4ba880446896 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -3781,6 +3781,8 @@ static int phy_probe(struct device *dev) sfp_bus_del_upstream(phydev->sfp_bus); phydev->sfp_bus = NULL; + phy_cleanup_ports(phydev); + if (!phydev->is_on_sfp_module) phy_led_triggers_unregister(phydev); From 4497f5028675b7e51c4aa59c3f4df01f29424277 Mon Sep 17 00:00:00 2001 From: Maxime Chevallier Date: Thu, 4 Jun 2026 11:28:17 +0200 Subject: [PATCH 209/257] net: phy: Clean the phy_ports after unregistering the downstream SFP bus As reported by sashiko when looking a other patches, we need to ensure that the downstream SFP bus gets unregistered prior to destroying the phy_ports attached to a phy_device, as the SFP code may reference these ports. Let's make sure we follow that ordering in phy_remove(). Fixes: 589e934d2735 ("net: phy: Introduce PHY ports representation") Signed-off-by: Maxime Chevallier Reviewed-by: Nicolai Buchwitz Link: https://patch.msgid.link/20260604092819.723505-4-maxime.chevallier@bootlin.com Signed-off-by: Jakub Kicinski --- drivers/net/phy/phy_device.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 4ba880446896..b48722589f40 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -3806,11 +3806,11 @@ static int phy_remove(struct device *dev) phydev->state = PHY_DOWN; - phy_cleanup_ports(phydev); - sfp_bus_del_upstream(phydev->sfp_bus); phydev->sfp_bus = NULL; + phy_cleanup_ports(phydev); + if (phydev->drv && phydev->drv->remove) phydev->drv->remove(phydev); From 5a0082ec20a05ef2378410323a5089a8f1786f4a Mon Sep 17 00:00:00 2001 From: Maxime Chevallier Date: Thu, 4 Jun 2026 11:28:18 +0200 Subject: [PATCH 210/257] net: phy: don't try to setup PHY-driven SFP cages when using genphy We don't have support for PHY-driver SFP cages with the genphy code. On top of that, it was found by sashiko that running sfp_bus_add_upstream() for genphy deadlocks, as for genphy the PHY probing runs under RTNL, which isn't the case for non-genphy drivers. This problem was reproduced, and does lead to a deadlock on RTNL. Before the blamed commit, the phy_sfp_probe() call was made by individual PHY drivers, so there was no way to get to the SFP probing path when using genphy. Let's therefore only run phy_sfp_probe when not using genphy. Reviewed-by: Nicolai Buchwitz Fixes: bad869b5e41a ("net: phy: Only rely on phy_port for PHY-driven SFP") Signed-off-by: Maxime Chevallier Link: https://patch.msgid.link/20260604092819.723505-5-maxime.chevallier@bootlin.com Signed-off-by: Jakub Kicinski --- drivers/net/phy/phy_device.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index b48722589f40..1511385b9b36 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -3512,9 +3512,15 @@ static int phy_setup_ports(struct phy_device *phydev) if (ret) return ret; - ret = phy_sfp_probe(phydev); - if (ret) - goto out; + /* We don't support SFP with genphy drivers. Also, genphy driver + * binding occurs with RTNL help, which will deadlock the call to + * sfp_bus_add_upstream(). + */ + if (!phydev->is_genphy_driven) { + ret = phy_sfp_probe(phydev); + if (ret) + goto out; + } if (phydev->n_ports < phydev->max_n_ports) { ret = phy_default_setup_single_port(phydev); From a2171131ecda1ed61a594a1eb715e75fdad0fef5 Mon Sep 17 00:00:00 2001 From: Mingyu Wang <25181214217@stu.xidian.edu.cn> Date: Thu, 4 Jun 2026 14:48:01 +0800 Subject: [PATCH 211/257] net: qrtr: fix refcount saturation and potential UAF in qrtr_port_remove In qrtr_port_remove(), the socket reference count is decremented via __sock_put() before the port is removed from the qrtr_ports XArray and before the RCU grace period elapses. This breaks the fundamental RCU update paradigm. It exposes a race window where a concurrent RCU reader (such as qrtr_reset_ports() or qrtr_port_lookup()) can obtain a pointer to the socket from the XArray, and attempt to call sock_hold() on a socket whose reference count has already dropped to zero. This exact race condition was hit during syzkaller fuzzing, leading to the following refcount saturation warning and a potential Use-After-Free: refcount_t: saturated; leaking memory. WARNING: CPU: 3 PID: 1273 at lib/refcount.c:22 refcount_warn_saturate+0xae/0x1d0 Modules linked in: qrtr(+) bochs drm_shmem_helper ... Call Trace: qrtr_reset_ports net/qrtr/af_qrtr.c:768 [inline] [qrtr] __qrtr_bind.isra.0+0x48b/0x570 net/qrtr/af_qrtr.c:805 [qrtr] qrtr_bind+0x17d/0x210 net/qrtr/af_qrtr.c:901 [qrtr] kernel_bind+0xe4/0x120 net/socket.c:3592 qrtr_ns_init+0x1a6/0x380 net/qrtr/ns.c:715 [qrtr] qrtr_proto_init+0x3b/0xff0 net/qrtr/af_qrtr.c:169 [qrtr] do_one_initcall+0xf5/0x5e0 init/main.c:1283 ... Fix this by deferring the reference count decrement until after the xa_erase() and the synchronize_rcu() complete. (Note: The v1 of this patch incorrectly replaced __sock_put() with sock_put(). As Simon Horman pointed out, the callers of qrtr_port_remove() still hold a reference to the socket, so freeing the socket memory here would lead to a subsequent UAF in the caller. Thus, the __sock_put() is kept, but only repositioned to close the RCU race.) Fixes: bdabad3e363d ("net: Add Qualcomm IPC router") Signed-off-by: Mingyu Wang <25181214217@stu.xidian.edu.cn> Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260604064801.1180388-1-w15303746062@163.com Signed-off-by: Jakub Kicinski --- net/qrtr/af_qrtr.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/qrtr/af_qrtr.c b/net/qrtr/af_qrtr.c index 7cec6a7859b0..db823177e636 100644 --- a/net/qrtr/af_qrtr.c +++ b/net/qrtr/af_qrtr.c @@ -707,13 +707,13 @@ static void qrtr_port_remove(struct qrtr_sock *ipc) if (port == QRTR_PORT_CTRL) port = 0; - __sock_put(&ipc->sk); - xa_erase(&qrtr_ports, port); /* Ensure that if qrtr_port_lookup() did enter the RCU read section we * wait for it to up increment the refcount */ synchronize_rcu(); + + __sock_put(&ipc->sk); } /* Assign port number to socket. From 894e036a24a26a6dd7b17d8d3fb5c53ab48a6074 Mon Sep 17 00:00:00 2001 From: Dragos Tatulea Date: Thu, 4 Jun 2026 16:58:49 +0300 Subject: [PATCH 212/257] net/mlx5: Fix slab-out-of-bounds in mlx5_query_nic_vport_mac_list mlx5_query_nic_vport_mac_list() sizes its firmware command buffer using the PF's log_max_current_uc/mc_list capabilities. When querying a VF vport with a larger configured max (via devlink), the firmware response can overflow this buffer: BUG: KASAN: slab-out-of-bounds in mlx5_query_nic_vport_mac_list+0x453/0x4c0 [mlx5_core] Read of size 4 at addr ff1100013ffc8a12 by task kworker/u96:2/385 CPU: 12 UID: 0 PID: 385 Comm: kworker/u96:2 Not tainted 7.0.0-rc6+ #1 PREEMPT Hardware name: QEMU Standard PC (Q35 + ICH9, 2009) Workqueue: mlx5_esw_wq esw_vport_change_handler [mlx5_core] Call Trace: dump_stack_lvl+0x69/0xa0 print_report+0x176/0x4e4 kasan_report+0xc8/0x100 mlx5_query_nic_vport_mac_list+0x453/0x4c0 [mlx5_core] esw_update_vport_addr_list+0x2e3/0xda0 [mlx5_core] esw_vport_change_handle_locked+0xa1f/0x1060 [mlx5_core] esw_vport_change_handler+0x6a/0x90 [mlx5_core] process_one_work+0x87f/0x15e0 worker_thread+0x62b/0x1020 kthread+0x375/0x490 ret_from_fork+0x4dc/0x810 ret_from_fork_asm+0x11/0x20 Fix by querying the vport's own HCA caps to size the buffer correctly. Refactor the function to allocate and return the MAC list internally, removing the caller's dependency on knowing the correct max. Fixes: e16aea2744ab ("net/mlx5: Introduce access functions to modify/query vport mac lists") Signed-off-by: Dragos Tatulea Reviewed-by: Carolina Jubran Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260604135849.458060-1-tariqt@nvidia.com Signed-off-by: Jakub Kicinski --- .../net/ethernet/mellanox/mlx5/core/eswitch.c | 13 +--- .../net/ethernet/mellanox/mlx5/core/vport.c | 72 ++++++++++++++----- include/linux/mlx5/vport.h | 4 +- 3 files changed, 59 insertions(+), 30 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c index 7c8311f41232..236f89a6483a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c @@ -533,23 +533,16 @@ static void esw_update_vport_addr_list(struct mlx5_eswitch *esw, struct mlx5_vport *vport, int list_type) { bool is_uc = list_type == MLX5_NVPRT_LIST_TYPE_UC; - u8 (*mac_list)[ETH_ALEN]; + u8 (*mac_list)[ETH_ALEN] = NULL; struct l2addr_node *node; struct vport_addr *addr; struct hlist_head *hash; struct hlist_node *tmp; - int size; + int size = 0; int err; int hi; int i; - size = is_uc ? MLX5_MAX_UC_PER_VPORT(esw->dev) : - MLX5_MAX_MC_PER_VPORT(esw->dev); - - mac_list = kcalloc(size, ETH_ALEN, GFP_KERNEL); - if (!mac_list) - return; - hash = is_uc ? vport->uc_list : vport->mc_list; for_each_l2hash_node(node, tmp, hash, hi) { @@ -561,7 +554,7 @@ static void esw_update_vport_addr_list(struct mlx5_eswitch *esw, goto out; err = mlx5_query_nic_vport_mac_list(esw->dev, vport->vport, list_type, - mac_list, &size); + &mac_list, &size); if (err) goto out; esw_debug(esw->dev, "vport[%d] context update %s list size (%d)\n", diff --git a/drivers/net/ethernet/mellanox/mlx5/core/vport.c b/drivers/net/ethernet/mellanox/mlx5/core/vport.c index 4effe37fd455..d63b0e8806b5 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/vport.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/vport.c @@ -324,35 +324,63 @@ int mlx5_modify_nic_vport_mtu(struct mlx5_core_dev *mdev, u16 mtu) } EXPORT_SYMBOL_GPL(mlx5_modify_nic_vport_mtu); +static int mlx5_vport_max_mac_list_size(struct mlx5_core_dev *dev, u16 vport, + enum mlx5_list_type list_type) +{ + void *query_ctx, *hca_caps; + int ret = 0; + + if (!vport && !mlx5_core_is_ecpf(dev)) + return list_type == MLX5_NVPRT_LIST_TYPE_UC ? + 1 << MLX5_CAP_GEN(dev, log_max_current_uc_list) : + 1 << MLX5_CAP_GEN(dev, log_max_current_mc_list); + + query_ctx = kzalloc(MLX5_ST_SZ_BYTES(query_hca_cap_out), GFP_KERNEL); + if (!query_ctx) + return -ENOMEM; + + ret = mlx5_vport_get_other_func_general_cap(dev, vport, query_ctx); + if (ret) + goto out; + + hca_caps = MLX5_ADDR_OF(query_hca_cap_out, query_ctx, capability); + ret = list_type == MLX5_NVPRT_LIST_TYPE_UC ? + 1 << MLX5_GET(cmd_hca_cap, hca_caps, log_max_current_uc_list) : + 1 << MLX5_GET(cmd_hca_cap, hca_caps, log_max_current_mc_list); + +out: + kfree(query_ctx); + + return ret; +} + int mlx5_query_nic_vport_mac_list(struct mlx5_core_dev *dev, u16 vport, enum mlx5_list_type list_type, - u8 addr_list[][ETH_ALEN], - int *list_size) + u8 (**addr_list)[ETH_ALEN], + int *addr_list_size) { u32 in[MLX5_ST_SZ_DW(query_nic_vport_context_in)] = {0}; + int allowed_list_size; void *nic_vport_ctx; int max_list_size; - int req_list_size; int out_sz; void *out; int err; int i; - req_list_size = *list_size; + if (!addr_list || !addr_list_size) + return -EINVAL; - max_list_size = list_type == MLX5_NVPRT_LIST_TYPE_UC ? - 1 << MLX5_CAP_GEN(dev, log_max_current_uc_list) : - 1 << MLX5_CAP_GEN(dev, log_max_current_mc_list); + *addr_list = NULL; + *addr_list_size = 0; - if (req_list_size > max_list_size) { - mlx5_core_warn(dev, "Requested list size (%d) > (%d) max_list_size\n", - req_list_size, max_list_size); - req_list_size = max_list_size; - } + max_list_size = mlx5_vport_max_mac_list_size(dev, vport, list_type); + if (max_list_size < 0) + return max_list_size; out_sz = MLX5_ST_SZ_BYTES(query_nic_vport_context_out) + - req_list_size * MLX5_ST_SZ_BYTES(mac_address_layout); + max_list_size * MLX5_ST_SZ_BYTES(mac_address_layout); out = kvzalloc(out_sz, GFP_KERNEL); if (!out) @@ -371,16 +399,24 @@ int mlx5_query_nic_vport_mac_list(struct mlx5_core_dev *dev, nic_vport_ctx = MLX5_ADDR_OF(query_nic_vport_context_out, out, nic_vport_context); - req_list_size = MLX5_GET(nic_vport_context, nic_vport_ctx, - allowed_list_size); + allowed_list_size = MLX5_GET(nic_vport_context, nic_vport_ctx, + allowed_list_size); + if (!allowed_list_size) + goto out; - *list_size = req_list_size; - for (i = 0; i < req_list_size; i++) { + *addr_list = kcalloc(allowed_list_size, ETH_ALEN, GFP_KERNEL); + if (!*addr_list) { + err = -ENOMEM; + goto out; + } + + for (i = 0; i < allowed_list_size; i++) { u8 *mac_addr = MLX5_ADDR_OF(nic_vport_context, nic_vport_ctx, current_uc_mac_address[i]) + 2; - ether_addr_copy(addr_list[i], mac_addr); + ether_addr_copy((*addr_list)[i], mac_addr); } + *addr_list_size = allowed_list_size; out: kvfree(out); return err; diff --git a/include/linux/mlx5/vport.h b/include/linux/mlx5/vport.h index dfa2fe32217a..282ed5442282 100644 --- a/include/linux/mlx5/vport.h +++ b/include/linux/mlx5/vport.h @@ -102,8 +102,8 @@ int mlx5_query_hca_vport_node_guid(struct mlx5_core_dev *dev, int mlx5_query_nic_vport_mac_list(struct mlx5_core_dev *dev, u16 vport, enum mlx5_list_type list_type, - u8 addr_list[][ETH_ALEN], - int *list_size); + u8 (**mac_list)[ETH_ALEN], + int *mac_list_size); int mlx5_modify_nic_vport_mac_list(struct mlx5_core_dev *dev, enum mlx5_list_type list_type, u8 addr_list[][ETH_ALEN], From b69004f5a6ad32da84d8aa5b23b9c0caafe6252e Mon Sep 17 00:00:00 2001 From: Dragos Tatulea Date: Thu, 4 Jun 2026 16:54:46 +0300 Subject: [PATCH 213/257] net/mlx5e: xsk: Fix DMA and xdp_frame leak on XDP_TX xmit failure In the XSK branch of mlx5e_xmit_xdp_buff(), when sq->xmit_xdp_frame() returns false (e.g. XDPSQ is full), the function returns without unmapping the DMA address or freeing the xdp_frame allocated by xdp_convert_zc_to_xdp_frame(). The xdpi_fifo push only happens on success, so the completion path cannot recover these entries. With CONFIG_DMA_API_DEBUG=y, the leak surfaces on driver unbind: DMA-API: pci 0000:08:00.0: device driver has pending DMA allocations while released from device [count=1116] One of leaked entries details: [device address=0x000000010ffd7028] [size=1534 bytes] [mapped with DMA_TO_DEVICE] [mapped as phy] WARNING: kernel/dma/debug.c:881 at dma_debug_device_change+0x127/0x180 ... DMA-API: Mapped at: debug_dma_map_phys+0x4b/0xd0 dma_map_phys+0xfd/0x2d0 mlx5e_xdp_handle+0x5ae/0xac0 [mlx5_core] mlx5e_xsk_skb_from_cqe_mpwrq_linear+0xc4/0x170 [mlx5_core] mlx5e_handle_rx_cqe_mpwrq+0xc1/0x290 [mlx5_core] Add the missing unmap + xdp_return_frame, matching the cleanup already done in mlx5e_xdp_xmit(). has_frags is rejected earlier in this branch, so no per-frag unmap is needed. Fixes: 84a0a2310d6d ("net/mlx5e: XDP_TX from UMEM support") Signed-off-by: Dragos Tatulea Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260604135446.456119-1-tariqt@nvidia.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c b/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c index d3bab198c99c..d8c7cb8837d7 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c @@ -103,9 +103,15 @@ mlx5e_xmit_xdp_buff(struct mlx5e_xdpsq *sq, struct mlx5e_rq *rq, xdptxd->dma_addr = dma_addr; - if (unlikely(!INDIRECT_CALL_2(sq->xmit_xdp_frame, mlx5e_xmit_xdp_frame_mpwqe, - mlx5e_xmit_xdp_frame, sq, xdptxd, 0, NULL))) + if (unlikely(!INDIRECT_CALL_2(sq->xmit_xdp_frame, + mlx5e_xmit_xdp_frame_mpwqe, + mlx5e_xmit_xdp_frame, + sq, xdptxd, 0, NULL))) { + dma_unmap_single(sq->pdev, dma_addr, xdptxd->len, + DMA_TO_DEVICE); + xdp_return_frame(xdpf); return false; + } /* xmit_mode == MLX5E_XDP_XMIT_MODE_FRAME */ mlx5e_xdpi_fifo_push(&sq->db.xdpi_fifo, From a7767290e77ca2e926b49f8bfa29daa12262c612 Mon Sep 17 00:00:00 2001 From: Fushuai Wang Date: Fri, 5 Jun 2026 18:21:12 +0800 Subject: [PATCH 214/257] net/mlx5: Use effective affinity mask for IRQ selection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a sf is created after a CPU has been taken offline, the IRQ pool may contain IRQs with affinity masks that include the offline CPU. Since only online CPUs should be considered for IRQ placement, cpumask_subset() check would fail because the iter_mask contains offline CPUs that are not present in req_mask, causing sf creation to fail. This is an example: 1. When mlx5 driver loads, it initializes the IRQ pools. For sf_ctrl_pool with ≤64 sf: - xa_num_irqs = {N, N} (There is only one slot) 2. When the first SF is created: - The ctrl IRQ is allocated with mask=cpu_online_mask={0-191} 2. We take CPU 20 offline 3. Existing ctl irq still have mask={0-191} 4. Create a new SF: - req_mask={0-19,21-191} - iter_mask={0-191} - {0-191} is NOT a subset of {0-19,21-191} - least_loaded_irq=NULL 5. Try to allocate a new irq via irq_pool_request_irq() 6. xa_alloc() fails because the pool is full(There is only one slot) 7. sf creation fails with error Use irq_get_effective_affinity_mask() instead, which returns the IRQ's actual effective affinity that already excludes offline CPUs. Fixes: 061f5b23588a ("net/mlx5: SF, Use all available cpu for setting cpu affinity") Suggested-by: Shay Drory Signed-off-by: Fushuai Wang Reviewed-by: Shay Drory Reviewed-by: Tariq Toukan Link: https://patch.msgid.link/20260605102112.91772-1-fushuai.wang@linux.dev Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mellanox/mlx5/core/irq_affinity.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/irq_affinity.c b/drivers/net/ethernet/mellanox/mlx5/core/irq_affinity.c index 994fe83da4be..a0bb8ee44e35 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/irq_affinity.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/irq_affinity.c @@ -105,9 +105,12 @@ irq_pool_find_least_loaded(struct mlx5_irq_pool *pool, const struct cpumask *req lockdep_assert_held(&pool->lock); xa_for_each_range(&pool->irqs, index, iter, start, end) { - struct cpumask *iter_mask = mlx5_irq_get_affinity_mask(iter); int iter_refcount = mlx5_irq_read_locked(iter); + const struct cpumask *iter_mask; + iter_mask = irq_get_effective_affinity_mask(mlx5_irq_get_irq(iter)); + if (!iter_mask) + continue; if (!cpumask_subset(iter_mask, req_mask)) /* skip IRQs with a mask which is not subset of req_mask */ continue; From f0e42f0c4337b1f220de1ddd63f47197c7dee4de Mon Sep 17 00:00:00 2001 From: Kyle Zeng Date: Fri, 5 Jun 2026 00:34:48 -0700 Subject: [PATCH 215/257] ipv6: sit: reload inner IPv6 header after GSO offloads ipip6_tunnel_xmit() caches the inner IPv6 header pointer at function entry and continues using it after iptunnel_handle_offloads(). For GSO skbs, iptunnel_handle_offloads() calls skb_header_unclone(). When the skb header is cloned, skb_header_unclone() can call pskb_expand_head(), which may move the skb head. The pskb_expand_head() contract requires pointers into the skb header to be reloaded after the call. If the later skb_realloc_headroom() branch is not taken, SIT uses the stale iph6 pointer to read the inner hop limit and DS field. That can read from a freed skb head after the old head's remaining clone is released. Reload iph6 after the offload helper succeeds and before subsequent reads from the inner IPv6 header. Keep the existing reload after skb_realloc_headroom(), since that branch can also replace the skb. Fixes: 14909664e4e1 ("sit: Setup and TX path for sit/UDP foo-over-udp encapsulation") Signed-off-by: Kyle Zeng Reviewed-by: Eric Dumazet Reported-by: syzbot+6eb9ca986d80f6f88cf9@syzkaller.appspotmail.com Link: https://patch.msgid.link/20260605073448.6524-1-kylebot@openai.com Signed-off-by: Jakub Kicinski --- net/ipv6/sit.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index 201347b4e127..b41e231a669b 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -961,6 +961,7 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb, ip_rt_put(rt); goto tx_error; } + iph6 = ipv6_hdr(skb); if (df) { mtu = dst4_mtu(&rt->dst) - t_hlen; From ee30dd2909d8b98619f4341c70ec8dc8e155ab02 Mon Sep 17 00:00:00 2001 From: Adrian Moreno Date: Thu, 4 Jun 2026 14:19:46 +0200 Subject: [PATCH 216/257] net: openvswitch: fix possible kfree_skb of ERR_PTR After the patch in the "Fixes" tag, the allocation of the "reply" skb can happen either before or after locking the ovs_mutex. However, error cleanups still follow the classical reversed order, assuming "reply" is allocated before locking: it is freed after unlocking. If "reply" allocation happens after locking the mutex and it fails, "reply" is left with an ERR_PTR, and execution jumps to the correspondent cleanup stage which will try to free an invalid pointer. Fix this by setting the pointer to NULL after having saved its error value. Fixes: 893f139b9a6c ("openvswitch: Minimize ovs_flow_cmd_new|set critical sections.") Signed-off-by: Adrian Moreno Reviewed-by: Aaron Conole Acked-by: Eelco Chaudron Link: https://patch.msgid.link/20260604121946.942164-1-amorenoz@redhat.com Signed-off-by: Jakub Kicinski --- net/openvswitch/datapath.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index bbbde50fc649..f0164817d9b7 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c @@ -1316,6 +1316,7 @@ static int ovs_flow_cmd_set(struct sk_buff *skb, struct genl_info *info) if (IS_ERR(reply)) { error = PTR_ERR(reply); + reply = NULL; goto err_unlock_ovs; } } From 19440600e729d4f74a42591a872099cf25c7d28a Mon Sep 17 00:00:00 2001 From: Chih Kai Hsu Date: Thu, 4 Jun 2026 17:22:47 +0800 Subject: [PATCH 217/257] r8152: handle the return value of usb_reset_device() If usb_reset_device() returns a negative error code, stop the process of probing. Fixes: 10c3271712f5 ("r8152: disable the ECM mode") Signed-off-by: Chih Kai Hsu Reviewed-by: Hayes Wang Reviewed-by: Andrew Lunn Link: https://patch.msgid.link/20260604092247.27158-450-nic_swsd@realtek.com Signed-off-by: Paolo Abeni --- drivers/net/usb/r8152.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index 1ace1d2398c9..b1268553cd70 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -9851,7 +9851,12 @@ static int rtl8152_probe_once(struct usb_interface *intf, struct net_device *netdev; int ret; - usb_reset_device(udev); + ret = usb_reset_device(udev); + if (ret < 0) { + dev_err(&intf->dev, "USB reset failed, errno=%d\n", ret); + return ret; + } + netdev = alloc_etherdev(sizeof(struct r8152)); if (!netdev) { dev_err(&intf->dev, "Out of memory\n"); From 333b6d5bb9f87827ac2639c737bf9613dbae7253 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 4 Jun 2026 12:46:00 +0100 Subject: [PATCH 218/257] rxrpc: Fix the ACK parser to extract the SACK table for parsing Fix modification of the received skbuff in rxrpc_input_soft_acks() and a potential incorrect access of the buffer in a fragmented UDP packet (the packet would probably have to be deliberately pre-generated as fragmented) when AF_RXRPC tries to extract the contents of the SACK table by copying out the contents of the SACK table into a buffer before attempting to parse AF_RXRPC assumes that it can just call skb_condense() and then validly access the SACK table from skb->data and that it will be a flat buffer - but skb_condense() can silently fail to do anything under some circumstances. Note that whilst rxrpc_input_soft_acks() should be able to parse extended ACKs, the rest of AF_RXRPC doesn't currently support that. Further, there's then no need to call skb_condense() in rxrpc_input_ack(), so don't. Fixes: d57a3a151660 ("rxrpc: Save last ACK's SACK table rather than marking txbufs") Reported-by: Michael Bommarito Link: https://lore.kernel.org/r/20260513180907.2061972-1-michael.bommarito@gmail.com Signed-off-by: David Howells cc: Marc Dionne cc: Jeffrey Altman cc: Eric Dumazet cc: "David S. Miller" cc: Jakub Kicinski cc: Paolo Abeni cc: Simon Horman cc: linux-afs@lists.infradead.org cc: netdev@vger.kernel.org cc: stable@kernel.org Link: https://patch.msgid.link/105362.1780573560@warthog.procyon.org.uk Signed-off-by: Paolo Abeni --- net/rxrpc/input.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/net/rxrpc/input.c b/net/rxrpc/input.c index 24aceb183c2c..ce761466b02d 100644 --- a/net/rxrpc/input.c +++ b/net/rxrpc/input.c @@ -963,23 +963,34 @@ static void rxrpc_input_soft_acks(struct rxrpc_call *call, struct rxrpc_skb_priv *sp = rxrpc_skb(skb); struct rxrpc_txqueue *tq = call->tx_queue; unsigned long extracted = ~0UL; - unsigned int nr = 0; + unsigned int nr = 0, nsack; rxrpc_seq_t seq = call->acks_hard_ack + 1; rxrpc_seq_t lowest_nak = seq + sp->ack.nr_acks; - u8 *acks = skb->data + sizeof(struct rxrpc_wire_header) + sizeof(struct rxrpc_ackpacket); + u8 sack[256] __aligned(sizeof(unsigned long)); + u8 *acks = sack; _enter("%x,%x,%u", tq->qbase, seq, sp->ack.nr_acks); while (after(seq, tq->qbase + RXRPC_NR_TXQUEUE - 1)) tq = tq->next; + /* Extract an individual SACK table. A normal SACK table is up to 255 + * bytes with 1 ACK flag per byte, but an extended SACK table can be up + * to 256 bytes with up to 8 ACK/NACK flags per byte. The ACK flags go + * across all bit 0's then all bit 1's, then all bit 2's, ... + */ + memset(sack, 0, sizeof(sack)); + nsack = umin(sp->ack.nr_acks, 256); + if (skb_copy_bits(skb, + sizeof(struct rxrpc_wire_header) + sizeof(struct rxrpc_ackpacket), + sack, nsack) < 0) + return; + for (unsigned int i = 0; i < sp->ack.nr_acks; i++) { /* Decant ACKs until we hit a txqueue boundary. */ + if ((i & 255) == 0) + acks = sack; shiftr_adv_rotr(acks, extracted); - if (i == 256) { - acks -= i; - i = 0; - } seq++; nr++; if ((seq & RXRPC_TXQ_MASK) != 0) @@ -1117,9 +1128,6 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb) skb_copy_bits(skb, ioffset, &trailer, sizeof(trailer)) < 0) return rxrpc_proto_abort(call, 0, rxrpc_badmsg_short_ack_trailer); - if (nr_acks > 0) - skb_condense(skb); - call->acks_latest_ts = ktime_get_real(); call->acks_hard_ack = hard_ack; call->acks_prev_seq = prev_pkt; From 09a5bf856aa759513afc4afd233d15bcc711b84e Mon Sep 17 00:00:00 2001 From: Dawei Feng Date: Thu, 4 Jun 2026 22:37:56 +0800 Subject: [PATCH 219/257] octeontx2-af: fix memory leak in rvu_setup_hw_resources() If rvu_npc_exact_init() fails in rvu_setup_hw_resources(), the function returns directly instead of jumping to the error handling path. This causes a resource leak for the previously initialized CGX, NPC, fwdata, and MSI-X states. Fix this by replacing the direct return with goto cgx_err to ensure proper cleanup. The bug was first flagged by an experimental analysis tool we are developing for kernel memory-management bugs while analyzing v6.13-rc1. The tool is still under development and is not yet publicly available. Manual inspection confirms that the bug is still present in v7.1-rc6. An x86_64 allyesconfig build showed no new warnings. As we do not have access to Marvell OcteonTX2 RVU AF hardware to test with, no runtime testing was able to be performed. Fixes: 3571fe07a090 ("octeontx2-af: Drop rules for NPC MCAM") Cc: stable@vger.kernel.org Signed-off-by: Dawei Feng Signed-off-by: Zilin Guan Link: https://patch.msgid.link/20260604143756.1524482-1-dawei.feng@seu.edu.cn Signed-off-by: Paolo Abeni --- drivers/net/ethernet/marvell/octeontx2/af/rvu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c index 3cf131508ecf..6e907ee19164 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c @@ -1160,7 +1160,7 @@ static int rvu_setup_hw_resources(struct rvu *rvu) err = rvu_npc_exact_init(rvu); if (err) { dev_err(rvu->dev, "failed to initialize exact match table\n"); - return err; + goto cgx_err; } /* Assign MACs for CGX mapped functions */ From 004e9ecfe6c5384f9e0b2f6f6389d42ec22789af Mon Sep 17 00:00:00 2001 From: Anton Leontev Date: Thu, 4 Jun 2026 19:59:38 +0300 Subject: [PATCH 220/257] hv_netvsc: use kmap_local_page in netvsc_copy_to_send_buf netvsc_copy_to_send_buf() copies page buffer entries into the VMBus send buffer using phys_to_virt() on the entry PFN. Entries for the RNDIS header and the skb linear data come from kmalloc'd memory and are always in the kernel direct map, but entries for skb fragments reference page cache or user pages, which on 32-bit x86 with CONFIG_HIGHMEM=y can live above the LOWMEM boundary. For such a page phys_to_virt() returns an address outside the direct map and the subsequent memcpy() faults on the transmit softirq path, which is fatal. Map the pages with kmap_local_page() instead, handling two properties of the page buffer entries: - pb[i].pfn is a Hyper-V PFN at HV_HYP_PAGE_SIZE (4K) granularity, not a native PFN. Reconstruct the physical address first and derive the native page from it, so the mapping stays correct where PAGE_SIZE > HV_HYP_PAGE_SIZE (e.g. arm64 with 64K pages). - Since commit 41a6328b2c55 ("hv_netvsc: Preserve contiguous PFN grouping in the page buffer array"), an entry describes a full physically contiguous fragment and pb[i].len can exceed PAGE_SIZE, while kmap_local_page() maps a single page. Copy page by page, splitting at native page boundaries. The copy path only handles packets smaller than the send section size (6144 bytes by default); larger packets take the cp_partial path where only the RNDIS header is copied. So entries here are bounded by the section size and a copy is split at most once on 4K-page systems. On !CONFIG_HIGHMEM configs kmap_local_page() folds to page_address() and no mapping work is added. Fixes: c25aaf814a63 ("hyperv: Enable sendbuf mechanism on the send path") Cc: stable@vger.kernel.org Signed-off-by: Anton Leontev Link: https://patch.msgid.link/20260604165938.32033-1-leontyevantony@gmail.com Signed-off-by: Paolo Abeni --- drivers/net/hyperv/netvsc.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index 59e95341f9b1..4d319c50955e 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -965,12 +966,22 @@ static void netvsc_copy_to_send_buf(struct netvsc_device *net_device, } for (i = 0; i < page_count; i++) { - char *src = phys_to_virt(pb[i].pfn << HV_HYP_PAGE_SHIFT); - u32 offset = pb[i].offset; + phys_addr_t paddr = (pb[i].pfn << HV_HYP_PAGE_SHIFT) + + pb[i].offset; u32 len = pb[i].len; - memcpy(dest, (src + offset), len); - dest += len; + while (len) { + struct page *page = phys_to_page(paddr); + u32 off = offset_in_page(paddr); + u32 chunk = min_t(u32, len, PAGE_SIZE - off); + char *src = kmap_local_page(page); + + memcpy(dest, src + off, chunk); + kunmap_local(src); + dest += chunk; + paddr += chunk; + len -= chunk; + } } if (padding) From 6edb934de9bda3b7abcec856eaee6fc8b4278dd1 Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Tue, 9 Jun 2026 15:33:13 +0800 Subject: [PATCH 221/257] gpio: zynq: fix runtime PM leak on remove pm_runtime_get_sync() increments the runtime PM usage counter even when it returns an error. zynq_gpio_remove() uses it to keep the controller active while removing the GPIO chip, but never drops the usage counter again. Balance the get with pm_runtime_put_noidle() after disabling runtime PM. Fixes: 3242ba117e9b ("gpio: Add driver for Zynq GPIO controller") Signed-off-by: Ruoyu Wang Link: https://patch.msgid.link/20260609073313.5-1-ruoyuw560@gmail.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-zynq.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c index 571e366624d2..fafca91128b2 100644 --- a/drivers/gpio/gpio-zynq.c +++ b/drivers/gpio/gpio-zynq.c @@ -1014,6 +1014,7 @@ static void zynq_gpio_remove(struct platform_device *pdev) gpiochip_remove(&gpio->chip); device_set_wakeup_capable(&pdev->dev, 0); pm_runtime_disable(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); } static struct platform_driver zynq_gpio_driver = { From 446e8c31d0fc7f1d92c06c2d2f7e7ed27f55f0c6 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Tue, 9 Jun 2026 00:45:38 +0000 Subject: [PATCH 222/257] gpio: mockup: reject invalid gpio_mockup_ranges widths gpio-mockup validates only that each second gpio_mockup_ranges value is non-negative before creating the mock chips. The fixed-base form uses the second value as the first GPIO number after the range, while the dynamic-base form uses it as the number of GPIOs. gpio_mockup_register_chip() stores the resulting number of GPIOs in a u16 and passes it through a PROPERTY_ENTRY_U16("nr-gpios", ...). Values greater than U16_MAX therefore truncate silently. For example, gpio_mockup_ranges=-1,65537 creates a one-line mock GPIO chip instead of rejecting the invalid request. Reject zero-width, reversed, and over-U16 ranges before registering any mock chip. Assisted-by: Codex:gpt-5.5-cyber-preview Signed-off-by: Samuel Moelius Link: https://patch.msgid.link/20260609004538.1240091.3fba33a20b88.gpio-mockup-ngpio-u16-truncation@trailofbits.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-mockup.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c index a7d69f3835c1..91ff789c4fa6 100644 --- a/drivers/gpio/gpio-mockup.c +++ b/drivers/gpio/gpio-mockup.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -578,7 +579,7 @@ static int __init gpio_mockup_register_chip(int idx) static int __init gpio_mockup_init(void) { - int i, num_chips, err; + int i, num_chips, err, base, ngpio; if ((gpio_mockup_num_ranges % 2) || (gpio_mockup_num_ranges > GPIO_MOCKUP_MAX_RANGES)) @@ -592,8 +593,19 @@ static int __init gpio_mockup_init(void) * always be greater than 0. */ for (i = 0; i < num_chips; i++) { - if (gpio_mockup_range_ngpio(i) < 0) + base = gpio_mockup_range_base(i); + ngpio = gpio_mockup_range_ngpio(i); + + if (ngpio <= 0) return -EINVAL; + + if (base < 0) { + if (ngpio > U16_MAX) + return -EINVAL; + } else { + if (ngpio <= base || ngpio - base > U16_MAX) + return -EINVAL; + } } gpio_mockup_dbg_dir = debugfs_create_dir("gpio-mockup", NULL); From 1c1e0fc88d6ef65bf15d517853251f75ab9d18c3 Mon Sep 17 00:00:00 2001 From: Marco Scardovi Date: Mon, 8 Jun 2026 01:05:02 +0200 Subject: [PATCH 223/257] gpio: rockchip: fix generic IRQ chip leak on remove The driver allocates domain generic chips using irq_alloc_domain_generic_chips() during probe. However, on driver remove/teardown, the generic chips are not automatically freed when the IRQ domain is removed because the domain flags do not include IRQ_DOMAIN_FLAG_DESTROY_GC. This causes both the domain generic chips structure and the associated generic chips to be leaked. Additionally, the generic chips remain on the global gc_list and may later be visited by generic IRQ chip suspend, resume, or shutdown callbacks after the GPIO bank has been removed, potentially resulting in a use-after-free and kernel crash. Fix the resource leak by explicitly calling irq_domain_remove_generic_chips() before removing the IRQ domain in rockchip_gpio_remove(). Fixes: 936ee2675eee ("gpio/rockchip: add driver for rockchip gpio") Assisted-by: Antigravity:gemini-3.5-flash Signed-off-by: Marco Scardovi Link: https://patch.msgid.link/20260607230504.35392-2-scardracs@disroot.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-rockchip.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-rockchip.c b/drivers/gpio/gpio-rockchip.c index bc97d5d5d329..9478a58f1caa 100644 --- a/drivers/gpio/gpio-rockchip.c +++ b/drivers/gpio/gpio-rockchip.c @@ -802,8 +802,10 @@ static void rockchip_gpio_remove(struct platform_device *pdev) struct rockchip_pin_bank *bank = platform_get_drvdata(pdev); irq_set_chained_handler_and_data(bank->irq, NULL, NULL); - if (bank->domain) + if (bank->domain) { + irq_domain_remove_generic_chips(bank->domain); irq_domain_remove(bank->domain); + } gpiochip_remove(&bank->gpio_chip); } From 54665dce982689e2fd99b32e9a0dcc204fda8a51 Mon Sep 17 00:00:00 2001 From: Jeremy Kerr Date: Mon, 8 Jun 2026 09:25:40 +0800 Subject: [PATCH 224/257] net: mctp: usb: fix race between urb completion and rx_retry cancellation It's possible that sequencing between setting ->stopped and cancelling the rx_retry work (in ndo_stop) could leave us with an urb queued: T1: ndo_stop T2: rx_retry_work ------------ ---------------- LD: ->stopped => false ST: ->stopped <= true usb_kill_urb() mctp_usb_rx_queue() usb_submit_urb() cancel_delayed_work_sync() That urb completion can then re-schedule rx_retry_work. Strenghen the sequencing between the stop (preventing another requeue) and the cancel by updating both atomically under a new rx lock. After setting ->rx_stopped, and cancelling pending work, we know that the requeue cannot occur, so all that's left is killing any pending urb. Fixes: 0791c0327a6e ("net: mctp: Add MCTP USB transport driver") Signed-off-by: Jeremy Kerr Link: https://patch.msgid.link/20260608-dev-mctp-usb-rx-requeue-v2-1-29a3aa507609@codeconstruct.com.au Signed-off-by: Paolo Abeni --- drivers/net/mctp/mctp-usb.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/drivers/net/mctp/mctp-usb.c b/drivers/net/mctp/mctp-usb.c index 3b5dff144177..cf6f6a93a451 100644 --- a/drivers/net/mctp/mctp-usb.c +++ b/drivers/net/mctp/mctp-usb.c @@ -22,7 +22,6 @@ struct mctp_usb { struct usb_device *usbdev; struct usb_interface *intf; - bool stopped; struct net_device *netdev; @@ -32,6 +31,9 @@ struct mctp_usb { struct urb *tx_urb; struct urb *rx_urb; + /* enforces atomic access to rx_stopped and requeuing the retry work */ + spinlock_t rx_lock; + bool rx_stopped; struct delayed_work rx_retry_work; }; @@ -122,6 +124,7 @@ static const unsigned long RX_RETRY_DELAY = HZ / 4; static int mctp_usb_rx_queue(struct mctp_usb *mctp_usb, gfp_t gfp) { + unsigned long flags; struct sk_buff *skb; int rc; @@ -147,7 +150,10 @@ static int mctp_usb_rx_queue(struct mctp_usb *mctp_usb, gfp_t gfp) return rc; err_retry: - schedule_delayed_work(&mctp_usb->rx_retry_work, RX_RETRY_DELAY); + spin_lock_irqsave(&mctp_usb->rx_lock, flags); + if (!mctp_usb->rx_stopped) + schedule_delayed_work(&mctp_usb->rx_retry_work, RX_RETRY_DELAY); + spin_unlock_irqrestore(&mctp_usb->rx_lock, flags); return rc; } @@ -248,9 +254,6 @@ static void mctp_usb_rx_retry_work(struct work_struct *work) struct mctp_usb *mctp_usb = container_of(work, struct mctp_usb, rx_retry_work.work); - if (READ_ONCE(mctp_usb->stopped)) - return; - mctp_usb_rx_queue(mctp_usb, GFP_KERNEL); } @@ -258,7 +261,7 @@ static int mctp_usb_open(struct net_device *dev) { struct mctp_usb *mctp_usb = netdev_priv(dev); - WRITE_ONCE(mctp_usb->stopped, false); + WRITE_ONCE(mctp_usb->rx_stopped, false); netif_start_queue(dev); @@ -268,17 +271,21 @@ static int mctp_usb_open(struct net_device *dev) static int mctp_usb_stop(struct net_device *dev) { struct mctp_usb *mctp_usb = netdev_priv(dev); + unsigned long flags; netif_stop_queue(dev); /* prevent RX submission retry */ - WRITE_ONCE(mctp_usb->stopped, true); + spin_lock_irqsave(&mctp_usb->rx_lock, flags); + mctp_usb->rx_stopped = true; + cancel_delayed_work(&mctp_usb->rx_retry_work); + spin_unlock_irqrestore(&mctp_usb->rx_lock, flags); + + flush_delayed_work(&mctp_usb->rx_retry_work); usb_kill_urb(mctp_usb->rx_urb); usb_kill_urb(mctp_usb->tx_urb); - cancel_delayed_work_sync(&mctp_usb->rx_retry_work); - return 0; } @@ -331,6 +338,7 @@ static int mctp_usb_probe(struct usb_interface *intf, dev->netdev = netdev; dev->usbdev = interface_to_usbdev(intf); dev->intf = intf; + spin_lock_init(&dev->rx_lock); usb_set_intfdata(intf, dev); dev->ep_in = ep_in->bEndpointAddress; From 881a3113b74964918cdd72747e3bc119c02b0c0c Mon Sep 17 00:00:00 2001 From: Jeremy Kerr Date: Mon, 8 Jun 2026 09:25:41 +0800 Subject: [PATCH 225/257] net: mctp: usb: don't fail mctp_usb_rx_queue on a deferred submission In the ndo_open path, a deferred queue open will report a failure, and so the netdev will not be ndo_stop()ed, leaving us with the rx_retry work potentially pending. Don't report a deferred queue as an error, as we are still operational. This means we use the ndo_stop() path for future cleanup, which handles rx_retry_work cancellation. Fixes: 0791c0327a6e ("net: mctp: Add MCTP USB transport driver") Signed-off-by: Jeremy Kerr Link: https://patch.msgid.link/20260608-dev-mctp-usb-rx-requeue-v2-2-29a3aa507609@codeconstruct.com.au Signed-off-by: Paolo Abeni --- drivers/net/mctp/mctp-usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/mctp/mctp-usb.c b/drivers/net/mctp/mctp-usb.c index cf6f6a93a451..fade65f2f269 100644 --- a/drivers/net/mctp/mctp-usb.c +++ b/drivers/net/mctp/mctp-usb.c @@ -154,7 +154,7 @@ static int mctp_usb_rx_queue(struct mctp_usb *mctp_usb, gfp_t gfp) if (!mctp_usb->rx_stopped) schedule_delayed_work(&mctp_usb->rx_retry_work, RX_RETRY_DELAY); spin_unlock_irqrestore(&mctp_usb->rx_lock, flags); - return rc; + return 0; } static void mctp_usb_in_complete(struct urb *urb) From 26aad08a928901296aabfbc7a33ecb951656bb98 Mon Sep 17 00:00:00 2001 From: Alessandro Schino <7991aleschino@gmail.com> Date: Fri, 5 Jun 2026 14:22:15 +0200 Subject: [PATCH 226/257] esp: fix page frag reference leak on skb_to_sgvec failure In esp_output_tail(), when esp->inplace is false, the old skb page frags are replaced with a new page from the xfrm page_frag cache The source scatterlist (sg) is built from the old frags before the replacement, and esp_ssg_unref() is responsible for releasing the old page references after the crypto operation completes However, if the second skb_to_sgvec() call (which builds the destination scatterlist from the new page) fails, the code jumps to error_free which only calls kfree(tmp). The old page frag references captured in the source scatterlist are never released: 1 sg[] is built from old frags via skb_to_sgvec() (no extra get_page) 2 nr_frags is set to 1 and frag[0] is replaced with the new page 3 Second skb_to_sgvec() fails -> goto error_free Fix this by adding a bool parameter to esp_ssg_unref() that, when true, unconditionally unrefs the source scatterlist frags. Since req->src is not yet initialized by aead_request_set_crypt() at the point of the error, the source scatterlist is obtained directly via esp_req_sg() Existing callers pass false to preserve the original behavior The same issue exists in both esp4 and esp6 as the code is identical Fixes: cac2661c53f3 ("esp4: Avoid skb_cow_data whenever possible") Fixes: 03e2a30f6a27 ("esp6: Avoid skb_cow_data whenever possible") Signed-off-by: Alessandro Schino <7991aleschino@gmail.com> Signed-off-by: Steffen Klassert --- net/ipv4/esp4.c | 17 +++++++++++------ net/ipv6/esp6.c | 17 +++++++++++------ 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c index 513c8215c947..dfc81ee969ae 100644 --- a/net/ipv4/esp4.c +++ b/net/ipv4/esp4.c @@ -96,7 +96,7 @@ static inline struct scatterlist *esp_req_sg(struct crypto_aead *aead, __alignof__(struct scatterlist)); } -static void esp_ssg_unref(struct xfrm_state *x, void *tmp, struct sk_buff *skb) +static void esp_ssg_unref(struct xfrm_state *x, void *tmp, struct sk_buff *skb, bool already_unref) { struct crypto_aead *aead = x->data; int extralen = 0; @@ -113,10 +113,13 @@ static void esp_ssg_unref(struct xfrm_state *x, void *tmp, struct sk_buff *skb) /* Unref skb_frag_pages in the src scatterlist if necessary. * Skip the first sg which comes from skb->data. */ - if (req->src != req->dst) - for (sg = sg_next(req->src); sg; sg = sg_next(sg)) + if (already_unref || req->src != req->dst) { + struct scatterlist *src = already_unref ? esp_req_sg(aead, req) : req->src; + + for (sg = sg_next(src); sg; sg = sg_next(sg)) skb_page_unref(page_to_netmem(sg_page(sg)), skb->pp_recycle); + } } #ifdef CONFIG_INET_ESPINTCP @@ -220,7 +223,7 @@ static void esp_output_done(void *data, int err) } tmp = ESP_SKB_CB(skb)->tmp; - esp_ssg_unref(x, tmp, skb); + esp_ssg_unref(x, tmp, skb, false); kfree(tmp); if (xo && (xo->flags & XFRM_DEV_RESUME)) { @@ -569,8 +572,10 @@ int esp_output_tail(struct xfrm_state *x, struct sk_buff *skb, struct esp_info * err = skb_to_sgvec(skb, dsg, (unsigned char *)esph - skb->data, assoclen + ivlen + esp->clen + alen); - if (unlikely(err < 0)) + if (unlikely(err < 0)) { + esp_ssg_unref(x, tmp, skb, true); goto error_free; + } } if ((x->props.flags & XFRM_STATE_ESN)) @@ -602,7 +607,7 @@ int esp_output_tail(struct xfrm_state *x, struct sk_buff *skb, struct esp_info * } if (sg != dsg) - esp_ssg_unref(x, tmp, skb); + esp_ssg_unref(x, tmp, skb, false); if (!err && x->encap && x->encap->encap_type == TCP_ENCAP_ESPINTCP) err = esp_output_tail_tcp(x, skb); diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c index 57481e423e59..296b57926abb 100644 --- a/net/ipv6/esp6.c +++ b/net/ipv6/esp6.c @@ -113,7 +113,7 @@ static inline struct scatterlist *esp_req_sg(struct crypto_aead *aead, __alignof__(struct scatterlist)); } -static void esp_ssg_unref(struct xfrm_state *x, void *tmp, struct sk_buff *skb) +static void esp_ssg_unref(struct xfrm_state *x, void *tmp, struct sk_buff *skb, bool already_unref) { struct crypto_aead *aead = x->data; int extralen = 0; @@ -130,10 +130,13 @@ static void esp_ssg_unref(struct xfrm_state *x, void *tmp, struct sk_buff *skb) /* Unref skb_frag_pages in the src scatterlist if necessary. * Skip the first sg which comes from skb->data. */ - if (req->src != req->dst) - for (sg = sg_next(req->src); sg; sg = sg_next(sg)) + if (already_unref || req->src != req->dst) { + struct scatterlist *src = already_unref ? esp_req_sg(aead, req) : req->src; + + for (sg = sg_next(src); sg; sg = sg_next(sg)) skb_page_unref(page_to_netmem(sg_page(sg)), skb->pp_recycle); + } } #ifdef CONFIG_INET6_ESPINTCP @@ -254,7 +257,7 @@ static void esp_output_done(void *data, int err) } tmp = ESP_SKB_CB(skb)->tmp; - esp_ssg_unref(x, tmp, skb); + esp_ssg_unref(x, tmp, skb, false); kfree(tmp); esp_output_encap_csum(skb); @@ -600,8 +603,10 @@ int esp6_output_tail(struct xfrm_state *x, struct sk_buff *skb, struct esp_info err = skb_to_sgvec(skb, dsg, (unsigned char *)esph - skb->data, assoclen + ivlen + esp->clen + alen); - if (unlikely(err < 0)) + if (unlikely(err < 0)) { + esp_ssg_unref(x, tmp, skb, true); goto error_free; + } } if ((x->props.flags & XFRM_STATE_ESN)) @@ -634,7 +639,7 @@ int esp6_output_tail(struct xfrm_state *x, struct sk_buff *skb, struct esp_info } if (sg != dsg) - esp_ssg_unref(x, tmp, skb); + esp_ssg_unref(x, tmp, skb, false); if (!err && x->encap && x->encap->encap_type == TCP_ENCAP_ESPINTCP) err = esp_output_tail_tcp(x, skb); From a5f8a90ac9f77c678a9781c0a464b635e0d63e49 Mon Sep 17 00:00:00 2001 From: Wyatt Feng Date: Fri, 5 Jun 2026 13:53:42 +0800 Subject: [PATCH 227/257] sctp: stream: fully roll back denied add-stream state When ADD_OUT_STREAMS is denied, SCTP only shrinks the queued chunks and then lowers outcnt. That leaves removed stream metadata behind, so a later re-add can reuse a stale ext and hit a null-pointer dereference in the scheduler get path. Fix the rollback by tearing down the removed stream state the same way other stream resizes do. Unschedule the current scheduler state, drop the removed stream ext state with sctp_stream_outq_migrate(), and then reschedule the remaining streams. This keeps scheduler-private RR/FC/PRIO lists consistent while fully rolling back denied outgoing stream additions. Fixes: 637784ade221 ("sctp: introduce priority based stream scheduler") Cc: stable@kernel.org Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Zhengchuan Liang Reported-by: Xin Liu Signed-off-by: Wyatt Feng Signed-off-by: Ren Wei Acked-by: Xin Long Link: https://patch.msgid.link/d78954ecd94954653ee299400e98d74a03a6f7d3.1780603399.git.bronzed_45_vested@icloud.com Signed-off-by: Jakub Kicinski --- net/sctp/stream.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/net/sctp/stream.c b/net/sctp/stream.c index c2247793c88b..5c2fdedea088 100644 --- a/net/sctp/stream.c +++ b/net/sctp/stream.c @@ -1038,6 +1038,7 @@ struct sctp_chunk *sctp_process_strreset_resp( stsn, rtsn, GFP_ATOMIC); } else if (req->type == SCTP_PARAM_RESET_ADD_OUT_STREAMS) { struct sctp_strreset_addstrm *addstrm; + const struct sctp_sched_ops *sched; __u16 number; addstrm = (struct sctp_strreset_addstrm *)req; @@ -1048,7 +1049,10 @@ struct sctp_chunk *sctp_process_strreset_resp( for (i = number; i < stream->outcnt; i++) SCTP_SO(stream, i)->state = SCTP_STREAM_OPEN; } else { - sctp_stream_shrink_out(stream, number); + sched = sctp_sched_ops_from_stream(stream); + sched->unsched_all(stream); + sctp_stream_outq_migrate(stream, NULL, number); + sched->sched_all(stream); stream->outcnt = number; } From d930276f2cddd0b7294cac7a8fe7b877f6d9e08d Mon Sep 17 00:00:00 2001 From: Kyle Meyer Date: Fri, 5 Jun 2026 17:25:24 -0500 Subject: [PATCH 228/257] bnxt_en: Fix NULL pointer dereference PCIe errors detected by a Root Port or Downstream Port cause error recovery services to run on all subordinate devices regardless of administrative state. The .error_detected() callback, bnxt_io_error_detected(), disables and synchronizes IRQs via bnxt_disable_int_sync(), which calls bnxt_cp_num_to_irq_num() to map completion rings to IRQs using bp->bnapi. Since bp->bnapi is allocated on NIC open and freed on NIC close, PCIe error recovery on a closed NIC can dereference a NULL pointer. Check if bp->bnapi is NULL before disabling and synchronizing IRQs. Fixes: e5811b8c09df ("bnxt_en: Add IRQ remapping logic.") Cc: stable@vger.kernel.org Signed-off-by: Kyle Meyer Reviewed-by: Pavan Chebbi Link: https://patch.msgid.link/aiNM1CY2-StPilxW@hpe.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 35e1f8f663c7..c999f9733326 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -5748,7 +5748,7 @@ static void bnxt_disable_int_sync(struct bnxt *bp) { int i; - if (!bp->irq_tbl) + if (!bp->irq_tbl || !bp->bnapi) return; atomic_inc(&bp->intr_sem); From f8373d7090b745728de66308deeecc67e8d319ce Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Mon, 8 Jun 2026 08:22:34 -0400 Subject: [PATCH 229/257] sctp: fix uninit-value in __sctp_rcv_asconf_lookup() __sctp_rcv_asconf_lookup() in net/sctp/input.c only checks that the ASCONF chunk can hold the ADDIP header and a parameter header, then calls af->from_addr_param(), which reads the full address (16 bytes for IPv6) trusting the parameter's declared length. An unauthenticated peer can send a truncated trailing ASCONF chunk that declares an IPv6 address parameter but stops after the 4-byte parameter header; reached from the no-association lookup path, from_addr_param() then reads uninitialized bytes past the parameter. Impact: an unauthenticated SCTP peer makes the receive path read up to 16 bytes of uninitialized memory past a truncated ASCONF address parameter. The sibling __sctp_rcv_init_lookup() bounds parameters with sctp_walk_params(); this path open-codes the fetch and omits the bound. Verify the whole address parameter lies within the chunk before from_addr_param() reads it, the same class of fix as commit 51e5ad549c43 ("net: sctp: fix KMSAN uninit-value in sctp_inq_pop"). Fixes: df2185771439 ("[SCTP]: Update association lookup to look at ASCONF chunks as well") Signed-off-by: Michael Bommarito Acked-by: Xin Long Link: https://patch.msgid.link/20260608122234.459098-1-michael.bommarito@gmail.com Signed-off-by: Jakub Kicinski --- net/sctp/input.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/net/sctp/input.c b/net/sctp/input.c index e119e460ccde..864741fae418 100644 --- a/net/sctp/input.c +++ b/net/sctp/input.c @@ -1204,6 +1204,14 @@ static struct sctp_association *__sctp_rcv_asconf_lookup( /* Skip over the ADDIP header and find the Address parameter */ param = (union sctp_addr_param *)(asconf + 1); + /* The whole address parameter must lie within the chunk before + * af->from_addr_param() reads the variable-length address; otherwise a + * truncated trailing ASCONF chunk lets it read uninitialized bytes past + * the parameter. + */ + if (sizeof(*asconf) + ntohs(param->p.length) > ntohs(ch->length)) + return NULL; + af = sctp_get_af_specific(param_type2af(param->p.type)); if (unlikely(!af)) return NULL; From d289d5307762d1838aaece22c6b6fcad9e8865f9 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 8 Jun 2026 15:59:18 +0000 Subject: [PATCH 230/257] ip6_vti: set netns_immutable on the fallback device. john1988 and Noam Rathaus reported that vti6_init_net() does not set the netns_immutable flag on the per-netns fallback tunnel device (ip6_vti0). Other similar tunnel drivers (like ip6_tunnel, sit, ip6_gre, and ip_tunnel) correctly set this flag during their fallback device initialization to prevent them from being moved to another network namespace. Fixes: 61220ab34948 ("vti6: Enable namespace changing") Reported-by: Noam Rathaus Signed-off-by: Eric Dumazet Cc: Steffen Klassert Reviewed-by: Nicolas Dichtel Link: https://patch.msgid.link/20260608155918.787644-1-edumazet@google.com Signed-off-by: Jakub Kicinski --- net/ipv6/ip6_vti.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c index df793c8bfffb..d2b74a6f2cf6 100644 --- a/net/ipv6/ip6_vti.c +++ b/net/ipv6/ip6_vti.c @@ -1159,6 +1159,7 @@ static int __net_init vti6_init_net(struct net *net) goto err_alloc_dev; dev_net_set(ip6n->fb_tnl_dev, net); ip6n->fb_tnl_dev->rtnl_link_ops = &vti6_link_ops; + ip6n->fb_tnl_dev->netns_immutable = true; err = vti6_fb_tnl_dev_init(ip6n->fb_tnl_dev); if (err < 0) From 6f4c80a2a7e6d06753b89a578b710a2499a5e62b Mon Sep 17 00:00:00 2001 From: Xin Long Date: Sun, 7 Jun 2026 19:03:47 -0400 Subject: [PATCH 231/257] sctp: validate embedded INIT chunk and address list lengths in cookie sctp_unpack_cookie() only checked that the embedded INIT chunk length did not exceed the remaining cookie payload, but did not ensure that the INIT chunk is large enough to contain a complete INIT header. A malformed COOKIE_ECHO can therefore carry a truncated INIT chunk whose length field is smaller than sizeof(struct sctp_init_chunk). Later, sctp_process_init() accesses INIT parameters unconditionally, which may lead to out-of-bounds reads. In addition, raw_addr_list_len is not fully validated against the remaining cookie payload. When cookie authentication is disabled, an attacker can supply an oversized raw_addr_list_len and cause sctp_raw_to_bind_addrs() to read beyond the end of the cookie. The address parser also lacks sufficient bounds checks for parameter headers and lengths, allowing malformed address parameters to trigger out-of-bounds reads. Fix this by: - requiring the embedded INIT chunk length to be at least sizeof(struct sctp_init_chunk); - validating that the INIT chunk and raw address list together fit within the cookie payload; - verifying sufficient data exists for each address parameter header and payload before parsing it. Note that sctp_verify_init() must be called after sctp_unpack_cookie() and before sctp_process_init() when cookie authentication is disabled. This will be addressed in a separate patch. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reported-by: Sashiko Signed-off-by: Xin Long Link: https://patch.msgid.link/75af23a89adf881a0895d511775e4770da367cbf.1780873427.git.lucien.xin@gmail.com Signed-off-by: Jakub Kicinski --- net/sctp/bind_addr.c | 11 ++++++++++- net/sctp/sm_make_chunk.c | 9 +++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/net/sctp/bind_addr.c b/net/sctp/bind_addr.c index 75e3e61d494e..31737f144c7f 100644 --- a/net/sctp/bind_addr.c +++ b/net/sctp/bind_addr.c @@ -275,6 +275,16 @@ int sctp_raw_to_bind_addrs(struct sctp_bind_addr *bp, __u8 *raw_addr_list, param = (struct sctp_paramhdr *)raw_addr_list; rawaddr = (union sctp_addr_param *)raw_addr_list; + if (addrs_len < sizeof(*param)) { + retval = -EINVAL; + goto out_err; + } + len = ntohs(param->length); + if (addrs_len < len) { + retval = -EINVAL; + goto out_err; + } + af = sctp_get_af_specific(param_type2af(param->type)); if (unlikely(!af) || !af->from_addr_param(&addr, rawaddr, htons(port), 0)) { @@ -291,7 +301,6 @@ int sctp_raw_to_bind_addrs(struct sctp_bind_addr *bp, __u8 *raw_addr_list, goto out_err; next: - len = ntohs(param->length); addrs_len -= len; raw_addr_list += len; } diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index 85264862fb6b..1741a9f33d8c 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -1731,8 +1731,8 @@ struct sctp_association *sctp_unpack_cookie( struct sk_buff *skb = chunk->skb; struct sctp_cookie *bear_cookie; struct sctp_chunkhdr *ch; + unsigned int len, chlen; enum sctp_scope scope; - unsigned int len; ktime_t kt; /* Header size is static data prior to the actual cookie, including @@ -1761,7 +1761,12 @@ struct sctp_association *sctp_unpack_cookie( bear_cookie = &cookie->c; ch = (struct sctp_chunkhdr *)(bear_cookie + 1); - if (ntohs(ch->length) > len - fixed_size) + chlen = ntohs(ch->length); + if (chlen < sizeof(struct sctp_init_chunk)) + goto malformed; + if (chlen > len - fixed_size) + goto malformed; + if (bear_cookie->raw_addr_list_len > len - fixed_size - chlen) goto malformed; /* Verify the cookie's MAC, if cookie authentication is enabled. */ From 1ee90b77b727df903033db873c75caac5c27ec98 Mon Sep 17 00:00:00 2001 From: Kyle Zeng Date: Sat, 6 Jun 2026 19:18:19 -0700 Subject: [PATCH 232/257] net: guard timestamp cmsgs to real error queue skbs skb_is_err_queue() treats PACKET_OUTGOING as the sole marker for an skb from sk_error_queue. That assumption is not true for AF_PACKET sockets: outgoing packet taps are also delivered to packet sockets with skb->pkt_type == PACKET_OUTGOING, but their skb->cb is owned by AF_PACKET instead of struct sock_exterr_skb. If such an skb is received with timestamping enabled, the generic timestamp cmsg path can read AF_PACKET control-buffer state as sock_exterr_skb::opt_stats. With SO_RXQ_OVFL enabled, the packet drop counter overlaps opt_stats. An odd drop count makes the path emit SCM_TIMESTAMPING_OPT_STATS with skb->len and skb->data. For non-linear skbs this copies past the linear head and can trigger hardened usercopy or disclose adjacent heap contents. Keep skb_is_err_queue() local to net/socket.c, but make it verify that the PACKET_OUTGOING marker is paired with the sock_rmem_free destructor installed by sock_queue_err_skb(). AF_PACKET receive skbs use normal receive ownership and no longer pass as error-queue skbs, while legitimate sk_error_queue entries keep the PACKET_OUTGOING marker and sock_rmem_free ownership. Fixes: 8605330aac5a ("tcp: fix SCM_TIMESTAMPING_OPT_STATS for normal skbs") Signed-off-by: Kyle Zeng Reviewed-by: Kuniyuki Iwashima Reviewed-by: Willem de Bruijn Link: https://patch.msgid.link/20260607021819.49698-1-kylebot@openai.com Signed-off-by: Jakub Kicinski --- include/net/sock.h | 1 + net/core/skbuff.c | 6 +++--- net/socket.c | 11 ++++++----- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/include/net/sock.h b/include/net/sock.h index dccd3738c368..95e157eee8d9 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -1856,6 +1856,7 @@ struct sk_buff *sock_omalloc(struct sock *sk, unsigned long size, gfp_t priority); void skb_orphan_partial(struct sk_buff *skb); void sock_rfree(struct sk_buff *skb); +void sock_rmem_free(struct sk_buff *skb); void sock_efree(struct sk_buff *skb); #ifdef CONFIG_INET void sock_edemux(struct sk_buff *skb); diff --git a/net/core/skbuff.c b/net/core/skbuff.c index c02f0a507ba8..8eab8eb5006a 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -5450,7 +5450,7 @@ int skb_cow_data(struct sk_buff *skb, int tailbits, struct sk_buff **trailer) } EXPORT_SYMBOL_GPL(skb_cow_data); -static void sock_rmem_free(struct sk_buff *skb) +void sock_rmem_free(struct sk_buff *skb) { struct sock *sk = skb->sk; @@ -5459,8 +5459,8 @@ static void sock_rmem_free(struct sk_buff *skb) static void skb_set_err_queue(struct sk_buff *skb) { - /* pkt_type of skbs received on local sockets is never PACKET_OUTGOING. - * So, it is safe to (mis)use it to mark skbs on the error queue. + /* The error-queue test in skb_is_err_queue() matches this marker + * with the sock_rmem_free destructor installed by sock_queue_err_skb(). */ skb->pkt_type = PACKET_OUTGOING; BUILD_BUG_ON(PACKET_OUTGOING == 0); diff --git a/net/socket.c b/net/socket.c index 22a412fdec07..c2698a1441a7 100644 --- a/net/socket.c +++ b/net/socket.c @@ -852,12 +852,13 @@ EXPORT_SYMBOL(kernel_sendmsg); static bool skb_is_err_queue(const struct sk_buff *skb) { - /* pkt_type of skbs enqueued on the error queue are set to - * PACKET_OUTGOING in skb_set_err_queue(). This is only safe to do - * in recvmsg, since skbs received on a local socket will never - * have a pkt_type of PACKET_OUTGOING. + /* Error-queue skbs are marked as PACKET_OUTGOING in + * skb_set_err_queue() and use the destructor installed by + * sock_queue_err_skb(). PACKET_OUTGOING alone is not unique: + * AF_PACKET outgoing taps use the same pkt_type. */ - return skb->pkt_type == PACKET_OUTGOING; + return skb->pkt_type == PACKET_OUTGOING && + skb->destructor == sock_rmem_free; } /* On transmit, software and hardware timestamps are returned independently. From 34080db3e70ddf94c38512ad2331e3c3afca6cc1 Mon Sep 17 00:00:00 2001 From: Weiming Shi Date: Sat, 6 Jun 2026 12:24:48 -0700 Subject: [PATCH 233/257] net/rds: fix NULL deref in rds_ib_send_cqe_handler() on masked atomic completion rds_ib_xmit_atomic() always programs a masked atomic opcode (IB_WR_MASKED_ATOMIC_CMP_AND_SWP or IB_WR_MASKED_ATOMIC_FETCH_AND_ADD) for every RDS atomic cmsg. But the completion-side switch in rds_ib_send_unmap_op() only handles the non-masked opcodes, so a masked atomic completion falls through to default and returns rm == NULL while send->s_op is left set. rds_ib_send_cqe_handler() then dereferences the NULL rm via rm->m_final_op, oopsing in softirq context. An unprivileged AF_RDS sendmsg() of an atomic cmsg over an active RDS/IB connection triggers it; on hardware that natively accepts masked atomics (mlx4, mlx5) no extra setup is needed. RDS/IB: rds_ib_send_unmap_op: unexpected opcode 0xd in WR! Oops: general protection fault [#1] SMP KASAN KASAN: null-ptr-deref in range [0x0000000000000190-0x0000000000000197] RIP: rds_ib_send_cqe_handler+0x25c/0xb10 (net/rds/ib_send.c:282) Call Trace: rds_ib_send_cqe_handler (net/rds/ib_send.c:282) poll_scq (net/rds/ib_cm.c:274) rds_ib_tasklet_fn_send (net/rds/ib_cm.c:294) tasklet_action_common (kernel/softirq.c:943) handle_softirqs (kernel/softirq.c:573) run_ksoftirqd (kernel/softirq.c:479) Kernel panic - not syncing: Fatal exception in interrupt Handle the masked atomic opcodes in the same case as the non-masked ones: they map to the same struct rds_message.atomic union member, so the existing container_of()/rds_ib_send_unmap_atomic() body is correct for them. Fixes: 20c72bd5f5f9 ("RDS: Implement masked atomic operations") Reported-by: Xiang Mei Signed-off-by: Weiming Shi Reviewed-by: Allison Henderson Link: https://patch.msgid.link/20260606192447.1179255-2-bestswngs@gmail.com Signed-off-by: Jakub Kicinski --- net/rds/ib_send.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/rds/ib_send.c b/net/rds/ib_send.c index fcd04c29f543..d6be95542119 100644 --- a/net/rds/ib_send.c +++ b/net/rds/ib_send.c @@ -170,6 +170,8 @@ static struct rds_message *rds_ib_send_unmap_op(struct rds_ib_connection *ic, break; case IB_WR_ATOMIC_FETCH_AND_ADD: case IB_WR_ATOMIC_CMP_AND_SWP: + case IB_WR_MASKED_ATOMIC_FETCH_AND_ADD: + case IB_WR_MASKED_ATOMIC_CMP_AND_SWP: if (send->s_op) { rm = container_of(send->s_op, struct rds_message, atomic); rds_ib_send_unmap_atomic(ic, send->s_op, wc_status); From 7f2fcff15e99bb852f6967396ed12b38376e2c8d Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Sat, 6 Jun 2026 22:44:28 -0700 Subject: [PATCH 234/257] tun: zero the whole vnet header in tun_put_user() tun_put_user() declares an on-stack struct virtio_net_hdr_v1_hash_tunnel without zeroing it. For a non-tunnel skb, virtio_net_hdr_tnl_from_skb() only initializes the first 10 bytes (sizeof(struct virtio_net_hdr)), leaving bytes 10..23 (num_buffers and the hash/tunnel fields) as stack garbage. An unprivileged user can set the vnet header size to 24 with TUNSETVNETHDRSZ, so __tun_vnet_hdr_put() copies all 24 bytes of the partially-initialized struct to userspace, leaking 14 bytes of kernel stack on every read of a non-tunnel packet. Fix it the same way tun_get_user() already does by zeroing the whole header right after declaration. Fixes: 288f30435132 ("tun: enable gso over UDP tunnel support.") Reported-by: Weiming Shi Signed-off-by: Xiang Mei Reviewed-by: Willem de Bruijn Link: https://patch.msgid.link/20260607054428.3050243-1-xmei5@asu.edu Signed-off-by: Jakub Kicinski --- drivers/net/tun.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 9e7744eb57a3..fed9dfdfcc3b 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -2070,6 +2070,7 @@ static ssize_t tun_put_user(struct tun_struct *tun, struct virtio_net_hdr_v1_hash_tunnel hdr; struct virtio_net_hdr *gso; + memset(&hdr, 0, sizeof(hdr)); ret = tun_vnet_hdr_tnl_from_skb(tun->flags, tun->dev, skb, &hdr); if (ret) From 627366c51145a07f675b1800fb5ea2ec960bd900 Mon Sep 17 00:00:00 2001 From: Vadim Fedorenko Date: Mon, 8 Jun 2026 15:59:52 +0000 Subject: [PATCH 235/257] ptp: ocp: fix resource freeing order Commit a60fc3294a37 ("ptp: rework ptp_clock_unregister() to disable events") added a call to ptp_disable_all_events() which changes the configuration of pins if they support EXTTS events. In ptp_ocp_detach() pins resources are freed before ptp_clock_unregister() and it leads to use-after-free during driver removal. Fix it by changing the order of free/unregister calls. To avoid irq handler running on the other core while ptp device unregistering, call synchronize_irq() after HW is configured to stop producing irqs and no irqs are in-flight. Fixes: a60fc3294a37 ("ptp: rework ptp_clock_unregister() to disable events") Signed-off-by: Vadim Fedorenko Link: https://patch.msgid.link/20260608155952.240304-1-vadim.fedorenko@linux.dev Signed-off-by: Jakub Kicinski --- drivers/ptp/ptp_ocp.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c index beacc2ffb166..735385539b9f 100644 --- a/drivers/ptp/ptp_ocp.c +++ b/drivers/ptp/ptp_ocp.c @@ -2479,8 +2479,13 @@ ptp_ocp_ts_enable(void *priv, u32 req, bool enable) iowrite32(1, ®->intr_mask); iowrite32(1, ®->intr); } else { + int irq_vec = pci_irq_vector(bp->pdev, ext->irq_vec); + iowrite32(0, ®->intr_mask); iowrite32(0, ®->enable); + ioread32(®->intr_mask); + if (irq_vec > 0) + synchronize_irq(irq_vec); } return 0; @@ -4867,6 +4872,22 @@ ptp_ocp_detach(struct ptp_ocp *bp) ptp_ocp_detach_sysfs(bp); ptp_ocp_attr_group_del(bp); timer_delete_sync(&bp->watchdog); + /* Disable interrupts on all timestampers */ + if (bp->ts0) + ptp_ocp_ts_enable(bp->ts0, 0, false); + if (bp->ts1) + ptp_ocp_ts_enable(bp->ts1, 0, false); + if (bp->ts2) + ptp_ocp_ts_enable(bp->ts2, 0, false); + if (bp->ts3) + ptp_ocp_ts_enable(bp->ts3, 0, false); + if (bp->ts4) + ptp_ocp_ts_enable(bp->ts4, 0, false); + if (bp->pps) + ptp_ocp_ts_enable(bp->pps, ~0, false); + if (bp->ptp) + ptp_clock_unregister(bp->ptp); + kfree(bp->ptp_info.pin_config); ptp_ocp_unregister_ext(bp->ts0); ptp_ocp_unregister_ext(bp->ts1); ptp_ocp_unregister_ext(bp->ts2); @@ -4884,9 +4905,6 @@ ptp_ocp_detach(struct ptp_ocp *bp) clk_hw_unregister_fixed_rate(bp->i2c_clk); if (bp->n_irqs) pci_free_irq_vectors(bp->pdev); - if (bp->ptp) - ptp_clock_unregister(bp->ptp); - kfree(bp->ptp_info.pin_config); device_unregister(&bp->dev); } From 64911f5aac534191e6b9a52ca1d50ba870a12d86 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 9 Jun 2026 14:17:50 +0200 Subject: [PATCH 236/257] gpio: fix cleanup path on hog failure If gpiochip_hog_lines() successfully processes some hogs but fails on a later one, the error handling path in gpiochip_add_data_with_key() jumps directly to err_remove_of_chip. This leaks resources allocated earlier for ACPI, interrupts and hogs that were successfully processed. Use the right label in error path. Closes: https://sashiko.dev/#/patchset/20260608210108.36248-1-dan%40reactivated.net Fixes: d1d564ec4992 ("gpio: move hogs into GPIO core") Reviewed-by: Andy Shevchenko Reviewed-by: Mika Westerberg Link: https://patch.msgid.link/20260609-gpio-hogs-fixes-v1-2-b4064f8070e7@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 1e6dce430dca..d3e0d77f74e7 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1291,7 +1291,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, ret = gpiochip_hog_lines(gc); if (ret) - goto err_remove_of_chip; + goto err_free_hogs; ret = gpiochip_irqchip_init_valid_mask(gc); if (ret) From a23226b7c1f69eafd9ced4e037fb51c9758c0501 Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Mon, 8 Jun 2026 22:01:08 +0100 Subject: [PATCH 237/257] gpiolib: handle gpio-hogs only once Commit d1d564ec49929 ("gpio: move hogs into GPIO core") introduced a behaviour change that breaks boot on Raspberry Pi 5 when using the firmware-supplied device tree: gpiochip_add_data_with_key: GPIOs 544..575 (/soc@107c000000/gpio@7d517c00) failed to register, -22 brcmstb-gpio 107d517c00.gpio: Could not add gpiochip for bank 1 brcmstb-gpio 107d517c00.gpio: probe with driver brcmstb-gpio failed with error -22 gpio-brcmstb registers two gpio_chips against the device tree node gpio@7d517c00, one for each bank. The firmware-supplied DT includes a gpio-hog on RP1 RUN, and this gpio-hog is attempted to be applied to *both* gpio_chips. This succeeds against bank 0 (which hosts the GPIO) and fails for bank 1 (which does not). In the previous implementation, failures to apply gpio-hogs were quietly ignored. In the new code, the error code propagates and causes probe to fail. Closely approximate the previous behaviour by using the OF_POPULATED flag to ensure that each gpio-hog is processed only once. The flag was previously being set before the gpio-hogs were processed, so as part of this change, the flag now gets set only after the gpio-hog is actioned. The handling of gpio-hogs on a DT node with multiple gpio_chips remains a bit incomplete/unclear, but this at least retains the ability to apply hogs to the first gpio_chip per node. Fixes: d1d564ec49929 ("gpio: move hogs into GPIO core") Signed-off-by: Daniel Drake Link: https://patch.msgid.link/20260608210108.36248-1-dan@reactivated.net Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-of.c | 5 ----- drivers/gpio/gpiolib.c | 8 ++++++++ 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 2c923d17541f..813dbcb91f6f 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -1066,11 +1066,6 @@ int of_gpiochip_add(struct gpio_chip *chip) of_node_get(np); - for_each_available_child_of_node_scoped(np, child) { - if (of_property_read_bool(child, "gpio-hog")) - of_node_set_flag(child, OF_POPULATED); - } - return ret; } diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index d3e0d77f74e7..c1f9c0d367d5 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1031,9 +1031,17 @@ static int gpiochip_hog_lines(struct gpio_chip *gc) if (!fwnode_property_present(fwnode, "gpio-hog")) continue; + /* The hog may have been handled by another gpio_chip on the same fwnode */ + if (is_of_node(fwnode) && + of_node_check_flag(to_of_node(fwnode), OF_POPULATED)) + continue; + ret = gpiochip_add_hog(gc, fwnode); if (ret) return ret; + + if (is_of_node(fwnode)) + of_node_set_flag(to_of_node(fwnode), OF_POPULATED); } return 0; From 805d5a2b792819171be100c50c9ddafa0f8c2231 Mon Sep 17 00:00:00 2001 From: Jann Horn Date: Fri, 5 Jun 2026 22:27:33 +0200 Subject: [PATCH 238/257] namespace: restrict OPEN_TREE_NAMESPACE/FSMOUNT_NAMESPACE to directories open_tree(..., OPEN_TREE_NAMESPACE) and fsmount(..., FSMOUNT_NAMESPACE, ...) currently work on non-directories, like regular files. That's bad for two reasons: - It ends up mounting a regular file over the inherited namespace root, which is a directory; mounting a non-directory over a directory is normally explicitly forbidden, see for example do_move_mount() - It causes setns() on the new namespace to set the cwd to a regular file, which the rest of VFS does not expect Fix it by restricting create_new_namespace() (which is used by both of these flags) to directories. Leave the behavior for OPEN_TREE_CLONE as-is, that seems unproblematic. Fixes: 9b8a0ba68246 ("mount: add OPEN_TREE_NAMESPACE") Cc: Al Viro Cc: Christian Brauner Cc: Jan Kara Cc: stable@kernel.org Signed-off-by: Jann Horn Signed-off-by: Linus Torvalds --- fs/namespace.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/namespace.c b/fs/namespace.c index f5905f4ec560..341ddd353b3a 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -3103,6 +3103,9 @@ static struct mnt_namespace *create_new_namespace(struct path *path, unsigned int copy_flags = 0; bool locked = false, recurse = flags & MOUNT_COPY_RECURSIVE; + if (unlikely(!d_can_lookup(path->dentry))) + return ERR_PTR(-ENOTDIR); + if (user_ns != ns->user_ns) copy_flags |= CL_SLAVE; From a5c0359f5cbc51a2e2b114d6041e0f3c73f903e9 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 8 Jun 2026 16:46:13 +0000 Subject: [PATCH 239/257] ip6_vti: fix incorrect tunnel matching in vti6_tnl_lookup() In vti6_tnl_lookup(), when an exact match for a tunnel fails, the code falls back to searching for wildcard tunnels: - Tunnels matching the packet's local address, with any remote address wildcard remote). - Tunnels matching the packet's remote address, with any local address (wildcard local). However, vti6 stores all these different types of tunnels in the same hash table (ip6n->tnls_r_l) prone to hash collisions. The bug is that the fallback search loops in vti6_tnl_lookup() were missing checks to ensure that the candidate tunnel actually has a wildcard address. Fixes: fbe68ee87522 ("vti6: Add a lookup method for tunnels with wildcard endpoints.") Signed-off-by: Eric Dumazet Cc: Steffen Klassert Reviewed-by: Nicolas Dichtel Link: https://patch.msgid.link/20260608164613.933023-1-edumazet@google.com Signed-off-by: Jakub Kicinski --- net/ipv6/ip6_vti.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c index d2b74a6f2cf6..d871cab6938d 100644 --- a/net/ipv6/ip6_vti.c +++ b/net/ipv6/ip6_vti.c @@ -106,6 +106,7 @@ vti6_tnl_lookup(struct net *net, const struct in6_addr *remote, hash = HASH(&any, local); for_each_vti6_tunnel_rcu(ip6n->tnls_r_l[hash]) { if (ipv6_addr_equal(local, &t->parms.laddr) && + ipv6_addr_any(&t->parms.raddr) && (t->dev->flags & IFF_UP)) return t; } @@ -113,6 +114,7 @@ vti6_tnl_lookup(struct net *net, const struct in6_addr *remote, hash = HASH(remote, &any); for_each_vti6_tunnel_rcu(ip6n->tnls_r_l[hash]) { if (ipv6_addr_equal(remote, &t->parms.raddr) && + ipv6_addr_any(&t->parms.laddr) && (t->dev->flags & IFF_UP)) return t; } From 512db8267b73a220a64180d95ab5eebe7c4964a8 Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Mon, 8 Jun 2026 02:32:05 -0700 Subject: [PATCH 240/257] rds: mark snapshot pages dirty in rds_info_getsockopt() rds_info_getsockopt() pins the destination user pages with FOLL_WRITE and the RDS_INFO_* producers memcpy the snapshot into them through kmap_atomic(). Because that copy goes through the kernel direct map, the dirty bit on the user PTE is never set, so unpin_user_pages() releases the pages without marking them dirty. A file-backed destination page can then be reclaimed without writeback, silently discarding the copied data. Use unpin_user_pages_dirty_lock() with make_dirty=true so the modified pages are marked dirty before they are unpinned. Fixes: a8c879a7ee98 ("RDS: Info and stats") Signed-off-by: Breno Leitao Reviewed-by: Allison Henderson Link: https://patch.msgid.link/20260608-rds_fix-v1-1-006c88543408@debian.org Signed-off-by: Jakub Kicinski --- net/rds/info.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/rds/info.c b/net/rds/info.c index f1b29994934a..17061f6ff74e 100644 --- a/net/rds/info.c +++ b/net/rds/info.c @@ -235,7 +235,7 @@ int rds_info_getsockopt(struct socket *sock, int optname, char __user *optval, out: if (pages) - unpin_user_pages(pages, nr_pages); + unpin_user_pages_dirty_lock(pages, nr_pages, true); kfree(pages); return ret; From ccb9fd4b87538ccf19ccff78ee26700526d94867 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 2 Jun 2026 17:04:25 +0200 Subject: [PATCH 241/257] netfilter: revalidate bridge ports ebt_redirect_tg() dereferences br_port_get_rcu() return without a NULL check, causing a kernel panic when the bridge port has been removed between the original hook invocation and an NFQUEUE reinject. A mere NULL check isn't sufficient, however. As sashiko review points out userspace can not only remove the port from the bridge, it could also place the device in a different virtual device, e.g. macvlan. If this happens, we must drop the packet, there is no way for us to reinject it into the bridge path. Switch to _upper API, we don't need the bridge port structure. Also, this fix keeps another bug intact: Both nfnetlink_log and nfnetlink_queue use CONFIG_BRIDGE_NETFILTER too aggressive, which prevents certain logging features when queueing in bridge family: NETFILTER_FAMILY_BRIDGE can be enabled while the old CONFIG_BRIDGE_NETFILTER cruft is off. Fixes tag is a common ancestor, this was always broken. Fixes: f350a0a87374 ("bridge: use rx_handler_data pointer to store net_bridge_port pointer") Reported-by: Ji'an Zhou Assisted-by: Claude:claude-sonnet-4-6 Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/bridge/netfilter/ebt_dnat.c | 4 +- net/bridge/netfilter/ebt_redirect.c | 16 +++++--- net/netfilter/nfnetlink_log.c | 23 +++++++++-- net/netfilter/nfnetlink_queue.c | 64 +++++++++++++++++++++++++---- 4 files changed, 89 insertions(+), 18 deletions(-) diff --git a/net/bridge/netfilter/ebt_dnat.c b/net/bridge/netfilter/ebt_dnat.c index 3fda71a8579d..73f185cccd63 100644 --- a/net/bridge/netfilter/ebt_dnat.c +++ b/net/bridge/netfilter/ebt_dnat.c @@ -39,7 +39,9 @@ ebt_dnat_tg(struct sk_buff *skb, const struct xt_action_param *par) dev = xt_in(par); break; case NF_BR_PRE_ROUTING: - dev = br_port_get_rcu(xt_in(par))->br->dev; + dev = netdev_master_upper_dev_get_rcu(xt_in(par)); + if (!dev) /* bridge port removed? */ + return EBT_DROP; break; default: dev = NULL; diff --git a/net/bridge/netfilter/ebt_redirect.c b/net/bridge/netfilter/ebt_redirect.c index 307790562b49..83486cd4d564 100644 --- a/net/bridge/netfilter/ebt_redirect.c +++ b/net/bridge/netfilter/ebt_redirect.c @@ -24,12 +24,18 @@ ebt_redirect_tg(struct sk_buff *skb, const struct xt_action_param *par) if (skb_ensure_writable(skb, 0)) return EBT_DROP; - if (xt_hooknum(par) != NF_BR_BROUTING) - /* rcu_read_lock()ed by nf_hook_thresh */ - ether_addr_copy(eth_hdr(skb)->h_dest, - br_port_get_rcu(xt_in(par))->br->dev->dev_addr); - else + if (xt_hooknum(par) != NF_BR_BROUTING) { + const struct net_device *dev; + + dev = netdev_master_upper_dev_get_rcu(xt_in(par)); + if (!dev) + return EBT_DROP; + + ether_addr_copy(eth_hdr(skb)->h_dest, dev->dev_addr); + } else { ether_addr_copy(eth_hdr(skb)->h_dest, xt_in(par)->dev_addr); + } + skb->pkt_type = PACKET_HOST; return info->target; } diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c index 2439cbbd5b26..fa3657599861 100644 --- a/net/netfilter/nfnetlink_log.c +++ b/net/netfilter/nfnetlink_log.c @@ -451,6 +451,23 @@ static int nfulnl_put_bridge(struct nfulnl_instance *inst, const struct sk_buff return -1; } +#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) +static int nflog_put_master_ifindex(struct sk_buff *nlskb, int attr, + const struct net_device *dev) +{ + const struct net_device *upper; + + if (dev && !netif_is_bridge_port(dev)) + return 0; + + upper = netdev_master_upper_dev_get_rcu((struct net_device *)dev); + if (upper && nla_put_be32(nlskb, attr, htonl(upper->ifindex))) + return -EMSGSIZE; + + return 0; +} +#endif + /* This is an inline function, we don't really care about a long * list of arguments */ static inline int @@ -505,8 +522,7 @@ __build_packet_message(struct nfnl_log_net *log, /* rcu_read_lock()ed by nf_hook_thresh or * nf_log_packet. */ - nla_put_be32(inst->skb, NFULA_IFINDEX_INDEV, - htonl(br_port_get_rcu(indev)->br->dev->ifindex))) + nflog_put_master_ifindex(inst->skb, NFULA_IFINDEX_INDEV, indev)) goto nla_put_failure; } else { int physinif; @@ -542,8 +558,7 @@ __build_packet_message(struct nfnl_log_net *log, /* rcu_read_lock()ed by nf_hook_thresh or * nf_log_packet. */ - nla_put_be32(inst->skb, NFULA_IFINDEX_OUTDEV, - htonl(br_port_get_rcu(outdev)->br->dev->ifindex))) + nflog_put_master_ifindex(inst->skb, NFULA_IFINDEX_OUTDEV, outdev)) goto nla_put_failure; } else { struct net_device *physoutdev; diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c index 60ab88d45096..c5e29fec419b 100644 --- a/net/netfilter/nfnetlink_queue.c +++ b/net/netfilter/nfnetlink_queue.c @@ -440,10 +440,47 @@ static bool nf_ct_drop_unconfirmed(const struct nf_queue_entry *entry, bool *is_ return false; } +static bool nf_bridge_port_valid(const struct net_device *dev) +{ + if (!dev) + return true; + + return netif_is_bridge_port(dev); +} + +/* queued skbs leave rcu protection. We bump device refcount so that + * the device cannot go away. However, while packet was out the port + * could have been removed from the bridge. + * + * Ensure in+outdev are still part of a bridge at reinject time. + * + * The device rx_handler_data could even be pointing at data that is + * not a net_bridge_port structure. + */ +static bool nf_bridge_ports_valid(const struct nf_queue_entry *entry) +{ +#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) + if (!nf_bridge_port_valid(entry->physin) || + !nf_bridge_port_valid(entry->physout)) + return false; +#endif + if (entry->state.pf != PF_BRIDGE) + return true; + + if (!nf_bridge_port_valid(entry->state.in) || + !nf_bridge_port_valid(entry->state.out)) + return false; + + return true; +} + static void nfqnl_reinject(struct nf_queue_entry *entry, unsigned int verdict) { const struct nf_ct_hook *ct_hook; + if (!nf_bridge_ports_valid(entry)) + verdict = NF_DROP; + if (verdict == NF_ACCEPT || verdict == NF_REPEAT || verdict == NF_STOP) { @@ -636,6 +673,23 @@ static int nf_queue_checksum_help(struct sk_buff *entskb) return skb_checksum_help(entskb); } +#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) +static int nfqnl_put_master_ifindex(struct sk_buff *nlskb, int attr, + const struct net_device *dev) +{ + const struct net_device *upper; + + if (dev && !netif_is_bridge_port(dev)) + return 0; + + upper = netdev_master_upper_dev_get_rcu((struct net_device *)dev); + if (upper && nla_put_be32(nlskb, attr, htonl(upper->ifindex))) + return -EMSGSIZE; + + return 0; +} +#endif + static struct sk_buff * nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue, struct nf_queue_entry *entry, @@ -771,10 +825,7 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue, * netfilter_bridge) */ if (nla_put_be32(skb, NFQA_IFINDEX_PHYSINDEV, htonl(indev->ifindex)) || - /* this is the bridge group "brX" */ - /* rcu_read_lock()ed by __nf_queue */ - nla_put_be32(skb, NFQA_IFINDEX_INDEV, - htonl(br_port_get_rcu(indev)->br->dev->ifindex))) + nfqnl_put_master_ifindex(skb, NFQA_IFINDEX_INDEV, indev)) goto nla_put_failure; } else { int physinif; @@ -805,10 +856,7 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue, * netfilter_bridge) */ if (nla_put_be32(skb, NFQA_IFINDEX_PHYSOUTDEV, htonl(outdev->ifindex)) || - /* this is the bridge group "brX" */ - /* rcu_read_lock()ed by __nf_queue */ - nla_put_be32(skb, NFQA_IFINDEX_OUTDEV, - htonl(br_port_get_rcu(outdev)->br->dev->ifindex))) + nfqnl_put_master_ifindex(skb, NFQA_IFINDEX_OUTDEV, outdev)) goto nla_put_failure; } else { int physoutif; From efc542561729a2859397dad51bda1fe41262beb1 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Fri, 5 Jun 2026 13:47:12 +0200 Subject: [PATCH 242/257] netfilter: nf_tables_offload: drop device refcount on error Reported by sashiko: If nft_flow_action_entry_next() returns NULL, dev reference leaks. Fixes: c6f85577584b ("netfilter: nf_tables_offload: add nft_flow_action_entry_next() and use it") Reported-by: Juri Lelli Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_dup_netdev.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/netfilter/nf_dup_netdev.c b/net/netfilter/nf_dup_netdev.c index 3b0a70e154cd..3d88ef927f31 100644 --- a/net/netfilter/nf_dup_netdev.c +++ b/net/netfilter/nf_dup_netdev.c @@ -74,16 +74,18 @@ int nft_fwd_dup_netdev_offload(struct nft_offload_ctx *ctx, struct flow_action_entry *entry; struct net_device *dev; - /* nft_flow_rule_destroy() releases the reference on this device. */ dev = dev_get_by_index(ctx->net, oif); if (!dev) return -EOPNOTSUPP; entry = nft_flow_action_entry_next(ctx, flow); - if (!entry) + if (!entry) { + dev_put(dev); return -E2BIG; + } entry->id = id; + /* nft_flow_rule_destroy() releases the reference on this device. */ entry->dev = dev; return 0; From c3009418f9fa1dcb3eb86f4d8c92583537b5faa3 Mon Sep 17 00:00:00 2001 From: Weiming Shi Date: Wed, 3 Jun 2026 00:38:17 -0700 Subject: [PATCH 243/257] netfilter: nf_conntrack: destroy stale expectfn expectations on unregister NAT helpers such as nf_nat_h323 store a raw pointer to module text in exp->expectfn (e.g. ip_nat_q931_expect). nf_ct_helper_expectfn_unregister() only unlinks the callback descriptor and never walks the expectation table, so an expectation pending at module removal survives with a dangling exp->expectfn into freed module text. When the expected connection arrives, init_conntrack() invokes exp->expectfn(), now a stale pointer into the unloaded module. Reproduced on a KASAN build by loading the H.323 helpers, creating a Q.931 expectation, unloading nf_nat_h323, then connecting to the expected port: Oops: int3: 0000 [#1] SMP KASAN NOPTI RIP: 0010:0xffffffffa06102d1 init_conntrack.isra.0 (net/netfilter/nf_conntrack_core.c:1862) nf_conntrack_in (net/netfilter/nf_conntrack_core.c:2049) ipv4_conntrack_local (net/netfilter/nf_conntrack_proto.c:223) nf_hook_slow (net/netfilter/core.c:619) __ip_local_out (net/ipv4/ip_output.c:120) __tcp_transmit_skb (net/ipv4/tcp_output.c:1715) tcp_connect (net/ipv4/tcp_output.c:4374) tcp_v4_connect (net/ipv4/tcp_ipv4.c:345) __sys_connect (net/socket.c:2167) Modules linked in: nf_conntrack_h323 [last unloaded: nf_nat_h323] Reaching the dangling state requires CAP_SYS_MODULE in the initial user namespace to remove a NAT helper that still has live expectations, so this is a robustness fix; leaving an expectation pointing at freed text is wrong regardless. Add nf_ct_helper_expectfn_destroy(), which walks the expectation table and drops every expectation whose ->expectfn matches the descriptor being torn down. Call it from each NAT helper's exit path after the existing RCU grace period, so no expectation outlives the code it points at and no extra synchronize_rcu() is introduced. With the fix, the same reproducer runs to completion without the Oops. Fixes: f587de0e2feb ("[NETFILTER]: nf_conntrack/nf_nat: add H.323 helper port") Reported-by: Xiang Mei Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Weiming Shi Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_helper.h | 1 + net/ipv4/netfilter/nf_nat_h323.c | 2 ++ net/netfilter/nf_conntrack_helper.c | 19 +++++++++++++++++++ net/netfilter/nf_nat_core.c | 2 ++ net/netfilter/nf_nat_sip.c | 1 + 5 files changed, 25 insertions(+) diff --git a/include/net/netfilter/nf_conntrack_helper.h b/include/net/netfilter/nf_conntrack_helper.h index de2f956abf34..24cf3d2d9745 100644 --- a/include/net/netfilter/nf_conntrack_helper.h +++ b/include/net/netfilter/nf_conntrack_helper.h @@ -155,6 +155,7 @@ void nf_ct_helper_log(struct sk_buff *skb, const struct nf_conn *ct, void nf_ct_helper_expectfn_register(struct nf_ct_helper_expectfn *n); void nf_ct_helper_expectfn_unregister(struct nf_ct_helper_expectfn *n); +void nf_ct_helper_expectfn_destroy(const struct nf_ct_helper_expectfn *n); struct nf_ct_helper_expectfn * nf_ct_helper_expectfn_find_by_name(const char *name); struct nf_ct_helper_expectfn * diff --git a/net/ipv4/netfilter/nf_nat_h323.c b/net/ipv4/netfilter/nf_nat_h323.c index faee20af4856..10e1b0837731 100644 --- a/net/ipv4/netfilter/nf_nat_h323.c +++ b/net/ipv4/netfilter/nf_nat_h323.c @@ -555,6 +555,8 @@ static void __exit nf_nat_h323_fini(void) nf_ct_helper_expectfn_unregister(&q931_nat); nf_ct_helper_expectfn_unregister(&callforwarding_nat); synchronize_rcu(); + nf_ct_helper_expectfn_destroy(&q931_nat); + nf_ct_helper_expectfn_destroy(&callforwarding_nat); } /****************************************************************************/ diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c index 17e971bd4c74..2c5a71735561 100644 --- a/net/netfilter/nf_conntrack_helper.c +++ b/net/netfilter/nf_conntrack_helper.c @@ -283,6 +283,25 @@ void nf_ct_helper_expectfn_unregister(struct nf_ct_helper_expectfn *n) } EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_unregister); +static bool expect_iter_expectfn(struct nf_conntrack_expect *exp, void *data) +{ + const struct nf_ct_helper_expectfn *n = data; + + /* Relies on registered expectfn descriptors having unique ->expectfn + * pointers, which holds for the in-tree NAT helpers. + */ + return exp->expectfn == n->expectfn; +} + +/* Destroy expectations still pointing at @n->expectfn; call after the + * caller's RCU grace period so none outlives the (often modular) callback. + */ +void nf_ct_helper_expectfn_destroy(const struct nf_ct_helper_expectfn *n) +{ + nf_ct_expect_iterate_destroy(expect_iter_expectfn, (void *)n); +} +EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_destroy); + /* Caller should hold the rcu lock */ struct nf_ct_helper_expectfn * nf_ct_helper_expectfn_find_by_name(const char *name) diff --git a/net/netfilter/nf_nat_core.c b/net/netfilter/nf_nat_core.c index 74ec224ce0d6..2bbf5163c0e2 100644 --- a/net/netfilter/nf_nat_core.c +++ b/net/netfilter/nf_nat_core.c @@ -1341,6 +1341,7 @@ static int __init nf_nat_init(void) RCU_INIT_POINTER(nf_nat_hook, NULL); nf_ct_helper_expectfn_unregister(&follow_master_nat); synchronize_net(); + nf_ct_helper_expectfn_destroy(&follow_master_nat); unregister_pernet_subsys(&nat_net_ops); kvfree(nf_nat_bysource); } @@ -1358,6 +1359,7 @@ static void __exit nf_nat_cleanup(void) RCU_INIT_POINTER(nf_nat_hook, NULL); synchronize_net(); + nf_ct_helper_expectfn_destroy(&follow_master_nat); kvfree(nf_nat_bysource); unregister_pernet_subsys(&nat_net_ops); } diff --git a/net/netfilter/nf_nat_sip.c b/net/netfilter/nf_nat_sip.c index 9fbfc6bff0c2..00838c0cc5bb 100644 --- a/net/netfilter/nf_nat_sip.c +++ b/net/netfilter/nf_nat_sip.c @@ -655,6 +655,7 @@ static void __exit nf_nat_sip_fini(void) RCU_INIT_POINTER(nf_nat_sip_hooks, NULL); nf_ct_helper_expectfn_unregister(&sip_nat); synchronize_rcu(); + nf_ct_helper_expectfn_destroy(&sip_nat); } static const struct nf_nat_sip_hooks sip_hooks = { From f7f2fbb0e893a0238dc464f8d8c0f5609bec584f Mon Sep 17 00:00:00 2001 From: Kyle Zeng Date: Sat, 6 Jun 2026 01:10:31 -0700 Subject: [PATCH 244/257] netfilter: x_tables: avoid leaking percpu counter pointers The native and compat get-entries paths copy the fixed rule entry header from the kernelized rule blob to userspace before overwriting the entry's counter fields with a sanitized counter snapshot. On SMP kernels, entry->counters.pcnt contains the percpu allocation address used by x_tables rule counters. A caller can provide a userspace buffer that faults during the initial fixed-header copy after pcnt has been copied but before the later sanitized counter copy runs. The syscall then returns -EFAULT while leaving the raw percpu pointer in userspace. Copy only the fixed entry prefix before counters from the kernelized rule blob, then copy the sanitized counter snapshot into the counter field. Apply this ordering to the IPv4, IPv6, and ARP native and compat get-entries implementations so a fault cannot expose the internal percpu counter pointer. Fixes: 71ae0dff02d7 ("netfilter: xtables: use percpu rule counters") Signed-off-by: Kyle Zeng Signed-off-by: Pablo Neira Ayuso --- net/ipv4/netfilter/arp_tables.c | 15 ++++++--------- net/ipv4/netfilter/ip_tables.c | 15 ++++++--------- net/ipv6/netfilter/ip6_tables.c | 15 ++++++--------- 3 files changed, 18 insertions(+), 27 deletions(-) diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c index ad2259678c78..0ea513bf77fb 100644 --- a/net/ipv4/netfilter/arp_tables.c +++ b/net/ipv4/netfilter/arp_tables.c @@ -702,14 +702,12 @@ static int copy_entries_to_user(unsigned int total_size, const struct xt_entry_target *t; e = loc_cpu_entry + off; - if (copy_to_user(userptr + off, e, sizeof(*e))) { - ret = -EFAULT; - goto free_counters; - } - if (copy_to_user(userptr + off + if (copy_to_user(userptr + off, e, + offsetof(struct arpt_entry, counters)) || + copy_to_user(userptr + off + offsetof(struct arpt_entry, counters), &counters[num], - sizeof(counters[num])) != 0) { + sizeof(counters[num]))) { ret = -EFAULT; goto free_counters; } @@ -1327,9 +1325,8 @@ static int compat_copy_entry_to_user(struct arpt_entry *e, void __user **dstptr, origsize = *size; ce = *dstptr; - if (copy_to_user(ce, e, sizeof(struct arpt_entry)) != 0 || - copy_to_user(&ce->counters, &counters[i], - sizeof(counters[i])) != 0) + if (copy_to_user(ce, e, offsetof(struct compat_arpt_entry, counters)) || + copy_to_user(&ce->counters, &counters[i], sizeof(counters[i]))) return -EFAULT; *dstptr += sizeof(struct compat_arpt_entry); diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index 5cbdb0815857..ca8ff0ae6cdb 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -832,14 +832,12 @@ copy_entries_to_user(unsigned int total_size, const struct xt_entry_target *t; e = loc_cpu_entry + off; - if (copy_to_user(userptr + off, e, sizeof(*e))) { - ret = -EFAULT; - goto free_counters; - } - if (copy_to_user(userptr + off + if (copy_to_user(userptr + off, e, + offsetof(struct ipt_entry, counters)) || + copy_to_user(userptr + off + offsetof(struct ipt_entry, counters), &counters[num], - sizeof(counters[num])) != 0) { + sizeof(counters[num]))) { ret = -EFAULT; goto free_counters; } @@ -1228,9 +1226,8 @@ compat_copy_entry_to_user(struct ipt_entry *e, void __user **dstptr, origsize = *size; ce = *dstptr; - if (copy_to_user(ce, e, sizeof(struct ipt_entry)) != 0 || - copy_to_user(&ce->counters, &counters[i], - sizeof(counters[i])) != 0) + if (copy_to_user(ce, e, offsetof(struct compat_ipt_entry, counters)) || + copy_to_user(&ce->counters, &counters[i], sizeof(counters[i]))) return -EFAULT; *dstptr += sizeof(struct compat_ipt_entry); diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index 9d9c3763f2f5..e34d5ba1460c 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -848,14 +848,12 @@ copy_entries_to_user(unsigned int total_size, const struct xt_entry_target *t; e = loc_cpu_entry + off; - if (copy_to_user(userptr + off, e, sizeof(*e))) { - ret = -EFAULT; - goto free_counters; - } - if (copy_to_user(userptr + off + if (copy_to_user(userptr + off, e, + offsetof(struct ip6t_entry, counters)) || + copy_to_user(userptr + off + offsetof(struct ip6t_entry, counters), &counters[num], - sizeof(counters[num])) != 0) { + sizeof(counters[num]))) { ret = -EFAULT; goto free_counters; } @@ -1244,9 +1242,8 @@ compat_copy_entry_to_user(struct ip6t_entry *e, void __user **dstptr, origsize = *size; ce = *dstptr; - if (copy_to_user(ce, e, sizeof(struct ip6t_entry)) != 0 || - copy_to_user(&ce->counters, &counters[i], - sizeof(counters[i])) != 0) + if (copy_to_user(ce, e, offsetof(struct compat_ip6t_entry, counters)) || + copy_to_user(&ce->counters, &counters[i], sizeof(counters[i]))) return -EFAULT; *dstptr += sizeof(struct compat_ip6t_entry); From a84b6fedbc97078788be78dbdd7517d143ad1a77 Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Tue, 9 Jun 2026 15:55:02 -0700 Subject: [PATCH 245/257] netfilter: nf_log: validate MAC header was set before dumping it The fallback path of dump_mac_header() guards the MAC header access only with "skb->mac_header != skb->network_header", without checking skb_mac_header_was_set(). When the MAC header is unset, mac_header is 0xffff, so the test passes and skb_mac_header(skb) returns skb->head + 0xffff, ~64 KiB past the buffer; the loop then reads dev->hard_header_len bytes out of bounds into the kernel log. This is reachable via the netdev logger: nf_log_unknown_packet() calls dump_mac_header() unconditionally, and an skb sent through AF_PACKET with PACKET_QDISC_BYPASS reaches the egress hook with mac_header still unset (__dev_queue_xmit(), which would reset it, is bypassed). Add the skb_mac_header_was_set() check the ARPHRD_ETHER path already uses, and replace the open-coded MAC header length test with skb_mac_header_len(). Only skbs with an unset MAC header are affected; valid ones are dumped as before. BUG: KASAN: slab-out-of-bounds in dump_mac_header (net/netfilter/nf_log_syslog.c:831) Read of size 1 at addr ffff88800ea49d3f by task exploit/148 Call Trace: kasan_report (mm/kasan/report.c:595) dump_mac_header (net/netfilter/nf_log_syslog.c:831) nf_log_netdev_packet (net/netfilter/nf_log_syslog.c:938 net/netfilter/nf_log_syslog.c:963) nf_log_packet (net/netfilter/nf_log.c:260) nft_log_eval (net/netfilter/nft_log.c:60) nft_do_chain (net/netfilter/nf_tables_core.c:285) nft_do_chain_netdev (net/netfilter/nft_chain_filter.c:307) nf_hook_slow (net/netfilter/core.c:619) nf_hook_direct_egress (net/packet/af_packet.c:257) packet_xmit (net/packet/af_packet.c:280) packet_sendmsg (net/packet/af_packet.c:3114) __sys_sendto (net/socket.c:2265) Fixes: 7eb9282cd0ef ("netfilter: ipt_LOG/ip6t_LOG: add option to print decoded MAC header") Reported-by: Weiming Shi Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Xiang Mei Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_log_syslog.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/netfilter/nf_log_syslog.c b/net/netfilter/nf_log_syslog.c index 7a8952b049d1..e37b09b3203b 100644 --- a/net/netfilter/nf_log_syslog.c +++ b/net/netfilter/nf_log_syslog.c @@ -815,8 +815,8 @@ static void dump_mac_header(struct nf_log_buf *m, fallback: nf_log_buf_add(m, "MAC="); - if (dev->hard_header_len && - skb->mac_header != skb->network_header) { + if (dev->hard_header_len && skb_mac_header_was_set(skb) && + skb_mac_header_len(skb) != 0) { const unsigned char *p = skb_mac_header(skb); unsigned int i; From 772cecf198da732faebb5dcfc46d66a505be8495 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 9 Jun 2026 21:28:09 +0200 Subject: [PATCH 246/257] netfilter: nft_exthdr: fix register tracking for F_PRESENT flag nft_exthdr_init() passes user-controlled priv->len to nft_parse_register_store(), which marks that many bytes in the register bitmap as initialized. However, when NFT_EXTHDR_F_PRESENT is set, the eval paths write only 1 byte (nft_reg_store8) or 4 bytes (*dest = 0 on TCP/DCCP error path). When len > 4, registers beyond the first are never written, retaining uninitialized stack data from nft_regs. Bail out if userspace requests too much data when F_PRESENT is set. Reported-by: Ji'an Zhou Fixes: c078ca3b0c5b ("netfilter: nft_exthdr: Add support for existence check") Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nft_exthdr.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/netfilter/nft_exthdr.c b/net/netfilter/nft_exthdr.c index e6a07c0df207..d3fc7969f123 100644 --- a/net/netfilter/nft_exthdr.c +++ b/net/netfilter/nft_exthdr.c @@ -532,6 +532,9 @@ static int nft_exthdr_init(const struct nft_ctx *ctx, return err; } + if ((flags & NFT_EXTHDR_F_PRESENT) && len != 1) + return -EINVAL; + priv->type = nla_get_u8(tb[NFTA_EXTHDR_TYPE]); priv->offset = offset; priv->len = len; From ab185e0c4fb82dfba6fb86f8271e06f931d9c64c Mon Sep 17 00:00:00 2001 From: Davide Ornaghi Date: Wed, 10 Jun 2026 12:39:12 +0200 Subject: [PATCH 247/257] netfilter: nft_fib: fix stale stack leak via the OIFNAME register For NFT_FIB_RESULT_OIFNAME the destination register is declared with len = IFNAMSIZ (four 32-bit registers), but on the lookup-fail, RTN_LOCAL and oif-mismatch paths nft_fib{4,6}_eval() only writes one register via "*dest = 0". The remaining three registers are left as whatever was on the stack in nft_do_chain()'s struct nft_regs, and a downstream expression that loads the register span can leak that uninitialised kernel stack to userspace. The NFTA_FIB_F_PRESENT existence check has the same shape: it is only meaningful for NFT_FIB_RESULT_OIF, yet it was accepted for any result type while the eval stores a single byte via nft_reg_store8(), leaving the rest of the declared span stale. Fix both: - replace the bare "*dest = 0" in the eval with nft_fib_store_result(), which strscpy_pad()s the whole IFNAMSIZ for OIFNAME (and is already used on the other early-return path), and - restrict NFTA_FIB_F_PRESENT to NFT_FIB_RESULT_OIF and declare its destination as a single u8, so the marked span matches the one byte the eval writes. Fixes: f6d0cbcf09c5 ("netfilter: nf_tables: add fib expression") Suggested-by: Florian Westphal Cc: stable@vger.kernel.org Signed-off-by: Davide Ornaghi Signed-off-by: Pablo Neira Ayuso --- net/ipv4/netfilter/nft_fib_ipv4.c | 2 +- net/ipv6/netfilter/nft_fib_ipv6.c | 2 +- net/netfilter/nft_fib.c | 6 ++++++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/net/ipv4/netfilter/nft_fib_ipv4.c b/net/ipv4/netfilter/nft_fib_ipv4.c index 9d0c6d75109b..177d738825b4 100644 --- a/net/ipv4/netfilter/nft_fib_ipv4.c +++ b/net/ipv4/netfilter/nft_fib_ipv4.c @@ -128,7 +128,7 @@ void nft_fib4_eval(const struct nft_expr *expr, struct nft_regs *regs, fl4.saddr = get_saddr(iph->daddr); } - *dest = 0; + nft_fib_store_result(dest, priv, NULL); if (fib_lookup(nft_net(pkt), &fl4, &res, FIB_LOOKUP_IGNORE_LINKSTATE)) return; diff --git a/net/ipv6/netfilter/nft_fib_ipv6.c b/net/ipv6/netfilter/nft_fib_ipv6.c index 2dbe44715df3..b9ad7cac1417 100644 --- a/net/ipv6/netfilter/nft_fib_ipv6.c +++ b/net/ipv6/netfilter/nft_fib_ipv6.c @@ -239,7 +239,7 @@ void nft_fib6_eval(const struct nft_expr *expr, struct nft_regs *regs, lookup_flags = nft_fib6_flowi_init(&fl6, priv, pkt, oif, iph); - *dest = 0; + nft_fib_store_result(dest, priv, NULL); ret = nft_fib6_lookup(nft_net(pkt), &fl6, &res, lookup_flags); if (ret || res.fib6_flags & (RTF_REJECT | RTF_ANYCAST | RTF_LOCAL)) return; diff --git a/net/netfilter/nft_fib.c b/net/netfilter/nft_fib.c index 327a5f33659c..a1632e308f18 100644 --- a/net/netfilter/nft_fib.c +++ b/net/netfilter/nft_fib.c @@ -107,6 +107,12 @@ int nft_fib_init(const struct nft_ctx *ctx, const struct nft_expr *expr, return -EINVAL; } + if (priv->flags & NFTA_FIB_F_PRESENT) { + if (priv->result != NFT_FIB_RESULT_OIF) + return -EINVAL; + len = sizeof(u8); + } + err = nft_parse_register_store(ctx, tb[NFTA_FIB_DREG], &priv->dreg, NULL, NFT_DATA_VALUE, len); if (err < 0) From c7d573551f9286100a055ef696cde6af54549677 Mon Sep 17 00:00:00 2001 From: Davide Ornaghi Date: Wed, 10 Jun 2026 12:39:13 +0200 Subject: [PATCH 248/257] netfilter: nft_meta_bridge: fix stale stack leak via IIFHWADDR register NFT_META_BRI_IIFHWADDR declares its destination register with len = ETH_ALEN (6 bytes), which the register-init tracking rounds up to two 32-bit registers (8 bytes). nft_meta_bridge_get_eval() then does memcpy(dest, br_dev->dev_addr, ETH_ALEN), writing only 6 bytes and leaving the upper 2 bytes of the second register as uninitialised nft_do_chain() stack. A downstream load of that register span leaks those stale bytes to userspace. Zero the second register before the memcpy so the full declared span is written. Fixes: cbd2257dc96e ("netfilter: nft_meta_bridge: introduce NFT_META_BRI_IIFHWADDR support") Cc: stable@vger.kernel.org Signed-off-by: Davide Ornaghi Signed-off-by: Pablo Neira Ayuso --- net/bridge/netfilter/nft_meta_bridge.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/bridge/netfilter/nft_meta_bridge.c b/net/bridge/netfilter/nft_meta_bridge.c index 7763e78abb00..219c40680260 100644 --- a/net/bridge/netfilter/nft_meta_bridge.c +++ b/net/bridge/netfilter/nft_meta_bridge.c @@ -64,6 +64,8 @@ static void nft_meta_bridge_get_eval(const struct nft_expr *expr, if (!br_dev) goto err; + /* ETH_ALEN (6) is shorter than the destination register span (8) */ + dest[1] = 0; memcpy(dest, br_dev->dev_addr, ETH_ALEN); return; default: From 180235600934bef6add3be637c296d6cf3272e67 Mon Sep 17 00:00:00 2001 From: Til Kaiser Date: Sun, 7 Jun 2026 15:49:40 +0200 Subject: [PATCH 249/257] net: mvpp2: sync RX data at the hardware packet offset mvpp2 programs the RX queue packet offset, so hardware writes received data at dma_addr + MVPP2_SKB_HEADROOM. The current CPU sync starts at dma_addr and only covers rx_bytes + MVPP2_MH_SIZE bytes, which syncs the unused headroom and misses the same number of bytes at the packet tail. On non-coherent DMA systems this can leave the CPU reading stale cache contents for the end of the received frame. Use dma_sync_single_range_for_cpu() with MVPP2_SKB_HEADROOM as the range offset so the sync covers the Marvell header and packet data actually written by hardware. Fixes: e1921168bbd4 ("mvpp2: sync only the received frame") Signed-off-by: Til Kaiser Link: https://patch.msgid.link/20260607134943.21996-2-mail@tk154.de Signed-off-by: Paolo Abeni --- drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c index f442b874bb59..92a701f4fe3f 100644 --- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c @@ -3946,9 +3946,10 @@ static int mvpp2_rx(struct mvpp2_port *port, struct napi_struct *napi, dma_dir = DMA_FROM_DEVICE; } - dma_sync_single_for_cpu(dev->dev.parent, dma_addr, - rx_bytes + MVPP2_MH_SIZE, - dma_dir); + dma_sync_single_range_for_cpu(dev->dev.parent, dma_addr, + MVPP2_SKB_HEADROOM, + rx_bytes + MVPP2_MH_SIZE, + dma_dir); /* Buffer header not supported */ if (rx_status & MVPP2_RXD_BUF_HDR) From f3c6aa078927e6fe8121c9c591ddee8716c5305a Mon Sep 17 00:00:00 2001 From: Til Kaiser Date: Sun, 7 Jun 2026 15:49:41 +0200 Subject: [PATCH 250/257] net: mvpp2: limit XDP frame size to the RX buffer mvpp2 has short and long BM pools, and short pool buffers can be smaller than PAGE_SIZE. The XDP path nevertheless initializes every xdp_buff with PAGE_SIZE as frame size. XDP helpers use frame_sz to validate tail growth and to derive the hard end of the data area. Advertising PAGE_SIZE for short buffers can let bpf_xdp_adjust_tail() grow a packet past the real allocation, corrupting memory or later tripping skb tailroom checks. Initialize the XDP buffer with bm_pool->frag_size so XDP tailroom matches the actual buffer backing the packet. Fixes: 07dd0a7aae7f ("mvpp2: add basic XDP support") Signed-off-by: Til Kaiser Link: https://patch.msgid.link/20260607134943.21996-3-mail@tk154.de Signed-off-by: Paolo Abeni --- drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c index 92a701f4fe3f..3372ed27cc8d 100644 --- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c @@ -3979,7 +3979,7 @@ static int mvpp2_rx(struct mvpp2_port *port, struct napi_struct *napi, else xdp_rxq = &rxq->xdp_rxq_long; - xdp_init_buff(&xdp, PAGE_SIZE, xdp_rxq); + xdp_init_buff(&xdp, bm_pool->frag_size, xdp_rxq); xdp_prepare_buff(&xdp, data, MVPP2_MH_SIZE + MVPP2_SKB_HEADROOM, rx_bytes, true); From 5e8e2a9624df72fca7c736b2966b2cbf6c9c3ff6 Mon Sep 17 00:00:00 2001 From: Til Kaiser Date: Sun, 7 Jun 2026 15:49:42 +0200 Subject: [PATCH 251/257] net: mvpp2: refill RX buffers before XDP or skb use The RX error path returns the current descriptor buffer to the hardware BM pool. That is only valid while the driver still owns the buffer. mvpp2_rx_refill() can fail after the current buffer has been handed to XDP or attached to an skb. In those cases mvpp2_run_xdp() may have recycled, redirected, or queued the page for XDP_TX, and an skb free also retires the data buffer. Returning such a buffer to BM lets hardware DMA into memory that is no longer owned by the RX ring. Refill the BM pool before handing the current buffer to XDP or to the skb. If the allocation fails there, drop the packet and return the still-owned current buffer to BM, preserving the pool depth. Once the refill succeeds, later local drops retire/free the current buffer instead of returning it to BM. Fixes: 07dd0a7aae7f ("mvpp2: add basic XDP support") Fixes: d6526926de73 ("net: mvpp2: fix memory leak in mvpp2_rx") Signed-off-by: Til Kaiser Link: https://patch.msgid.link/20260607134943.21996-4-mail@tk154.de Signed-off-by: Paolo Abeni --- .../net/ethernet/marvell/mvpp2/mvpp2_main.c | 43 +++++++++++-------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c index 3372ed27cc8d..481daafdc1cb 100644 --- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c @@ -3971,6 +3971,12 @@ static int mvpp2_rx(struct mvpp2_port *port, struct napi_struct *napi, else frag_size = bm_pool->frag_size; + err = mvpp2_rx_refill(port, bm_pool, pp, pool); + if (err) { + netdev_err(port->dev, "failed to refill BM pools\n"); + goto err_drop_frame; + } + if (xdp_prog) { struct xdp_rxq_info *xdp_rxq; @@ -3988,12 +3994,6 @@ static int mvpp2_rx(struct mvpp2_port *port, struct napi_struct *napi, if (ret) { xdp_ret |= ret; - err = mvpp2_rx_refill(port, bm_pool, pp, pool); - if (err) { - netdev_err(port->dev, "failed to refill BM pools\n"); - goto err_drop_frame; - } - ps.rx_packets++; ps.rx_bytes += rx_bytes; continue; @@ -4008,8 +4008,21 @@ static int mvpp2_rx(struct mvpp2_port *port, struct napi_struct *napi, skb = slab_build_skb(data); if (!skb) { netdev_warn(port->dev, "skb build failed\n"); - goto err_drop_frame; + if (pp) { + page_pool_put_page(pp, virt_to_head_page(data), + rx_bytes + MVPP2_MH_SIZE, + true); + } else { + dma_unmap_single_attrs(dev->dev.parent, dma_addr, + bm_pool->buf_size, + DMA_FROM_DEVICE, + DMA_ATTR_SKIP_CPU_SYNC); + mvpp2_frag_free(bm_pool, pp, data); + } + goto err_drop_frame_retired; } + if (pp) + skb_mark_for_recycle(skb); /* If we have RX hardware timestamping enabled, grab the * timestamp from the queue and convert. @@ -4020,16 +4033,7 @@ static int mvpp2_rx(struct mvpp2_port *port, struct napi_struct *napi, skb_hwtstamps(skb)); } - err = mvpp2_rx_refill(port, bm_pool, pp, pool); - if (err) { - netdev_err(port->dev, "failed to refill BM pools\n"); - dev_kfree_skb_any(skb); - goto err_drop_frame; - } - - if (pp) - skb_mark_for_recycle(skb); - else + if (!pp) dma_unmap_single_attrs(dev->dev.parent, dma_addr, bm_pool->buf_size, DMA_FROM_DEVICE, DMA_ATTR_SKIP_CPU_SYNC); @@ -4048,13 +4052,14 @@ static int mvpp2_rx(struct mvpp2_port *port, struct napi_struct *napi, continue; err_drop_frame: - dev->stats.rx_errors++; - mvpp2_rx_error(port, rx_desc); /* Return the buffer to the pool */ if (rx_status & MVPP2_RXD_BUF_HDR) mvpp2_buff_hdr_pool_put(port, rx_desc, pool, rx_status); else mvpp2_bm_pool_put(port, pool, dma_addr, phys_addr); +err_drop_frame_retired: + dev->stats.rx_errors++; + mvpp2_rx_error(port, rx_desc); } if (xdp_ret & MVPP2_XDP_REDIR) From 77a6b90ce56bc982dcfa94229b8e28e6abb16e95 Mon Sep 17 00:00:00 2001 From: Til Kaiser Date: Sun, 7 Jun 2026 15:49:43 +0200 Subject: [PATCH 252/257] net: mvpp2: build skb from XDP-adjusted data on XDP_PASS When an XDP program uses bpf_xdp_adjust_head() or bpf_xdp_adjust_tail() and then returns XDP_PASS, mvpp2 still builds the skb from fixed offsets derived from the original RX descriptor. Packet geometry changes made by the XDP program are therefore discarded before the skb reaches the stack. Update rx_offset and rx_bytes from xdp.data and xdp.data_end for XDP_PASS. This makes skb_reserve() and skb_put() reflect the packet seen by XDP, and makes RX byte accounting for XDP_PASS follow the length of the skb passed to the network stack. Keep a separate rx_sync_size for page-pool recycling on skb allocation failure, which must stay tied to the received buffer range. Non-PASS verdicts continue to account the descriptor length because no skb is passed up in those cases. Fixes: 07dd0a7aae7f ("mvpp2: add basic XDP support") Signed-off-by: Til Kaiser Link: https://patch.msgid.link/20260607134943.21996-5-mail@tk154.de Signed-off-by: Paolo Abeni --- .../net/ethernet/marvell/mvpp2/mvpp2_main.c | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c index 481daafdc1cb..ccc24a1301f2 100644 --- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c @@ -3917,10 +3917,10 @@ static int mvpp2_rx(struct mvpp2_port *port, struct napi_struct *napi, struct mvpp2_bm_pool *bm_pool; struct page_pool *pp = NULL; struct sk_buff *skb; - unsigned int frag_size; + unsigned int frag_size, rx_sync_size; dma_addr_t dma_addr; phys_addr_t phys_addr; - int pool, rx_bytes, err, ret; + int pool, rx_bytes, rx_offset, err, ret; struct page *page; void *data; @@ -3933,6 +3933,8 @@ static int mvpp2_rx(struct mvpp2_port *port, struct napi_struct *napi, rx_status = mvpp2_rxdesc_status_get(port, rx_desc); rx_bytes = mvpp2_rxdesc_size_get(port, rx_desc); rx_bytes -= MVPP2_MH_SIZE; + rx_sync_size = rx_bytes + MVPP2_MH_SIZE; + rx_offset = MVPP2_MH_SIZE + MVPP2_SKB_HEADROOM; dma_addr = mvpp2_rxdesc_dma_addr_get(port, rx_desc); pool = (rx_status & MVPP2_RXD_BM_POOL_ID_MASK) >> @@ -3948,7 +3950,7 @@ static int mvpp2_rx(struct mvpp2_port *port, struct napi_struct *napi, dma_sync_single_range_for_cpu(dev->dev.parent, dma_addr, MVPP2_SKB_HEADROOM, - rx_bytes + MVPP2_MH_SIZE, + rx_sync_size, dma_dir); /* Buffer header not supported */ @@ -3999,6 +4001,14 @@ static int mvpp2_rx(struct mvpp2_port *port, struct napi_struct *napi, continue; } + rx_sync_size = max_t(unsigned int, rx_sync_size, + xdp.data_end - xdp.data_hard_start - + MVPP2_SKB_HEADROOM); + + /* Update offset and length to reflect any XDP adjustments. */ + rx_offset = xdp.data - data; + rx_bytes = xdp.data_end - xdp.data; + metasize = xdp.data - xdp.data_meta; } @@ -4010,8 +4020,7 @@ static int mvpp2_rx(struct mvpp2_port *port, struct napi_struct *napi, netdev_warn(port->dev, "skb build failed\n"); if (pp) { page_pool_put_page(pp, virt_to_head_page(data), - rx_bytes + MVPP2_MH_SIZE, - true); + rx_sync_size, true); } else { dma_unmap_single_attrs(dev->dev.parent, dma_addr, bm_pool->buf_size, @@ -4041,7 +4050,7 @@ static int mvpp2_rx(struct mvpp2_port *port, struct napi_struct *napi, ps.rx_packets++; ps.rx_bytes += rx_bytes; - skb_reserve(skb, MVPP2_MH_SIZE + MVPP2_SKB_HEADROOM); + skb_reserve(skb, rx_offset); skb_put(skb, rx_bytes); if (metasize) skb_metadata_set(skb, metasize); From 0487cfca46517ff6699c72dc1a8872b0af3c31a9 Mon Sep 17 00:00:00 2001 From: Jiawen Wu Date: Mon, 8 Jun 2026 15:08:40 +0800 Subject: [PATCH 253/257] net: txgbe: initialize module info buffer The module info buffer should be initialized to 0 before the firmware returns information. Otherwise, there is a risk that the buffer field not filled by the firmware is random value. Fixes: 343929799ace ("net: txgbe: Support to handle GPIO IRQs for AML devices") Signed-off-by: Jiawen Wu Link: https://patch.msgid.link/20260608070842.36504-2-jiawenwu@trustnetic.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c index f0514251d4f3..a7e81f9e1be1 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c @@ -335,7 +335,7 @@ static int txgbe_qsfp_to_linkmodes(struct wx *wx, struct txgbe_sff_id *id) int txgbe_identify_module(struct wx *wx) { - struct txgbe_hic_get_module_info buffer; + struct txgbe_hic_get_module_info buffer = { 0 }; struct txgbe_sff_id *id; int err = 0; u32 mod_abs; From f2df54ddbfb04a006ee326a5d8270434a414e0af Mon Sep 17 00:00:00 2001 From: Jiawen Wu Date: Mon, 8 Jun 2026 15:08:41 +0800 Subject: [PATCH 254/257] net: txgbe: distinguish module types by checking identifier Rework txgbe_identify_module() to validate module identifiers through explicit type checks instead of relying on transceiver_type heuristics. When using the SFP module, transceiver_type could be a random value, because it was read from an invalid register. Fixes: 57d39faed4c9 ("net: txgbe: improve functions of AML 40G devices") Signed-off-by: Jiawen Wu Link: https://patch.msgid.link/20260608070842.36504-3-jiawenwu@trustnetic.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c index a7e81f9e1be1..bdac654a2364 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c @@ -357,18 +357,16 @@ int txgbe_identify_module(struct wx *wx) } id = &buffer.id; - if (id->identifier != TXGBE_SFF_IDENTIFIER_SFP && - id->identifier != TXGBE_SFF_IDENTIFIER_QSFP && - id->identifier != TXGBE_SFF_IDENTIFIER_QSFP_PLUS && - id->identifier != TXGBE_SFF_IDENTIFIER_QSFP28) { - wx_err(wx, "Invalid module\n"); - return -ENODEV; - } - - if (id->transceiver_type == 0xFF) + if (id->identifier == TXGBE_SFF_IDENTIFIER_SFP) return txgbe_sfp_to_linkmodes(wx, id); - return txgbe_qsfp_to_linkmodes(wx, id); + if (id->identifier == TXGBE_SFF_IDENTIFIER_QSFP || + id->identifier == TXGBE_SFF_IDENTIFIER_QSFP_PLUS || + id->identifier == TXGBE_SFF_IDENTIFIER_QSFP28) + return txgbe_qsfp_to_linkmodes(wx, id); + + wx_err(wx, "Invalid module\n"); + return -EINVAL; } void txgbe_setup_link(struct wx *wx) From 47f848aac4e79bdb197f849fa86e71fff1ad36ef Mon Sep 17 00:00:00 2001 From: Jiawen Wu Date: Mon, 8 Jun 2026 15:08:42 +0800 Subject: [PATCH 255/257] net: txgbe: initialize PHY interface to 0 DECLARE_PHY_INTERFACE_MASK() does not guarantee zeroed contents. Add a new macro DECLARE_PHY_INTERFACE_MASK_ZERO(), make the stack variable to be zeroed before setting supported interfaces. Fixes: 57d39faed4c9 ("net: txgbe: improve functions of AML 40G devices") Signed-off-by: Jiawen Wu Link: https://patch.msgid.link/20260608070842.36504-4-jiawenwu@trustnetic.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c | 4 ++-- drivers/net/ethernet/wangxun/txgbe/txgbe_type.h | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c index bdac654a2364..8fc32df8e49a 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c @@ -204,7 +204,7 @@ int txgbe_set_phy_link(struct wx *wx) static int txgbe_sfp_to_linkmodes(struct wx *wx, struct txgbe_sff_id *id) { __ETHTOOL_DECLARE_LINK_MODE_MASK(modes) = { 0, }; - DECLARE_PHY_INTERFACE_MASK(interfaces); + DECLARE_PHY_INTERFACE_MASK_ZERO(interfaces); struct txgbe *txgbe = wx->priv; if (id->cable_tech & TXGBE_SFF_DA_PASSIVE_CABLE) { @@ -271,7 +271,7 @@ static int txgbe_sfp_to_linkmodes(struct wx *wx, struct txgbe_sff_id *id) static int txgbe_qsfp_to_linkmodes(struct wx *wx, struct txgbe_sff_id *id) { __ETHTOOL_DECLARE_LINK_MODE_MASK(modes) = { 0, }; - DECLARE_PHY_INTERFACE_MASK(interfaces); + DECLARE_PHY_INTERFACE_MASK_ZERO(interfaces); struct txgbe *txgbe = wx->priv; if (id->transceiver_type & TXGBE_SFF_ETHERNET_40G_CR4) { diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h index 6b05f32b4a01..877234e3fdc2 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h @@ -315,6 +315,9 @@ void txgbe_up(struct wx *wx); int txgbe_setup_tc(struct net_device *dev, u8 tc); void txgbe_do_reset(struct net_device *netdev); +#define DECLARE_PHY_INTERFACE_MASK_ZERO(name) \ + unsigned long name[PHY_INTERFACE_MODE_MAX] = { 0, } + #define TXGBE_LINK_SPEED_UNKNOWN 0 #define TXGBE_LINK_SPEED_10GB_FULL 4 #define TXGBE_LINK_SPEED_25GB_FULL 0x10 From b70c687b7cf267fb08586667a3946c8851cad672 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 9 Jun 2026 17:54:48 +0300 Subject: [PATCH 256/257] ipv6: Fix a potential NPD in cleanup_prefix_route() addrconf_get_prefix_route() can return the fib6_null_entry sentinel entry which has a NULL fib6_table pointer. Therefore, before setting the route's expiration time, check that we are not working with this entry, as otherwise a NPD will be triggered [1]. Note that the other callers of addrconf_get_prefix_route() are not susceptible to this bug: 1. addrconf_prefix_rcv(): Requests a route with the 'RTF_ADDRCONF | RTF_PREFIX_RT' flags which are not set on fib6_null_entry. 2. modify_prefix_route(): Fixed by commit a747e02430df ("ipv6: avoid possible NULL deref in modify_prefix_route()"). 3. __ipv6_ifa_notify(): Calls ip6_del_rt() which specifically checks for fib6_null_entry and returns an error. [1] Oops: general protection fault, probably for non-canonical address 0xdffffc0000000006: 0000 [#1] SMP KASAN KASAN: null-ptr-deref in range [0x0000000000000030-0x0000000000000037] [...] Call Trace: __kasan_check_byte (mm/kasan/common.c:573) lock_acquire.part.0 (kernel/locking/lockdep.c:5842 (discriminator 1)) _raw_spin_lock_bh (kernel/locking/spinlock.c:182 (discriminator 1)) cleanup_prefix_route (net/ipv6/addrconf.c:1280) ipv6_del_addr (net/ipv6/addrconf.c:1342) inet6_addr_del.isra.0 (net/ipv6/addrconf.c:3119) inet6_rtm_deladdr (net/ipv6/addrconf.c:4812) rtnetlink_rcv_msg (net/core/rtnetlink.c:6997) netlink_rcv_skb (net/netlink/af_netlink.c:2555) netlink_unicast (net/netlink/af_netlink.c:1344) netlink_sendmsg (net/netlink/af_netlink.c:1899) __sock_sendmsg (net/socket.c:802 (discriminator 4)) ____sys_sendmsg (net/socket.c:2698) ___sys_sendmsg (net/socket.c:2752) __sys_sendmsg (net/socket.c:2784) do_syscall_64 (arch/x86/entry/syscall_64.c:63 arch/x86/entry/syscall_64.c:94) entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121) Fixes: 5eb902b8e719 ("net/ipv6: Remove expired routes with a separated list of routes.") Reported-by: Ji'an Zhou Reviewed-by: David Ahern Signed-off-by: Ido Schimmel Link: https://patch.msgid.link/20260609145448.768318-1-idosch@nvidia.com Signed-off-by: Paolo Abeni --- net/ipv6/addrconf.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index bb84a78b80f6..c9e5d3e48ab9 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -1265,6 +1265,7 @@ static void cleanup_prefix_route(struct inet6_ifaddr *ifp, unsigned long expires, bool del_rt, bool del_peer) { + struct net *net = dev_net(ifp->idev->dev); struct fib6_table *table; struct fib6_info *f6i; @@ -1273,9 +1274,10 @@ cleanup_prefix_route(struct inet6_ifaddr *ifp, unsigned long expires, ifp->idev->dev, 0, RTF_DEFAULT, true); if (f6i) { if (del_rt) - ip6_del_rt(dev_net(ifp->idev->dev), f6i, false); + ip6_del_rt(net, f6i, false); else { - if (!(f6i->fib6_flags & RTF_EXPIRES)) { + if (f6i != net->ipv6.fib6_null_entry && + !(f6i->fib6_flags & RTF_EXPIRES)) { table = f6i->fib6_table; spin_lock_bh(&table->tb6_lock); From 7360b96099806396f4ce15233f6dddcb69248d34 Mon Sep 17 00:00:00 2001 From: Kiran Kumar K Date: Mon, 8 Jun 2026 15:24:55 +0530 Subject: [PATCH 257/257] octeontx2-af: fix IP fragment flag corruption on custom KPU profile load npc_cn20k_apply_custom_kpu() overwrites KPU profile entries with custom firmware values and then calls npc_cn20k_update_action_entries_n_flags() over all entries. Since the same function already ran during default profile initialisation, entries not overridden by the custom firmware get their flags translated twice, corrupting the CN20K-specific values. Fix this by extracting the per-entry translation into a helper npc_cn20k_translate_action_flags() and calling it as each custom entry is loaded, removing the redundant batch call at the end. Fixes: ef992a0f12e8 ("octeontx2-af: npc: cn20k: MKEX profile support") Cc: Suman Ghosh Signed-off-by: Kiran Kumar K Signed-off-by: Nitin Shetty J Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260608095455.1499203-1-nshettyj@marvell.com Signed-off-by: Paolo Abeni --- .../ethernet/marvell/octeontx2/af/cn20k/npc.c | 72 +++++++++---------- 1 file changed, 34 insertions(+), 38 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c index 6b3f453fd500..fe8c4ffcd8f7 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c @@ -1571,53 +1571,49 @@ static u8 npc_map2cn20k_flag(u8 flag) return 0xff; } +static void npc_cn20k_translate_action_flags(struct npc_kpu_profile_action *act) +{ + u8 ltype, val; + + if (act->lid != NPC_LID_LC) + return; + + ltype = act->ltype; + if (ltype != NPC_LT_LC_IP && + ltype != NPC_LT_LC_IP6 && + ltype != NPC_LT_LC_IP_OPT && + ltype != NPC_LT_LC_IP6_EXT) + return; + + switch (act->flags) { + case NPC_F_LC_U_IP_FRAG: + case NPC_F_LC_U_IP6_FRAG: + case NPC_F_LC_L_6TO4: + case NPC_F_LC_L_MPLS_IN_IP: + case NPC_F_LC_L_IP6_TUN_IP6: + case NPC_F_LC_L_IP6_MPLS_IN_IP: + val = npc_map2cn20k_flag(act->flags); + if (val != 0xFF) + act->flags = val; + break; + default: + break; + } +} + void npc_cn20k_update_action_entries_n_flags(struct rvu *rvu, struct npc_kpu_profile_adapter *pfl) { struct npc_kpu_profile_action *action; - int entries, ltype; - u8 flags, val; + int entries; for (int i = 0; i < pfl->kpus; i++) { action = pfl->kpu[i].action; entries = pfl->kpu[i].action_entries; - for (int j = 0; j < entries; j++) { - if (action[j].lid != NPC_LID_LC) - continue; - - ltype = action[j].ltype; - - if (ltype != NPC_LT_LC_IP && - ltype != NPC_LT_LC_IP6 && - ltype != NPC_LT_LC_IP_OPT && - ltype != NPC_LT_LC_IP6_EXT) - continue; - - flags = action[j].flags; - - switch (flags) { - case NPC_F_LC_U_IP_FRAG: - case NPC_F_LC_U_IP6_FRAG: - case NPC_F_LC_L_6TO4: - case NPC_F_LC_L_MPLS_IN_IP: - case NPC_F_LC_L_IP6_TUN_IP6: - case NPC_F_LC_L_IP6_MPLS_IN_IP: - val = npc_map2cn20k_flag(flags); - if (val == 0xFF) { - dev_err(rvu->dev, - "%s: Error to get flag value\n", - __func__); - return; - } - - action[j].flags = val; - break; - default: - break; - } - } + for (int j = 0; j < entries; j++) + npc_cn20k_translate_action_flags(&action[j]); } } @@ -1709,9 +1705,9 @@ int npc_cn20k_apply_custom_kpu(struct rvu *rvu, for (entry = 0; entry < entries; entry++) { profile->kpu[kpu].cam[entry] = cam[entry]; profile->kpu[kpu].action[entry] = action[entry]; + npc_cn20k_translate_action_flags(&profile->kpu[kpu].action[entry]); } } - npc_cn20k_update_action_entries_n_flags(rvu, profile); return 0; }