From ee7863e43228a3143398dc5bbb943c9a735a8fca Mon Sep 17 00:00:00 2001 From: Akhil R Date: Tue, 31 Mar 2026 15:52:55 +0530 Subject: [PATCH 0001/1054] arm64: tegra: Remove fallback compatible for GPCDMA Remove the fallback compatible string "nvidia,tegra186-gpcdma" for GPCDMA in Tegra264. Tegra186 compatible cannot work on Tegra264 because of the register offset changes and absence of the reset property. Fixes: 65ef237e4810 ("arm64: tegra: Add Tegra264 support") Signed-off-by: Akhil R Reviewed-by: Jon Hunter Signed-off-by: Thierry Reding --- arch/arm64/boot/dts/nvidia/tegra264.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/nvidia/tegra264.dtsi b/arch/arm64/boot/dts/nvidia/tegra264.dtsi index 2d8e7e37830f..3dfdd7bb28a9 100644 --- a/arch/arm64/boot/dts/nvidia/tegra264.dtsi +++ b/arch/arm64/boot/dts/nvidia/tegra264.dtsi @@ -3208,7 +3208,7 @@ agic_page5: interrupt-controller@99b0000 { }; gpcdma: dma-controller@8400000 { - compatible = "nvidia,tegra264-gpcdma", "nvidia,tegra186-gpcdma"; + compatible = "nvidia,tegra264-gpcdma"; reg = <0x0 0x08400000 0x0 0x210000>; interrupts = , , From 7258770e5814f15e8308ebda82ac9acf6964ba8e Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Mon, 15 Jun 2026 19:16:25 +0100 Subject: [PATCH 0002/1054] KVM: arm64: vgic: Handle race between interrupt affinity change and LPI disabling Hyunwoo Kim reports some really bad races should the following situation occur: - LPI-I is pending in vcpu-B's AP list - vcpu-A writes to vcpu-B's RD to disable its LPIs - vcpu-C moves I from B to C If the last two race nicely enough, vgic_prune_ap_list() can drop the irq and AP list locks, reacquire them, and in the interval the irq has been freed. UAF follows. The fix is two-fold: - Before dropping the irq and ap_list locks, take a reference on the irq - Do not try to handle migration of the pending bit: there is no expectation that this state is retained, as per the architecture With that, we're sure that the interrupt is still around, and we safely remove it from the AP list as it has no target at this stage (unless another interrupt fires, but that's another story). Reported-by: Hyunwoo Kim Tested-by: Hyunwoo Kim Link: https://lore.kernel.org/r/ailsCnyoS82r_QRz@v4bel Link: https://patch.msgid.link/20260615181625.3029352-1-maz@kernel.org Fixes: 5dd4b924e390a ("KVM: arm/arm64: vgic: Add refcounting for IRQs") Signed-off-by: Marc Zyngier Cc: stable@vger.kernel.org --- arch/arm64/kvm/vgic/vgic.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/arch/arm64/kvm/vgic/vgic.c b/arch/arm64/kvm/vgic/vgic.c index 5a4768d8cd4f..70a161383e5a 100644 --- a/arch/arm64/kvm/vgic/vgic.c +++ b/arch/arm64/kvm/vgic/vgic.c @@ -203,6 +203,7 @@ void vgic_flush_pending_lpis(struct kvm_vcpu *vcpu) list_for_each_entry_safe(irq, tmp, &vgic_cpu->ap_list_head, ap_list) { if (irq_is_lpi(vcpu->kvm, irq->intid)) { raw_spin_lock(&irq->irq_lock); + irq->pending_latch = false; list_del(&irq->ap_list); irq->vcpu = NULL; raw_spin_unlock(&irq->irq_lock); @@ -792,7 +793,11 @@ static void vgic_prune_ap_list(struct kvm_vcpu *vcpu) continue; } - /* This interrupt looks like it has to be migrated. */ + /* + * This interrupt looks like it has to be migrated, + * make sure it is kept alive while locks are dropped. + */ + vgic_get_irq_ref(irq); raw_spin_unlock(&irq->irq_lock); raw_spin_unlock(&vgic_cpu->ap_list_lock); @@ -836,6 +841,8 @@ static void vgic_prune_ap_list(struct kvm_vcpu *vcpu) raw_spin_unlock(&vcpuB->arch.vgic_cpu.ap_list_lock); raw_spin_unlock(&vcpuA->arch.vgic_cpu.ap_list_lock); + deleted_lpis |= vgic_put_irq_norelease(vcpu->kvm, irq); + if (target_vcpu_needs_kick) { kvm_make_request(KVM_REQ_IRQ_PENDING, target_vcpu); kvm_vcpu_kick(target_vcpu); From 0074b82cdfcb5fd13710a0ac308ade68ac6f6fbe Mon Sep 17 00:00:00 2001 From: Hyunwoo Kim Date: Fri, 5 Jun 2026 05:59:15 +0900 Subject: [PATCH 0003/1054] KVM: arm64: vgic: Check the interrupt is still ours before migrating it vgic_prune_ap_list() drops both ap_list_lock and irq_lock while migrating an interrupt to another vCPU. After reacquiring the locks it only checks that the affinity is unchanged (target_vcpu == vgic_target_oracle(irq)) before moving the interrupt, which assumes that an interrupt whose affinity is preserved is still queued on this vCPU's ap_list. That assumption no longer holds if the interrupt is taken off the ap_list while the locks are dropped. vgic_flush_pending_lpis() removes the interrupt from the list and sets irq->vcpu to NULL, but leaves enabled/pending/target_vcpu untouched. As the interrupt is still enabled and pending, vgic_target_oracle() returns the same target_vcpu, so the affinity check passes and list_del() is run a second time on an entry that has already been removed. Also check that the interrupt is still assigned to this vCPU (irq->vcpu == vcpu) before moving it. Fixes: 0919e84c0fc1 ("KVM: arm/arm64: vgic-new: Add IRQ sync/flush framework") Signed-off-by: Hyunwoo Kim Link: https://patch.msgid.link/aiHnI1mu6SGQrgnz@v4bel Signed-off-by: Marc Zyngier Cc: stable@vger.kernel.org --- arch/arm64/kvm/vgic/vgic.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/arch/arm64/kvm/vgic/vgic.c b/arch/arm64/kvm/vgic/vgic.c index 70a161383e5a..ccb7e3a90cd0 100644 --- a/arch/arm64/kvm/vgic/vgic.c +++ b/arch/arm64/kvm/vgic/vgic.c @@ -820,15 +820,16 @@ static void vgic_prune_ap_list(struct kvm_vcpu *vcpu) raw_spin_lock(&irq->irq_lock); /* - * If the affinity has been preserved, move the - * interrupt around. Otherwise, it means things have - * changed while the interrupt was unlocked, and we - * need to replay this. + * If the interrupt is still ours and its affinity has + * been preserved, move it around. Otherwise, it means + * things have changed while the interrupt was unlocked + * (it may even have been taken off the list with its + * affinity left untouched), and we need to replay this. * * In all cases, we cannot trust the list not to have * changed, so we restart from the beginning. */ - if (target_vcpu == vgic_target_oracle(irq)) { + if (irq->vcpu == vcpu && target_vcpu == vgic_target_oracle(irq)) { struct vgic_cpu *new_cpu = &target_vcpu->arch.vgic_cpu; list_del(&irq->ap_list); From ff1022c3de46753eb7eba2f6efd990569e66ff95 Mon Sep 17 00:00:00 2001 From: Weiming Shi Date: Wed, 17 Jun 2026 12:08:21 +0800 Subject: [PATCH 0004/1054] KVM: arm64: nv: Fix SPSR_EL2 restore in kvm_hyp_handle_mops() kvm_hyp_handle_mops() resets the single-step state machine as part of rewinding state for a MOPS exception by modifying vcpu_cpsr() and writing the result directly into hardware. In the case of nested virtualization, vcpu_cpsr() is a synthetic value such that the rest of KVM can deal with vEL2 cleanly. That means the value requires translation before being written into hardware, which is unfortunately missing from the MOPS handler. Fix it by directly modifying SPSR_EL2 and avoiding the synthetic state altogether, which will be resynchronized on the next 'full' exit back to KVM. Fixes: 2de451a329cf ("KVM: arm64: Add handler for MOPS exceptions") Reported-by: Zhong Wang Reported-by: Xuanqing Shi Link: https://lore.kernel.org/all/ajE4lHQevXNHpl1M@Air.local/ Cc: stable@vger.kernel.org Signed-off-by: Weiming Shi Link: https://patch.msgid.link/20260617040820.2194831-2-bestswngs@gmail.com Signed-off-by: Marc Zyngier --- arch/arm64/kvm/hyp/include/hyp/switch.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/arch/arm64/kvm/hyp/include/hyp/switch.h b/arch/arm64/kvm/hyp/include/hyp/switch.h index 161bb2a3e1d9..d56371b189bf 100644 --- a/arch/arm64/kvm/hyp/include/hyp/switch.h +++ b/arch/arm64/kvm/hyp/include/hyp/switch.h @@ -446,16 +446,19 @@ static inline bool __populate_fault_info(struct kvm_vcpu *vcpu) static inline bool kvm_hyp_handle_mops(struct kvm_vcpu *vcpu, u64 *exit_code) { + u64 spsr; + *vcpu_pc(vcpu) = read_sysreg_el2(SYS_ELR); arm64_mops_reset_regs(vcpu_gp_regs(vcpu), vcpu->arch.fault.esr_el2); write_sysreg_el2(*vcpu_pc(vcpu), SYS_ELR); /* * Finish potential single step before executing the prologue - * instruction. + * instruction. Modify the hardware SPSR_EL2 directly, as vcpu_cpsr() + * may hold a synthetic (vEL2) value for a guest hypervisor. */ - *vcpu_cpsr(vcpu) &= ~DBG_SPSR_SS; - write_sysreg_el2(*vcpu_cpsr(vcpu), SYS_SPSR); + spsr = read_sysreg_el2(SYS_SPSR); + write_sysreg_el2(spsr & ~DBG_SPSR_SS, SYS_SPSR); return true; } From 9f1667098c6ae7ec81a9a56859cfdacb822aa0d0 Mon Sep 17 00:00:00 2001 From: Oliver Upton Date: Sun, 14 Jun 2026 22:13:24 -0700 Subject: [PATCH 0005/1054] KVM: arm64: nv: Drop bogus WARN for write to ZCR_EL2 It is entirely possible for a guest to write to the ZCR_EL2 sysreg alias while in a nested context, as it is expected if FEAT_NV2 is advertised to the L1 hypervisor. Get rid of the bogus WARN which, since the hyp vectors were installed at this point, has the effect of a hyp_panic... Cc: stable@vger.kernel.org Fixes: 0cfc85b8f5cf ("KVM: arm64: nv: Load guest FP state for ZCR_EL2 trap") Signed-off-by: Oliver Upton Link: https://patch.msgid.link/20260615051324.830045-1-oupton@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kvm/hyp/include/hyp/switch.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/arm64/kvm/hyp/include/hyp/switch.h b/arch/arm64/kvm/hyp/include/hyp/switch.h index d56371b189bf..ea22d1b50512 100644 --- a/arch/arm64/kvm/hyp/include/hyp/switch.h +++ b/arch/arm64/kvm/hyp/include/hyp/switch.h @@ -598,8 +598,6 @@ static inline bool kvm_hyp_handle_fpsimd(struct kvm_vcpu *vcpu, u64 *exit_code) return false; break; case ESR_ELx_EC_SYS64: - if (WARN_ON_ONCE(!is_hyp_ctxt(vcpu))) - return false; fallthrough; case ESR_ELx_EC_SVE: if (!sve_guest) From e2cb1f4578625e71f461d5c1ce70984193389cbb Mon Sep 17 00:00:00 2001 From: Fuad Tabba Date: Mon, 15 Jun 2026 14:11:16 +0100 Subject: [PATCH 0006/1054] KVM: arm64: nv: Write ESR_EL2 for injected nested SError exceptions kvm_inject_el2_exception() writes ESR_EL2 for synchronous exceptions but not for SError. enter_exception64() does not write ESR_ELx for any exception type, so the constructed syndrome is dropped. A guest L2 hypervisor taking a nested SError observes stale ESR_EL2. This affects both kvm_inject_nested_serror() and the EASE path in kvm_inject_nested_sea(). Write ESR_EL2 for except_type_serror, matching except_type_sync. Fixes: 77ee70a07357 ("KVM: arm64: nv: Honor SError exception routing / masking") Reported-by: sashiko Signed-off-by: Fuad Tabba Link: https://patch.msgid.link/20260615131116.390977-1-tabba@google.com Signed-off-by: Marc Zyngier Cc: stable@vger.kernel.org --- arch/arm64/kvm/emulate-nested.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/kvm/emulate-nested.c b/arch/arm64/kvm/emulate-nested.c index e688bc5139c1..76c3e6c1144c 100644 --- a/arch/arm64/kvm/emulate-nested.c +++ b/arch/arm64/kvm/emulate-nested.c @@ -2826,6 +2826,7 @@ static void kvm_inject_el2_exception(struct kvm_vcpu *vcpu, u64 esr_el2, break; case except_type_serror: kvm_pend_exception(vcpu, EXCEPT_AA64_EL2_SERR); + vcpu_write_sys_reg(vcpu, esr_el2, ESR_EL2); break; default: WARN_ONCE(1, "Unsupported EL2 exception injection %d\n", type); From ec40342aaca8162bc8ab2607076535ebab1838b8 Mon Sep 17 00:00:00 2001 From: Fuad Tabba Date: Fri, 12 Jun 2026 12:34:14 +0100 Subject: [PATCH 0007/1054] KVM: arm64: Sync SPSR_EL1 when injecting an exception into a pVM When pKVM injects a synchronous exception into a protected guest, it re-enters without restoring the guest's EL1 sysregs and writes the EL1 exception registers to hardware by hand: ESR_EL1 and ELR_EL1, but not SPSR_EL1. enter_exception64() sets SPSR_EL1 (the interrupted PSTATE) only in memory, so the guest's handler reads a stale SPSR_EL1 and restores the wrong PSTATE on eret. Write SPSR_EL1 alongside the other exception registers. Fixes: 6c30bfb18d0b ("KVM: arm64: Add handlers for protected VM System Registers") Reported-by: sashiko Signed-off-by: Fuad Tabba Acked-by: Will Deacon Link: https://patch.msgid.link/20260612113414.1022901-1-tabba@google.com Signed-off-by: Marc Zyngier --- arch/arm64/kvm/hyp/nvhe/sys_regs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/kvm/hyp/nvhe/sys_regs.c b/arch/arm64/kvm/hyp/nvhe/sys_regs.c index 8c3fbb413a06..1a7d5cd16d72 100644 --- a/arch/arm64/kvm/hyp/nvhe/sys_regs.c +++ b/arch/arm64/kvm/hyp/nvhe/sys_regs.c @@ -268,6 +268,7 @@ static void inject_sync64(struct kvm_vcpu *vcpu, u64 esr) write_sysreg_el1(esr, SYS_ESR); write_sysreg_el1(read_sysreg_el2(SYS_ELR), SYS_ELR); + write_sysreg_el1(read_sysreg_el2(SYS_SPSR), SYS_SPSR); write_sysreg_el2(*vcpu_pc(vcpu), SYS_ELR); write_sysreg_el2(*vcpu_cpsr(vcpu), SYS_SPSR); } From 0e8e955b9bcf84a70f20079390e19971fec1586d Mon Sep 17 00:00:00 2001 From: Fuad Tabba Date: Wed, 17 Jun 2026 15:49:07 +0100 Subject: [PATCH 0008/1054] KVM: arm64: nv: Fix PSTATE construction on illegal exception return kvm_check_illegal_exception_return() sourced the flags {N,Z,C,V} and masks {D,A,I,F} of the resulting PSTATE from the current PSTATE, but R_VWJHB takes them from the SPSR being returned to and leaves PSTATE.{EL,SP,nRW} (and EXLOCK when FEAT_GCS) unchanged. PAN, ALLINT and PM were not applied at all. Build the PSTATE by taking those fields from the SPSR while preserving EL, SP, nRW and EXLOCK from the current PSTATE, then set IL. Fixes: 47f3a2fc765a ("KVM: arm64: nv: Support virtual EL2 exceptions") Suggested-by: Marc Zyngier Link: https://lore.kernel.org/all/86wlvxs5r0.wl-maz@kernel.org/ Signed-off-by: Fuad Tabba Link: https://patch.msgid.link/20260617144907.2972095-1-tabba@google.com [maz: tidied things a bit] Signed-off-by: Marc Zyngier --- arch/arm64/kvm/emulate-nested.c | 36 ++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/arch/arm64/kvm/emulate-nested.c b/arch/arm64/kvm/emulate-nested.c index 76c3e6c1144c..96ebe7e3b408 100644 --- a/arch/arm64/kvm/emulate-nested.c +++ b/arch/arm64/kvm/emulate-nested.c @@ -2746,17 +2746,33 @@ static u64 kvm_check_illegal_exception_return(struct kvm_vcpu *vcpu, u64 spsr) (spsr & PSR_MODE32_BIT) || (vcpu_el2_tge_is_set(vcpu) && (mode == PSR_MODE_EL1t || mode == PSR_MODE_EL1h))) { - /* - * The guest is playing with our nerves. Preserve EL, SP, - * masks, flags from the existing PSTATE, and set IL. - * The HW will then generate an Illegal State Exception - * immediately after ERET. - */ - spsr = *vcpu_cpsr(vcpu); + u64 mask; - spsr &= (PSR_D_BIT | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT | - PSR_N_BIT | PSR_Z_BIT | PSR_C_BIT | PSR_V_BIT | - PSR_MODE_MASK | PSR_MODE32_BIT); + /* + * On an illegal exception return, the flags and masks are + * taken from the SPSR while PSTATE.{EL,SP,nRW} and, if + * FEAT_GCS, PSTATE.EXLOCK are unchanged (R_VWJHB). Set IL + * so the HW generates an Illegal State Exception right + * after ERET. + */ + mask = PSR_D_BIT | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT | + PSR_N_BIT | PSR_Z_BIT | PSR_C_BIT | PSR_V_BIT; + + if (kvm_has_feat(vcpu->kvm, ID_AA64MMFR1_EL1, PAN, IMP)) + mask |= PSR_PAN_BIT; + if (kvm_has_feat(vcpu->kvm, ID_AA64PFR1_EL1, NMI, IMP)) + mask |= ALLINT_ALLINT; + /* FEAT_SPE_EXC and FEAT_TRBE_EXC also gate PSTATE.PM one day... */ + if (kvm_has_feat(vcpu->kvm, ID_AA64DFR1_EL1, EBEP, IMP)) + mask |= BIT_ULL(32); /* SPSR_ELx.PM */ + + spsr &= mask; + + mask = PSR_MODE_MASK | PSR_MODE32_BIT; + if (kvm_has_feat(vcpu->kvm, ID_AA64PFR1_EL1, GCS, IMP)) + mask |= BIT_ULL(34); /* PSTATE.EXLOCK */ + + spsr |= *vcpu_cpsr(vcpu) & mask; spsr |= PSR_IL_BIT; } From fcda30b134d262a1b1826555352bd7349051486e Mon Sep 17 00:00:00 2001 From: Fuad Tabba Date: Wed, 17 Jun 2026 14:12:36 +0100 Subject: [PATCH 0009/1054] KVM: arm64: Add Fuad Tabba as a reviewer I have been working on KVM/arm64 for a couple of years, mostly on pKVM, and am currently upstreaming protected guest support, with more to come later. I already review KVM/arm64 patches more broadly, and am happy to continue doing so in an official capacity. Add myself to the KVM/arm64 reviewer list. Signed-off-by: Fuad Tabba Acked-by: Oliver Upton Link: https://patch.msgid.link/20260617131237.2842619-1-tabba@google.com Signed-off-by: Marc Zyngier --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index c2c6d79275c6..30cba9f7491e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14060,6 +14060,7 @@ F: virt/kvm/* KERNEL VIRTUAL MACHINE FOR ARM64 (KVM/arm64) M: Marc Zyngier M: Oliver Upton +R: Fuad Tabba R: Joey Gouly R: Steffen Eiden R: Suzuki K Poulose From e60d3f9b4c89ffff5472ddc2829d5e29bcb153fe Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 20 Jun 2026 21:23:14 +0200 Subject: [PATCH 0010/1054] drm/tegra: Fix a strange error handling path The resource freed at the 'put_aux' label is "sor->aux->dev". However, this resource is taken after devm_tegra_pmc_get(), so there is no point to release it in this error handling path. This is harmless because put_device() will be called with a NULL pointer, but this is confusing. So, fix the logic and return directly. Signed-off-by: Christophe JAILLET Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/sor.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c index a76095838133..6479f852e16a 100644 --- a/drivers/gpu/drm/tegra/sor.c +++ b/drivers/gpu/drm/tegra/sor.c @@ -3733,10 +3733,8 @@ static int tegra_sor_probe(struct platform_device *pdev) sor->num_settings = sor->soc->num_settings; sor->pmc = devm_tegra_pmc_get(&pdev->dev); - if (IS_ERR(sor->pmc)) { - err = PTR_ERR(sor->pmc); - goto put_aux; - } + if (IS_ERR(sor->pmc)) + return PTR_ERR(sor->pmc); np = of_parse_phandle(pdev->dev.of_node, "nvidia,dpaux", 0); if (np) { From 813e034925814858cc52e7de321ec4848314e15d Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 18 Jun 2026 16:36:50 +0200 Subject: [PATCH 0011/1054] soc/tegra: pmc: fix #ifdef block in header When build testing on ARM without the PMC driver, the other drivers fail to link: ld.lld: error: undefined symbol: tegra_pmc_core_domain_state_synced >>> referenced by regulators-tegra30.c >>> drivers/soc/tegra/regulators-tegra30.o:(tegra30_regulator_balance_voltage) in archive vmlinux.a >>> referenced by regulators-tegra20.c >>> drivers/soc/tegra/regulators-tegra20.o:(tegra20_core_rtc_update) in archive vmlinux.a Adapt the checks in the header to cover both cases on other architectures and without PMC. Fixes: 8318af5dd29c ("soc/tegra: pmc: Move legacy code behind CONFIG_ARM guard") Signed-off-by: Arnd Bergmann Signed-off-by: Thierry Reding --- include/soc/tegra/pmc.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/soc/tegra/pmc.h b/include/soc/tegra/pmc.h index 4bcbf19d75ac..53f6d02889db 100644 --- a/include/soc/tegra/pmc.h +++ b/include/soc/tegra/pmc.h @@ -210,7 +210,6 @@ tegra_pmc_io_pad_power_disable(struct tegra_pmc *pmc, enum tegra_io_pad id) bool tegra_pmc_cpu_is_powered(unsigned int cpuid); int tegra_pmc_cpu_power_on(unsigned int cpuid); int tegra_pmc_cpu_remove_clamping(unsigned int cpuid); -bool tegra_pmc_core_domain_state_synced(void); #if defined(CONFIG_SOC_TEGRA_PMC) && defined(CONFIG_PM_SLEEP) enum tegra_suspend_mode tegra_pmc_get_suspend_mode(void); @@ -230,6 +229,10 @@ static inline void tegra_pmc_enter_suspend_mode(enum tegra_suspend_mode mode) { } #endif +#endif + +#if defined(CONFIG_ARM) && defined(CONFIG_SOC_TEGRA_PMC) +bool tegra_pmc_core_domain_state_synced(void); #else /* needed for COMPILE_TEST */ static inline bool tegra_pmc_core_domain_state_synced(void) From 265d5d4032c5f6eb089a6e6241d37fdbde7da180 Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Thu, 4 Jun 2026 03:36:17 -0700 Subject: [PATCH 0012/1054] soc/tegra: fuse: Fix spurious straps warning on SMCCC platforms My Grace host started to show this warning: WARNING: drivers/soc/tegra/fuse/tegra-apbmisc.c:120 at tegra_read_straps tegra30_fuse_add_randomness tegra30_fuse_init tegra_fuse_probe tegra_read_straps() warns when the static "chipid" cache is still zero, using it as a proxy for "APBMISC has been initialised". However chipid is only ever populated lazily by tegra_read_chipid() when it reads the APBMISC register. Guard on apbmisc_base instead, which is set unconditionally in tegra_init_apbmisc_resources() for all platforms and is already the sentinel used by tegra_read_chipid(). Fixes: 8b8ee2e56f95 ("soc/tegra: Use ARM SMCCC to get chip ID, revision, and platform info") Signed-off-by: Breno Leitao Signed-off-by: Thierry Reding --- drivers/soc/tegra/fuse/tegra-apbmisc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soc/tegra/fuse/tegra-apbmisc.c b/drivers/soc/tegra/fuse/tegra-apbmisc.c index 87ae63a7e52d..7aba7c58bad0 100644 --- a/drivers/soc/tegra/fuse/tegra-apbmisc.c +++ b/drivers/soc/tegra/fuse/tegra-apbmisc.c @@ -117,7 +117,7 @@ bool tegra_is_silicon(void) u32 tegra_read_straps(void) { - WARN(!chipid, "Tegra ABP MISC not yet available\n"); + WARN(!apbmisc_base, "Tegra ABP MISC not yet available\n"); return strapping; } From 2684e02bac41c5220f6c1ab2bdcc957b71812977 Mon Sep 17 00:00:00 2001 From: Oliver Upton Date: Thu, 18 Jun 2026 16:42:02 -0700 Subject: [PATCH 0013/1054] KVM: arm64: nv: Respect read-only PFN when mapping L1 VNCR KVM currently maps the L1 VNCR into the host stage-1 by relying entirely on the permissions of the guest stage-1. At the same time, it is entirely possible that the backing PFN is read-only (e.g. RO memslot), meaning that the L1 VNCR should use at most a read-only mapping. Cache the writability of the PFN in the VNCR TLB and use it to constrain the resulting fixmap permissions. Promote VNCR permission faults to an SEA in the case where the guest attempts to write to a read-only endpoint. Conveniently, this also plugs a page leak found by Sashiko [*] resulting from the early return for a read-only PFN. Cc: stable@vger.kernel.org Fixes: 2a359e072596 ("KVM: arm64: nv: Handle mapping of VNCR_EL2 at EL2") Link: https://lore.kernel.org/kvm/20260608082603.16AEC1F00893@smtp.kernel.org/ Signed-off-by: Oliver Upton Link: https://patch.msgid.link/20260618234207.1063941-2-oupton@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kvm/nested.c | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c index 3a5571c3c114..903ccabca78c 100644 --- a/arch/arm64/kvm/nested.c +++ b/arch/arm64/kvm/nested.c @@ -24,6 +24,7 @@ struct vncr_tlb { struct s1_walk_result wr; u64 hpa; + bool hpa_writable; /* -1 when not mapped on a CPU */ int cpu; @@ -1401,7 +1402,7 @@ static int kvm_translate_vncr(struct kvm_vcpu *vcpu, bool *is_gmem) if (!*is_gmem) { pfn = __kvm_faultin_pfn(memslot, gfn, write_fault ? FOLL_WRITE : 0, &writable, &page); - if (is_error_noslot_pfn(pfn) || (write_fault && !writable)) + if (is_error_noslot_pfn(pfn)) return -EFAULT; } else { ret = kvm_gmem_get_pfn(vcpu->kvm, memslot, gfn, &pfn, &page, NULL); @@ -1410,6 +1411,8 @@ static int kvm_translate_vncr(struct kvm_vcpu *vcpu, bool *is_gmem) write_fault, false, false); return ret; } + + writable = !(memslot->flags & KVM_MEM_READONLY); } scoped_guard(write_lock, &vcpu->kvm->mmu_lock) { @@ -1420,28 +1423,41 @@ static int kvm_translate_vncr(struct kvm_vcpu *vcpu, bool *is_gmem) vt->gva = va; vt->hpa = pfn << PAGE_SHIFT; + vt->hpa_writable = writable; vt->valid = true; vt->cpu = -1; kvm_make_request(KVM_REQ_MAP_L1_VNCR_EL2, vcpu); - kvm_release_faultin_page(vcpu->kvm, page, false, vt->wr.pw); + kvm_release_faultin_page(vcpu->kvm, page, false, vt->wr.pw && vt->hpa_writable); } - if (vt->wr.pw) + if (vt->wr.pw && vt->hpa_writable) mark_page_dirty(vcpu->kvm, gfn); return 0; } -static void inject_vncr_perm(struct kvm_vcpu *vcpu) +static void handle_vncr_perm(struct kvm_vcpu *vcpu) { struct vncr_tlb *vt = vcpu->arch.vncr_tlb; u64 esr = kvm_vcpu_get_esr(vcpu); + u64 fsc; + + /* + * Promote to an external abort if the stage-1 permits writes but the + * HPA is read-only (e.g. RO memslot). + */ + if (kvm_is_write_fault(vcpu) && vt->wr.pw && !vt->hpa_writable) + fsc = ESR_ELx_FSC_EXTABT; + /* + * Otherwise, inject a permission fault using the guest's translation + * level rather than the host's. + */ + else + fsc = ESR_ELx_FSC_PERM_L(vt->wr.level); - /* Adjust the fault level to reflect that of the guest's */ esr &= ~ESR_ELx_FSC; - esr |= FIELD_PREP(ESR_ELx_FSC, - ESR_ELx_FSC_PERM_L(vt->wr.level)); + esr |= FIELD_PREP(ESR_ELx_FSC, fsc); kvm_inject_nested_sync(vcpu, esr); } @@ -1475,7 +1491,7 @@ int kvm_handle_vncr_abort(struct kvm_vcpu *vcpu) return kvm_handle_guest_sea(vcpu); if (esr_fsc_is_permission_fault(esr)) { - inject_vncr_perm(vcpu); + handle_vncr_perm(vcpu); } else if (esr_fsc_is_translation_fault(esr)) { bool valid, is_gmem = false; int ret; @@ -1523,7 +1539,7 @@ int kvm_handle_vncr_abort(struct kvm_vcpu *vcpu) break; case -EPERM: /* Hack to deal with POE until we get kernel support */ - inject_vncr_perm(vcpu); + handle_vncr_perm(vcpu); break; case 0: break; @@ -1567,7 +1583,7 @@ static void kvm_map_l1_vncr(struct kvm_vcpu *vcpu) vt->cpu = smp_processor_id(); - if (vt->wr.pw && vt->wr.pr) + if (vt->hpa_writable && vt->wr.pw && vt->wr.pr) prot = PAGE_KERNEL; else if (vt->wr.pr) prot = PAGE_KERNEL_RO; From 9f3e83345a56280efffe235c65593c7e544c0fcc Mon Sep 17 00:00:00 2001 From: Oliver Upton Date: Thu, 18 Jun 2026 16:42:03 -0700 Subject: [PATCH 0014/1054] KVM: arm64: nv: Inject SEA if kvm_translate_vncr() can't resolve PFN kvm_handle_vncr_abort() assumes that s1_walk_result conveys an abort when kvm_translate_vncr() returns -EFAULT. This is not always the case as it's possible to encounter 'late' failures on the output of S1 translation, e.g. a GFN outside of the memslots. Fix it by preparing an external abort before returning from kvm_translate_vncr(). Get rid of the BUG_ON() in the fault injection path while at it. Cc: stable@vger.kernel.org Fixes: 2a359e072596 ("KVM: arm64: nv: Handle mapping of VNCR_EL2 at EL2") Signed-off-by: Oliver Upton Link: https://patch.msgid.link/20260618234207.1063941-3-oupton@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/include/asm/kvm_nested.h | 8 ++++++++ arch/arm64/kvm/at.c | 8 -------- arch/arm64/kvm/nested.c | 10 ++++++---- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/arch/arm64/include/asm/kvm_nested.h b/arch/arm64/include/asm/kvm_nested.h index dc2957662ff2..cbdaaa2a2903 100644 --- a/arch/arm64/include/asm/kvm_nested.h +++ b/arch/arm64/include/asm/kvm_nested.h @@ -388,6 +388,14 @@ struct s1_walk_result { bool failed; }; +static inline void fail_s1_walk(struct s1_walk_result *wr, u8 fst, bool s1ptw) +{ + wr->fst = fst; + wr->ptw = s1ptw; + wr->s2 = s1ptw; + wr->failed = true; +} + int __kvm_translate_va(struct kvm_vcpu *vcpu, struct s1_walk_info *wi, struct s1_walk_result *wr, u64 va); int __kvm_find_s1_desc_level(struct kvm_vcpu *vcpu, u64 va, u64 ipa, diff --git a/arch/arm64/kvm/at.c b/arch/arm64/kvm/at.c index 30e6fa8ac07c..8263c648207b 100644 --- a/arch/arm64/kvm/at.c +++ b/arch/arm64/kvm/at.c @@ -11,14 +11,6 @@ #include #include -static void fail_s1_walk(struct s1_walk_result *wr, u8 fst, bool s1ptw) -{ - wr->fst = fst; - wr->ptw = s1ptw; - wr->s2 = s1ptw; - wr->failed = true; -} - #define S1_MMU_DISABLED (-127) static int get_ia_size(struct s1_walk_info *wi) diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c index 903ccabca78c..53dea9c3f14f 100644 --- a/arch/arm64/kvm/nested.c +++ b/arch/arm64/kvm/nested.c @@ -1395,15 +1395,19 @@ static int kvm_translate_vncr(struct kvm_vcpu *vcpu, bool *is_gmem) gfn = vt->wr.pa >> PAGE_SHIFT; memslot = gfn_to_memslot(vcpu->kvm, gfn); - if (!memslot) + if (!memslot) { + fail_s1_walk(&vt->wr, ESR_ELx_FSC_EXTABT, false); return -EFAULT; + } *is_gmem = kvm_slot_has_gmem(memslot); if (!*is_gmem) { pfn = __kvm_faultin_pfn(memslot, gfn, write_fault ? FOLL_WRITE : 0, &writable, &page); - if (is_error_noslot_pfn(pfn)) + if (is_error_noslot_pfn(pfn)) { + fail_s1_walk(&vt->wr, ESR_ELx_FSC_EXTABT, false); return -EFAULT; + } } else { ret = kvm_gmem_get_pfn(vcpu->kvm, memslot, gfn, &pfn, &page, NULL); if (ret) { @@ -1530,8 +1534,6 @@ int kvm_handle_vncr_abort(struct kvm_vcpu *vcpu) * Translation failed, inject the corresponding * exception back to EL2. */ - BUG_ON(!vt->wr.failed); - esr &= ~ESR_ELx_FSC; esr |= FIELD_PREP(ESR_ELx_FSC, vt->wr.fst); From bb645aa0a4caeaf7f9cd32e9a948594d434c1a8f Mon Sep 17 00:00:00 2001 From: Oliver Upton Date: Thu, 18 Jun 2026 16:42:04 -0700 Subject: [PATCH 0015/1054] KVM: arm64: nv: Re-translate VNCR before injecting abort KVM faults in the VNCR page with FOLL_WRITE whenever the guest aborts for a write, similar to how a regular stage-2 mapping is handled. It is entirely possible that the guest reads from the VNCR before writing to it, in which case the PFN could only be read-only. Invalidate the VNCR TLB and re-fetch the translation upon taking a VNCR abort, allowing the host mapping to be faulted in for write the second time around. Interestingly enough, this also satisfies the ordering requirements of FEAT_ETS2/3 between descriptor updates and MMU faults. Cc: stable@vger.kernel.org Fixes: 2a359e072596 ("KVM: arm64: nv: Handle mapping of VNCR_EL2 at EL2") Reported-by: Sashiko Signed-off-by: Oliver Upton Link: https://patch.msgid.link/20260618234207.1063941-4-oupton@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kvm/nested.c | 115 +++++++++++++++------------------------- 1 file changed, 44 insertions(+), 71 deletions(-) diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c index 53dea9c3f14f..7fffd86eee94 100644 --- a/arch/arm64/kvm/nested.c +++ b/arch/arm64/kvm/nested.c @@ -1466,88 +1466,61 @@ static void handle_vncr_perm(struct kvm_vcpu *vcpu) kvm_inject_nested_sync(vcpu, esr); } -static bool kvm_vncr_tlb_lookup(struct kvm_vcpu *vcpu) -{ - struct vncr_tlb *vt = vcpu->arch.vncr_tlb; - - lockdep_assert_held_read(&vcpu->kvm->mmu_lock); - - if (!vt->valid) - return false; - - if (read_vncr_el2(vcpu) != vt->gva) - return false; - - if (vt->wr.nG) - return get_asid_by_regime(vcpu, TR_EL20) == vt->wr.asid; - - return true; -} - int kvm_handle_vncr_abort(struct kvm_vcpu *vcpu) { struct vncr_tlb *vt = vcpu->arch.vncr_tlb; u64 esr = kvm_vcpu_get_esr(vcpu); + bool is_gmem = false; + bool perm; + int ret; WARN_ON_ONCE(!(esr & ESR_ELx_VNCR)); if (kvm_vcpu_abt_issea(vcpu)) return kvm_handle_guest_sea(vcpu); - if (esr_fsc_is_permission_fault(esr)) { - handle_vncr_perm(vcpu); - } else if (esr_fsc_is_translation_fault(esr)) { - bool valid, is_gmem = false; - int ret; - - scoped_guard(read_lock, &vcpu->kvm->mmu_lock) - valid = kvm_vncr_tlb_lookup(vcpu); - - if (!valid) - ret = kvm_translate_vncr(vcpu, &is_gmem); - else - ret = -EPERM; - - switch (ret) { - case -EAGAIN: - /* Let's try again... */ - break; - case -ENOMEM: - /* - * For guest_memfd, this indicates that it failed to - * create a folio to back the memory. Inform userspace. - */ - if (is_gmem) - return 0; - /* Otherwise, let's try again... */ - break; - case -EFAULT: - case -EIO: - case -EHWPOISON: - if (is_gmem) - return 0; - fallthrough; - case -EINVAL: - case -ENOENT: - case -EACCES: - /* - * Translation failed, inject the corresponding - * exception back to EL2. - */ - esr &= ~ESR_ELx_FSC; - esr |= FIELD_PREP(ESR_ELx_FSC, vt->wr.fst); - - kvm_inject_nested_sync(vcpu, esr); - break; - case -EPERM: - /* Hack to deal with POE until we get kernel support */ - handle_vncr_perm(vcpu); - break; - case 0: - break; - } - } else { + if (!esr_fsc_is_translation_fault(esr) && !esr_fsc_is_permission_fault(esr)) { WARN_ONCE(1, "Unhandled VNCR abort, ESR=%llx\n", esr); + return 1; + } + + ret = kvm_translate_vncr(vcpu, &is_gmem); + switch (ret) { + case -EAGAIN: + /* Let's try again... */ + return 1; + case -ENOMEM: + /* + * For guest_memfd, this indicates that it failed to + * create a folio to back the memory. Inform userspace. + */ + if (is_gmem) + return 0; + /* Otherwise, let's try again... */ + break; + case -EFAULT: + case -EIO: + case -EHWPOISON: + if (is_gmem) + return 0; + fallthrough; + case -EINVAL: + case -ENOENT: + case -EACCES: + /* + * Translation failed, inject the corresponding + * exception back to EL2. + */ + esr &= ~ESR_ELx_FSC; + esr |= FIELD_PREP(ESR_ELx_FSC, vt->wr.fst); + + kvm_inject_nested_sync(vcpu, esr); + break; + case 0: + perm = kvm_is_write_fault(vcpu) ? vt->wr.pw && vt->hpa_writable : vt->wr.pr; + if (!perm) + handle_vncr_perm(vcpu); + break; } return 1; From 4bd7dbe0b2243e6aa735cae4d5e1ff988b30b2a6 Mon Sep 17 00:00:00 2001 From: Oliver Upton Date: Thu, 18 Jun 2026 16:42:05 -0700 Subject: [PATCH 0016/1054] KVM: arm64: nv: Inject SEA if guest VNCR isn't normal memory When constructing an L1 VNCR mapping, KVM unconditionally uses cacheable memory attributes, even if the underlying PFN isn't memory. This gets particularly hairy if the endpoint doesn't support cacheable memory attributes, potentially throwing an SError on writeback... While KVM does permit cacheable memory attributes on certain PFNMAP VMAs, kvm_translate_vncr() isn't currently grabbing the VMA. So do the simpler thing for now and just reject everything that isn't memory. Cc: stable@vger.kernel.org Fixes: 2a359e072596 ("KVM: arm64: nv: Handle mapping of VNCR_EL2 at EL2") Signed-off-by: Oliver Upton Link: https://patch.msgid.link/20260618234207.1063941-5-oupton@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kvm/nested.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c index 7fffd86eee94..d4c9a9b05e3f 100644 --- a/arch/arm64/kvm/nested.c +++ b/arch/arm64/kvm/nested.c @@ -1419,6 +1419,17 @@ static int kvm_translate_vncr(struct kvm_vcpu *vcpu, bool *is_gmem) writable = !(memslot->flags & KVM_MEM_READONLY); } + /* + * FIXME: This check is too restrictive as KVM allows cacheable memory + * attributes for PFNMAP VMAs that have cacheable attributes in host + * stage-1. + */ + if (!pfn_is_map_memory(pfn)) { + kvm_release_faultin_page(vcpu->kvm, page, true, false); + fail_s1_walk(&vt->wr, ESR_ELx_FSC_EXTABT, false); + return -EINVAL; + } + scoped_guard(write_lock, &vcpu->kvm->mmu_lock) { if (mmu_invalidate_retry(vcpu->kvm, mmu_seq)) { kvm_release_faultin_page(vcpu->kvm, page, true, false); From 265b58aba51b6aaaad81678fbc57fcdb2d4ed480 Mon Sep 17 00:00:00 2001 From: Oliver Upton Date: Thu, 18 Jun 2026 16:42:06 -0700 Subject: [PATCH 0017/1054] KVM: arm64: nv: Mark VM as bugged for unexpected VNCR abort KVM is unlikely to resolve an unexpected VNCR abort, meaning that returning to the guest will likely leave the vCPU stuck in an abort loop. Bug the VM and exit to userspace instead. Signed-off-by: Oliver Upton Link: https://patch.msgid.link/20260618234207.1063941-6-oupton@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kvm/nested.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c index d4c9a9b05e3f..94df26de6990 100644 --- a/arch/arm64/kvm/nested.c +++ b/arch/arm64/kvm/nested.c @@ -1491,8 +1491,8 @@ int kvm_handle_vncr_abort(struct kvm_vcpu *vcpu) return kvm_handle_guest_sea(vcpu); if (!esr_fsc_is_translation_fault(esr) && !esr_fsc_is_permission_fault(esr)) { - WARN_ONCE(1, "Unhandled VNCR abort, ESR=%llx\n", esr); - return 1; + KVM_BUG(1, vcpu->kvm, "Unhandled VNCR abort, ESR=%llx\n", esr); + return -EIO; } ret = kvm_translate_vncr(vcpu, &is_gmem); From daa71eca24fdfb43029830bd57ddaddf70c59b23 Mon Sep 17 00:00:00 2001 From: Fuad Tabba Date: Thu, 18 Jun 2026 13:16:37 +0100 Subject: [PATCH 0018/1054] KVM: arm64: Set ESR_ELx.IL for injected undefined exceptions at EL2 inject_undef64() constructs an ESR with EC=0 (Unknown) but does not set IL. The architecture mandates IL=1 for EC=0 unconditionally (ARM DDI 0487, ESR_ELx.IL description), so the injected syndrome is one that conforming hardware cannot produce. Set ESR_ELx_IL in the constructed syndrome. Fixes: e5d40a5a97c1 ("KVM: arm64: pkvm: Add a generic synchronous exception injection primitive") Reported-by: sashiko Signed-off-by: Fuad Tabba Link: https://patch.msgid.link/20260618121643.4105064-2-tabba@google.com Signed-off-by: Marc Zyngier --- arch/arm64/kvm/hyp/nvhe/sys_regs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/kvm/hyp/nvhe/sys_regs.c b/arch/arm64/kvm/hyp/nvhe/sys_regs.c index 1a7d5cd16d72..b1411fb54139 100644 --- a/arch/arm64/kvm/hyp/nvhe/sys_regs.c +++ b/arch/arm64/kvm/hyp/nvhe/sys_regs.c @@ -279,7 +279,7 @@ static void inject_sync64(struct kvm_vcpu *vcpu, u64 esr) */ static void inject_undef64(struct kvm_vcpu *vcpu) { - inject_sync64(vcpu, (ESR_ELx_EC_UNKNOWN << ESR_ELx_EC_SHIFT)); + inject_sync64(vcpu, (ESR_ELx_EC_UNKNOWN << ESR_ELx_EC_SHIFT) | ESR_ELx_IL); } static u64 read_id_reg(const struct kvm_vcpu *vcpu, From 1d695dc827957e9570d1b56abac1250d2d13bf0c Mon Sep 17 00:00:00 2001 From: Fuad Tabba Date: Thu, 18 Jun 2026 13:16:38 +0100 Subject: [PATCH 0019/1054] KVM: arm64: Unconditionally set IL for injected undefined exceptions inject_undef64() derives IL from the triggering trap's instruction length (kvm_vcpu_trap_il_is32bit()), but the IL of the injected exception is fixed by its EC, not by the triggering instruction. The architecture mandates IL=1 for EC=0 (Unknown) unconditionally, so the conditional is wrong. The undef-injection paths are not reached from 16-bit instructions, so there is no functional change today, but the logic should not rely on that. Set ESR_ELx_IL unconditionally. Fixes: aa8eff9bfbd5 ("arm64: KVM: fault injection into a guest") Signed-off-by: Fuad Tabba Link: https://patch.msgid.link/20260618121643.4105064-3-tabba@google.com Signed-off-by: Marc Zyngier --- arch/arm64/kvm/inject_fault.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/arch/arm64/kvm/inject_fault.c b/arch/arm64/kvm/inject_fault.c index 89982bd3345f..9dfae1bcdf99 100644 --- a/arch/arm64/kvm/inject_fault.c +++ b/arch/arm64/kvm/inject_fault.c @@ -170,14 +170,7 @@ void kvm_inject_sync(struct kvm_vcpu *vcpu, u64 esr) static void inject_undef64(struct kvm_vcpu *vcpu) { - u64 esr = (ESR_ELx_EC_UNKNOWN << ESR_ELx_EC_SHIFT); - - /* - * Build an unknown exception, depending on the instruction - * set. - */ - if (kvm_vcpu_trap_il_is32bit(vcpu)) - esr |= ESR_ELx_IL; + u64 esr = (ESR_ELx_EC_UNKNOWN << ESR_ELx_EC_SHIFT) | ESR_ELx_IL; kvm_inject_sync(vcpu, esr); } From add40af98b34764ff5603dce297160fde12d784c Mon Sep 17 00:00:00 2001 From: Fuad Tabba Date: Thu, 18 Jun 2026 13:16:39 +0100 Subject: [PATCH 0020/1054] KVM: arm64: Unconditionally set IL for injected abort exceptions inject_abt64() derives IL from the triggering trap's instruction length (kvm_vcpu_trap_il_is32bit()), but the IL of the injected abort is fixed by its EC, not by the triggering instruction. The architecture mandates IL=1 for Instruction Aborts unconditionally and for Data Aborts with ISV=0, and this function never sets ISV (the FSC is always EXTABT or SEA_TTW). For a 16-bit T32 trap (a 32-bit EL0 task under an AArch64 EL1 guest) the trap has IL=0, so the abort is injected with the wrong IL. Set ESR_ELx_IL unconditionally. Fixes: aa8eff9bfbd5 ("arm64: KVM: fault injection into a guest") Signed-off-by: Fuad Tabba Link: https://patch.msgid.link/20260618121643.4105064-4-tabba@google.com Signed-off-by: Marc Zyngier --- arch/arm64/kvm/inject_fault.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/arch/arm64/kvm/inject_fault.c b/arch/arm64/kvm/inject_fault.c index 9dfae1bcdf99..444d219b0217 100644 --- a/arch/arm64/kvm/inject_fault.c +++ b/arch/arm64/kvm/inject_fault.c @@ -138,11 +138,10 @@ static void inject_abt64(struct kvm_vcpu *vcpu, bool is_iabt, unsigned long addr pend_sync_exception(vcpu); /* - * Build an {i,d}abort, depending on the level and the - * instruction set. Report an external synchronous abort. + * Build an {i,d}abort, depending on the level. + * Report an external synchronous abort. */ - if (kvm_vcpu_trap_il_is32bit(vcpu)) - esr |= ESR_ELx_IL; + esr |= ESR_ELx_IL; /* * Here, the guest runs in AArch64 mode when in EL1. If we get From a52d6d68ad30374dd794bff300d8538e35ee49a8 Mon Sep 17 00:00:00 2001 From: Fuad Tabba Date: Thu, 18 Jun 2026 13:16:40 +0100 Subject: [PATCH 0021/1054] KVM: arm64: Set IL for injected FPAC exceptions during ERET emulation The FPAC syndrome constructed during nested ERET emulation does not set IL. For FPAC (EC=0x1C), IL reflects the instruction length. ERET and its authenticated variants are always A64 32-bit instructions, so IL must be 1. Fixes: 213b3d1ea161 ("KVM: arm64: nv: Handle ERETA[AB] instructions") Suggested-by: Marc Zyngier Signed-off-by: Fuad Tabba Link: https://patch.msgid.link/20260618121643.4105064-5-tabba@google.com Signed-off-by: Marc Zyngier --- arch/arm64/kvm/emulate-nested.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/kvm/emulate-nested.c b/arch/arm64/kvm/emulate-nested.c index 96ebe7e3b408..a15b2f41a12c 100644 --- a/arch/arm64/kvm/emulate-nested.c +++ b/arch/arm64/kvm/emulate-nested.c @@ -2800,7 +2800,7 @@ void kvm_emulate_nested_eret(struct kvm_vcpu *vcpu) * ERET handling, and the guest will have a little surprise. */ if (kvm_has_pauth(vcpu->kvm, FPACCOMBINE) && !(spsr & PSR_IL_BIT)) { - esr &= ESR_ELx_ERET_ISS_ERETA; + esr &= (ESR_ELx_ERET_ISS_ERETA | ESR_ELx_IL); esr |= FIELD_PREP(ESR_ELx_EC_MASK, ESR_ELx_EC_FPAC); kvm_inject_nested_sync(vcpu, esr); return; From 7514f1785d526207af8512cc6ccb1c35c5c61767 Mon Sep 17 00:00:00 2001 From: Fuad Tabba Date: Thu, 18 Jun 2026 13:16:41 +0100 Subject: [PATCH 0022/1054] KVM: arm64: Set IL for emulated SError injection kvm_inject_serror_esr() constructs an SError syndrome without IL. The architecture mandates IL=1 for SError unconditionally. Fixes: f6e2262dfa1a ("KVM: arm64: Populate ESR_ELx.EC for emulated SError injection") Signed-off-by: Fuad Tabba Link: https://patch.msgid.link/20260618121643.4105064-6-tabba@google.com Signed-off-by: Marc Zyngier --- arch/arm64/kvm/inject_fault.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/kvm/inject_fault.c b/arch/arm64/kvm/inject_fault.c index 444d219b0217..d6c4fc16f879 100644 --- a/arch/arm64/kvm/inject_fault.c +++ b/arch/arm64/kvm/inject_fault.c @@ -381,7 +381,7 @@ int kvm_inject_serror_esr(struct kvm_vcpu *vcpu, u64 esr) */ if (!serror_is_masked(vcpu)) { pend_serror_exception(vcpu); - esr |= FIELD_PREP(ESR_ELx_EC_MASK, ESR_ELx_EC_SERROR); + esr |= FIELD_PREP(ESR_ELx_EC_MASK, ESR_ELx_EC_SERROR) | ESR_ELx_IL; vcpu_write_sys_reg(vcpu, esr, exception_esr_elx(vcpu)); return 1; } From a69412287a33c931dca9e48d30c0dbf8cde0ffe6 Mon Sep 17 00:00:00 2001 From: Fuad Tabba Date: Thu, 18 Jun 2026 13:16:42 +0100 Subject: [PATCH 0023/1054] KVM: arm64: Set IL for nested SError injection kvm_inject_nested_serror() constructs an SError syndrome without IL. The architecture mandates IL=1 for SError unconditionally. Fixes: 77ee70a07357 ("KVM: arm64: nv: Honor SError exception routing / masking") Signed-off-by: Fuad Tabba Link: https://patch.msgid.link/20260618121643.4105064-7-tabba@google.com Signed-off-by: Marc Zyngier --- arch/arm64/kvm/emulate-nested.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/kvm/emulate-nested.c b/arch/arm64/kvm/emulate-nested.c index a15b2f41a12c..3c82f392845d 100644 --- a/arch/arm64/kvm/emulate-nested.c +++ b/arch/arm64/kvm/emulate-nested.c @@ -2967,6 +2967,6 @@ int kvm_inject_nested_serror(struct kvm_vcpu *vcpu, u64 esr) * vSError injection. Manually populate EC for an emulated SError * exception. */ - esr |= FIELD_PREP(ESR_ELx_EC_MASK, ESR_ELx_EC_SERROR); + esr |= FIELD_PREP(ESR_ELx_EC_MASK, ESR_ELx_EC_SERROR) | ESR_ELx_IL; return kvm_inject_nested(vcpu, esr, except_type_serror); } From cbe2278aa3dd6832c544782c6cfed1fbc1f71a42 Mon Sep 17 00:00:00 2001 From: Fuad Tabba Date: Thu, 18 Jun 2026 13:16:43 +0100 Subject: [PATCH 0024/1054] KVM: arm64: Set IL in fake ESR for pKVM memory sharing exit __pkvm_memshare_page_req() constructs a fake DABT ESR_EL2 to exit to the host without setting IL. The ESR has ISV=0, so IL must be 1 per the architecture. The host does not read IL on this path, but the constructed syndrome should still be architecturally valid. Set ESR_ELx_IL. Fixes: 03313efed5e2 ("KVM: arm64: Implement the MEM_SHARE hypercall for protected VMs") Signed-off-by: Fuad Tabba Link: https://patch.msgid.link/20260618121643.4105064-8-tabba@google.com Signed-off-by: Marc Zyngier --- arch/arm64/kvm/hyp/nvhe/pkvm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm64/kvm/hyp/nvhe/pkvm.c b/arch/arm64/kvm/hyp/nvhe/pkvm.c index 3b2c4fbc34d8..24d6f164129a 100644 --- a/arch/arm64/kvm/hyp/nvhe/pkvm.c +++ b/arch/arm64/kvm/hyp/nvhe/pkvm.c @@ -1056,7 +1056,8 @@ static u64 __pkvm_memshare_page_req(struct kvm_vcpu *vcpu, u64 ipa) /* Fake up a data abort (level 3 translation fault on write) */ vcpu->arch.fault.esr_el2 = (ESR_ELx_EC_DABT_LOW << ESR_ELx_EC_SHIFT) | - ESR_ELx_WNR | ESR_ELx_FSC_FAULT | + ESR_ELx_IL | ESR_ELx_WNR | + ESR_ELx_FSC_FAULT | FIELD_PREP(ESR_ELx_FSC_LEVEL, 3); /* Shuffle the IPA around into the HPFAR */ From d098bb75d14fde2f12155f1a95ec0168160867ce Mon Sep 17 00:00:00 2001 From: Bradley Morgan Date: Sun, 21 Jun 2026 21:31:55 +0000 Subject: [PATCH 0025/1054] KVM: arm64: account pKVM reclaim against the VM mm Protected guest faults charge long term pins to the VM's mm. Teardown can run later from file release, where current->mm may be unrelated. Drop the charge from kvm->mm instead. Fixes: 4e6e03f9eadd ("KVM: arm64: Hook up reclaim hypercall to pkvm_pgtable_stage2_destroy()") Signed-off-by: Bradley Morgan Reviewed-by: Fuad Tabba Tested-by: Fuad Tabba Link: https://patch.msgid.link/20260621213155.6019-1-include@grrlz.net Signed-off-by: Marc Zyngier Cc: stable@vger.kernel.org --- arch/arm64/kvm/pkvm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/kvm/pkvm.c b/arch/arm64/kvm/pkvm.c index 053e4f733e4b..428723b1b0f5 100644 --- a/arch/arm64/kvm/pkvm.c +++ b/arch/arm64/kvm/pkvm.c @@ -352,7 +352,7 @@ static int __pkvm_pgtable_stage2_reclaim(struct kvm_pgtable *pgt, u64 start, u64 page = pfn_to_page(mapping->pfn); WARN_ON_ONCE(mapping->nr_pages != 1); unpin_user_pages_dirty_lock(&page, 1, true); - account_locked_vm(current->mm, 1, false); + account_locked_vm(kvm->mm, 1, false); pkvm_mapping_remove(mapping, &pgt->pkvm_mappings); kfree(mapping); } From 314c243b201b678fa89226b1eaea51a71340454e Mon Sep 17 00:00:00 2001 From: Jens Wiklander Date: Mon, 8 Jun 2026 10:21:50 +0200 Subject: [PATCH 0026/1054] MAINTAINERS: .mailmap: update Jens Wiklander's email address Update Jens Wiklander's email address to @kernel.org. Cc: Arnd Bergmann Cc: Sumit Garg Cc: Greg Kroah-Hartman Signed-off-by: Jens Wiklander --- .mailmap | 1 + MAINTAINERS | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.mailmap b/.mailmap index 0b9298a55d2d..4c30a9bea996 100644 --- a/.mailmap +++ b/.mailmap @@ -396,6 +396,7 @@ Jens Axboe Jens Axboe Jens Axboe Jens Osterkamp +Jens Wiklander Jernej Skrabec Jesper Dangaard Brouer Jesper Dangaard Brouer diff --git a/MAINTAINERS b/MAINTAINERS index c8d4b913f26c..0e42b30877bd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -19959,7 +19959,7 @@ W: http://www.onsemi.com F: drivers/net/phy/ncn* OP-TEE DRIVER -M: Jens Wiklander +M: Jens Wiklander L: op-tee@lists.trustedfirmware.org (moderated for non-subscribers) S: Maintained F: Documentation/ABI/testing/sysfs-bus-optee-devices @@ -23305,7 +23305,7 @@ F: Documentation/devicetree/bindings/media/allwinner,sun8i-a83t-de2-rotate.yaml F: drivers/media/platform/sunxi/sun8i-rotate/ RPMB SUBSYSTEM -M: Jens Wiklander +M: Jens Wiklander L: linux-kernel@vger.kernel.org S: Supported F: drivers/misc/rpmb-core.c @@ -26223,7 +26223,7 @@ F: drivers/media/i2c/tw9910.c F: include/media/i2c/tw9910.h TEE SUBSYSTEM -M: Jens Wiklander +M: Jens Wiklander R: Sumit Garg L: op-tee@lists.trustedfirmware.org (moderated for non-subscribers) S: Maintained From 4cd5de72b6f8951cef6b45c177a582824bc13d46 Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Wed, 24 Jun 2026 02:27:43 -0700 Subject: [PATCH 0027/1054] sched_ext: Avoid flooding the log with deprecation warnings The deprecation notices for direct p->scx.slice/dsq_vtime writes and for ops->cpu_acquire/release() use plain pr_warn(), so they repeat on every scheduler (re)load and can flood the kernel log. The slice/dsq_vtime notice is the worst offender: it is emitted from the BPF verifier's btf_struct_access callback, which is re-evaluated as the verifier explores program paths, so a single scheduler load can print it many times -- hundreds of lines on some hosts, dozens within the same second. Switch both notices to pr_warn_ratelimited() so each deprecation is still reported but bursts no longer spam the log, and add the missing newline to the slice/dsq_vtime message. Signed-off-by: Breno Leitao Reviewed-by: Andrea Righi Signed-off-by: Tejun Heo --- kernel/sched/ext/ext.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/sched/ext/ext.c b/kernel/sched/ext/ext.c index 691d53fe0f64..d62b93f48a85 100644 --- a/kernel/sched/ext/ext.c +++ b/kernel/sched/ext/ext.c @@ -6988,7 +6988,7 @@ static int validate_ops(struct scx_sched *sch, const struct sched_ext_ops *ops) * run past the BPF allocation. Skip for cid-form. */ if (!sch->is_cid_type && (ops->cpu_acquire || ops->cpu_release)) - pr_warn("ops->cpu_acquire/release() are deprecated, use sched_switch TP instead\n"); + pr_warn_ratelimited("ops->cpu_acquire/release() are deprecated, use sched_switch TP instead\n"); /* * Sub-scheduler support is tied to the cid-form struct_ops. A sub-sched @@ -7806,7 +7806,7 @@ static int bpf_scx_btf_struct_access(struct bpf_verifier_log *log, off + size <= offsetofend(struct task_struct, scx.slice)) || (off >= offsetof(struct task_struct, scx.dsq_vtime) && off + size <= offsetofend(struct task_struct, scx.dsq_vtime))) { - pr_warn("sched_ext: Writing directly to p->scx.slice/dsq_vtime is deprecated, use scx_bpf_task_set_slice/dsq_vtime()"); + pr_warn_ratelimited("sched_ext: Writing directly to p->scx.slice/dsq_vtime is deprecated, use scx_bpf_task_set_slice/dsq_vtime()\n"); return SCALAR_VALUE; } From 46d65096ce8d278abf4528e254878c14ddd0b459 Mon Sep 17 00:00:00 2001 From: Doehyun Baek Date: Sat, 20 Jun 2026 12:27:51 +0000 Subject: [PATCH 0028/1054] Docs/admin-guide/cgroup-v2: fix memory.stat doc details MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix minor cgroup v2 memory.stat documentation issues. Correct the vmalloc per-node marker now that vmalloc uses the native NR_VMALLOC node stat, and document zswap_incomp as a byte-valued memory amount instead of as a page counter. Fixes: c466412c73c3 ("mm: memcontrol: switch to native NR_VMALLOC vmstat counter") Fixes: 5ad41a38c364 ("mm: zswap: add per-memcg stat for incompressible pages") Signed-off-by: Doehyun Baek Reviewed-by: Nhat Pham Acked-by: Michal Koutný Signed-off-by: Tejun Heo --- Documentation/admin-guide/cgroup-v2.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/admin-guide/cgroup-v2.rst b/Documentation/admin-guide/cgroup-v2.rst index 993446ab66d0..ce6741f78f4f 100644 --- a/Documentation/admin-guide/cgroup-v2.rst +++ b/Documentation/admin-guide/cgroup-v2.rst @@ -1570,7 +1570,7 @@ The following nested keys are defined. sock (npn) Amount of memory used in network transmission buffers - vmalloc (npn) + vmalloc Amount of memory used for vmap backed memory. shmem @@ -1735,7 +1735,7 @@ The following nested keys are defined. Number of pages written from zswap to swap. zswap_incomp - Number of incompressible pages currently stored in zswap + Amount of memory used by incompressible pages currently stored in zswap without compression. These pages could not be compressed to a size smaller than PAGE_SIZE, so they are stored as-is. From 5771e79e461e72140b752496b69275b9e6bd1a75 Mon Sep 17 00:00:00 2001 From: Andrea Righi Date: Mon, 8 Jun 2026 18:55:57 +0200 Subject: [PATCH 0029/1054] sched_ext: Reset dsq_vtime and slice when a task leaves SCX When a task leaves the BPF scheduler's control, p->scx.dsq_vtime and p->scx.slice keep whatever values they last held. The slice value is core-managed and is refilled on the next enqueue, but dsq_vtime is owned by the BPF scheduler and is never cleared by the core, so a task that leaves SCX and later returns carries a stale dsq_vtime across the round-trip. The stale values are also visible to other SCX schedulers that inspect the scx fields of non-SCX tasks. Fix this by resetting both dsq_vtime and slice in scx_disable_task(), after ops.disable(), so the BPF scheduler can still observe the task's final values and non-SCX tasks do not retain stale SCX state. Signed-off-by: Andrea Righi Signed-off-by: Tejun Heo --- kernel/sched/ext/ext.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/kernel/sched/ext/ext.c b/kernel/sched/ext/ext.c index d62b93f48a85..5efc97437c0a 100644 --- a/kernel/sched/ext/ext.c +++ b/kernel/sched/ext/ext.c @@ -3647,6 +3647,13 @@ static void scx_disable_task(struct scx_sched *sch, struct task_struct *p) SCX_CALL_OP_TASK(sch, disable, rq, p); scx_set_task_state(p, SCX_TASK_READY); + /* + * Reset the SCX-managed fields when @p leaves the BPF scheduler's + * control, after ops.disable() has observed their final values. + */ + p->scx.dsq_vtime = 0; + p->scx.slice = 0; + /* * Verify the task is not in BPF scheduler's custody. If flag * transitions are consistent, the flag should always be clear From 5c94a3ab6ed94ff1257631a49893a535098be0b6 Mon Sep 17 00:00:00 2001 From: Kuba Piecuch Date: Fri, 19 Jun 2026 13:23:59 +0000 Subject: [PATCH 0030/1054] sched_ext: Check remote rq eligibility under task's rq lock task_can_run_on_remote_rq() operates under the assumption that p->migration_disabled is stable, i.e. if the kernel observed is_migration_disabled(p) == true, then the BPF scheduler must have also been able to see this when dispatching the task, and it's the BPF scheduler's fault that it tried to dispatch a task with migration disabled to a CPU other than the task's current CPU. This assumption does not always hold. It's possible that the BPF scheduler saw is_migration_disabled(p) == false, while the kernel observes is_migration_disabled(p) == true in dispatch_to_local_dsq() -> task_can_run_on_remote_rq(). The crucial thing here is that with CONFIG_PREEMPT_RCU, migration is disabled while a task is executing a BPF program. So, if there's a situation where the BPF scheduler checks a task while it's not executing a BPF program, while the kernel checks it while it is executing one, the BPF scheduler will be killed through no fault of its own. Consider the following scenario: 1. SCX task @p is executing on CPU A and CPU A gets preempted by a higher-priority scheduling class. On entry to __schedule(), p->migration_disabled == 0. 2. In put_prev_task_scx() @p is enqueued on the BPF scheduler's internal data structures, making it available for other CPUs to dispatch. 3. CPU B enters ops.dispatch(), pops @p from the BPF scheduler's data structures, checks is_migration_disabled(p) which returns false, and dispatches @p to CPU B's local DSQ. 4. On CPU A, @p hasn't been switched out yet. Execution reaches trace_sched_switch() which enters a BPF program, as the BPF scheduler hooks into the sched_switch tracepoint to detect idle->fair transitions. On entry into the BPF program, @p disables migration. 5. CPU B enters finish_dispatch() -> dispatch_to_local_dsq() -> task_can_run_on_remote_rq() which observes is_migration_disabled(p) == true, triggering scx_error(). This all happens while holding CPU B's rq lock, so it's not synchronized with @p switching out. This patch fixes this by moving the call to task_can_run_on_remote_rq() after @p's rq lock is acquired in dispatch_to_local_dsq(). This way, we synchronize with @p switching out, since @p holds its rq lock all the way until it's switched out. Thus, any BPF programs that are called between put_prev_task_scx() and the end of the context switch are guaranteed to have finished and cannot influence p->migration_disabled. Also add a lockdep assertion in task_can_run_on_remote_rq() which ensures the task rq lock is held if enforce == true. Signed-off-by: Kuba Piecuch Reviewed-by: Andrea Righi Signed-off-by: Tejun Heo --- kernel/sched/ext/ext.c | 24 ++++++++++++++++-------- kernel/sched/ext/internal.h | 23 +++++++++++++---------- 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/kernel/sched/ext/ext.c b/kernel/sched/ext/ext.c index 5efc97437c0a..5418e6357249 100644 --- a/kernel/sched/ext/ext.c +++ b/kernel/sched/ext/ext.c @@ -2307,6 +2307,7 @@ static void move_remote_task_to_local_dsq(struct task_struct *p, u64 enq_flags, * no to the BPF scheduler initiated migrations while offline. * * The caller must ensure that @p and @rq are on different CPUs. + * If enforce == true, caller must hold @p's rq lock. */ static bool task_can_run_on_remote_rq(struct scx_sched *sch, struct task_struct *p, struct rq *rq, @@ -2314,6 +2315,14 @@ static bool task_can_run_on_remote_rq(struct scx_sched *sch, { s32 cpu = cpu_of(rq); + /* + * To prevent races with @p still running on its old CPU while switching + * out, make sure we're holding @p's rq lock so as not to risk + * erroneously killing the BPF scheduler. + */ + if (enforce) + lockdep_assert_rq_held(task_rq(p)); + WARN_ON_ONCE(task_cpu(p) == cpu); /* @@ -2581,13 +2590,6 @@ static void dispatch_to_local_dsq(struct scx_sched *sch, struct rq *rq, return; } - if (src_rq != dst_rq && - unlikely(!task_can_run_on_remote_rq(sch, p, dst_rq, true))) { - dispatch_enqueue(sch, rq, find_global_dsq(sch, task_cpu(p)), p, - enq_flags | SCX_ENQ_CLEAR_OPSS | SCX_ENQ_GDSQ_FALLBACK); - return; - } - /* * @p is on a possibly remote @src_rq which we need to lock to move the * task. If dequeue is in progress, it'd be locking @src_rq and waiting @@ -2614,6 +2616,7 @@ static void dispatch_to_local_dsq(struct scx_sched *sch, struct rq *rq, /* task_rq couldn't have changed if we're still the holding cpu */ if (likely(p->scx.holding_cpu == raw_smp_processor_id()) && !WARN_ON_ONCE(src_rq != task_rq(p))) { + bool fallback = false; /* * If @p is staying on the same rq, there's no need to go * through the full deactivate/activate cycle. Optimize by @@ -2623,6 +2626,11 @@ static void dispatch_to_local_dsq(struct scx_sched *sch, struct rq *rq, p->scx.holding_cpu = -1; dispatch_enqueue(sch, dst_rq, &dst_rq->scx.local_dsq, p, enq_flags); + } else if (unlikely(!task_can_run_on_remote_rq(sch, p, dst_rq, true))) { + p->scx.holding_cpu = -1; + fallback = true; + dispatch_enqueue(sch, src_rq, find_global_dsq(sch, task_cpu(p)), + p, enq_flags | SCX_ENQ_GDSQ_FALLBACK); } else { move_remote_task_to_local_dsq(p, enq_flags, src_rq, dst_rq); @@ -2631,7 +2639,7 @@ static void dispatch_to_local_dsq(struct scx_sched *sch, struct rq *rq, } /* if the destination CPU is idle, wake it up */ - if (sched_class_above(p->sched_class, dst_rq->curr->sched_class)) + if (!fallback && sched_class_above(p->sched_class, dst_rq->curr->sched_class)) resched_curr(dst_rq); } diff --git a/kernel/sched/ext/internal.h b/kernel/sched/ext/internal.h index 145272cb4d8a..673059fa9d72 100644 --- a/kernel/sched/ext/internal.h +++ b/kernel/sched/ext/internal.h @@ -1469,21 +1469,24 @@ static const char *scx_enable_state_str[] = { * The sched_ext core uses a "lock dancing" protocol coordinated by * p->scx.holding_cpu. When moving a task to a different rq: * - * 1. Verify task can be moved (CPU affinity, migration_disabled, etc.) - * 2. Set p->scx.holding_cpu to the current CPU - * 3. Set task state to %SCX_OPSS_NONE; dequeue waits while DISPATCHING + * 1. Set p->scx.holding_cpu to the current CPU + * 2. Set task state to %SCX_OPSS_NONE; dequeue waits while DISPATCHING * is set, so clearing DISPATCHING first prevents the circular wait * (safe to lock the rq we need) - * 4. Unlock the current CPU's rq - * 5. Lock src_rq (where the task currently lives) - * 6. Verify p->scx.holding_cpu == current CPU, if not, dequeue won the + * 3. Unlock the current CPU's rq + * 4. Lock src_rq (where the task currently lives) + * 5. Verify p->scx.holding_cpu == current CPU, if not, dequeue won the * race (dequeue clears holding_cpu to -1 when it takes the task), in * this case migration is aborted - * 7. If src_rq == dst_rq: clear holding_cpu and enqueue directly + * 6. If src_rq == dst_rq: clear holding_cpu and enqueue directly * into dst_rq's local DSQ (no lock swap needed) - * 8. Otherwise: call move_remote_task_to_local_dsq(), which releases - * src_rq, locks dst_rq, and performs the deactivate/activate - * migration cycle (dst_rq is held on return) + * 7. Otherwise, verify under src_rq lock that the task can be moved to dst_rq + * (CPU affinity, migration_disabled, etc.). If not, clear holding_cpu, + * leave the task on src_rq, and enqueue it on the fallback DSQ. + * 8. Otherwise (i.e. if the task can be moved to dst_rq), call + * move_remote_task_to_local_dsq(), which releases src_rq, locks dst_rq, + * and performs the deactivate/activate migration cycle + * (dst_rq is held on return) * 9. Unlock dst_rq and re-lock the current CPU's rq to restore * the lock state expected by the caller * From 115d1ce989747045bd7745c7ab020982660c7e42 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 24 Jun 2026 12:40:51 -1000 Subject: [PATCH 0031/1054] sched_ext: Annotate ksyncs with __rcu in alloc/free_kick_syncs() scx_kick_syncs is a per-CPU __rcu pointer, so per_cpu_ptr() returns struct scx_kick_syncs __rcu **. alloc_kick_syncs() and free_kick_syncs() stored it in a plain struct scx_kick_syncs **ksyncs, which sparse flags as an __rcu address-space mismatch. Annotate ksyncs to match. Its accesses already go through rcu_*_pointer(). Fixes: 987e00035c0e ("sched_ext: Rename pnt_seq to kick_sync") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202606122315.pbnDHP0n-lkp@intel.com/ Signed-off-by: Tejun Heo --- kernel/sched/ext/ext.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/sched/ext/ext.c b/kernel/sched/ext/ext.c index 5418e6357249..aecbb021d6d7 100644 --- a/kernel/sched/ext/ext.c +++ b/kernel/sched/ext/ext.c @@ -5687,7 +5687,7 @@ static void free_kick_syncs(void) int cpu; for_each_possible_cpu(cpu) { - struct scx_kick_syncs **ksyncs = per_cpu_ptr(&scx_kick_syncs, cpu); + struct scx_kick_syncs __rcu **ksyncs = per_cpu_ptr(&scx_kick_syncs, cpu); struct scx_kick_syncs *to_free; to_free = rcu_replace_pointer(*ksyncs, NULL, true); @@ -6668,7 +6668,7 @@ static int alloc_kick_syncs(void) * can exceed percpu allocator limits on large machines. */ for_each_possible_cpu(cpu) { - struct scx_kick_syncs **ksyncs = per_cpu_ptr(&scx_kick_syncs, cpu); + struct scx_kick_syncs __rcu **ksyncs = per_cpu_ptr(&scx_kick_syncs, cpu); struct scx_kick_syncs *new_ksyncs; WARN_ON_ONCE(rcu_access_pointer(*ksyncs)); From 0dfa1e960f86e032007882b032c5cc7d14ebe73e Mon Sep 17 00:00:00 2001 From: Sumit Gupta Date: Wed, 21 Jan 2026 16:15:34 +0530 Subject: [PATCH 0032/1054] arm64: tegra: Fix CPU compatible string to cortex-a78ae on Tegra234 The Tegra234 SoC uses Cortex-A78AE cores, not Cortex-A78. Update the compatible string for all CPU nodes to match the actual hardware. Tegra234 hardware reports: # head /proc/cpuinfo | egrep 'implementer|part' CPU implementer : 0x41 CPU part : 0xd42 Which maps to (from arch/arm64/include/asm/cputype.h): #define ARM_CPU_IMP_ARM 0x41 #define ARM_CPU_PART_CORTEX_A78AE 0xD42 Fixes: a12cf5c339b08 ("arm64: tegra: Describe Tegra234 CPU hierarchy") Signed-off-by: Sumit Gupta Signed-off-by: Thierry Reding --- arch/arm64/boot/dts/nvidia/tegra234.dtsi | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/arch/arm64/boot/dts/nvidia/tegra234.dtsi b/arch/arm64/boot/dts/nvidia/tegra234.dtsi index 8e0c51e496e2..820670dd6042 100644 --- a/arch/arm64/boot/dts/nvidia/tegra234.dtsi +++ b/arch/arm64/boot/dts/nvidia/tegra234.dtsi @@ -5355,7 +5355,7 @@ cpus { #size-cells = <0>; cpu0_0: cpu@0 { - compatible = "arm,cortex-a78"; + compatible = "arm,cortex-a78ae"; device_type = "cpu"; reg = <0x00000>; @@ -5374,7 +5374,7 @@ cpu0_0: cpu@0 { }; cpu0_1: cpu@100 { - compatible = "arm,cortex-a78"; + compatible = "arm,cortex-a78ae"; device_type = "cpu"; reg = <0x00100>; @@ -5393,7 +5393,7 @@ cpu0_1: cpu@100 { }; cpu0_2: cpu@200 { - compatible = "arm,cortex-a78"; + compatible = "arm,cortex-a78ae"; device_type = "cpu"; reg = <0x00200>; @@ -5412,7 +5412,7 @@ cpu0_2: cpu@200 { }; cpu0_3: cpu@300 { - compatible = "arm,cortex-a78"; + compatible = "arm,cortex-a78ae"; device_type = "cpu"; reg = <0x00300>; @@ -5431,7 +5431,7 @@ cpu0_3: cpu@300 { }; cpu1_0: cpu@10000 { - compatible = "arm,cortex-a78"; + compatible = "arm,cortex-a78ae"; device_type = "cpu"; reg = <0x10000>; @@ -5450,7 +5450,7 @@ cpu1_0: cpu@10000 { }; cpu1_1: cpu@10100 { - compatible = "arm,cortex-a78"; + compatible = "arm,cortex-a78ae"; device_type = "cpu"; reg = <0x10100>; @@ -5469,7 +5469,7 @@ cpu1_1: cpu@10100 { }; cpu1_2: cpu@10200 { - compatible = "arm,cortex-a78"; + compatible = "arm,cortex-a78ae"; device_type = "cpu"; reg = <0x10200>; @@ -5488,7 +5488,7 @@ cpu1_2: cpu@10200 { }; cpu1_3: cpu@10300 { - compatible = "arm,cortex-a78"; + compatible = "arm,cortex-a78ae"; device_type = "cpu"; reg = <0x10300>; @@ -5507,7 +5507,7 @@ cpu1_3: cpu@10300 { }; cpu2_0: cpu@20000 { - compatible = "arm,cortex-a78"; + compatible = "arm,cortex-a78ae"; device_type = "cpu"; reg = <0x20000>; @@ -5526,7 +5526,7 @@ cpu2_0: cpu@20000 { }; cpu2_1: cpu@20100 { - compatible = "arm,cortex-a78"; + compatible = "arm,cortex-a78ae"; device_type = "cpu"; reg = <0x20100>; @@ -5545,7 +5545,7 @@ cpu2_1: cpu@20100 { }; cpu2_2: cpu@20200 { - compatible = "arm,cortex-a78"; + compatible = "arm,cortex-a78ae"; device_type = "cpu"; reg = <0x20200>; @@ -5564,7 +5564,7 @@ cpu2_2: cpu@20200 { }; cpu2_3: cpu@20300 { - compatible = "arm,cortex-a78"; + compatible = "arm,cortex-a78ae"; device_type = "cpu"; reg = <0x20300>; From 806a66f926c2b6652aeb88983d01f25081b41a73 Mon Sep 17 00:00:00 2001 From: Sumit Gupta Date: Wed, 21 Jan 2026 16:15:35 +0530 Subject: [PATCH 0033/1054] arm64: tegra: Fix CPU1 node unit-address on Tegra264 Fix the unit-address of cpu1 node to match its reg property value. Fixes: f6d1890e5f4d ("arm64: tegra: Add device tree for Tegra264") Signed-off-by: Sumit Gupta Signed-off-by: Thierry Reding --- arch/arm64/boot/dts/nvidia/tegra264.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/nvidia/tegra264.dtsi b/arch/arm64/boot/dts/nvidia/tegra264.dtsi index 3dfdd7bb28a9..2d2cb1a3d95c 100644 --- a/arch/arm64/boot/dts/nvidia/tegra264.dtsi +++ b/arch/arm64/boot/dts/nvidia/tegra264.dtsi @@ -4070,7 +4070,7 @@ cpu0: cpu@0 { d-cache-sets = <256>; }; - cpu1: cpu@1 { + cpu1: cpu@10000 { compatible = "arm,neoverse-v3ae"; device_type = "cpu"; reg = <0x10000>; From 6dbaa4d288432c697cea47028480481b8b29bd6a Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Tue, 23 Jun 2026 21:58:34 +0800 Subject: [PATCH 0034/1054] spi: sh-msiof: abort transfers when reset times out sh_msiof_spi_reset_regs() asserts TX/RX reset and polls until the reset bits clear, but the poll result is ignored. sh_msiof_transfer_one() can therefore continue programming a transfer after the controller did not leave reset. Return the reset poll result from the helper and abort the transfer on timeout, matching the existing transfer path's error-return style. Fixes: fedd6940682a ("spi: sh-msiof: Add reset of registers before starting transfer") Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260623135834.55442-1-pengpeng@iscas.ac.cn Signed-off-by: Mark Brown --- drivers/spi/spi-sh-msiof.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index f23db85a1889..1aeab7ec0bc8 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -114,7 +114,7 @@ static irqreturn_t sh_msiof_spi_irq(int irq, void *data) return IRQ_HANDLED; } -static void sh_msiof_spi_reset_regs(struct sh_msiof_spi_priv *p) +static int sh_msiof_spi_reset_regs(struct sh_msiof_spi_priv *p) { u32 mask = SICTR_TXRST | SICTR_RXRST; u32 data; @@ -123,8 +123,8 @@ static void sh_msiof_spi_reset_regs(struct sh_msiof_spi_priv *p) data |= mask; sh_msiof_write(p, SICTR, data); - readl_poll_timeout_atomic(p->mapbase + SICTR, data, !(data & mask), 1, - 100); + return readl_poll_timeout_atomic(p->mapbase + SICTR, data, + !(data & mask), 1, 100); } static void sh_msiof_spi_set_clk_regs(struct sh_msiof_spi_priv *p, @@ -834,7 +834,9 @@ static int sh_msiof_transfer_one(struct spi_controller *ctlr, int ret; /* reset registers */ - sh_msiof_spi_reset_regs(p); + ret = sh_msiof_spi_reset_regs(p); + if (ret) + return ret; /* setup clocks (clock already enabled in chipselect()) */ if (!spi_controller_is_target(p->ctlr)) From 8522d806d84e2c3816c275ae6dd79e124c1b3dac Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Thu, 25 Jun 2026 21:29:03 +0800 Subject: [PATCH 0035/1054] ACPI: TAD: Check AC wake capability before enabling wakeup ACPI_TAD_AC_WAKE is a non-zero bit definition, so testing the macro itself is always true. As a result, every TAD device is initialized as a system wakeup device, including RTC-only devices and devices whose wake capability bits were cleared because _PRW is absent. Test the capability value returned by _GCP instead. This keeps RTC-only TAD devices usable without advertising a wakeup capability that the firmware does not provide. Fixes: 6c711fde3a1c ("ACPI: TAD: Support RTC without wakeup") Cc: All applicable Signed-off-by: Xu Rao Link: https://patch.msgid.link/961A84FF37B50665+20260625132903.2840457-1-raoxu@uniontech.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpi_tad.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/acpi_tad.c b/drivers/acpi/acpi_tad.c index cac07e997028..1a60fba59fda 100644 --- a/drivers/acpi/acpi_tad.c +++ b/drivers/acpi/acpi_tad.c @@ -852,7 +852,7 @@ static int acpi_tad_probe(struct platform_device *pdev) * runtime suspend. Everything else should be taken care of by the ACPI * PM domain callbacks. */ - if (ACPI_TAD_AC_WAKE) { + if (caps & ACPI_TAD_AC_WAKE) { device_init_wakeup(dev, true); dev_pm_set_driver_flags(dev, DPM_FLAG_SMART_SUSPEND | DPM_FLAG_MAY_SKIP_RESUME); From 31620fc1c81078746c4794e0eb6de539ab37e695 Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Tue, 16 Jun 2026 13:09:16 +0300 Subject: [PATCH 0036/1054] xhci: dbc: support runtime suspend while DbC is in enabled state Allow xHC to runtime suspend if DbC is in 'enabled' state for over 15 seconds without a connect. Idea is that every time we go to 'enabled' state we make sure DbC runtime pm usage is '1' and save a timestamp. if the event loop still finds DbC in enabled state 15 seconds later then it decrease DbC runtime pm usage by calling pm_runtime_put(). Enabled state is reached either when DbC is enabled by userspace or a connected/configured DbC is disconnected. When a connect is detected we make sure DbC usage count is 1. If DbC has been in 'enabled' state for 15 seconds and DbC usage is decreased to 0 by pm_runtime_put, then the whole xHC controller may runtime suspends to PCI D3 state if no other devices are using it DbC sysfs file will show 'suspended' when xHC is suspended and will wake up and enable DbC at cable connect, or when user writes 'enable' to the file. This patch was originally part of a larger DbC series, but dropped before the series was submitted to 7.2-rc1. The series has a locking issue in commit 520058b73ba3 ("xhci: dbc: serialize enabling and disabling dbc") which is also resolved by this patch Fixes: 520058b73ba3 ("xhci: dbc: serialize enabling and disabling dbc") Reported-by: Chaitanya Kumar Borah Closes: https://lore.kernel.org/linux-usb/9ce24ff5-efab-4089-92d7-709862d68e6d@intel.com Tested-by: Chaitanya Kumar Borah Signed-off-by: Mathias Nyman Link: https://patch.msgid.link/20260616100916.2234205-2-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- .../testing/sysfs-bus-pci-drivers-xhci_hcd | 2 +- drivers/usb/host/xhci-dbgcap.c | 60 ++++++++++++++++++- drivers/usb/host/xhci-dbgcap.h | 3 + 3 files changed, 62 insertions(+), 3 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-bus-pci-drivers-xhci_hcd b/Documentation/ABI/testing/sysfs-bus-pci-drivers-xhci_hcd index 98a8376a83d2..991765d84201 100644 --- a/Documentation/ABI/testing/sysfs-bus-pci-drivers-xhci_hcd +++ b/Documentation/ABI/testing/sysfs-bus-pci-drivers-xhci_hcd @@ -22,7 +22,7 @@ Description: Reading this attribute gives the state of the DbC. It can be one of the following states: disabled, enabled, - initialized, connected or configured. + initialized, connected, configured or suspended. What: /sys/bus/pci/drivers/xhci_hcd/.../dbc_idVendor Date: March 2023 diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c index b1cabf5582fa..48ee6a4f9e1c 100644 --- a/drivers/usb/host/xhci-dbgcap.c +++ b/drivers/usb/host/xhci-dbgcap.c @@ -646,6 +646,31 @@ static int xhci_dbc_enable_dce(struct xhci_dbc *dbc, bool enable) static void xhci_dbc_set_state(struct xhci_dbc *dbc, enum dbc_state new_state) { + if (dbc->state == new_state) + return; + + switch (new_state) { + case DS_ENABLED: + /* + * DbC pm usage is 1 here, both when moved from disconnect or + * configured states, or when setting initial DbC enable state. + * Just enable pending put + */ + dev_dbg(dbc->dev, "DbC set pending_rpm_put = 1\n"); + dbc->pending_rpm_put = 1; + break; + case DS_CONNECTED: + if (dbc->pending_rpm_put) + /* DbC pm usage still 1, just remove pending put */ + dbc->pending_rpm_put = 0; + else + /* DbC pm usage was put to 0, call get */ + pm_runtime_get(dbc->dev); + break; + default: + break; + } + dbc->state_timestamp = jiffies; dbc->state = new_state; } @@ -681,7 +706,7 @@ static int xhci_dbc_start(struct xhci_dbc *dbc) WARN_ON(!dbc); - pm_runtime_get_sync(dbc->dev); /* note this was self.controller */ + pm_runtime_get(dbc->dev); spin_lock_irqsave(&dbc->lock, flags); ret = xhci_do_dbc_start(dbc); @@ -706,6 +731,7 @@ static int xhci_dbc_start(struct xhci_dbc *dbc) static void xhci_dbc_stop(struct xhci_dbc *dbc) { unsigned long flags; + bool need_rpm_put = false; WARN_ON(!dbc); @@ -731,12 +757,20 @@ static void xhci_dbc_stop(struct xhci_dbc *dbc) spin_lock_irqsave(&dbc->lock, flags); writel(0, &dbc->regs->control); + + if (dbc->state == DS_CONNECTED || dbc->state == DS_CONFIGURED || + dbc->pending_rpm_put) + need_rpm_put = true; + + dbc->pending_rpm_put = 0; + xhci_dbc_set_state(dbc, DS_DISABLED); spin_unlock_irqrestore(&dbc->lock, flags); xhci_dbc_mem_cleanup(dbc); - pm_runtime_put(dbc->dev); /* note, was self.controller */ + if (need_rpm_put) + pm_runtime_put(dbc->dev); } static void @@ -908,6 +942,12 @@ static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc) dev_info(dbc->dev, "DbC connected\n"); } else if (!(ctrl & DBC_CTRL_DBC_ENABLE)) { dev_err(dbc->dev, "unexpected DbC disable, xHC reset?\n"); + } else if (dbc->pending_rpm_put && + time_is_before_jiffies(dbc->state_timestamp + + msecs_to_jiffies(DBC_AUTOSUSPEND_DELAY))) { + dbc->pending_rpm_put = 0; + dev_dbg(dbc->dev, "DbC Enabled state for 15 seconds, allow rpm suspend\n"); + pm_runtime_put(dbc->dev); } return EVT_DONE; @@ -1096,6 +1136,9 @@ static ssize_t dbc_show(struct device *dev, if (dbc->state >= ARRAY_SIZE(dbc_state_strings)) return sysfs_emit(buf, "unknown\n"); + if (dbc->resume_required) + return sysfs_emit(buf, "suspended\n"); + return sysfs_emit(buf, "%s\n", dbc_state_strings[dbc->state]); } @@ -1110,12 +1153,25 @@ static ssize_t dbc_store(struct device *dev, dbc = xhci->dbc; if (sysfs_streq(buf, "enable")) { + pm_runtime_get_sync(dbc->dev); + mutex_lock(&dbc->enable_mutex); + /* + * DbC may already be enabled here if xhci was suspended with + * dbc->resume_required set, and resumed by pm_runtime_get_sync() + * above. In this case we end up calling xhci_dbc_start() twice, + * second time returns an error but is harmless + */ xhci_dbc_start(dbc); + mutex_unlock(&dbc->enable_mutex); + pm_runtime_put(dbc->dev); } else if (sysfs_streq(buf, "disable")) { mutex_lock(&dbc->enable_mutex); + + dbc->resume_required = 0; xhci_dbc_stop(dbc); + mutex_unlock(&dbc->enable_mutex); } else { return -EINVAL; diff --git a/drivers/usb/host/xhci-dbgcap.h b/drivers/usb/host/xhci-dbgcap.h index df7aca8bfe99..5b18efb2c1ea 100644 --- a/drivers/usb/host/xhci-dbgcap.h +++ b/drivers/usb/host/xhci-dbgcap.h @@ -114,6 +114,8 @@ struct dbc_ep { #define DBC_POLL_INTERVAL_MAX 5000 /* milliseconds */ #define DBC_XFER_INACTIVITY_TIMEOUT 10 /* milliseconds */ #define DBC_ENUMERATION_TIMEOUT 2000 /* milliseconds */ +#define DBC_AUTOSUSPEND_DELAY 15000 /* milliseconds */ + /* * Private structure for DbC hardware state: */ @@ -166,6 +168,7 @@ struct xhci_dbc { unsigned long xfer_timestamp; unsigned long state_timestamp; unsigned resume_required:1; + unsigned pending_rpm_put:1; struct dbc_ep eps[2]; const struct dbc_driver *driver; From 6eba58568f6cc3ff8515a00b05e258d8cfb72b72 Mon Sep 17 00:00:00 2001 From: Jared Baldridge Date: Sat, 30 May 2026 18:19:48 -0400 Subject: [PATCH 0037/1054] usb: cdc_acm: Add quirk for Uniden BC125AT scanner Uniden BC125AT radio scanner has a USB interface which fails to work with the cdc_acm driver: usb 1-1: new full-speed USB device number 2 using uhci_hcd cdc_acm 1-1:1.0: Zero length descriptor references cdc_acm 1-1:1.0: probe with driver cdc_acm failed with error -22 usbcore: registered new interface driver cdc_acm Adding the NO_UNION_NORMAL quirk for the device fixes the issue: usb 1-1: new full-speed USB device number 2 using uhci_hcd cdc_acm 1-1:1.0: ttyACM0: USB ACM device usbcore: registered new interface driver cdc_acm `lsusb -v` of the device: Bus 001 Device 002: ID 1965:0017 Uniden Corporation BC125AT Negotiated speed: Full Speed (12Mbps) Device Descriptor: bLength 18 bDescriptorType 1 bcdUSB 2.00 bDeviceClass 2 Communications bDeviceSubClass 0 [unknown] bDeviceProtocol 0 bMaxPacketSize0 64 idVendor 0x1965 Uniden Corporation idProduct 0x0017 BC125AT bcdDevice 0.01 iManufacturer 1 Uniden America Corp. iProduct 2 BC125AT iSerial 3 0001 bNumConfigurations 1 Configuration Descriptor: bLength 9 bDescriptorType 2 wTotalLength 0x0030 bNumInterfaces 2 bConfigurationValue 1 iConfiguration 0 bmAttributes 0x80 (Bus Powered) MaxPower 500mA Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 0 bAlternateSetting 0 bNumEndpoints 1 bInterfaceClass 2 Communications bInterfaceSubClass 2 Abstract (modem) bInterfaceProtocol 0 iInterface 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x87 EP 7 IN bmAttributes 3 Transfer Type Interrupt Synch Type None Usage Type Data wMaxPacketSize 0x0008 1x 8 bytes bInterval 10 Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 1 bAlternateSetting 0 bNumEndpoints 2 bInterfaceClass 10 CDC Data bInterfaceSubClass 0 [unknown] bInterfaceProtocol 0 iInterface 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x81 EP 1 IN bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0040 1x 64 bytes bInterval 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x02 EP 2 OUT bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0040 1x 64 bytes bInterval 0 Device Status: 0x0000 (Bus Powered) Signed-off-by: Jared Baldridge Cc: stable Link: https://patch.msgid.link/20260530221959.612526-1-jrb@expunge.us Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 49ab02f25872..7bc5329fa3ed 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1816,6 +1816,9 @@ static const struct usb_device_id acm_ids[] = { { USB_DEVICE(0x1901, 0x0006), /* GE Healthcare Patient Monitor UI Controller */ .driver_info = DISABLE_ECHO, /* DISABLE ECHO in termios flag */ }, + { USB_DEVICE(0x1965, 0x0017), /* Uniden BC125AT */ + .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ + }, { USB_DEVICE(0x1965, 0x0018), /* Uniden UBC125XLT */ .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ }, From fc591787785b9709a0bb65a7df3ba2537d611c47 Mon Sep 17 00:00:00 2001 From: "Erich E. Hoover" Date: Tue, 2 Jun 2026 14:45:08 -0600 Subject: [PATCH 0038/1054] USB: quirks: add NO_LPM for the Samsung T5 EVO Portable SSD The Samsung T5 EVO Portable SSD (04e8:6200) exhibit two forms of link instability when USB Link Power Management is enabled: 1. The units fail to initialize properly on first detection, resulting in a lockup in the drive where it must be power cycled or the kernel will not recognize the presence of the device. 2. If used for sustained operations (small amounts of continuous data are transferred to the unit) then the unit will "hiccup" after roughly 8 hours of use and will disconnect and reconnect. This has a certain probability of triggering the first issue, but also causes mount points to become invalid since the device gets issued a new letter. Signed-off-by: Erich E. Hoover Cc: stable Link: https://patch.msgid.link/20260602204508.48856-1-erich.e.hoover@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/quirks.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 87810eff974e..80b61a799e8b 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -296,6 +296,9 @@ static const struct usb_device_id usb_quirk_list[] = { /* CarrolTouch 4500U */ { USB_DEVICE(0x04e7, 0x0030), .driver_info = USB_QUIRK_RESET_RESUME }, + /* Samsung T5 EVO Portable SSD */ + { USB_DEVICE(0x04e8, 0x6200), .driver_info = USB_QUIRK_NO_LPM }, + /* Samsung Android phone modem - ID conflict with SPH-I500 */ { USB_DEVICE(0x04e8, 0x6601), .driver_info = USB_QUIRK_CONFIG_INTF_STRINGS }, From bd728c3d9b1cc0bb0fda6a7055c5c8b55d7477b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20Lugathe=20da=20Concei=C3=A7=C3=A3o=20Alves?= Date: Wed, 3 Jun 2026 08:36:26 -0300 Subject: [PATCH 0039/1054] USB: core: add USB_QUIRK_NO_LPM for VIA Labs USB 2.0 hub MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The VIA Labs, Inc. USB 2.0 hub controller (2109:2817), found in a KVM switch, fails to enumerate high-power devices during cold boot and system restart. Applying the kernel parameter usbcore.quirks=2109:2817:k resolves the issue. Enumeration failure log: usb 1-1.2.3: device descriptor read/64, error -32 usb 1-1.2.3: Device not responding to setup address. usb 1-1.2.3: device not accepting address 11, error -71 usb 1-1.2-port3: unable to enumerate USB device Add USB_QUIRK_NO_LPM for this device. Signed-off-by: Rodrigo Lugathe da Conceição Alves Cc: stable Link: https://patch.msgid.link/20260603113626.395612-1-lugathe2@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/quirks.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 80b61a799e8b..87ee2d938bc0 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -579,6 +579,9 @@ static const struct usb_device_id usb_quirk_list[] = { /* VLI disk */ { USB_DEVICE(0x2109, 0x0711), .driver_info = USB_QUIRK_NO_LPM }, + /* VIA Labs, Inc. USB2.0 Hub */ + { USB_DEVICE(0x2109, 0x2817), .driver_info = USB_QUIRK_NO_LPM }, + /* Raydium Touchscreen */ { USB_DEVICE(0x2386, 0x3114), .driver_info = USB_QUIRK_NO_LPM }, From 2c00e09e3f9f06f8434f5ea2ee6179ce46692ee6 Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Tue, 2 Jun 2026 13:38:42 +0800 Subject: [PATCH 0040/1054] USB: storage: include US_FL_NO_SAME in quirks mask usb_stor_adjust_quirks() parses the usb-storage.quirks module parameter into a new flag set and then applies it with the quirk mask to override built-in flags. The mask is meant to cover the flags that can be overridden by the module parameter. The 'k' quirk character sets US_FL_NO_SAME, but US_FL_NO_SAME is not included in the mask. As a result, the module parameter can set US_FL_NO_SAME, but it cannot clear a built-in US_FL_NO_SAME flag by providing an override entry that omits 'k'. Add US_FL_NO_SAME to the mask so that the module parameter can override it in the same way as the other supported flags. Fixes: 8010622c86ca ("USB: UAS: introduce a quirk to set no_write_same") Cc: stable Signed-off-by: Xu Rao Reviewed-by: Alan Stern Link: https://patch.msgid.link/3BCE5880F9A45C2E+20260602053842.2920137-1-raoxu@uniontech.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index fa83fe0defe2..064c7fc8e368 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -570,7 +570,7 @@ void usb_stor_adjust_quirks(struct usb_device *udev, u64 *fflags) US_FL_INITIAL_READ10 | US_FL_WRITE_CACHE | US_FL_NO_ATA_1X | US_FL_NO_REPORT_OPCODES | US_FL_MAX_SECTORS_240 | US_FL_NO_REPORT_LUNS | - US_FL_ALWAYS_SYNC); + US_FL_ALWAYS_SYNC | US_FL_NO_SAME); p = quirks; while (*p) { From 8af6812795869a66e9b26044f455b13deecdb69c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 8 Jun 2026 16:58:03 +0200 Subject: [PATCH 0041/1054] USB: ulpi: fix memory leak on registration failure The allocated device name is never freed on early ULPI device registration failures. Fix this by initialising the device structure earlier and releasing the initial reference whenever registration fails. Fixes: 289fcff4bcdb ("usb: add bus type for USB ULPI") Cc: stable Cc: Heikki Krogerus Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260608145803.69360-1-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/common/ulpi.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/drivers/usb/common/ulpi.c b/drivers/usb/common/ulpi.c index 9b69148128e5..7e43429e996e 100644 --- a/drivers/usb/common/ulpi.c +++ b/drivers/usb/common/ulpi.c @@ -281,28 +281,24 @@ static int ulpi_register(struct device *dev, struct ulpi *ulpi) ulpi->dev.parent = dev; /* needed early for ops */ ulpi->dev.bus = &ulpi_bus; ulpi->dev.type = &ulpi_dev_type; + + device_initialize(&ulpi->dev); + dev_set_name(&ulpi->dev, "%s.ulpi", dev_name(dev)); ACPI_COMPANION_SET(&ulpi->dev, ACPI_COMPANION(dev)); ret = ulpi_of_register(ulpi); - if (ret) { - kfree(ulpi); + if (ret) return ret; - } ret = ulpi_read_id(ulpi); - if (ret) { - of_node_put(ulpi->dev.of_node); - kfree(ulpi); + if (ret) return ret; - } - ret = device_register(&ulpi->dev); - if (ret) { - put_device(&ulpi->dev); + ret = device_add(&ulpi->dev); + if (ret) return ret; - } root = debugfs_create_dir(dev_name(&ulpi->dev), ulpi_root); debugfs_create_file("regs", 0444, root, ulpi, &ulpi_regs_fops); @@ -334,9 +330,10 @@ struct ulpi *ulpi_register_interface(struct device *dev, ulpi->ops = ops; ret = ulpi_register(dev, ulpi); - if (ret) + if (ret) { + put_device(&ulpi->dev); return ERR_PTR(ret); - + } return ulpi; } From d092d7edf8faefa3e27b9fc7f0e7904b06c833a2 Mon Sep 17 00:00:00 2001 From: Andrei Kuchynski Date: Mon, 1 Jun 2026 14:28:37 +0000 Subject: [PATCH 0042/1054] usb: typec: ucsi: Invert DisplayPort role assignment The existing implementation assigned these flags backwards, configuring the partner's DisplayPort role to match the port's role instead of complementing it. This prevents proper configuration during DP altmode activation, often causing `pin_assignment` to remain 0 in `dp_altmode_configure()` and resulting in VDM negotiation failures: [ 583.328246] typec port1.1: VDM 0xff01a150 failed Additionally, the fix ensures that the `pin_assignment` sysfs attribute displays the correct values. Cc: stable Fixes: af8622f6a585 ("usb: typec: ucsi: Support for DisplayPort alt mode") Signed-off-by: Andrei Kuchynski Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/20260601142837.3240207-1-akuchynski@chromium.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/displayport.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/typec/ucsi/displayport.c b/drivers/usb/typec/ucsi/displayport.c index 67a0991a7b76..c44da2fae81f 100644 --- a/drivers/usb/typec/ucsi/displayport.c +++ b/drivers/usb/typec/ucsi/displayport.c @@ -166,12 +166,12 @@ static int ucsi_displayport_status_update(struct ucsi_dp *dp) * that Multi-function is preferred. */ if (DP_CAP_CAPABILITY(cap) & DP_CAP_UFP_D) { - dp->data.status |= DP_STATUS_CON_UFP_D; + dp->data.status |= DP_STATUS_CON_DFP_D; if (DP_CAP_UFP_D_PIN_ASSIGN(cap) & BIT(DP_PIN_ASSIGN_D)) dp->data.status |= DP_STATUS_PREFER_MULTI_FUNC; } else { - dp->data.status |= DP_STATUS_CON_DFP_D; + dp->data.status |= DP_STATUS_CON_UFP_D; if (DP_CAP_DFP_D_PIN_ASSIGN(cap) & BIT(DP_PIN_ASSIGN_D)) dp->data.status |= DP_STATUS_PREFER_MULTI_FUNC; From baa6b6068a3f2bf2ed525a1cb37975905dadc658 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Tue, 9 Jun 2026 17:29:05 +0200 Subject: [PATCH 0043/1054] usb: gadget: f_fs: Fix DMA fence leak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In ffs_dmabuf_transfer(), a ffs_dma_fence object is kmalloc'd, with the underlying dma_fence later initialized by dma_fence_init(), which sets its kref counter to 1. Then, dma_resv_add_fence() gets a second reference, and a pointer to the ffs_dma_fence is passed as the usb_request's "context" field. The dma-resv mechanism will manage the second reference, but the first reference is never properly released; the ffs_dmabuf_cleanup() function decreases the reference count, but only to balance with the reference grab in ffs_dmabuf_signal_done(). The code will then slowly leak memory as more ffs_dma_fence objects are created without being ever freed. Address this issue by transferring ownership of the fence to the DMA reservation object, by calling dma_fence_put() right after dma_resv_add_fence(). The ffs_dma_fence then gets properly discarded after being signalled. Fixes: 7b07a2a7ca02 ("usb: gadget: functionfs: Add DMABUF import interface") Cc: stable Signed-off-by: Paul Cercueil Tested-by: Nuno Sá Reviewed-by: Nuno Sá Link: https://patch.msgid.link/20260609152905.729328-1-paul@crapouillou.net Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_fs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 75912ce6ab55..7cc446502980 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -1704,6 +1704,7 @@ static int ffs_dmabuf_transfer(struct file *file, resv_dir = epfile->in ? DMA_RESV_USAGE_READ : DMA_RESV_USAGE_WRITE; dma_resv_add_fence(dmabuf->resv, &fence->base, resv_dir); + dma_fence_put(&fence->base); dma_resv_unlock(dmabuf->resv); /* Now that the dma_fence is in place, queue the transfer. */ From 81fe702ff1760da32bcd3ef4494b2a33dbeced72 Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Thu, 25 Jun 2026 12:45:47 +0800 Subject: [PATCH 0044/1054] ntfs: avoid stale runlist element dereference in MFT writeback ntfs_write_mft_block() maps each $MFT record through the $MFT data runlist. For sub-folio clusters it looks up a struct runlist_element under ni->runlist.lock, drops the lock, and later uses rl->length and rl->vcn when choosing folio_sz. That pointer is only borrowed from ni->runlist.rl. Concurrent $MFT allocation extension can merge a replacement runlist under the same lock, and ntfs_rl_realloc() can free the old backing array. If that happens between the lookup and the later folio_sz decision, writeback can dereference freed runlist storage. The buggy scenario involves two paths, with each column showing the order within that path: MFT writeback path: $MFT allocation extension: 1. Look up rl under 1. Extend the $MFT data allocation. ni->runlist.lock. 2. Publish a replacement runlist. 2. Drop ni->runlist.lock. 3. Free the old runlist array. 3. Read rl->length and rl->vcn to choose folio_sz. Compute the remaining run length while ni->runlist.lock is still held, and use that scalar after unlock. This preserves the existing folio sizing decision without carrying a borrowed runlist_element across the lock boundary. Validation reproduced this kernel report: BUG: KASAN: slab-use-after-free in ntfs_mft_writepages+0x1c8d/0x1fb0 Call Trace: dump_stack_lvl+0x66/0xa0 print_report+0xce/0x630 ? ntfs_mft_writepages+0x1c8d/0x1fb0 ? srso_alias_return_thunk+0x5/0xfbef5 ? __virt_addr_valid+0x20d/0x410 ? ntfs_mft_writepages+0x1c8d/0x1fb0 kasan_report+0xe0/0x110 ? ntfs_mft_writepages+0x1c8d/0x1fb0 ntfs_mft_writepages+0x1c8d/0x1fb0 ? __pfx_ntfs_mft_writepages+0x10/0x10 ? __pfx___mutex_unlock_slowpath+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? iput+0x92/0xa80 do_writepages+0x219/0x530 ? __pfx_do_writepages+0x10/0x10 __writeback_single_inode+0x117/0xf50 ? do_raw_spin_lock+0x130/0x270 ? __pfx_do_raw_spin_lock+0x10/0x10 ? __pfx___writeback_single_inode+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 writeback_sb_inodes+0x65b/0x1810 ? srso_alias_return_thunk+0x5/0xfbef5 ? lock_acquire+0x2b8/0x2f0 ? __pfx_writeback_sb_inodes+0x10/0x10 ? lock_release+0x1e0/0x280 ? _raw_spin_unlock+0x23/0x40 ? move_expired_inodes+0x2b8/0x850 __writeback_inodes_wb+0xf4/0x270 ? __pfx___writeback_inodes_wb+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? queue_io+0x2e4/0x410 wb_writeback+0x666/0x880 ? srso_alias_return_thunk+0x5/0xfbef5 ? __pfx_wb_writeback+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? srso_alias_return_thunk+0x5/0xfbef5 ? get_nr_dirty_inodes+0x1c/0x170 wb_workfn+0x75e/0xbb0 ? srso_alias_return_thunk+0x5/0xfbef5 ? _raw_spin_unlock_irqrestore+0x27/0x60 ? __pfx_wb_workfn+0x10/0x10 ? __pfx_debug_object_deactivate+0x10/0x10 ? lock_acquire+0x2b8/0x2f0 ? srso_alias_return_thunk+0x5/0xfbef5 ? lock_release+0x1e0/0x280 process_one_work+0x8d0/0x1870 ? __pfx_process_one_work+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 worker_thread+0x575/0xf80 ? __pfx_worker_thread+0x10/0x10 kthread+0x2e7/0x3c0 ? __pfx_kthread+0x10/0x10 ret_from_fork+0x576/0x810 ? __pfx_ret_from_fork+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? __switch_to+0x57e/0xe10 ? __switch_to_asm+0x33/0x70 ? __pfx_kthread+0x10/0x10 ret_from_fork_asm+0x1a/0x30 Allocated by task 970: kasan_save_stack+0x33/0x60 kasan_save_track+0x14/0x30 __kasan_kmalloc+0xaa/0xb0 __kvmalloc_node_noprof+0x353/0x920 ntfs_rl_realloc+0x3c/0x80 ntfs_runlists_merge+0x1212/0x3010 ntfs_mft_data_extend_allocation_nolock+0x3e0/0x1f40 ntfs_mft_record_alloc+0x1ab4/0x4f10 __ntfs_create+0x680/0x2e50 ntfs_create+0x1e6/0x3a0 path_openat+0x2b55/0x3c10 do_file_open+0x1f4/0x460 do_sys_openat2+0xde/0x170 __x64_sys_openat+0x122/0x1e0 do_syscall_64+0x115/0x6a0 entry_SYSCALL_64_after_hwframe+0x77/0x7f Freed by task 1294: kasan_save_stack+0x33/0x60 kasan_save_track+0x14/0x30 kasan_save_free_info+0x3b/0x60 __kasan_slab_free+0x5f/0x80 kfree+0x307/0x580 ntfs_rl_realloc+0x66/0x80 ntfs_runlists_merge+0x1212/0x3010 ntfs_mft_data_extend_allocation_nolock+0x3e0/0x1f40 ntfs_mft_record_alloc+0x1ab4/0x4f10 __ntfs_create+0x680/0x2e50 ntfs_create+0x1e6/0x3a0 path_openat+0x2b55/0x3c10 do_file_open+0x1f4/0x460 do_sys_openat2+0xde/0x170 __x64_sys_openat+0x122/0x1e0 do_syscall_64+0x115/0x6a0 entry_SYSCALL_64_after_hwframe+0x77/0x7f Fixes: 115380f9a2f9 ("ntfs: update mft operations") Assisted-by: Codex:gpt-5.5 Signed-off-by: Cen Zhang Reviewed-by: Hyunchul Lee Signed-off-by: Namjae Jeon --- fs/ntfs/mft.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/fs/ntfs/mft.c b/fs/ntfs/mft.c index a5019e80951b..fd20d7abd6f5 100644 --- a/fs/ntfs/mft.c +++ b/fs/ntfs/mft.c @@ -2637,7 +2637,6 @@ static int ntfs_write_mft_block(struct folio *folio, struct writeback_control *w s64 vcn = ntfs_pidx_to_cluster(vol, folio->index); s64 end_vcn = ntfs_bytes_to_cluster(vol, ni->allocated_size); unsigned int folio_sz; - struct runlist_element *rl = NULL; loff_t i_size = i_size_read(vi); ntfs_debug("Entering for inode 0x%llx, attribute type 0x%x, folio index 0x%lx.", @@ -2682,6 +2681,7 @@ static int ntfs_write_mft_block(struct folio *folio, struct writeback_control *w &tni, &ref_inos[nr_ref_inos])) { unsigned int mft_record_off = 0; s64 vcn_off = vcn; + s64 rl_len = 0; /* * The record should be written. If a locked ntfs @@ -2701,8 +2701,12 @@ static int ntfs_write_mft_block(struct folio *folio, struct writeback_control *w } if (vol->cluster_size < folio_size(folio)) { + struct runlist_element *rl; + down_write(&ni->runlist.lock); rl = ntfs_attr_vcn_to_rl(ni, vcn_off, &lcn); + if (!IS_ERR(rl)) + rl_len = rl->length - (vcn_off - rl->vcn); up_write(&ni->runlist.lock); if (IS_ERR(rl) || lcn < 0) { err = -EIO; @@ -2733,7 +2737,7 @@ static int ntfs_write_mft_block(struct folio *folio, struct writeback_control *w if (vol->cluster_size == NTFS_BLOCK_SIZE && (mft_record_off || - (rl && rl->length - (vcn_off - rl->vcn) == 1) || + rl_len == 1 || mft_ofs + NTFS_BLOCK_SIZE >= PAGE_SIZE)) folio_sz = NTFS_BLOCK_SIZE; else From 3348f444a4ce43dd5c2d1aa41634cb6eff33aa64 Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Mon, 22 Jun 2026 13:26:27 +0800 Subject: [PATCH 0045/1054] usb: cdnsp: fix stream context array leak in cdnsp_alloc_stream_info() cdnsp_alloc_stream_info() allocates stream_info->stream_ctx_array with cdnsp_alloc_stream_ctx(). If a later stream ring allocation or stream mapping update fails, the error path frees the allocated stream rings and stream_rings array, but leaves stream_ctx_array allocated. Free the stream context array before falling through to the stream_rings cleanup path. Fixes: 3d82904559f4 ("usb: cdnsp: cdns3 Add main part of Cadence USBSSP DRD Driver") Cc: stable Signed-off-by: Haoxiang Li Acked-by: Peter Chen Link: https://patch.msgid.link/20260622052627.696373-1-haoxiang_li2024@163.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/cdns3/cdnsp-mem.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/cdns3/cdnsp-mem.c b/drivers/usb/cdns3/cdnsp-mem.c index 5d8cdc91927d..83f3384b735d 100644 --- a/drivers/usb/cdns3/cdnsp-mem.c +++ b/drivers/usb/cdns3/cdnsp-mem.c @@ -631,6 +631,8 @@ int cdnsp_alloc_stream_info(struct cdnsp_device *pdev, } } + cdnsp_free_stream_ctx(pdev, pep); + cleanup_stream_rings: kfree(pep->stream_info.stream_rings); From 3137b243c93982fe3460335e12f9247739766e10 Mon Sep 17 00:00:00 2001 From: Tyler Baker Date: Tue, 9 Jun 2026 15:36:34 -0400 Subject: [PATCH 0046/1054] usb: gadget: f_fs: initialize reset_work at allocation time MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ffs_fs_kill_sb() unconditionally calls cancel_work_sync() on ffs->reset_work when a functionfs instance is unmounted: ffs_data_reset(ffs); cancel_work_sync(&ffs->reset_work); However ffs->reset_work is only ever initialized via INIT_WORK() in ffs_func_set_alt() and ffs_func_disable(), and only on the FFS_DEACTIVATED path. That state is reached solely by ffs_data_closed() when the instance is mounted with the "no_disconnect" option, so for the common case (no "no_disconnect", or mounted and unmounted without ever being deactivated) reset_work is never initialized. ffs_data_new() allocates the ffs_data with kzalloc_obj() and does not initialize reset_work, and ffs_data_reset()/ffs_data_clear() do not touch it either, so reset_work.func is left NULL. cancel_work_sync() on such a work then trips the WARN_ON(!work->func) guard in __flush_work(): WARNING: kernel/workqueue.c:4301 at __flush_work+0x330/0x360, CPU#3: umount Call trace: __flush_work cancel_work_sync ffs_fs_kill_sb [usb_f_fs] deactivate_locked_super deactivate_super cleanup_mnt __cleanup_mnt task_work_run exit_to_user_mode_loop el0_svc On older kernels cancel_work_sync() on a zero-initialized work struct was a silent no-op, which hid the missing initialization. Initialize reset_work once in ffs_data_new() so it is always valid for the lifetime of the ffs_data, and drop the now-redundant INIT_WORK() calls from the two deactivation paths. Fixes: 18d6b32fca38 ("usb: gadget: f_fs: add "no_disconnect" mode") Cc: stable Signed-off-by: Tyler Baker Cc: Loic Poulain Cc: Dmitry Baryshkov Cc: Srinivas Kandagatla Tested-by: Loic Poulain Reviewed-by: Peter Chen Acked-by: MichaÅ‚ Nazarewicz Link: https://patch.msgid.link/20260609193635.2284430-1-tyler.baker@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_fs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 7cc446502980..745c44d251f7 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -288,6 +288,7 @@ static int ffs_acquire_dev(const char *dev_name, struct ffs_data *ffs_data); static void ffs_release_dev(struct ffs_dev *ffs_dev); static int ffs_ready(struct ffs_data *ffs); static void ffs_closed(struct ffs_data *ffs); +static void ffs_reset_work(struct work_struct *work); /* Misc helper functions ****************************************************/ @@ -2222,6 +2223,7 @@ static struct ffs_data *ffs_data_new(const char *dev_name) init_waitqueue_head(&ffs->ev.waitq); init_waitqueue_head(&ffs->wait); init_completion(&ffs->ep0req_completion); + INIT_WORK(&ffs->reset_work, ffs_reset_work); /* XXX REVISIT need to update it in some places, or do we? */ ffs->ev.can_stall = 1; @@ -3776,7 +3778,6 @@ static int ffs_func_set_alt(struct usb_function *f, if (ffs->state == FFS_DEACTIVATED) { ffs->state = FFS_CLOSING; spin_unlock_irqrestore(&ffs->eps_lock, flags); - INIT_WORK(&ffs->reset_work, ffs_reset_work); schedule_work(&ffs->reset_work); return -ENODEV; } @@ -3807,7 +3808,6 @@ static void ffs_func_disable(struct usb_function *f) if (ffs->state == FFS_DEACTIVATED) { ffs->state = FFS_CLOSING; spin_unlock_irqrestore(&ffs->eps_lock, flags); - INIT_WORK(&ffs->reset_work, ffs_reset_work); schedule_work(&ffs->reset_work); return; } From bc0e4f16c44e50daa0b1ea729934baa3b4815dee Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Sat, 23 May 2026 19:05:23 +0200 Subject: [PATCH 0047/1054] USB: iowarrior: fix use-after-free on disconnect Submitted write URBs are not stopped on close() and therefore need to be stopped unconditionally on disconnect() to avoid use-after-free in the completion handler. Fixes: b5f8d46867ca ("USB: iowarrior: fix use-after-free after driver unbind") Fixes: 946b960d13c1 ("USB: add driver for iowarrior devices.") Reported-by: syzbot+ad2aac2febc3bedf0962@syzkaller.appspotmail.com Link: https://lore.kernel.org/all/6a0ce39b.170a0220.39a13.0007.GAE@google.com/ Cc: stable Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260523170523.1074563-1-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/iowarrior.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c index 22504c0a2841..88c6d1d1da11 100644 --- a/drivers/usb/misc/iowarrior.c +++ b/drivers/usb/misc/iowarrior.c @@ -905,13 +905,15 @@ static void iowarrior_disconnect(struct usb_interface *interface) /* prevent device read, write and ioctl */ dev->present = 0; + /* write urbs are not stopped on close() so kill unconditionally */ + usb_kill_anchored_urbs(&dev->submitted); + if (dev->opened) { /* There is a process that holds a filedescriptor to the device , so we only shutdown read-/write-ops going on. Deleting the device is postponed until close() was called. */ usb_kill_urb(dev->int_in_urb); - usb_kill_anchored_urbs(&dev->submitted); wake_up_interruptible(&dev->read_wait); wake_up_interruptible(&dev->write_wait); mutex_unlock(&dev->mutex); From c602254ba4c10f60a73cd99d147874f86a3f485c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 22 Jun 2026 17:26:09 +0200 Subject: [PATCH 0048/1054] USB: iowarrior: fix use-after-free on disconnect race mutex_unlock() may access the mutex structure after releasing the lock and therefore cannot be used to manage lifetime of objects directly (unlike spinlocks and refcounts). [1][2] Use a kref to release the driver data to avoid use-after-free in mutex_unlock() when release() races with disconnect(). [1] a51749ab34d9 ("locking/mutex: Document that mutex_unlock() is non-atomic") [2] 2b9d9e0a9ba0 ("locking/mutex: Clarify that mutex_unlock(), and most other sleeping locks, can still use the lock object after it's unlocked") Fixes: 946b960d13c1 ("USB: add driver for iowarrior devices.") Cc: stable Reported-by: Yue Sun Link: https://lore.kernel.org/r/20260618080204.38322-1-samsun1006219@gmail.com Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260622152612.116422-2-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/iowarrior.c | 57 +++++++++++++++--------------------- 1 file changed, 24 insertions(+), 33 deletions(-) diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c index 88c6d1d1da11..de2b236ef903 100644 --- a/drivers/usb/misc/iowarrior.c +++ b/drivers/usb/misc/iowarrior.c @@ -72,6 +72,7 @@ static struct usb_driver iowarrior_driver; /* Structure to hold all of our device specific stuff */ struct iowarrior { + struct kref kref; struct mutex mutex; /* locks this structure */ struct usb_device *udev; /* save off the usb device pointer */ struct usb_interface *interface; /* the interface for this device */ @@ -240,8 +241,10 @@ static void iowarrior_write_callback(struct urb *urb) /* * iowarrior_delete */ -static inline void iowarrior_delete(struct iowarrior *dev) +static inline void iowarrior_delete(struct kref *kref) { + struct iowarrior *dev = container_of(kref, struct iowarrior, kref); + kfree(dev->int_in_buffer); usb_free_urb(dev->int_in_urb); kfree(dev->read_queue); @@ -637,6 +640,9 @@ static int iowarrior_open(struct inode *inode, struct file *file) } /* increment our usage count for the driver */ ++dev->opened; + + kref_get(&dev->kref); + /* save our object in the file's private structure */ file->private_data = dev; retval = 0; @@ -652,7 +658,6 @@ static int iowarrior_open(struct inode *inode, struct file *file) static int iowarrior_release(struct inode *inode, struct file *file) { struct iowarrior *dev; - int retval = 0; dev = file->private_data; if (!dev) @@ -660,29 +665,18 @@ static int iowarrior_release(struct inode *inode, struct file *file) /* lock our device */ mutex_lock(&dev->mutex); + dev->opened = 0; /* we're closing now */ - if (dev->opened <= 0) { - retval = -ENODEV; /* close called more than once */ - mutex_unlock(&dev->mutex); - } else { - dev->opened = 0; /* we're closing now */ - retval = 0; - if (dev->present) { - /* - The device is still connected so we only shutdown - pending read-/write-ops. - */ - usb_kill_urb(dev->int_in_urb); - wake_up_interruptible(&dev->read_wait); - wake_up_interruptible(&dev->write_wait); - mutex_unlock(&dev->mutex); - } else { - /* The device was unplugged, cleanup resources */ - mutex_unlock(&dev->mutex); - iowarrior_delete(dev); - } + if (dev->present) { + usb_kill_urb(dev->int_in_urb); + wake_up_interruptible(&dev->read_wait); + wake_up_interruptible(&dev->write_wait); } - return retval; + mutex_unlock(&dev->mutex); + + kref_put(&dev->kref, iowarrior_delete); + + return 0; } static __poll_t iowarrior_poll(struct file *file, poll_table * wait) @@ -767,6 +761,7 @@ static int iowarrior_probe(struct usb_interface *interface, if (!dev) return retval; + kref_init(&dev->kref); mutex_init(&dev->mutex); atomic_set(&dev->intr_idx, 0); @@ -885,7 +880,8 @@ static int iowarrior_probe(struct usb_interface *interface, return retval; error: - iowarrior_delete(dev); + kref_put(&dev->kref, iowarrior_delete); + return retval; } @@ -909,19 +905,14 @@ static void iowarrior_disconnect(struct usb_interface *interface) usb_kill_anchored_urbs(&dev->submitted); if (dev->opened) { - /* There is a process that holds a filedescriptor to the device , - so we only shutdown read-/write-ops going on. - Deleting the device is postponed until close() was called. - */ usb_kill_urb(dev->int_in_urb); wake_up_interruptible(&dev->read_wait); wake_up_interruptible(&dev->write_wait); - mutex_unlock(&dev->mutex); - } else { - /* no process is using the device, cleanup now */ - mutex_unlock(&dev->mutex); - iowarrior_delete(dev); } + + mutex_unlock(&dev->mutex); + + kref_put(&dev->kref, iowarrior_delete); } /* usb specific object needed to register this driver with the usb subsystem */ From ff002c153f9722caece3983cc23dc4d9d4652cb4 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 22 Jun 2026 17:26:10 +0200 Subject: [PATCH 0049/1054] USB: idmouse: fix use-after-free on disconnect race mutex_unlock() may access the mutex structure after releasing the lock and therefore cannot be used to manage lifetime of objects directly (unlike spinlocks and refcounts). [1][2] Use a kref to release the driver data to avoid use-after-free in mutex_unlock() when release() races with disconnect(). [1] a51749ab34d9 ("locking/mutex: Document that mutex_unlock() is non-atomic") [2] 2b9d9e0a9ba0 ("locking/mutex: Clarify that mutex_unlock(), and most other sleeping locks, can still use the lock object after it's unlocked") Fixes: 54d2bc068fd2 ("USB: fix locking in idmouse") Cc: stable@vger.kernel.org # 2.6.24 Cc: Oliver Neukum Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260622152612.116422-3-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/idmouse.c | 45 +++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/drivers/usb/misc/idmouse.c b/drivers/usb/misc/idmouse.c index 0f6b3464c2d6..3e37adf2bb57 100644 --- a/drivers/usb/misc/idmouse.c +++ b/drivers/usb/misc/idmouse.c @@ -63,6 +63,7 @@ MODULE_DEVICE_TABLE(usb, idmouse_table); /* structure to hold all of our device specific stuff */ struct usb_idmouse { + struct kref kref; struct usb_device *udev; /* save off the usb device pointer */ struct usb_interface *interface; /* the interface for this device */ @@ -209,8 +210,10 @@ static int idmouse_resume(struct usb_interface *intf) return 0; } -static inline void idmouse_delete(struct usb_idmouse *dev) +static inline void idmouse_delete(struct kref *kref) { + struct usb_idmouse *dev = container_of(kref, struct usb_idmouse, kref); + kfree(dev->bulk_in_buffer); kfree(dev); } @@ -254,6 +257,8 @@ static int idmouse_open(struct inode *inode, struct file *file) /* increment our usage count for the driver */ ++dev->open; + kref_get(&dev->kref); + /* save our object in the file's private structure */ file->private_data = dev; @@ -277,16 +282,11 @@ static int idmouse_release(struct inode *inode, struct file *file) /* lock our device */ mutex_lock(&dev->lock); - --dev->open; + mutex_unlock(&dev->lock); + + kref_put(&dev->kref, idmouse_delete); - if (!dev->present) { - /* the device was unplugged before the file was released */ - mutex_unlock(&dev->lock); - idmouse_delete(dev); - } else { - mutex_unlock(&dev->lock); - } return 0; } @@ -334,6 +334,7 @@ static int idmouse_probe(struct usb_interface *interface, if (dev == NULL) return -ENOMEM; + kref_init(&dev->kref); mutex_init(&dev->lock); dev->udev = udev; dev->interface = interface; @@ -342,8 +343,7 @@ static int idmouse_probe(struct usb_interface *interface, result = usb_find_bulk_in_endpoint(iface_desc, &endpoint); if (result) { dev_err(&interface->dev, "Unable to find bulk-in endpoint.\n"); - idmouse_delete(dev); - return result; + goto err_put_kref; } dev->orig_bi_size = usb_endpoint_maxp(endpoint); @@ -351,8 +351,8 @@ static int idmouse_probe(struct usb_interface *interface, dev->bulk_in_endpointAddr = endpoint->bEndpointAddress; dev->bulk_in_buffer = kmalloc(IMGSIZE + dev->bulk_in_size, GFP_KERNEL); if (!dev->bulk_in_buffer) { - idmouse_delete(dev); - return -ENOMEM; + result = -ENOMEM; + goto err_put_kref; } /* allow device read, write and ioctl */ @@ -364,14 +364,18 @@ static int idmouse_probe(struct usb_interface *interface, if (result) { /* something prevented us from registering this device */ dev_err(&interface->dev, "Unable to allocate minor number.\n"); - idmouse_delete(dev); - return result; + goto err_put_kref; } /* be noisy */ dev_info(&interface->dev,"%s now attached\n",DRIVER_DESC); return 0; + +err_put_kref: + kref_put(&dev->kref, idmouse_delete); + + return result; } static void idmouse_disconnect(struct usb_interface *interface) @@ -387,14 +391,9 @@ static void idmouse_disconnect(struct usb_interface *interface) /* prevent device read, write and ioctl */ dev->present = 0; - /* if the device is opened, idmouse_release will clean this up */ - if (!dev->open) { - mutex_unlock(&dev->lock); - idmouse_delete(dev); - } else { - /* unlock */ - mutex_unlock(&dev->lock); - } + mutex_unlock(&dev->lock); + + kref_put(&dev->kref, idmouse_delete); dev_info(&interface->dev, "disconnected\n"); } From 19bdfc7b3c179331eafa423d87e1336f43bbfeb8 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 22 Jun 2026 17:26:11 +0200 Subject: [PATCH 0050/1054] USB: ldusb: fix use-after-free on disconnect race mutex_unlock() may access the mutex structure after releasing the lock and therefore cannot be used to manage lifetime of objects directly (unlike spinlocks and refcounts). [1][2] Use a kref to release the driver data to avoid use-after-free in mutex_unlock() when release() races with disconnect(). [1] a51749ab34d9 ("locking/mutex: Document that mutex_unlock() is non-atomic") [2] 2b9d9e0a9ba0 ("locking/mutex: Clarify that mutex_unlock(), and most other sleeping locks, can still use the lock object after it's unlocked") Fixes: ce0d7d3f575f ("usb: ldusb: ld_usb semaphore to mutex") Cc: stable Cc: Daniel Walker Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260622152612.116422-4-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/ldusb.c | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/drivers/usb/misc/ldusb.c b/drivers/usb/misc/ldusb.c index c74f142f6637..71132a15e771 100644 --- a/drivers/usb/misc/ldusb.c +++ b/drivers/usb/misc/ldusb.c @@ -150,6 +150,7 @@ MODULE_PARM_DESC(min_interrupt_out_interval, "Minimum interrupt out interval in /* Structure to hold all of our device specific stuff */ struct ld_usb { + struct kref kref; struct mutex mutex; /* locks this structure */ struct usb_interface *intf; /* save off the usb interface pointer */ unsigned long disconnected:1; @@ -201,8 +202,10 @@ static void ld_usb_abort_transfers(struct ld_usb *dev) /* * ld_usb_delete */ -static void ld_usb_delete(struct ld_usb *dev) +static void ld_usb_delete(struct kref *kref) { + struct ld_usb *dev = container_of(kref, struct ld_usb, kref); + /* free data structures */ usb_free_urb(dev->interrupt_in_urb); usb_free_urb(dev->interrupt_out_urb); @@ -355,6 +358,8 @@ static int ld_usb_open(struct inode *inode, struct file *file) goto unlock_exit; } + kref_get(&dev->kref); + /* save device in the file's private structure */ file->private_data = dev; @@ -381,17 +386,8 @@ static int ld_usb_release(struct inode *inode, struct file *file) mutex_lock(&dev->mutex); - if (dev->open_count != 1) { - retval = -ENODEV; + if (dev->disconnected) goto unlock_exit; - } - if (dev->disconnected) { - /* the device was unplugged before the file was released */ - mutex_unlock(&dev->mutex); - /* unlock here as ld_usb_delete frees dev */ - ld_usb_delete(dev); - goto exit; - } /* wait until write transfer is finished */ if (dev->interrupt_out_busy) @@ -401,7 +397,7 @@ static int ld_usb_release(struct inode *inode, struct file *file) unlock_exit: mutex_unlock(&dev->mutex); - + kref_put(&dev->kref, ld_usb_delete); exit: return retval; } @@ -659,6 +655,8 @@ static int ld_usb_probe(struct usb_interface *intf, const struct usb_device_id * dev = kzalloc_obj(*dev); if (!dev) goto exit; + + kref_init(&dev->kref); mutex_init(&dev->mutex); spin_lock_init(&dev->rbsl); dev->intf = intf; @@ -740,7 +738,7 @@ static int ld_usb_probe(struct usb_interface *intf, const struct usb_device_id * return retval; error: - ld_usb_delete(dev); + kref_put(&dev->kref, ld_usb_delete); return retval; } @@ -768,18 +766,18 @@ static void ld_usb_disconnect(struct usb_interface *intf) mutex_lock(&dev->mutex); - /* if the device is not opened, then we clean up right now */ - if (!dev->open_count) { - mutex_unlock(&dev->mutex); - ld_usb_delete(dev); - } else { - dev->disconnected = 1; + dev->disconnected = 1; + + if (dev->open_count) { /* wake up pollers */ wake_up_interruptible_all(&dev->read_wait); wake_up_interruptible_all(&dev->write_wait); - mutex_unlock(&dev->mutex); } + mutex_unlock(&dev->mutex); + + kref_put(&dev->kref, ld_usb_delete); + dev_info(&intf->dev, "LD USB Device #%d now disconnected\n", (minor - USB_LD_MINOR_BASE)); } From 62fc8eb1b1481051f7bab4aa93d79809053dd09f Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 22 Jun 2026 17:26:12 +0200 Subject: [PATCH 0051/1054] USB: legousbtower: fix use-after-free on disconnect race mutex_unlock() may access the mutex structure after releasing the lock and therefore cannot be used to manage lifetime of objects directly (unlike spinlocks and refcounts). [1][2] Use a kref to release the driver data to avoid use-after-free in mutex_unlock() when release() races with disconnect(). [1] a51749ab34d9 ("locking/mutex: Document that mutex_unlock() is non-atomic") [2] 2b9d9e0a9ba0 ("locking/mutex: Clarify that mutex_unlock(), and most other sleeping locks, can still use the lock object after it's unlocked") Fixes: 18bcbcfe9ca2 ("USB: misc: legousbtower: semaphore to mutex") Cc: stable Cc: Daniel Walker Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260622152612.116422-5-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/legousbtower.c | 37 +++++++++++++++++---------------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c index 052ffc2e71ee..18dd4115befb 100644 --- a/drivers/usb/misc/legousbtower.c +++ b/drivers/usb/misc/legousbtower.c @@ -185,6 +185,7 @@ MODULE_DEVICE_TABLE(usb, tower_table); /* Structure to hold all of our device specific stuff */ struct lego_usb_tower { + struct kref kref; struct mutex lock; /* locks this structure */ struct usb_device *udev; /* save off the usb device pointer */ unsigned char minor; /* the starting minor number for this device */ @@ -220,7 +221,6 @@ struct lego_usb_tower { /* local function prototypes */ static ssize_t tower_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos); static ssize_t tower_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos); -static inline void tower_delete(struct lego_usb_tower *dev); static int tower_open(struct inode *inode, struct file *file); static int tower_release(struct inode *inode, struct file *file); static __poll_t tower_poll(struct file *file, poll_table *wait); @@ -286,8 +286,10 @@ static inline void lego_usb_tower_debug_data(struct device *dev, /* * tower_delete */ -static inline void tower_delete(struct lego_usb_tower *dev) +static inline void tower_delete(struct kref *kref) { + struct lego_usb_tower *dev = container_of(kref, struct lego_usb_tower, kref); + /* free data structures */ usb_free_urb(dev->interrupt_in_urb); usb_free_urb(dev->interrupt_out_urb); @@ -381,6 +383,8 @@ static int tower_open(struct inode *inode, struct file *file) dev->open_count = 1; + kref_get(&dev->kref); + unlock_exit: mutex_unlock(&dev->lock); @@ -404,14 +408,8 @@ static int tower_release(struct inode *inode, struct file *file) mutex_lock(&dev->lock); - if (dev->disconnected) { - /* the device was unplugged before the file was released */ - - /* unlock here as tower_delete frees dev */ - mutex_unlock(&dev->lock); - tower_delete(dev); - goto exit; - } + if (dev->disconnected) + goto out_unlock; /* wait until write transfer is finished */ if (dev->interrupt_out_busy) { @@ -425,7 +423,9 @@ static int tower_release(struct inode *inode, struct file *file) dev->open_count = 0; +out_unlock: mutex_unlock(&dev->lock); + kref_put(&dev->kref, tower_delete); exit: return retval; } @@ -752,6 +752,7 @@ static int tower_probe(struct usb_interface *interface, const struct usb_device_ if (!dev) goto exit; + kref_init(&dev->kref); mutex_init(&dev->lock); dev->udev = usb_get_dev(udev); spin_lock_init(&dev->read_buffer_lock); @@ -828,7 +829,7 @@ static int tower_probe(struct usb_interface *interface, const struct usb_device_ return retval; error: - tower_delete(dev); + kref_put(&dev->kref, tower_delete); return retval; } @@ -856,18 +857,18 @@ static void tower_disconnect(struct usb_interface *interface) mutex_lock(&dev->lock); - /* if the device is not opened, then we clean up right now */ - if (!dev->open_count) { - mutex_unlock(&dev->lock); - tower_delete(dev); - } else { - dev->disconnected = 1; + dev->disconnected = 1; + + if (dev->open_count) { /* wake up pollers */ wake_up_interruptible_all(&dev->read_wait); wake_up_interruptible_all(&dev->write_wait); - mutex_unlock(&dev->lock); } + mutex_unlock(&dev->lock); + + kref_put(&dev->kref, tower_delete); + dev_info(&interface->dev, "LEGO USB Tower #%d now disconnected\n", (minor - LEGO_USB_TOWER_MINOR_BASE)); } From 7b681dd5fbf60b24a13c14661e5b7735759fb491 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Mon, 22 Jun 2026 22:08:03 +0000 Subject: [PATCH 0052/1054] usb: typec: tcpm: Validate SVID index in svdm_consume_modes() In svdm_consume_modes(), the SVID value is read from pmdata->svids using pmdata->svid_index as an array index without bounds validation: paltmode->svid = pmdata->svids[pmdata->svid_index]; If pmdata->svid_index is driven beyond SVID_DISCOVERY_MAX (16), it results in an out-of-bounds read of the pmdata->svids array. Because pd_mode_data is embedded inside struct tcpm_port, indexing past svids reads into adjacent fields. In particular: - At index 16, it reads the altmodes count. - At index 18 and beyond, it reads into altmode_desc[], which contains partner-supplied SVDM Discovery Modes VDOs. By injecting a chosen SVID into altmode_desc[0].vdo and driving svid_index to 20, the partner can force paltmode->svid to be loaded with an arbitrary, partner- chosen SVID, which is then registered via typec_partner_register_altmode(). Fix this by validating that pmdata->svid_index is non-negative and strictly less than pmdata->nsvids before accessing the pmdata->svids array inside svdm_consume_modes(). Assisted-by: Antigravity:gemini-3.5-flash Fixes: 4ab8c18d4d67 ("usb: typec: Register a device for every mode") Cc: stable Signed-off-by: Badhri Jagan Sridharan Reviewed-by: RD Babiera Acked-by: Heikki Krogerus Link: https://patch.msgid.link/20260622220803.305750-1-badhri@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpm.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 7ef746a90a17..bc531923b1ca 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -2000,6 +2000,11 @@ static void svdm_consume_modes(struct tcpm_port *port, const u32 *p, int cnt, return; } + if (pmdata->svid_index < 0 || pmdata->svid_index >= pmdata->nsvids) { + tcpm_log(port, "Invalid SVID index %d", pmdata->svid_index); + return; + } + for (i = 1; i < cnt; i++) { if (pmdata->altmodes >= ALTMODE_DISCOVERY_MAX) { /* Already logged in svdm_consume_svids() */ From 1f0bdc2884b67de337215079bba166df0cdf4ac5 Mon Sep 17 00:00:00 2001 From: Fan Wu Date: Tue, 16 Jun 2026 13:20:11 +0000 Subject: [PATCH 0053/1054] usb: typec: ucsi: ccg: Fix use-after-free of ucsi on remove The threaded IRQ handler ccg_irq_handler() calls ucsi_notify_common(), which on a connector-change event calls ucsi_connector_change() and schedules connector work. In ucsi_ccg_remove(), ucsi_destroy() frees uc->ucsi (kfree) before free_irq() is called, so a handler invocation already in flight may access the freed object after ucsi_destroy(). CPU 0 (remove) | CPU 1 (threaded IRQ) ucsi_destroy(uc->ucsi) | ccg_irq_handler() kfree(ucsi) // FREE | ucsi_notify_common(uc->ucsi) // USE Move free_irq() before ucsi_destroy() in the remove path. It is kept after ucsi_unregister(): ucsi_unregister() cancels connector work whose handler issues GET_CONNECTOR_STATUS through ucsi_send_command_common(), which waits for a completion that is signalled from the IRQ handler, so the IRQ must stay active until that work has been cancelled. The probe error path already orders free_irq() before ucsi_destroy(). This bug was found by static analysis. Fixes: e32fd989ac1c ("usb: typec: ucsi: ccg: Move to the new API") Cc: stable Signed-off-by: Fan Wu Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/20260616132011.103279-1-fanwu01@zju.edu.cn Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi_ccg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c index d46ca942026e..91c2958a708c 100644 --- a/drivers/usb/typec/ucsi/ucsi_ccg.c +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c @@ -1521,8 +1521,8 @@ static void ucsi_ccg_remove(struct i2c_client *client) cancel_work_sync(&uc->work); pm_runtime_disable(uc->dev); ucsi_unregister(uc->ucsi); - ucsi_destroy(uc->ucsi); free_irq(uc->irq, uc); + ucsi_destroy(uc->ucsi); } static const struct of_device_id ucsi_ccg_of_match_table[] = { From 82cfd4739011bdc7e87b5d585703427e89ddfaa5 Mon Sep 17 00:00:00 2001 From: Neill Kapron Date: Fri, 19 Jun 2026 04:06:03 +0000 Subject: [PATCH 0054/1054] usb: gadget: f_fs: Initialize epfile->in early to fix endpoint direction checks When parsing endpoint descriptors, ffs_data_got_descs() generates the eps_addrmap which contains the endpoint direction. However, epfile->in was previously only populated in ffs_func_eps_enable() which executes upon USB host connection. As a result, early userspace ioctls like FUNCTIONFS_DMABUF_ATTACH that run before the host connects would see epfile->in as 0, leading to incorrect DMA directions. By moving the initialization to ffs_epfiles_create(), epfile->in is accurate before userspace opens the endpoint files. Fixes: 7b07a2a7ca02 ("usb: gadget: functionfs: Add DMABUF import interface") Cc: stable Assisted-by: Antigravity:gemini-3.1-pro Signed-off-by: Neill Kapron Link: https://patch.msgid.link/20260619040609.4010746-2-nkapron@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_fs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 745c44d251f7..d9b95fb830df 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -2367,6 +2367,7 @@ static int ffs_epfiles_create(struct ffs_data *ffs) sprintf(epfile->name, "ep%02x", ffs->eps_addrmap[i]); else sprintf(epfile->name, "ep%u", i); + epfile->in = (ffs->eps_addrmap[i] & USB_ENDPOINT_DIR_MASK) ? 1 : 0; err = ffs_sb_create_file(ffs->sb, epfile->name, epfile, &ffs_epfile_operations); if (err) { @@ -2456,7 +2457,6 @@ static int ffs_func_eps_enable(struct ffs_function *func) ret = usb_ep_enable(ep->ep); if (!ret) { epfile->ep = ep; - epfile->in = usb_endpoint_dir_in(ep->ep->desc); epfile->isoc = usb_endpoint_xfer_isoc(ep->ep->desc); } else { break; From 8bdcf96eb135aebacac319667f87db034fb38406 Mon Sep 17 00:00:00 2001 From: Neill Kapron Date: Fri, 19 Jun 2026 04:06:04 +0000 Subject: [PATCH 0055/1054] usb: gadget: f_fs: Tie read_buffer lifetime to ffs_epfile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, ffs_epfile_release unconditionally frees the endpoint's read_buffer when a file descriptor is closed. If userspace explicitly opens the endpoint multiple times and closes one, the read_buffer is destroyed. This can lead to silent data loss if other file descriptors are still actively reading from the endpoint. By tying the lifetime of the read_buffer to the ffs_epfile structure itself (which is destroyed when the functionfs instance is torn down in ffs_epfiles_destroy), we eliminate the brittle dependency on open/release calls while correctly matching the conceptual lifetime of unread data on the hardware endpoint. Fixes: 9353afbbfa7b ("usb: gadget: f_fs: buffer data from ‘oversized’ OUT requests") Cc: stable Assisted-by: Antigravity:gemini-3.1-pro Signed-off-by: Neill Kapron Link: https://patch.msgid.link/20260619040609.4010746-3-nkapron@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_fs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index d9b95fb830df..44218be1e676 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -1375,7 +1375,6 @@ ffs_epfile_release(struct inode *inode, struct file *file) mutex_unlock(&epfile->dmabufs_mutex); - __ffs_epfile_read_buffer_free(epfile); ffs_data_closed(epfile->ffs); return 0; @@ -2393,6 +2392,7 @@ static void ffs_epfiles_destroy(struct super_block *sb, for (; count; --count, ++epfile) { BUG_ON(mutex_is_locked(&epfile->mutex)); + __ffs_epfile_read_buffer_free(epfile); simple_remove_by_name(root, epfile->name, clear_one); } From e2674dfbed8a30d57e2bc872c4bfa6c3eec918bf Mon Sep 17 00:00:00 2001 From: Mauricio Faria de Oliveira Date: Tue, 26 May 2026 14:09:44 -0300 Subject: [PATCH 0056/1054] usb: atm: ueagle-atm: wait for pre-firmware load in .disconnect() ueagle-atm uses the asynchronous request_firmware_nowait() in .probe(), but does not wait for its completion, not even in .disconnect(); so, if the device is unplugged meanwhile, its teardown runs concurrently with that. Even though this inconsistency is worth addressing on its own, it has also triggered several bug reports in syzbot over the years (some auto-closed) where the firmware sysfs fallback mechanism (CONFIG_FW_LOADER_USER_HELPER) creates a firmware subdirectory in the device directory during its removal, which might hit unexpected conditions in kernfs, apparently, depending at which point the add and remove operations raced. (See links.) The pattern is: usb ?-?: Direct firmware load for ueagle-atm/eagle?.fw failed with error -2 usb ?-?: Falling back to sysfs fallback for: ueagle-atm/eagle?.fw Call trace: ... kernfs_create_dir_ns sysfs_create_dir_ns create_dir kobject_add_internal kobject_add_varg kobject_add class_dir_create_and_add get_device_parent device_add fw_load_sysfs_fallback fw_load_from_user_helper firmware_fallback_sysfs _request_firmware request_firmware_work_func ... (Some variations are observed, after fw_load_sysfs_fallback(), e.g., [1].) While the kernfs side is being looked at, the ueagle-atm side can be fixed by waiting for the pre-firmware load in the .disconnect() handler. This change has a similar approach to previous work by Andrey Tsygunka [2] (wait_for_completion() in .disconnect()), but it is relatively different in design/implementation; using the Originally-by tag for credit assignment. This has been tested with: - synthetic reproducer to check the error path; - USB gadget (virtual device) to check the firmware upload path; - QEMU device emulator to check the device ID re-enumeration path; (The latter two were written by Claude; no other code/text in this commit.) Links (year first reported): 2025 https://syzbot.org/bug?extid=ce1e5a1b4e086b43e56d 2025 https://syzbot.org/bug?extid=9af8471255ac36e34fd4 2024 https://syzbot.org/bug?extid=306212936b13e520679d 2023 https://syzkaller.appspot.com/bug?extid=457452d30bcdda75ead2 2022 https://syzbot.org/bug?extid=782984d6f1701b526edb 2021 https://syzbot.org/bug?id=f3f221579f4ef7e9691281f3c6f56c05f83e8490 2021 https://syzbot.org/bug?id=84d86f0d71394829df6fc53daf6642c045983881 2021 https://syzbot.org/bug?id=3302dc1c0e2b9c94f2e8edb404eabc9267bc6f90 [1] https://syzkaller.appspot.com/bug?extid=457452d30bcdda75ead2 [2] https://lore.kernel.org/lkml/20250410093146.3776801-2-aitsygunka@yandex.ru/ Cc: stable Reported-by: syzbot+ce1e5a1b4e086b43e56d@syzkaller.appspotmail.com Closes: https://syzbot.org/bug?extid=ce1e5a1b4e086b43e56d Reported-by: syzbot+306212936b13e520679d@syzkaller.appspotmail.com Closes: https://syzbot.org/bug?extid=306212936b13e520679d Reported-by: syzbot+457452d30bcdda75ead2@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=457452d30bcdda75ead2 Originally-by: Andrey Tsygunka Fixes: b72458a80c75 ("[PATCH] USB: Eagle and ADI 930 usb adsl modem driver") Assisted-by: Claude:claude-opus-4.7 # usb gadget & qemu device for testing Signed-off-by: Mauricio Faria de Oliveira Acked-by: Stanislaw Gruszka Link: https://patch.msgid.link/20260526-ueagle-atm_req-fw-sync-v3-1-93c01961daaf@igalia.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/atm/ueagle-atm.c | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c index d610cdcef7d0..4e71ed679a76 100644 --- a/drivers/usb/atm/ueagle-atm.c +++ b/drivers/usb/atm/ueagle-atm.c @@ -594,7 +594,9 @@ static int uea_send_modem_cmd(struct usb_device *usb, static void uea_upload_pre_firmware(const struct firmware *fw_entry, void *context) { - struct usb_device *usb = context; + struct usb_interface *intf = context; + struct usb_device *usb = interface_to_usbdev(intf); + struct completion *fw_done = usb_get_intfdata(intf); const u8 *pfw; u8 value; u32 crc = 0; @@ -663,15 +665,17 @@ static void uea_upload_pre_firmware(const struct firmware *fw_entry, uea_err(usb, "firmware is corrupted\n"); err: release_firmware(fw_entry); + complete(fw_done); } /* * uea_load_firmware - Load usb firmware for pre-firmware devices. */ -static int uea_load_firmware(struct usb_device *usb, unsigned int ver) +static int uea_load_firmware(struct usb_interface *intf, unsigned int ver) { int ret; char *fw_name = EAGLE_FIRMWARE; + struct usb_device *usb = interface_to_usbdev(intf); uea_info(usb, "pre-firmware device, uploading firmware\n"); @@ -694,7 +698,7 @@ static int uea_load_firmware(struct usb_device *usb, unsigned int ver) } ret = request_firmware_nowait(THIS_MODULE, 1, fw_name, &usb->dev, - GFP_KERNEL, usb, + GFP_KERNEL, intf, uea_upload_pre_firmware); if (ret) uea_err(usb, "firmware %s is not available\n", fw_name); @@ -2555,8 +2559,23 @@ static int uea_probe(struct usb_interface *intf, const struct usb_device_id *id) usb_reset_device(usb); - if (UEA_IS_PREFIRM(id)) - return uea_load_firmware(usb, UEA_CHIP_VERSION(id)); + if (UEA_IS_PREFIRM(id)) { + struct completion *fw_done; + + /* Wait for the firmware load to be done, in .disconnect() */ + fw_done = kzalloc_obj(*fw_done); + if (!fw_done) + return -ENOMEM; + + init_completion(fw_done); + usb_set_intfdata(intf, fw_done); + + ret = uea_load_firmware(intf, UEA_CHIP_VERSION(id)); + if (ret) + kfree(fw_done); + + return ret; + } ret = usbatm_usb_probe(intf, id, &uea_usbatm_driver); if (ret == 0) { @@ -2586,6 +2605,13 @@ static void uea_disconnect(struct usb_interface *intf) usbatm_usb_disconnect(intf); mutex_unlock(&uea_mutex); uea_info(usb, "ADSL device removed\n"); + } else if (usb->config->desc.bNumInterfaces == 1) { + struct completion *fw_done = usb_get_intfdata(intf); + + uea_dbg(usb, "pre-firmware device, waiting firmware upload\n"); + wait_for_completion(fw_done); + uea_dbg(usb, "pre-firmware device, finished waiting\n"); + kfree(fw_done); } } From f8f680609c2b3ab795ffcd6f21585b6dfc46d395 Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Wed, 27 May 2026 23:08:32 +0800 Subject: [PATCH 0057/1054] usb: gadget: composite: fix dead empty check in the USB_DT_OTG handler The OTG branch of composite_setup() falls back to the first configuration when none is selected: if (cdev->config) config = cdev->config; else config = list_first_entry(&cdev->configs, struct usb_configuration, list); if (!config) goto done; ... memcpy(req->buf, config->descriptors[0], value); list_first_entry() never returns NULL. On an empty list it returns container_of() of the list head. So the "if (!config)" check is dead. When cdev->configs is empty, config points at the head inside struct usb_composite_dev. config->descriptors[0] reads whatever sits at that offset. The memcpy copies up to w_length bytes of it into the response buffer. cdev->configs can be empty in two cases. One is a teardown race on gadget unbind with a control transfer in flight. The other is a driver that sets is_otg before it adds a config. A reproducer that holds cdev->configs empty triggers a KASAN fault in this branch. Use list_first_entry_or_null() so the existing check does its job. Fixes: 53e6242db8d6 ("usb: gadget: composite: add USB_DT_OTG request handling") Cc: stable Signed-off-by: Maoyi Xie Link: https://patch.msgid.link/20260527150832.2943293-1-maoyixie.tju@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/composite.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index dc3664374596..df39e3487c1f 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1863,9 +1863,10 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) if (cdev->config) config = cdev->config; else - config = list_first_entry( + config = list_first_entry_or_null( &cdev->configs, - struct usb_configuration, list); + struct usb_configuration, + list); if (!config) goto done; From 692c354bef03b77b30e57e61934da502c8a12d45 Mon Sep 17 00:00:00 2001 From: WenTao Liang Date: Thu, 11 Jun 2026 21:11:21 +0800 Subject: [PATCH 0058/1054] usb: dwc3: meson-g12a: fix refcount leak in dwc3_meson_g12a_resume() If dwc3_meson_g12a_resume() succeeds in calling reset_control_reset(), an internal triggered_count reference is acquired. If any later step fails (usb_init, phy_init, phy_power_on, regulator_enable, or usb_post_init), the function returns the error without rearming the reset control. This leaks the reference and leaves the reset control in a triggered state, causing future reset_control_reset() calls to incorrectly return early as if already reset. Add an error path that calls reset_control_rearm() to balance the reference before returning the error. Cc: stable Fixes: 5b0ba0caaf3a ("usb: dwc3: meson-g12a: refactor usb init") Signed-off-by: WenTao Liang Link: https://patch.msgid.link/20260611131121.81784-1-vulab@iscas.ac.cn Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/dwc3-meson-g12a.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-meson-g12a.c b/drivers/usb/dwc3/dwc3-meson-g12a.c index 55e144ba8cfc..4d611c08e8a4 100644 --- a/drivers/usb/dwc3/dwc3-meson-g12a.c +++ b/drivers/usb/dwc3/dwc3-meson-g12a.c @@ -907,35 +907,39 @@ static int __maybe_unused dwc3_meson_g12a_resume(struct device *dev) ret = priv->drvdata->usb_init(priv); if (ret) - return ret; + goto err_rearm; /* Init PHYs */ for (i = 0 ; i < PHY_COUNT ; ++i) { ret = phy_init(priv->phys[i]); if (ret) - return ret; + goto err_rearm; } /* Set PHY Power */ for (i = 0 ; i < PHY_COUNT ; ++i) { ret = phy_power_on(priv->phys[i]); if (ret) - return ret; + goto err_rearm; } if (priv->vbus && priv->otg_phy_mode == PHY_MODE_USB_HOST) { ret = regulator_enable(priv->vbus); if (ret) - return ret; + goto err_rearm; } if (priv->drvdata->usb_post_init) { ret = priv->drvdata->usb_post_init(priv); if (ret) - return ret; + goto err_rearm; } return 0; + +err_rearm: + reset_control_rearm(priv->reset); + return ret; } static const struct dev_pm_ops dwc3_meson_g12a_dev_pm_ops = { From 0bddda5a11665c210339de76d27ebbd1a2e0b43c Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Tue, 23 Jun 2026 17:33:25 +0800 Subject: [PATCH 0059/1054] usb: mtu3: unmap request DMA on queue failure mtu3_gadget_queue() maps the request before checking whether the QMU GPD ring can accept another transfer. the request is returned with -EAGAIN before it is linked on the endpoint request list if mtu3_prepare_transfer() fails. Normal completion and dequeue paths unmap requests from mtu3_req_complete(), but this error path never reaches that helper, so the DMA mapping is left active. Unmap the request before returning from the failed queue path. Fixes: df2069acb005 ("usb: Add MediaTek USB3 DRD driver") Cc: stable Signed-off-by: Haoxiang Li Link: https://patch.msgid.link/20260623093325.2105323-1-haoxiang_li2024@163.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/mtu3/mtu3_gadget.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/mtu3/mtu3_gadget.c b/drivers/usb/mtu3/mtu3_gadget.c index da29f467943f..f224f2ee379a 100644 --- a/drivers/usb/mtu3/mtu3_gadget.c +++ b/drivers/usb/mtu3/mtu3_gadget.c @@ -305,6 +305,7 @@ static int mtu3_gadget_queue(struct usb_ep *ep, if (mtu3_prepare_transfer(mep)) { ret = -EAGAIN; + usb_gadget_unmap_request(&mtu->g, req, mep->is_in); goto error; } From 8c6314489550fa81d41723a0ff33f655b5b6c7b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?HE=20WEI=20=28=E3=82=AE=E3=82=AB=E3=82=AF=29?= Date: Wed, 24 Jun 2026 18:09:52 +0900 Subject: [PATCH 0060/1054] usb: misc: usbio: bound bulk IN response length to the received transfer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit usbio_bulk_msg() copies bpkt_len = le16_to_cpu(bpkt->len) bytes out of the bulk IN buffer (usbio->rxbuf, allocated with size usbio->rxbuf_len) into the caller's buffer. bpkt_len is fully controlled by the device and is only checked against ibuf_len; ibuf_len in turn is checked against usbio->txbuf_len, not against rxbuf_len: if ((obuf_len > (usbio->txbuf_len - sizeof(*bpkt))) || (ibuf_len > (usbio->txbuf_len - sizeof(*bpkt)))) return -EMSGSIZE; txbuf_len and rxbuf_len are taken independently from the bulk OUT and bulk IN endpoint wMaxPacketSize in usbio_probe(). A malicious or malfunctioning device that advertises a large bulk OUT endpoint and a small bulk IN endpoint (e.g. by claiming one of the quirk-free IDs such as the Lattice NX33U, 0x2ac1:0x20cb) therefore makes ibuf_len, and hence the device-supplied bpkt_len, exceed rxbuf_len. memcpy() then reads up to txbuf_len - rxbuf_len bytes past the end of the rxbuf slab object. The over-read bytes are handed back to the i2c layer and on to user space through i2c-dev, disclosing adjacent slab memory; with KASAN this is reported as a slab-out-of-bounds read. The number of bytes actually received is already known: act equals the URB actual_length and is bounded by rxbuf_len. Reject any response that claims more payload than was received, mirroring the existing "act < sizeof(*bpkt)" check just above. The control path (usbio_ctrl_msg()) is not affected: it uses a single buffer (ctrlbuf) for both directions, so its analogous copy can never leave the allocation. Found by code review. The out-of-bounds read was confirmed under AddressSanitizer with a faithful userspace model of usbio_bulk_msg()'s receive path (an rxbuf_len-sized buffer, the same act/ibuf_len/bpkt_len checks and the memcpy). A USB raw-gadget + dummy_hcd reproducer is also available. Fixes: 121a0f839dbb ("usb: misc: Add Intel USBIO bridge driver") Cc: stable Signed-off-by: HE WEI (ギカク) Link: https://patch.msgid.link/20260624090952.86439-1-skyexpoc@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/usbio.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/usb/misc/usbio.c b/drivers/usb/misc/usbio.c index 02d1e0760f0c..24c4cd0df829 100644 --- a/drivers/usb/misc/usbio.c +++ b/drivers/usb/misc/usbio.c @@ -344,6 +344,10 @@ int usbio_bulk_msg(struct auxiliary_device *adev, u8 type, u8 cmd, bool last, if (ibuf_len < bpkt_len) return -ENOSPC; + /* The device must not claim more payload than it actually sent. */ + if (bpkt_len > act - sizeof(*bpkt)) + return -EPROTO; + memcpy(ibuf, bpkt->data, bpkt_len); return bpkt_len; From c1bab046d4786c5b17aab7c5225bf0d4a2a2d19b Mon Sep 17 00:00:00 2001 From: Praveen Talari Date: Thu, 25 Jun 2026 21:26:02 +0530 Subject: [PATCH 0061/1054] spi: core: Abort active target transfer on controller suspend When an SPI controller operating in target mode has a transfer in progress at the time of system suspend, the suspend path proceeds without aborting the ongoing transfer. This can leave the hardware in an inconsistent state, potentially causing the system to hang or fail to resume cleanly. Fix this by invoking the controller's target_abort callback from spi_controller_suspend() when the controller is in target mode and the callback is registered. This ensures any active target transfer is cleanly terminated before the controller is suspended. Signed-off-by: Praveen Talari Link: https://patch.msgid.link/20260625-abort_active_transfer_duirng_s2r-v2-1-1d6f724406b6@oss.qualcomm.com Signed-off-by: Mark Brown --- drivers/spi/spi.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index d7e584afa301..f35d288c64fe 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -3671,6 +3671,9 @@ int spi_controller_suspend(struct spi_controller *ctlr) { int ret = 0; + if (ctlr->cur_msg && spi_controller_is_target(ctlr) && ctlr->target_abort) + ctlr->target_abort(ctlr); + /* Basically no-ops for non-queued controllers */ if (ctlr->queued) { ret = spi_stop_queue(ctlr); From 0fa749771993033befb9dda60b023782cb5fd2d9 Mon Sep 17 00:00:00 2001 From: Vivian Wang Date: Thu, 25 Jun 2026 14:34:15 +0800 Subject: [PATCH 0062/1054] riscv: Raise default NR_CPUS for 64BIT to 256 SpacemiT has already produced a 80-core RVA23 RISC-V server [1], and going further back, the dual-socket SG2042-based Sophgo Pisces has 128 cores (although that had some issues achieving mainline support). Therefore, an NR_CPUS of 64 is not enough. Raise default NR_CPUS to 256 for 64BIT (when !RISCV_SBI_V01, since very old firmware can't support more than 64 cores). The number was picked as a power of two that is at least double the known max. I believe this should be the right balance between not wasting too much memory and not having to touch this too often. Ubuntu has already been shipping NR_CPUS=512 for riscv64. We have also been testing NR_CPUS=256 internally at ISCAS and found negligible performance impact and no ill effects. Reported-by: Lufei Zheng Link: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1140651 # [1] Suggested-by: Han Gao Signed-off-by: Vivian Wang Link: https://patch.msgid.link/20260625-riscv-more-nr-cpus-v1-1-5da8c72b9269@iscas.ac.cn Signed-off-by: Paul Walmsley --- arch/riscv/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index 3f0a647218e4..c0a6992933e4 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -454,7 +454,8 @@ config NR_CPUS range 2 32 if RISCV_SBI_V01 && 32BIT range 2 64 if RISCV_SBI_V01 && 64BIT default "32" if 32BIT - default "64" if 64BIT + default "64" if RISCV_SBI_V01 && 64BIT + default "256" if !RISCV_SBI_V01 && 64BIT config HOTPLUG_CPU bool "Support for hot-pluggable CPUs" From 625ee71c3283dd322856060f9f4d344e2edc3c14 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Mon, 22 Jun 2026 14:52:07 +0100 Subject: [PATCH 0063/1054] raid6: fix riscv symbol undeclared warnigns The riscv rvv.c file is missing the include of pq_arch.h which defines all the exported functions. Include this to remove the following sparse warnings: lib/raid/raid6/riscv/rvv.c:1225:1: warning: symbol 'raid6_rvvx1' was not declared. Should it be static? lib/raid/raid6/riscv/rvv.c:1226:1: warning: symbol 'raid6_rvvx2' was not declared. Should it be static? lib/raid/raid6/riscv/rvv.c:1227:1: warning: symbol 'raid6_rvvx4' was not declared. Should it be static? lib/raid/raid6/riscv/rvv.c:1228:1: warning: symbol 'raid6_rvvx8' was not declared. Should it be static? Signed-off-by: Ben Dooks Reviewed-by: Nam Cao Link: https://patch.msgid.link/20260622135207.480540-1-ben.dooks@codethink.co.uk Signed-off-by: Paul Walmsley --- lib/raid/raid6/riscv/rvv.c | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/raid/raid6/riscv/rvv.c b/lib/raid/raid6/riscv/rvv.c index 75c9dafedb28..4ac50606f3dc 100644 --- a/lib/raid/raid6/riscv/rvv.c +++ b/lib/raid/raid6/riscv/rvv.c @@ -10,6 +10,7 @@ */ #include "rvv.h" +#include "pq_arch.h" #ifdef __riscv_vector #error "This code must be built without compiler support for vector" From 5c5dea43f6354e8dbd13bcb7e478f85593e19d90 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Mon, 22 Jun 2026 14:55:35 +0100 Subject: [PATCH 0064/1054] raid6: fix raid6_recov_rvv symbol undeclared warning The riscv recov_rvv.c should have included pq_arch.h for the definition of raid6_recov_rvv. Add the include to fix the following sparse warning: lib/raid/raid6/riscv/recov_rvv.c:218:32: warning: symbol 'raid6_recov_rvv' was not declared. Should it be static? Signed-off-by: Ben Dooks Reviewed-by: Nam Cao Link: https://patch.msgid.link/20260622135535.481534-1-ben.dooks@codethink.co.uk Signed-off-by: Paul Walmsley --- lib/raid/raid6/riscv/recov_rvv.c | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/raid/raid6/riscv/recov_rvv.c b/lib/raid/raid6/riscv/recov_rvv.c index 2305940276dd..78e158a3e332 100644 --- a/lib/raid/raid6/riscv/recov_rvv.c +++ b/lib/raid/raid6/riscv/recov_rvv.c @@ -8,6 +8,7 @@ #include #include "algos.h" #include "rvv.h" +#include "pq_arch.h" static void __raid6_2data_recov_rvv(int bytes, u8 *p, u8 *q, u8 *dp, u8 *dq, const u8 *pbmul, From c8c5a7835f5c9e34c8a15190519a2cc9ecb9b5b5 Mon Sep 17 00:00:00 2001 From: Bastian Blank Date: Thu, 18 Jun 2026 18:12:30 +0200 Subject: [PATCH 0065/1054] riscv: Add build salt to the vDSO The vDSO needs to have a unique build id in a similar manner to the kernel and modules. Use the build salt macro. Signed-off-by: Bastian Blank Reviewed-by: Nam Cao Link: https://patch.msgid.link/ajQY7n0an0YwQ--j@steamhammer.waldi.eu.org Signed-off-by: Paul Walmsley --- arch/riscv/kernel/vdso/note.S | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/riscv/kernel/vdso/note.S b/arch/riscv/kernel/vdso/note.S index 3d92cc956b95..69bfe48be037 100644 --- a/arch/riscv/kernel/vdso/note.S +++ b/arch/riscv/kernel/vdso/note.S @@ -4,6 +4,7 @@ * Here we can supply some information useful to userland. */ +#include #include #include #include @@ -12,4 +13,6 @@ ELFNOTE_START(Linux, 0, "a") .long LINUX_VERSION_CODE ELFNOTE_END +BUILD_SALT + emit_riscv_feature_1_and From b8f62414fa05144924257db283c5c35f74d21121 Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Thu, 25 Jun 2026 17:47:02 +0800 Subject: [PATCH 0066/1054] ACPI: RIMT: Only defer the IOMMU configuration in init stage The IOMMU configuration will be deferred if the IOMMU driver isn't probed by the time. Make this deferral only in the initialization stage with driver_deferred_probe_check_state(). Otherwise the devices depends on IOMMU will be deferred forever in case the IOMMU device probe failed or it doesn't appear in the ACPI namespace. Fixes: 8f7729552582 ("ACPI: RISC-V: Add support for RIMT") Signed-off-by: Yicong Yang Link: https://patch.msgid.link/20260625094702.11558-1-yang.yicong@picoheart.com [pjw@kernel.org: added Fixes line] Signed-off-by: Paul Walmsley --- drivers/acpi/riscv/rimt.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/acpi/riscv/rimt.c b/drivers/acpi/riscv/rimt.c index 906282b0e63c..e4538fa6c2c8 100644 --- a/drivers/acpi/riscv/rimt.c +++ b/drivers/acpi/riscv/rimt.c @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -257,11 +258,11 @@ static int rimt_iommu_xlate(struct device *dev, struct acpi_rimt_node *node, u32 rimt_fwnode = rimt_get_fwnode(node); /* - * The IOMMU drivers may not be probed yet. - * Defer the IOMMU configuration + * The IOMMU drivers may not be probed yet. Defer the IOMMU + * configuration if it's still in initialization stage. */ if (!rimt_fwnode) - return -EPROBE_DEFER; + return driver_deferred_probe_check_state(dev); /* * EPROBE_DEFER ensures IOMMU is probed before the devices that From 68fb3c026bec6f5dbd8ed5f2e57ef6535ec13341 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Fri, 12 Jun 2026 01:25:38 +0200 Subject: [PATCH 0067/1054] riscv: smp: use secs_to_jiffies in __cpu_up Use secs_to_jiffies() to simplify the code. Drop the redundant zero initialization while at it. Signed-off-by: Thorsten Blum Link: https://patch.msgid.link/20260611232537.467398-3-thorsten.blum@linux.dev Signed-off-by: Paul Walmsley --- arch/riscv/kernel/smpboot.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/arch/riscv/kernel/smpboot.c b/arch/riscv/kernel/smpboot.c index 8b628580fe11..f6ef57930b50 100644 --- a/arch/riscv/kernel/smpboot.c +++ b/arch/riscv/kernel/smpboot.c @@ -189,13 +189,12 @@ int arch_cpuhp_kick_ap_alive(unsigned int cpu, struct task_struct *tidle) #else int __cpu_up(unsigned int cpu, struct task_struct *tidle) { - int ret = 0; + int ret; tidle->thread_info.cpu = cpu; ret = start_secondary_cpu(cpu, tidle); if (!ret) { - wait_for_completion_timeout(&cpu_running, - msecs_to_jiffies(1000)); + wait_for_completion_timeout(&cpu_running, secs_to_jiffies(1)); if (!cpu_online(cpu)) { pr_crit("CPU%u: failed to come online\n", cpu); From 57ad674d032baf5426a38b0d6b2ddd60cbd3913f Mon Sep 17 00:00:00 2001 From: Wang Han Date: Tue, 9 Jun 2026 14:29:52 +0800 Subject: [PATCH 0068/1054] scripts/sorttable: Handle RISC-V patchable ftrace entries RISC-V uses -fpatchable-function-entry=8,4 when the compressed ISA is enabled and -fpatchable-function-entry=4,2 otherwise. In both cases, the patchable NOP area starts 8 bytes before the function symbol address. The __mcount_loc entries therefore point at the patchable NOP area associated with a function, while nm reports the function symbol at the entry address used for the function range check. After RISC-V selected HAVE_BUILDTIME_MCOUNT_SORT, sorttable started applying that range check at build time. Without allowing entries just before the reported function address, the mcount sorter treats valid RISC-V ftrace callsites as invalid weak-function entries and writes them back as zero. The resulting kernel boots with no ftrace entries, breaking dynamic ftrace and users such as livepatch. The failure is silent during the final link because zeroing weak-function entries is an expected sorttable operation. At boot, those zero entries are skipped by ftrace_process_locs(), so the only obvious symptom is that the vmlinux ftrace table has lost valid callsites and ftrace users cannot attach to them. CONFIG_FTRACE_SORT_STARTUP_TEST also reports the table as sorted in this state: it only checks that the __mcount_loc entries are in ascending order, which a fully zeroed table trivially satisfies. The original commit relied on this check and did not see the regression. On an affected RISC-V QEMU boot with both CONFIG_FTRACE_SORT_STARTUP_TEST and CONFIG_FTRACE_STARTUP_TEST enabled, the sort check still passes while ftrace reports zero usable entries and the early selftests fail: [ 0.000000] ftrace section at ffffffff8101da98 sorted properly [ 0.000000] ftrace: allocating 0 entries in 128 pages [ 0.054999] Testing tracer function: .. no entries found ..FAILED! [ 0.172407] tracer: function failed selftest, disabling [ 0.178186] Failed to init function_graph tracer, init returned -19 Handle RISC-V like arm64 for the function-range check and allow patchable entries up to 8 bytes before the function address. With this fix, a RISC-V QEMU smoke boot with ftrace startup tests shows the vmlinux ftrace table is populated and dynamic ftrace still works: [ 0.000000] ftrace: allocating 46749 entries in 184 pages [ 0.051115] Testing tracer function: PASSED [ 1.283782] Testing dynamic ftrace: PASSED [ 6.275456] Testing tracer function_graph: PASSED Fixes: 0ca1724b56af ("riscv: ftrace: select HAVE_BUILDTIME_MCOUNT_SORT") Suggested-by: Steven Rostedt (Google) Reviewed-by: Steven Rostedt Reviewed-by: Shuai Xue Reviewed-by: Chen Pei Link: https://lore.kernel.org/all/20260527113028.4b21a5de@fedora/ Signed-off-by: Wang Han Reviewed-by: Martin Kaiser Link: https://patch.msgid.link/20260609063002.3943001-1-wanghan@linux.alibaba.com Signed-off-by: Paul Walmsley --- scripts/sorttable.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/scripts/sorttable.c b/scripts/sorttable.c index e8ed11c680c6..d8dc2a1b7c31 100644 --- a/scripts/sorttable.c +++ b/scripts/sorttable.c @@ -891,17 +891,22 @@ static int do_file(char const *const fname, void *addr) table_sort_t custom_sort = NULL; switch (elf_map_machine(ehdr)) { - case EM_AARCH64: #ifdef MCOUNT_SORT_ENABLED + case EM_AARCH64: + /* arm64 also needs RELA-based weak-function fixups. */ sort_reloc = true; rela_type = 0x403; - /* arm64 uses patchable function entry placing before function */ + /* fallthrough */ + case EM_RISCV: + /* arm64 and RISC-V place patchable entries before the function. */ before_func = 8; +#else + case EM_AARCH64: + case EM_RISCV: #endif /* fallthrough */ case EM_386: case EM_LOONGARCH: - case EM_RISCV: case EM_S390: case EM_X86_64: custom_sort = sort_relative_table_with_data; From c4c7756a81b5baef286bf9be1ea404f3e4dd7a3c Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Wed, 24 Jun 2026 19:31:48 +0800 Subject: [PATCH 0069/1054] riscv: Fix 32-bit call_on_irq_stack() frame pointer ABI call_on_irq_stack() uses struct member offsets to set up its link in the frame record list. On riscv32, struct stackframe is the wrong size to maintain stack pointer alignment, so STACKFRAME_SIZE_ON_STACK includes padding. However, the ABI requires the frame record to be placed immediately below the address stored in s0, so the padding must come before the struct members. Fix the layout by making STACKFRAME_FP and STACKFRAME_RA the negative offsets from s0, instead of the positive offsets from sp. Fixes: 82982fdd5133 ("riscv: Deduplicate IRQ stack switching") Signed-off-by: Samuel Holland Reviewed-by: Matthew Bystrin Signed-off-by: Rui Qi Link: https://lore.kernel.org/all/20240530001733.1407654-2-samuel.holland@sifive.com/ Reviewed-by: Nam Cao Link: https://patch.msgid.link/20260624113148.3723541-1-qirui.001@bytedance.com [pjw@kernel.org: cleaned up the patch tags and added Matthew's Reviewed-by] Signed-off-by: Paul Walmsley --- arch/riscv/kernel/asm-offsets.c | 4 ++-- arch/riscv/kernel/entry.S | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/arch/riscv/kernel/asm-offsets.c b/arch/riscv/kernel/asm-offsets.c index af827448a609..a75f0cfea1e9 100644 --- a/arch/riscv/kernel/asm-offsets.c +++ b/arch/riscv/kernel/asm-offsets.c @@ -501,8 +501,8 @@ void asm_offsets(void) OFFSET(SBI_HART_BOOT_STACK_PTR_OFFSET, sbi_hart_boot_data, stack_ptr); DEFINE(STACKFRAME_SIZE_ON_STACK, ALIGN(sizeof(struct stackframe), STACK_ALIGN)); - OFFSET(STACKFRAME_FP, stackframe, fp); - OFFSET(STACKFRAME_RA, stackframe, ra); + DEFINE(STACKFRAME_FP, offsetof(struct stackframe, fp) - sizeof(struct stackframe)); + DEFINE(STACKFRAME_RA, offsetof(struct stackframe, ra) - sizeof(struct stackframe)); #ifdef CONFIG_FUNCTION_TRACER DEFINE(FTRACE_OPS_FUNC, offsetof(struct ftrace_ops, func)); #ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS diff --git a/arch/riscv/kernel/entry.S b/arch/riscv/kernel/entry.S index c6988983cdf7..08df724e13b9 100644 --- a/arch/riscv/kernel/entry.S +++ b/arch/riscv/kernel/entry.S @@ -386,8 +386,8 @@ SYM_CODE_END(ret_from_fork_user_asm) SYM_FUNC_START(call_on_irq_stack) /* Create a frame record to save ra and s0 (fp) */ addi sp, sp, -STACKFRAME_SIZE_ON_STACK - REG_S ra, STACKFRAME_RA(sp) - REG_S s0, STACKFRAME_FP(sp) + REG_S ra, (STACKFRAME_SIZE_ON_STACK + STACKFRAME_RA)(sp) + REG_S s0, (STACKFRAME_SIZE_ON_STACK + STACKFRAME_FP)(sp) addi s0, sp, STACKFRAME_SIZE_ON_STACK /* Switch to the per-CPU shadow call stack */ @@ -405,8 +405,8 @@ SYM_FUNC_START(call_on_irq_stack) /* Switch back to the thread stack and restore ra and s0 */ addi sp, s0, -STACKFRAME_SIZE_ON_STACK - REG_L ra, STACKFRAME_RA(sp) - REG_L s0, STACKFRAME_FP(sp) + REG_L ra, (STACKFRAME_SIZE_ON_STACK + STACKFRAME_RA)(sp) + REG_L s0, (STACKFRAME_SIZE_ON_STACK + STACKFRAME_FP)(sp) addi sp, sp, STACKFRAME_SIZE_ON_STACK ret From 6860b467f569f732b11cbc588ae7e195e90e7e23 Mon Sep 17 00:00:00 2001 From: Petr Wozniak Date: Sun, 21 Jun 2026 12:03:26 +0200 Subject: [PATCH 0070/1054] xfrm: propagate -EINPROGRESS from validate_xmit_xfrm() validate_xmit_xfrm() returns NULL both when a packet is dropped and when it is stolen by async crypto (-EINPROGRESS from ->xmit()). Callers cannot distinguish the two cases. f53c723902d1 ("net: Add asynchronous callbacks for xfrm on layer 2.") changed the semantics of a NULL return from "dropped" to "stolen or dropped", but __dev_queue_xmit() was not updated. On virtual/bridge interfaces (noqueue qdisc) __dev_queue_xmit() initialises rc=-ENOMEM and jumps to out: when skb is NULL, returning -ENOMEM to the caller even though the packet will be delivered correctly via xfrm_dev_resume(). Return ERR_PTR(-EINPROGRESS) from validate_xmit_xfrm() for the async case so callers can tell it apart from a real drop. Update __dev_queue_xmit() to handle ERR_PTR(-EINPROGRESS) from validate_xmit_skb() correctly. Update validate_xmit_skb_list() to use IS_ERR_OR_NULL() so that ERR_PTR(-EINPROGRESS) is not mistakenly added to the transmitted list. Fixes: f53c723902d1 ("net: Add asynchronous callbacks for xfrm on layer 2.") Suggested-by: Sabrina Dubroca Signed-off-by: Petr Wozniak Signed-off-by: Steffen Klassert --- net/core/dev.c | 10 ++++++++-- net/xfrm/xfrm_device.c | 4 ++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/net/core/dev.c b/net/core/dev.c index 4b3d5cfdf6e0..5933c5dab09e 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -4018,6 +4018,9 @@ static struct sk_buff *validate_xmit_unreadable_skb(struct sk_buff *skb, return NULL; } +/* Returns the skb on success, NULL if dropped, or ERR_PTR(-EINPROGRESS) + * if stolen by async xfrm crypto (delivered via xfrm_dev_resume()). + */ static struct sk_buff *validate_xmit_skb(struct sk_buff *skb, struct net_device *dev, bool *again) { netdev_features_t features; @@ -4089,7 +4092,7 @@ struct sk_buff *validate_xmit_skb_list(struct sk_buff *skb, struct net_device *d skb->prev = skb; skb = validate_xmit_skb(skb, dev, again); - if (!skb) + if (IS_ERR_OR_NULL(skb)) continue; if (!head) @@ -4860,8 +4863,11 @@ int __dev_queue_xmit(struct sk_buff *skb, struct net_device *sb_dev) goto recursion_alert; skb = validate_xmit_skb(skb, dev, &again); - if (!skb) + if (IS_ERR_OR_NULL(skb)) { + if (PTR_ERR(skb) == -EINPROGRESS) + rc = NET_XMIT_SUCCESS; goto out; + } HARD_TX_LOCK(dev, txq, cpu); diff --git a/net/xfrm/xfrm_device.c b/net/xfrm/xfrm_device.c index 630f3dd31cc5..19c77f09acc9 100644 --- a/net/xfrm/xfrm_device.c +++ b/net/xfrm/xfrm_device.c @@ -182,7 +182,7 @@ struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t featur err = x->type_offload->xmit(x, skb, esp_features); if (err) { if (err == -EINPROGRESS) - return NULL; + return ERR_PTR(-EINPROGRESS); XFRM_INC_STATS(xs_net(x), LINUX_MIB_XFRMOUTSTATEPROTOERROR); kfree_skb(skb); @@ -224,7 +224,7 @@ struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t featur pskb = skb2; } - return skb; + return skb ? skb : ERR_PTR(-EINPROGRESS); } EXPORT_SYMBOL_GPL(validate_xmit_xfrm); From 3f4c3919baf0944ad96580467c302bc6c7758b00 Mon Sep 17 00:00:00 2001 From: Petr Wozniak Date: Sun, 21 Jun 2026 12:03:27 +0200 Subject: [PATCH 0071/1054] xfrm: fix stale skb->prev after async crypto steals a GSO segment skb_gso_segment() leaves the segment list head with ->prev pointing at the last segment, an invariant validate_xmit_skb_list() relies on when it sets its tail pointer (tail = skb->prev). When validate_xmit_xfrm() walks a GSO list and some segments are stolen by async crypto (->xmit() returns -EINPROGRESS), those segments are unlinked from the list but the head ->prev is never updated. If the last segment is the one stolen, the returned head still has ->prev pointing at it, even though it is now owned by the crypto engine and may be freed. validate_xmit_skb_list() later does tail->next = skb, writing through that stale pointer -- a use-after-free. Repoint skb->prev at the last retained segment before returning. Fixes: f53c723902d1 ("net: Add asynchronous callbacks for xfrm on layer 2.") Signed-off-by: Petr Wozniak Signed-off-by: Steffen Klassert --- net/xfrm/xfrm_device.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/net/xfrm/xfrm_device.c b/net/xfrm/xfrm_device.c index 19c77f09acc9..aec1e1184a71 100644 --- a/net/xfrm/xfrm_device.c +++ b/net/xfrm/xfrm_device.c @@ -224,6 +224,14 @@ struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t featur pskb = skb2; } + /* skb_gso_segment() set skb->prev to the last segment, but async + * crypto may have stolen it above without updating ->prev. Repoint + * it at the last retained segment so validate_xmit_skb_list() does + * not chain onto a segment now owned by the crypto engine. + */ + if (skb) + skb->prev = pskb; + return skb ? skb : ERR_PTR(-EINPROGRESS); } EXPORT_SYMBOL_GPL(validate_xmit_xfrm); From 479e91fc92416a4d54d2b3150aa1e4550d9cc759 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Wed, 24 Jun 2026 21:16:45 +0800 Subject: [PATCH 0072/1054] gpio: mvebu: fail probe if gpiochip registration fails mvebu_gpio_probe() registers the GPIO chip with devm_gpiochip_add_data() but ignores the return value. If registration fails, probe continues and leaves later code operating on a GPIO chip that was never published to gpiolib. Return the registration error so the device fails probe cleanly. Fixes: fefe7b092345 ("gpio: introduce gpio-mvebu driver for Marvell SoCs") Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260624131645.86884-1-pengpeng@iscas.ac.cn Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-mvebu.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c index c030d1f00abc..689dc6354c2d 100644 --- a/drivers/gpio/gpio-mvebu.c +++ b/drivers/gpio/gpio-mvebu.c @@ -1221,7 +1221,10 @@ static int mvebu_gpio_probe(struct platform_device *pdev) BUG(); } - devm_gpiochip_add_data(&pdev->dev, &mvchip->chip, mvchip); + err = devm_gpiochip_add_data(&pdev->dev, &mvchip->chip, mvchip); + if (err) + return dev_err_probe(&pdev->dev, err, + "failed to register gpiochip\n"); /* Some MVEBU SoCs have simple PWM support for GPIO lines */ if (IS_REACHABLE(CONFIG_PWM)) { From 17326db5f0ab4ec1901e75d052b5ebef486b467f Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Wed, 24 Jun 2026 21:18:28 +0800 Subject: [PATCH 0073/1054] gpio: htc-egpio: use managed gpiochip registration egpio_probe() registers each nested gpio_chip with gpiochip_add_data() but ignores the return value. If one registration fails, probe still returns success even though one of the chips was not published to gpiolib. Use devm_gpiochip_add_data() and fail probe if any chip registration fails. This lets devres unwind already registered chips and prevents the driver from publishing a partially initialized device. Fixes: a1635b8fe59d ("[ARM] 4947/1: htc-egpio, a driver for GPIO/IRQ expanders with fixed input/output pins") Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260624131828.94139-1-pengpeng@iscas.ac.cn Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-htc-egpio.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-htc-egpio.c b/drivers/gpio/gpio-htc-egpio.c index d15423c718d0..25a4d4494f3c 100644 --- a/drivers/gpio/gpio-htc-egpio.c +++ b/drivers/gpio/gpio-htc-egpio.c @@ -268,6 +268,7 @@ static int __init egpio_probe(struct platform_device *pdev) struct gpio_chip *chip; unsigned int irq, irq_end; int i; + int ret; /* Initialize ei data structure. */ ei = devm_kzalloc(&pdev->dev, struct_size(ei, chip, pdata->num_chips), GFP_KERNEL); @@ -326,7 +327,10 @@ static int __init egpio_probe(struct platform_device *pdev) chip->base = pdata->chip[i].gpio_base; chip->ngpio = pdata->chip[i].num_gpios; - gpiochip_add_data(chip, &ei->chip[i]); + ret = devm_gpiochip_add_data(&pdev->dev, chip, &ei->chip[i]); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "failed to register gpiochip %d\n", i); } /* Set initial pin values */ From 30e542a36228db353e81efcd39e4dbc7a95c88c5 Mon Sep 17 00:00:00 2001 From: Anuj Gupta Date: Wed, 17 Jun 2026 21:20:51 +0530 Subject: [PATCH 0074/1054] blk-mq: bound blk_hctx_poll() to one jiffy blk_hctx_poll() can busy-poll until a completion is found or need_resched() becomes true. On preemptible kernels, the scheduler can set TIF_NEED_RESCHED on the timer tick and preempt the task at IRQ return before the loop condition re-evaluates it. After the context switch, the flag is cleared, so the poller can continue spinning instead of returning to its caller. This can happen with io_uring IOPOLL reads inside iocb_bio_iopoll(), which holds the rcu_read_lock() while calling bio_poll(). If another poller on the same polled queue drains the available completions, this poller may repeatedly find no completions and remain inside the RCU read-side critical section long enough to trigger RCU stall reports: rcu: INFO: rcu_preempt detected stalls on CPUs/tasks: rcu: Tasks blocked on level-1 rcu_node (CPUs 0-9): P3961 rcu: (detected by 3, t=60002 jiffies, g=18533, q=4943 ncpus=20) task:fio state:R running task stack:0 pid:3961 Call Trace: ? nvme_poll+0x36/0xa0 [nvme] ? blk_hctx_poll+0x39/0x90 ? blk_mq_poll+0x30/0x60 ? bio_poll+0x87/0x170 ? iocb_bio_iopoll+0x32/0x50 ? io_uring_classic_poll+0x25/0x50 ? io_do_iopoll+0x216/0x420 ? __do_sys_io_uring_enter+0x2c7/0x7c0 Reproducible with: fio -filename=/dev/nvme0n1 -direct=1 -size=4g -rw=randread \ --numjobs=32 -bs=4K -ioengine=io_uring -hipri=1 -iodepth=1 \ --registerfiles=1 --group_reporting --thread Record the starting jiffy and exit the loop once jiffies has advanced. This bounds each blk_hctx_poll() invocation while also covering the case where the reschedule flag was cleared by the context switch before the loop condition could observe it. Fixes: f22ecf9c14c1 ("blk-mq: delete task running check in blk_hctx_poll()") Reviewed-by: Fengnan Chang Suggested-by: Fengnan Chang Signed-off-by: Anuj Gupta Signed-off-by: Alok Rathore Link: https://patch.msgid.link/20260617155051.1266079-1-anuj20.g@samsung.com Signed-off-by: Jens Axboe --- block/blk-mq.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/block/blk-mq.c b/block/blk-mq.c index 88cb5acc4f39..2c850330a32b 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -5218,6 +5218,7 @@ static int blk_hctx_poll(struct request_queue *q, struct blk_mq_hw_ctx *hctx, struct io_comp_batch *iob, unsigned int flags) { int ret; + unsigned long timeout = jiffies + 2; do { ret = q->mq_ops->poll(hctx, iob); @@ -5228,7 +5229,7 @@ static int blk_hctx_poll(struct request_queue *q, struct blk_mq_hw_ctx *hctx, if (ret < 0 || (flags & BLK_POLL_ONESHOT)) break; cpu_relax(); - } while (!need_resched()); + } while (!need_resched() && time_before(jiffies, timeout)); return 0; } From 464fe6c0cc9437bc91f6e880b666d37b424e3d7b Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 25 Jun 2026 16:08:34 +0100 Subject: [PATCH 0075/1054] MAINTAINERS: USB: add usb.rs to USB subsystem file list As was recently noted on the rust-for-linux list, the usb.rs file is not listed as part of the USB SUBSYSTEM files, which can cause changes to it to be not sent to the proper list and people. Fix this up by adding it to the USB SUBSYSTEM file list Reported-by: Danilo Krummrich Link: https://patch.msgid.link/2026062533-achiness-outsell-a93a@gregkh Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 3eeb134cb611..f477e348ecc0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -28054,6 +28054,7 @@ F: include/dt-bindings/usb/ F: include/linux/usb.h F: include/linux/usb/ F: include/uapi/linux/usb/ +F: rust/kernel/usb.rs USB TYPEC BUS FOR ALTERNATE MODES M: Heikki Krogerus From 24ca1fea8f2753bf33e1d458ec1ae5d9b7796a65 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 23 Jun 2026 17:12:29 +0200 Subject: [PATCH 0076/1054] USB: serial: digi_acceleport: fix write buffer corruption The digi_write_inb_command() is supposed to wait for the write urb to become available or return an error, but instead it updates the transfer buffer and tries to resubmit the urb on timeout. To make things worse, for commands like break control where no timeout is used, the driver would corrupt the urb immediately due to a broken jiffies comparison (on 32-bit machines this takes five minutes of uptime to trigger due to INITIAL_JIFFIES). Fix this by adding the missing return on timeout and waiting indefinitely when no timeout has been specified as intended. This issue was (sort of) flagged by Sashiko when reviewing an unrelated change to the driver. Link: https://sashiko.dev/#/patchset/20260610132232.356139-1-johan%40kernel.org?part=11 Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/digi_acceleport.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index 6899aebfd6ae..5f3a95682179 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -427,20 +427,22 @@ static int digi_write_inb_command(struct usb_serial_port *port, int len; struct digi_port *priv = usb_get_serial_port_data(port); unsigned char *data = port->write_urb->transfer_buffer; + unsigned long expire; unsigned long flags; dev_dbg(&port->dev, "digi_write_inb_command: TOP: port=%d, count=%d\n", priv->dp_port_num, count); if (timeout) - timeout += jiffies; - else - timeout = ULONG_MAX; + expire = jiffies + timeout; spin_lock_irqsave(&priv->dp_port_lock, flags); while (count > 0 && ret == 0) { - while (priv->dp_write_urb_in_use && - time_before(jiffies, timeout)) { + while (priv->dp_write_urb_in_use) { + if (timeout && time_after(jiffies, expire)) { + ret = -ETIMEDOUT; + break; + } cond_wait_interruptible_timeout_irqrestore( &priv->write_wait, DIGI_RETRY_TIMEOUT, &priv->dp_port_lock, flags); @@ -449,6 +451,9 @@ static int digi_write_inb_command(struct usb_serial_port *port, spin_lock_irqsave(&priv->dp_port_lock, flags); } + if (ret) + break; + /* len must be a multiple of 4 and small enough to */ /* guarantee the write will send buffered data first, */ /* so commands are in order with data and not split */ From 5c1ea24b53bf3bfb859f0a05573997487975da23 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 23 Jun 2026 17:11:10 +0200 Subject: [PATCH 0077/1054] USB: serial: digi_acceleport: fix hard lockup on disconnect If submitting the OOB write urb fails persistently (e.g if the device is being disconnected) the driver would loop indefinitely with interrupts disabled. Check for urb submission errors when sending OOB commands to avoid hanging if, for example, open(), set_termios() or close() races with a physical disconnect. This is issue was flagged by Sashiko when reviewing an unrelated change to the driver. Link: https://sashiko.dev/#/patchset/20260610132232.356139-1-johan%40kernel.org?part=1 Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/digi_acceleport.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index 5f3a95682179..fa5c3539f806 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -392,12 +392,14 @@ static int digi_write_oob_command(struct usb_serial_port *port, len &= ~3; memcpy(oob_port->write_urb->transfer_buffer, buf, len); oob_port->write_urb->transfer_buffer_length = len; + ret = usb_submit_urb(oob_port->write_urb, GFP_ATOMIC); - if (ret == 0) { - oob_priv->dp_write_urb_in_use = 1; - count -= len; - buf += len; - } + if (ret) + break; + + oob_priv->dp_write_urb_in_use = 1; + count -= len; + buf += len; } spin_unlock_irqrestore(&oob_priv->dp_port_lock, flags); if (ret) From c752838874578b7f0267d9f3e73892b1c7a0d9cc Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 17 Jun 2026 07:58:02 +0200 Subject: [PATCH 0078/1054] xfs: split up xfs_buf_alloc_backing_mem Split out helpers for folio and vmalloc allocations to prepare for a bug fix. Signed-off-by: Christoph Hellwig Reviewed-by: Carlos Maiolino Signed-off-by: Carlos Maiolino --- fs/xfs/xfs_buf.c | 61 +++++++++++++++++++++++++++++++----------------- 1 file changed, 40 insertions(+), 21 deletions(-) diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c index 3ce12fe1c307..d1b7729559f0 100644 --- a/fs/xfs/xfs_buf.c +++ b/fs/xfs/xfs_buf.c @@ -120,6 +120,22 @@ xfs_buf_free( call_rcu(&bp->b_rcu, xfs_buf_free_callback); } +static int +xfs_buf_alloc_folio( + struct xfs_buf *bp, + size_t size, + gfp_t gfp_mask) +{ + struct folio *folio; + + folio = folio_alloc(gfp_mask, get_order(size)); + if (!folio) + return -ENOMEM; + bp->b_addr = folio_address(folio); + trace_xfs_buf_backing_folio(bp, _RET_IP_); + return 0; +} + static int xfs_buf_alloc_kmem( struct xfs_buf *bp, @@ -148,6 +164,27 @@ xfs_buf_alloc_kmem( return 0; } +static int +xfs_buf_alloc_vmalloc( + struct xfs_buf *bp, + size_t size, + gfp_t gfp_mask, + xfs_buf_flags_t flags) +{ + for (;;) { + bp->b_addr = __vmalloc(size, gfp_mask); + if (bp->b_addr) + break; + if (flags & XBF_READ_AHEAD) + return -ENOMEM; + XFS_STATS_INC(bp->b_mount, xb_page_retries); + memalloc_retry_wait(gfp_mask); + } + + trace_xfs_buf_backing_vmalloc(bp, _RET_IP_); + return 0; +} + /* * Allocate backing memory for a buffer. * @@ -175,7 +212,6 @@ xfs_buf_alloc_backing_mem( { size_t size = BBTOB(bp->b_length); gfp_t gfp_mask = GFP_KERNEL | __GFP_NOLOCKDEP | __GFP_NOWARN; - struct folio *folio; if (xfs_buftarg_is_mem(bp->b_target)) return xmbuf_map_backing_mem(bp); @@ -216,33 +252,16 @@ xfs_buf_alloc_backing_mem( */ if (size > PAGE_SIZE) { if (!is_power_of_2(size)) - goto fallback; + return xfs_buf_alloc_vmalloc(bp, size, gfp_mask, flags); gfp_mask &= ~__GFP_DIRECT_RECLAIM; gfp_mask |= __GFP_NORETRY; } - folio = folio_alloc(gfp_mask, get_order(size)); - if (!folio) { + if (xfs_buf_alloc_folio(bp, size, gfp_mask) < 0) { if (size <= PAGE_SIZE) return -ENOMEM; trace_xfs_buf_backing_fallback(bp, _RET_IP_); - goto fallback; + return xfs_buf_alloc_vmalloc(bp, size, gfp_mask, flags); } - bp->b_addr = folio_address(folio); - trace_xfs_buf_backing_folio(bp, _RET_IP_); - return 0; - -fallback: - for (;;) { - bp->b_addr = __vmalloc(size, gfp_mask); - if (bp->b_addr) - break; - if (flags & XBF_READ_AHEAD) - return -ENOMEM; - XFS_STATS_INC(bp->b_mount, xb_page_retries); - memalloc_retry_wait(gfp_mask); - } - - trace_xfs_buf_backing_vmalloc(bp, _RET_IP_); return 0; } From 27d7d15184d701b6e4db2b7431e4cee67a041a1c Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 17 Jun 2026 07:58:03 +0200 Subject: [PATCH 0079/1054] xfs: lift setting __GFP_NOFAIL from xfs_buf_alloc_kmem to the caller The current __GFP_NOFAIL setting is wrong in some cases. Prepare for fixing that by giving control to the caller. Signed-off-by: Christoph Hellwig Reviewed-by: Carlos Maiolino Signed-off-by: Carlos Maiolino --- fs/xfs/xfs_buf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c index d1b7729559f0..538ae441e212 100644 --- a/fs/xfs/xfs_buf.c +++ b/fs/xfs/xfs_buf.c @@ -145,7 +145,7 @@ xfs_buf_alloc_kmem( ASSERT(is_power_of_2(size)); ASSERT(size < PAGE_SIZE); - bp->b_addr = kmalloc(size, gfp_mask | __GFP_NOFAIL); + bp->b_addr = kmalloc(size, gfp_mask); if (!bp->b_addr) return -ENOMEM; @@ -230,7 +230,7 @@ xfs_buf_alloc_backing_mem( * smaller than PAGE_SIZE buffers used by XFS. */ if (size < PAGE_SIZE && is_power_of_2(size)) - return xfs_buf_alloc_kmem(bp, size, gfp_mask); + return xfs_buf_alloc_kmem(bp, size, gfp_mask | __GFP_NOFAIL); /* * Don't bother with the retry loop for single PAGE allocations: vmalloc From 0144bcf619602e82843a514431d3cf2cd13ec08a Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 17 Jun 2026 07:58:04 +0200 Subject: [PATCH 0080/1054] xfs: fix incorrect use of gfp flags in xfs_buf_alloc_backing_mem xfs_buf_alloc_backing_mem currently has two issues with how the GFP_ flags are set: - when aiming for a large folio allocation, the gfp mask is adjusted to try less hard, but these flags then persist for the vmalloc allocation, which is bogus. - the __GFP_NOFAIL for small allocations is also applied when readahead force __GFP_NORETRY which doesn't make any sense. Fix this by only applying __GFP_NOFAIL when __GFP_NORETRY is not set, and by reordering the code so that the large folio gfp adjustments are performed locally just for that allocation. Fixes: 94c78cfa3bd1 ("xfs: convert buffer cache to use high order folios") Signed-off-by: Christoph Hellwig Reviewed-by: Carlos Maiolino Signed-off-by: Carlos Maiolino --- fs/xfs/xfs_buf.c | 49 +++++++++++++++++++++++------------------------- 1 file changed, 23 insertions(+), 26 deletions(-) diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c index 538ae441e212..4cf2a154dba8 100644 --- a/fs/xfs/xfs_buf.c +++ b/fs/xfs/xfs_buf.c @@ -223,22 +223,6 @@ xfs_buf_alloc_backing_mem( if (flags & XBF_READ_AHEAD) gfp_mask |= __GFP_NORETRY; - /* - * For buffers smaller than PAGE_SIZE use a kmalloc allocation if that - * is properly aligned. The slab allocator now guarantees an aligned - * allocation for all power of two sizes, which matches most of the - * smaller than PAGE_SIZE buffers used by XFS. - */ - if (size < PAGE_SIZE && is_power_of_2(size)) - return xfs_buf_alloc_kmem(bp, size, gfp_mask | __GFP_NOFAIL); - - /* - * Don't bother with the retry loop for single PAGE allocations: vmalloc - * won't do any better. - */ - if (size <= PAGE_SIZE) - gfp_mask |= __GFP_NOFAIL; - /* * Optimistically attempt a single high order folio allocation for * larger than PAGE_SIZE buffers. @@ -251,18 +235,31 @@ xfs_buf_alloc_backing_mem( * path for them instead of wasting memory here. */ if (size > PAGE_SIZE) { - if (!is_power_of_2(size)) - return xfs_buf_alloc_vmalloc(bp, size, gfp_mask, flags); - gfp_mask &= ~__GFP_DIRECT_RECLAIM; - gfp_mask |= __GFP_NORETRY; - } - if (xfs_buf_alloc_folio(bp, size, gfp_mask) < 0) { - if (size <= PAGE_SIZE) - return -ENOMEM; - trace_xfs_buf_backing_fallback(bp, _RET_IP_); + if (is_power_of_2(size)) { + gfp_t folio_gfp = gfp_mask; + + folio_gfp &= ~__GFP_DIRECT_RECLAIM; + folio_gfp |= __GFP_NORETRY; + if (xfs_buf_alloc_folio(bp, size, folio_gfp) == 0) + return 0; + trace_xfs_buf_backing_fallback(bp, _RET_IP_); + } return xfs_buf_alloc_vmalloc(bp, size, gfp_mask, flags); } - return 0; + + /* + * The slab allocator now guarantees aligned allocations for all power + * of two sizes. This covers most smaller XFS buffers, so just use + * kmalloc in this case. + * + * Don't bother with the vmalloc fallback for allocations of page size + * or less: vmalloc won't do any better. + */ + if (!(gfp_mask & __GFP_NORETRY)) + gfp_mask |= __GFP_NOFAIL; + if (size < PAGE_SIZE && is_power_of_2(size)) + return xfs_buf_alloc_kmem(bp, size, gfp_mask); + return xfs_buf_alloc_folio(bp, size, gfp_mask); } static int From 19dc95d4b6cf81e1878a3abd587afc967b75d5ce Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 17 Jun 2026 07:58:05 +0200 Subject: [PATCH 0081/1054] xfs: simplify the failure path in xfs_buf_alloc_vmalloc Look at the __GFP_NORETRY flag set for readahead so that we don't have to pass both the gfp_t and the flags in. Signed-off-by: Christoph Hellwig Reviewed-by: Carlos Maiolino Signed-off-by: Carlos Maiolino --- fs/xfs/xfs_buf.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c index 4cf2a154dba8..2a7d696d394a 100644 --- a/fs/xfs/xfs_buf.c +++ b/fs/xfs/xfs_buf.c @@ -168,14 +168,13 @@ static int xfs_buf_alloc_vmalloc( struct xfs_buf *bp, size_t size, - gfp_t gfp_mask, - xfs_buf_flags_t flags) + gfp_t gfp_mask) { for (;;) { bp->b_addr = __vmalloc(size, gfp_mask); if (bp->b_addr) break; - if (flags & XBF_READ_AHEAD) + if (gfp_mask & __GFP_NORETRY) return -ENOMEM; XFS_STATS_INC(bp->b_mount, xb_page_retries); memalloc_retry_wait(gfp_mask); @@ -244,7 +243,7 @@ xfs_buf_alloc_backing_mem( return 0; trace_xfs_buf_backing_fallback(bp, _RET_IP_); } - return xfs_buf_alloc_vmalloc(bp, size, gfp_mask, flags); + return xfs_buf_alloc_vmalloc(bp, size, gfp_mask); } /* From 7f53bf79bdbac36b644b9fe7a77516baf8de5109 Mon Sep 17 00:00:00 2001 From: jiazhenyuan Date: Tue, 23 Jun 2026 10:41:53 +0800 Subject: [PATCH 0082/1054] xfs: fix AGFL extent count calculation in xrep_agfl_fill In xrep_agfl_fill(), the call to xagb_bitmap_set() passes 'agbno - 1' as the length argument. However, xagb_bitmap_set() expects a length (number of blocks), not an end block number. Passing 'agbno - 1' causes used_extents to record an incorrect range. Fix this by calculating the correct length as 'agbno - start', which represents the actual number of blocks filled into the AGFL. Signed-off-by: jiazhenyuan Fixes: 014ad53732d2ba ("xfs: use per-AG bitmaps to reap unused AG metadata blocks during repair") Reviewed-by: "Darrick J. Wong" Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/agheader_repair.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/xfs/scrub/agheader_repair.c b/fs/xfs/scrub/agheader_repair.c index 7114c9a44182..2554494847ff 100644 --- a/fs/xfs/scrub/agheader_repair.c +++ b/fs/xfs/scrub/agheader_repair.c @@ -652,7 +652,7 @@ xrep_agfl_fill( while (agbno < start + len && af->fl_off < af->flcount) af->agfl_bno[af->fl_off++] = cpu_to_be32(agbno++); - error = xagb_bitmap_set(&af->used_extents, start, agbno - 1); + error = xagb_bitmap_set(&af->used_extents, start, agbno - start); if (error) return error; From 032a6f6ce21fc701468fd15403d2f53a30107f5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Draszik?= Date: Thu, 18 Jun 2026 13:00:39 +0100 Subject: [PATCH 0083/1054] dma-fence: use correct callback in dma_fence_timeline_name() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dma_fence_timeline_name() is a wrapper around dma_fence_ops::get_timeline_name(). Since the blamed commit below, it calls an incorrect callback. Update it to restore functionality by calling the intended callback. Fixes: 62918542b7bf ("dma-fence: Fix sparse warnings due __rcu annotations") Cc: # v7.1+ [tursulin: added cc stable] Signed-off-by: André Draszik Reviewed-by: Philipp Stanner Signed-off-by: Tvrtko Ursulin Link: https://lore.kernel.org/r/20260618-linux-drm_crtc_fix-v1-1-801f29c9853d@linaro.org --- drivers/dma-buf/dma-fence.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma-buf/dma-fence.c b/drivers/dma-buf/dma-fence.c index a2aa82f4eedd..7120610f4850 100644 --- a/drivers/dma-buf/dma-fence.c +++ b/drivers/dma-buf/dma-fence.c @@ -1201,7 +1201,7 @@ const char __rcu *dma_fence_timeline_name(struct dma_fence *fence) /* RCU protection is required for safe access to returned string */ ops = rcu_dereference(fence->ops); if (!dma_fence_test_signaled_flag(fence)) - return (const char __rcu *)ops->get_driver_name(fence); + return (const char __rcu *)ops->get_timeline_name(fence); else return (const char __rcu *)"signaled-timeline"; } From 46c8beeccd8ab2c863827254a85ea877654a3534 Mon Sep 17 00:00:00 2001 From: Manish Khadka Date: Fri, 15 May 2026 22:27:00 +0545 Subject: [PATCH 0084/1054] HID: letsketch: fix UAF on inrange_timer at driver unbind letsketch_driver does not provide a .remove callback, but letsketch_probe() arms a per-device timer: timer_setup(&data->inrange_timer, letsketch_inrange_timeout, 0); The timer is re-armed from letsketch_raw_event() with a 100 ms timeout on every pen-in-range report, and its callback dereferences data->input_tablet to deliver a synthetic BTN_TOOL_PEN release. letsketch_data is allocated with devm_kzalloc(), and its input_dev fields are devm-allocated via letsketch_setup_input_tablet(). On device unbind (USB unplug or rmmod), the HID core runs its default teardown and devm cleanup frees both letsketch_data and the input devices. Because no .remove callback exists, nothing drains the timer first: if raw_event armed it within ~100 ms of the unbind, the pending timer fires on freed memory. This is a UAF read of data and of data->input_tablet, followed by input_report_key() / input_sync() into the freed input_dev. The same problem can occur on the probe error path: if hid_hw_start() enabled I/O on an always-poll-quirk device and then failed, raw_event may have armed the timer before devm releases data. Fix by adding a .remove callback that calls hid_hw_stop() first. hid_hw_stop() synchronously kills the URBs that deliver raw_event(), so once it returns no path can re-arm the timer. timer_shutdown_sync() then drains any in-flight callback and permanently disables further mod_timer() calls. Apply the same timer_shutdown_sync() in the probe error path so the timer is guaranteed not to outlive data. Fixes: 33a5c2793451 ("HID: Add new Letsketch tablet driver") Cc: stable@vger.kernel.org Signed-off-by: Manish Khadka Signed-off-by: Jiri Kosina --- drivers/hid/hid-letsketch.c | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/drivers/hid/hid-letsketch.c b/drivers/hid/hid-letsketch.c index 11e21f988723..b52e93a91ae5 100644 --- a/drivers/hid/hid-letsketch.c +++ b/drivers/hid/hid-letsketch.c @@ -296,13 +296,42 @@ static int letsketch_probe(struct hid_device *hdev, const struct hid_device_id * ret = letsketch_setup_input_tablet(data); if (ret) - return ret; + goto err_shutdown_timer; ret = letsketch_setup_input_tablet_pad(data); if (ret) - return ret; + goto err_shutdown_timer; - return hid_hw_start(hdev, HID_CONNECT_HIDRAW); + ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW); + if (ret) + goto err_shutdown_timer; + + return 0; + +err_shutdown_timer: + /* + * Drain any pending callback and permanently disable the timer + * before devm releases data: if hid_hw_start() enabled I/O on an + * always-poll-quirk device and then failed, raw_event may have + * armed the timer already. + */ + timer_shutdown_sync(&data->inrange_timer); + return ret; +} + +static void letsketch_remove(struct hid_device *hdev) +{ + struct letsketch_data *data = hid_get_drvdata(hdev); + + /* + * hid_hw_stop() synchronously kills the URBs that deliver + * raw_event(), so once it returns no path can re-arm + * inrange_timer. timer_shutdown_sync() then drains any + * in-flight callback and permanently disables further + * mod_timer() calls before devm releases data. + */ + hid_hw_stop(hdev); + timer_shutdown_sync(&data->inrange_timer); } static const struct hid_device_id letsketch_devices[] = { @@ -315,6 +344,7 @@ static struct hid_driver letsketch_driver = { .name = "letsketch", .id_table = letsketch_devices, .probe = letsketch_probe, + .remove = letsketch_remove, .raw_event = letsketch_raw_event, }; module_hid_driver(letsketch_driver); From 75fe87e19d8aff81eb2c64d15d244ab8da4de945 Mon Sep 17 00:00:00 2001 From: Manish Khadka Date: Fri, 15 May 2026 23:17:52 +0545 Subject: [PATCH 0085/1054] HID: appleir: fix UAF on pending key_up_timer in remove() appleir_remove() runs hid_hw_stop() before timer_delete_sync(). hid_hw_stop() synchronously unregisters the HID input device via hid_disconnect() -> hidinput_disconnect() -> input_unregister_device(), which drops the last reference and frees the underlying input_dev when no userspace handle holds it open. key_up_tick() reads appleir->input_dev and calls input_report_key() / input_sync() on it. The timer is armed from appleir_raw_event() with a HZ/8 (~125 ms) timeout on every keydown and key-repeat report. If a key was pressed shortly before the device is disconnected, the timer can fire after hid_hw_stop() has freed input_dev but before the teardown drains it. A simple reorder is not sufficient. Putting the timer drain first still leaves a window where a USB URB completion (raw_event) running during hid_hw_stop() can call mod_timer() and re-arm the timer, which then fires after hidinput_disconnect() has freed input_dev. The same URB-completion window also lets raw_event() reach key_up(), key_down() and battery_flat() directly, all of which dereference appleir->input_dev. Introduce a 'removing' flag on struct appleir, gated by the existing spinlock. appleir_remove() sets the flag under the lock and then shuts down the timer with timer_shutdown_sync(), which both drains any in-flight callback and permanently disables further mod_timer() calls. appleir_raw_event() and key_up_tick() bail out early if the flag is set, so no path can arm or run the timer, or dereference appleir->input_dev, after remove() has started tearing down. The keyrepeat and flatbattery branches of appleir_raw_event() previously called into the input layer without holding the spinlock; take it now so the flag check is well-defined. This incidentally closes a pre-existing read-side race on appleir->current_key in the keyrepeat branch. This bug is structurally a sibling of commit 4db2af929279 ("HID: appletb-kbd: fix UAF in inactivity-timer cleanup path") and has been present since the driver was introduced. Fixes: 9a4a5574ce42 ("HID: appleir: add support for Apple ir devices") Cc: stable@vger.kernel.org Signed-off-by: Manish Khadka Signed-off-by: Jiri Kosina --- drivers/hid/hid-appleir.c | 45 ++++++++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/drivers/hid/hid-appleir.c b/drivers/hid/hid-appleir.c index 5e8ced7bc05a..adaa44a858ed 100644 --- a/drivers/hid/hid-appleir.c +++ b/drivers/hid/hid-appleir.c @@ -109,9 +109,10 @@ struct appleir { struct hid_device *hid; unsigned short keymap[ARRAY_SIZE(appleir_key_table)]; struct timer_list key_up_timer; /* timer for key up */ - spinlock_t lock; /* protects .current_key */ + spinlock_t lock; /* protects .current_key, .removing */ int current_key; /* the currently pressed key */ int prev_key_idx; /* key index in a 2 packets message */ + bool removing; /* set during teardown; gates input_dev access */ }; static int get_key(int data) @@ -172,7 +173,7 @@ static void key_up_tick(struct timer_list *t) unsigned long flags; spin_lock_irqsave(&appleir->lock, flags); - if (appleir->current_key) { + if (!appleir->removing && appleir->current_key) { key_up(hid, appleir, appleir->current_key); appleir->current_key = 0; } @@ -195,6 +196,10 @@ static int appleir_raw_event(struct hid_device *hid, struct hid_report *report, int index; spin_lock_irqsave(&appleir->lock, flags); + if (appleir->removing) { + spin_unlock_irqrestore(&appleir->lock, flags); + goto out; + } /* * If we already have a key down, take it up before marking * this one down @@ -229,17 +234,25 @@ static int appleir_raw_event(struct hid_device *hid, struct hid_report *report, appleir->prev_key_idx = 0; if (!memcmp(data, keyrepeat, sizeof(keyrepeat))) { - key_down(hid, appleir, appleir->current_key); - /* - * Remote doesn't do key up, either pull them up, in the test - * above, or here set a timer which pulls them up after 1/8 s - */ - mod_timer(&appleir->key_up_timer, jiffies + HZ / 8); + spin_lock_irqsave(&appleir->lock, flags); + if (!appleir->removing) { + key_down(hid, appleir, appleir->current_key); + /* + * Remote doesn't do key up, either pull them up, in + * the test above, or here set a timer which pulls them + * up after 1/8 s + */ + mod_timer(&appleir->key_up_timer, jiffies + HZ / 8); + } + spin_unlock_irqrestore(&appleir->lock, flags); goto out; } if (!memcmp(data, flatbattery, sizeof(flatbattery))) { - battery_flat(appleir); + spin_lock_irqsave(&appleir->lock, flags); + if (!appleir->removing) + battery_flat(appleir); + spin_unlock_irqrestore(&appleir->lock, flags); /* Fall through */ } @@ -318,8 +331,20 @@ static int appleir_probe(struct hid_device *hid, const struct hid_device_id *id) static void appleir_remove(struct hid_device *hid) { struct appleir *appleir = hid_get_drvdata(hid); + unsigned long flags; + + /* + * Mark the driver as tearing down so that any concurrent raw_event + * (e.g. from a USB URB completion that hid_hw_stop() has not yet + * killed) and the key_up_timer softirq stop touching input_dev + * before hid_hw_stop() frees it via hidinput_disconnect(). + */ + spin_lock_irqsave(&appleir->lock, flags); + appleir->removing = true; + spin_unlock_irqrestore(&appleir->lock, flags); + + timer_shutdown_sync(&appleir->key_up_timer); hid_hw_stop(hid); - timer_delete_sync(&appleir->key_up_timer); } static const struct hid_device_id appleir_devices[] = { From 0021eb09041f021c079be1022934a280f7f176c0 Mon Sep 17 00:00:00 2001 From: Georgiy Osokin Date: Sun, 17 May 2026 15:06:39 +0300 Subject: [PATCH 0086/1054] HID: picolcd: prevent NULL pointer dereference in picolcd_send_and_wait() In picolcd_send_and_wait(), an integer overflow of the signed loop counter 'k' can theoretically lead to a NULL pointer dereference of 'raw_data'. If the loop executes more than INT_MAX times, 'k' becomes negative, making the condition 'k < size' true even when 'size' is 0. Change the type of 'k' to 'unsigned int' to prevent the overflow and eliminate the out-of-bounds access. Found by Linux Verification Center (linuxtesting.org) with the Svace static analysis tool. [jkosina@suse.com: extended hash length] Fixes: fabdbf2fd22fa17 ("HID: picoLCD: split driver code") Signed-off-by: Georgiy Osokin Signed-off-by: Jiri Kosina --- drivers/hid/hid-picolcd_core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/hid/hid-picolcd_core.c b/drivers/hid/hid-picolcd_core.c index 2cc01e1bc1a8..d73e97c8b853 100644 --- a/drivers/hid/hid-picolcd_core.c +++ b/drivers/hid/hid-picolcd_core.c @@ -72,7 +72,8 @@ struct picolcd_pending *picolcd_send_and_wait(struct hid_device *hdev, struct picolcd_pending *work; struct hid_report *report = picolcd_out_report(report_id, hdev); unsigned long flags; - int i, j, k; + int i, j; + unsigned int k; if (!report || !data) return NULL; From af1a9b65ebe8a948eda805c14b78d4d0767cb1b5 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 16 Jun 2026 11:26:56 +0000 Subject: [PATCH 0087/1054] HID: core: Fix OOB read in hid_get_report for numbered reports When a caller passes a size of 0 to hid_report_raw_event() for a numbered report, the function originally called hid_get_report() before performing any size validation. Inside hid_get_report(), if the report is numbered (report_enum->numbered is true), it unconditionally dereferences data[0] to extract the report ID. With a size of 0, this results in an out-of-bounds read or kernel panic. Fix this by moving the numbered report size validation check before the call to hid_get_report(), ensuring that size is at least 1 before dereferencing the data pointer. Fixes: 2c85c61d1332 ("HID: pass the buffer size to hid_report_raw_event") Signed-off-by: Lee Jones Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 41a79e43c82b..cf123347a2af 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -2045,6 +2045,13 @@ int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 * u8 *cdata = data; int ret = 0; + if (report_enum->numbered && (size < 1 || bufsize < 1)) { + hid_warn_ratelimited(hid, + "Event data for numbered report is too short (%d vs %zu)\n", + size, bufsize); + return -EINVAL; + } + report = hid_get_report(report_enum, data); if (!report) return 0; From 590cc4d782487632a52f37c2171bee1eeea29627 Mon Sep 17 00:00:00 2001 From: HyeongJun An Date: Thu, 18 Jun 2026 15:37:37 +0900 Subject: [PATCH 0088/1054] HID: logitech-dj: Fix maxfield check in DJ short report validation Commit b6a57912854e ("HID: logitech-dj: Prevent REPORT_ID_DJ_SHORT related user initiated OOB write") added validation for the DJ short output report, but the error path dereferences rep->field[0] even when rep->maxfield is zero. Commit 8b9a097eb2fc ("HID: logitech-dj: fix wrong detection of bad DJ_SHORT output report") made the check conditional on rep being present, but a crafted descriptor can still create report ID 0x20 with only padding output items. hid-core registers the report, ignores the padding field, and leaves rep->maxfield as zero. In that case the validation enters the rep->maxfield < 1 branch and then dereferences rep->field[0]->report_count while printing the error message, causing a NULL pointer dereference during probe. This is reproducible with uhid by emulating a Logitech receiver with a padding-only DJ short output report: BUG: KASAN: null-ptr-deref in logi_dj_probe+0xb1/0x754 [hid_logitech_dj] Read of size 4 at addr 0000000000000028 by task kworker/4:1/129 ... Call Trace: logi_dj_probe+0xb1/0x754 [hid_logitech_dj] hid_device_probe+0x329/0x3f0 [hid] really_probe+0x162/0x570 __device_attach+0x137/0x2c0 bus_probe_device+0x38/0xc0 device_add+0xa56/0xce0 hid_add_device+0x19c/0x280 [hid] uhid_device_add_worker+0x2c/0xb0 [uhid] Reject the zero-field report before printing the field report_count. Fixes: b6a57912854e ("HID: logitech-dj: Prevent REPORT_ID_DJ_SHORT related user initiated OOB write") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: HyeongJun An Signed-off-by: Jiri Kosina --- drivers/hid/hid-logitech-dj.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c index 381e4dc5aba7..9c574ab8b60b 100644 --- a/drivers/hid/hid-logitech-dj.c +++ b/drivers/hid/hid-logitech-dj.c @@ -1907,8 +1907,13 @@ static int logi_dj_probe(struct hid_device *hdev, output_report_enum = &hdev->report_enum[HID_OUTPUT_REPORT]; rep = output_report_enum->report_id_hash[REPORT_ID_DJ_SHORT]; - if (rep && (rep->maxfield < 1 || - rep->field[0]->report_count != DJREPORT_SHORT_LENGTH - 1)) { + if (rep && rep->maxfield < 1) { + hid_err(hdev, "Expected size of DJ short report is %d, but got 0", + DJREPORT_SHORT_LENGTH - 1); + return -EINVAL; + } + + if (rep && rep->field[0]->report_count != DJREPORT_SHORT_LENGTH - 1) { hid_err(hdev, "Expected size of DJ short report is %d, but got %d", DJREPORT_SHORT_LENGTH - 1, rep->field[0]->report_count); return -EINVAL; From 7705b4140d188ce22656f6e541ae7ef834c7e11a Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Thu, 18 Jun 2026 15:06:35 +0800 Subject: [PATCH 0089/1054] HID: lg-g15: cancel pending work on remove to fix a use-after-free lg_g15_data is allocated with devm and holds a work item. The report handlers schedule that work straight from device input. lg_g15_event() and lg_g15_v2_event() do it on the backlight cycle key, and lg_g510_leds_event() does it too. The worker dereferences the lg_g15_data back through container_of. The driver had no remove callback and never cancelled the work. So if a report scheduled the work and the keyboard was then unplugged, devres freed lg_g15_data while the work was still pending or running, and the worker touched freed memory. This is a use-after-free. It is reachable as a race on device unplug. Add a remove callback that cancels the work before devres frees the state. g15->work is only initialized for the models that schedule it (G15, G15 v2, G510). The G13 and Z-10 leave it zeroed, so guard the cancel on g15->work.func to avoid cancelling a work that was never set up. The g15 NULL test mirrors the one already in lg_g15_raw_event(). Fixes: 97b741aba918 ("HID: lg-g15: Add keyboard and LCD backlight control") Cc: stable@vger.kernel.org Suggested-by: Hans de Goede Signed-off-by: Maoyi Xie Reviewed-by: Hans de Goede Signed-off-by: Jiri Kosina --- drivers/hid/hid-lg-g15.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/hid/hid-lg-g15.c b/drivers/hid/hid-lg-g15.c index 1a88bc44ada4..02ef3e2094b4 100644 --- a/drivers/hid/hid-lg-g15.c +++ b/drivers/hid/hid-lg-g15.c @@ -1374,11 +1374,27 @@ static const struct hid_device_id lg_g15_devices[] = { }; MODULE_DEVICE_TABLE(hid, lg_g15_devices); +static void lg_g15_remove(struct hid_device *hdev) +{ + struct lg_g15_data *g15 = hid_get_drvdata(hdev); + + /* + * g15->work is only initialized for the models that schedule it + * (G15, G15 v2, G510). The G13 and Z-10 leave it zeroed, so only + * cancel it when it was set up. + */ + if (g15 && g15->work.func) + cancel_work_sync(&g15->work); + + hid_hw_stop(hdev); +} + static struct hid_driver lg_g15_driver = { .name = "lg-g15", .id_table = lg_g15_devices, .raw_event = lg_g15_raw_event, .probe = lg_g15_probe, + .remove = lg_g15_remove, }; module_hid_driver(lg_g15_driver); From eacaf5ae747f7dead6cc268de17a7382d79031fc Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Wed, 6 May 2026 23:43:00 +0100 Subject: [PATCH 0090/1054] MIPS: DEC: Ensure RTC platform device deregistration upon failure Switch RTC platform device registration from platform_device_register() to platform_add_devices() so as to make sure any failure will result in automatic device unregistration. Fixes: fae67ad43114 ("arch/mips/dec: switch DECstation systems to rtc-cmos") Signed-off-by: Maciej W. Rozycki Acked-by: Thomas Bogendoerfer Signed-off-by: Thomas Bogendoerfer --- arch/mips/dec/platform.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/mips/dec/platform.c b/arch/mips/dec/platform.c index 723ce16cbfc0..a005246a0ac5 100644 --- a/arch/mips/dec/platform.c +++ b/arch/mips/dec/platform.c @@ -38,6 +38,10 @@ static struct platform_device dec_rtc_device = { .num_resources = ARRAY_SIZE(dec_rtc_resources), }; +static struct platform_device *dec_rtc_devices[] __initdata = { + &dec_rtc_device, +}; + static struct resource dec_dz_resources[] = { { .name = "dz", .flags = IORESOURCE_MEM, }, { .name = "dz", .flags = IORESOURCE_IRQ, }, @@ -137,7 +141,7 @@ static int __init dec_add_devices(void) } num_zs = i; - ret1 = platform_device_register(&dec_rtc_device); + ret1 = platform_add_devices(dec_rtc_devices, 1); ret2 = IS_ENABLED(CONFIG_32BIT) ? platform_add_devices(dec_dz_devices, num_dz) : 0; ret3 = platform_add_devices(dec_zs_devices, num_zs); From a9e0237d2eb5f1e35f500cfa1a82a242b7c8686c Mon Sep 17 00:00:00 2001 From: Bastian Blank Date: Thu, 18 Jun 2026 18:12:21 +0200 Subject: [PATCH 0091/1054] mips: Add build salt to the vDSO The vDSO needs to have a unique build id in a similar manner to the kernel and modules. Use the build salt macro. Signed-off-by: Bastian Blank Signed-off-by: Thomas Bogendoerfer --- arch/mips/vdso/elf.S | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/mips/vdso/elf.S b/arch/mips/vdso/elf.S index a25cb147f1ca..821fcffe7655 100644 --- a/arch/mips/vdso/elf.S +++ b/arch/mips/vdso/elf.S @@ -8,6 +8,7 @@ #include +#include #include #include @@ -15,6 +16,8 @@ ELFNOTE_START(Linux, 0, "a") .long LINUX_VERSION_CODE ELFNOTE_END +BUILD_SALT + /* * The .MIPS.abiflags section must be defined with the FP ABI flags set * to 'any' to be able to link with both old and new libraries. From 6d5fbecd0213489bc4de71a0da194d18e654fd6e Mon Sep 17 00:00:00 2001 From: Kyle Hendry Date: Sun, 21 Jun 2026 11:47:02 -0700 Subject: [PATCH 0092/1054] MIPS: mm: Add check for highmem before removing memory block If a device has less physical memory than the highmem threshold bootmem_init() doesn't set highstart_pfn. This results in highmem_init() wrongly disabling the entire memory range if the cpu doesn't support highmem. Add a check that highstart_pfn is non zero before removing the highmem block. Fixes: f171b55f1441 ("mips: fix HIGHMEM initialization") Signed-off-by: Kyle Hendry Signed-off-by: Thomas Bogendoerfer --- arch/mips/mm/init.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/arch/mips/mm/init.c b/arch/mips/mm/init.c index 1c07ca84ee21..352718e43f69 100644 --- a/arch/mips/mm/init.c +++ b/arch/mips/mm/init.c @@ -426,10 +426,11 @@ static inline void __init highmem_init(void) unsigned long tmp; /* - * If CPU cannot support HIGHMEM discard the memory above highstart_pfn + * If CPU cannot support HIGHMEM discard any memory above highstart_pfn */ if (cpu_has_dc_aliases) { - memblock_remove(PFN_PHYS(highstart_pfn), -1); + if (highstart_pfn) + memblock_remove(PFN_PHYS(highstart_pfn), -1); return; } From dceafc180309977fa06ff668b5f4f978d5c2dbee Mon Sep 17 00:00:00 2001 From: Xi Ruoyao Date: Wed, 24 Jun 2026 01:27:21 +0800 Subject: [PATCH 0093/1054] MIPS: loongson64: add IRQ work based on self-IPI Since the commit 91840be8f710 ("irq_work: Fix use-after-free in irq_work_single() on PREEMPT_RT"), we observed the performance of execve() is significantly impacted on MIPS. While we are unsure how that commit caused the impact or how to improve it (or even if it can be improved at all), implementing IRQ work with self-IPI seems able to mitigate the impaction. Perhaps this can/should be implemented for other MIPS architecture processors as well, but we don't have the enough knowledge of them, nor access to the hardware. So only implement it for loongson64 here. Link: https://lore.kernel.org/6be1cdd5f91dd7418a32ff372a6f3ae259b19195.camel@xry111.site/ Signed-off-by: Xi Ruoyao Reviewed-by: Huacai Chen Reviewed-by: Sebastian Andrzej Siewior Signed-off-by: Thomas Bogendoerfer --- arch/mips/include/asm/irq_work.h | 9 +++++++++ arch/mips/include/asm/smp.h | 2 ++ arch/mips/loongson64/smp.c | 10 ++++++++++ 3 files changed, 21 insertions(+) create mode 100644 arch/mips/include/asm/irq_work.h diff --git a/arch/mips/include/asm/irq_work.h b/arch/mips/include/asm/irq_work.h new file mode 100644 index 000000000000..d4fa2d80aabc --- /dev/null +++ b/arch/mips/include/asm/irq_work.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef _ASM_MIPS_IRQ_WORK_H +#define _ASM_MIPS_IRQ_WORK_H +static inline bool arch_irq_work_has_interrupt(void) +{ + return IS_ENABLED(CONFIG_MACH_LOONGSON64) && IS_ENABLED(CONFIG_SMP); +} +#endif /* _ASM_MIPS_IRQ_WORK_H */ diff --git a/arch/mips/include/asm/smp.h b/arch/mips/include/asm/smp.h index 2427d76f953f..a545568f1cac 100644 --- a/arch/mips/include/asm/smp.h +++ b/arch/mips/include/asm/smp.h @@ -50,6 +50,8 @@ extern int __cpu_logical_map[NR_CPUS]; #define SMP_CALL_FUNCTION 0x2 /* Octeon - Tell another core to flush its icache */ #define SMP_ICACHE_FLUSH 0x4 +/* Loongson64 - Self IPI for IRQ work */ +#define SMP_IRQ_WORK 0x8 /* Mask of CPUs which are currently definitely operating coherently */ extern cpumask_t cpu_coherent_mask; diff --git a/arch/mips/loongson64/smp.c b/arch/mips/loongson64/smp.c index 147acd972a07..e584299d0fde 100644 --- a/arch/mips/loongson64/smp.c +++ b/arch/mips/loongson64/smp.c @@ -381,6 +381,13 @@ loongson3_send_ipi_mask(const struct cpumask *mask, unsigned int action) ipi_write_action(cpu_logical_map(i), (u32)action); } +#ifdef CONFIG_IRQ_WORK +void arch_irq_work_raise(void) +{ + loongson3_send_ipi_single(smp_processor_id(), SMP_IRQ_WORK); +} +#endif + static irqreturn_t loongson3_ipi_interrupt(int irq, void *dev_id) { int cpu = smp_processor_id(); @@ -397,6 +404,9 @@ static irqreturn_t loongson3_ipi_interrupt(int irq, void *dev_id) irq_exit(); } + if (action & SMP_IRQ_WORK) + irq_work_run(); + return IRQ_HANDLED; } From 0880884b36d1230a80a0322abc9b9c7b26942b65 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Wed, 24 Jun 2026 16:17:39 +0800 Subject: [PATCH 0094/1054] MIPS: configs: Enable the current Ingenic USB PHY symbol The Ingenic USB PHY provider is now built from phy-ingenic-usb.o under `CONFIG_PHY_INGENIC_USB`. The Ingenic defconfigs below still enable the stale `CONFIG_JZ4770_PHY` symbol. That symbol no longer carries the provider object, so the defconfigs lose the intended USB PHY provider after olddefconfig. Use `CONFIG_PHY_INGENIC_USB` instead. Signed-off-by: Pengpeng Hou Signed-off-by: Thomas Bogendoerfer --- arch/mips/configs/cu1000-neo_defconfig | 2 +- arch/mips/configs/cu1830-neo_defconfig | 2 +- arch/mips/configs/gcw0_defconfig | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/mips/configs/cu1000-neo_defconfig b/arch/mips/configs/cu1000-neo_defconfig index 19517beaf540..ed2a620e4f86 100644 --- a/arch/mips/configs/cu1000-neo_defconfig +++ b/arch/mips/configs/cu1000-neo_defconfig @@ -86,7 +86,7 @@ CONFIG_DMA_JZ4780=y # CONFIG_INGENIC_TIMER is not set CONFIG_INGENIC_SYSOST=y # CONFIG_IOMMU_SUPPORT is not set -CONFIG_JZ4770_PHY=y +CONFIG_PHY_INGENIC_USB=y CONFIG_EXT4_FS=y # CONFIG_DNOTIFY is not set CONFIG_AUTOFS_FS=y diff --git a/arch/mips/configs/cu1830-neo_defconfig b/arch/mips/configs/cu1830-neo_defconfig index b403e67ab105..9b4aeeef58ce 100644 --- a/arch/mips/configs/cu1830-neo_defconfig +++ b/arch/mips/configs/cu1830-neo_defconfig @@ -89,7 +89,7 @@ CONFIG_DMA_JZ4780=y # CONFIG_INGENIC_TIMER is not set CONFIG_INGENIC_SYSOST=y # CONFIG_IOMMU_SUPPORT is not set -CONFIG_JZ4770_PHY=y +CONFIG_PHY_INGENIC_USB=y CONFIG_EXT4_FS=y # CONFIG_DNOTIFY is not set CONFIG_AUTOFS_FS=y diff --git a/arch/mips/configs/gcw0_defconfig b/arch/mips/configs/gcw0_defconfig index adb9fd62ddb0..6d737810b470 100644 --- a/arch/mips/configs/gcw0_defconfig +++ b/arch/mips/configs/gcw0_defconfig @@ -99,7 +99,7 @@ CONFIG_USB_MUSB_HDRC=y CONFIG_USB_MUSB_GADGET=y CONFIG_USB_MUSB_JZ4740=y CONFIG_USB_INVENTRA_DMA=y -CONFIG_JZ4770_PHY=y +CONFIG_PHY_INGENIC_USB=y CONFIG_USB_GADGET=y CONFIG_USB_GADGET_VBUS_DRAW=500 CONFIG_USB_ETH=y From 83a3dfc018943b05b6daf3a6f891833e1aabfa1f Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 23 Jun 2026 17:08:15 +0200 Subject: [PATCH 0095/1054] USB: serial: digi_acceleport: fix broken rx after throttle If the port is closed while throttled, the read urb is never resubmitted and the port will not receive any further data until the device is reconnected (or the driver is rebound). Clear the throttle flags and submit the urb if needed when opening the port. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Johan Hovold --- drivers/usb/serial/digi_acceleport.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index fa5c3539f806..dea039163661 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -1076,6 +1076,7 @@ static int digi_open(struct tty_struct *tty, struct usb_serial_port *port) unsigned char buf[32]; struct digi_port *priv = usb_get_serial_port_data(port); struct ktermios not_termios; + int throttled; /* be sure the device is started up */ if (digi_startup_device(port->serial) != 0) @@ -1103,6 +1104,21 @@ static int digi_open(struct tty_struct *tty, struct usb_serial_port *port) not_termios.c_iflag = ~tty->termios.c_iflag; digi_set_termios(tty, port, ¬_termios); } + + spin_lock_irq(&priv->dp_port_lock); + throttled = priv->dp_throttle_restart; + priv->dp_throttled = 0; + priv->dp_throttle_restart = 0; + spin_unlock_irq(&priv->dp_port_lock); + + if (throttled) { + ret = usb_submit_urb(port->read_urb, GFP_KERNEL); + if (ret) { + dev_err(&port->dev, "failed to submit read urb: %d\n", ret); + return ret; + } + } + return 0; } From 53716a4d745f1dac7aff33f3d1494b701eb2f888 Mon Sep 17 00:00:00 2001 From: Seth Forshee Date: Tue, 2 Jun 2026 21:54:06 +0000 Subject: [PATCH 0096/1054] firmware: arm_ffa: Respect firmware advertised RX/TX buffer size limits FFA_FEATURES reports the minimum size and alignment boundary required for RXTX_MAP. In FF-A v1.2 and later it can also report a maximum buffer size, with zero meaning that no maximum is enforced. The driver only used the minimum value and then rounded it up to PAGE_SIZE before invoking RXTX_MAP after commit 83210251fd70 ("firmware: arm_ffa: Use the correct buffer size during RXTX_MAP"). On systems where PAGE_SIZE is larger than the advertised minimum, this can exceed a non-zero maximum reported by firmware. Older implementations do not advertise a maximum and may also reject the rounded-up size. Decode the maximum size and clamp the page-aligned minimum to it when it is present. If no maximum is advertised and RXTX_MAP rejects the rounded size with INVALID_PARAMETERS, retry with the advertised minimum size. Record drv_info->rxtx_bufsz only after RXTX_MAP succeeds so it reflects the size registered with firmware. While there, also update RXTX_MAP_MIN_BUFSZ() to use FIELD_GET() for consistency. Fixes: 83210251fd70 ("firmware: arm_ffa: Use the correct buffer size during RXTX_MAP") Suggested-by: Sudeep Holla Signed-off-by: Seth Forshee Link: https://patch.msgid.link/20260602-b4-ffa-rxtx-map-fixes-v2-1-7cb06508da84@nvidia.com (sudeep.holla: Minor rewording subject and commit message) Signed-off-by: Sudeep Holla --- drivers/firmware/arm_ffa/driver.c | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c index 0f468362c288..bc2685331b27 100644 --- a/drivers/firmware/arm_ffa/driver.c +++ b/drivers/firmware/arm_ffa/driver.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -59,7 +60,9 @@ (FIELD_PREP(SENDER_ID_MASK, (s)) | FIELD_PREP(RECEIVER_ID_MASK, (r))) #define RXTX_MAP_MIN_BUFSZ_MASK GENMASK(1, 0) -#define RXTX_MAP_MIN_BUFSZ(x) ((x) & RXTX_MAP_MIN_BUFSZ_MASK) +#define RXTX_MAP_MAX_BUFSZ_MASK GENMASK(31, 16) +#define RXTX_MAP_MIN_BUFSZ(x) (FIELD_GET(RXTX_MAP_MIN_BUFSZ_MASK, (x))) +#define RXTX_MAP_MAX_BUFSZ(x) (FIELD_GET(RXTX_MAP_MAX_BUFSZ_MASK, (x))) #define FFA_MAX_NOTIFICATIONS 64 @@ -2101,7 +2104,7 @@ static int ffa_probe(struct platform_device *pdev) { int ret; u32 buf_sz; - size_t rxtx_bufsz = SZ_4K; + size_t rxtx_min_bufsz = SZ_4K, rxtx_max_bufsz = 0, rxtx_bufsz; if (IS_BUILTIN(CONFIG_ARM_FFA_TRANSPORT) && is_protected_kvm_enabled() && !is_pkvm_initialized()) @@ -2132,15 +2135,18 @@ static int ffa_probe(struct platform_device *pdev) ret = ffa_features(FFA_FN_NATIVE(RXTX_MAP), 0, &buf_sz, NULL); if (!ret) { if (RXTX_MAP_MIN_BUFSZ(buf_sz) == 1) - rxtx_bufsz = SZ_64K; + rxtx_min_bufsz = SZ_64K; else if (RXTX_MAP_MIN_BUFSZ(buf_sz) == 2) - rxtx_bufsz = SZ_16K; + rxtx_min_bufsz = SZ_16K; else - rxtx_bufsz = SZ_4K; + rxtx_min_bufsz = SZ_4K; + + rxtx_max_bufsz = RXTX_MAP_MAX_BUFSZ(buf_sz) * SZ_4K; + if (rxtx_max_bufsz != 0 && rxtx_max_bufsz < rxtx_min_bufsz) + rxtx_max_bufsz = rxtx_min_bufsz; } - rxtx_bufsz = PAGE_ALIGN(rxtx_bufsz); - drv_info->rxtx_bufsz = rxtx_bufsz; + rxtx_bufsz = min_not_zero(PAGE_ALIGN(rxtx_min_bufsz), rxtx_max_bufsz); drv_info->rx_buffer = alloc_pages_exact(rxtx_bufsz, GFP_KERNEL); if (!drv_info->rx_buffer) { ret = -ENOMEM; @@ -2156,10 +2162,17 @@ static int ffa_probe(struct platform_device *pdev) ret = ffa_rxtx_map(virt_to_phys(drv_info->tx_buffer), virt_to_phys(drv_info->rx_buffer), rxtx_bufsz / FFA_PAGE_SIZE); + if (ret == -EINVAL && !rxtx_max_bufsz && rxtx_min_bufsz < rxtx_bufsz) { + rxtx_bufsz = rxtx_min_bufsz; + ret = ffa_rxtx_map(virt_to_phys(drv_info->tx_buffer), + virt_to_phys(drv_info->rx_buffer), + rxtx_bufsz / FFA_PAGE_SIZE); + } if (ret) { pr_err("failed to register FFA RxTx buffers\n"); goto free_pages; } + drv_info->rxtx_bufsz = rxtx_bufsz; mutex_init(&drv_info->rx_lock); mutex_init(&drv_info->tx_lock); From 5cff1529a2f9b3461a7f5a6e36a86682fc290534 Mon Sep 17 00:00:00 2001 From: WenTao Liang Date: Sat, 27 Jun 2026 12:29:49 +0800 Subject: [PATCH 0097/1054] ALSA: us144mkii: capture_urb_complete: redundant usb_anchor_urb corrupts anchor list on each resubmission In capture_urb_complete(), usb_anchor_urb() is called on every completion callback, but the URB is already anchored from the initial submission in tascam_trigger_start(). Each redundant call corrupts the anchor's doubly-linked list and inflates the URB refcount. When usb_kill_anchored_urbs() traverses the list during stream stop / suspend / disconnect, the corrupted list leads to use-after-free. Remove the redundant usb_anchor_urb() from the resubmit path. Cc: stable@vger.kernel.org Fixes: c1bb0c13e430 ("ALSA: usb-audio: us144mkii: Implement audio capture and decoding") Signed-off-by: WenTao Liang Link: https://patch.msgid.link/20260627042949.61767-1-vulab@iscas.ac.cn Signed-off-by: Takashi Iwai --- sound/usb/usx2y/us144mkii_capture.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/usb/usx2y/us144mkii_capture.c b/sound/usb/usx2y/us144mkii_capture.c index af120bf62173..fa01da98151a 100644 --- a/sound/usb/usx2y/us144mkii_capture.c +++ b/sound/usb/usx2y/us144mkii_capture.c @@ -302,7 +302,6 @@ void capture_urb_complete(struct urb *urb) } usb_get_urb(urb); - usb_anchor_urb(urb, &tascam->capture_anchor); ret = usb_submit_urb(urb, GFP_ATOMIC); if (ret < 0) { dev_err_ratelimited(tascam->card->dev, @@ -312,6 +311,7 @@ void capture_urb_complete(struct urb *urb) usb_put_urb(urb); atomic_dec( &tascam->active_urbs); /* Decrement on failed resubmission */ + return; } out: usb_put_urb(urb); From 18a4e5cf633fad5c40ac9d936c51bf38db68796d Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Wed, 10 Jun 2026 15:52:01 +0800 Subject: [PATCH 0098/1054] arm64: smp: Fix hot-unplug tearing by forcing unregistration Sashiko review pointed out the following issue[1]. Commit eba4675008a6 ("arm64: arch_register_cpu() variant to check if an ACPI handle is now available.") introduced architectural safety blocks inside arch_unregister_cpu(). If a hot-unplug operation is determined to be a physical hardware removal (where _STA evaluates to !ACPI_STA_DEVICE_PRESENT), or if firmware evaluation fails, it aborts the unregistration transaction early to protect unreadied arm64 infrastructure. However, returning early from arch_unregister_cpu() causes a catastrophic state tearing because the generic ACPI layer (acpi_processor_post_eject()) unconditionally continues its cleanup flow. This leaves the stale sysfs device leaked in the memory, deadlocking any subsequent hot-add attempts on the same CPU. Fix it by simplifying arch_unregister_cpu() to always proceed with the unregistration, as a pr_err_once() warning is sufficient to make it more visible for currently not supported physical CPU removal. Also remove the redundant NULL check on acpi_handle as it cannot be NULL when calling arch_unregister_cpu(). Cc: Catalin Marinas Cc: Jonathan Cameron Cc: James Morse Cc: stable@vger.kernel.org Link: https://sashiko.dev/#/patchset/20260520022023.126670-1-ruanjinjie@huawei.com [1] Fixes: eba4675008a6 ("arm64: arch_register_cpu() variant to check if an ACPI handle is now available.") Suggested-by: Catalin Marinas Signed-off-by: Jinjie Ruan Reviewed-by: Catalin Marinas Signed-off-by: Will Deacon --- arch/arm64/kernel/smp.c | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c index d46022f72075..0584d17f33ba 100644 --- a/arch/arm64/kernel/smp.c +++ b/arch/arm64/kernel/smp.c @@ -535,23 +535,13 @@ void arch_unregister_cpu(int cpu) { acpi_handle acpi_handle = acpi_get_processor_handle(cpu); struct cpu *c = &per_cpu(cpu_devices, cpu); - acpi_status status; unsigned long long sta; - - if (!acpi_handle) { - pr_err_once("Removing a CPU without associated ACPI handle\n"); - return; - } + acpi_status status; status = acpi_evaluate_integer(acpi_handle, "_STA", NULL, &sta); - if (ACPI_FAILURE(status)) - return; - - /* For now do not allow anything that looks like physical CPU HP */ - if (cpu_present(cpu) && !(sta & ACPI_STA_DEVICE_PRESENT)) { + if (!ACPI_FAILURE(status) && + cpu_present(cpu) && !(sta & ACPI_STA_DEVICE_PRESENT)) pr_err_once("Changing CPU present bit is not supported\n"); - return; - } unregister_cpu(c); } From f9a82544c7174851f5c7524622f5966dcafd3a47 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Wed, 10 Jun 2026 15:52:02 +0800 Subject: [PATCH 0099/1054] cpu/hotplug: Fix NULL kobject warning in cpuhp_smt_enable() On arm64, when booting with `maxcpus` greater than the number of present CPUs (e.g., QEMU -smp cpus=4,maxcpus=8), some CPUs are marked as 'present' but have not yet been registered via register_cpu(). Consequently, the per-cpu device objects for these CPUs are not yet initialized. In cpuhp_smt_enable(), the code iterates over all present CPUs. Calling _cpu_up() for these unregistered CPUs eventually leads to sysfs_create_group() being called with a NULL kobject (or a kobject without a directory), triggering the following warning in fs/sysfs/group.c: WARNING: fs/sysfs/group.c:137 at internal_create_group+0x41c/0x4bc, CPU#2: sh/181 [...] Call trace: internal_create_group+0x41c/0x4bc (P) sysfs_create_group+0x18/0x24 topology_add_dev+0x1c/0x28 cpuhp_invoke_callback+0x104/0x20c __cpuhp_invoke_callback_range+0x94/0x11c _cpu_up+0x200/0x37c When booting with ACPI, arm64 smp_prepare_cpus() currently sets all enumerated CPUs as "present" regardless of their status in the MADT. This causes issues with SMT hotplug control. For instance, with QEMU's "-smp 4,maxcpus=8" configuration, the MADT GICC entries are populated as follows: 1. The first four CPUs: `Enabled` set but `Online Capable` not set. 2. The remaining four CPUs: `Online Capable` set but `Enabled` not set to support potential hot-plugging. Fix this by: 1. When booting with ACPI, checking the ACPI_MADT_ENABLED flag in the GICC entry before calling set_cpu_present() during SMP initialization. 2. Properly managing the present mask in acpi_map_cpu() and acpi_unmap_cpu() to support actual CPU hotplug events, This aligns with other architectures like x86 and LoongArch. 3. Update the arm64 CPU hotplug documentation to no longer state that all online-capable vCPUs are marked as present by the kernel at boot time. This ensures that only physically available or explicitly enabled CPUs are in the present mask, keeping the SMT control logic consistent with the actual hardware state. Cc: Catalin Marinas Cc: Jonathan Cameron Cc: James Morse Cc: Yicong Yang Cc: stable@vger.kernel.org Link: https://uefi.org/specs/ACPI/6.5/05_ACPI_Software_Programming_Model.html#gic-cpu-interface-gicc-structure Fixes: eed4583bcf9a ("arm64: Kconfig: Enable HOTPLUG_SMT") Reviewed-by: Catalin Marinas Suggested-by: Catalin Marinas Signed-off-by: Jinjie Ruan Signed-off-by: Will Deacon --- Documentation/arch/arm64/cpu-hotplug.rst | 28 ++++++++++++++---------- arch/arm64/kernel/acpi.c | 2 ++ arch/arm64/kernel/smp.c | 12 +++++++++- 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/Documentation/arch/arm64/cpu-hotplug.rst b/Documentation/arch/arm64/cpu-hotplug.rst index 8fb438bf7781..7c3379b704aa 100644 --- a/Documentation/arch/arm64/cpu-hotplug.rst +++ b/Documentation/arch/arm64/cpu-hotplug.rst @@ -47,11 +47,12 @@ ever have can be described at boot. There are no power-domain considerations as such devices are emulated. CPU Hotplug on virtual systems is supported. It is distinct from physical -CPU Hotplug as all resources are described as ``present``, but CPUs may be -marked as disabled by firmware. Only the CPU's online/offline behaviour is -influenced by firmware. An example is where a virtual machine boots with a -single CPU, and additional CPUs are added once a cloud orchestrator deploys -the workload. +CPU Hotplug as all vCPU resources are statically described in the firmware +configuration tables (e.g. MADT), meaning their maximum possible count is +known at boot. However, vCPUs that are not enabled at boot are not marked +as ``present`` by the kernel until they are hotplugged. An example is where +a virtual machine boots with a single CPU, and additional CPUs are added +once a cloud orchestrator deploys the workload. For a virtual machine, the VMM (e.g. Qemu) plays the part of firmware. @@ -60,16 +61,19 @@ brought online. Firmware can enforce its policy via PSCI's return codes. e.g. ``DENIED``. The ACPI tables must describe all the resources of the virtual machine. CPUs -that firmware wishes to disable either from boot (or later) should not be -``enabled`` in the MADT GICC structures, but should have the ``online capable`` -bit set, to indicate they can be enabled later. The boot CPU must be marked as -``enabled``. The 'always on' GICR structure must be used to describe the -redistributors. +that are hot-pluggable must have the ``online capable`` bit set and the +``enabled`` bit cleared in the MADT GICC structures to indicate they can be +enabled later. The boot CPU must be marked as ``enabled`` with its +``online capable`` bit cleared. The 'always on' GICR structure must be used +to describe the redistributors. CPUs described as ``online capable`` but not ``enabled`` can be set to enabled by the DSDT's Processor object's _STA method. On virtual systems the _STA method -must always report the CPU as ``present``. Changes to the firmware policy can -be notified to the OS via device-check or eject-request. +must always set the ``ACPI_STA_DEVICE_PRESENT`` bit, while toggling the +``ACPI_STA_DEVICE_ENABLED`` bit to reflect its plug status. The kernel will +then dynamically mark the vCPU as ``present`` within the OS when the +``ACPI_STA_DEVICE_ENABLED`` bit becomes set during hot-add. Changes to the +firmware policy can be notified to the OS via device-check or eject-request. CPUs described as ``enabled`` in the static table, should not have their _STA modified dynamically by firmware. Soft-restart features such as kexec will diff --git a/arch/arm64/kernel/acpi.c b/arch/arm64/kernel/acpi.c index 5891f92c2035..681aa2bbc399 100644 --- a/arch/arm64/kernel/acpi.c +++ b/arch/arm64/kernel/acpi.c @@ -448,12 +448,14 @@ int acpi_map_cpu(acpi_handle handle, phys_cpuid_t physid, u32 apci_id, return *pcpu; } + set_cpu_present(*pcpu, true); return 0; } EXPORT_SYMBOL(acpi_map_cpu); int acpi_unmap_cpu(int cpu) { + set_cpu_present(cpu, false); return 0; } EXPORT_SYMBOL(acpi_unmap_cpu); diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c index 0584d17f33ba..cdcdd160e5b6 100644 --- a/arch/arm64/kernel/smp.c +++ b/arch/arm64/kernel/smp.c @@ -556,6 +556,11 @@ struct acpi_madt_generic_interrupt *acpi_cpu_get_madt_gicc(int cpu) } EXPORT_SYMBOL_GPL(acpi_cpu_get_madt_gicc); +static bool acpi_cpu_is_present(int cpu) +{ + return acpi_cpu_get_madt_gicc(cpu)->flags & ACPI_MADT_ENABLED; +} + /* * acpi_map_gic_cpu_interface - parse processor MADT entry * @@ -660,6 +665,10 @@ static void __init acpi_parse_and_init_cpus(void) early_map_cpu_to_node(i, acpi_numa_get_nid(i)); } #else +static bool acpi_cpu_is_present(int cpu) +{ + return false; +} #define acpi_parse_and_init_cpus(...) do { } while (0) #endif @@ -804,7 +813,8 @@ void __init smp_prepare_cpus(unsigned int max_cpus) if (err) continue; - set_cpu_present(cpu, true); + if (acpi_disabled || acpi_cpu_is_present(cpu)) + set_cpu_present(cpu, true); numa_store_cpu_info(cpu); } } From 534eb6940a89ff7ca3f2ab6582f3548ca97674c3 Mon Sep 17 00:00:00 2001 From: Catalin Marinas Date: Wed, 10 Jun 2026 11:37:16 +0100 Subject: [PATCH 0100/1054] arm64: Avoid eager DVMSync reclaim batches with C1-Pro SME erratum The C1-Pro SME DVMSync workaround currently samples mm_cpumask() from arch_tlbbatch_add_pending(). It requires a DSB after every batched TLBI so that the mask read is ordered after the hardware DVMSync, defeating much of the reclaim batching benefit. Introduce the sme_active_cpus mask tracking which CPUs run in user-space with SME enabled and use it for batch flushing instead of accumulating the mm_cpumask() of the unmapped pages. Fixes: 0baba94a9779 ("arm64: errata: Work around early CME DVMSync acknowledgement") Signed-off-by: Catalin Marinas Cc: Will Deacon Tested-by: Joshua Liu Signed-off-by: Will Deacon --- arch/arm64/include/asm/tlbbatch.h | 10 ++----- arch/arm64/include/asm/tlbflush.h | 49 +++++-------------------------- arch/arm64/kernel/fpsimd.c | 10 +++++-- arch/arm64/kernel/process.c | 35 ---------------------- 4 files changed, 17 insertions(+), 87 deletions(-) diff --git a/arch/arm64/include/asm/tlbbatch.h b/arch/arm64/include/asm/tlbbatch.h index 6297631532e5..767f35ea62b3 100644 --- a/arch/arm64/include/asm/tlbbatch.h +++ b/arch/arm64/include/asm/tlbbatch.h @@ -2,17 +2,11 @@ #ifndef _ARCH_ARM64_TLBBATCH_H #define _ARCH_ARM64_TLBBATCH_H -#include - struct arch_tlbflush_unmap_batch { -#ifdef CONFIG_ARM64_ERRATUM_4193714 /* - * Track CPUs that need SME DVMSync on completion of this batch. - * Otherwise, the arm64 HW can do tlb shootdown, so we don't need to - * record cpumask for sending IPI + * For arm64, HW can do TLB shootdown, so we don't need to record a + * cpumask for sending IPIs. */ - cpumask_var_t cpumask; -#endif }; #endif /* _ARCH_ARM64_TLBBATCH_H */ diff --git a/arch/arm64/include/asm/tlbflush.h b/arch/arm64/include/asm/tlbflush.h index d52ac8c17190..e0e84332f51b 100644 --- a/arch/arm64/include/asm/tlbflush.h +++ b/arch/arm64/include/asm/tlbflush.h @@ -82,6 +82,8 @@ static inline unsigned long get_trans_granule(void) #ifdef CONFIG_ARM64_ERRATUM_4193714 +extern cpumask_t sme_active_cpus; + void sme_do_dvmsync(const struct cpumask *mask); static inline void sme_dvmsync(struct mm_struct *mm) @@ -92,42 +94,12 @@ static inline void sme_dvmsync(struct mm_struct *mm) sme_do_dvmsync(mm_cpumask(mm)); } -static inline void sme_dvmsync_add_pending(struct arch_tlbflush_unmap_batch *batch, - struct mm_struct *mm) +static inline void sme_dvmsync_batch(void) { if (!alternative_has_cap_unlikely(ARM64_WORKAROUND_4193714)) return; - /* - * Order the mm_cpumask() read after the hardware DVMSync. - */ - dsb(ish); - if (cpumask_empty(mm_cpumask(mm))) - return; - - /* - * Allocate the batch cpumask on first use. Fall back to an immediate - * IPI for this mm in case of failure. - */ - if (!cpumask_available(batch->cpumask) && - !zalloc_cpumask_var(&batch->cpumask, GFP_ATOMIC)) { - sme_do_dvmsync(mm_cpumask(mm)); - return; - } - - cpumask_or(batch->cpumask, batch->cpumask, mm_cpumask(mm)); -} - -static inline void sme_dvmsync_batch(struct arch_tlbflush_unmap_batch *batch) -{ - if (!alternative_has_cap_unlikely(ARM64_WORKAROUND_4193714)) - return; - - if (!cpumask_available(batch->cpumask)) - return; - - sme_do_dvmsync(batch->cpumask); - cpumask_clear(batch->cpumask); + sme_do_dvmsync(&sme_active_cpus); } #else @@ -135,11 +107,7 @@ static inline void sme_dvmsync_batch(struct arch_tlbflush_unmap_batch *batch) static inline void sme_dvmsync(struct mm_struct *mm) { } -static inline void sme_dvmsync_add_pending(struct arch_tlbflush_unmap_batch *batch, - struct mm_struct *mm) -{ -} -static inline void sme_dvmsync_batch(struct arch_tlbflush_unmap_batch *batch) +static inline void sme_dvmsync_batch(void) { } @@ -285,11 +253,11 @@ static inline void __tlbi_sync_s1ish(struct mm_struct *mm) sme_dvmsync(mm); } -static inline void __tlbi_sync_s1ish_batch(struct arch_tlbflush_unmap_batch *batch) +static inline void __tlbi_sync_s1ish_batch(void) { dsb(ish); __repeat_tlbi_sync(vale1is, 0); - sme_dvmsync_batch(batch); + sme_dvmsync_batch(); } static inline void __tlbi_sync_s1ish_kernel(void) @@ -434,7 +402,7 @@ static inline bool arch_tlbbatch_should_defer(struct mm_struct *mm) */ static inline void arch_tlbbatch_flush(struct arch_tlbflush_unmap_batch *batch) { - __tlbi_sync_s1ish_batch(batch); + __tlbi_sync_s1ish_batch(); } /* @@ -722,7 +690,6 @@ static inline void arch_tlbbatch_add_pending(struct arch_tlbflush_unmap_batch *b __flush_tlb_range(&vma, start, end, PAGE_SIZE, 3, TLBF_NOWALKCACHE | TLBF_NOSYNC); - sme_dvmsync_add_pending(batch, mm); } static inline bool __pte_flags_need_flush(ptval_t oldval, ptval_t newval) diff --git a/arch/arm64/kernel/fpsimd.c b/arch/arm64/kernel/fpsimd.c index 25dc5afe9ba0..e7f1682a3059 100644 --- a/arch/arm64/kernel/fpsimd.c +++ b/arch/arm64/kernel/fpsimd.c @@ -1355,6 +1355,7 @@ void do_sve_acc(unsigned long esr, struct pt_regs *regs) * SME/CME erratum handling. */ static cpumask_t sme_dvmsync_cpus; +cpumask_t sme_active_cpus; /* * These helpers are only called from non-preemptible contexts, so @@ -1368,13 +1369,15 @@ void sme_set_active(void) return; cpumask_set_cpu(cpu, mm_cpumask(current->mm)); + cpumask_set_cpu(cpu, &sme_active_cpus); /* * A subsequent (post ERET) SME access may use a stale address * translation. On C1-Pro, a TLBI+DSB on a different CPU will wait for - * the completion of cpumask_set_cpu() above as it appears in program - * order before the SME access. The post-TLBI+DSB read of mm_cpumask() - * will lead to the IPI being issued. + * the completion of the cpumask_set_cpu() operations above as they + * appear in program order before the SME access. The post-TLBI+DSB + * read of mm_cpumask() or sme_active_cpus will lead to the IPI being + * issued. * * https://lore.kernel.org/r/ablEXwhfKyJW1i7l@J2N7QTR9R3 */ @@ -1392,6 +1395,7 @@ void sme_clear_active(void) * completed on entering EL1. */ cpumask_clear_cpu(cpu, mm_cpumask(current->mm)); + cpumask_clear_cpu(cpu, &sme_active_cpus); } static void sme_dvmsync_ipi(void *unused) diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c index 033643cd4e5e..581f80e9b9b7 100644 --- a/arch/arm64/kernel/process.c +++ b/arch/arm64/kernel/process.c @@ -341,41 +341,8 @@ void flush_thread(void) flush_gcs(); } -#ifdef CONFIG_ARM64_ERRATUM_4193714 - -static void arch_dup_tlbbatch_mask(struct task_struct *dst) -{ - /* - * Clear the inherited cpumask with memset() to cover both cases where - * cpumask_var_t is a pointer or an array. It will be allocated lazily - * in sme_dvmsync_add_pending() if CPUMASK_OFFSTACK=y. - */ - if (alternative_has_cap_unlikely(ARM64_WORKAROUND_4193714)) - memset(&dst->tlb_ubc.arch.cpumask, 0, - sizeof(dst->tlb_ubc.arch.cpumask)); -} - -static void arch_release_tlbbatch_mask(struct task_struct *tsk) -{ - if (alternative_has_cap_unlikely(ARM64_WORKAROUND_4193714)) - free_cpumask_var(tsk->tlb_ubc.arch.cpumask); -} - -#else - -static void arch_dup_tlbbatch_mask(struct task_struct *dst) -{ -} - -static void arch_release_tlbbatch_mask(struct task_struct *tsk) -{ -} - -#endif /* CONFIG_ARM64_ERRATUM_4193714 */ - void arch_release_task_struct(struct task_struct *tsk) { - arch_release_tlbbatch_mask(tsk); fpsimd_release_task(tsk); } @@ -391,8 +358,6 @@ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src) *dst = *src; - arch_dup_tlbbatch_mask(dst); - /* * Drop stale reference to src's sve_state and convert dst to * non-streaming FPSIMD mode. From ff4c5a0de1f2ef7737a8688a86e19301e567020d Mon Sep 17 00:00:00 2001 From: Anshuman Khandual Date: Fri, 26 Jun 2026 02:28:45 +0100 Subject: [PATCH 0101/1054] arm64/mm: Optimize TLB flush in unmap_hotplug_[pmd|pud]_range() Commit 48478b9f7913 ("arm64/mm: Enable batched TLB flush in unmap_hotplug_range") inadvertently introduced redundant TLB invalidation when clearing a block entry, resulting in unnecessary broadcast invalidation on CPUs without support for range-based invalidation. Re-introduce the old behaviour, along with some expanded comments to help people working in this area next time around. Cc: Catalin Marinas Cc: Will Deacon Cc: Ryan Roberts Cc: David Hildenbrand Cc: linux-arm-kernel@lists.infradead.org Cc: linux-kernel@vger.kernel.org Reported-by: Ben Hutchings Closes: https://lore.kernel.org/all/b0d5836032ce3135bfc473f6bff791306d086925.camel@decadent.org.uk/ Fixes: 48478b9f7913 ("arm64/mm: Enable batched TLB flush in unmap_hotplug_range()") Signed-off-by: Anshuman Khandual Reviewed-by: David Hildenbrand (Arm) Reviewed-by: Catalin Marinas [will: Reword comments and commit message] Signed-off-by: Will Deacon --- arch/arm64/mm/mmu.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c index f2be501468ce..a25d8beacc83 100644 --- a/arch/arm64/mm/mmu.c +++ b/arch/arm64/mm/mmu.c @@ -1515,7 +1515,13 @@ static void unmap_hotplug_pmd_range(pud_t *pudp, unsigned long addr, if (free_mapped) { /* CONT blocks are not supported in the vmemmap */ WARN_ON(pmd_cont(pmd)); - flush_tlb_kernel_range(addr, addr + PMD_SIZE); + /* + * Invalidating a block entry requires just + * a single overlapping TLB invalidation, + * so limit the range of the flush to a single + * page. + */ + flush_tlb_kernel_range(addr, addr + PAGE_SIZE); free_hotplug_page_range(pmd_page(pmd), PMD_SIZE, altmap); } @@ -1545,7 +1551,8 @@ static void unmap_hotplug_pud_range(p4d_t *p4dp, unsigned long addr, if (pud_leaf(pud)) { pud_clear(pudp); if (free_mapped) { - flush_tlb_kernel_range(addr, addr + PUD_SIZE); + /* See comment in unmap_hotplug_pmd_range(). */ + flush_tlb_kernel_range(addr, addr + PAGE_SIZE); free_hotplug_page_range(pud_page(pud), PUD_SIZE, altmap); } From 035219a760edb35ae9a9e96beba7f122e26a997b Mon Sep 17 00:00:00 2001 From: Philipp Stanner Date: Mon, 29 Jun 2026 09:56:37 +0200 Subject: [PATCH 0102/1054] dma-buf: dma-fence: Fix potential NULL pointer dereference MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The commit mentioned in the fixes tag below introduced a mechanism through which fence producers can fully decouple from fence consumers. This, desirable, mechanism is based on the fence's signaled-bit as the "decoupling point". A sophisticated interaction between RCU and atomic instructions attempts to ensure that fence consumers can still interact with fence producers through the dma_fence_ops (callback pointers into the producer). This is the desired behavior: to check for decoupling, the signaled-bit is first checked. If it's not yet signaled, RCU ensures that the ops pointer cannot yet be NULL. Hereby, dma_fence_signal_timestamp_locked() first sets the signaled-bit, and then sets the ops pointer to NULL. Readers first load the ops pointer, and then check through the signaled-bit whether the pointer can legally be accessed. These set and load operations could occur out of order on weakly ordered platforms. This problem can be solved very elegantly by using the ops pointer itself as the synchronization point. The pointer is either NULL, or cannot become NULL while it is being used thanks to RCU. Replace the signaled-bit check in dma_fence_timeline_name() and dma_fence_driver_name(). Cc: stable@vger.kernel.org Fixes: f4cc3ab824d6 ("dma-buf: protected fence ops by RCU v8") Signed-off-by: Philipp Stanner Reviewed-by: Boris Brezillon Reviewed-by: Christian König Reviewed-by: Danilo Krummrich Link: https://lore.kernel.org/r/20260629075636.2513214-2-phasta@kernel.org Signed-off-by: Christian König --- drivers/dma-buf/dma-fence.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/dma-buf/dma-fence.c b/drivers/dma-buf/dma-fence.c index 7120610f4850..151c3344459c 100644 --- a/drivers/dma-buf/dma-fence.c +++ b/drivers/dma-buf/dma-fence.c @@ -1167,7 +1167,7 @@ const char __rcu *dma_fence_driver_name(struct dma_fence *fence) /* RCU protection is required for safe access to returned string */ ops = rcu_dereference(fence->ops); - if (!dma_fence_test_signaled_flag(fence)) + if (ops) return (const char __rcu *)ops->get_driver_name(fence); else return (const char __rcu *)"detached-driver"; @@ -1200,7 +1200,7 @@ const char __rcu *dma_fence_timeline_name(struct dma_fence *fence) /* RCU protection is required for safe access to returned string */ ops = rcu_dereference(fence->ops); - if (!dma_fence_test_signaled_flag(fence)) + if (ops) return (const char __rcu *)ops->get_timeline_name(fence); else return (const char __rcu *)"signaled-timeline"; From 77a9298741f8f9e8b963c977f5582ab21c6d3427 Mon Sep 17 00:00:00 2001 From: Baineng Shou Date: Mon, 29 Jun 2026 11:13:46 +0800 Subject: [PATCH 0103/1054] dma-fence: Make dma_fence_dedup_array() robust against 0-count input MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dma_fence_dedup_array() returns 1 when called with num_fences == 0: the for-loop body never executes, j stays at 0, and the final `return ++j` yields 1. This contradicts both the kernel-doc ("Return: Number of unique fences remaining in the array") and the natural expectation that 0 input gives 0 output. The caller __dma_fence_unwrap_merge() bails out via the `if (count == 0 || count == 1)` fast path and so is save. But amdgpu_userq_wait_*() could reach the dedup call with a zero local count and dereference an uninitialized fence slot in the array. Make the contract match the documentation by returning 0 early. This also skips an unnecessary sort() call on an empty array. Cc: stable@vger.kernel.org Signed-off-by: Baineng Shou Reviewed-by: Christian König Fixes: 575ec9b0c2f1 ("dma-fence: Add helper to sort and deduplicate dma_fence arrays") Signed-off-by: Christian König Link: https://lore.kernel.org/r/20260629031346.3875683-1-shoubaineng@gmail.com --- drivers/dma-buf/dma-fence-unwrap.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/dma-buf/dma-fence-unwrap.c b/drivers/dma-buf/dma-fence-unwrap.c index 07fe9bf45aea..cc11c036f2b1 100644 --- a/drivers/dma-buf/dma-fence-unwrap.c +++ b/drivers/dma-buf/dma-fence-unwrap.c @@ -97,6 +97,9 @@ int dma_fence_dedup_array(struct dma_fence **fences, int num_fences) { int i, j; + if (!num_fences) + return 0; + sort(fences, num_fences, sizeof(*fences), fence_cmp, NULL); /* From d2e52d610b9b09694261632340b801a421e0b0c5 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Sat, 6 Jun 2026 16:01:55 -0400 Subject: [PATCH 0104/1054] IB/mad: Drop unmatched RMPP responses before reassembly Kernel-handled RMPP receive processing starts reassembly for active DATA responses before the response is matched to an outstanding send. The normal match happens later, after ib_process_rmpp_recv_wc() has either assembled a complete message or consumed the segment. That ordering lets an unsolicited response that routes to a kernel RMPP agent by the high TID bits allocate or extend RMPP receive state before the full TID and source address are checked against a real request. A reordered burst can therefore reach the receive-side insertion path even though the response would not match any send. For kernel-handled RMPP DATA responses, require the existing ib_find_send_mad() match before entering RMPP reassembly. The matcher already checks the full TID, management class and source address/GID against the agent wait, backlog and in-flight send lists. If there is no match, drop the response without creating RMPP state. This leaves the RMPP window behavior unchanged and only rejects responses that have no corresponding request. Fixes: fa619a77046b ("[PATCH] IB: Add RMPP implementation") Assisted-by: Codex:gpt-5-5-xhigh Signed-off-by: Michael Bommarito Link: https://patch.msgid.link/3170ff3bc389a930bb1641f2caa394a0b2241579.1780774907.git.michael.bommarito@gmail.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/core/mad.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/drivers/infiniband/core/mad.c b/drivers/infiniband/core/mad.c index 8d19613179e3..e0b3b36b8b14 100644 --- a/drivers/infiniband/core/mad.c +++ b/drivers/infiniband/core/mad.c @@ -2031,6 +2031,24 @@ void ib_mark_mad_done(struct ib_mad_send_wr_private *mad_send_wr) change_mad_state(mad_send_wr, IB_MAD_STATE_EARLY_RESP); } +static bool is_kernel_rmpp_data_response(struct ib_mad_agent_private *agent, + struct ib_mad_recv_wc *mad_recv_wc) +{ + const struct ib_mad_hdr *mad_hdr = &mad_recv_wc->recv_buf.mad->mad_hdr; + struct ib_rmpp_mad *rmpp_mad; + + if (!ib_mad_kernel_rmpp_agent(&agent->agent) || + !ib_response_mad(mad_hdr) || + !ib_is_mad_class_rmpp(mad_hdr->mgmt_class)) + return false; + + rmpp_mad = (struct ib_rmpp_mad *)mad_recv_wc->recv_buf.mad; + + return (ib_get_rmpp_flags(&rmpp_mad->rmpp_hdr) & + IB_MGMT_RMPP_FLAG_ACTIVE) && + rmpp_mad->rmpp_hdr.rmpp_type == IB_MGMT_RMPP_TYPE_DATA; +} + static void ib_mad_complete_recv(struct ib_mad_agent_private *mad_agent_priv, struct ib_mad_recv_wc *mad_recv_wc) { @@ -2050,6 +2068,18 @@ static void ib_mad_complete_recv(struct ib_mad_agent_private *mad_agent_priv, } list_add(&mad_recv_wc->recv_buf.list, &mad_recv_wc->rmpp_list); + if (is_kernel_rmpp_data_response(mad_agent_priv, mad_recv_wc)) { + spin_lock_irqsave(&mad_agent_priv->lock, flags); + mad_send_wr = ib_find_send_mad(mad_agent_priv, mad_recv_wc); + spin_unlock_irqrestore(&mad_agent_priv->lock, flags); + + if (!mad_send_wr) { + ib_free_recv_mad(mad_recv_wc); + deref_mad_agent(mad_agent_priv); + return; + } + } + if (ib_mad_kernel_rmpp_agent(&mad_agent_priv->agent)) { mad_recv_wc = ib_process_rmpp_recv_wc(mad_agent_priv, mad_recv_wc); From 0773610eef71c30df3cb4c113c8215625d2a7c23 Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Tue, 26 May 2026 17:03:05 +0200 Subject: [PATCH 0105/1054] ASoC: codecs: lpass-va-macro: Fix LPASS Codec Version for SC7280 According to both the static definition in downstream... yupik-audio-overlay.dtsi: qcom,bolero-version = <4>; #define BOLERO_VERSION_2_0 0x0004) and the runtime detection: CDC_VA_TOP_CSR_CORE_ID_0=0x1 CDC_VA_TOP_CSR_CORE_ID_1=0xf SC7280 has LPASS Codec Version 2.0 and not, as declared with sm8250_va_data LPASS_CODEC_VERSION_1_0. Create new va_macro_data with .version not set to use the runtime detection and correctly get .version = LPASS_CODEC_VERSION_2_0. Fixes: 77212f300bfd ("ASoC: codecs: lpass-va-macro: set the default codec version for sm8250") Signed-off-by: Luca Weiss Reviewed-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260526-sc7280-va-macro-2-0-v1-1-2c1b572fa388@fairphone.com Signed-off-by: Mark Brown --- sound/soc/codecs/lpass-va-macro.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/lpass-va-macro.c b/sound/soc/codecs/lpass-va-macro.c index 528d5b167ecf..58a5798823d7 100644 --- a/sound/soc/codecs/lpass-va-macro.c +++ b/sound/soc/codecs/lpass-va-macro.c @@ -244,6 +244,11 @@ static const struct va_macro_data sm8250_va_data = { .version = LPASS_CODEC_VERSION_1_0, }; +static const struct va_macro_data sc7280_va_data = { + .has_swr_master = false, + .has_npl_clk = false, +}; + static const struct va_macro_data sm8450_va_data = { .has_swr_master = true, .has_npl_clk = true, @@ -1755,7 +1760,7 @@ static const struct dev_pm_ops va_macro_pm_ops = { }; static const struct of_device_id va_macro_dt_match[] = { - { .compatible = "qcom,sc7280-lpass-va-macro", .data = &sm8250_va_data }, + { .compatible = "qcom,sc7280-lpass-va-macro", .data = &sc7280_va_data }, { .compatible = "qcom,sm6115-lpass-va-macro", .data = &sm8450_va_data }, { .compatible = "qcom,sm8250-lpass-va-macro", .data = &sm8250_va_data }, { .compatible = "qcom,sm8450-lpass-va-macro", .data = &sm8450_va_data }, From cceca8cb776b03e83985578f84bd730c30d57f70 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Sat, 13 Jun 2026 23:31:52 -0700 Subject: [PATCH 0106/1054] mtd: rawnand: ndfc: fix gcc uninitialized var Now that this can be built with COMPILE_TEST, an unassigned variable was found. Set to 0 to fix the W=1 error under GCC. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202606141301.iyVdFgl7-lkp@intel.com/ Signed-off-by: Rosen Penev Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/ndfc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/nand/raw/ndfc.c b/drivers/mtd/nand/raw/ndfc.c index a937ca3eeff5..a48274297d3b 100644 --- a/drivers/mtd/nand/raw/ndfc.c +++ b/drivers/mtd/nand/raw/ndfc.c @@ -188,7 +188,7 @@ static int ndfc_probe(struct platform_device *ofdev) const __be32 *reg; u32 ccr; u32 cs; - int err, len; + int err, len = 0; /* Read the reg property to get the chip select */ reg = of_get_property(ofdev->dev.of_node, "reg", &len); From 75c0c09541b49daa08fddbc2c18c2232f4eab7d8 Mon Sep 17 00:00:00 2001 From: Harshit Mogalapalli Date: Sun, 14 Jun 2026 01:10:52 -0700 Subject: [PATCH 0107/1054] mtd: virt_concat: fix use-after-free in mtd_virt_concat_destroy_joins() mtd_concat_destroy() frees item->concat so calling mtd_virt_concat_put_mtd_devices(item->concat) leads to a use after free. Fix this by moving mtd_virt_concat_put_mtd_devices() before mtd_concat_destroy() Fixes: 43db6366fc2d ("mtd: Add driver for concatenating devices") Signed-off-by: Harshit Mogalapalli Reviewed-by: Luca Ceresoli Signed-off-by: Miquel Raynal --- drivers/mtd/mtd_virt_concat.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/mtd_virt_concat.c b/drivers/mtd/mtd_virt_concat.c index 37075ead0f33..a3fb96788e9d 100644 --- a/drivers/mtd/mtd_virt_concat.c +++ b/drivers/mtd/mtd_virt_concat.c @@ -75,8 +75,8 @@ void mtd_virt_concat_destroy_joins(void) if (item->concat) { mtd_device_unregister(mtd); kfree(mtd->name); - mtd_concat_destroy(mtd); mtd_virt_concat_put_mtd_devices(item->concat); + mtd_concat_destroy(mtd); } } } From 82d9a2b45b170f0c52ac61e0e3e23f212cd065f0 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Mon, 15 Jun 2026 14:32:32 +0800 Subject: [PATCH 0108/1054] mtd: nand: ecc-mtk: handle ECC clock enable failures mtk_ecc_get() gets a reference to the ECC platform device, obtains the provider state and then enables the ECC clock before initializing the hardware. The clk_prepare_enable() return value is currently ignored. If enabling the clock fails, the code still touches the ECC registers and returns a live ECC handle to the caller. The provider device reference acquired by of_find_device_by_node() is also kept even though the handle setup failed. Propagate the clock enable error and drop the provider device reference on that failure path. Signed-off-by: Pengpeng Hou Signed-off-by: Miquel Raynal --- drivers/mtd/nand/ecc-mtk.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/nand/ecc-mtk.c b/drivers/mtd/nand/ecc-mtk.c index c75bb8b80cc1..39be2e3e4ee4 100644 --- a/drivers/mtd/nand/ecc-mtk.c +++ b/drivers/mtd/nand/ecc-mtk.c @@ -265,6 +265,7 @@ static struct mtk_ecc *mtk_ecc_get(struct device_node *np) { struct platform_device *pdev; struct mtk_ecc *ecc; + int ret; pdev = of_find_device_by_node(np); if (!pdev) @@ -276,7 +277,12 @@ static struct mtk_ecc *mtk_ecc_get(struct device_node *np) return ERR_PTR(-EPROBE_DEFER); } - clk_prepare_enable(ecc->clk); + ret = clk_prepare_enable(ecc->clk); + if (ret) { + put_device(&pdev->dev); + return ERR_PTR(ret); + } + mtk_ecc_hw_init(ecc); return ecc; From 1a8b2951ba3c35824ff4df0ea7d9f7a554c964a2 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Mon, 15 Jun 2026 14:33:32 +0800 Subject: [PATCH 0109/1054] mtd: rawnand: ingenic: handle ECC clock enable failures ingenic_ecc_get() obtains a provider device reference and then enables the ECC clock before returning the ECC handle. The clk_prepare_enable() return value is currently ignored. If enabling the clock fails, the function still returns the ECC handle and keeps the provider device reference even though the acquire operation did not complete. Return the clock enable error and drop the provider device reference on that failure path. Signed-off-by: Pengpeng Hou Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/ingenic/ingenic_ecc.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/nand/raw/ingenic/ingenic_ecc.c b/drivers/mtd/nand/raw/ingenic/ingenic_ecc.c index 525c34c281b6..beb033705cf3 100644 --- a/drivers/mtd/nand/raw/ingenic/ingenic_ecc.c +++ b/drivers/mtd/nand/raw/ingenic/ingenic_ecc.c @@ -67,6 +67,7 @@ static struct ingenic_ecc *ingenic_ecc_get(struct device_node *np) { struct platform_device *pdev; struct ingenic_ecc *ecc; + int ret; pdev = of_find_device_by_node(np); if (!pdev) @@ -78,7 +79,11 @@ static struct ingenic_ecc *ingenic_ecc_get(struct device_node *np) } ecc = platform_get_drvdata(pdev); - clk_prepare_enable(ecc->clk); + ret = clk_prepare_enable(ecc->clk); + if (ret) { + put_device(&pdev->dev); + return ERR_PTR(ret); + } return ecc; } From 4b45d7836b9526b8776af5f29219615be9417230 Mon Sep 17 00:00:00 2001 From: Harshit Mogalapalli Date: Tue, 16 Jun 2026 03:14:39 -0700 Subject: [PATCH 0110/1054] mtd: virt_concat: fix use-after-free in mtd_virt_concat_destroy() mtd_concat_destroy() frees item->concat so calling mtd_virt_concat_put_mtd_devices(item->concat) after that leads to a use-after-free. Fix it by moving mtd_virt_concat_put_mtd_devices() before mtd_concat_destroy(). Fixes: 43db6366fc2d ("mtd: Add driver for concatenating devices") Signed-off-by: Harshit Mogalapalli Signed-off-by: Miquel Raynal --- drivers/mtd/mtd_virt_concat.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/mtd_virt_concat.c b/drivers/mtd/mtd_virt_concat.c index a3fb96788e9d..c872a8827718 100644 --- a/drivers/mtd/mtd_virt_concat.c +++ b/drivers/mtd/mtd_virt_concat.c @@ -126,8 +126,8 @@ int mtd_virt_concat_destroy(struct mtd_info *mtd) if (concat->mtd.name) { del_mtd_device(&concat->mtd); kfree(concat->mtd.name); - mtd_concat_destroy(&concat->mtd); mtd_virt_concat_put_mtd_devices(item->concat); + mtd_concat_destroy(&concat->mtd); } for (idx = 0; idx < item->count; idx++) From 5d7a08125e69630dc1168d3e9ca5626ded3da807 Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Thu, 18 Jun 2026 02:37:49 +0800 Subject: [PATCH 0111/1054] mtd: spinand: initialize ret in regular page reads spinand_mtd_regular_page_read() returns ret after iterating over the requested pages. If the request contains no data or OOB bytes, the iterator does not run and ret is not assigned. Initialize it to 0 for the empty request path. Signed-off-by: Ruoyu Wang Signed-off-by: Miquel Raynal --- drivers/mtd/nand/spi/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index f86786344d52..74bb5ee83b31 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -822,7 +822,7 @@ static int spinand_mtd_regular_page_read(struct mtd_info *mtd, loff_t from, bool disable_ecc = false; bool ecc_failed = false; unsigned int retry_mode = 0; - int ret; + int ret = 0; old_stats = mtd->ecc_stats; From 97f9e509d839f7e5b9a32105ae322e5223c86786 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 18 Jun 2026 22:58:27 +0200 Subject: [PATCH 0112/1054] mtd: rawnand: ndfc: add CONFIG_OF dependency When compile-testing on x86 without CONFIG_OF, the ndfc driver produces a harmless warning: drivers/mtd/nand/raw/ndfc.c: In function 'ndfc_probe': include/linux/dev_printk.h:154:31: error: 'len' is used uninitialized [-Werror=uninitialized] 154 | dev_printk_index_wrap(_dev_err, KERN_ERR, dev, dev_fmt(fmt), ##__VA_ARGS__) | ^ drivers/mtd/nand/raw/ndfc.c:196:17: note: in expansion of macro 'dev_err' 196 | dev_err(&ofdev->dev, "unable read reg property (%d)\n", len); Limit compile-testing to configurations with CONFIG_OF to trivially avoid this. The driver will still be built in allmodconfig and many randconfig builds. Fixes: 4f2692a5383e ("mtd: rawnand: ndfc: use ioread32be/iowrite32be and allow COMPILE_TEST") Signed-off-by: Arnd Bergmann Reviewed-by: Rosen Penev Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index 64b8b99a3a68..1f4053e531fd 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -72,6 +72,7 @@ config MTD_NAND_AU1550 config MTD_NAND_NDFC tristate "IBM/MCC 4xx NAND controller" depends on 44x || COMPILE_TEST + depends on OF select MTD_NAND_ECC_SW_HAMMING select MTD_NAND_ECC_SW_HAMMING_SMC help From ae9d8058f13238cad5f5d16f676da91187684581 Mon Sep 17 00:00:00 2001 From: Nikolay Ivchenko Date: Sat, 20 Jun 2026 20:06:03 +0300 Subject: [PATCH 0113/1054] mtd: mtdpart: fix uninitialized erasesize on MTDPART_OFS_RETAIN error path When parsing partition layouts, if a partition requested with MTDPART_OFS_RETAIN runs out of space, the allocator jumps directly to 'out_register' to preserve partition numbering. However, this jump bypasses child->erasesize initialization, leaving it at zero. When add_mtd_device() is later called on this child, the registration fails and triggers a WARN_ON() due to the zero ->erasesize. Fix this by zeroing out child->part.offset and child->part.size, and initializing child->erasesize to parent->erasesize. This is the exact same pattern already used just a few lines below in the "out of reach" error check (child->part.offset >= parent_size) to safely register a disabled partition. Reported-by: syzbot+3ae80219c633aca5431c@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=3ae80219c633aca5431c Signed-off-by: Nikolay Ivchenko Signed-off-by: Miquel Raynal --- drivers/mtd/mtdpart.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 795a94e6b482..7f23f8a1b59c 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -118,6 +118,9 @@ static struct mtd_info *allocate_partition(struct mtd_info *parent, part->name, parent_size - child->part.offset, child->part.size); /* register to preserve ordering */ + child->part.offset = 0; + child->part.size = 0; + child->erasesize = parent->erasesize; goto out_register; } } From a74e31d0a2d52c65d9a6647c87b54bf78c0d9317 Mon Sep 17 00:00:00 2001 From: Deepanshu Kartikey Date: Sun, 21 Jun 2026 04:26:25 +0530 Subject: [PATCH 0114/1054] mtd: mtdpart: validate partition bounds in mtd_add_partition() mtd_add_partition() checks that 'length' is positive but does not validate that 'offset + length' fits within the parent partition's size. A userspace caller using the BLKPG_ADD_PARTITION ioctl can supply a crafted large 'length' value that passes the length <= 0 check, causing add_mtd_device() to fire a WARN_ON() when it detects the oversized partition. Fix this by adding explicit bounds checks before allocate_partition() is called: - Reject negative or out-of-range offsets. - Use u64 arithmetic to safely check offset + length <= parent_size, avoiding potential signed integer overflow. Reported-by: syzbot+3ae80219c633aca5431c@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=3ae80219c633aca5431c Signed-off-by: Deepanshu Kartikey Signed-off-by: Miquel Raynal --- drivers/mtd/mtdpart.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 7f23f8a1b59c..4b41550fd374 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -267,6 +267,11 @@ int mtd_add_partition(struct mtd_info *parent, const char *name, if (length <= 0) return -EINVAL; + if (offset < 0 || offset >= (long long)parent_size) + return -EINVAL; + + if ((u64)offset + (u64)length > parent_size) + return -EINVAL; memset(&part, 0, sizeof(part)); part.name = name; part.size = length; From 66fb31358108d10245b9e4ef0eef3e7d9747055e Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Tue, 23 Jun 2026 09:53:17 +0800 Subject: [PATCH 0115/1054] mtd: mtdswap: remove debugfs stats file on teardown mtdswap_add_debugfs() creates an mtdswap_stats debugfs file under the per-MTD debugfs directory, but mtdswap_remove_dev() never removes it before freeing the mtdswap_dev. Store the returned dentry and remove it during device teardown before the driver-private state is freed. Fixes: a32159024620 ("mtd: Add mtdswap block driver") Signed-off-by: Pengpeng Hou Signed-off-by: Miquel Raynal --- drivers/mtd/mtdswap.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/mtdswap.c b/drivers/mtd/mtdswap.c index 866933fc8426..f33f753f0a9f 100644 --- a/drivers/mtd/mtdswap.c +++ b/drivers/mtd/mtdswap.c @@ -125,6 +125,7 @@ struct mtdswap_dev { char *page_buf; char *oob_buf; + struct dentry *debugfs_stats; }; struct mtdswap_oobdata { @@ -1262,7 +1263,8 @@ static int mtdswap_add_debugfs(struct mtdswap_dev *d) if (IS_ERR_OR_NULL(root)) return -1; - debugfs_create_file("mtdswap_stats", S_IRUSR, root, d, &mtdswap_fops); + d->debugfs_stats = debugfs_create_file("mtdswap_stats", 0400, root, + d, &mtdswap_fops); return 0; } @@ -1463,6 +1465,7 @@ static void mtdswap_remove_dev(struct mtd_blktrans_dev *dev) { struct mtdswap_dev *d = MTDSWAP_MBD_TO_MTDSWAP(dev); + debugfs_remove(d->debugfs_stats); del_mtd_blktrans_dev(dev); mtdswap_cleanup(d); kfree(d); From 16f7ec8d5dc100eafd2c8e06cd30340a30b104a1 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Tue, 23 Jun 2026 21:57:29 +0800 Subject: [PATCH 0116/1054] mtd: nand: mtk-ecc: stop on ECC idle timeouts mtk_ecc_wait_idle() logs when the encoder or decoder does not become idle, but returns void. Callers can therefore configure a non-idle ECC engine or read parity bytes after an unconfirmed encoder idle state. Return the idle poll result and propagate it from the enable and encode paths that require the engine to be idle before continuing. Fixes: 1d6b1e464950 ("mtd: mediatek: driver for MTK Smart Device") Signed-off-by: Pengpeng Hou Signed-off-by: Miquel Raynal --- drivers/mtd/nand/ecc-mtk.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/mtd/nand/ecc-mtk.c b/drivers/mtd/nand/ecc-mtk.c index 39be2e3e4ee4..66f0985ef7cd 100644 --- a/drivers/mtd/nand/ecc-mtk.c +++ b/drivers/mtd/nand/ecc-mtk.c @@ -123,8 +123,8 @@ static int mt7622_ecc_regs[] = { [ECC_DECIRQ_STA] = 0x144, }; -static inline void mtk_ecc_wait_idle(struct mtk_ecc *ecc, - enum mtk_ecc_operation op) +static inline int mtk_ecc_wait_idle(struct mtk_ecc *ecc, + enum mtk_ecc_operation op) { struct device *dev = ecc->dev; u32 val; @@ -136,6 +136,8 @@ static inline void mtk_ecc_wait_idle(struct mtk_ecc *ecc, if (ret) dev_warn(dev, "%s NOT idle\n", op == ECC_ENCODE ? "encoder" : "decoder"); + + return ret; } static irqreturn_t mtk_ecc_irq(int irq, void *id) @@ -318,7 +320,11 @@ int mtk_ecc_enable(struct mtk_ecc *ecc, struct mtk_ecc_config *config) return ret; } - mtk_ecc_wait_idle(ecc, op); + ret = mtk_ecc_wait_idle(ecc, op); + if (ret) { + mutex_unlock(&ecc->lock); + return ret; + } ret = mtk_ecc_config(ecc, config); if (ret) { @@ -418,7 +424,9 @@ int mtk_ecc_encode(struct mtk_ecc *ecc, struct mtk_ecc_config *config, if (ret) goto timeout; - mtk_ecc_wait_idle(ecc, ECC_ENCODE); + ret = mtk_ecc_wait_idle(ecc, ECC_ENCODE); + if (ret) + goto timeout; /* Program ECC bytes to OOB: per sector oob = FDM + ECC + SPARE */ len = (config->strength * ecc->caps->parity_bits + 7) >> 3; From b33ab1dd80f5c1742f49eb6ec7b337c5ffcf3d32 Mon Sep 17 00:00:00 2001 From: Fabio Porcedda Date: Fri, 12 Jun 2026 13:39:16 +0200 Subject: [PATCH 0117/1054] USB: serial: option: add Telit Cinterion FE990D50 compositions Add support for Telit Cinterion FE990D50 compositions: 0x990: RNDIS + tty (AT/NMEA) + tty (AT) + tty (AT) + tty (AT) + tty (diag) + ADPL + adb T: Bus=01 Lev=01 Prnt=01 Port=06 Cnt=03 Dev#= 3 Spd=480 MxCh= 0 D: Ver= 2.10 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1 P: Vendor=1bc7 ProdID=0990 Rev=06.06 S: Manufacturer=Telit Cinterion S: Product=FE990 S: SerialNumber=90b6a3ed C: #Ifs=10 Cfg#= 1 Atr=e0 MxPwr=500mA I: If#= 0 Alt= 0 #EPs= 1 Cls=ef(misc ) Sub=04 Prot=01 Driver=rndis_host E: Ad=82(I) Atr=03(Int.) MxPS= 8 Ivl=32ms I: If#= 1 Alt= 0 #EPs= 2 Cls=0a(data ) Sub=00 Prot=00 Driver=rndis_host E: Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=60 Driver=option E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=83(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=84(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 3 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=03(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=85(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=86(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 4 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=04(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=87(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=88(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 5 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=05(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=89(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=8a(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 6 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=30 Driver=option E: Ad=06(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=8b(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 7 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=ff Prot=80 Driver=(none) E: Ad=8c(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 8 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=ff Prot=70 Driver=(none) E: Ad=8d(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 9 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=42 Prot=01 Driver=(none) E: Ad=07(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=8e(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms 0x991: rmnet + tty (AT/NMEA) + tty (AT) + tty (AT) + tty (AT) + tty (diag) + ADPL + adb T: Bus=01 Lev=01 Prnt=01 Port=06 Cnt=03 Dev#= 9 Spd=480 MxCh= 0 D: Ver= 2.10 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1 P: Vendor=1bc7 ProdID=0991 Rev=06.06 S: Manufacturer=Telit Cinterion S: Product=FE990 S: SerialNumber=90b6a3ed C: #Ifs= 9 Cfg#= 1 Atr=e0 MxPwr=500mA I: If#= 0 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=50 Driver=(none) E: Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=82(I) Atr=03(Int.) MxPS= 8 Ivl=32ms I: If#= 1 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=60 Driver=option E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=83(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=84(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=03(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=85(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=86(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 3 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=04(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=87(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=88(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 4 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=05(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=89(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=8a(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 5 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=30 Driver=option E: Ad=06(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=8b(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 6 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=ff Prot=80 Driver=(none) E: Ad=8c(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 7 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=ff Prot=70 Driver=(none) E: Ad=8d(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 8 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=42 Prot=01 Driver=(none) E: Ad=07(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=8e(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms 0x992: MBIM + tty (AT/NMEA) + tty (AT) + tty (AT) + tty (AT) + tty (diag) + ADPL + adb T: Bus=01 Lev=01 Prnt=01 Port=06 Cnt=03 Dev#= 12 Spd=480 MxCh= 0 D: Ver= 2.10 Cls=ef(misc ) Sub=02 Prot=01 MxPS=64 #Cfgs= 1 P: Vendor=1bc7 ProdID=0992 Rev=06.06 S: Manufacturer=Telit Cinterion S: Product=FE990 S: SerialNumber=90b6a3ed C: #Ifs=10 Cfg#= 1 Atr=e0 MxPwr=500mA I: If#= 0 Alt= 0 #EPs= 1 Cls=02(commc) Sub=0e Prot=00 Driver=cdc_mbim E: Ad=82(I) Atr=03(Int.) MxPS= 64 Ivl=32ms I: If#= 1 Alt= 1 #EPs= 2 Cls=0a(data ) Sub=00 Prot=02 Driver=cdc_mbim E: Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=60 Driver=option E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=83(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=84(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 3 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=03(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=85(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=86(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 4 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=04(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=87(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=88(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 5 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=05(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=89(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=8a(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 6 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=30 Driver=option E: Ad=06(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=8b(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 7 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=ff Prot=80 Driver=(none) E: Ad=8c(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 8 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=ff Prot=70 Driver=(none) E: Ad=8d(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 9 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=42 Prot=01 Driver=(none) E: Ad=07(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=8e(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms 0x993: ECM + tty (AT/NMEA) + tty (AT) + tty (AT) + tty (AT) + tty (diag) + ADPL + adb T: Bus=01 Lev=01 Prnt=01 Port=06 Cnt=03 Dev#= 15 Spd=480 MxCh= 0 D: Ver= 2.10 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1 P: Vendor=1bc7 ProdID=0993 Rev=06.06 S: Manufacturer=Telit Cinterion S: Product=FE990 S: SerialNumber=90b6a3ed C: #Ifs=10 Cfg#= 1 Atr=e0 MxPwr=500mA I: If#= 0 Alt= 0 #EPs= 1 Cls=02(commc) Sub=06 Prot=00 Driver=cdc_ether E: Ad=82(I) Atr=03(Int.) MxPS= 16 Ivl=32ms I: If#= 1 Alt= 1 #EPs= 2 Cls=0a(data ) Sub=00 Prot=00 Driver=cdc_ether E: Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=60 Driver=option E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=83(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=84(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 3 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=03(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=85(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=86(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 4 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=04(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=87(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=88(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 5 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=05(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=89(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=8a(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 6 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=30 Driver=option E: Ad=06(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=8b(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 7 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=ff Prot=80 Driver=(none) E: Ad=8c(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 8 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=ff Prot=70 Driver=(none) E: Ad=8d(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 9 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=42 Prot=01 Driver=(none) E: Ad=07(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=8e(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms Cc: stable@vger.kernel.org Signed-off-by: Fabio Porcedda Signed-off-by: Johan Hovold --- drivers/usb/serial/option.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 4c4009b8a46d..7275f4e7f569 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -1325,6 +1325,22 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_CC864_SINGLE) }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_DE910_DUAL) }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_UE910_V2) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0990, 0xff, 0xff, 0x30), /* Telit FE990D50 (RNDIS) */ + .driver_info = NCTRL(6) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0990, 0xff, 0xff, 0x40) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0990, 0xff, 0xff, 0x60) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0991, 0xff, 0xff, 0x30), /* Telit FE990D50 (rmnet) */ + .driver_info = NCTRL(5) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0991, 0xff, 0xff, 0x40) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0991, 0xff, 0xff, 0x60) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0992, 0xff, 0xff, 0x30), /* Telit FE990D50 (MBIM) */ + .driver_info = NCTRL(6) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0992, 0xff, 0xff, 0x40) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0992, 0xff, 0xff, 0x60) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0993, 0xff, 0xff, 0x30), /* Telit FE990D50 (ECM) */ + .driver_info = NCTRL(6) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0993, 0xff, 0xff, 0x40) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0993, 0xff, 0xff, 0x60) }, { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1031, 0xff), /* Telit LE910C1-EUX */ .driver_info = NCTRL(0) | RSVD(3) }, { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1033, 0xff), /* Telit LE910C1-EUX (ECM) */ From d322d820e0b0f16260e929de3f4e6b33b242c91c Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Mon, 15 Jun 2026 12:40:35 +0800 Subject: [PATCH 0118/1054] spi: dw: fix first spi transfer with dma always fallback to PIO Even with proper dma engine support, the first spi transfer always fallback to PIO, the reason is the dws->n_bytes is 0 after initialization, so the dw_spi_can_dma() calling from __spi_map_msg() return false, thus both tx_sg_mapped and rx_sg_mapped are false, so for the first spi transfer, the spi_xfer_is_dma_mapped() reports false thus fallback to PIO. Although this brings no harm, we can simply fix this issue by calcuating the "n_bytes" from xfer->bits_per_word. Signed-off-by: Jisheng Zhang Link: https://patch.msgid.link/20260615044039.9750-2-jszhang@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-dw-dma.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-dw-dma.c b/drivers/spi/spi-dw-dma.c index fe726b9b1780..bd70a7ed8067 100644 --- a/drivers/spi/spi-dw-dma.c +++ b/drivers/spi/spi-dw-dma.c @@ -247,11 +247,12 @@ static bool dw_spi_can_dma(struct spi_controller *ctlr, { struct dw_spi *dws = spi_controller_get_devdata(ctlr); enum dma_slave_buswidth dma_bus_width; + u8 n_bytes = roundup_pow_of_two(BITS_TO_BYTES(xfer->bits_per_word)); if (xfer->len <= dws->fifo_len) return false; - dma_bus_width = dw_spi_dma_convert_width(dws->n_bytes); + dma_bus_width = dw_spi_dma_convert_width(n_bytes); return dws->dma_addr_widths & BIT(dma_bus_width); } From 991af5d809a1697c4225120358b6b2cf9eb3c4ff Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Mon, 15 Jun 2026 12:40:36 +0800 Subject: [PATCH 0119/1054] spi: dw: use the correct error msg if request_irq() fails If request_irq() fails, report "can not request IRQ" rather than "can not get IRQ" which may be misread as platform_get_irq() failure. Signed-off-by: Jisheng Zhang Link: https://patch.msgid.link/20260615044039.9750-3-jszhang@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-dw-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c index 9a85a3ce5652..fcaf3191f381 100644 --- a/drivers/spi/spi-dw-core.c +++ b/drivers/spi/spi-dw-core.c @@ -947,7 +947,7 @@ int dw_spi_add_controller(struct device *dev, struct dw_spi *dws) ret = request_irq(dws->irq, dw_spi_irq, IRQF_SHARED, dev_name(dev), ctlr); if (ret < 0 && ret != -ENOTCONN) { - dev_err(dev, "can not get IRQ\n"); + dev_err(dev, "can not request IRQ\n"); goto err_free_ctlr; } From 1dbbc7f98cde1e2433c1b1617053ae2ffab00086 Mon Sep 17 00:00:00 2001 From: Lizhi Hou Date: Wed, 10 Jun 2026 22:51:48 -0700 Subject: [PATCH 0120/1054] accel/amdxdna: Fix amdxdna_client lifetime race during device removal In amdxdna_remove(), all amdxdna_client structures are freed after calling drm_dev_unplug(). However, drm_dev_unplug() does not force existing file descriptors to be closed, so amdxdna_drm_close() may be called after amdxdna_remove() has completed. As a result, accessing client->pid for debug output in amdxdna_drm_close() can lead to a use-after-free, since the access is not protected by drm_dev_enter(). Fix this by decoupling hardware teardown from client cleanup. amdxdna_remove() only performs hardware-related cleanup, while per-client resources are released from amdxdna_drm_close() when the corresponding file is closed. Fixes: be462c97b7df ("accel/amdxdna: Add hardware context") Reviewed-by: Mario Limonciello (AMD) Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/20260611055150.3070216-1-lizhi.hou@amd.com --- drivers/accel/amdxdna/amdxdna_pci_drv.c | 26 ++++++++++++------------- drivers/accel/amdxdna/amdxdna_pci_drv.h | 1 + 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/drivers/accel/amdxdna/amdxdna_pci_drv.c b/drivers/accel/amdxdna/amdxdna_pci_drv.c index 65489bb3f2b0..593766682940 100644 --- a/drivers/accel/amdxdna/amdxdna_pci_drv.c +++ b/drivers/accel/amdxdna/amdxdna_pci_drv.c @@ -138,9 +138,11 @@ static int amdxdna_drm_open(struct drm_device *ddev, struct drm_file *filp) xdna->dev_info->dev_heap_max_size); mutex_init(&client->mm_lock); + mutex_lock(&xdna->client_lock); mutex_lock(&xdna->dev_lock); list_add_tail(&client->node, &xdna->client_list); mutex_unlock(&xdna->dev_lock); + mutex_unlock(&xdna->client_lock); filp->driver_priv = client; client->filp = filp; @@ -174,18 +176,14 @@ static void amdxdna_drm_close(struct drm_device *ddev, struct drm_file *filp) { struct amdxdna_client *client = filp->driver_priv; struct amdxdna_dev *xdna = to_xdna_dev(ddev); - int idx; XDNA_DBG(xdna, "closing pid %d", client->pid); - if (!drm_dev_enter(&xdna->ddev, &idx)) - return; - + mutex_lock(&xdna->client_lock); mutex_lock(&xdna->dev_lock); amdxdna_client_cleanup(client); mutex_unlock(&xdna->dev_lock); - - drm_dev_exit(idx); + mutex_unlock(&xdna->client_lock); } static int amdxdna_drm_get_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) @@ -371,6 +369,10 @@ static int amdxdna_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (!xdna->dev_info) return -ENODEV; + ret = drmm_mutex_init(ddev, &xdna->client_lock); + if (ret) + return ret; + drmm_mutex_init(ddev, &xdna->dev_lock); init_rwsem(&xdna->notifier_lock); INIT_LIST_HEAD(&xdna->client_list); @@ -442,18 +444,16 @@ static void amdxdna_remove(struct pci_dev *pdev) drm_dev_unplug(&xdna->ddev); amdxdna_sysfs_fini(xdna); + mutex_lock(&xdna->client_lock); mutex_lock(&xdna->dev_lock); - client = list_first_entry_or_null(&xdna->client_list, - struct amdxdna_client, node); - while (client) { - amdxdna_client_cleanup(client); - - client = list_first_entry_or_null(&xdna->client_list, - struct amdxdna_client, node); + list_for_each_entry(client, &xdna->client_list, node) { + amdxdna_hwctx_remove_all(client); + amdxdna_sva_fini(client); } xdna->dev_info->ops->fini(xdna); mutex_unlock(&xdna->dev_lock); + mutex_unlock(&xdna->client_lock); amdxdna_iommu_fini(xdna); } diff --git a/drivers/accel/amdxdna/amdxdna_pci_drv.h b/drivers/accel/amdxdna/amdxdna_pci_drv.h index 34271c14d359..a997d27a504d 100644 --- a/drivers/accel/amdxdna/amdxdna_pci_drv.h +++ b/drivers/accel/amdxdna/amdxdna_pci_drv.h @@ -120,6 +120,7 @@ struct amdxdna_dev { struct mutex dev_lock; /* per device lock */ struct list_head client_list; + struct mutex client_lock; /* client_list */ struct amdxdna_fw_ver fw_ver; struct rw_semaphore notifier_lock; /* for mmu notifier*/ struct workqueue_struct *notifier_wq; From 5c72124186d6983e90b7c44229fcb768e3ff769a Mon Sep 17 00:00:00 2001 From: Lizhi Hou Date: Wed, 10 Jun 2026 22:51:49 -0700 Subject: [PATCH 0121/1054] accel/amdxdna: Fix notifier_wq lifetime race during device removal amdxdna_remove() destroys notifier_wq. If amdxdna_gem_obj_free() is called after device removal, it may attempt to flush notifier_wq, resulting in a use-after-free. Fix the race by allocating notifier_wq with drmm_alloc_ordered_workqueue(), so its lifetime is managed by DRM and remains valid until all managed resources are released. Fixes: e486147c912f ("accel/amdxdna: Add BO import and export") Reviewed-by: Mario Limonciello (AMD) Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/20260611055150.3070216-2-lizhi.hou@amd.com --- drivers/accel/amdxdna/amdxdna_pci_drv.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/accel/amdxdna/amdxdna_pci_drv.c b/drivers/accel/amdxdna/amdxdna_pci_drv.c index 593766682940..e94d8290a807 100644 --- a/drivers/accel/amdxdna/amdxdna_pci_drv.c +++ b/drivers/accel/amdxdna/amdxdna_pci_drv.c @@ -392,9 +392,9 @@ static int amdxdna_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (ret) return ret; - xdna->notifier_wq = alloc_ordered_workqueue("notifier_wq", WQ_MEM_RECLAIM); - if (!xdna->notifier_wq) { - ret = -ENOMEM; + xdna->notifier_wq = drmm_alloc_ordered_workqueue(ddev, "notifier_wq", WQ_MEM_RECLAIM); + if (IS_ERR(xdna->notifier_wq)) { + ret = PTR_ERR(xdna->notifier_wq); goto iommu_fini; } @@ -403,7 +403,7 @@ static int amdxdna_probe(struct pci_dev *pdev, const struct pci_device_id *id) mutex_unlock(&xdna->dev_lock); if (ret) { XDNA_ERR(xdna, "Hardware init failed, ret %d", ret); - goto destroy_notifier_wq; + goto iommu_fini; } ret = amdxdna_sysfs_init(xdna); @@ -427,8 +427,6 @@ static int amdxdna_probe(struct pci_dev *pdev, const struct pci_device_id *id) mutex_lock(&xdna->dev_lock); xdna->dev_info->ops->fini(xdna); mutex_unlock(&xdna->dev_lock); -destroy_notifier_wq: - destroy_workqueue(xdna->notifier_wq); iommu_fini: amdxdna_iommu_fini(xdna); return ret; @@ -439,8 +437,6 @@ static void amdxdna_remove(struct pci_dev *pdev) struct amdxdna_dev *xdna = pci_get_drvdata(pdev); struct amdxdna_client *client; - destroy_workqueue(xdna->notifier_wq); - drm_dev_unplug(&xdna->ddev); amdxdna_sysfs_fini(xdna); From b4a0500fdf6e61a6c5f92ff2e61bc91578075803 Mon Sep 17 00:00:00 2001 From: Lizhi Hou Date: Wed, 10 Jun 2026 22:51:50 -0700 Subject: [PATCH 0122/1054] accel/amdxdna: Fix iommu domain lifetime race during device removal When force_iova mode is enabled, amdxdna_remove() frees xdna->domain. If amdxdna_gem_obj_free() is called after device removal, it may attempt to access xdna->domain, resulting in a use-after-free. Fix the race by adding freeing xdna->domain as a managed release action, so its lifetime is managed by DRM and remains valid until all managed resources are released. Fixes: ece3e8980907 ("accel/amdxdna: Allow forcing IOVA-based DMA via module parameter") Reviewed-by: Mario Limonciello (AMD) Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/20260611055150.3070216-3-lizhi.hou@amd.com --- drivers/accel/amdxdna/amdxdna_iommu.c | 43 ++++++++++++++++++--------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/drivers/accel/amdxdna/amdxdna_iommu.c b/drivers/accel/amdxdna/amdxdna_iommu.c index eff00131d0f8..4f245b969eef 100644 --- a/drivers/accel/amdxdna/amdxdna_iommu.c +++ b/drivers/accel/amdxdna/amdxdna_iommu.c @@ -4,6 +4,7 @@ */ #include +#include #include #include @@ -153,10 +154,30 @@ void amdxdna_iommu_free(struct amdxdna_dev *xdna, size_t size, free_pages((unsigned long)cpu_addr, get_order(size)); } +static void amdxdna_cleanup_force_iova(struct drm_device *dev, void *res) +{ + struct amdxdna_dev *xdna = to_xdna_dev(dev); + + if (xdna->domain) { + iommu_detach_group(xdna->domain, xdna->group); + put_iova_domain(&xdna->iovad); + iova_cache_put(); + iommu_domain_free(xdna->domain); + } + + iommu_group_put(xdna->group); +} + +void amdxdna_iommu_fini(struct amdxdna_dev *xdna) +{ + if (xdna->group && !xdna->domain) + iommu_group_put(xdna->group); +} + int amdxdna_iommu_init(struct amdxdna_dev *xdna) { unsigned long order; - int ret; + int ret = 0; xdna->group = iommu_group_get(xdna->ddev.dev); if (!xdna->group || !force_iova) @@ -182,8 +203,14 @@ int amdxdna_iommu_init(struct amdxdna_dev *xdna) if (ret) goto put_iova; + ret = drmm_add_action(&xdna->ddev, amdxdna_cleanup_force_iova, NULL); + if (ret) + goto detach_group; + return 0; +detach_group: + iommu_detach_group(xdna->domain, xdna->group); put_iova: put_iova_domain(&xdna->iovad); iova_cache_put(); @@ -191,20 +218,8 @@ int amdxdna_iommu_init(struct amdxdna_dev *xdna) iommu_domain_free(xdna->domain); put_group: iommu_group_put(xdna->group); + xdna->group = NULL; xdna->domain = NULL; return ret; } - -void amdxdna_iommu_fini(struct amdxdna_dev *xdna) -{ - if (xdna->domain) { - iommu_detach_group(xdna->domain, xdna->group); - put_iova_domain(&xdna->iovad); - iova_cache_put(); - iommu_domain_free(xdna->domain); - } - - if (xdna->group) - iommu_group_put(xdna->group); -} From 91a6dba5183d269b8215d6170c079b654ef4cab7 Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Mon, 29 Jun 2026 10:57:22 +0800 Subject: [PATCH 0123/1054] MAINTAINERS: ASoC: SOF: add AMD reviewer for Sound Open Firmware SOF spans multiple vendors and hardware paths. AMD ships ACP-based platforms and contributes to the shared SOF tree, so list an AMD reviewer (R:) on the SOF MAINTAINERS entry for balanced review and correct get_maintainer coverage when shared SOF code changes. Add: R: Vijendar Mukunda Signed-off-by: Vijendar Mukunda Reviewed-by: Liam Girdwood Signed-off-by: Bard Liao Link: https://patch.msgid.link/20260629025722.1982120-1-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 15011f5752a9..558bc04f2e45 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -25377,6 +25377,7 @@ M: Bard Liao M: Daniel Baluta R: Kai Vehmanen R: Pierre-Louis Bossart +R: Vijendar Mukunda L: sound-open-firmware@alsa-project.org (moderated for non-subscribers) S: Supported W: https://github.com/thesofproject/linux/ From cb36d81e751173c4b59b5e561c596c925144ea48 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 25 Jun 2026 13:23:56 -1000 Subject: [PATCH 0124/1054] sched_ext: Pin parent scx_sched across a child sub-scheduler's lifetime A child sub-scheduler dereferences its parent scx_sched throughout its life, e.g., in scx_sub_disable() which reparents the child's tasks and calls parent->ops.sub_detach() after unlinking from the parent. However, the parent is pinned only through parent->sub_kset, which is dropped during disable. The parent scx_sched can be RCU-freed while a child is still disabling. Take a direct reference on the parent in scx_alloc_and_add_sched(), dropped in scx_sched_free_rcu_work(), so a parent always outlives its descendants. Signed-off-by: Tejun Heo --- kernel/sched/ext/ext.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/kernel/sched/ext/ext.c b/kernel/sched/ext/ext.c index aecbb021d6d7..3b2e13bc924b 100644 --- a/kernel/sched/ext/ext.c +++ b/kernel/sched/ext/ext.c @@ -4916,6 +4916,8 @@ static void scx_sched_free_rcu_work(struct work_struct *work) cgroup_put(sch_cgroup(sch)); if (sch->sub_kset) kobject_put(&sch->sub_kset->kobj); + if (scx_parent(sch)) + kobject_put(&scx_parent(sch)->kobj); #endif /* CONFIG_EXT_SUB_SCHED */ for_each_possible_cpu(cpu) { @@ -6863,12 +6865,19 @@ static struct scx_sched *scx_alloc_and_add_sched(struct scx_enable_cmd *cmd, INIT_LIST_HEAD(&sch->children); INIT_LIST_HEAD(&sch->sibling); - if (parent) + if (parent) { + /* + * Pin @parent for @sch's lifetime. The kobject hierarchy pins + * it only via @parent->sub_kset, which is dropped during + * disable. Released in scx_sched_free_rcu_work(). + */ + kobject_get(&parent->kobj); ret = kobject_init_and_add(&sch->kobj, &scx_ktype, &parent->sub_kset->kobj, "sub-%llu", cgroup_id(cgrp)); - else + } else { ret = kobject_init_and_add(&sch->kobj, &scx_ktype, NULL, "root"); + } if (ret < 0) { RCU_INIT_POINTER(ops->priv, NULL); From 6697091b386a4e2830bdd38512c87a4befff2b32 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Tue, 2 Jun 2026 16:45:39 +0000 Subject: [PATCH 0125/1054] iio: adc: ad7380: select REGMAP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The AD7380 driver uses generic regmap types and APIs. However, its Kconfig entry does not select REGMAP. As a result, AD7380 can be enabled from an allnoconfig-derived config with SPI_MASTER=y while REGMAP remains unset, causing ad7380.o to fail to build. Fixes: b095217c104b ("iio: adc: ad7380: new driver for AD7380 ADCs") Signed-off-by: Samuel Moelius Reviewed-by: Andy Shevchenko Reviewed-by: Nuno Sá Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 1c663c98c6c9..6fb0766ca27a 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -328,6 +328,7 @@ config AD7298 config AD7380 tristate "Analog Devices AD7380 ADC driver" depends on SPI_MASTER + select REGMAP select SPI_OFFLOAD select IIO_BUFFER select IIO_BUFFER_DMAENGINE From adf4bc07f814da8329278d32600147f5a150938c Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Wed, 3 Jun 2026 20:16:40 +0800 Subject: [PATCH 0126/1054] iio: adc: ti-ads1119: fix PM reference leak in buffer preenable ads1119_triggered_buffer_preenable() resumes the device with pm_runtime_resume_and_get() before starting a conversion. If i2c_smbus_write_byte() fails, the function returns the error directly and leaves the runtime PM usage counter elevated. The matching postdisable callback is not called when preenable fails, so the reference is leaked and the device may remain runtime-active indefinitely. Store the I2C transfer result in ret and drop the runtime PM reference on failure before returning the error. Fixes: a9306887eba41 ("iio: adc: ti-ads1119: Add driver") Signed-off-by: Guangshuo Li Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-ads1119.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/ti-ads1119.c b/drivers/iio/adc/ti-ads1119.c index d31f3d6eb781..b0f04741ddc6 100644 --- a/drivers/iio/adc/ti-ads1119.c +++ b/drivers/iio/adc/ti-ads1119.c @@ -459,7 +459,11 @@ static int ads1119_triggered_buffer_preenable(struct iio_dev *indio_dev) if (ret) return ret; - return i2c_smbus_write_byte(st->client, ADS1119_CMD_START_SYNC); + ret = i2c_smbus_write_byte(st->client, ADS1119_CMD_START_SYNC); + if (ret) + pm_runtime_put_autosuspend(dev); + + return ret; } static int ads1119_triggered_buffer_postdisable(struct iio_dev *indio_dev) From e74c0d0eef7e1fa9fd387b81b2787b4581e0b11c Mon Sep 17 00:00:00 2001 From: Ariana Lazar Date: Thu, 4 Jun 2026 16:46:47 +0300 Subject: [PATCH 0127/1054] iio: dac: mcp47feb02: Fix passing uninitialized vref1_uV for no Vref1 case Ensure that if a device has Vref1 but reading the regulator returns an error, mcp47feb02_init_ctrl_regs() is not called with an uninitialized vref1_uV value. Also add a device_property_present() check for the Vref1 supply before reading the regulator. Fixes: dd154646d292 ("iio: dac: mcp47feb02: Fix Vref validation [1-999] case") Reported-by: Dan Carpenter Closes: https://lore.kernel.org/all/adiPnla0M5EzvgD-@stanley.mountain/ Signed-off-by: Ariana Lazar Reviewed-by: David Lechner Signed-off-by: Jonathan Cameron --- drivers/iio/dac/mcp47feb02.c | 37 +++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/drivers/iio/dac/mcp47feb02.c b/drivers/iio/dac/mcp47feb02.c index 217f78e44af1..a823c2a673a2 100644 --- a/drivers/iio/dac/mcp47feb02.c +++ b/drivers/iio/dac/mcp47feb02.c @@ -1136,26 +1136,33 @@ static int mcp47feb02_probe(struct i2c_client *client) vdd_uV = ret; - ret = devm_regulator_get_enable_read_voltage(dev, "vref"); - if (ret > 0) { - vref_uV = ret; + if (device_property_present(dev, "vref-supply")) { + vref_uV = devm_regulator_get_enable_read_voltage(dev, "vref"); + if (vref_uV < 0) + return vref_uV; + + if (vref_uV == 0) + return dev_err_probe(dev, -EINVAL, "Vref is 0 uV.\n"); + data->use_vref = true; } else { vref_uV = 0; - dev_dbg(dev, "using internal band gap as voltage reference.\n"); - dev_dbg(dev, "Vref is unavailable.\n"); + dev_dbg(dev, "Using internal band gap as voltage reference.\n"); } - if (chip_features->have_ext_vref1) { - ret = devm_regulator_get_enable_read_voltage(dev, "vref1"); - if (ret > 0) { - vref1_uV = ret; - data->use_vref1 = true; - } else { - vref1_uV = 0; - dev_dbg(dev, "using internal band gap as voltage reference 1.\n"); - dev_dbg(dev, "Vref1 is unavailable.\n"); - } + if (chip_features->have_ext_vref1 && + device_property_present(dev, "vref1-supply")) { + vref1_uV = devm_regulator_get_enable_read_voltage(dev, "vref1"); + if (vref1_uV < 0) + return vref1_uV; + + if (vref1_uV == 0) + return dev_err_probe(dev, -EINVAL, "Vref1 is 0 uV.\n"); + + data->use_vref1 = true; + } else { + vref1_uV = 0; + dev_dbg(dev, "Using internal band gap as voltage reference 1.\n"); } ret = mcp47feb02_init_ctrl_regs(data); From ce0e1cae26096fe959a0da5563a6d6d5a801d5fb Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Sat, 13 Jun 2026 02:18:39 -0500 Subject: [PATCH 0128/1054] iio: accel: bmc150: clamp the device-reported FIFO frame count __bmc150_accel_fifo_flush() copies the number of samples the device reports in its hardware FIFO into an on-stack buffer u16 buffer[BMC150_ACCEL_FIFO_LENGTH * 3]; which is sized for at most BMC150_ACCEL_FIFO_LENGTH (32) samples. The frame count is read from the FIFO_STATUS register and only masked to its 7 valid bits: count = val & 0x7F; so it can be 0..127. The only other limit applied to it is the optional caller-supplied sample budget: if (samples && count > samples) count = samples; which does not constrain count on the flush-all path (samples == 0), and leaves it well above 32 whenever samples is larger. count samples are then transferred into buffer[]: bmc150_accel_fifo_transfer(data, (u8 *)buffer, count); bmc150_accel_fifo_transfer() reads count * 6 bytes through regmap, so a malfunctioning, malicious or counterfeit accelerometer (or an attacker tampering with the I2C/SPI bus) that reports up to 127 frames writes up to 762 bytes into the 192-byte buffer: a stack out-of-bounds write of up to 570 bytes that clobbers the stack canary, saved registers and the return address. Clamp count to BMC150_ACCEL_FIFO_LENGTH, the number of samples buffer[] is sized for, before the transfer, mirroring the watermark clamp already done in bmc150_accel_set_watermark(). A well-formed flush reports at most BMC150_ACCEL_FIFO_LENGTH frames, so legitimate devices are unaffected. Fixes: 3bbec9773389 ("iio: bmc150_accel: add support for hardware fifo") Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas Signed-off-by: Jonathan Cameron --- drivers/iio/accel/bmc150-accel-core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/iio/accel/bmc150-accel-core.c b/drivers/iio/accel/bmc150-accel-core.c index 2398eb7e12cd..dc8a6285cf3d 100644 --- a/drivers/iio/accel/bmc150-accel-core.c +++ b/drivers/iio/accel/bmc150-accel-core.c @@ -991,6 +991,8 @@ static int __bmc150_accel_fifo_flush(struct iio_dev *indio_dev, if (samples && count > samples) count = samples; + count = min_t(u8, count, BMC150_ACCEL_FIFO_LENGTH); + ret = bmc150_accel_fifo_transfer(data, (u8 *)buffer, count); if (ret) return ret; From 44a5fd874bb6873bdaec59f722c1d57832fbc9df Mon Sep 17 00:00:00 2001 From: Biren Pandya Date: Sun, 14 Jun 2026 12:45:46 +0530 Subject: [PATCH 0129/1054] iio: accel: kxsd9: fix runtime PM imbalance on write_raw() error kxsd9_write_raw() takes a runtime PM reference with pm_runtime_get_sync() but returns -EINVAL directly when a scale with a non-zero integer part is requested, skipping the matching pm_runtime_put_autosuspend(). This leaks a runtime PM usage-counter reference on every such write, after which the device can no longer autosuspend. Set the error code and fall through to the existing put instead of returning early. Fixes: 9a9a369d6178 ("iio: accel: kxsd9: Deploy system and runtime PM") Signed-off-by: Biren Pandya Assisted-by: Claude:claude-opus-4-8 coccinelle Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/accel/kxsd9.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/iio/accel/kxsd9.c b/drivers/iio/accel/kxsd9.c index 4717d80fc24a..7ac885d94d7f 100644 --- a/drivers/iio/accel/kxsd9.c +++ b/drivers/iio/accel/kxsd9.c @@ -147,8 +147,9 @@ static int kxsd9_write_raw(struct iio_dev *indio_dev, if (mask == IIO_CHAN_INFO_SCALE) { /* Check no integer component */ if (val) - return -EINVAL; - ret = kxsd9_write_scale(indio_dev, val2); + ret = -EINVAL; + else + ret = kxsd9_write_scale(indio_dev, val2); } pm_runtime_put_autosuspend(st->dev); From fbe67ff37a6fd855a6c097f84f3738bd13d0a898 Mon Sep 17 00:00:00 2001 From: Biren Pandya Date: Sun, 14 Jun 2026 12:45:48 +0530 Subject: [PATCH 0130/1054] iio: pressure: mpl115: fix runtime PM leak on read error mpl115_read_raw() takes a runtime PM reference with pm_runtime_get_sync() before reading the processed pressure or raw temperature, but on the read error path it returns without calling pm_runtime_put_autosuspend(). Each failed read therefore leaks a runtime PM reference and prevents the device from autosuspending. Drop the reference before checking the return value so both the success and error paths are balanced. Fixes: 0c3a333524a3 ("iio: pressure: mpl115: Implementing low power mode by shutdown gpio") Signed-off-by: Biren Pandya Assisted-by: Claude:claude-opus-4-8 coccinelle Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/mpl115.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/pressure/mpl115.c b/drivers/iio/pressure/mpl115.c index 830a5065c008..16e112b796ba 100644 --- a/drivers/iio/pressure/mpl115.c +++ b/drivers/iio/pressure/mpl115.c @@ -106,18 +106,18 @@ static int mpl115_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_PROCESSED: pm_runtime_get_sync(data->dev); ret = mpl115_comp_pressure(data, val, val2); + pm_runtime_put_autosuspend(data->dev); if (ret < 0) return ret; - pm_runtime_put_autosuspend(data->dev); return IIO_VAL_INT_PLUS_MICRO; case IIO_CHAN_INFO_RAW: pm_runtime_get_sync(data->dev); /* temperature -5.35 C / LSB, 472 LSB is 25 C */ ret = mpl115_read_temp(data); + pm_runtime_put_autosuspend(data->dev); if (ret < 0) return ret; - pm_runtime_put_autosuspend(data->dev); *val = ret >> 6; return IIO_VAL_INT; From 38b72267b7e22768a1f26d9935de4e1752a1dc85 Mon Sep 17 00:00:00 2001 From: Biren Pandya Date: Sun, 14 Jun 2026 12:45:49 +0530 Subject: [PATCH 0131/1054] iio: light: gp2ap002: fix runtime PM leak on read error gp2ap002_read_raw() calls pm_runtime_get_sync() before reading the lux value, but if gp2ap002_get_lux() fails, it returns directly. This skips the pm_runtime_put_autosuspend() call at the "out" label, permanently leaking a runtime PM reference and preventing the device from autosuspending. Replace the direct return with a "goto out" to ensure the reference is properly dropped on the error path. Fixes: f6dbf83c17cb ("iio: light: gp2ap002: Take runtime PM reference on light read") Signed-off-by: Biren Pandya Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/light/gp2ap002.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/light/gp2ap002.c b/drivers/iio/light/gp2ap002.c index c83f67ff2464..a8db514cca5e 100644 --- a/drivers/iio/light/gp2ap002.c +++ b/drivers/iio/light/gp2ap002.c @@ -258,7 +258,7 @@ static int gp2ap002_read_raw(struct iio_dev *indio_dev, case IIO_LIGHT: ret = gp2ap002_get_lux(gp2ap002); if (ret < 0) - return ret; + goto out; *val = ret; ret = IIO_VAL_INT; goto out; From e561b35633f450ee607e87a6401d97f156a0cd54 Mon Sep 17 00:00:00 2001 From: Maxwell Doose Date: Fri, 12 Jun 2026 19:58:10 -0500 Subject: [PATCH 0132/1054] iio: adc: lpc32xx: Initialize completion before requesting IRQ In the report from Jaeyoung Chung: "lpc32xx_adc_probe() in drivers/iio/adc/lpc32xx_adc.c registers its interrupt handler with devm_request_irq() before it initializes st->completion with init_completion(). If an interrupt arrives after devm_request_irq() and before init_completion(), the handler calls complete() on an uninitialized completion, causing a kernel panic. The probe path, in lpc32xx_adc_probe(): iodev = devm_iio_device_alloc(&pdev->dev, sizeof(*st)); /* st kzalloc-zeroed */ ... retval = devm_request_irq(&pdev->dev, irq, lpc32xx_adc_isr, 0, LPC32XXAD_NAME, st); /* register handler */ ... init_completion(&st->completion); /* initialize completion */ lpc32xx_adc_isr() calls complete(): complete(&st->completion); If the device raises an interrupt before init_completion() runs, complete() acquires the uninitialized wait.lock and walks the zeroed task_list in swake_up_locked(). The zeroed task_list makes list_empty() return false, so swake_up_locked() dereferences a NULL list entry, triggering a KASAN wild-memory-access." Fix the chance of a spurious IRQ causing an uninitialized pointer dereference by moving init_completion() above devm_request_irq(). Fixes: 7901b2a1453e ("staging:iio:adc:lpc32xx rename local state structure to _state") Reported-by: Sangyun Kim Reported-by: Kyungwook Boo Reported-by: Jaeyoung Chung Closes: https://lore.kernel.org/linux-iio/20260610115700.774689-1-jjy600901@snu.ac.kr/ Signed-off-by: Maxwell Doose Reviewed-by: Vladimir Zapolskiy Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/lpc32xx_adc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/adc/lpc32xx_adc.c b/drivers/iio/adc/lpc32xx_adc.c index 43a7bc8158b5..db3a602327ff 100644 --- a/drivers/iio/adc/lpc32xx_adc.c +++ b/drivers/iio/adc/lpc32xx_adc.c @@ -179,6 +179,8 @@ static int lpc32xx_adc_probe(struct platform_device *pdev) if (irq < 0) return irq; + init_completion(&st->completion); + retval = devm_request_irq(&pdev->dev, irq, lpc32xx_adc_isr, 0, LPC32XXAD_NAME, st); if (retval < 0) { @@ -197,8 +199,6 @@ static int lpc32xx_adc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, iodev); - init_completion(&st->completion); - iodev->name = LPC32XXAD_NAME; iodev->info = &lpc32xx_adc_iio_info; iodev->modes = INDIO_DIRECT_MODE; From 3ee2128b6f0eb0be7b6cb8f6e0f1f113a65201a0 Mon Sep 17 00:00:00 2001 From: Maxwell Doose Date: Fri, 12 Jun 2026 19:58:11 -0500 Subject: [PATCH 0133/1054] iio: adc: spear: Initialize completion before requesting IRQ In the report from Jaeyoung Chung: "spear_adc_probe() in drivers/iio/adc/spear_adc.c registers its interrupt handler with devm_request_irq() before it initializes st->completion with init_completion(). If an interrupt arrives after devm_request_irq() and before init_completion(), the handler calls complete() on an uninitialized completion, causing a kernel panic. The probe path, in spear_adc_probe(): iodev = devm_iio_device_alloc(&pdev->dev, sizeof(*st)); /* st kzalloc-zeroed */ ... retval = devm_request_irq(&pdev->dev, irq, spear_adc_isr, 0, LPC32XXAD_NAME, st); /* register handler */ ... init_completion(&st->completion); /* initialize completion */ spear_adc_isr() calls complete(): complete(&st->completion); If the device raises an interrupt before init_completion() runs, complete() acquires the uninitialized wait.lock and walks the zeroed task_list in swake_up_locked(). The zeroed task_list makes list_empty() return false, so swake_up_locked() dereferences a NULL list entry, triggering a KASAN wild-memory-access." Fix the chance of a spurious IRQ causing an uninitialized pointer dereference by moving init_completion() above devm_request_irq(). Fixes: b586e5d9eee0 ("staging:iio:adc:spear rename device specific state structure to _state") Reported-by: Sangyun Kim Reported-by: Kyungwook Boo Reported-by: Jaeyoung Chung Closes: https://lore.kernel.org/linux-iio/20260610115700.774689-1-jjy600901@snu.ac.kr/ Signed-off-by: Maxwell Doose Reviewed-by: Vladimir Zapolskiy Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/spear_adc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/iio/adc/spear_adc.c b/drivers/iio/adc/spear_adc.c index 4be722406bb5..ab02a14682ed 100644 --- a/drivers/iio/adc/spear_adc.c +++ b/drivers/iio/adc/spear_adc.c @@ -283,6 +283,7 @@ static int spear_adc_probe(struct platform_device *pdev) st = iio_priv(indio_dev); st->dev = dev; + init_completion(&st->completion); mutex_init(&st->lock); /* @@ -329,8 +330,6 @@ static int spear_adc_probe(struct platform_device *pdev) spear_adc_configure(st); - init_completion(&st->completion); - indio_dev->name = SPEAR_ADC_MOD_NAME; indio_dev->info = &spear_adc_info; indio_dev->modes = INDIO_DIRECT_MODE; From f784fcea450617055d2d12eec5b2f6e0e38bf878 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Wed, 10 Jun 2026 16:29:09 +0800 Subject: [PATCH 0134/1054] HID: sensor-hub: Add sensor_hub_input_attr_read_values() for multi-byte reads sensor_hub_input_attr_get_raw_value() is limited to returning a single 32-bit value, which is insufficient for sensors that report data larger than 32 bits, such as a quaternion with four s16 elements. Add sensor_hub_input_attr_read_values() that accepts a caller-provided buffer and accumulates incoming data until the buffer is full. The two paths are distinguished in sensor_hub_raw_event() by pending.max_raw_size being non-zero, preserving backward compatibility. Signed-off-by: Srinivas Pandruvada Co-developed-by: Zhang Lixu Signed-off-by: Zhang Lixu Acked-by: Jiri Kosina Reviewed-by: Andy Shevchenko Cc: Signed-off-by: Jonathan Cameron --- drivers/hid/hid-sensor-hub.c | 77 +++++++++++++++++++++++++++++++--- include/linux/hid-sensor-hub.h | 25 +++++++++++ 2 files changed, 96 insertions(+), 6 deletions(-) diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c index 90666ff629de..34f710c465b8 100644 --- a/drivers/hid/hid-sensor-hub.c +++ b/drivers/hid/hid-sensor-hub.c @@ -286,6 +286,54 @@ int sensor_hub_get_feature(struct hid_sensor_hub_device *hsdev, u32 report_id, } EXPORT_SYMBOL_GPL(sensor_hub_get_feature); +int sensor_hub_input_attr_read_values(struct hid_sensor_hub_device *hsdev, + u32 usage_id, u32 attr_usage_id, + u32 report_id, + enum sensor_hub_read_flags flag, + u32 buffer_size, u8 *buffer) +{ + struct sensor_hub_data *data = hid_get_drvdata(hsdev->hdev); + struct hid_report *report; + unsigned long flags; + long cycles; + int ret; + + report = sensor_hub_report(report_id, hsdev->hdev, HID_INPUT_REPORT); + if (!report) + return -EINVAL; + + mutex_lock(hsdev->mutex_ptr); + if (flag == SENSOR_HUB_SYNC) { + memset(&hsdev->pending, 0, sizeof(hsdev->pending)); + init_completion(&hsdev->pending.ready); + hsdev->pending.usage_id = usage_id; + hsdev->pending.attr_usage_id = attr_usage_id; + hsdev->pending.max_raw_size = buffer_size; + hsdev->pending.raw_data = buffer; + + spin_lock_irqsave(&data->lock, flags); + hsdev->pending.status = true; + spin_unlock_irqrestore(&data->lock, flags); + } + mutex_lock(&data->mutex); + hid_hw_request(hsdev->hdev, report, HID_REQ_GET_REPORT); + mutex_unlock(&data->mutex); + ret = 0; + if (flag == SENSOR_HUB_SYNC) { + cycles = wait_for_completion_interruptible_timeout(&hsdev->pending.ready, + HZ * 5); + if (cycles == 0) + ret = -ETIMEDOUT; + else if (cycles < 0) + ret = cycles; + + hsdev->pending.status = false; + } + mutex_unlock(hsdev->mutex_ptr); + + return ret; +} +EXPORT_SYMBOL_GPL(sensor_hub_input_attr_read_values); int sensor_hub_input_attr_get_raw_value(struct hid_sensor_hub_device *hsdev, u32 usage_id, @@ -478,6 +526,8 @@ static int sensor_hub_raw_event(struct hid_device *hdev, struct hid_collection *collection = NULL; void *priv = NULL; struct hid_sensor_hub_device *hsdev = NULL; + u32 copy_size; + u32 avail; hid_dbg(hdev, "sensor_hub_raw_event report id:0x%x size:%d type:%d\n", report->id, size, report->type); @@ -518,12 +568,27 @@ static int sensor_hub_raw_event(struct hid_device *hdev, hsdev->pending.attr_usage_id == report->field[i]->logical)) { hid_dbg(hdev, "data was pending ...\n"); - hsdev->pending.raw_data = kmemdup(ptr, sz, GFP_ATOMIC); - if (hsdev->pending.raw_data) - hsdev->pending.raw_size = sz; - else - hsdev->pending.raw_size = 0; - complete(&hsdev->pending.ready); + if (hsdev->pending.max_raw_size) { + if (hsdev->pending.index < hsdev->pending.max_raw_size) { + avail = hsdev->pending.max_raw_size - hsdev->pending.index; + copy_size = clamp(sz, 0U, avail); + + memcpy(hsdev->pending.raw_data + hsdev->pending.index, + ptr, copy_size); + hsdev->pending.index += copy_size; + if (hsdev->pending.index >= hsdev->pending.max_raw_size) { + hsdev->pending.raw_size = hsdev->pending.index; + complete(&hsdev->pending.ready); + } + } + } else { + hsdev->pending.raw_data = kmemdup(ptr, sz, GFP_ATOMIC); + if (hsdev->pending.raw_data) + hsdev->pending.raw_size = sz; + else + hsdev->pending.raw_size = 0; + complete(&hsdev->pending.ready); + } } if (callback->capture_sample) { if (report->field[i]->logical) diff --git a/include/linux/hid-sensor-hub.h b/include/linux/hid-sensor-hub.h index e71056553108..ab5cc8db3fbb 100644 --- a/include/linux/hid-sensor-hub.h +++ b/include/linux/hid-sensor-hub.h @@ -43,6 +43,8 @@ struct hid_sensor_hub_attribute_info { * @attr_usage_id: Usage Id of a field, e.g. X-axis for a gyro. * @raw_size: Response size for a read request. * @raw_data: Place holder for received response. + * @index: Current write index into raw_data for multi-byte reads. + * @max_raw_size: Total buffer size for multi-byte reads; 0 for single-value reads. */ struct sensor_hub_pending { bool status; @@ -51,6 +53,8 @@ struct sensor_hub_pending { u32 attr_usage_id; int raw_size; u8 *raw_data; + u32 index; + u32 max_raw_size; }; /** @@ -183,6 +187,27 @@ int sensor_hub_input_attr_get_raw_value(struct hid_sensor_hub_device *hsdev, bool is_signed ); +/** + * sensor_hub_input_attr_read_values() - Synchronous multi-byte read request + * @hsdev: Hub device instance. + * @usage_id: Attribute usage id of parent physical device as per spec + * @attr_usage_id: Attribute usage id as per spec + * @report_id: Report id to look for + * @flag: Synchronous or asynchronous read + * @buffer_size: Size of the buffer in bytes + * @buffer: Buffer to store the read data + * + * Issues a synchronous or asynchronous read request for an input attribute, + * accumulating data into the provided buffer until it is full. + * Return: 0 on success, -ETIMEDOUT if the device did not respond, or a + * negative error code. + */ +int sensor_hub_input_attr_read_values(struct hid_sensor_hub_device *hsdev, + u32 usage_id, u32 attr_usage_id, + u32 report_id, + enum sensor_hub_read_flags flag, + u32 buffer_size, u8 *buffer); + /** * sensor_hub_set_feature() - Feature set request * @hsdev: Hub device instance. From 3ce8d099e0afc5a7da75a2007a67f67c4f5a4af1 Mon Sep 17 00:00:00 2001 From: Zhang Lixu Date: Wed, 10 Jun 2026 16:29:10 +0800 Subject: [PATCH 0135/1054] iio: hid-sensor-rotation: Fix stale or zero output when reading raw values When reading the raw quaternion attribute (in_rot_quaternion_raw), the driver currently returns either all zeros (if the sensor was never enabled) or stale data (if the sensor was previously enabled) because it reads from the internal buffer without explicitly requesting a new sample from the sensor. To fix this, power up the sensor, call sensor_hub_input_attr_read_values() to issue a synchronous GET_REPORT and receive the full quaternion data directly into a local buffer, then decode the four components. Fixes: fc18dddc0625 ("iio: hid-sensors: Added device rotation support") Signed-off-by: Zhang Lixu Reviewed-by: Andy Shevchenko Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/orientation/hid-sensor-rotation.c | 40 ++++++++++++++++++- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/drivers/iio/orientation/hid-sensor-rotation.c b/drivers/iio/orientation/hid-sensor-rotation.c index 4a11e4555099..1c6f02374f3c 100644 --- a/drivers/iio/orientation/hid-sensor-rotation.c +++ b/drivers/iio/orientation/hid-sensor-rotation.c @@ -86,6 +86,13 @@ static int dev_rot_read_raw(struct iio_dev *indio_dev, long mask) { struct dev_rot_state *rot_state = iio_priv(indio_dev); + struct hid_sensor_hub_device *hsdev = rot_state->common_attributes.hsdev; + struct hid_sensor_hub_attribute_info *info = &rot_state->quaternion; + u32 usage_id = HID_USAGE_SENSOR_ORIENT_QUATERNION; + union { + s16 val16[4]; + s32 val32[4]; + } raw_buf; int ret_type; int i; @@ -95,8 +102,37 @@ static int dev_rot_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: if (size >= 4) { - for (i = 0; i < 4; ++i) - vals[i] = rot_state->scan.sampled_vals[i]; + if (info->size <= 0 || info->size > sizeof(raw_buf)) + return -EINVAL; + + hid_sensor_power_state(&rot_state->common_attributes, true); + + ret_type = sensor_hub_input_attr_read_values(hsdev, + hsdev->usage, + usage_id, + info->report_id, + SENSOR_HUB_SYNC, + info->size, + (u8 *)&raw_buf); + + hid_sensor_power_state(&rot_state->common_attributes, false); + + if (ret_type < 0) + return ret_type; + + switch (info->size) { + case sizeof(raw_buf.val16): + for (i = 0; i < ARRAY_SIZE(raw_buf.val16); i++) + vals[i] = raw_buf.val16[i]; + break; + case sizeof(raw_buf.val32): + for (i = 0; i < ARRAY_SIZE(raw_buf.val32); i++) + vals[i] = raw_buf.val32[i]; + break; + default: + return -EINVAL; + } + ret_type = IIO_VAL_INT_MULTIPLE; *val_len = 4; } else From 6e1b9bff1202da55c464e36bd34a2b6863d7fe30 Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Thu, 4 Jun 2026 09:42:46 +0800 Subject: [PATCH 0136/1054] iio: imu: adis: add IRQF_NO_THREAD to non-FIFO trigger IRQ devm_adis_probe_trigger() registers iio_trigger_generic_data_rdy_poll() through devm_request_irq() on the non-FIFO path, but it does not add IRQF_NO_THREAD to the IRQ flags. When the kernel is booted with forced IRQ threading, the parent IRQ can otherwise be threaded by the IRQ core and the subsequent IIO trigger child IRQ is then dispatched from irq/... thread context instead of hardirq context. Because iio_trigger_generic_data_rdy_poll() immediately drives iio_trigger_poll(), this violates the hardirq-only IIO trigger helper contract and can push downstream trigger consumers through the wrong execution context. Add IRQF_NO_THREAD on top of the existing adis->irq_flag value for the non-FIFO request_irq() path, while preserving the current trigger polarity and IRQF_NO_AUTOEN behavior. Fixes: fec86c6b8369 ("iio: imu: adis: Add Managed device functions") Cc: stable@vger.kernel.org Signed-off-by: Runyu Xiao Signed-off-by: Jonathan Cameron --- drivers/iio/imu/adis_trigger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/imu/adis_trigger.c b/drivers/iio/imu/adis_trigger.c index d76e13cbac68..3e6a7af6ab01 100644 --- a/drivers/iio/imu/adis_trigger.c +++ b/drivers/iio/imu/adis_trigger.c @@ -94,7 +94,7 @@ int devm_adis_probe_trigger(struct adis *adis, struct iio_dev *indio_dev) else ret = devm_request_irq(&adis->spi->dev, adis->spi->irq, &iio_trigger_generic_data_rdy_poll, - adis->irq_flag, + adis->irq_flag | IRQF_NO_THREAD, indio_dev->name, adis->trig); if (ret) From cd5a6a5096b246e10600da3ac47a1274ce9573c8 Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Thu, 4 Jun 2026 09:42:47 +0800 Subject: [PATCH 0137/1054] iio: imu: bmi160: add IRQF_NO_THREAD to data-ready trigger IRQ bmi160_probe_trigger() registers iio_trigger_generic_data_rdy_poll() through devm_request_irq(), but it passes only irq_type and does not add IRQF_NO_THREAD. When the kernel is booted with forced IRQ threading, the parent IRQ can otherwise be threaded by the IRQ core and the subsequent IIO trigger child IRQ is dispatched from irq/... thread context instead of hardirq context. Because the handler immediately pushes the event into iio_trigger_poll(), this violates the hardirq-only IIO trigger helper contract and can drive downstream trigger consumers through the wrong execution context. Add IRQF_NO_THREAD on top of irq_type when registering the BMI160 data- ready trigger handler. Fixes: 895bf81e6bbf ("iio:bmi160: add drdy interrupt support") Cc: stable@vger.kernel.org Signed-off-by: Runyu Xiao Signed-off-by: Jonathan Cameron --- drivers/iio/imu/bmi160/bmi160_core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/iio/imu/bmi160/bmi160_core.c b/drivers/iio/imu/bmi160/bmi160_core.c index 4abb83b75e2e..86f6ecfd64aa 100644 --- a/drivers/iio/imu/bmi160/bmi160_core.c +++ b/drivers/iio/imu/bmi160/bmi160_core.c @@ -788,7 +788,8 @@ int bmi160_probe_trigger(struct iio_dev *indio_dev, int irq, u32 irq_type) ret = devm_request_irq(&indio_dev->dev, irq, &iio_trigger_generic_data_rdy_poll, - irq_type, "bmi160", data->trig); + irq_type | IRQF_NO_THREAD, + "bmi160", data->trig); if (ret) return ret; From 55052184ac9011db2ea983e54d6c21f0b1079a12 Mon Sep 17 00:00:00 2001 From: Herman van Hazendonk Date: Tue, 16 Jun 2026 15:02:04 +0200 Subject: [PATCH 0138/1054] iio: common: st_sensors: honour channel endianness in read_axis_data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit st_sensors_read_axis_data() unconditionally decoded multi-byte results with get_unaligned_le16() / get_unaligned_le24() regardless of the channel's declared scan_type.endianness. For every ST sensor that has used this helper since it was introduced this happened to be fine because the ST IMU/accel/gyro/pressure families publish their data registers as little-endian and the channel specs in those drivers declare IIO_LE accordingly. The LSM303DLH magnetometer however publishes its X/Y/Z output as a pair of big-endian bytes (the H register sits at the lower address, 0x03/0x05/0x07, and the L register immediately after), and its channel specs in st_magn_core.c correctly declare IIO_BE -- but read_axis_data() ignored that and decoded as little-endian, swapping the high and low bytes of every magnetometer sample. The LSM303DLHC and LSM303DLM share the same st_magn_16bit_channels (IIO_BE) and were therefore byte-swapped by the same bug; users of those parts will see different in_magn_*_raw values after this fix lands. The bug is most visible on a stationary chip: in earth's field the true X reading is small and the high byte sits at 0x00, so swapping the bytes pins sysfs X at exactly the low byte's pattern (e.g. 0x00F0 = 240). Y and Z still appear "to vary" because their magnitudes are larger and the noise in the low byte produces big swings in the swapped high byte: before (LSM303DLH flat, sysfs in_magn_*_raw): X=240 (stuck), Y= 12032..23296, Z=-16128..-9728 after (direct i2c-dev big-endian decode, same chip same orientation): X≈-4096, Y≈210, Z≈80 (sensible values reflecting earth's ambient field at low gauss range) Fix read_axis_data() to dispatch on ch->scan_type.endianness and call get_unaligned_be16() / get_unaligned_be24() when the channel declares IIO_BE. Existing IIO_LE consumers (st_accel, st_gyro, st_pressure, st_lsm6dsx and others) are unaffected because their channel specs already declare IIO_LE and the LE path is unchanged. While restructuring the branches, replace the previously implicit silent-success-with-uninitialised-*data fall-through for byte_for_channel outside 1..3 with an explicit return -EINVAL. No in-tree ST sensor publishes such a channel, but the new behaviour is strictly safer than handing userspace garbage. Fixes: 23491b513bcd ("iio:common: Add STMicroelectronics common library") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 sparse smatch clang-analyzer coccinelle checkpatch Assisted-by: Sashiko:claude-opus-4-7 Signed-off-by: Herman van Hazendonk Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- .../iio/common/st_sensors/st_sensors_core.c | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c index dbc5e16fbde4..76f91696f66a 100644 --- a/drivers/iio/common/st_sensors/st_sensors_core.c +++ b/drivers/iio/common/st_sensors/st_sensors_core.c @@ -498,6 +498,7 @@ static int st_sensors_read_axis_data(struct iio_dev *indio_dev, u8 *outdata; struct st_sensor_data *sdata = iio_priv(indio_dev); unsigned int byte_for_channel; + u32 tmp; byte_for_channel = DIV_ROUND_UP(ch->scan_type.realbits + ch->scan_type.shift, 8); @@ -508,12 +509,22 @@ static int st_sensors_read_axis_data(struct iio_dev *indio_dev, if (err < 0) return err; - if (byte_for_channel == 1) - *data = (s8)*outdata; - else if (byte_for_channel == 2) - *data = (s16)get_unaligned_le16(outdata); - else if (byte_for_channel == 3) - *data = (s32)sign_extend32(get_unaligned_le24(outdata), 23); + if (byte_for_channel == 1) { + tmp = *outdata; + } else if (byte_for_channel == 2) { + if (ch->scan_type.endianness == IIO_BE) + tmp = get_unaligned_be16(outdata); + else + tmp = get_unaligned_le16(outdata); + } else if (byte_for_channel == 3) { + if (ch->scan_type.endianness == IIO_BE) + tmp = get_unaligned_be24(outdata); + else + tmp = get_unaligned_le24(outdata); + } else { + return -EINVAL; + } + *data = sign_extend32(tmp, BYTES_TO_BITS(byte_for_channel) - 1); return 0; } From cf1afec09e9f004a62c54c471863209ed249fca7 Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Mon, 8 Jun 2026 11:33:53 +0100 Subject: [PATCH 0139/1054] btrfs: validate properties before setting them We set the xattr and then attempt to apply the property. If the apply fails we then attempt to delete the xattr to avoid an inconsistency. However we don't verify if the deletion succeed, so if it fails we leave an inconsistency between the state in the btree and the in-memory inode. Address this by validating first if we can apply the property, then set the xattr, then apply the property, and this last step should not fail since the validation succeeded before - assert that it does not fail but leave code to attempt to delete the xattr if it happens, and then abort the transaction only if the xattr delete failed. Reviewed-by: Qu Wenruo Signed-off-by: Filipe Manana Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/props.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/fs/btrfs/props.c b/fs/btrfs/props.c index adc956432d2f..bb77d46376d4 100644 --- a/fs/btrfs/props.c +++ b/fs/btrfs/props.c @@ -127,14 +127,24 @@ int btrfs_set_prop(struct btrfs_trans_handle *trans, struct btrfs_inode *inode, return ret; } + ret = handler->validate(inode, value, value_len); + if (ret) + return ret; ret = btrfs_setxattr(trans, &inode->vfs_inode, handler->xattr_name, value, value_len, flags); if (ret) return ret; ret = handler->apply(inode, value, value_len); - if (ret) { - btrfs_setxattr(trans, &inode->vfs_inode, handler->xattr_name, NULL, - 0, flags); + /* We validated before, so it should not fail here. */ + ASSERT(ret == 0); + if (unlikely(ret)) { + int ret2; + + /* Try to delete xattr, if not possible abort transaction. */ + ret2 = btrfs_setxattr(trans, &inode->vfs_inode, handler->xattr_name, + NULL, 0, flags); + if (unlikely(ret2)) + btrfs_abort_transaction(trans, ret2); return ret; } From 9d78a98796f215d9973e1e53871b2d63420f3608 Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Mon, 8 Jun 2026 11:48:58 +0100 Subject: [PATCH 0140/1054] btrfs: fix transaction abort logic in btrfs_fileattr_set() There's no need to abort the transaction if we failed to set or delete a property, as we haven't done any change. However we need to abort if we set a property or delete a property and then fail to update the inode item, as that would leave the inode's state in subvolume tree inconsistent. Reviewed-by: Qu Wenruo Signed-off-by: Filipe Manana Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/ioctl.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 9d47d16394fc..81e87bc39828 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -289,6 +289,7 @@ int btrfs_fileattr_set(struct mnt_idmap *idmap, int ret; const char *comp = NULL; u32 inode_flags; + bool prop_set = false; if (btrfs_root_readonly(root)) return -EROFS; @@ -401,16 +402,15 @@ int btrfs_fileattr_set(struct mnt_idmap *idmap, if (comp) { ret = btrfs_set_prop(trans, inode, "btrfs.compression", comp, strlen(comp), 0); - if (unlikely(ret)) { - btrfs_abort_transaction(trans, ret); + if (ret) goto out_end_trans; - } + prop_set = true; } else { ret = btrfs_set_prop(trans, inode, "btrfs.compression", NULL, 0, 0); - if (unlikely(ret && ret != -ENODATA)) { - btrfs_abort_transaction(trans, ret); + prop_set = (ret == 0); + /* If ret == -ENODATA ignore and proceed to update inode item. */ + if (ret && ret != -ENODATA) goto out_end_trans; - } } update_flags: @@ -420,6 +420,12 @@ int btrfs_fileattr_set(struct mnt_idmap *idmap, inode_inc_iversion(&inode->vfs_inode); inode_set_ctime_current(&inode->vfs_inode); ret = btrfs_update_inode(trans, inode); + /* + * If we set a property or deleted one, we must abort if we fail to + * update the inode, to avoid persisting an inconsistent state. + */ + if (unlikely(ret && prop_set)) + btrfs_abort_transaction(trans, ret); out_end_trans: btrfs_end_transaction(trans); From a2d8d5647ed854e38f941741aea45b9eb15a6350 Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Wed, 10 Jun 2026 10:29:26 -0700 Subject: [PATCH 0141/1054] btrfs: reject free space cache with more entries than pages When loading a v1 free space cache, __load_free_space_cache() takes num_entries and num_bitmaps straight from the on-disk btrfs_free_space_header. That header is stored in the tree_root under a key with type 0, which the tree-checker has no case for, so neither count is validated before the load trusts it. The load loops num_entries times and maps the next page whenever the current one runs out, going through io_ctl_check_crc() -> io_ctl_map_page(), which does io_ctl->pages[io_ctl->index++]. But pages[] is allocated in io_ctl_init() from the cache inode's i_size, not from num_entries: num_pages = DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE); io_ctl->pages = kcalloc(num_pages, sizeof(struct page *), GFP_NOFS); So if num_entries claims more records than the pages can hold, io_ctl->index runs off the end of pages[]. The write side never hits this because io_ctl_add_entry() and io_ctl_add_bitmap() both stop once io_ctl->index >= io_ctl->num_pages; the read side just never had the same check. To trigger it, take a clean cache (num_entries = here), set num_entries in the header to 0x10000, and fix up the leaf checksum so it still passes the tree-checker. The cache inode has i_size = 65536, so num_pages is 16 and pages[] is a 16-pointer (kmalloc-128) array. The load now tries to read 65536 entries, io_ctl->index walks up to 16, and pages[16] is read past the array: BUG: KASAN: slab-out-of-bounds in io_ctl_check_crc (fs/btrfs/free-space-cache.c:420 fs/btrfs/free-space-cache.c:565) Read of size 8 at addr ffff88800c833a80 by task kworker/u8:3/58 io_ctl_check_crc (fs/btrfs/free-space-cache.c:420 fs/btrfs/free-space-cache.c:565) __load_free_space_cache (fs/btrfs/free-space-cache.c:655 fs/btrfs/free-space-cache.c:820) load_free_space_cache (fs/btrfs/free-space-cache.c:1017) caching_thread (fs/btrfs/block-group.c:880) btrfs_work_helper (fs/btrfs/async-thread.c:312) process_one_work worker_thread kthread ret_from_fork free-space-cache.c:420 is io_ctl_map_page(), inlined into io_ctl_check_crc() at line 565, which is why that is the frame KASAN names. The out-of-bounds slot is then treated as a struct page and handed to crc32c(), so the bad read turns into a GP fault. Add the missing check to io_ctl_check_crc(), which is where both the entry loop and the bitmap loop end up. When num_entries is too large the load now fails like any corrupt cache: __load_free_space_cache() drops it and rebuilds the free space from the extent tree, so a valid cache is never rejected. Reported-by: Weiming Shi Fixes: 5b0e95bf607d ("Btrfs: inline checksums into the disk free space cache") Link: https://lore.kernel.org/linux-btrfs/CAPpSM+RMPByMCKXvM5QFKToxsyNccfuFLWMdD0mfd0wh2Ja62w@mail.gmail.com/ Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Qu Wenruo Signed-off-by: Xiang Mei Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/free-space-cache.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c index 6009b1477232..e2af75a205ea 100644 --- a/fs/btrfs/free-space-cache.c +++ b/fs/btrfs/free-space-cache.c @@ -551,6 +551,9 @@ static int io_ctl_check_crc(struct btrfs_io_ctl *io_ctl, int index) u32 crc = ~(u32)0; unsigned offset = 0; + if (index >= io_ctl->num_pages) + return -EIO; + if (index == 0) offset = sizeof(u32) * io_ctl->num_pages; From ce6050bafb4e33377dc17fcc357736bfc351180c Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Thu, 11 Jun 2026 15:16:21 +0100 Subject: [PATCH 0142/1054] btrfs: fix root leak if its reloc root is unexpected in merge_reloc_roots() If we have an unexpected reloc_root for our root, we jump to the out label but never drop the reference we obtained for root, resulting in a leak. Add a missing btrfs_put_root() call. Fixes: 24213fa46c70 ("btrfs: do proper error handling in merge_reloc_roots") Reviewed-by: Qu Wenruo Reviewed-by: Johannes Thumshirn Signed-off-by: Filipe Manana Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/relocation.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c index fb85bc8b345c..9447d009a9a0 100644 --- a/fs/btrfs/relocation.c +++ b/fs/btrfs/relocation.c @@ -1912,6 +1912,7 @@ void merge_reloc_roots(struct reloc_control *rc) * corruption, e.g. bad reloc tree key offset. */ ret = -EINVAL; + btrfs_put_root(root); goto out; } ret = merge_reloc_root(rc, root); From 3dcd50730814e5220072d2b26d0587af6bfb6dbe Mon Sep 17 00:00:00 2001 From: Boris Burkov Date: Thu, 11 Jun 2026 14:40:17 -0700 Subject: [PATCH 0143/1054] btrfs: fallback to transaction csum tree on a commit root csum miss We have been running with commit root csums enabled for some time and have noticed a slight uptick in zero csum errors. Investigating those revealed that they were same transaction reads of extents that were just relocated, but the extent map generation was long ago. It turns out that relocation intentionally does not update the extent generation (replace_file_extents()), but must write a new csum since the data has moved, so we must account for this with commit root csum reading. Luckily this is a short lived condition: after the relocation transaction the commit root will once again have the csum. So we can add a generic fallback to the lookup to try again with the transaction csum root. Fixes: f07b855c56b1 ("btrfs: try to search for data csums in commit root") Reviewed-by: Filipe Manana Signed-off-by: Boris Burkov Signed-off-by: David Sterba --- fs/btrfs/file-item.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c index 9f6454e9db81..cf50fd623f41 100644 --- a/fs/btrfs/file-item.c +++ b/fs/btrfs/file-item.c @@ -358,6 +358,7 @@ int btrfs_lookup_bio_sums(struct btrfs_bio *bbio) const unsigned int nblocks = orig_len >> fs_info->sectorsize_bits; int ret = 0; u32 bio_offset = 0; + bool using_commit_root = false; if ((inode->flags & BTRFS_INODE_NODATASUM) || test_bit(BTRFS_FS_STATE_NO_DATA_CSUMS, &fs_info->fs_state)) @@ -431,6 +432,7 @@ int btrfs_lookup_bio_sums(struct btrfs_bio *bbio) * from across transactions. */ if (bbio->csum_search_commit_root) { + using_commit_root = true; path->search_commit_root = true; path->skip_locking = true; down_read(&fs_info->commit_root_sem); @@ -463,6 +465,28 @@ int btrfs_lookup_bio_sums(struct btrfs_bio *bbio) * assume this is the case. */ if (count == 0) { + /* + * If an extent is relocated in the current transaction + * then relocation writes a new csum without updating + * the extent map generation. Until the next commit, we + * will see a hole in that case, so we need to fallback + * to searching the transaction csum root. + * + * Note that a commit root lookup of a referenced extent can + * only miss, not return a stale csum. A freed extent's csum + * is deleted in the same transaction and its bytenr is not + * reusable until that transaction has committed and the + * extent is unpinned. + */ + if (using_commit_root) { + up_read(&fs_info->commit_root_sem); + using_commit_root = false; + path->search_commit_root = false; + path->skip_locking = false; + btrfs_release_path(path); + continue; + } + memset(csum_dst, 0, csum_size); count = 1; @@ -481,7 +505,7 @@ int btrfs_lookup_bio_sums(struct btrfs_bio *bbio) bio_offset += count * sectorsize; } - if (bbio->csum_search_commit_root) + if (using_commit_root) up_read(&fs_info->commit_root_sem); return ret; } From 1cb15b153c636096740519f62040ec6dc3e50aef Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Mon, 8 Jun 2026 08:23:49 +0930 Subject: [PATCH 0144/1054] btrfs: lzo: add error message for invalid headers Inside btrfs we always pair -EUCLEAN error with an error message to indicate which data is corrupted. However there are 3 cases inside lzo decompression where there is no error message for corrupted headers. Add those missing error messages to show exactly where the corruption is. Signed-off-by: Qu Wenruo Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/lzo.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/fs/btrfs/lzo.c b/fs/btrfs/lzo.c index 6e4aa22853ab..1531adb117d1 100644 --- a/fs/btrfs/lzo.c +++ b/fs/btrfs/lzo.c @@ -552,17 +552,26 @@ int lzo_decompress(struct list_head *ws, const u8 *data_in, size_t max_segment_len = workspace_buf_length(fs_info); int ret; - if (unlikely(srclen < LZO_LEN || srclen > max_segment_len + LZO_LEN * 2)) + if (unlikely(srclen < LZO_LEN || srclen > max_segment_len + LZO_LEN * 2)) { + btrfs_err(fs_info, "invalid lzo header length, has %zu expect (%u, %zu)", + srclen, LZO_LEN, max_segment_len + LZO_LEN * 2); return -EUCLEAN; + } in_len = get_unaligned_le32(data_in); - if (unlikely(in_len != srclen)) + if (unlikely(in_len != srclen)) { + btrfs_err(fs_info, "invalid lzo header length, has %zu expect %zu", + in_len, srclen); return -EUCLEAN; + } data_in += LZO_LEN; in_len = get_unaligned_le32(data_in); - if (unlikely(in_len != srclen - LZO_LEN * 2)) + if (unlikely(in_len != srclen - LZO_LEN * 2)) { + btrfs_err(fs_info, "invalid lzo segment length, has %zu expect %zu", + in_len, srclen - LZO_LEN * 2); return -EUCLEAN; + } data_in += LZO_LEN; out_len = sectorsize; From 3dc22abc21f5892406c09202fa2627196cc96967 Mon Sep 17 00:00:00 2001 From: Weiming Shi Date: Thu, 11 Jun 2026 07:56:12 +0930 Subject: [PATCH 0145/1054] btrfs: tree-checker: validate INODE_REF's namelen [BUG] A crafted btrfs image can trigger the following crash: BUG: unable to handle page fault for address: ffffd1dc42884000 #PF: supervisor write access in kernel mode #PF: error_code(0x0002) - not-present page CPU: 9 UID: 0 PID: 1034 Comm: poc Not tainted 7.1.0-rc4-custom+ #383 PREEMPT(full) 46af0a92938a63be7132e0dfd71e62327c51d5c2 Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS unknown 02/02/2022 RIP: 0010:memcpy+0xc/0x10 Call Trace: read_extent_buffer+0xe4/0x100 [btrfs 3cf0785dd58fec8c5ff84633b772f17ce1f92a8f] btrfs_get_name+0x15e/0x1e0 [btrfs 3cf0785dd58fec8c5ff84633b772f17ce1f92a8f] reconnect_path+0x165/0x390 exportfs_decode_fh_raw+0x337/0x400 ? drop_caches_sysctl_handler+0xb0/0xb0 ---[ end trace 0000000000000000 ]--- RIP: 0010:memcpy+0xc/0x10 Kernel panic - not syncing: Fatal exception [CAUSE] TThe crafted image has the following corrupted INODE_REF item: item 9 key (258 INODE_REF 257) itemoff 11544 itemsize 4106 index 2 namelen 4096 name: d\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000 The itemsize matches the namelen, but the namelen is 4096, way larger than normal name length limit (BTRFS_NAME_LEN, 255). Meanwhile the memory of the @name is only 255 byte sized, this will cause out-of-boundary access, and cause the above crash. [FIX] Add extra namelen verification for INODE_REF, just like what we have done in ROOT_REF checks. Now the crafted image can be rejected gracefully: BTRFS critical (device dm-2): corrupt leaf: root=5 block=30572544 slot=14 ino=259, invalid inode ref name length, has 4096 expect [1, 255] BTRFS error (device dm-2): read time tree block corruption detected on logical 30572544 mirror 2 Reported-by: Xiang Mei Link: https://lore.kernel.org/linux-btrfs/aik0hEV6ehKx6Ldv@Air.local/ Acked-by: Weiming Shi Signed-off-by: Weiming Shi [ Rebase, add a Link: tag, add an simple cause analyze ] Signed-off-by: Qu Wenruo Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/tree-checker.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fs/btrfs/tree-checker.c b/fs/btrfs/tree-checker.c index cb3e676a81cc..0ce91396b517 100644 --- a/fs/btrfs/tree-checker.c +++ b/fs/btrfs/tree-checker.c @@ -1923,6 +1923,12 @@ static int check_inode_ref(struct extent_buffer *leaf, iref = (struct btrfs_inode_ref *)ptr; namelen = btrfs_inode_ref_name_len(leaf, iref); + if (unlikely(namelen == 0 || namelen > BTRFS_NAME_LEN)) { + inode_ref_err(leaf, slot, + "invalid inode ref name length, has %u expect [1, %u]", + namelen, BTRFS_NAME_LEN); + return -EUCLEAN; + } if (unlikely(ptr + sizeof(*iref) + namelen > end)) { inode_ref_err(leaf, slot, "inode ref overflow, ptr %lu end %lu namelen %u", From 9e37d187e1e62bd3d450429203f98402cefb3ef2 Mon Sep 17 00:00:00 2001 From: Johannes Thumshirn Date: Mon, 22 Jun 2026 10:22:24 +0200 Subject: [PATCH 0146/1054] btrfs: decentralize transaction aborts in create_reloc_root() Decentralize transaction aborts in create_reloc_root(), so that it is obvious which call failed and what caused the transaction abort. Reviewed-by: Filipe Manana Signed-off-by: Johannes Thumshirn Signed-off-by: David Sterba --- fs/btrfs/relocation.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c index 9447d009a9a0..0d63d117db59 100644 --- a/fs/btrfs/relocation.c +++ b/fs/btrfs/relocation.c @@ -719,21 +719,19 @@ static struct btrfs_root *create_reloc_root(struct btrfs_trans_handle *trans, ret = btrfs_insert_root(trans, fs_info->tree_root, &root_key, root_item); - if (ret) - goto abort; + if (unlikely(ret)) { + btrfs_abort_transaction(trans, ret); + return ERR_PTR(ret); + } reloc_root = btrfs_read_tree_root(fs_info->tree_root, &root_key); if (IS_ERR(reloc_root)) { - ret = PTR_ERR(reloc_root); - goto abort; + btrfs_abort_transaction(trans, PTR_ERR(reloc_root)); + return ERR_CAST(reloc_root); } set_bit(BTRFS_ROOT_SHAREABLE, &reloc_root->state); btrfs_set_root_last_trans(reloc_root, trans->transid); return reloc_root; - -abort: - btrfs_abort_transaction(trans, ret); - return ERR_PTR(ret); } /* From c0041b502e579a5c52e5cae918b90678f03faddd Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Sun, 21 Jun 2026 18:41:48 +0930 Subject: [PATCH 0147/1054] btrfs: print-tree: print header owner as signed When dumping a tree block, btrfs_header::owner is printed as unsigned, which can result in numbers that are hard to read, e.g.: BTRFS info (device loop0): leaf 8908800 gen 16 total ptrs 28 free space 1676 owner 18446744073709551607 For the above output, 18446744073709551607 is (s64)-9, the root id of data reloc tree. Despite those predefined root ids that are already negative, existing subvolume trees will not have any negative values, as subvolume trees can only utilize the lower 48 bits, so there will be no output change for existing subvolumes, thus no extra confusion. Reviewed-by: Filipe Manana Reviewed-by: Sun YangKai Reviewed-by: Boris Burkov Signed-off-by: Qu Wenruo Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/print-tree.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/btrfs/print-tree.c b/fs/btrfs/print-tree.c index 87e60a2d4bd8..53e726119ca7 100644 --- a/fs/btrfs/print-tree.c +++ b/fs/btrfs/print-tree.c @@ -449,9 +449,9 @@ void btrfs_print_leaf(const struct extent_buffer *l) nr = btrfs_header_nritems(l); btrfs_info(fs_info, - "leaf %llu gen %llu total ptrs %d free space %d owner %llu", + "leaf %llu gen %llu total ptrs %d free space %d owner %lld", btrfs_header_bytenr(l), btrfs_header_generation(l), nr, - btrfs_leaf_free_space(l), btrfs_header_owner(l)); + btrfs_leaf_free_space(l), (s64)btrfs_header_owner(l)); print_eb_refs_lock(l); for (i = 0 ; i < nr ; i++) { char key_buf[KEY_TYPE_BUF_SIZE]; @@ -600,10 +600,10 @@ void btrfs_print_tree(const struct extent_buffer *c, bool follow) return; } btrfs_info(fs_info, - "node %llu level %d gen %llu total ptrs %d free spc %u owner %llu", + "node %llu level %d gen %llu total ptrs %d free spc %u owner %lld", btrfs_header_bytenr(c), level, btrfs_header_generation(c), nr, (u32)BTRFS_NODEPTRS_PER_BLOCK(fs_info) - nr, - btrfs_header_owner(c)); + (s64)btrfs_header_owner(c)); print_eb_refs_lock(c); for (i = 0; i < nr; i++) { btrfs_node_key_to_cpu(c, &key, i); From ee89db004238bd0b034f2a6176e175561658750b Mon Sep 17 00:00:00 2001 From: Florian Fuchs Date: Mon, 29 Jun 2026 18:33:42 -0700 Subject: [PATCH 0148/1054] Input: maplemouse - fix NULL pointer dereference in open() Commit 555c765b0cc2 ("Input: mouse - drop unnecessary calls to input_set_drvdata") dropped the input_set_drvdata() call in probe because the data appeared to be unused. However, dc_mouse_open() and dc_mouse_close() were using maple_get_drvdata(to_maple_dev(&dev->dev)). This appears to be accessing the data attached to an instance of maple_device structure, while in reality this actually retrieves driver data from the input device's embedded struct device (doing invalid conversion of input device structure to maple device). After input_set_drvdata() was removed, that lookup started returning NULL and opening the input device dereferences mse->mdev. Restore input_set_drvdata() and convert open() and close() to use input_get_drvdata() so the dependency is no longer hidden. Fixes: 6b3480855aad ("maple: input: fix up maple mouse driver") Fixes: 555c765b0cc2 ("Input: mouse - drop unnecessary calls to input_set_drvdata") Signed-off-by: Florian Fuchs Link: https://patch.msgid.link/20260628230715.2982552-1-fuchsfl@gmail.com Cc: stable@vger.kernel.org Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/maplemouse.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/input/mouse/maplemouse.c b/drivers/input/mouse/maplemouse.c index c99f7e234219..c41182766538 100644 --- a/drivers/input/mouse/maplemouse.c +++ b/drivers/input/mouse/maplemouse.c @@ -48,7 +48,7 @@ static void dc_mouse_callback(struct mapleq *mq) static int dc_mouse_open(struct input_dev *dev) { - struct dc_mouse *mse = maple_get_drvdata(to_maple_dev(&dev->dev)); + struct dc_mouse *mse = input_get_drvdata(dev); maple_getcond_callback(mse->mdev, dc_mouse_callback, HZ/50, MAPLE_FUNC_MOUSE); @@ -58,7 +58,7 @@ static int dc_mouse_open(struct input_dev *dev) static void dc_mouse_close(struct input_dev *dev) { - struct dc_mouse *mse = maple_get_drvdata(to_maple_dev(&dev->dev)); + struct dc_mouse *mse = input_get_drvdata(dev); maple_getcond_callback(mse->mdev, dc_mouse_callback, 0, MAPLE_FUNC_MOUSE); @@ -88,6 +88,7 @@ static int probe_maple_mouse(struct device *dev) mse->dev = input_dev; mse->mdev = mdev; + input_set_drvdata(input_dev, mse); input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE); From 1006b2f57f77325bfbf5bd36685efe60334fa360 Mon Sep 17 00:00:00 2001 From: Gao Xiang Date: Tue, 30 Jun 2026 11:18:13 +0800 Subject: [PATCH 0149/1054] erofs: use more informative s_id for file-backed mounts For file-backed mounts, set sb->s_id to the MAJOR:MINOR of sb->s_dev (which fstat() will return) so that kernel messages and the sysfs name are more informative rather than just "erofs: (device erofs): ...". Reviewed-by: Hongbo Li Signed-off-by: Gao Xiang --- fs/erofs/super.c | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/fs/erofs/super.c b/fs/erofs/super.c index 86fa5c6a0c70..c5881bb8d52b 100644 --- a/fs/erofs/super.c +++ b/fs/erofs/super.c @@ -595,17 +595,6 @@ static const struct export_operations erofs_export_ops = { .get_parent = erofs_get_parent, }; -static void erofs_set_sysfs_name(struct super_block *sb) -{ - struct erofs_sb_info *sbi = EROFS_SB(sb); - - if (erofs_is_fileio_mode(sbi)) - super_set_sysfs_name_generic(sb, "%s", - bdi_dev_name(sb->s_bdi)); - else - super_set_sysfs_name_id(sb); -} - static int erofs_fc_fill_super(struct super_block *sb, struct fs_context *fc) { struct inode *inode; @@ -657,12 +646,14 @@ static int erofs_fc_fill_super(struct super_block *sb, struct fs_context *fc) err = super_setup_bdi(sb); if (err) return err; + + snprintf(sb->s_id, sizeof(sb->s_id), + "%u:%u", MAJOR(sb->s_dev), MINOR(sb->s_dev)); } else { if (!sb_set_blocksize(sb, PAGE_SIZE)) { errorfc(fc, "failed to set initial blksize"); return -EINVAL; } - sbi->dif0.dax_dev = fs_dax_get_by_bdev(sb->s_bdev, &sbi->dif0.dax_part_off, NULL, NULL); } @@ -740,7 +731,7 @@ static int erofs_fc_fill_super(struct super_block *sb, struct fs_context *fc) if (err) return err; - erofs_set_sysfs_name(sb); + super_set_sysfs_name_id(sb); err = erofs_register_sysfs(sb); if (err) return err; From 1e33f0de5fdcd09e51fdec1e5822448970b6420f Mon Sep 17 00:00:00 2001 From: Joonas Lahtinen Date: Wed, 24 Jun 2026 12:09:40 +0300 Subject: [PATCH 0150/1054] drm/i915: Return NULL on error in active_instance MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid returning &node->base when node is NULL due to OOM during GFP_ATOMIC allocation. Discovered using AI-assisted static analysis confirmed by Intel Product Security. Reported-by: Martin Hodo Fixes: bfaae47db3c0 ("drm/i915: make lockdep slightly happier about execbuf.") Cc: Maarten Lankhorst Cc: Thomas Hellström Cc: Simona Vetter Cc: # v5.13+ Signed-off-by: Joonas Lahtinen Reviewed-by: Sebastian Brzezinka Reviewed-by: Maarten Lankhorst Link: https://patch.msgid.link/20260624090940.74840-1-joonas.lahtinen@linux.intel.com (cherry picked from commit 6029bc064f0b1bac184203a50fbaaf070fa18832) Signed-off-by: Joonas Lahtinen --- drivers/gpu/drm/i915/i915_active.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_active.c b/drivers/gpu/drm/i915/i915_active.c index 5cb7a72774a0..aa77def0bc0d 100644 --- a/drivers/gpu/drm/i915/i915_active.c +++ b/drivers/gpu/drm/i915/i915_active.c @@ -318,7 +318,7 @@ active_instance(struct i915_active *ref, u64 idx) */ node = kmem_cache_alloc(slab_cache, GFP_ATOMIC); if (!node) - goto out; + goto err; __i915_active_fence_init(&node->base, NULL, node_retire); node->ref = ref; @@ -332,6 +332,11 @@ active_instance(struct i915_active *ref, u64 idx) spin_unlock_irq(&ref->tree_lock); return &node->base; + +err: + spin_unlock_irq(&ref->tree_lock); + + return NULL; } void __i915_active_init(struct i915_active *ref, From bbb15a6b042d02e5508a02b4847e02d2579ee7bc Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Thu, 25 Jun 2026 20:03:04 +0300 Subject: [PATCH 0151/1054] drm/i915/hdcp: check streams[] bounds before overflow The data->streams[] overflow check is done after the buffer overflow has already happened. Move the overflow check before the write. Side note, emitting a warning splat with a backtrace might be overkill here, but prefer not changing the behaviour other than not doing the overrun. Discovered using AI-assisted static analysis confirmed by Intel Product Security. Reported-by: Martin Hodo Fixes: e03187e12cae ("drm/i915/hdcp: MST streams support in hdcp port_data") Cc: stable@vger.kernel.org # v5.12+ Cc: Anshuman Gupta Cc: Suraj Kandpal Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260625170304.1104723-1-jani.nikula@intel.com Signed-off-by: Jani Nikula (cherry picked from commit 9284ab3b6e776c315883ac2611283d263c9460fd) Signed-off-by: Joonas Lahtinen --- drivers/gpu/drm/i915/display/intel_hdcp.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_hdcp.c b/drivers/gpu/drm/i915/display/intel_hdcp.c index e88fec24af49..521786a75c42 100644 --- a/drivers/gpu/drm/i915/display/intel_hdcp.c +++ b/drivers/gpu/drm/i915/display/intel_hdcp.c @@ -145,6 +145,9 @@ intel_hdcp_required_content_stream(struct intel_atomic_state *state, if (!new_conn_state || !new_conn_state->crtc) continue; + if (drm_WARN_ON(display->drm, data->k >= INTEL_NUM_PIPES(display))) + return -EINVAL; + data->streams[data->k].stream_id = intel_conn_to_vcpi(state, connector); data->k++; @@ -155,7 +158,7 @@ intel_hdcp_required_content_stream(struct intel_atomic_state *state, } drm_connector_list_iter_end(&conn_iter); - if (drm_WARN_ON(display->drm, data->k > INTEL_NUM_PIPES(display) || data->k == 0)) + if (drm_WARN_ON(display->drm, !data->k)) return -EINVAL; /* From db9e64c983dcb07ff256bd455f258c44aa530ff8 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Thu, 25 Jun 2026 13:44:07 +0300 Subject: [PATCH 0152/1054] drm/i915/hdcp: require monotonically increasing seq_num_v The HDCP 2.2 specification requires the seq_num_v to be monotonically increasing, and repeated seq_num_v needs to be treated as an integrity failure. Make it so. For the first message, seq_num_v must be zero, and is already checked. We can only check for less-than-or-equal for the subsequent messages, where hdcp2_encrypted is true. Discovered using AI-assisted static analysis confirmed by Intel Product Security. Reported-by: Martin Hodo Fixes: d849178e2c9e ("drm/i915: Implement HDCP2.2 repeater authentication") Cc: stable@vger.kernel.org # v5.2+ Cc: Suraj Kandpal Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260625104407.1025614-1-jani.nikula@intel.com Signed-off-by: Jani Nikula (cherry picked from commit 58a224375c81179b52558c53d8857b93196d2687) Signed-off-by: Joonas Lahtinen --- drivers/gpu/drm/i915/display/intel_hdcp.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_hdcp.c b/drivers/gpu/drm/i915/display/intel_hdcp.c index 521786a75c42..0a076d2ed70a 100644 --- a/drivers/gpu/drm/i915/display/intel_hdcp.c +++ b/drivers/gpu/drm/i915/display/intel_hdcp.c @@ -1801,9 +1801,10 @@ int hdcp2_authenticate_repeater_topology(struct intel_connector *connector) return -EINVAL; } - if (seq_num_v < hdcp->seq_num_v) { - /* Roll over of the seq_num_v from repeater. Reauthenticate. */ - drm_dbg_kms(display->drm, "Seq_num_v roll over.\n"); + if (hdcp->hdcp2_encrypted && seq_num_v <= hdcp->seq_num_v) { + /* Reauthenticate on Seq_num_v repeat or rollover */ + drm_dbg_kms(display->drm, "Seq_num_v %s\n", + seq_num_v == hdcp->seq_num_v ? "repeat" : "rollover"); return -EINVAL; } From 9a6c0b6ea12746d50cf53d59a7e05fd83f974bda Mon Sep 17 00:00:00 2001 From: Paul Louvel Date: Mon, 29 Jun 2026 16:07:02 +0200 Subject: [PATCH 0153/1054] gpio-f7188x: Add support for NCT6126D version B The Nuvoton NCT6126D Super-I/O is available in two hardware revisions. According to the manufacturer datasheet revision 2.4, version A reports chip ID 0xD283, while version B reports chip ID 0xD284. The driver currently only recognizes only the version A ID. Version B only contains hardware fixes unrelated to the GPIO functionality, so it can be supported by simply adding its chip ID without any other driver changes. Fixes: 3002b8642f01 ("gpio-f7188x: fix chip name and pin count on Nuvoton chip") Cc: stable@vger.kernel.org Signed-off-by: Paul Louvel Link: https://patch.msgid.link/20260629-gpio-f7188x-nct6126d-version-b-v1-1-a06226c02a2d@bootlin.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-f7188x.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-f7188x.c b/drivers/gpio/gpio-f7188x.c index 4d5b927ad70f..fb007b978729 100644 --- a/drivers/gpio/gpio-f7188x.c +++ b/drivers/gpio/gpio-f7188x.c @@ -48,7 +48,8 @@ /* * Nuvoton devices. */ -#define SIO_NCT6126D_ID 0xD283 /* NCT6126D chipset ID */ +#define SIO_NCT6126D_VER_A_ID 0xD283 /* NCT6126D version A chipset ID */ +#define SIO_NCT6126D_VER_B_ID 0xD284 /* NCT6126D version B chipset ID */ #define SIO_LD_GPIO_NUVOTON 0x07 /* GPIO logical device */ @@ -564,7 +565,8 @@ static int __init f7188x_find(int addr, struct f7188x_sio *sio) case SIO_F81865_ID: sio->type = f81865; break; - case SIO_NCT6126D_ID: + case SIO_NCT6126D_VER_A_ID: + case SIO_NCT6126D_VER_B_ID: sio->device = SIO_LD_GPIO_NUVOTON; sio->type = nct6126d; break; From 6bfc8d01ac4068eced509f8fc74d0cd205e4dcec Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 29 Jun 2026 14:45:26 +0200 Subject: [PATCH 0154/1054] USB: serial: keyspan_pda: fix information leak The write() callback is supposed to return the number of characters accepted or a negative errno. Since the addition of write fifo support the keyspan_pda implementation will however return the number characters submitted to the device if the write urb is not already in use. If this number is larger than the number of characters passed to write(), the line discipline continues writing data from beyond the tty write buffer. Fix the information leak by making sure that keyspan_pda_write_start() returns zero on success as intended. Fixes: 034e38e8f687 ("USB: serial: keyspan_pda: add write-fifo support") Cc: stable@vger.kernel.org # 5.11 Signed-off-by: Johan Hovold --- drivers/usb/serial/keyspan_pda.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index 3b99f9676c35..f05bcce60600 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -516,7 +516,7 @@ static int keyspan_pda_write_start(struct usb_serial_port *port) if (count == room) schedule_work(&priv->unthrottle_work); - return count; + return 0; } static void keyspan_pda_write_bulk_callback(struct urb *urb) From 8ae5f8e4836667fcaffdf2e3c6068b0a8b364dd8 Mon Sep 17 00:00:00 2001 From: Unnathi Chalicheemala Date: Wed, 17 Jun 2026 16:35:00 -0700 Subject: [PATCH 0155/1054] firmware: arm_ffa: Fix NULL dereference in ffa_partition_info_get() ffa_partition_info_get() passes uuid_str directly to uuid_parse() without a NULL check. When a caller passes NULL, uuid_parse() -> __uuid_parse() -> uuid_is_valid() dereferences the pointer, causing a kernel panic: | Unable to handle kernel NULL pointer dereference at virtual address | 0000000000000040 | pc : uuid_parse+0x40/0xac | lr : ffa_partition_info_get+0x1c/0x94 [arm_ffa] Add a NULL guard before uuid_parse() so a NULL argument returns -ENODEV instead of crashing. Callers are expected to always supply a valid partition UUID, so NULL is not a supported input. Fixes: d0c0bce83122 ("firmware: arm_ffa: Setup in-kernel users of FFA partitions") Signed-off-by: Unnathi Chalicheemala Link: https://patch.msgid.link/20260617-ffa_partition_nullptr_fix-v2-1-bc801b4ce34c@oss.qualcomm.com Signed-off-by: Sudeep Holla --- drivers/firmware/arm_ffa/driver.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c index bc2685331b27..d475ff83132d 100644 --- a/drivers/firmware/arm_ffa/driver.c +++ b/drivers/firmware/arm_ffa/driver.c @@ -1142,7 +1142,7 @@ static int ffa_partition_info_get(const char *uuid_str, uuid_t uuid; struct ffa_partition_info *pbuf; - if (uuid_parse(uuid_str, &uuid)) { + if (!uuid_str || uuid_parse(uuid_str, &uuid)) { pr_err("invalid uuid (%s)\n", uuid_str); return -ENODEV; } From 05e5ffde9b666ee6d3ec225efeda9c038fa74ac8 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 3 Jun 2026 17:17:51 +0200 Subject: [PATCH 0156/1054] firmware: arm_scmi: Grammar s/may needed/may be needed/ Fix grammar in the help text for the ARM_SCMI_POWER_CONTROL symbol. Signed-off-by: Geert Uytterhoeven Link: https://patch.msgid.link/5180d04abfb8e3074a321e2eb73bacfdd61c30c5.1780499850.git.geert+renesas@glider.be Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/firmware/arm_scmi/Kconfig b/drivers/firmware/arm_scmi/Kconfig index e3fb36825978..783c24a20e29 100644 --- a/drivers/firmware/arm_scmi/Kconfig +++ b/drivers/firmware/arm_scmi/Kconfig @@ -96,7 +96,7 @@ config ARM_SCMI_POWER_CONTROL firmware. This driver can also be built as a module. If so, the module will be - called scmi_power_control. Note this may needed early in boot to catch - early shutdown/reboot SCMI requests. + called scmi_power_control. Note this may be needed early in boot to + catch early shutdown/reboot SCMI requests. endmenu From ac4aa4b41bee8d6353cd2992fe8ecbb8ef2123cb Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Mon, 15 Jun 2026 18:30:27 +0300 Subject: [PATCH 0157/1054] drm/dp: fix kernel-doc for struct drm_dp_as_sdp Add the missing coasting_vtotal kernel-doc member documentation for struct drm_dp_as_sdp. Reviewed-by: Thomas Zimmermann Link: https://patch.msgid.link/20260615153027.1899784-1-jani.nikula@intel.com Signed-off-by: Jani Nikula --- include/drm/display/drm_dp_helper.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/drm/display/drm_dp_helper.h b/include/drm/display/drm_dp_helper.h index 8c2d77a032f0..ab16c1be3900 100644 --- a/include/drm/display/drm_dp_helper.h +++ b/include/drm/display/drm_dp_helper.h @@ -115,6 +115,7 @@ struct drm_dp_vsc_sdp { * @duration_decr_ms: Successive frame duration decrease * @target_rr_divider: Target refresh rate divider * @mode: Adaptive Sync Operation Mode + * @coasting_vtotal: Coasting vtotal */ struct drm_dp_as_sdp { unsigned char sdp_type; From 9ec3aeace4334e0d2aa105d1d25fd8a95fb9ba95 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Mon, 15 Jun 2026 18:30:12 +0300 Subject: [PATCH 0158/1054] drm/fixed: fix kernel-doc for drm_sm2fixp() Fix the kernel-doc comment for drm_sm2fixp(). Reviewed-by: Thomas Zimmermann Link: https://patch.msgid.link/20260615153012.1899576-1-jani.nikula@intel.com Signed-off-by: Jani Nikula --- include/drm/drm_fixed.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/drm/drm_fixed.h b/include/drm/drm_fixed.h index 33de514a5221..21d822aeed55 100644 --- a/include/drm/drm_fixed.h +++ b/include/drm/drm_fixed.h @@ -79,7 +79,8 @@ static inline u32 dfixed_div(fixed20_12 A, fixed20_12 B) #define DRM_FIXED_ALMOST_ONE (DRM_FIXED_ONE - DRM_FIXED_EPSILON) /** - * @drm_sm2fixp + * drm_sm2fixp() - convert signed-magnitude to fixed point + * @a: 1.31.32 signed-magnitude fixed point * * Convert a 1.31.32 signed-magnitude fixed point to 32.32 * 2s-complement fixed point From 6c18817c01f6f76d9e2739903abde4d69397f2c6 Mon Sep 17 00:00:00 2001 From: Shubham Nayak Date: Mon, 29 Jun 2026 19:04:55 +0530 Subject: [PATCH 0159/1054] ALSA: hda/realtek: Add quirk for HP Victus 16-e0xxx (88EE) to enable mute LED The mute LED on the HP Victus 16-e0xxx (board ID 88EE, ALC245 codec) does not function by default. Add the ALC245_FIXUP_HP_MUTE_LED_COEFBIT quirk to enable it. Tested on my HP Victus 16-e0xxx with kernel 7.1.2. Signed-off-by: Shubham Nayak Link: https://patch.msgid.link/20260629-hp-victus-16-mute-led-v1-1-ab0f4a8a533b@gmail.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index d9e2384fc0ba..f7700713dc62 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -7068,6 +7068,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x88d1, "HP Pavilion 15-eh1xxx (mainboard 88D1)", ALC245_FIXUP_HP_MUTE_LED_V1_COEFBIT), SND_PCI_QUIRK(0x103c, 0x88dd, "HP Pavilion 15z-ec200", ALC285_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x88eb, "HP Victus 16-e0xxx", ALC245_FIXUP_HP_MUTE_LED_V2_COEFBIT), + SND_PCI_QUIRK(0x103c, 0x88ee, "HP Victus 16-e0xxx (MB 88EE)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), SND_PCI_QUIRK(0x103c, 0x8902, "HP OMEN 16", ALC285_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x890e, "HP 255 G8 Notebook PC", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), SND_PCI_QUIRK(0x103c, 0x8919, "HP Pavilion Aero Laptop 13-be0xxx", ALC287_FIXUP_HP_GPIO_LED), From 64ace85a725957e2359785d2a22cd285eec966de Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Mon, 15 Jun 2026 18:29:49 +0300 Subject: [PATCH 0160/1054] drm/ras: include linux/types.h in drm_ras.h drm_ras.h uses u32. Include linux/types.h for it. Reviewed-by: Thomas Zimmermann Link: https://patch.msgid.link/20260615152949.1899358-1-jani.nikula@intel.com Signed-off-by: Jani Nikula --- include/drm/drm_ras.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/drm/drm_ras.h b/include/drm/drm_ras.h index f2a787bc4f64..0beede3ddc4e 100644 --- a/include/drm/drm_ras.h +++ b/include/drm/drm_ras.h @@ -6,6 +6,8 @@ #ifndef __DRM_RAS_H__ #define __DRM_RAS_H__ +#include + #include /** From 4e1a53892ba7f8a3e1da6bfc53c83ae7c812dccd Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Sat, 20 Jun 2026 21:43:34 -0500 Subject: [PATCH 0161/1054] drm/virtio: bound EDID block reads to the response buffer virtio_get_edid_block() validates the read offset only against the device-supplied resp->size field, never against the fixed-size resp->edid array. The EDID block index is driven by the device-supplied extension count, so a malicious virtio-gpu backend can advertise a large size together with a high block count and read far past the array into adjacent kernel memory, which is then surfaced in the parsed EDID (an out-of-bounds read / info leak). Also reject any read whose end exceeds the size of the edid array. Conforming EDID responses stay within the array and are unaffected. Fixes: b4b01b4995fb ("drm/virtio: add edid support") Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas Signed-off-by: Dmitry Osipenko Link: https://patch.msgid.link/20260620-b4-disp-22bba7bf-v1-1-b95924cee742@proton.me --- drivers/gpu/drm/virtio/virtgpu_vq.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/virtio/virtgpu_vq.c b/drivers/gpu/drm/virtio/virtgpu_vq.c index 67865810a2e7..c8b9475a7472 100644 --- a/drivers/gpu/drm/virtio/virtgpu_vq.c +++ b/drivers/gpu/drm/virtio/virtgpu_vq.c @@ -897,7 +897,8 @@ static int virtio_get_edid_block(void *data, u8 *buf, struct virtio_gpu_resp_edid *resp = data; size_t start = block * EDID_LENGTH; - if (start + len > le32_to_cpu(resp->size)) + if (start + len > le32_to_cpu(resp->size) || + start + len > sizeof(resp->edid)) return -EINVAL; memcpy(buf, resp->edid + start, len); return 0; From 226f4a490d1a938fc838d8f8c46a4eca864c0d78 Mon Sep 17 00:00:00 2001 From: Qianyu Luo Date: Thu, 25 Jun 2026 13:55:08 +0800 Subject: [PATCH 0162/1054] xfrm: nat_keepalive: avoid double free on send error nat_keepalive_send() frees the keepalive skb whenever the IPv4 or IPv6 send helper reports an error. That cleanup is only correct before the skb is handed to the output path. Once ip_build_and_send_pkt() or ip6_xmit() takes ownership, the networking stack may already have consumed the skb before returning an error, so freeing it again is unsafe. Handle the pre-handoff failure cases inside nat_keepalive_send_ipv4() and nat_keepalive_send_ipv6(), where the caller still owns the skb, and keep nat_keepalive_send() responsible only for family dispatch and the unsupported-family cleanup path. Fixes: f531d13bdfe3 ("xfrm: support sending NAT keepalives in ESP in UDP states") Cc: stable@vger.kernel.org Reported-by: Yuan Tan Reported-by: Xin Liu Signed-off-by: Qianyu Luo Signed-off-by: Ren Wei Reviewed-by: Eyal Birger Signed-off-by: Steffen Klassert --- net/xfrm/xfrm_nat_keepalive.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/net/xfrm/xfrm_nat_keepalive.c b/net/xfrm/xfrm_nat_keepalive.c index 458931062a04..eb1b6f67739e 100644 --- a/net/xfrm/xfrm_nat_keepalive.c +++ b/net/xfrm/xfrm_nat_keepalive.c @@ -55,8 +55,10 @@ static int nat_keepalive_send_ipv4(struct sk_buff *skb, ka->encap_sport, sock_net_uid(net, NULL)); rt = ip_route_output_key(net, &fl4); - if (IS_ERR(rt)) + if (IS_ERR(rt)) { + kfree_skb(skb); return PTR_ERR(rt); + } skb_dst_set(skb, &rt->dst); @@ -101,6 +103,7 @@ static int nat_keepalive_send_ipv6(struct sk_buff *skb, dst = ip6_dst_lookup_flow(net, sk, &fl6, NULL); if (IS_ERR(dst)) { local_unlock_nested_bh(&nat_keepalive_sk_ipv6.bh_lock); + kfree_skb(skb); return PTR_ERR(dst); } @@ -118,7 +121,6 @@ static void nat_keepalive_send(struct nat_keepalive *ka) sizeof(struct ipv6hdr)) + sizeof(struct udphdr); const u8 nat_ka_payload = 0xFF; - int err = -EAFNOSUPPORT; struct sk_buff *skb; struct udphdr *uh; @@ -140,16 +142,17 @@ static void nat_keepalive_send(struct nat_keepalive *ka) switch (ka->family) { case AF_INET: - err = nat_keepalive_send_ipv4(skb, ka); + nat_keepalive_send_ipv4(skb, ka); break; #if IS_ENABLED(CONFIG_IPV6) case AF_INET6: - err = nat_keepalive_send_ipv6(skb, ka, uh); + nat_keepalive_send_ipv6(skb, ka, uh); break; #endif - } - if (err) + default: kfree_skb(skb); + break; + } } struct nat_keepalive_work_ctx { From efecde8a254d1f207b75c5ebcfba2c51f4c771d9 Mon Sep 17 00:00:00 2001 From: Viacheslav Bocharov Date: Tue, 30 Jun 2026 13:15:44 +0300 Subject: [PATCH 0163/1054] gpio: shared-proxy: always serialize with a sleeping mutex The shared GPIO descriptor used either a mutex or a spinlock, chosen at runtime from the underlying chip's can_sleep: shared_desc->can_sleep = gpiod_cansleep(shared_desc->desc); ... if (can_sleep) mutex_lock(); else spin_lock_irqsave(); can_sleep describes only the value path (->get/->set). Under the same lock, however, the proxy may call gpiod_set_config() and gpiod_direction_*(), which can reach pinctrl paths that take a mutex (e.g. gpiod_set_config() -> gpiochip_generic_config() -> pinctrl_gpio_set_config()), independent of can_sleep. On a controller with non-sleeping MMIO value ops the descriptor lock was a spinlock, so the sleeping pinctrl call ran from atomic context. Reproduced on an Amlogic A113X board with the workaround from commit 28f240683871 ("pinctrl: meson: mark the GPIO controller as sleeping") reverted; the original Khadas VIM3 report hit the same path: BUG: sleeping function called from invalid context __mutex_lock pinctrl_get_device_gpio_range pinctrl_gpio_set_config gpiochip_generic_config gpiod_set_config gpio_shared_proxy_set_config <- voting spinlock held ... mmc_pwrseq_simple_probe The spinlock existed to take the value vote from atomic context, but the vote and the (possibly sleeping) control operations share the same state and lock, so this scheme cannot serialize config under a mutex and still offer atomic value access. Always serialize the shared descriptor with a mutex instead and mark the proxy a sleeping gpiochip, driving the underlying GPIO through the cansleep value accessors: those are valid for both sleeping and non-sleeping chips, so value access keeps working on fast controllers, at the cost of no longer being atomic. With every vote edge now driven through the cansleep value setter, gpio_shared_proxy_set_unlocked() no longer needs a per-call setter: drop its set_func callback and call gpiod_set_value_cansleep() directly. The shared direction_output path reaches it only once the line is already an output, so driving the value there is equivalent to re-issuing gpiod_direction_output(), without the redundant per-edge re-assertion of drive config and bias. This is observable: consumers gating on gpiod_cansleep() take their sleeping branch on a proxied GPIO (mmc-pwrseq-emmc skips its emergency-restart reset handler; its normal reset is unaffected), and consumers that reject sleeping GPIOs (pwm-gpio, ps2-gpio, ...) would fail to probe. Such atomic users do not share a pin through the proxy, whose purpose is voting on shared reset/enable lines. The same narrowing already applies on Amlogic since that workaround, and rockchip addressed the identical splat per-driver in commit 7ca497be0016 ("gpio: rockchip: Stop calling pinctrl for set_direction"); fixing the proxy addresses the locking error once, for every controller. The lock type was added by commit a060b8c511ab ("gpiolib: implement low-level, shared GPIO support"); the sleeping call under it arrived with the proxy driver. Fixes: e992d54c6f97 ("gpio: shared-proxy: implement the shared GPIO proxy driver") Reported-by: Marek Szyprowski Closes: https://lore.kernel.org/all/00107523-7737-4b92-a785-14ce4e93b8cb@samsung.com/ Signed-off-by: Viacheslav Bocharov Link: https://patch.msgid.link/20260630101545.800625-2-v@baodeep.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-shared-proxy.c | 76 ++++++++++++-------------------- drivers/gpio/gpiolib-shared.c | 9 +--- drivers/gpio/gpiolib-shared.h | 28 +----------- 3 files changed, 32 insertions(+), 81 deletions(-) diff --git a/drivers/gpio/gpio-shared-proxy.c b/drivers/gpio/gpio-shared-proxy.c index 6941e4be6cf1..10ca2ef77ef3 100644 --- a/drivers/gpio/gpio-shared-proxy.c +++ b/drivers/gpio/gpio-shared-proxy.c @@ -9,8 +9,10 @@ #include #include #include +#include #include #include +#include #include #include @@ -24,15 +26,13 @@ struct gpio_shared_proxy_data { }; static int -gpio_shared_proxy_set_unlocked(struct gpio_shared_proxy_data *proxy, - int (*set_func)(struct gpio_desc *desc, int value), - int value) +gpio_shared_proxy_set_unlocked(struct gpio_shared_proxy_data *proxy, int value) { struct gpio_shared_desc *shared_desc = proxy->shared_desc; struct gpio_desc *desc = shared_desc->desc; int ret = 0; - gpio_shared_lockdep_assert(shared_desc); + lockdep_assert_held(&shared_desc->mutex); if (value) { /* User wants to set value to high. */ @@ -46,7 +46,7 @@ gpio_shared_proxy_set_unlocked(struct gpio_shared_proxy_data *proxy, * Current value is low, need to actually set value * to high. */ - ret = set_func(desc, 1); + ret = gpiod_set_value_cansleep(desc, 1); if (ret) goto out; } @@ -65,7 +65,7 @@ gpio_shared_proxy_set_unlocked(struct gpio_shared_proxy_data *proxy, /* We previously voted for high. */ if (shared_desc->highcnt == 1) { /* This is the last remaining vote for high, set value to low. */ - ret = set_func(desc, 0); + ret = gpiod_set_value_cansleep(desc, 0); if (ret) goto out; } @@ -89,7 +89,7 @@ static int gpio_shared_proxy_request(struct gpio_chip *gc, unsigned int offset) struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc); struct gpio_shared_desc *shared_desc = proxy->shared_desc; - guard(gpio_shared_desc_lock)(shared_desc); + guard(mutex)(&shared_desc->mutex); proxy->shared_desc->usecnt++; @@ -105,11 +105,10 @@ static void gpio_shared_proxy_free(struct gpio_chip *gc, unsigned int offset) struct gpio_shared_desc *shared_desc = proxy->shared_desc; int ret; - guard(gpio_shared_desc_lock)(shared_desc); + guard(mutex)(&shared_desc->mutex); if (proxy->voted_high) { - ret = gpio_shared_proxy_set_unlocked(proxy, - shared_desc->can_sleep ? gpiod_set_value_cansleep : gpiod_set_value, 0); + ret = gpio_shared_proxy_set_unlocked(proxy, 0); if (ret) dev_err(proxy->dev, "Failed to unset the shared GPIO value on release: %d\n", ret); @@ -129,7 +128,7 @@ static int gpio_shared_proxy_set_config(struct gpio_chip *gc, struct gpio_desc *desc = shared_desc->desc; int ret; - guard(gpio_shared_desc_lock)(shared_desc); + guard(mutex)(&shared_desc->mutex); if (shared_desc->usecnt > 1) { if (shared_desc->cfg != cfg) { @@ -157,7 +156,7 @@ static int gpio_shared_proxy_direction_input(struct gpio_chip *gc, struct gpio_desc *desc = shared_desc->desc; int dir; - guard(gpio_shared_desc_lock)(shared_desc); + guard(mutex)(&shared_desc->mutex); if (shared_desc->usecnt == 1) { dev_dbg(proxy->dev, @@ -187,7 +186,7 @@ static int gpio_shared_proxy_direction_output(struct gpio_chip *gc, struct gpio_desc *desc = shared_desc->desc; int ret, dir; - guard(gpio_shared_desc_lock)(shared_desc); + guard(mutex)(&shared_desc->mutex); if (shared_desc->usecnt == 1) { dev_dbg(proxy->dev, @@ -219,14 +218,7 @@ static int gpio_shared_proxy_direction_output(struct gpio_chip *gc, return -EPERM; } - return gpio_shared_proxy_set_unlocked(proxy, gpiod_direction_output, value); -} - -static int gpio_shared_proxy_get(struct gpio_chip *gc, unsigned int offset) -{ - struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc); - - return gpiod_get_value(proxy->shared_desc->desc); + return gpio_shared_proxy_set_unlocked(proxy, value); } static int gpio_shared_proxy_get_cansleep(struct gpio_chip *gc, @@ -237,29 +229,14 @@ static int gpio_shared_proxy_get_cansleep(struct gpio_chip *gc, return gpiod_get_value_cansleep(proxy->shared_desc->desc); } -static int gpio_shared_proxy_do_set(struct gpio_shared_proxy_data *proxy, - int (*set_func)(struct gpio_desc *desc, int value), - int value) -{ - guard(gpio_shared_desc_lock)(proxy->shared_desc); - - return gpio_shared_proxy_set_unlocked(proxy, set_func, value); -} - -static int gpio_shared_proxy_set(struct gpio_chip *gc, unsigned int offset, - int value) -{ - struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc); - - return gpio_shared_proxy_do_set(proxy, gpiod_set_value, value); -} - static int gpio_shared_proxy_set_cansleep(struct gpio_chip *gc, unsigned int offset, int value) { struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc); - return gpio_shared_proxy_do_set(proxy, gpiod_set_value_cansleep, value); + guard(mutex)(&proxy->shared_desc->mutex); + + return gpio_shared_proxy_set_unlocked(proxy, value); } static int gpio_shared_proxy_get_direction(struct gpio_chip *gc, @@ -302,20 +279,25 @@ static int gpio_shared_proxy_probe(struct auxiliary_device *adev, gc->label = dev_name(dev); gc->parent = dev; gc->owner = THIS_MODULE; - gc->can_sleep = shared_desc->can_sleep; + /* + * Under the descriptor mutex the proxy may call + * gpiod_set_config()/gpiod_direction_*(), which can reach pinctrl + * paths that take a mutex (e.g. gpiod_set_config() -> + * gpiochip_generic_config() -> pinctrl_gpio_set_config()), independent + * of the underlying chip's can_sleep. So the descriptor lock must be a + * mutex and the proxy gpiochip is therefore always sleeping; drive the + * underlying GPIO through the cansleep value accessors, which are valid + * for both sleeping and non-sleeping chips. + */ + gc->can_sleep = true; gc->request = gpio_shared_proxy_request; gc->free = gpio_shared_proxy_free; gc->set_config = gpio_shared_proxy_set_config; gc->direction_input = gpio_shared_proxy_direction_input; gc->direction_output = gpio_shared_proxy_direction_output; - if (gc->can_sleep) { - gc->set = gpio_shared_proxy_set_cansleep; - gc->get = gpio_shared_proxy_get_cansleep; - } else { - gc->set = gpio_shared_proxy_set; - gc->get = gpio_shared_proxy_get; - } + gc->set = gpio_shared_proxy_set_cansleep; + gc->get = gpio_shared_proxy_get_cansleep; gc->get_direction = gpio_shared_proxy_get_direction; gc->to_irq = gpio_shared_proxy_to_irq; diff --git a/drivers/gpio/gpiolib-shared.c b/drivers/gpio/gpiolib-shared.c index de72776fb154..495bd3d0ddf0 100644 --- a/drivers/gpio/gpiolib-shared.c +++ b/drivers/gpio/gpiolib-shared.c @@ -627,8 +627,7 @@ static void gpio_shared_release(struct kref *kref) shared_desc = entry->shared_desc; gpio_device_put(shared_desc->desc->gdev); - if (shared_desc->can_sleep) - mutex_destroy(&shared_desc->mutex); + mutex_destroy(&shared_desc->mutex); kfree(shared_desc); entry->shared_desc = NULL; } @@ -659,11 +658,7 @@ gpiod_shared_desc_create(struct gpio_shared_entry *entry) } shared_desc->desc = &gdev->descs[entry->offset]; - shared_desc->can_sleep = gpiod_cansleep(shared_desc->desc); - if (shared_desc->can_sleep) - mutex_init(&shared_desc->mutex); - else - spin_lock_init(&shared_desc->spinlock); + mutex_init(&shared_desc->mutex); return shared_desc; } diff --git a/drivers/gpio/gpiolib-shared.h b/drivers/gpio/gpiolib-shared.h index 15e72a8dcdb1..bbdc0ab7b647 100644 --- a/drivers/gpio/gpiolib-shared.h +++ b/drivers/gpio/gpiolib-shared.h @@ -3,10 +3,7 @@ #ifndef __LINUX_GPIO_SHARED_H #define __LINUX_GPIO_SHARED_H -#include -#include #include -#include struct gpio_device; struct gpio_desc; @@ -42,35 +39,12 @@ static inline int gpio_shared_add_proxy_lookup(struct device *consumer, struct gpio_shared_desc { struct gpio_desc *desc; - bool can_sleep; unsigned long cfg; unsigned int usecnt; unsigned int highcnt; - union { - struct mutex mutex; - spinlock_t spinlock; - }; + struct mutex mutex; /* serializes all proxy operations on this descriptor */ }; struct gpio_shared_desc *devm_gpiod_shared_get(struct device *dev); -DEFINE_LOCK_GUARD_1(gpio_shared_desc_lock, struct gpio_shared_desc, - if (_T->lock->can_sleep) - mutex_lock(&_T->lock->mutex); - else - spin_lock_irqsave(&_T->lock->spinlock, _T->flags), - if (_T->lock->can_sleep) - mutex_unlock(&_T->lock->mutex); - else - spin_unlock_irqrestore(&_T->lock->spinlock, _T->flags), - unsigned long flags) - -static inline void gpio_shared_lockdep_assert(struct gpio_shared_desc *shared_desc) -{ - if (shared_desc->can_sleep) - lockdep_assert_held(&shared_desc->mutex); - else - lockdep_assert_held(&shared_desc->spinlock); -} - #endif /* __LINUX_GPIO_SHARED_H */ From b7d9c359e5cf867f7eb23df3bb1c6b9e58af24da Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 29 Jun 2026 12:55:48 -1000 Subject: [PATCH 0164/1054] sched_ext: Don't warn on core-sched forced idle in put_prev_task_scx() put_prev_task_scx() warns when a runnable task drops to a lower sched_class without SCX_OPS_ENQ_LAST, on the assumption that balance_one() would have kept it running. Core scheduling breaks that: a forced-idle SMT sibling reschedules through the core_pick fast path in pick_next_task(), which skips pick_task_scx() and thus balance_one(), so a runnable task can drop to idle with ENQ_LAST unset. Gate the warning on sched_cpu_cookie_match(): a cookie mismatch means core scheduling forced the idle, while a match (or core scheduling off) still catches a genuine missing-ENQ_LAST drop. Fixes: 7c65ae81ea86 ("sched_ext: Don't call put_prev_task_scx() before picking the next task") Signed-off-by: Tejun Heo Reviewed-by: Andrea Righi --- kernel/sched/ext/ext.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/kernel/sched/ext/ext.c b/kernel/sched/ext/ext.c index 3b2e13bc924b..e75e2fd5ab7e 100644 --- a/kernel/sched/ext/ext.c +++ b/kernel/sched/ext/ext.c @@ -3090,9 +3090,14 @@ static void put_prev_task_scx(struct rq *rq, struct task_struct *p, * sched_class, %SCX_OPS_ENQ_LAST must be set. Tell * ops.enqueue() that @p is the only one available for this cpu, * which should trigger an explicit follow-up scheduling event. + * + * Core scheduling can force this CPU idle while @p stays + * runnable. @p's cookie then won't match the core's, so skip + * the warning in that case. */ if (next && sched_class_above(&ext_sched_class, next->sched_class)) { - WARN_ON_ONCE(!(sch->ops.flags & SCX_OPS_ENQ_LAST)); + WARN_ON_ONCE(sched_cpu_cookie_match(rq, p) && + !(sch->ops.flags & SCX_OPS_ENQ_LAST)); do_enqueue_task(rq, p, SCX_ENQ_LAST, -1); } else { do_enqueue_task(rq, p, 0, -1); From 88496c4ac5a6ade75619f4b1015706a8b924d50a Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Sat, 27 Jun 2026 12:27:58 +0800 Subject: [PATCH 0165/1054] ntfs: avoid stale runlist element dereference in fallocate ntfs_attr_fallocate() allocates holes and delayed allocations inside initialized size by looking up the current runlist element under ni->runlist.lock. The returned struct runlist_element is only a borrowed pointer into ni->runlist.rl. A writer can replace and free that array after the read lock is dropped, so later reads of rl->lcn, rl->length and rl->vcn can touch freed memory. The buggy scenario involves two paths, with each column showing the order within that path: ntfs_attr_fallocate(): 1. Take ni->runlist.lock for read. 2. Get rl from ntfs_attr_find_vcn_nolock(). 3. Drop ni->runlist.lock. 4. Read rl->lcn, rl->length and rl->vcn. mmap page_mkwrite: 1. Enter ntfs_filemap_page_mkwrite(). 2. Reach __ntfs_write_iomap_begin() and ntfs_attr_map_cluster(). 3. Merge allocation state with ntfs_runlists_merge(). 4. Reallocate ni->runlist.rl in ntfs_rl_realloc(), freeing the old array. Validation reproduced this kernel report: BUG: KASAN: slab-use-after-free in ntfs_attr_fallocate+0xbb8/0xd00 Call Trace: dump_stack_lvl+0x66/0xa0 print_report+0xce/0x630 ? ntfs_attr_fallocate+0xbb8/0xd00 ? srso_alias_return_thunk+0x5/0xfbef5 ? __virt_addr_valid+0x20d/0x410 ? ntfs_attr_fallocate+0xbb8/0xd00 kasan_report+0xe0/0x110 ? ntfs_attr_fallocate+0xbb8/0xd00 ntfs_attr_fallocate+0xbb8/0xd00 ? lock_acquire+0x2b8/0x2f0 ? __pfx_ntfs_attr_fallocate+0x10/0x10 ? 0xffffffffc0000095 ? down_write+0x10d/0x1e0 ntfs_fallocate+0x5c9/0x1d00 ? __pfx_ntfs_fallocate+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? lock_acquire+0x2b8/0x2f0 ? srso_alias_return_thunk+0x5/0xfbef5 ? selinux_file_permission+0x3a7/0x510 vfs_fallocate+0x29d/0xd30 __x64_sys_fallocate+0xc7/0x150 ? do_syscall_64+0x81/0x6a0 do_syscall_64+0x115/0x6a0 entry_SYSCALL_64_after_hwframe+0x77/0x7f Allocated by task 410: kasan_save_stack+0x33/0x60 kasan_save_track+0x14/0x30 __kasan_kmalloc+0xaa/0xb0 __kvmalloc_node_noprof+0x353/0x920 ntfs_rl_realloc+0x3f/0x110 ntfs_runlists_merge+0xaa3/0x3010 ntfs_attr_map_cluster+0x4e5/0xf80 ntfs_attr_fallocate+0x53f/0xd00 ntfs_fallocate+0x5c9/0x1d00 vfs_fallocate+0x29d/0xd30 __x64_sys_fallocate+0xc7/0x150 do_syscall_64+0x115/0x6a0 entry_SYSCALL_64_after_hwframe+0x77/0x7f Freed by task 424: kasan_save_stack+0x33/0x60 kasan_save_track+0x14/0x30 kasan_save_free_info+0x3b/0x60 __kasan_slab_free+0x5f/0x80 kfree+0x307/0x580 ntfs_rl_realloc+0x6f/0x110 ntfs_runlists_merge+0x7b1/0x3010 ntfs_attr_map_cluster+0x4e5/0xf80 __ntfs_write_iomap_begin+0x8cd/0x2280 iomap_iter+0x6de/0x11e0 iomap_page_mkwrite+0x391/0x650 ntfs_filemap_page_mkwrite+0x1ac/0x400 do_page_mkwrite+0x15c/0x280 __handle_mm_fault+0xd6d/0x1ca0 handle_mm_fault+0x19c/0x470 do_user_addr_fault+0x23b/0x9c0 exc_page_fault+0x5c/0xc0 asm_exc_page_fault+0x26/0x30 Fix this by copying the needed runlist fields while the read lock is still held and using only those scalar snapshots after unlocking. After the snapshot, ntfs_attr_map_cluster() can also find that the range is already mapped and return balloc=false. Only call ntfs_dio_zero_range() when new clusters were allocated, matching the write iomap path and preserving the zero-newly-allocated-holes behavior. Fixes: 495e90fa3348 ("ntfs: update attrib operations") Assisted-by: Codex:gpt-5.5 Signed-off-by: Cen Zhang Reviewed-by: Hyunchul Lee Signed-off-by: Namjae Jeon --- fs/ntfs/attrib.c | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c index dd8828098511..a99b84751eb1 100644 --- a/fs/ntfs/attrib.c +++ b/fs/ntfs/attrib.c @@ -5536,6 +5536,7 @@ int ntfs_attr_fallocate(struct ntfs_inode *ni, loff_t start, loff_t byte_len, bo s64 old_data_size; s64 vcn_start, vcn_end, vcn_uninit, vcn, try_alloc_cnt; s64 lcn, alloc_cnt; + s64 rl_lcn, rl_length, rl_vcn; int err = 0; struct runlist_element *rl; bool balloc; @@ -5615,19 +5616,23 @@ int ntfs_attr_fallocate(struct ntfs_inode *ni, loff_t start, loff_t byte_len, bo while (vcn < vcn_uninit) { down_read(&ni->runlist.lock); rl = ntfs_attr_find_vcn_nolock(ni, vcn, NULL); - up_read(&ni->runlist.lock); if (IS_ERR(rl)) { + up_read(&ni->runlist.lock); err = PTR_ERR(rl); goto out; } + rl_lcn = rl->lcn; + rl_length = rl->length; + rl_vcn = rl->vcn; + up_read(&ni->runlist.lock); - if (rl->lcn > 0) { - vcn += rl->length - (vcn - rl->vcn); - } else if (rl->lcn == LCN_DELALLOC || rl->lcn == LCN_HOLE) { - try_alloc_cnt = min(rl->length - (vcn - rl->vcn), + if (rl_lcn > 0) { + vcn += rl_length - (vcn - rl_vcn); + } else if (rl_lcn == LCN_DELALLOC || rl_lcn == LCN_HOLE) { + try_alloc_cnt = min(rl_length - (vcn - rl_vcn), vcn_uninit - vcn); - if (rl->lcn == LCN_DELALLOC) { + if (rl_lcn == LCN_DELALLOC) { vcn += try_alloc_cnt; continue; } @@ -5642,11 +5647,14 @@ int ntfs_attr_fallocate(struct ntfs_inode *ni, loff_t start, loff_t byte_len, bo if (err) goto out; - err = ntfs_dio_zero_range(VFS_I(ni), - lcn << vol->cluster_size_bits, - alloc_cnt << vol->cluster_size_bits); - if (err > 0) - goto out; + if (balloc) { + err = ntfs_dio_zero_range(VFS_I(ni), + lcn << vol->cluster_size_bits, + alloc_cnt << + vol->cluster_size_bits); + if (err > 0) + goto out; + } if (signal_pending(current)) goto out; From eb94f5a41a193a425e09a63cb75dffd151d8f42e Mon Sep 17 00:00:00 2001 From: Peiyang He Date: Tue, 30 Jun 2026 11:08:56 +0800 Subject: [PATCH 0166/1054] ntfs: fix mrec_lock ABBA deadlock in rename ntfs_file_fsync(), ntfs_dir_fsync() and __ntfs_write_inode() lock an inode's mrec_lock before taking the mrec_lock of its parent directory. ntfs_rename() takes old_ni->mrec_lock and old_dir_ni->mrec_lock before taking new_ni->mrec_lock for an existing target, or new_dir_ni->mrec_lock for a cross-directory rename. This can deadlock when ntfs_file_fsync() or __ntfs_write_inode() holds the target inode, or when ntfs_dir_fsync() holds a child target directory, while rename() holds the parent directory and waits for the target. Fix this by locking the existing target inode before taking any parent directory mrec_lock. For cross-directory renames where the target parent is a descendant of the source parent, lock the target parent before the source parent so the directory order matches the child-to-parent order used by ntfs_file_fsync(), ntfs_dir_fsync(), and __ntfs_write_inode(). Reported-by: Peiyang He Closes: https://lore.kernel.org/all/C4D296F0E9F3D66C+9397ffbc-eb55-44bb-9b3f-5da4809e7955@smail.nju.edu.cn/ Fixes: af0db57d4293 ("ntfs: update inode operations") Cc: stable@vger.kernel.org Signed-off-by: Peiyang He Assisted-by: Codex:gpt-5.5 Signed-off-by: Namjae Jeon --- fs/ntfs/namei.c | 62 ++++++++++++++++++++++++------------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/fs/ntfs/namei.c b/fs/ntfs/namei.c index a19626a135bd..5ff25e9aaa32 100644 --- a/fs/ntfs/namei.c +++ b/fs/ntfs/namei.c @@ -1266,6 +1266,7 @@ static int ntfs_rename(struct mnt_idmap *idmap, struct inode *old_dir, struct ntfs_volume *vol = NTFS_SB(sb); struct ntfs_inode *old_ni, *new_ni = NULL; struct ntfs_inode *old_dir_ni = NTFS_I(old_dir), *new_dir_ni = NTFS_I(new_dir); + bool new_dir_first = false; if (NVolShutdown(old_dir_ni->vol)) return -EIO; @@ -1301,36 +1302,39 @@ static int ntfs_rename(struct mnt_idmap *idmap, struct inode *old_dir, old_inode = old_dentry->d_inode; new_inode = new_dentry->d_inode; old_ni = NTFS_I(old_inode); + if (new_inode) + new_ni = NTFS_I(new_inode); + if (old_dir != new_dir) + new_dir_first = is_subdir(new_dentry->d_parent, + old_dentry->d_parent); if (!(vol->vol_flags & VOLUME_IS_DIRTY)) ntfs_set_volume_flags(vol, VOLUME_IS_DIRTY); mutex_lock_nested(&old_ni->mrec_lock, NTFS_INODE_MUTEX_NORMAL); - mutex_lock_nested(&old_dir_ni->mrec_lock, NTFS_INODE_MUTEX_PARENT); + if (new_ni) + mutex_lock_nested(&new_ni->mrec_lock, NTFS_INODE_MUTEX_NORMAL_2); - if (NInoBeingDeleted(old_ni) || NInoBeingDeleted(old_dir_ni)) { + if (old_dir == new_dir) { + mutex_lock_nested(&old_dir_ni->mrec_lock, NTFS_INODE_MUTEX_PARENT); + } else if (new_dir_first) { + mutex_lock_nested(&new_dir_ni->mrec_lock, NTFS_INODE_MUTEX_PARENT); + mutex_lock_nested(&old_dir_ni->mrec_lock, NTFS_INODE_MUTEX_PARENT_2); + } else { + mutex_lock_nested(&old_dir_ni->mrec_lock, NTFS_INODE_MUTEX_PARENT); + mutex_lock_nested(&new_dir_ni->mrec_lock, NTFS_INODE_MUTEX_PARENT_2); + } + + if (NInoBeingDeleted(old_ni) || NInoBeingDeleted(old_dir_ni) || + (new_ni && NInoBeingDeleted(new_ni)) || + (old_dir != new_dir && NInoBeingDeleted(new_dir_ni))) { err = -ENOENT; - goto unlock_old; + goto err_out; } is_dir = S_ISDIR(old_inode->i_mode); if (new_inode) { - new_ni = NTFS_I(new_inode); - mutex_lock_nested(&new_ni->mrec_lock, NTFS_INODE_MUTEX_NORMAL_2); - if (old_dir != new_dir) { - mutex_lock_nested(&new_dir_ni->mrec_lock, NTFS_INODE_MUTEX_PARENT_2); - if (NInoBeingDeleted(new_dir_ni)) { - err = -ENOENT; - goto err_out; - } - } - - if (NInoBeingDeleted(new_ni)) { - err = -ENOENT; - goto err_out; - } - if (is_dir) { struct mft_record *ni_mrec; @@ -1348,14 +1352,6 @@ static int ntfs_rename(struct mnt_idmap *idmap, struct inode *old_dir, err = ntfs_delete(new_ni, new_dir_ni, uname_new, new_name_len, false); if (err) goto err_out; - } else { - if (old_dir != new_dir) { - mutex_lock_nested(&new_dir_ni->mrec_lock, NTFS_INODE_MUTEX_PARENT_2); - if (NInoBeingDeleted(new_dir_ni)) { - err = -ENOENT; - goto err_out; - } - } } err = __ntfs_link(old_ni, new_dir_ni, uname_new, new_name_len); @@ -1386,13 +1382,17 @@ static int ntfs_rename(struct mnt_idmap *idmap, struct inode *old_dir, inode_inc_iversion(new_dir); err_out: - if (old_dir != new_dir) + if (old_dir == new_dir) { + mutex_unlock(&old_dir_ni->mrec_lock); + } else if (new_dir_first) { + mutex_unlock(&old_dir_ni->mrec_lock); mutex_unlock(&new_dir_ni->mrec_lock); - if (new_inode) + } else { + mutex_unlock(&new_dir_ni->mrec_lock); + mutex_unlock(&old_dir_ni->mrec_lock); + } + if (new_ni) mutex_unlock(&new_ni->mrec_lock); - -unlock_old: - mutex_unlock(&old_dir_ni->mrec_lock); mutex_unlock(&old_ni->mrec_lock); if (uname_new) kmem_cache_free(ntfs_name_cache, uname_new); From 46f715a16989f4e7bbbc2eb41447051874b027f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustavo=20Kenji=20Mendon=C3=A7a=20Kaneko?= Date: Tue, 9 Jun 2026 13:08:19 +0000 Subject: [PATCH 0167/1054] drm/arm/malidp: use clk_bulk API in runtime PM resume and suspend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit malidp_runtime_pm_resume() calls clk_prepare_enable() three times without checking the return value. If any clock fails to enable, the driver silently proceeds with unclocked hardware, leading to undefined behavior. Convert both the resume and suspend paths to use the clk_bulk API: clk_bulk_prepare_enable() in resume checks the return value and rolls back any successfully enabled clocks on failure; clk_bulk_disable_unprepare() in suspend keeps the two paths symmetric. This issue was found by code review without access to Mali DP hardware. Signed-off-by: Gustavo Kenji Mendonça Kaneko Reviewed-by: Liviu Dudau Link: https://patch.msgid.link/20260609130812.1065699-1-kaneko.dev@pm.me Signed-off-by: Liviu Dudau --- drivers/gpu/drm/arm/malidp_drv.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/arm/malidp_drv.c b/drivers/gpu/drm/arm/malidp_drv.c index 9abe800f598a..23fa942ae4bb 100644 --- a/drivers/gpu/drm/arm/malidp_drv.c +++ b/drivers/gpu/drm/arm/malidp_drv.c @@ -670,6 +670,11 @@ static int malidp_runtime_pm_suspend(struct device *dev) struct drm_device *drm = dev_get_drvdata(dev); struct malidp_drm *malidp = drm_to_malidp(drm); struct malidp_hw_device *hwdev = malidp->dev; + struct clk_bulk_data clks[] = { + { .clk = hwdev->pclk }, + { .clk = hwdev->aclk }, + { .clk = hwdev->mclk }, + }; /* we can only suspend if the hardware is in config mode */ WARN_ON(!hwdev->hw->in_config_mode(hwdev)); @@ -677,9 +682,7 @@ static int malidp_runtime_pm_suspend(struct device *dev) malidp_se_irq_fini(hwdev); malidp_de_irq_fini(hwdev); hwdev->pm_suspended = true; - clk_disable_unprepare(hwdev->mclk); - clk_disable_unprepare(hwdev->aclk); - clk_disable_unprepare(hwdev->pclk); + clk_bulk_disable_unprepare(ARRAY_SIZE(clks), clks); return 0; } @@ -689,10 +692,17 @@ static int malidp_runtime_pm_resume(struct device *dev) struct drm_device *drm = dev_get_drvdata(dev); struct malidp_drm *malidp = drm_to_malidp(drm); struct malidp_hw_device *hwdev = malidp->dev; + struct clk_bulk_data clks[] = { + { .clk = hwdev->pclk }, + { .clk = hwdev->aclk }, + { .clk = hwdev->mclk }, + }; + int err; + + err = clk_bulk_prepare_enable(ARRAY_SIZE(clks), clks); + if (err) + return err; - clk_prepare_enable(hwdev->pclk); - clk_prepare_enable(hwdev->aclk); - clk_prepare_enable(hwdev->mclk); hwdev->pm_suspended = false; malidp_de_irq_hw_init(hwdev); malidp_se_irq_hw_init(hwdev); From 6502eb8cfcd6f7bc5f1f8b73ee524112bd93319d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustavo=20Kenji=20Mendon=C3=A7a=20Kaneko?= Date: Tue, 9 Jun 2026 13:08:33 +0000 Subject: [PATCH 0168/1054] drm/arm/komeda: fix error handling for clk_prepare_enable() and callers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit komeda_dev_resume() calls clk_prepare_enable() without checking the return value. If the clock fails to enable, the function returns 0 (success) while IRQs are enabled and IOMMU is connected on potentially unclocked hardware, causing undefined behavior on resume. Propagate the error from clk_prepare_enable() and fix all call sites in komeda_drv.c that previously ignored the return value of komeda_dev_resume(): - komeda_platform_probe(): if resume fails, jump to err_destroy_mdev (skipping the suspend call, since the clock was never enabled) - komeda_pm_resume(): propagate the error and skip drm_mode_config_helper_resume() on failure This issue was found by code review without access to Komeda hardware. Signed-off-by: Gustavo Kenji Mendonça Kaneko Reviewed-by: Liviu Dudau Link: https://patch.msgid.link/20260609130828.1066038-1-kaneko.dev@pm.me Signed-off-by: Liviu Dudau --- drivers/gpu/drm/arm/display/komeda/komeda_dev.c | 6 +++++- drivers/gpu/drm/arm/display/komeda/komeda_drv.c | 14 +++++++++----- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_dev.c b/drivers/gpu/drm/arm/display/komeda/komeda_dev.c index 5ba62e637a61..9aad1d1d28ec 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_dev.c +++ b/drivers/gpu/drm/arm/display/komeda/komeda_dev.c @@ -313,7 +313,11 @@ void komeda_dev_destroy(struct komeda_dev *mdev) int komeda_dev_resume(struct komeda_dev *mdev) { - clk_prepare_enable(mdev->aclk); + int err; + + err = clk_prepare_enable(mdev->aclk); + if (err) + return err; mdev->funcs->enable_irq(mdev); diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_drv.c b/drivers/gpu/drm/arm/display/komeda/komeda_drv.c index 4bb5f250e95e..67fffab018ae 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_drv.c +++ b/drivers/gpu/drm/arm/display/komeda/komeda_drv.c @@ -74,8 +74,11 @@ static int komeda_platform_probe(struct platform_device *pdev) } pm_runtime_enable(dev); - if (!pm_runtime_enabled(dev)) - komeda_dev_resume(mdrv->mdev); + if (!pm_runtime_enabled(dev)) { + err = komeda_dev_resume(mdrv->mdev); + if (err) + goto err_destroy_mdev; + } mdrv->kms = komeda_kms_attach(mdrv->mdev); if (IS_ERR(mdrv->kms)) { @@ -93,7 +96,7 @@ static int komeda_platform_probe(struct platform_device *pdev) pm_runtime_disable(dev); else komeda_dev_suspend(mdrv->mdev); - +err_destroy_mdev: komeda_dev_destroy(mdrv->mdev); free_mdrv: @@ -140,11 +143,12 @@ static int __maybe_unused komeda_pm_suspend(struct device *dev) static int __maybe_unused komeda_pm_resume(struct device *dev) { struct komeda_drv *mdrv = dev_get_drvdata(dev); + int err = 0; if (!pm_runtime_status_suspended(dev)) - komeda_dev_resume(mdrv->mdev); + err = komeda_dev_resume(mdrv->mdev); - return drm_mode_config_helper_resume(&mdrv->kms->base); + return err ? err : drm_mode_config_helper_resume(&mdrv->kms->base); } static const struct dev_pm_ops komeda_pm_ops = { From 778c57d624974e64535ef1c9d9b4d8e5066153f4 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 25 Jun 2026 14:40:27 +0200 Subject: [PATCH 0169/1054] drm/panthor: Always use the IRQ-safe variant when acquiring the fence lock Since dma_fence objects can be shared with other subsystems, they may be accessed from hardirq context in those drivers, and we have to take that into account by also using the IRQ-safe variant when acquiring the lock. While at it, switch to the guard model. Fixes: de8548813824 ("drm/panthor: Add the scheduler logical block") Reported-by: sashiko-bot@kernel.org Closes: https://sashiko.dev/#/patchset/20260625-panthor-signal-from-irq-v5-0-8836a74e0ef9@collabora.com?part=11 Signed-off-by: Boris Brezillon Reviewed-by: Liviu Dudau Signed-off-by: Liviu Dudau Link: https://patch.msgid.link/20260625-panthor-misc-fixes-v1-1-b67ed973fea6@collabora.com --- drivers/gpu/drm/panthor/panthor_sched.c | 83 ++++++++++++------------- 1 file changed, 39 insertions(+), 44 deletions(-) diff --git a/drivers/gpu/drm/panthor/panthor_sched.c b/drivers/gpu/drm/panthor/panthor_sched.c index 5b34032deff8..e97f29469d28 100644 --- a/drivers/gpu/drm/panthor/panthor_sched.c +++ b/drivers/gpu/drm/panthor/panthor_sched.c @@ -1151,15 +1151,14 @@ queue_suspend_timeout_locked(struct panthor_queue *queue) static void queue_suspend_timeout(struct panthor_queue *queue) { - spin_lock(&queue->fence_ctx.lock); + guard(spinlock_irqsave)(&queue->fence_ctx.lock); queue_suspend_timeout_locked(queue); - spin_unlock(&queue->fence_ctx.lock); } static void queue_resume_timeout(struct panthor_queue *queue) { - spin_lock(&queue->fence_ctx.lock); + guard(spinlock_irqsave)(&queue->fence_ctx.lock); if (queue_timeout_is_suspended(queue)) { mod_delayed_work(queue->scheduler.timeout_wq, @@ -1168,8 +1167,6 @@ queue_resume_timeout(struct panthor_queue *queue) queue->timeout.remaining = MAX_SCHEDULE_TIMEOUT; } - - spin_unlock(&queue->fence_ctx.lock); } /** @@ -1542,7 +1539,7 @@ cs_slot_process_fault_event_locked(struct panthor_device *ptdev, u64 cs_extract = queue->iface.output->extract; struct panthor_job *job; - spin_lock(&queue->fence_ctx.lock); + guard(spinlock_irqsave)(&queue->fence_ctx.lock); list_for_each_entry(job, &queue->fence_ctx.in_flight_jobs, node) { if (cs_extract >= job->ringbuf.end) continue; @@ -1552,7 +1549,6 @@ cs_slot_process_fault_event_locked(struct panthor_device *ptdev, dma_fence_set_error(job->done_fence, -EINVAL); } - spin_unlock(&queue->fence_ctx.lock); } if (group) { @@ -2183,13 +2179,13 @@ group_term_post_processing(struct panthor_group *group) if (!queue) continue; - spin_lock(&queue->fence_ctx.lock); - list_for_each_entry_safe(job, tmp, &queue->fence_ctx.in_flight_jobs, node) { - list_move_tail(&job->node, &faulty_jobs); - dma_fence_set_error(job->done_fence, err); - dma_fence_signal_locked(job->done_fence); + scoped_guard(spinlock_irqsave, &queue->fence_ctx.lock) { + list_for_each_entry_safe(job, tmp, &queue->fence_ctx.in_flight_jobs, node) { + list_move_tail(&job->node, &faulty_jobs); + dma_fence_set_error(job->done_fence, err); + dma_fence_signal_locked(job->done_fence); + } } - spin_unlock(&queue->fence_ctx.lock); /* Manually update the syncobj seqno to unblock waiters. */ syncobj = group->syncobjs->kmap + (i * sizeof(*syncobj)); @@ -3049,39 +3045,39 @@ static bool queue_check_job_completion(struct panthor_queue *queue) LIST_HEAD(done_jobs); cookie = dma_fence_begin_signalling(); - spin_lock(&queue->fence_ctx.lock); - list_for_each_entry_safe(job, job_tmp, &queue->fence_ctx.in_flight_jobs, node) { - if (!syncobj) { - struct panthor_group *group = job->group; + scoped_guard(spinlock_irqsave, &queue->fence_ctx.lock) { + list_for_each_entry_safe(job, job_tmp, &queue->fence_ctx.in_flight_jobs, node) { + if (!syncobj) { + struct panthor_group *group = job->group; - syncobj = group->syncobjs->kmap + - (job->queue_idx * sizeof(*syncobj)); + syncobj = group->syncobjs->kmap + + (job->queue_idx * sizeof(*syncobj)); + } + + if (syncobj->seqno < job->done_fence->seqno) + break; + + list_move_tail(&job->node, &done_jobs); + dma_fence_signal_locked(job->done_fence); } - if (syncobj->seqno < job->done_fence->seqno) - break; + if (list_empty(&queue->fence_ctx.in_flight_jobs)) { + /* If we have no job left, we cancel the timer, and reset remaining + * time to its default so it can be restarted next time + * queue_resume_timeout() is called. + */ + queue_suspend_timeout_locked(queue); - list_move_tail(&job->node, &done_jobs); - dma_fence_signal_locked(job->done_fence); + /* If there's no job pending, we consider it progress to avoid a + * spurious timeout if the timeout handler and the sync update + * handler raced. + */ + progress = true; + } else if (!list_empty(&done_jobs)) { + queue_reset_timeout_locked(queue); + progress = true; + } } - - if (list_empty(&queue->fence_ctx.in_flight_jobs)) { - /* If we have no job left, we cancel the timer, and reset remaining - * time to its default so it can be restarted next time - * queue_resume_timeout() is called. - */ - queue_suspend_timeout_locked(queue); - - /* If there's no job pending, we consider it progress to avoid a - * spurious timeout if the timeout handler and the sync update - * handler raced. - */ - progress = true; - } else if (!list_empty(&done_jobs)) { - queue_reset_timeout_locked(queue); - progress = true; - } - spin_unlock(&queue->fence_ctx.lock); dma_fence_end_signalling(cookie); list_for_each_entry_safe(job, job_tmp, &done_jobs, node) { @@ -3346,9 +3342,8 @@ queue_run_job(struct drm_sched_job *sched_job) job->ringbuf.end = job->ringbuf.start + (instrs.count * sizeof(u64)); panthor_job_get(&job->base); - spin_lock(&queue->fence_ctx.lock); - list_add_tail(&job->node, &queue->fence_ctx.in_flight_jobs); - spin_unlock(&queue->fence_ctx.lock); + scoped_guard(spinlock_irqsave, &queue->fence_ctx.lock) + list_add_tail(&job->node, &queue->fence_ctx.in_flight_jobs); /* Make sure the ring buffer is updated before the INSERT * register. From 1b8d771fb214e1f783d66caf13d35d7eda39a643 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 25 Jun 2026 14:40:28 +0200 Subject: [PATCH 0170/1054] drm/panthor: Keep the reset work disabled until everything is initialized The reset work will sub-component reset helpers, which might not be ready if the reset happens during initialization, leading to NULL pointer dereferences or worse. Avoid that by keeping the reset work disabled while we're initializing those sub-components. Fixes: 5fe909cae118 ("drm/panthor: Add the device logical block") Reported-by: sashiko-bot@kernel.org Closes: https://sashiko.dev/#/patchset/20260625-panthor-signal-from-irq-v5-0-8836a74e0ef9@collabora.com?part=4 Signed-off-by: Boris Brezillon Reviewed-by: Liviu Dudau Signed-off-by: Liviu Dudau Link: https://patch.msgid.link/20260625-panthor-misc-fixes-v1-2-b67ed973fea6@collabora.com --- drivers/gpu/drm/panthor/panthor_device.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/panthor/panthor_device.c b/drivers/gpu/drm/panthor/panthor_device.c index bd417d6ae8c0..0b25abebb803 100644 --- a/drivers/gpu/drm/panthor/panthor_device.c +++ b/drivers/gpu/drm/panthor/panthor_device.c @@ -207,6 +207,7 @@ int panthor_device_init(struct panthor_device *ptdev) *dummy_page_virt = 1; INIT_WORK(&ptdev->reset.work, panthor_device_reset_work); + disable_work(&ptdev->reset.work); ptdev->reset.wq = alloc_ordered_workqueue("panthor-reset-wq", 0); if (!ptdev->reset.wq) return -ENOMEM; @@ -285,6 +286,9 @@ int panthor_device_init(struct panthor_device *ptdev) panthor_gem_init(ptdev); + /* Now that everything is initialized, we can enable the reset work. */ + enable_work(&ptdev->reset.work); + /* ~3 frames */ pm_runtime_set_autosuspend_delay(ptdev->base.dev, 50); pm_runtime_use_autosuspend(ptdev->base.dev); From b39436d0ba1571dbcda69d20ec567344b3eecfc7 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 25 Jun 2026 14:40:30 +0200 Subject: [PATCH 0171/1054] drm/panthor: Fix potential invalid pointer deref in group_process_tiler_oom() If heaps is an ERR_PTR(), panthor_heap_pool_put() will deref an invalid pointer. Make sure we set it to NULL in that case. Fixes: de8548813824 ("drm/panthor: Add the scheduler logical block") Reported-by: sashiko-bot@kernel.org Closes: https://sashiko.dev/#/patchset/20260625-panthor-signal-from-irq-v5-0-8836a74e0ef9@collabora.com?part=2 Signed-off-by: Boris Brezillon Reviewed-by: Liviu Dudau Signed-off-by: Liviu Dudau Link: https://patch.msgid.link/20260625-panthor-misc-fixes-v1-4-b67ed973fea6@collabora.com --- drivers/gpu/drm/panthor/panthor_sched.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/panthor/panthor_sched.c b/drivers/gpu/drm/panthor/panthor_sched.c index e97f29469d28..8fd4d97b062e 100644 --- a/drivers/gpu/drm/panthor/panthor_sched.c +++ b/drivers/gpu/drm/panthor/panthor_sched.c @@ -1600,7 +1600,10 @@ static int group_process_tiler_oom(struct panthor_group *group, u32 cs_id) if (unlikely(csg_id < 0)) return 0; - if (IS_ERR(heaps) || frag_end > vt_end || vt_end >= vt_start) { + if (IS_ERR(heaps)) { + ret = -EINVAL; + heaps = NULL; + } else if (frag_end > vt_end || vt_end >= vt_start) { ret = -EINVAL; } else { /* We do the allocation without holding the scheduler lock to avoid From fe4c05a59018964bac7923338706371fff3c09ef Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 25 Jun 2026 14:40:31 +0200 Subject: [PATCH 0172/1054] drm/panthor: Fix theoretical IOMEM access in suspended state In theory, our hardirq handler can be called while the device (and thus the panthor_irq) is suspended, because the IRQ line is shared. In practice though, in all the designs we've seen, the line is only shared within the GPU, and because sub-component suspend state is consistent (all-suspended or all-resumed), we shouldn't end up with an interrupt triggered while we're suspended. Fix the problem anyway, if nothing else, for our sanity. Fixes: 0b2d86670a84 ("drm/panthor: Rework panthor_irq::suspended into panthor_irq::state") Reported-by: sashiko-bot@kernel.org Closes: https://sashiko.dev/#/patchset/20260625-panthor-signal-from-irq-v4-0-3d2908912afa@collabora.com?part=1 Signed-off-by: Boris Brezillon Reviewed-by: Liviu Dudau Signed-off-by: Liviu Dudau Link: https://patch.msgid.link/20260625-panthor-misc-fixes-v1-5-b67ed973fea6@collabora.com --- drivers/gpu/drm/panthor/panthor_device.h | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/panthor/panthor_device.h b/drivers/gpu/drm/panthor/panthor_device.h index a412a50eec76..291e0de154bc 100644 --- a/drivers/gpu/drm/panthor/panthor_device.h +++ b/drivers/gpu/drm/panthor/panthor_device.h @@ -509,9 +509,6 @@ static irqreturn_t panthor_ ## __name ## _irq_raw_handler(int irq, void *data) struct panthor_irq *pirq = data; \ enum panthor_irq_state old_state; \ \ - if (!gpu_read(pirq->iomem, INT_STAT)) \ - return IRQ_NONE; \ - \ guard(spinlock_irqsave)(&pirq->mask_lock); \ old_state = atomic_cmpxchg(&pirq->state, \ PANTHOR_IRQ_STATE_ACTIVE, \ @@ -519,6 +516,13 @@ static irqreturn_t panthor_ ## __name ## _irq_raw_handler(int irq, void *data) if (old_state != PANTHOR_IRQ_STATE_ACTIVE) \ return IRQ_NONE; \ \ + if (!gpu_read(pirq->iomem, INT_STAT)) { \ + atomic_cmpxchg(&pirq->state, \ + PANTHOR_IRQ_STATE_PROCESSING, \ + PANTHOR_IRQ_STATE_ACTIVE); \ + return IRQ_NONE; \ + } \ + \ gpu_write(pirq->iomem, INT_MASK, 0); \ return IRQ_WAKE_THREAD; \ } \ From 6fec8b473497b7f32e604a6dd92b32b0889af3e8 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 25 Jun 2026 14:40:32 +0200 Subject: [PATCH 0173/1054] drm/panthor: Don't overrule pending immediate ticks in sched_resume_tick() We schedule immediate ticks when we need to process events on CSGs, but those immediate ticks don't change the resched_target because we want the other groups to stay scheduled for the remaining of the GPU timeslot they were given. Make sure these immediate ticks don't get overruled by a sched_queue_delayed_work() that would delay the tick execution. Fixes: 99820b4b7e50 ("drm/panthor: Make sure we resume the tick when new jobs are submitted") Reported-by: sashiko-bot@kernel.org Closes: https://sashiko.dev/#/patchset/20260625-panthor-signal-from-irq-v4-0-3d2908912afa@collabora.com?part=9 Signed-off-by: Boris Brezillon Reviewed-by: Karunika Choo Signed-off-by: Liviu Dudau Link: https://patch.msgid.link/20260625-panthor-misc-fixes-v1-6-b67ed973fea6@collabora.com --- drivers/gpu/drm/panthor/panthor_sched.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/panthor/panthor_sched.c b/drivers/gpu/drm/panthor/panthor_sched.c index 8fd4d97b062e..ab3e13e44a26 100644 --- a/drivers/gpu/drm/panthor/panthor_sched.c +++ b/drivers/gpu/drm/panthor/panthor_sched.c @@ -2667,7 +2667,14 @@ static void sched_resume_tick(struct panthor_device *ptdev) else delay_jiffies = 0; - sched_queue_delayed_work(sched, tick, delay_jiffies); + /* We schedule immediate ticks when we need to process events on CSGs, + * but those don't change the resched_target because we want the other + * groups to stay scheduled for the remaining of the GPU timeslot they + * were given. Make sure those immediate ticks don't get overruled by + * a sched_queue_delayed_work() that would delay the tick execution. + */ + if (!delayed_work_pending(&sched->tick_work)) + sched_queue_delayed_work(sched, tick, delay_jiffies); } static void group_schedule_locked(struct panthor_group *group, u32 queue_mask) From e62179fd3e23ecfaedf7101e19ec0d3e4f51de76 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 25 Jun 2026 14:40:33 +0200 Subject: [PATCH 0174/1054] drm/panthor: Fix panthor_pwr_unplug() We can't call panthor_pwr_irq_suspend() if the device is suspended, or this leads to a hang when the IOMEM region is accessed while the clks are disabled. Do what other sub-components do and conditionally call panthor_pwr_irq_suspend() if we know the PWR regbank block is accessible. Fixes: c27787f2b77f ("drm/panthor: Introduce panthor_pwr API and power control framework") Signed-off-by: Boris Brezillon Reviewed-by: Liviu Dudau Signed-off-by: Liviu Dudau Link: https://patch.msgid.link/20260625-panthor-misc-fixes-v1-7-b67ed973fea6@collabora.com --- drivers/gpu/drm/panthor/panthor_pwr.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/panthor/panthor_pwr.c b/drivers/gpu/drm/panthor/panthor_pwr.c index 7c7f424a1436..090362bd700b 100644 --- a/drivers/gpu/drm/panthor/panthor_pwr.c +++ b/drivers/gpu/drm/panthor/panthor_pwr.c @@ -453,7 +453,8 @@ void panthor_pwr_unplug(struct panthor_device *ptdev) return; /* Make sure the IRQ handler is not running after that point. */ - panthor_pwr_irq_suspend(&ptdev->pwr->irq); + if (!IS_ENABLED(CONFIG_PM) || pm_runtime_active(ptdev->base.dev)) + panthor_pwr_irq_suspend(&ptdev->pwr->irq); /* Wake-up all waiters. */ spin_lock_irqsave(&ptdev->pwr->reqs_lock, flags); From ee671cedfd204ac793134db32085efd3c23185f7 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 25 Jun 2026 14:40:34 +0200 Subject: [PATCH 0175/1054] drm/panthor: Drop a needless check in panthor_fw_unplug() panthor_fw_unplug() is only called if we at least managed to initialize the IRQ, so it's safe to drop the "is IRQ initialized" check. Signed-off-by: Boris Brezillon Reviewed-by: Liviu Dudau Signed-off-by: Liviu Dudau Link: https://patch.msgid.link/20260625-panthor-misc-fixes-v1-8-b67ed973fea6@collabora.com --- drivers/gpu/drm/panthor/panthor_fw.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/gpu/drm/panthor/panthor_fw.c b/drivers/gpu/drm/panthor/panthor_fw.c index 986151681b24..4fbddb9e18c8 100644 --- a/drivers/gpu/drm/panthor/panthor_fw.c +++ b/drivers/gpu/drm/panthor/panthor_fw.c @@ -1279,9 +1279,7 @@ void panthor_fw_unplug(struct panthor_device *ptdev) if (!IS_ENABLED(CONFIG_PM) || pm_runtime_active(ptdev->base.dev)) { /* Make sure the IRQ handler cannot be called after that point. */ - if (ptdev->fw->irq.irq) - panthor_job_irq_suspend(&ptdev->fw->irq); - + panthor_job_irq_suspend(&ptdev->fw->irq); panthor_fw_stop(ptdev); } From 6efeb9ddb4fbf5ac30aff03e8f09ffbdf966abd0 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 25 Jun 2026 14:40:35 +0200 Subject: [PATCH 0176/1054] drm/panthor: Fix a leak when a group is evicted before the tiler OOM is serviced A group ref is tied to the pending tiler_oom_work, so we need to release it if the cancel was effective. Fixes: de8548813824 ("drm/panthor: Add the scheduler logical block") Reported-by: sashiko-bot@kernel.org Closes: https://sashiko.dev/#/patchset/20260623-panthor-signal-from-irq-v3-0-2ece396f8ee0@collabora.com?part=7 Signed-off-by: Boris Brezillon Reviewed-by: Liviu Dudau Signed-off-by: Liviu Dudau Link: https://patch.msgid.link/20260625-panthor-misc-fixes-v1-9-b67ed973fea6@collabora.com --- drivers/gpu/drm/panthor/panthor_sched.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/panthor/panthor_sched.c b/drivers/gpu/drm/panthor/panthor_sched.c index ab3e13e44a26..5fe95d03f23e 100644 --- a/drivers/gpu/drm/panthor/panthor_sched.c +++ b/drivers/gpu/drm/panthor/panthor_sched.c @@ -1057,7 +1057,8 @@ group_unbind_locked(struct panthor_group *group) /* Tiler OOM events will be re-issued next time the group is scheduled. */ atomic_set(&group->tiler_oom, 0); - cancel_work(&group->tiler_oom_work); + if (cancel_work(&group->tiler_oom_work)) + group_put(group); for (u32 i = 0; i < group->queue_count; i++) group->queues[i]->doorbell_id = -1; From 1f27cef1f41dac0bd254d8741766f189936c9880 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 25 Jun 2026 14:40:36 +0200 Subject: [PATCH 0177/1054] drm/panthor: Interrupt group start/resumption if group_bind_locked() fails group_bind_locked() can fail if the MMU block is stuck. This is normally a reset situation, but by the time we reset the GPU, we might have tried to resume a group that's not resident, which will probably trip out the FW. So let's avoid that by bailing out when group_bind_locked() returns an error. We don't even try to start more groups because the GPU will be reset anyway. Fixes: de8548813824 ("drm/panthor: Add the scheduler logical block") Reported-by: sashiko-bot@kernel.org Closes: https://sashiko.dev/#/patchset/20260623-panthor-signal-from-irq-v3-0-2ece396f8ee0@collabora.com?part=7 Signed-off-by: Boris Brezillon Reviewed-by: Liviu Dudau Signed-off-by: Liviu Dudau Link: https://patch.msgid.link/20260625-panthor-misc-fixes-v1-10-b67ed973fea6@collabora.com --- drivers/gpu/drm/panthor/panthor_sched.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/panthor/panthor_sched.c b/drivers/gpu/drm/panthor/panthor_sched.c index 5fe95d03f23e..298b046c95ed 100644 --- a/drivers/gpu/drm/panthor/panthor_sched.c +++ b/drivers/gpu/drm/panthor/panthor_sched.c @@ -2368,7 +2368,13 @@ tick_ctx_apply(struct panthor_scheduler *sched, struct panthor_sched_tick_ctx *c csg_iface = panthor_fw_get_csg_iface(ptdev, csg_id); csg_slot = &sched->csg_slots[csg_id]; - group_bind_locked(group, csg_id); + ret = group_bind_locked(group, csg_id); + if (ret) { + panthor_device_schedule_reset(ptdev); + ctx->csg_upd_failed_mask |= BIT(csg_id); + return; + } + csg_slot_prog_locked(ptdev, csg_id, new_csg_prio--); csgs_upd_ctx_queue_reqs(ptdev, &upd_ctx, csg_id, group->state == PANTHOR_CS_GROUP_SUSPENDED ? From d50b4edeb1029b9a869c9581cfbe90300d35655f Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 25 Jun 2026 14:40:37 +0200 Subject: [PATCH 0178/1054] drm/panthor: Keep interrupts masked until they are needed The autogenerated panthor_request_xx_irq() helpers unmask Mali interrupts before we're sure we'll have a handler registered. For non-shared IRQ lines, that's fine, but for shared ones, it might cause an interrupt flood if the HW block raises an interrupt for any reason. We could reworking the calls in panthor_request_xx_irq(), but it's just simpler to let the caller decide when they are ready to handle interrupts and call panthor_pwr_irq_resume() themselves. While at it, rework the prototype to let users call panthor_pwr_irq_enable_events() explicitly instead of passing an initial mask to panthor_request_pwr_irq(). Fixes: 5fe909cae118 ("drm/panthor: Add the device logical block") Reported-by: Shashiko Closes: https://sashiko.dev/#/patchset/20260623-panthor-signal-from-irq-v3-0-2ece396f8ee0@collabora.com?part=3 Signed-off-by: Boris Brezillon Reviewed-by: Karunika Choo Reviewed-by: Liviu Dudau Signed-off-by: Liviu Dudau Link: https://patch.msgid.link/20260625-panthor-misc-fixes-v1-11-b67ed973fea6@collabora.com --- drivers/gpu/drm/panthor/panthor_device.h | 7 ++++--- drivers/gpu/drm/panthor/panthor_fw.c | 2 +- drivers/gpu/drm/panthor/panthor_gpu.c | 3 ++- drivers/gpu/drm/panthor/panthor_mmu.c | 9 +++++++-- drivers/gpu/drm/panthor/panthor_pwr.c | 7 ++++--- 5 files changed, 18 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/panthor/panthor_device.h b/drivers/gpu/drm/panthor/panthor_device.h index 291e0de154bc..98828e81db0b 100644 --- a/drivers/gpu/drm/panthor/panthor_device.h +++ b/drivers/gpu/drm/panthor/panthor_device.h @@ -585,14 +585,15 @@ static inline void panthor_ ## __name ## _irq_resume(struct panthor_irq *pirq) \ static int panthor_request_ ## __name ## _irq(struct panthor_device *ptdev, \ struct panthor_irq *pirq, \ - int irq, u32 mask, void __iomem *iomem) \ + int irq, void __iomem *iomem) \ { \ pirq->ptdev = ptdev; \ pirq->irq = irq; \ - pirq->mask = mask; \ + pirq->mask = 0; \ pirq->iomem = iomem; \ spin_lock_init(&pirq->mask_lock); \ - panthor_ ## __name ## _irq_resume(pirq); \ + atomic_set(&pirq->state, PANTHOR_IRQ_STATE_SUSPENDED); \ + gpu_write(pirq->iomem, INT_MASK, 0); \ \ return devm_request_threaded_irq(ptdev->base.dev, irq, \ panthor_ ## __name ## _irq_raw_handler, \ diff --git a/drivers/gpu/drm/panthor/panthor_fw.c b/drivers/gpu/drm/panthor/panthor_fw.c index 4fbddb9e18c8..de8e6689a869 100644 --- a/drivers/gpu/drm/panthor/panthor_fw.c +++ b/drivers/gpu/drm/panthor/panthor_fw.c @@ -1474,7 +1474,7 @@ int panthor_fw_init(struct panthor_device *ptdev) if (irq <= 0) return -ENODEV; - ret = panthor_request_job_irq(ptdev, &fw->irq, irq, 0, + ret = panthor_request_job_irq(ptdev, &fw->irq, irq, ptdev->iomem + JOB_INT_BASE); if (ret) { drm_err(&ptdev->base, "failed to request job irq"); diff --git a/drivers/gpu/drm/panthor/panthor_gpu.c b/drivers/gpu/drm/panthor/panthor_gpu.c index e52c5675981f..c013d6bf9a59 100644 --- a/drivers/gpu/drm/panthor/panthor_gpu.c +++ b/drivers/gpu/drm/panthor/panthor_gpu.c @@ -170,11 +170,12 @@ int panthor_gpu_init(struct panthor_device *ptdev) return irq; ret = panthor_request_gpu_irq(ptdev, &ptdev->gpu->irq, irq, - GPU_INTERRUPTS_MASK, ptdev->iomem + GPU_INT_BASE); if (ret) return ret; + panthor_gpu_irq_enable_events(&ptdev->gpu->irq, GPU_INTERRUPTS_MASK); + panthor_gpu_irq_resume(&ptdev->gpu->irq); return 0; } diff --git a/drivers/gpu/drm/panthor/panthor_mmu.c b/drivers/gpu/drm/panthor/panthor_mmu.c index dab6840e8857..e592a8ebb478 100644 --- a/drivers/gpu/drm/panthor/panthor_mmu.c +++ b/drivers/gpu/drm/panthor/panthor_mmu.c @@ -3262,7 +3262,6 @@ int panthor_mmu_init(struct panthor_device *ptdev) return -ENODEV; ret = panthor_request_mmu_irq(ptdev, &mmu->irq, irq, - panthor_mmu_fault_mask(ptdev, ~0), ptdev->iomem + MMU_INT_BASE); if (ret) return ret; @@ -3280,7 +3279,13 @@ int panthor_mmu_init(struct panthor_device *ptdev) ptdev->gpu_info.mmu_features |= BITS_PER_LONG; } - return drmm_add_action_or_reset(&ptdev->base, panthor_mmu_release_wq, mmu->vm.wq); + ret = drmm_add_action_or_reset(&ptdev->base, panthor_mmu_release_wq, mmu->vm.wq); + if (ret) + return ret; + + panthor_mmu_irq_enable_events(&mmu->irq, panthor_mmu_fault_mask(ptdev, ~0)); + panthor_mmu_irq_resume(&mmu->irq); + return 0; } #ifdef CONFIG_DEBUG_FS diff --git a/drivers/gpu/drm/panthor/panthor_pwr.c b/drivers/gpu/drm/panthor/panthor_pwr.c index 090362bd700b..f2c2c3000590 100644 --- a/drivers/gpu/drm/panthor/panthor_pwr.c +++ b/drivers/gpu/drm/panthor/panthor_pwr.c @@ -484,12 +484,13 @@ int panthor_pwr_init(struct panthor_device *ptdev) if (irq < 0) return irq; - err = panthor_request_pwr_irq( - ptdev, &pwr->irq, irq, PWR_INTERRUPTS_MASK, - pwr->iomem + PWR_INT_BASE); + err = panthor_request_pwr_irq(ptdev, &pwr->irq, irq, + pwr->iomem + PWR_INT_BASE); if (err) return err; + panthor_pwr_irq_enable_events(&pwr->irq, PWR_INTERRUPTS_MASK); + panthor_pwr_irq_resume(&pwr->irq); return 0; } From 1781172526d1092323af443fa03f00e6de560401 Mon Sep 17 00:00:00 2001 From: Sergio Paracuellos Date: Fri, 26 Jun 2026 08:01:09 +0200 Subject: [PATCH 0179/1054] gpio: mt7621: avoid corruption of shared interrupt trigger state The bank-shared fields like 'rising' and 'falling' are modified using non-atomic read-modify-write operations. Since every gpio chip instance represents an entire bank of 32 pins, if 'mediatek_gpio_irq_type()' is called concurrently for different IRQs on the same bank a possible overwrite of each other's configuration is possible. Thus, protect this state with 'gpio_generic_lock_irqsave' lock in the same way it is handled in irp_chip 'mediatek_gpio_irq_mask()' and 'mediatek_gpio_irq_unmask()' callbacks. Cc: stable@vger.kernel.org Reported-by: Sashiko Fixes: 4ba9c3afda41 ("gpio: mt7621: Add a driver for MT7621") Signed-off-by: Sergio Paracuellos Link: https://patch.msgid.link/20260626060112.2498324-2-sergio.paracuellos@gmail.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-mt7621.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpio/gpio-mt7621.c b/drivers/gpio/gpio-mt7621.c index a814885ccd5d..ceb99641baee 100644 --- a/drivers/gpio/gpio-mt7621.c +++ b/drivers/gpio/gpio-mt7621.c @@ -187,6 +187,8 @@ mediatek_gpio_irq_type(struct irq_data *d, unsigned int type) struct mtk_gc *rg = gpiochip_get_data(gc); u32 mask = BIT(mt7621_gpio_hwirq_to_offset(d->hwirq, rg)); + guard(gpio_generic_lock_irqsave)(&rg->chip); + if (type == IRQ_TYPE_PROBE) { if ((rg->rising | rg->falling | rg->hlevel | rg->llevel) & mask) From 839738536adabae1a7e98ed3fc332ce9cc991d27 Mon Sep 17 00:00:00 2001 From: Sergio Paracuellos Date: Fri, 26 Jun 2026 08:01:10 +0200 Subject: [PATCH 0180/1054] gpio: mt7621: more robust management of IRQ domain teardown The driver uses devm_gpiochip_add_data() to register the GPIO chips which means the devres subsystem will unregister them only after the function 'mt7621_gpio_remove()' returns. During the window between domain destruction and devres unregistering the GPIO chips, the chips are still fully active. If a consumer or userspace invokes gpiod_to_irq() during this window, 'mt7621_gpio_to_irq()' can dereference the already-freed irq domain pointer. Thus, manage the IRQ domain teardown using 'devm_add_action_or_reset()' to guarantee it is destroyed strictly after the GPIO chips are removed. Cc: stable@vger.kernel.org Reported-by: Sashiko Fixes: a46f2e5720f5 ("gpio: mt7621: fix interrupt banks mapping on gpio chips") Signed-off-by: Sergio Paracuellos Link: https://patch.msgid.link/20260626060112.2498324-3-sergio.paracuellos@gmail.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-mt7621.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/gpio/gpio-mt7621.c b/drivers/gpio/gpio-mt7621.c index ceb99641baee..57384ef74703 100644 --- a/drivers/gpio/gpio-mt7621.c +++ b/drivers/gpio/gpio-mt7621.c @@ -272,9 +272,9 @@ static const struct irq_chip mt7621_irq_chip = { }; static void -mt7621_gpio_remove(struct platform_device *pdev) +mt7621_gpio_remove(void *data) { - struct mtk *priv = platform_get_drvdata(pdev); + struct mtk *priv = data; int offset, virq; if (priv->gpio_irq > 0) @@ -475,14 +475,14 @@ mediatek_gpio_probe(struct platform_device *pdev) if (mtk->gpio_irq > 0) { ret = mt7621_gpio_irq_setup(pdev, mtk); if (ret) - goto fail; + return ret; } - return 0; + ret = devm_add_action_or_reset(dev, mt7621_gpio_remove, mtk); + if (ret) + return ret; -fail: - mt7621_gpio_remove(pdev); - return ret; + return 0; } static const struct of_device_id mediatek_gpio_match[] = { @@ -493,7 +493,6 @@ MODULE_DEVICE_TABLE(of, mediatek_gpio_match); static struct platform_driver mediatek_gpio_driver = { .probe = mediatek_gpio_probe, - .remove = mt7621_gpio_remove, .driver = { .name = "mt7621_gpio", .of_match_table = mediatek_gpio_match, From 0e024f58291dfcb28d98c512002e1a80fad69798 Mon Sep 17 00:00:00 2001 From: Sergio Paracuellos Date: Fri, 26 Jun 2026 08:01:11 +0200 Subject: [PATCH 0181/1054] gpio: mt7621: be sure IRQ domain is created before exposing GPIO chips Function 'mediatek_gpio_bank_probe()' registers three GPIO chips using 'devm_gpiochip_add_data()'. At this point, the chips become live and visible to consumers. However, the IRQ domain isn't allocated and set up until 'mt7621_gpio_irq_setup()' is called after the GPIO chips setup finishes. If a consumer requests a GPIO IRQ concurrently 'mt7621_gpio_to_irq()' can be called and pass a NULL irq domain pointer irq_create_mapping(), that can corrupt the mappings or cause a crash. Fix this possible problem seting up irq domain before GPIO chips setup is performed. Cc: stable@vger.kernel.org Reported-by: Sashiko Fixes: a46f2e5720f5 ("gpio: mt7621: fix interrupt banks mapping on gpio chips") Signed-off-by: Sergio Paracuellos Link: https://patch.msgid.link/20260626060112.2498324-4-sergio.paracuellos@gmail.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-mt7621.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/gpio/gpio-mt7621.c b/drivers/gpio/gpio-mt7621.c index 57384ef74703..1b0b5247d3c9 100644 --- a/drivers/gpio/gpio-mt7621.c +++ b/drivers/gpio/gpio-mt7621.c @@ -466,12 +466,6 @@ mediatek_gpio_probe(struct platform_device *pdev) mtk->num_gpios = MTK_BANK_WIDTH * MTK_BANK_CNT; platform_set_drvdata(pdev, mtk); - for (i = 0; i < MTK_BANK_CNT; i++) { - ret = mediatek_gpio_bank_probe(dev, i); - if (ret) - return ret; - } - if (mtk->gpio_irq > 0) { ret = mt7621_gpio_irq_setup(pdev, mtk); if (ret) @@ -482,6 +476,12 @@ mediatek_gpio_probe(struct platform_device *pdev) if (ret) return ret; + for (i = 0; i < MTK_BANK_CNT; i++) { + ret = mediatek_gpio_bank_probe(dev, i); + if (ret) + return ret; + } + return 0; } From 1358126fbed104e5657955d3ba029b283687ba02 Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Tue, 23 Jun 2026 15:37:44 +0800 Subject: [PATCH 0182/1054] irqchip/irq-riscv-imsic-early: Fix fwnode leak on state setup failure imsic_early_acpi_init() allocates a firmware node before setting up the IMSIC state. If imsic_setup_state() fails, the function returns without freeing the allocated fwnode. Free the fwnode and clear the global pointer on this error path, matching the cleanup already done when imsic_early_probe() fails. [ tglx: Use a common cleanup path instead of copying code around ] Fixes: fbe826b1c106 ("irqchip/riscv-imsic: Add ACPI support") Signed-off-by: Haoxiang Li Signed-off-by: Thomas Gleixner Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260623073744.2009137-1-haoxiang_li2024@163.com --- drivers/irqchip/irq-riscv-imsic-early.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/irqchip/irq-riscv-imsic-early.c b/drivers/irqchip/irq-riscv-imsic-early.c index a7a1852b548c..12efd241ce88 100644 --- a/drivers/irqchip/irq-riscv-imsic-early.c +++ b/drivers/irqchip/irq-riscv-imsic-early.c @@ -272,16 +272,13 @@ static int __init imsic_early_acpi_init(union acpi_subtable_headers *header, rc = imsic_setup_state(imsic_acpi_fwnode, imsic); if (rc) { pr_err("%pfwP: failed to setup state (error %d)\n", imsic_acpi_fwnode, rc); - return rc; + goto cleanup; } /* Do early setup of IMSIC state and IPIs */ rc = imsic_early_probe(imsic_acpi_fwnode); - if (rc) { - irq_domain_free_fwnode(imsic_acpi_fwnode); - imsic_acpi_fwnode = NULL; - return rc; - } + if (rc) + goto cleanup; rc = imsic_platform_acpi_probe(imsic_acpi_fwnode); @@ -300,8 +297,12 @@ static int __init imsic_early_acpi_init(union acpi_subtable_headers *header, * DT where IPI works but MSI probe fails for some reason. */ return 0; -} +cleanup: + irq_domain_free_fwnode(imsic_acpi_fwnode); + imsic_acpi_fwnode = NULL; + return rc; +} IRQCHIP_ACPI_DECLARE(riscv_imsic, ACPI_MADT_TYPE_IMSIC, NULL, 1, imsic_early_acpi_init); #endif From 2e1368a9d61bdf5502ddade004f223a5831c5b8c Mon Sep 17 00:00:00 2001 From: Yuho Choi Date: Sun, 28 Jun 2026 18:07:23 -0400 Subject: [PATCH 0183/1054] irqchip/gic-v3-its: Fix OF node reference leak of_get_cpu_node() returns a referenced device node. In its_cpu_init_collection(), the Cavium 23144 workaround only uses the node to compare the CPU NUMA node, but the reference is never dropped. Use the device_node cleanup helper for the CPU node reference so it is released when leaving the workaround block, including the NUMA mismatch return path. Fixes: fbf8f40e1658 ("irqchip/gicv3-its: numa: Enable workaround for Cavium thunderx erratum 23144") Signed-off-by: Yuho Choi Signed-off-by: Thomas Gleixner Reviewed-by: Zenghui Yu (Huawei) Acked-by: Marc Zyngier --- drivers/irqchip/irq-gic-v3-its.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index b57d81ad33a0..6f5811aae59c 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -3290,11 +3290,9 @@ static void its_cpu_init_collection(struct its_node *its) /* avoid cross node collections and its mapping */ if (its->flags & ITS_FLAGS_WORKAROUND_CAVIUM_23144) { - struct device_node *cpu_node; + struct device_node *cpu_node __free(device_node) = of_get_cpu_node(cpu, NULL); - cpu_node = of_get_cpu_node(cpu, NULL); - if (its->numa_node != NUMA_NO_NODE && - its->numa_node != of_node_to_nid(cpu_node)) + if (its->numa_node != NUMA_NO_NODE && its->numa_node != of_node_to_nid(cpu_node)) return; } From 98bf7e54cec07d514b3575c11896a8b12d50ecc4 Mon Sep 17 00:00:00 2001 From: Qingshuang Fu Date: Tue, 23 Jun 2026 09:52:11 +0800 Subject: [PATCH 0184/1054] irqchip/ts4800: Fix missing chained handler cleanup on remove The driver installs a chained handler for the parent interrupt during probe using irq_set_chained_handler_and_data(), but the remove function does not clear this handler. This leaves a dangling handler that may be called when the parent interrupt fires after the driver has been removed, potentially accessing freed memory and causing a kernel crash. Additionally, the parent_irq obtained via irq_of_parse_and_map() is not stored, making it inaccessible in the remove function. Moreover, interrupt mappings created during probe are not properly disposed. Fix this by: - Saving parent_irq in probe - Clearing the chained handler with NULL in ts4800_ic_remove() - Disposing all IRQ mappings before domain removal to prevent resource leaks Fixes: d01f8633d52e ("irqchip/ts4800: Add TS-4800 interrupt controller") Signed-off-by: Qingshuang Fu Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260623015211.109382-1-fffsqian@163.com --- drivers/irqchip/irq-ts4800.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/irqchip/irq-ts4800.c b/drivers/irqchip/irq-ts4800.c index 2e4013c6834d..c7c0b155e353 100644 --- a/drivers/irqchip/irq-ts4800.c +++ b/drivers/irqchip/irq-ts4800.c @@ -28,6 +28,7 @@ struct ts4800_irq_data { void __iomem *base; struct platform_device *pdev; struct irq_domain *domain; + unsigned int parent_irq; }; static void ts4800_irq_mask(struct irq_data *d) @@ -134,6 +135,7 @@ static int ts4800_ic_probe(struct platform_device *pdev) irq_set_chained_handler_and_data(parent_irq, ts4800_ic_chained_handle_irq, data); + data->parent_irq = parent_irq; platform_set_drvdata(pdev, data); return 0; @@ -142,6 +144,14 @@ static int ts4800_ic_probe(struct platform_device *pdev) static void ts4800_ic_remove(struct platform_device *pdev) { struct ts4800_irq_data *data = platform_get_drvdata(pdev); + unsigned int hwirq; + + irq_set_chained_handler_and_data(data->parent_irq, NULL, NULL); + + for (hwirq = 0; hwirq < 8; hwirq++) + irq_dispose_mapping(irq_find_mapping(data->domain, hwirq)); + + irq_dispose_mapping(data->parent_irq); irq_domain_remove(data->domain); } From d1f02ae4764247fd6220d56930ac7d33af1ac1cf Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Fri, 26 Jun 2026 20:03:22 +0200 Subject: [PATCH 0185/1054] spi: dt-bindings: snps,dw-apb-ssi: drop superfluous RZ/N1 entry Commit 164c05f03ffa ("spi: Convert DW SPI binding to DT schema") added an RZ/N1 entry which was not in the original txt-file. It doesn't follow the usual ", " style for Renesas SoCs which was properly added later with commit 029d32a892a8 ("spi: dw-apb-ssi: Integrate Renesas RZ/N1 SPI controller"). In that commit, removing the bogus entry was overlooked and is finally done now. Signed-off-by: Wolfram Sang Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260626180326.9593-2-wsa+renesas@sang-engineering.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml b/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml index 4458316326fc..447be88caf34 100644 --- a/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml +++ b/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml @@ -50,7 +50,6 @@ properties: - enum: - mscc,ocelot-spi - mscc,jaguar2-spi - - renesas,rzn1-spi - sophgo,sg2042-spi - thead,th1520-spi - const: snps,dw-apb-ssi From eeda4e1e6d7e178f9638b039f93a01ba2066bbfa Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Fri, 26 Jun 2026 20:03:23 +0200 Subject: [PATCH 0186/1054] spi: dt-bindings: snps,dw-apb-ssi: add 'power-domains' property This SPI controller likely belongs to a power domain for all the SoCs listed. For sure, it belongs to one on the Renesas RZ/N1 SoC, so enable the property to be able to describe its power domain in DTs. Suggested-by: Herve Codina Signed-off-by: Wolfram Sang Reviewed-by: Herve Codina Acked-by: Krzysztof Kozlowski Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260626180326.9593-3-wsa+renesas@sang-engineering.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml b/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml index 447be88caf34..95a5bd894e93 100644 --- a/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml +++ b/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml @@ -93,6 +93,9 @@ properties: - const: ssi_clk - const: pclk + power-domains: + maxItems: 1 + resets: maxItems: 1 From e782d687d2f5bf8b8113dc48ba22cca4b472c252 Mon Sep 17 00:00:00 2001 From: Jetha Chan Date: Tue, 30 Jun 2026 09:33:28 +0900 Subject: [PATCH 0187/1054] ASoC: amd: yc: Add Alienware m15 R7 AMD to DMIC quirk table The Alienware m15 R7 AMD exposes an ACP6x DMIC path, but its DMI product name is not present in the Yellow Carp ACP quirk table. As a result, the ACP machine driver does not enable the DMIC card on this system. Add the DMI product name for this machine. With this quirk applied, the kernel reports: acp_yc_mach acp_yc_mach.0: Enabling ACP DMIC support via DMI and ALSA exposes the ACP DMIC capture device: card 3: acp6x device 0: DMIC capture dmic-hifi-0 Tested on an Alienware m15 R7 AMD with product SKU 0B59. Link: https://jethachan.net/dev/2026/03/21/fixing-internal-microphone-alienware-linux.html Assisted-by: OpenAI-Codex:gpt-5.5 Signed-off-by: Jetha Chan Link: https://patch.msgid.link/20260630003328.15675-1-jethachan@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 a405cc2c11a3..d6df7de70b27 100644 --- a/sound/soc/amd/yc/acp6x-mach.c +++ b/sound/soc/amd/yc/acp6x-mach.c @@ -528,6 +528,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Raider A18 HX A9WJG"), } }, + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Alienware"), + DMI_MATCH(DMI_PRODUCT_NAME, "Alienware m15 R7 AMD"), + } + }, { .driver_data = &acp6x_card, .matches = { From 7fc2c3dcae28347a30ccd76c8817e5719005f1c3 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Sat, 27 Jun 2026 00:02:29 +0800 Subject: [PATCH 0188/1054] spi: rzv2h-rspi: Fix DMA transfer error handling for signal interruption wait_event_interruptible_timeout() can return a negative error code when interrupted by a signal. The original code treated all non-zero return values as success, which would incorrectly synchronize DMA channels and return 0 instead of propagating the interruption error. Fixes: fa08b566860b ("spi: rzv2h-rspi: add support for DMA mode") Signed-off-by: Felix Gu Reviewed-by: Cosmin Tanislav Tested-by: Cosmin Tanislav Reviewed-by: Wolfram Sang Link: https://patch.msgid.link/20260627-rspi-v1-1-170c93ee14da@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-rzv2h-rspi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-rzv2h-rspi.c b/drivers/spi/spi-rzv2h-rspi.c index 694e5305c638..3e0f1a584b92 100644 --- a/drivers/spi/spi-rzv2h-rspi.c +++ b/drivers/spi/spi-rzv2h-rspi.c @@ -365,14 +365,14 @@ static int rzv2h_rspi_transfer_dma(struct rzv2h_rspi_priv *rspi, rzv2h_rspi_clear_all_irqs(rspi); ret = wait_event_interruptible_timeout(rspi->wait, rspi->dma_callbacked, HZ); - if (ret) { + if (ret > 0) { dmaengine_synchronize(rspi->controller->dma_tx); dmaengine_synchronize(rspi->controller->dma_rx); ret = 0; } else { dmaengine_terminate_sync(rspi->controller->dma_tx); dmaengine_terminate_sync(rspi->controller->dma_rx); - ret = -ETIMEDOUT; + ret = ret ?: -ETIMEDOUT; } enable_irq(rspi->irq_rx); From c9a71daaecb2fb1d8c704545cc0b1c920b9bf5d7 Mon Sep 17 00:00:00 2001 From: Chi Wang Date: Fri, 19 Jun 2026 15:42:44 +0800 Subject: [PATCH 0189/1054] audit: Fix data races of skb_queue_len() readers on audit_queue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Multiple readers access audit_queue.qlen via skb_queue_len() without holding the queue lock or using READ_ONCE(), while kauditd writes to this field via the skb_dequeue() → __skb_unlink() path with WRITE_ONCE() protected by a spinlock. This constitutes data races. All affected skb_queue_len(&audit_queue) call sites: - kauditd_thread() wait_event_freezable() condition - audit_receive_msg() AUDIT_GET handler (s.backlog assignment) - audit_receive() backlog check - audit_log_start() backlog check and pr_warn() KCSAN reports the following conflicting access pattern (one example): ================================================================== BUG: KCSAN: data-race in audit_log_start / skb_dequeue write (marked) to 0xffffffff8512ee20 of 4 bytes by task 661 on cpu 57: skb_dequeue+0x70/0xf0 kauditd_send_queue+0x71/0x220 kauditd_thread+0x1cb/0x430 kthread+0x1c2/0x210 ret_from_fork+0x162/0x1a0 ret_from_fork_asm+0x1a/0x30 read to 0xffffffff8512ee20 of 4 bytes by task 36586 on cpu 1: audit_log_start+0x2a0/0x6b0 audit_core_dumps+0x64/0xa0 do_coredump+0x14b/0x1260 get_signal+0xeb2/0xf70 arch_do_signal_or_restart+0x41/0x170 exit_to_user_mode_loop+0xa2/0x1c0 do_syscall_64+0x1a3/0x1c0 entry_SYSCALL_64_after_hwframe+0x76/0xe0 value changed: 0x00000001 -> 0x00000000 ================================================================== Resolve the race by switching to lockless helper skb_queue_len_lockless(), which internally uses READ_ONCE() and properly pairs with the WRITE_ONCE() write accesses already present on the writer side. Cc: stable@vger.kernel.org Fixes: 3197542482df ("audit: rework audit_log_start()") Signed-off-by: Chi Wang Reviewed-by: Ricardo Robaina [PM: line length tweak] Signed-off-by: Paul Moore --- kernel/audit.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/kernel/audit.c b/kernel/audit.c index dcc657d35776..feaa4e2271d7 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -950,7 +950,7 @@ static int kauditd_thread(void *dummy) * do the multicast send and rotate records from the * main queue to the retry/hold queues */ wait_event_freezable(kauditd_wait, - (skb_queue_len(&audit_queue) ? 1 : 0)); + (skb_queue_len_lockless(&audit_queue) ? 1 : 0)); } return 0; @@ -1283,7 +1283,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh, s.rate_limit = audit_rate_limit; s.backlog_limit = audit_backlog_limit; s.lost = atomic_read(&audit_lost); - s.backlog = skb_queue_len(&audit_queue); + s.backlog = skb_queue_len_lockless(&audit_queue); s.feature_bitmap = AUDIT_FEATURE_BITMAP_ALL; s.backlog_wait_time = audit_backlog_wait_time; s.backlog_wait_time_actual = atomic_read(&audit_backlog_wait_time_actual); @@ -1627,7 +1627,7 @@ static void audit_receive(struct sk_buff *skb) /* can't block with the ctrl lock, so penalize the sender now */ if (audit_backlog_limit && - (skb_queue_len(&audit_queue) > audit_backlog_limit)) { + (skb_queue_len_lockless(&audit_queue) > audit_backlog_limit)) { DECLARE_WAITQUEUE(wait, current); /* wake kauditd to try and flush the queue */ @@ -1933,7 +1933,7 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask, long stime = audit_backlog_wait_time; while (audit_backlog_limit && - (skb_queue_len(&audit_queue) > audit_backlog_limit)) { + (skb_queue_len_lockless(&audit_queue) > audit_backlog_limit)) { /* wake kauditd to try and flush the queue */ wake_up_interruptible(&kauditd_wait); @@ -1953,7 +1953,7 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask, } else { if (audit_rate_check() && printk_ratelimit()) pr_warn("audit_backlog=%d > audit_backlog_limit=%d\n", - skb_queue_len(&audit_queue), + skb_queue_len_lockless(&audit_queue), audit_backlog_limit); audit_log_lost("backlog limit exceeded"); return NULL; From bc7b086a45521a986a49045907f017e3e46c763e Mon Sep 17 00:00:00 2001 From: Martin Kaiser Date: Tue, 30 Jun 2026 21:40:03 +0200 Subject: [PATCH 0190/1054] riscv: probes: save original sp in rethook trampoline Reading a word from the stack in a kretprobe crashes a risc-v kernel. $ cd /sys/kernel/tracing/ $ echo 'r n_tty_write $stack0' > dynamic_events $ echo 1 > events/kprobes/enable Unable to handle kernel paging request at virtual address 0000000200000128 ... [] regs_get_kernel_stack_nth+0x26/0x38 [] process_fetch_insn+0x3ee/0x760 [] kretprobe_trace_func+0x116/0x1f0 [] kretprobe_dispatcher+0x4a/0x58 [] kretprobe_rethook_handler+0x5e/0x90 [] rethook_trampoline_handler+0x70/0x108 [] arch_rethook_trampoline_callback+0x12/0x1c [] arch_rethook_trampoline+0x48/0x94 [] tty_write+0x1a/0x30 In regs_get_kernel_stack_nth, regs->sp contains an arbitrary value. arch_rethook_trampoline saves the registers from the probed function in a struct pt_regs. sp is not saved. Instead, sp is decremented for arch_rethook_trampoline's local stack. Fix this crash and save the original sp along with the other registers. Use a0 as a temporary register, it is overwritten anyway. Cc: stable@vger.kernel.org Fixes: c22b0bcb1dd02 ("riscv: Add kprobes supported") Signed-off-by: Martin Kaiser Acked-by: Masami Hiramatsu (Google) Link: https://patch.msgid.link/20260630194010.1824039-1-martin@kaiser.cx [pjw@kernel.org: added Fixes tag; cc'ed stable] Signed-off-by: Paul Walmsley --- arch/riscv/kernel/probes/rethook_trampoline.S | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/riscv/kernel/probes/rethook_trampoline.S b/arch/riscv/kernel/probes/rethook_trampoline.S index f2cd83d9b0f0..c3aa8d8cf5af 100644 --- a/arch/riscv/kernel/probes/rethook_trampoline.S +++ b/arch/riscv/kernel/probes/rethook_trampoline.S @@ -41,6 +41,9 @@ REG_S x29, PT_T4(sp) REG_S x30, PT_T5(sp) REG_S x31, PT_T6(sp) + /* save original sp */ + addi a0, sp, PT_SIZE_ON_STACK + REG_S a0, PT_SP(sp) .endm .macro restore_all_base_regs From a2d30022b7c316ad845d1b696e724058b88e5a4e Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Thu, 25 Jun 2026 21:38:08 +0200 Subject: [PATCH 0191/1054] iio: light: al3000a: add missing REGMAP_I2C to Kconfig The KConfig entry for the al3000a is missing a `select REGMAP_I2C`, causing build failures. Fixes: d531b9f78949 ("iio: light: Add support for AL3000a illuminance sensor") Signed-off-by: Joshua Crofts Reviewed-by: Andy Shevchenko Reviewed-by: David Heidelberg Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/light/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index ef36824f312f..a33920568904 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -45,6 +45,7 @@ config ADUX1020 config AL3000A tristate "AL3000a ambient light sensor" + select REGMAP_I2C depends on I2C help Say Y here if you want to build a driver for the Dyna Image AL3000a From 84486e3bbda18a2df1ed74ca78e1e14bde9a941b Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Thu, 25 Jun 2026 21:38:09 +0200 Subject: [PATCH 0192/1054] iio: light: al3010: add missing REGMAP_I2C to Kconfig The KConfig entry for the AL3010 is missing a `select REGMAP_I2C`, causing build failures. Fixes: 0e5e21e23dd6 ("iio: light: al3010: Implement regmap support") Signed-off-by: Joshua Crofts Reviewed-by: Andy Shevchenko Reviewed-by: David Heidelberg Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/light/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index a33920568904..4ba3151ebea7 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -56,6 +56,7 @@ config AL3000A config AL3010 tristate "AL3010 ambient light sensor" + select REGMAP_I2C depends on I2C help Say Y here if you want to build a driver for the Dyna Image AL3010 From 9efcc9ba9b2e940cc01e63d132ae741e4c5d09c7 Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Thu, 25 Jun 2026 21:38:10 +0200 Subject: [PATCH 0193/1054] iio: light: al3320a: add missing REGMAP_I2C to Kconfig The Kconfig entry for the al3320a is missing a `select REGMAP_I2C`, causing build failures. Fixes: 1850e6ae7f91 ("iio: light: al3320a: Implement regmap support") Signed-off-by: Joshua Crofts Reviewed-by: Andy Shevchenko Reviewed-by: David Heidelberg Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/light/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index 4ba3151ebea7..f23bbce12c72 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -67,6 +67,7 @@ config AL3010 config AL3320A tristate "AL3320A ambient light sensor" + select REGMAP_I2C depends on I2C help Say Y here if you want to build a driver for the Dyna Image AL3320A From d4d56b00c7df88cd5751e7415bdfabc9fdbc82a7 Mon Sep 17 00:00:00 2001 From: Qiang Liu Date: Wed, 24 Jun 2026 09:13:18 +0800 Subject: [PATCH 0194/1054] ksmbd: fix sd_ndr.data memory leak in ksmbd_vfs_set_sd_xattr ndr_encode_v4_ntacl() allocates sd_ndr.data via kzalloc() at entry. If any subsequent ndr_write_*() call returns error during encoding, the allocated sd_ndr.data won't be freed and causes memory leak. Move kfree(sd_ndr.data) into out label to ensure the buffer gets released on all success and error return paths. Signed-off-by: Qiang Liu Reviewed-by: ChenXiaoSong Acked-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/vfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c index f5fa22d87603..8e38c748d15b 100644 --- a/fs/smb/server/vfs.c +++ b/fs/smb/server/vfs.c @@ -1487,8 +1487,8 @@ int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn, if (rc < 0) pr_err("Failed to store XATTR ntacl :%d\n", rc); - kfree(sd_ndr.data); out: + kfree(sd_ndr.data); kfree(acl_ndr.data); kfree(smb_acl); kfree(def_smb_acl); From d708a36634bb7b6f94d0e76d587d2ec50b2b93b5 Mon Sep 17 00:00:00 2001 From: Qiang Liu Date: Wed, 24 Jun 2026 09:13:19 +0800 Subject: [PATCH 0195/1054] ksmbd: Fix acl.sd_buf memory leak and invalid sd_size error handling 1. When ndr_decode_v4_ntacl() fails, the code jumped to free_n_data which only freed n.data, skipping kfree(acl.sd_buf) and leaking the buffer. Zero-initialize struct xattr_ntacl acl, reorder error labels to out_free to release acl.sd_buf on all error paths. 2. if (acl.sd_size < sizeof(struct smb_ntsd)) is true, original code returned success without freeing sd_buf and left stale *pntsd. Set rc = -EINVAL before jumping to out_free to return error code and free buffer. Signed-off-by: Qiang Liu Reviewed-by: ChenXiaoSong Acked-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/vfs.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c index 8e38c748d15b..6528412eac2d 100644 --- a/fs/smb/server/vfs.c +++ b/fs/smb/server/vfs.c @@ -1504,7 +1504,7 @@ int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn, struct ndr n; struct inode *inode = d_inode(dentry); struct ndr acl_ndr = {0}; - struct xattr_ntacl acl; + struct xattr_ntacl acl = {0}; struct xattr_smb_acl *smb_acl = NULL, *def_smb_acl = NULL; __u8 cmp_hash[XATTR_SD_HASH_SIZE] = {0}; @@ -1515,7 +1515,7 @@ int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn, n.length = rc; rc = ndr_decode_v4_ntacl(&n, &acl); if (rc) - goto free_n_data; + goto out_free; smb_acl = ksmbd_vfs_make_xattr_posix_acl(idmap, inode, ACL_TYPE_ACCESS); @@ -1541,6 +1541,7 @@ int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn, *pntsd = acl.sd_buf; if (acl.sd_size < sizeof(struct smb_ntsd)) { pr_err("sd size is invalid\n"); + rc = -EINVAL; goto out_free; } @@ -1560,8 +1561,6 @@ int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn, kfree(acl.sd_buf); *pntsd = NULL; } - -free_n_data: kfree(n.data); return rc; } From 7ac657bb9c5c1b0f7bdf1fa6d3ad532f969be5cf Mon Sep 17 00:00:00 2001 From: Qiang Liu Date: Wed, 24 Jun 2026 09:13:20 +0800 Subject: [PATCH 0196/1054] ksmbd: fix n.data memory leak in ksmbd_vfs_set_dos_attrib_xattr Free ndr buffer data when ndr_encode_dos_attr() returns error to avoid memory leak. Signed-off-by: Qiang Liu Reviewed-by: ChenXiaoSong Acked-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/vfs.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c index 6528412eac2d..d0a0ad15d803 100644 --- a/fs/smb/server/vfs.c +++ b/fs/smb/server/vfs.c @@ -1575,14 +1575,15 @@ int ksmbd_vfs_set_dos_attrib_xattr(struct mnt_idmap *idmap, err = ndr_encode_dos_attr(&n, da); if (err) - return err; + goto out; err = ksmbd_vfs_setxattr(idmap, path, XATTR_NAME_DOS_ATTRIBUTE, (void *)n.data, n.offset, 0, get_write); if (err) ksmbd_debug(SMB, "failed to store dos attribute in xattr\n"); - kfree(n.data); +out: + kfree(n.data); return err; } From 60908f7ebcd9b6cde74ad5711fab0f49c7970949 Mon Sep 17 00:00:00 2001 From: Haofeng Li Date: Thu, 25 Jun 2026 14:48:07 +0000 Subject: [PATCH 0197/1054] ksmbd: reject undersized DACLs before parsing ACEs parse_dacl() limits the attacker-controlled ACE count by comparing it with the number of minimal ACEs that fit in the DACL size. The DACL size field is 16 bits, but the expression subtracts sizeof(struct smb_acl). Because sizeof() is unsigned, a DACL size smaller than the ACL header underflows to a large size_t. A malicious client can reach this with: SMB2_SET_INFO (InfoType=SMB2_O_INFO_SECURITY) -> smb2_set_info_sec() -> set_info_sec() -> parse_sec_desc() -> parse_dacl() -> init_acl_state(..., 0xffff) -> init_acl_state(..., 0xffff) -> kmalloc_objs(..., 0xffff) Thus a malformed security descriptor can make num_aces pass the guard and drive large temporary ACL state and pointer-array allocations. Reject DACLs smaller than struct smb_acl before doing the subtraction, so the ACE count check cannot be bypassed by the underflow. Fixes: e2f34481b24d ("cifsd: add server-side procedures for SMB3") Signed-off-by: Haofeng Li Reviewed-by: ChenXiaoSong Acked-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/smbacl.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/fs/smb/server/smbacl.c b/fs/smb/server/smbacl.c index 340ea98fa494..fc9937cedb01 100644 --- a/fs/smb/server/smbacl.c +++ b/fs/smb/server/smbacl.c @@ -374,6 +374,7 @@ static void parse_dacl(struct mnt_idmap *idmap, { int i, ret; u16 num_aces = 0; + u16 dacl_size; unsigned int acl_size; char *acl_base; struct smb_ace **ppace; @@ -403,7 +404,11 @@ static void parse_dacl(struct mnt_idmap *idmap, if (num_aces <= 0) return; - if (num_aces > (le16_to_cpu(pdacl->size) - sizeof(struct smb_acl)) / + dacl_size = le16_to_cpu(pdacl->size); + if (dacl_size < sizeof(struct smb_acl)) + return; + + if (num_aces > (dacl_size - sizeof(struct smb_acl)) / (offsetof(struct smb_ace, sid) + offsetof(struct smb_sid, sub_auth) + sizeof(__le16))) return; From 47f0b34f6bc98ed85bfdc293e8f3e432ec24958d Mon Sep 17 00:00:00 2001 From: Haofeng Li Date: Fri, 26 Jun 2026 00:52:17 +0000 Subject: [PATCH 0198/1054] ksmbd: validate num_subauth when copying ACE in set_ntacl_dacl set_ntacl_dacl() copies each ACE from the attacker-controlled stored security descriptor verbatim into the response DACL without checking sid.num_subauth. The ACE bytes (including an unchecked num_subauth) originate from an authenticated SMB2_SET_INFO(SecInfo=DACL) that is stored raw via ksmbd_vfs_set_sd_xattr(); parse_dacl() rejects a bad ACE with `break` rather than an error, so parse_sec_desc() still returns success and the malformed SD reaches the xattr intact. On a subsequent SMB2_QUERY_INFO(SecInfo=DACL) for an inode carrying a POSIX access ACL, build_sec_desc() -> set_ntacl_dacl() -> set_posix_acl_entries_dacl() walks the copied ACEs and reads ntace->sid.sub_auth[ntace->sid.num_subauth - 1] with num_subauth taken straight from the stored SD. Since sub_auth[] is fixed at SID_MAX_SUB_AUTHORITIES (15), a crafted num_subauth (e.g. 255) drives an out-of-bounds heap read of ~1 KB with an offset fully controlled by an authenticated client. The sibling functions already gate this field: parse_dacl() -- num_subauth == 0 || > SID_MAX_SUB_AUTHORITIES parse_sid() -- num_subauth > SID_MAX_SUB_AUTHORITIES smb_copy_sid() -- min_t(u8, num_subauth, SID_MAX_SUB_AUTHORITIES) set_ntacl_dacl() is the lone inconsistent path that omits the check. Add the same num_subauth validation in set_ntacl_dacl() before copying the ACE, matching the gate already enforced by parse_dacl(). Signed-off-by: Haofeng Li Reviewed-by: ChenXiaoSong Suggested-by: Namjae Jeon Acked-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/smbacl.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/fs/smb/server/smbacl.c b/fs/smb/server/smbacl.c index fc9937cedb01..9c59c8f73b66 100644 --- a/fs/smb/server/smbacl.c +++ b/fs/smb/server/smbacl.c @@ -745,12 +745,18 @@ static void set_ntacl_dacl(struct mnt_idmap *idmap, if (nt_ace_size > aces_size) break; + if (ntace->sid.num_subauth == 0 || + ntace->sid.num_subauth > SID_MAX_SUB_AUTHORITIES) + goto next_ace; + memcpy((char *)pndace + size, ntace, nt_ace_size); if (check_add_overflow(size, nt_ace_size, &size)) break; + num_aces++; + +next_ace: aces_size -= nt_ace_size; ntace = (struct smb_ace *)((char *)ntace + nt_ace_size); - num_aces++; } } From 6b9a2e09d4cc5cea824ce4b457bf91dffa4a41cb Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Fri, 26 Jun 2026 10:49:16 +0900 Subject: [PATCH 0199/1054] ksmbd: avoid zeroing the read buffer in smb2_read() smb2_read() allocates the read payload buffer with kvzalloc(), zeroing up to max_read_size bytes (1MB or more with multichannel) on every read, only to immediately overwrite the region with file data via kernel_read(). The zero-fill is pure overhead: ksmbd_vfs_read() returns the number of bytes actually read ('nbytes'), and only those nbytes are ever consumed - they are pinned into the response iov (ksmbd_iov_pin_rsp_read()), sent over the RDMA channel (smb2_read_rdma_channel()), or copied by the compression path (ksmbd_compress_response() uses iov_len == nbytes). The ALIGN(length, 8) tail padding and any short-read remainder are never read or transmitted, so they need not be initialized. Use kvmalloc() instead to skip the redundant zeroing. This reduces CPU and memory-bandwidth usage on large sequential reads. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/smb2pdu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 5859fa68bb84..73b3758f41ee 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -7324,7 +7324,7 @@ int smb2_read(struct ksmbd_work *work) ksmbd_debug(SMB, "filename %pD, offset %lld, len %zu\n", fp->filp, offset, length); - aux_payload_buf = kvzalloc(ALIGN(length, 8), KSMBD_DEFAULT_GFP); + aux_payload_buf = kvmalloc(ALIGN(length, 8), KSMBD_DEFAULT_GFP); if (!aux_payload_buf) { err = -ENOMEM; goto out; From 284dc80ff529a0b454f11b6c2fea0d5daf6f315f Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Fri, 26 Jun 2026 10:51:16 +0900 Subject: [PATCH 0200/1054] ksmbd: fix credit charge calculation for SMB2 QUERY_INFO smb2_validate_credit_charge() computes the credit charge a request is allowed to consume from the payload size: CreditCharge = (max(SendPayloadSize, ResponsePayloadSize) - 1)/65536 + 1 For SMB2 QUERY_INFO, the server must validate CreditCharge based on the *maximum* of InputBufferLength and OutputBufferLength. ksmbd instead summed the two lengths, which overestimates the required charge. As a result a single-credit QUERY_INFO whose InputBufferLength and OutputBufferLength each fit in 64KB but whose sum exceeds 64KB is rejected with STATUS_INVALID_PARAMETER, even though it is a valid request. IOCTL already uses max() of the request and response sizes; make QUERY_INFO consistent by feeding InputBufferLength as the request length and OutputBufferLength as the expected response length so that smb2_validate_credit_charge() takes their maximum. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/smb2misc.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/fs/smb/server/smb2misc.c b/fs/smb/server/smb2misc.c index a1ddca21c47b..35fba49d27d6 100644 --- a/fs/smb/server/smb2misc.c +++ b/fs/smb/server/smb2misc.c @@ -261,8 +261,12 @@ static int smb2_calc_size(void *buf, unsigned int *len) static inline int smb2_query_info_req_len(struct smb2_query_info_req *h) { - return le32_to_cpu(h->InputBufferLength) + - le32_to_cpu(h->OutputBufferLength); + return le32_to_cpu(h->InputBufferLength); +} + +static inline int smb2_query_info_resp_len(struct smb2_query_info_req *h) +{ + return le32_to_cpu(h->OutputBufferLength); } static inline int smb2_set_info_req_len(struct smb2_set_info_req *h) @@ -308,6 +312,7 @@ static int smb2_validate_credit_charge(struct ksmbd_conn *conn, switch (hdr->Command) { case SMB2_QUERY_INFO: req_len = smb2_query_info_req_len(__hdr); + expect_resp_len = smb2_query_info_resp_len(__hdr); break; case SMB2_SET_INFO: req_len = smb2_set_info_req_len(__hdr); From 4a0b7826615a01c47924334a2e8a9dbd84a598b2 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Fri, 26 Jun 2026 10:52:22 +0900 Subject: [PATCH 0201/1054] ksmbd: fix outstanding credit leak on abort and error paths smb2_validate_credit_charge() adds the request's CreditCharge to conn->outstanding_credits when an SMB2 PDU is received, and smb2_set_rsp_credits() subtracts it again when the response is built. However smb2_set_rsp_credits() only runs on the normal response path: - __process_request() returning SERVER_HANDLER_ABORT (unimplemented command, command index out of range, signature check failure, or a handler that sets send_no_response such as a cancelled blocking lock) breaks out of the processing loop before set_rsp_credits() is called; - smb2_set_rsp_credits() itself returns early with -EINVAL (total credit overflow or insufficient credits) before the subtraction. On all of these paths the charge added at receive time is never returned, so conn->outstanding_credits only grows. Because a client can repeatedly trigger them (e.g. by sending unimplemented commands or by issuing and cancelling blocking locks), outstanding_credits eventually reaches total_credits and smb2_validate_credit_charge() then rejects every subsequent request, wedging the connection. Record the charge that was added in work->credit_charge and release any charge still pending at the single send. exit point of __handle_ksmbd_work(), which all abort and error paths fall through to. smb2_set_rsp_credits() clears work->credit_charge once it has returned the charge so the response path is unchanged and the credit is never released twice. Paths that never charged a credit (no multi-credit support, validation failure) leave work->credit_charge at zero and are unaffected. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/ksmbd_work.h | 7 +++++++ fs/smb/server/server.c | 14 ++++++++++++++ fs/smb/server/smb2misc.c | 9 ++++++--- fs/smb/server/smb2pdu.c | 1 + 4 files changed, 28 insertions(+), 3 deletions(-) diff --git a/fs/smb/server/ksmbd_work.h b/fs/smb/server/ksmbd_work.h index df0554a2c50d..88104f0cf363 100644 --- a/fs/smb/server/ksmbd_work.h +++ b/fs/smb/server/ksmbd_work.h @@ -67,6 +67,13 @@ struct ksmbd_work { /* Number of granted credits */ unsigned int credits_granted; + /* + * Credit charge added to conn->outstanding_credits at receive time + * for the SMB2 PDU currently being processed, pending release. Zero + * once the charge has been returned (on the response or error path). + */ + unsigned short credit_charge; + /* response smb header size */ unsigned int response_sz; diff --git a/fs/smb/server/server.c b/fs/smb/server/server.c index 36feda7e0942..36a5ea4828ad 100644 --- a/fs/smb/server/server.c +++ b/fs/smb/server/server.c @@ -242,6 +242,20 @@ static void __handle_ksmbd_work(struct ksmbd_work *work, } while (is_chained == true); send: + /* + * Release any credit charge still outstanding for this request. On + * the normal path smb2_set_rsp_credits() already returned it, but the + * abort, error and send-no-response paths skip that call, so the + * charge would otherwise leak and eventually exhaust the connection's + * outstanding credit window. + */ + if (work->credit_charge) { + spin_lock(&conn->credits_lock); + conn->outstanding_credits -= work->credit_charge; + work->credit_charge = 0; + spin_unlock(&conn->credits_lock); + } + if (work->tcon) ksmbd_tree_connect_put(work->tcon); smb3_preauth_hash_rsp(work); diff --git a/fs/smb/server/smb2misc.c b/fs/smb/server/smb2misc.c index 35fba49d27d6..c0c4edd092c2 100644 --- a/fs/smb/server/smb2misc.c +++ b/fs/smb/server/smb2misc.c @@ -301,9 +301,10 @@ static inline int smb2_ioctl_resp_len(struct smb2_ioctl_req *h) le32_to_cpu(h->MaxOutputResponse); } -static int smb2_validate_credit_charge(struct ksmbd_conn *conn, +static int smb2_validate_credit_charge(struct ksmbd_work *work, struct smb2_hdr *hdr) { + struct ksmbd_conn *conn = work->conn; unsigned int req_len = 0, expect_resp_len = 0, calc_credit_num, max_len; unsigned short credit_charge = le16_to_cpu(hdr->CreditCharge); void *__hdr = hdr; @@ -361,8 +362,10 @@ static int smb2_validate_credit_charge(struct ksmbd_conn *conn, ksmbd_debug(SMB, "Limits exceeding the maximum allowable outstanding requests, given : %u, pending : %u\n", credit_charge, conn->outstanding_credits); ret = 1; - } else + } else { conn->outstanding_credits += credit_charge; + work->credit_charge = credit_charge; + } spin_unlock(&conn->credits_lock); @@ -465,7 +468,7 @@ int ksmbd_smb2_check_message(struct ksmbd_work *work) validate_credit: if ((work->conn->vals->req_capabilities & SMB2_GLOBAL_CAP_LARGE_MTU) && - smb2_validate_credit_charge(work->conn, hdr)) + smb2_validate_credit_charge(work, hdr)) return 1; return 0; diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 73b3758f41ee..727ba86ede36 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -363,6 +363,7 @@ int smb2_set_rsp_credits(struct ksmbd_work *work) conn->total_credits -= credit_charge; conn->outstanding_credits -= credit_charge; + work->credit_charge = 0; credits_requested = max_t(unsigned short, le16_to_cpu(req_hdr->CreditRequest), 1); From 684a00c291fbde2d42b6692c75855df7ff8894ee Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Sat, 27 Jun 2026 00:31:53 +0900 Subject: [PATCH 0202/1054] ksmbd: annotate oplock list traversals under m_lock session_fd_check() and ksmbd_reopen_durable_fd() walk ci->m_op_list with list_for_each_entry_rcu() while holding ci->m_lock for write. That is the local inode/oplock serializer, but the RCU-list iterator does not currently tell lockdep about it. Pass lockdep_is_held(&ci->m_lock) to these iterators so CONFIG_PROVE_RCU_LIST can see the rwsem protection already in place. This was found by our static analysis tool and then manually reviewed against the current tree. The dynamic triage evidence is a target-matched CONFIG_PROVE_RCU_LIST warning; the change is limited to documenting the existing protection contract. This is a lockdep annotation cleanup. It does not change oplock list lifetime or durable-handle behavior. Signed-off-by: Runyu Xiao Acked-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/vfs_cache.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c index fde22742d193..525e9a3a2e9b 100644 --- a/fs/smb/server/vfs_cache.c +++ b/fs/smb/server/vfs_cache.c @@ -1530,7 +1530,8 @@ static bool session_fd_check(struct ksmbd_tree_connect *tcon, conn = fp->conn; ci = fp->f_ci; down_write(&ci->m_lock); - list_for_each_entry_rcu(op, &ci->m_op_list, op_entry) { + list_for_each_entry_rcu(op, &ci->m_op_list, op_entry, + lockdep_is_held(&ci->m_lock)) { if (op->conn != conn) continue; ksmbd_conn_put(op->conn); @@ -1685,7 +1686,8 @@ int ksmbd_reopen_durable_fd(struct ksmbd_work *work, struct ksmbd_file *fp) ci = fp->f_ci; down_write(&ci->m_lock); - list_for_each_entry_rcu(op, &ci->m_op_list, op_entry) { + list_for_each_entry_rcu(op, &ci->m_op_list, op_entry, + lockdep_is_held(&ci->m_lock)) { if (op->conn) continue; op->conn = ksmbd_conn_get(fp->conn); From 23aa1873ce0a39881127a0445eff1048c788b7a6 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sat, 27 Jun 2026 10:16:14 +0900 Subject: [PATCH 0203/1054] ksmbd: doc: update feature support status for durable handles and compression Update ksmbd.rst to reflect the current implementation status of SMB features. Durable handles (v1, v2) and SMB3.1.1 Compression are now fully supported in ksmbd, so update their status from "Planned for future" to "Supported". Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- Documentation/filesystems/smb/ksmbd.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/filesystems/smb/ksmbd.rst b/Documentation/filesystems/smb/ksmbd.rst index 67cb68ea6e68..672c5d3892ff 100644 --- a/Documentation/filesystems/smb/ksmbd.rst +++ b/Documentation/filesystems/smb/ksmbd.rst @@ -97,7 +97,7 @@ ACLs Partially Supported. only DACLs available, SACLs to allow future support for running as a domain member. Kerberos Supported. -Durable handle v1,v2 Planned for future. +Durable handle v1,v2 Supported. Persistent handle Planned for future. SMB2 notify Planned for future. Sparse file support Supported. @@ -111,7 +111,7 @@ DCE/RPC support Partially Supported. a few calls(NetShareEnumAll, for Witness protocol e.g.) ksmbd/nfsd interoperability Planned for future. The features that ksmbd support are Leases, Notify, ACLs and Share modes. -SMB3.1.1 Compression Planned for future. +SMB3.1.1 Compression Supported. SMB3.1.1 over QUIC Planned for future. Signing/Encryption over RDMA Planned for future. SMB3.1.1 GMAC signing support Planned for future. From 27a9bc968d4ae809a3f5ca2f2b136856ee613cb4 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sun, 28 Jun 2026 14:40:22 +0900 Subject: [PATCH 0204/1054] ksmbd: don't hold ci->m_lock while waiting for a lease break ack When a cifs.ko client caches a read-handle (RH) lease via deferred close and a conflicting open arrives, ksmbd breaks the lease and waits for the acknowledgment in wait_for_break_ack() for up to OPLOCK_WAIT_TIME (35s). __smb_break_all_levII_oplock() runs that wait while holding ci->m_lock for read. cifs.ko reacts to a handle-lease break by closing the deferred handle rather than sending a lease break acknowledgment. That close path (close_id_del_oplock() -> opinfo_del()) takes ci->m_lock for write and is exactly what would wake the waiter, but it blocks on the read lock held by the waiting thread. The break is then resolved only by the 35s timeout, so xfstests generic/001 takes ~78s with leases enabled versus ~4s with oplocks only. Collect the target opinfos (each pinned with a reference) while holding ci->m_lock, then break them after releasing it, matching how smb_grant_oplock() already breaks a conflicting lease using only a reference. The reference keeps the opinfo (and its conn and lease) alive across the unlocked window, and a close racing the break is handled by the existing OPLOCK_CLOSING state check. Apply the same fix to the parent lease break paths. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/oplock.c | 66 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 60 insertions(+), 6 deletions(-) diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c index 31dd9f3479b2..64cc8c5805e3 100644 --- a/fs/smb/server/oplock.c +++ b/fs/smb/server/oplock.c @@ -1180,6 +1180,36 @@ static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level, return err; } +struct oplock_break_entry { + struct list_head list; + struct oplock_info *opinfo; +}; + +static int oplock_break_add(struct list_head *head, struct oplock_info *opinfo) +{ + struct oplock_break_entry *ent; + + ent = kmalloc_obj(struct oplock_break_entry, KSMBD_DEFAULT_GFP); + if (!ent) + return -ENOMEM; + + ent->opinfo = opinfo; + list_add_tail(&ent->list, head); + return 0; +} + +static void oplock_break_drain_none(struct list_head *head) +{ + struct oplock_break_entry *ent, *tmp; + + list_for_each_entry_safe(ent, tmp, head, list) { + oplock_break(ent->opinfo, SMB2_OPLOCK_LEVEL_NONE, NULL, false); + list_del(&ent->list); + opinfo_put(ent->opinfo); + kfree(ent); + } +} + void destroy_lease_table(struct ksmbd_conn *conn) { struct lease_table *lb, *lbtmp; @@ -1289,6 +1319,7 @@ void smb_send_parent_lease_break_noti(struct ksmbd_file *fp, { struct oplock_info *opinfo; struct ksmbd_inode *p_ci = NULL; + LIST_HEAD(brk_list); if (lctx->version != 2) return; @@ -1314,12 +1345,14 @@ void smb_send_parent_lease_break_noti(struct ksmbd_file *fp, continue; } - oplock_break(opinfo, SMB2_OPLOCK_LEVEL_NONE, NULL, false); - opinfo_put(opinfo); + if (oplock_break_add(&brk_list, opinfo)) + opinfo_put(opinfo); } } up_read(&p_ci->m_lock); + oplock_break_drain_none(&brk_list); + ksmbd_inode_put(p_ci); } @@ -1327,6 +1360,7 @@ void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp) { struct oplock_info *opinfo; struct ksmbd_inode *p_ci = NULL; + LIST_HEAD(brk_list); rcu_read_lock(); opinfo = rcu_dereference(fp->f_opinfo); @@ -1355,12 +1389,14 @@ void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp) continue; } - oplock_break(opinfo, SMB2_OPLOCK_LEVEL_NONE, NULL, false); - opinfo_put(opinfo); + if (oplock_break_add(&brk_list, opinfo)) + opinfo_put(opinfo); } } up_read(&p_ci->m_lock); + oplock_break_drain_none(&brk_list); + ksmbd_inode_put(p_ci); } @@ -1602,9 +1638,11 @@ static void __smb_break_all_levII_oplock(struct ksmbd_work *work, bool send_interim, bool send_oplock_break) { struct oplock_info *op, *brk_op; + struct oplock_break_entry *ent, *tmp; struct ksmbd_inode *ci; struct ksmbd_conn *conn = work->conn; bool sent_interim = false; + LIST_HEAD(brk_list); if (!test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_OPLOCKS)) @@ -1646,6 +1684,22 @@ static void __smb_break_all_levII_oplock(struct ksmbd_work *work, SMB2_LEASE_KEY_SIZE)) goto next; brk_op->open_trunc = is_trunc; + + /* + * Defer the break until ci->m_lock is released: oplock_break() + * may block waiting for the lease break acknowledgment, and the + * close that wakes that wait needs ci->m_lock for write. + */ + if (!oplock_break_add(&brk_list, brk_op)) + continue; +next: + opinfo_put(brk_op); + } + up_read(&ci->m_lock); + + list_for_each_entry_safe(ent, tmp, &brk_list, list) { + brk_op = ent->opinfo; + if (!brk_op->is_lease && !send_oplock_break) { brk_op->level = SMB2_OPLOCK_LEVEL_NONE; brk_op->op_state = OPLOCK_STATE_NONE; @@ -1657,10 +1711,10 @@ static void __smb_break_all_levII_oplock(struct ksmbd_work *work, false); } sent_interim = true; -next: + list_del(&ent->list); opinfo_put(brk_op); + kfree(ent); } - up_read(&ci->m_lock); if (op) opinfo_put(op); From 851ed9e09639e0daf79a506ce26097b296ed5518 Mon Sep 17 00:00:00 2001 From: ChenXiaoSong Date: Sun, 28 Jun 2026 07:42:43 +0000 Subject: [PATCH 0205/1054] smb/server: do not require delete access for non-replacing links Reproducer: 1. server: systemctl start ksmbd 2. client: mount -t cifs //${server_ip}/export /mnt 3. client: touch /mnt/file; ln /mnt/file /mnt/hardlink 4. client err log: ln: failed to create hard link 'hardlink' => 'file': Permission denied 5. server err log: ksmbd: no right to delete : 0x80 Fixes: 13f3942f2bf4 ("ksmbd: add per-handle permission check to FILE_LINK_INFORMATION") Cc: stable@vger.kernel.org Reported-by: Steve French Signed-off-by: ChenXiaoSong Acked-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/smb2pdu.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 727ba86ede36..097f51fc7ed6 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -6921,16 +6921,18 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp, } case FILE_LINK_INFORMATION: { - if (!(fp->daccess & FILE_DELETE_LE)) { - pr_err("no right to delete : 0x%x\n", fp->daccess); - return -EACCES; - } + struct smb2_file_link_info *file_info; if (buf_len < sizeof(struct smb2_file_link_info)) return -EMSGSIZE; - return smb2_create_link(work, work->tcon->share_conf, - (struct smb2_file_link_info *)buffer, + file_info = (struct smb2_file_link_info *)buffer; + if (file_info->ReplaceIfExists && !(fp->daccess & FILE_DELETE_LE)) { + pr_err("no right to delete : 0x%x\n", fp->daccess); + return -EACCES; + } + + return smb2_create_link(work, work->tcon->share_conf, file_info, buf_len, fp->filp, work->conn->local_nls); } From 38637163501fd9e2f684b8cd275d0db5d79f37c6 Mon Sep 17 00:00:00 2001 From: Gil Portnoy Date: Fri, 26 Jun 2026 20:38:20 +0300 Subject: [PATCH 0206/1054] ksmbd: fix use-after-free of fp->owner.name in durable handle owner check Two concurrent SMB2 durable reconnects (DH2C/DHnC) on the same persistent_id race the fp->owner.name compare-read in ksmbd_vfs_compare_durable_owner() against the kfree() in ksmbd_reopen_durable_fd()'s reopen-success path. fp->owner.name is a standalone kstrdup() buffer whose lifetime is independent of the fp refcount, and the two sites share no lock: the compare reads the buffer while the reopen frees it, so the strcmp() can dereference freed memory. Commit 7ce4fc40018d ("ksmbd: fix durable reconnect double-bind race in ksmbd_reopen_durable_fd") made the fp->conn claim atomic under global_ft.lock (closing the owner.name double-free and the ksmbd_file write-UAF), but the compare-read versus reopen-free pair was left unserialized. BUG: KASAN: slab-use-after-free in strcmp+0x2c/0x80 Read of size 1 by task kworker strcmp ksmbd_vfs_compare_durable_owner smb2_check_durable_oplock smb2_open Freed by task kworker: kfree ksmbd_reopen_durable_fd smb2_open Allocated by task kworker: kstrdup session_fd_check smb2_session_logoff The buggy address belongs to the cache kmalloc-8 Serialize both sides of the race with fp->f_lock. The global durable file-table lock still protects the durable reconnect claim, but fp->owner.name is per-open state and does not need to block unrelated durable table lookups or reconnects. The teardown is left at its existing location after the reopen-success point so that an __open_id() rollback still retains owner.name for a later legitimate reconnect to verify. Fixes: 49110a8ce654 ("ksmbd: validate owner of durable handle on reconnect") Assisted-by: Henry (Claude):claude-opus-4 Signed-off-by: Gil Portnoy Co-developed-by: Namjae Jeon Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/vfs_cache.c | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c index 525e9a3a2e9b..3c9443ac3522 100644 --- a/fs/smb/server/vfs_cache.c +++ b/fs/smb/server/vfs_cache.c @@ -1461,16 +1461,21 @@ void ksmbd_stop_durable_scavenger(void) static int ksmbd_vfs_copy_durable_owner(struct ksmbd_file *fp, struct ksmbd_user *user) { + char *name; + if (!user) return -EINVAL; /* Duplicate the user name to ensure identity persistence */ - fp->owner.name = kstrdup(user->name, GFP_KERNEL); - if (!fp->owner.name) + name = kstrdup(user->name, GFP_KERNEL); + if (!name) return -ENOMEM; + spin_lock(&fp->f_lock); fp->owner.uid = user->uid; fp->owner.gid = user->gid; + fp->owner.name = name; + spin_unlock(&fp->f_lock); return 0; } @@ -1488,18 +1493,24 @@ static int ksmbd_vfs_copy_durable_owner(struct ksmbd_file *fp, bool ksmbd_vfs_compare_durable_owner(struct ksmbd_file *fp, struct ksmbd_user *user) { - if (!user || !fp->owner.name) + bool ret = false; + + if (!user) return false; + spin_lock(&fp->f_lock); + if (!fp->owner.name) + goto out; + /* Check if the UID and GID match first (fast path) */ if (fp->owner.uid != user->uid || fp->owner.gid != user->gid) - return false; + goto out; /* Validate the account name to ensure the same SecurityContext */ - if (strcmp(fp->owner.name, user->name)) - return false; - - return true; + ret = (strcmp(fp->owner.name, user->name) == 0); +out: + spin_unlock(&fp->f_lock); + return ret; } static bool session_fd_check(struct ksmbd_tree_connect *tcon, @@ -1694,9 +1705,11 @@ int ksmbd_reopen_durable_fd(struct ksmbd_work *work, struct ksmbd_file *fp) } up_write(&ci->m_lock); + spin_lock(&fp->f_lock); fp->owner.uid = fp->owner.gid = 0; kfree(fp->owner.name); fp->owner.name = NULL; + spin_unlock(&fp->f_lock); return 0; } From c706195e5e06402d8d1d20908978cdc82eae6185 Mon Sep 17 00:00:00 2001 From: Gil Portnoy Date: Fri, 26 Jun 2026 22:11:14 +0300 Subject: [PATCH 0207/1054] ksmbd: close superseded durable handles through refcount handoff ksmbd_close_disconnected_durable_delete_on_close() collects disconnected durable handles for a name being superseded by a new delete-on-close open, drops ci->m_lock, then closes each collected handle directly with __ksmbd_close_fd(). That bypasses the FP_CLOSED and refcount handoff used by the other close paths. If a durable reconnect or the durable scavenger already took a reference to the same fp, the direct __ksmbd_close_fd() can free the ksmbd_file while that other holder still owns a live reference. Claim the disconnected durable handle before unlinking it from m_fp_list. While holding ci->m_lock and global_ft.lock, only take ownership when the durable lifetime reference is the only remaining reference. Then take a transient reference, remove the fp from global_ft, mark it FP_CLOSED, and move it to the local dispose list. If another holder already has a reference, leave the fp linked and let that holder complete its path. The dispose loop then drops both references owned by the claim. This keeps the force-close path in the same refcount handoff model as the durable scavenger and avoids leaving a live reconnected fp detached from m_fp_list. Fixes: 166e4c07023b ("ksmbd: supersede disconnected delete-on-close durable handle") Signed-off-by: Gil Portnoy Co-developed-by: Namjae Jeon Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/vfs_cache.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c index 3c9443ac3522..73d28942dc0a 100644 --- a/fs/smb/server/vfs_cache.c +++ b/fs/smb/server/vfs_cache.c @@ -581,7 +581,20 @@ bool ksmbd_close_disconnected_durable_delete_on_close(struct dentry *dentry) if (fp->conn || !fp->is_durable || fp->f_state != FP_INITED) continue; - list_move_tail(&fp->node, &dispose); + + /* + * Claim the close before unlinking fp from m_fp_list. + * refcount == 1 means only the durable lifetime ref is + * left. Add a transient ref so final close can drop both. + */ + write_lock(&global_ft.lock); + if (atomic_read(&fp->refcount) == 1) { + atomic_inc(&fp->refcount); + __ksmbd_remove_durable_fd(fp); + ksmbd_mark_fp_closed(fp); + list_move_tail(&fp->node, &dispose); + } + write_unlock(&global_ft.lock); } } up_write(&ci->m_lock); @@ -589,16 +602,18 @@ bool ksmbd_close_disconnected_durable_delete_on_close(struct dentry *dentry) /* * Drop our lookup reference before closing so the last __ksmbd_close_fd() * can drop m_count to zero and unlink the delete-on-close file. The - * collected handles still hold references, so ci stays valid until they - * are closed below. + * collected handles still hold the transient reference taken above, so + * ci stays valid until they are closed below. */ ksmbd_inode_put(ci); while (!list_empty(&dispose)) { fp = list_first_entry(&dispose, struct ksmbd_file, node); list_del_init(&fp->node); - __ksmbd_close_fd(NULL, fp); - closed = true; + if (atomic_sub_and_test(2, &fp->refcount)) { + __ksmbd_close_fd(NULL, fp); + closed = true; + } } return closed; From 5138c84dbb501363510f6f9c300797b240a119cb Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sun, 28 Jun 2026 09:30:00 +0900 Subject: [PATCH 0208/1054] ksmbd: snapshot previous oplock state before durable checks smb_grant_oplock() checks the previous oplock holder's o_fp to decide whether a durable handle should be invalidated when the oplock break cannot be delivered. prev_opinfo is obtained with opinfo_get_list(), which pins only the oplock_info. It does not pin the ksmbd_file stored in opinfo->o_fp. A concurrent last close can unlink the opinfo from ci->m_op_list under ci->m_lock and then free the ksmbd_file. The oplock_info can still be kept alive by the refcount taken by opinfo_get_list(), but o_fp may already point at freed memory by the time smb_grant_oplock() reads is_durable, conn, or tcon. Snapshot the previous holder's durable state while ci->m_lock is held, then use only the copied values after dropping the lock. This keeps the o_fp lifetime tied to the inode lock without taking an extra ksmbd_file reference. Taking such a reference is unsafe here because smb_grant_oplock() does not necessarily have the previous holder's session work, and dropping the temporary reference can otherwise become the final putter. Fixes: 26fa88dc877c ("ksmbd: invalidate durable handles on oplock break") Reported-by: Gil Portnoy Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/oplock.c | 43 +++++++++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c index 64cc8c5805e3..3c55ae5d6a11 100644 --- a/fs/smb/server/oplock.c +++ b/fs/smb/server/oplock.c @@ -278,10 +278,24 @@ struct oplock_info *opinfo_get(struct ksmbd_file *fp) return opinfo; } -static struct oplock_info *opinfo_get_list(struct ksmbd_inode *ci) +struct oplock_snapshot { + bool durable_open; + bool durable_detached; + unsigned long long fid; +}; + +static struct oplock_info *opinfo_get_list(struct ksmbd_inode *ci, + struct ksmbd_file *skip_fp, + struct oplock_snapshot *snapshot) { struct oplock_info *opinfo; + if (snapshot) { + snapshot->durable_open = false; + snapshot->durable_detached = false; + snapshot->fid = KSMBD_NO_FID; + } + down_read(&ci->m_lock); opinfo = list_first_entry_or_null(&ci->m_op_list, struct oplock_info, op_entry); @@ -295,6 +309,16 @@ static struct oplock_info *opinfo_get_list(struct ksmbd_inode *ci) opinfo = NULL; } } + + if (opinfo && snapshot && opinfo->o_fp && + opinfo->o_fp != skip_fp && + READ_ONCE(opinfo->o_fp->is_durable)) { + snapshot->durable_open = true; + snapshot->durable_detached = + !READ_ONCE(opinfo->o_fp->conn) || + !READ_ONCE(opinfo->o_fp->tcon); + snapshot->fid = opinfo->fid; + } } up_read(&ci->m_lock); @@ -314,7 +338,7 @@ void opinfo_put(struct oplock_info *opinfo) static bool ksmbd_inode_has_lease(struct ksmbd_inode *ci) { - struct oplock_info *opinfo = opinfo_get_list(ci); + struct oplock_info *opinfo = opinfo_get_list(ci, NULL, NULL); bool is_lease; if (!opinfo) @@ -1421,6 +1445,7 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid, struct oplock_info *opinfo = NULL, *prev_opinfo = NULL; struct ksmbd_inode *ci = fp->f_ci; struct lease_table *new_lb = NULL; + struct oplock_snapshot prev_op_snapshot; bool prev_op_has_lease; bool prev_durable_open = false; bool prev_durable_detached = false; @@ -1488,7 +1513,7 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid, goto out; } } - prev_opinfo = opinfo_get_list(ci); + prev_opinfo = opinfo_get_list(ci, fp, &prev_op_snapshot); if (!prev_opinfo || (prev_opinfo->level == SMB2_OPLOCK_LEVEL_NONE && lctx)) { opinfo_put(prev_opinfo); @@ -1510,13 +1535,9 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid, goto op_break_not_needed; } - if (prev_opinfo->o_fp && prev_opinfo->o_fp != fp && - prev_opinfo->o_fp->is_durable) { - prev_durable_open = true; - prev_durable_detached = !prev_opinfo->o_fp->conn || - !prev_opinfo->o_fp->tcon; - prev_fid = prev_opinfo->fid; - } + prev_durable_open = prev_op_snapshot.durable_open; + prev_durable_detached = prev_op_snapshot.durable_detached; + prev_fid = prev_op_snapshot.fid; err = oplock_break(prev_opinfo, break_level, work, share_ret < 0 && prev_opinfo->is_lease); @@ -1607,7 +1628,7 @@ static bool smb_break_all_write_oplock(struct ksmbd_work *work, struct oplock_info *brk_opinfo; bool sent_break = false; - brk_opinfo = opinfo_get_list(fp->f_ci); + brk_opinfo = opinfo_get_list(fp->f_ci, NULL, NULL); if (!brk_opinfo) return false; if (brk_opinfo->level != SMB2_OPLOCK_LEVEL_BATCH && From f363a0fb134a3eb9e47368b1edbd251fd76be84b Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sun, 28 Jun 2026 08:56:09 +0900 Subject: [PATCH 0209/1054] ksmbd: fix app-instance durable supersede session UAF ksmbd_close_fd_app_instance_id() looks up a prior durable handle by AppInstanceId and closes it through opinfo->sess->file_table. This is unsafe after the original session has been torn down. session_fd_check() preserves reconnectable durable handles in the global table and clears opinfo->conn/fp->conn, but opinfo->sess can still point to the freed ksmbd_session. Use opinfo->conn as the orphan sentinel, but make the check reliable by serializing it with session_fd_check(). That path clears opinfo->conn under fp->f_ci->m_lock, so hold the same lock while testing opinfo->conn and while dereferencing opinfo->sess->file_table. Also avoid closing through the session file table if the volatile id has already been unpublished by session teardown. Durable reconnect must keep the two fields consistent. Rebinding only opinfo->conn leaves opinfo->sess pointing at the old freed session, so a later app-instance supersede can pass the conn check and write-lock the freed session's file table. Clear opinfo->sess when preserving a durable handle during session teardown, and set it to the reconnecting session when opinfo->conn is rebound in ksmbd_reopen_durable_fd(). Fixes: 16c30649709d ("ksmbd: handle durable v2 app instance id") Reported-by: Gil Portnoy Co-developed-by: Gil Portnoy Signed-off-by: Gil Portnoy Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/vfs_cache.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c index 73d28942dc0a..d95c405eab11 100644 --- a/fs/smb/server/vfs_cache.c +++ b/fs/smb/server/vfs_cache.c @@ -853,19 +853,24 @@ int ksmbd_close_fd_app_instance_id(char *app_instance_id) return 0; opinfo = opinfo_get(fp); - if (!opinfo || !opinfo->sess) + if (!opinfo) goto out; + down_read(&fp->f_ci->m_lock); + if (!opinfo->conn) { + up_read(&fp->f_ci->m_lock); + goto out; + } + ft = &opinfo->sess->file_table; write_lock(&ft->lock); - if (fp->f_state == FP_INITED) { - if (has_file_id(fp->volatile_id)) { - idr_remove(ft->idr, fp->volatile_id); - fp->volatile_id = KSMBD_NO_FID; - } + if (fp->f_state == FP_INITED && has_file_id(fp->volatile_id)) { + idr_remove(ft->idr, fp->volatile_id); + fp->volatile_id = KSMBD_NO_FID; n_to_drop = ksmbd_mark_fp_closed(fp); } write_unlock(&ft->lock); + up_read(&fp->f_ci->m_lock); opinfo_put(opinfo); opinfo = NULL; @@ -1562,6 +1567,7 @@ static bool session_fd_check(struct ksmbd_tree_connect *tcon, continue; ksmbd_conn_put(op->conn); op->conn = NULL; + op->sess = NULL; } up_write(&ci->m_lock); @@ -1717,6 +1723,7 @@ int ksmbd_reopen_durable_fd(struct ksmbd_work *work, struct ksmbd_file *fp) if (op->conn) continue; op->conn = ksmbd_conn_get(fp->conn); + op->sess = work->sess; } up_write(&ci->m_lock); From f8a9262c7a6fc2de9802e14b0228114f0333869e Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Thu, 25 Jun 2026 16:10:40 +0300 Subject: [PATCH 0210/1054] drm/i915/vrr: require valid min/max vfreq for VRR Ensure the EDID provided min/max vfreq are valid. Most scenarios are already covered (by coincidence) through the checks in intel_vrr_is_capable() and intel_vrr_is_in_range(), but be more explicit about it. At worst, a zero min_vfreq could lead to a division by zero in intel_vrr_compute_vmax(). Discovered using AI-assisted static analysis confirmed by Intel Product Security. Reported-by: Martin Hodo Fixes: 117cd09ba528 ("drm/i915/display/dp: Compute VRR state in atomic_check") Cc: stable@vger.kernel.org # v5.12+ Cc: Ankit Nautiyal Reviewed-by: Ankit Nautiyal Link: https://patch.msgid.link/20260625131040.1051272-1-jani.nikula@intel.com Signed-off-by: Jani Nikula (cherry picked from commit 1765cf59f517b02f3b0591fe5120930d08bddeb6) Signed-off-by: Joonas Lahtinen --- drivers/gpu/drm/i915/display/intel_vrr.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_vrr.c b/drivers/gpu/drm/i915/display/intel_vrr.c index e03b5daac5be..aa587be908f1 100644 --- a/drivers/gpu/drm/i915/display/intel_vrr.c +++ b/drivers/gpu/drm/i915/display/intel_vrr.c @@ -74,6 +74,10 @@ bool intel_vrr_is_capable(struct intel_connector *connector) return false; } + if (!info->monitor_range.min_vfreq || !info->monitor_range.max_vfreq || + info->monitor_range.min_vfreq > info->monitor_range.max_vfreq) + return false; + return info->monitor_range.max_vfreq - info->monitor_range.min_vfreq > 10; } From 2084503f2d087bf956198e7f6eb25b03a7049cb2 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Fri, 26 Jun 2026 17:01:55 +0300 Subject: [PATCH 0211/1054] drm/i915/bios: range check LFP Data Block panel_type2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While the panel_type from LFP Data Block is range checked, panel_type2 is not. Add a few helpers for range checking, and use them to not only check panel_type2, but also improve clarity and correctness in the panel type selection. Discovered using AI-assisted static analysis confirmed by Intel Product Security. v2: - Fix commit message typo (MichaÅ‚) - Add is_panel_type_pnp() (Ville) Reported-by: Martin Hodo Fixes: 6434cf630086 ("drm/i915/bios: calculate panel type as per child device index in VBT") Cc: stable@vger.kernel.org # v6.0+ Cc: Animesh Manna Cc: Ville Syrjälä Reviewed-by: MichaÅ‚ Grzelak # v1 Reviewed-by: Ville Syrjälä Link: https://patch.msgid.link/20260626140155.1389655-1-jani.nikula@intel.com Signed-off-by: Jani Nikula (cherry picked from commit c9ebe5d2f25729d6cfbbb1235d640bf67f9275df) Signed-off-by: Joonas Lahtinen --- drivers/gpu/drm/i915/display/intel_bios.c | 36 ++++++++++++++++++----- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_bios.c b/drivers/gpu/drm/i915/display/intel_bios.c index b6fe87c29aa7..ded2ee497bbf 100644 --- a/drivers/gpu/drm/i915/display/intel_bios.c +++ b/drivers/gpu/drm/i915/display/intel_bios.c @@ -623,6 +623,21 @@ get_lfp_data_tail(const struct bdb_lfp_data *data, return NULL; } +static bool is_panel_type_valid(int panel_type) +{ + return panel_type >= 0 && panel_type < 16; +} + +static bool is_panel_type_pnp(int panel_type) +{ + return panel_type == 0xff; +} + +static bool is_panel_type_valid_or_pnp(int panel_type) +{ + return is_panel_type_valid(panel_type) || is_panel_type_pnp(panel_type); +} + static int opregion_get_panel_type(struct intel_display *display, const struct intel_bios_encoder_data *devdata, const struct drm_edid *drm_edid, bool use_fallback) @@ -640,15 +655,21 @@ static int vbt_get_panel_type(struct intel_display *display, if (!lfp_options) return -1; - if (lfp_options->panel_type > 0xf && - lfp_options->panel_type != 0xff) { + if (!is_panel_type_valid_or_pnp(lfp_options->panel_type)) { drm_dbg_kms(display->drm, "Invalid VBT panel type 0x%x\n", lfp_options->panel_type); return -1; } - if (devdata && devdata->child.handle == DEVICE_HANDLE_LFP2) + if (devdata && devdata->child.handle == DEVICE_HANDLE_LFP2) { + if (!is_panel_type_valid_or_pnp(lfp_options->panel_type2)) { + drm_dbg_kms(display->drm, "Invalid VBT panel type 2 0x%x\n", + lfp_options->panel_type2); + return -1; + } + return lfp_options->panel_type2; + } drm_WARN_ON(display->drm, devdata && devdata->child.handle != DEVICE_HANDLE_LFP1); @@ -762,13 +783,12 @@ static int get_panel_type(struct intel_display *display, panel_types[i].name, panel_types[i].panel_type); } - if (panel_types[PANEL_TYPE_OPREGION].panel_type >= 0) + if (is_panel_type_valid(panel_types[PANEL_TYPE_OPREGION].panel_type)) i = PANEL_TYPE_OPREGION; - else if (panel_types[PANEL_TYPE_VBT].panel_type == 0xff && - panel_types[PANEL_TYPE_PNPID].panel_type >= 0) + else if (is_panel_type_pnp(panel_types[PANEL_TYPE_VBT].panel_type) && + is_panel_type_valid(panel_types[PANEL_TYPE_PNPID].panel_type)) i = PANEL_TYPE_PNPID; - else if (panel_types[PANEL_TYPE_VBT].panel_type != 0xff && - panel_types[PANEL_TYPE_VBT].panel_type >= 0) + else if (is_panel_type_valid(panel_types[PANEL_TYPE_VBT].panel_type)) i = PANEL_TYPE_VBT; else i = PANEL_TYPE_FALLBACK; From 8d7e62d5e9b2d2ff146f472a9215d7e29c7e2307 Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy Date: Tue, 30 Jun 2026 17:51:48 +0300 Subject: [PATCH 0212/1054] gpio: timberdale: Return -ENOMEM on dynamic memory allocation in probe Out of memory situation on driver's probe is expected to be reported to the driver's framework with a proper -ENOMEM error code. Fixes: 35570ac6039e ("gpio: add GPIO driver for the Timberdale FPGA") Signed-off-by: Vladimir Zapolskiy Link: https://patch.msgid.link/20260630145148.4081967-1-vz@kernel.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-timberdale.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-timberdale.c b/drivers/gpio/gpio-timberdale.c index 78fe133f5d32..ec378a4220a7 100644 --- a/drivers/gpio/gpio-timberdale.c +++ b/drivers/gpio/gpio-timberdale.c @@ -228,7 +228,7 @@ static int timbgpio_probe(struct platform_device *pdev) tgpio = devm_kzalloc(dev, sizeof(*tgpio), GFP_KERNEL); if (!tgpio) - return -EINVAL; + return -ENOMEM; gc = &tgpio->gpio; From 9777530157e7b82fd994327ff878c4245dadc931 Mon Sep 17 00:00:00 2001 From: Viacheslav Bocharov Date: Thu, 25 Jun 2026 14:57:18 +0300 Subject: [PATCH 0213/1054] pinctrl: meson: restore non-sleeping GPIO access Commit 28f240683871 ("pinctrl: meson: mark the GPIO controller as sleeping") set gpio_chip.can_sleep = true to work around gpio-shared-proxy holding a spinlock across a sleeping pinctrl config path. That locking bug is now fixed in the shared-proxy itself ("gpio: shared-proxy: always serialize with a sleeping mutex"), so the controller-wide workaround is no longer needed; the meson GPIO controller does not sleep. meson_gpio_get/set/direction_* access MMIO through regmap. The regmap_mmio bus uses fast I/O (spinlock) locking, so these value callbacks do not contain sleeping operations. Since gpio_chip.can_sleep describes the get/set value path, restore can_sleep = false. Marking the controller sleeping also broke atomic value consumers such as w1-gpio (1-Wire bitbang): w1_io.c runs its read time slot under local_irq_save() and uses the non-cansleep gpiod_set_value() / gpiod_get_value(), which with can_sleep=true trigger WARN_ON(can_sleep) in gpiolib on every transferred bit (from w1_gpio_write_bit() / w1_gpio_read_bit() via w1_reset_bus() and w1_search()). The printk and stack dump inside the IRQs-off, microsecond-scale time slot destroy the bit timing, so reset/presence detection and ROM search fail: the bus master registers but w1_master_slave_count stays at 0 and no devices are found. Verified on an Amlogic A113X board (DS18B20 on GPIOA_14): with can_sleep restored to false the warnings are gone and the sensor is detected and read again. This must not be applied or backported without the shared-proxy locking fix above; otherwise the original Khadas VIM3 splat returns on boards that genuinely share a meson GPIO. Fixes: 28f240683871 ("pinctrl: meson: mark the GPIO controller as sleeping") Link: https://lore.kernel.org/all/20260105150509.56537-1-bartosz.golaszewski@oss.qualcomm.com/ Signed-off-by: Viacheslav Bocharov Acked-by: Linus Walleij Link: https://patch.msgid.link/20260625115718.1678991-3-v@baodeep.com Signed-off-by: Bartosz Golaszewski --- drivers/pinctrl/meson/pinctrl-meson.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pinctrl/meson/pinctrl-meson.c b/drivers/pinctrl/meson/pinctrl-meson.c index 4507dc8b5563..18295b15ecd9 100644 --- a/drivers/pinctrl/meson/pinctrl-meson.c +++ b/drivers/pinctrl/meson/pinctrl-meson.c @@ -619,7 +619,7 @@ static int meson_gpiolib_register(struct meson_pinctrl *pc) pc->chip.set = meson_gpio_set; pc->chip.base = -1; pc->chip.ngpio = pc->data->num_pins; - pc->chip.can_sleep = true; + pc->chip.can_sleep = false; ret = gpiochip_add_data(&pc->chip, pc); if (ret) { From d33846c8dcc06b83b7acdeac1e8bfbb5c0c26cb2 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Tue, 16 Jun 2026 21:41:49 -0400 Subject: [PATCH 0214/1054] xen/pvcalls: bound backend response req_id before indexing rsp[] pvcalls_front_event_handler() takes req_id directly from the backend-supplied ring response and uses it to index the fixed-size bedata->rsp[] array for a memcpy() and a store, with no range check. A malicious or buggy backend can set req_id past PVCALLS_NR_RSP_PER_RING and drive an out-of-bounds write past the bedata allocation. req_id was also declared int while the wire field rsp->req_id is u32, so a range check on the signed value alone is insufficient: a backend req_id of 0xffffffff becomes -1, passes a >= PVCALLS_NR_RSP_PER_RING test and indexes bedata->rsp[-1]. Declare req_id as u32 so a single bound covers both ends. A backend that sends an out-of-range req_id has violated the wire protocol, so rather than silently dropping the response, log once and stop trusting the backend: set bedata->disabled. The event handler then ignores further responses, and the request paths that wait for a response return -EIO instead of blocking forever. This mirrors the fatal-error handling xen-netback uses (xenvif_fatal_tx_err()). The pvcalls frontend currently trusts its backend, so this is not a classic-Xen security issue, but it matters for hardening PV frontends against malicious backends (confidential and disaggregated deployments). Fixes: 2195046bfd69 ("xen/pvcalls: implement socket command and handle events") Suggested-by: Juergen Gross Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Michael Bommarito Reviewed-by: Juergen Gross Signed-off-by: Juergen Gross Message-ID: <20260617014149.2647404-1-michael.bommarito@gmail.com> --- drivers/xen/pvcalls-front.c | 88 ++++++++++++++++++++++++++++++++----- 1 file changed, 76 insertions(+), 12 deletions(-) diff --git a/drivers/xen/pvcalls-front.c b/drivers/xen/pvcalls-front.c index 50ce4820f7ee..3e7aa807c317 100644 --- a/drivers/xen/pvcalls-front.c +++ b/drivers/xen/pvcalls-front.c @@ -32,6 +32,7 @@ struct pvcalls_bedata { struct xen_pvcalls_front_ring ring; grant_ref_t ref; int irq; + bool disabled; struct list_head socket_mappings; spinlock_t socket_lock; @@ -131,6 +132,20 @@ static inline int get_request(struct pvcalls_bedata *bedata, int *req_id) return 0; } +/* + * Wait for the backend's response to req_id, or for the frontend to be + * disabled because the backend violated the wire protocol. Returns 0 once + * the response has arrived, or -EIO if the frontend was disabled. + */ +static int pvcalls_front_wait_rsp(struct pvcalls_bedata *bedata, u32 req_id) +{ + wait_event(bedata->inflight_req, + READ_ONCE(bedata->rsp[req_id].req_id) == req_id || + READ_ONCE(bedata->disabled)); + + return READ_ONCE(bedata->disabled) ? -EIO : 0; +} + static bool pvcalls_front_write_todo(struct sock_mapping *map) { struct pvcalls_data_intf *intf = map->active.ring; @@ -168,7 +183,8 @@ static irqreturn_t pvcalls_front_event_handler(int irq, void *dev_id) struct pvcalls_bedata *bedata; struct xen_pvcalls_response *rsp; uint8_t *src, *dst; - int req_id = 0, more = 0, done = 0; + u32 req_id = 0; + int more = 0, done = 0; if (dev == NULL) return IRQ_HANDLED; @@ -179,12 +195,31 @@ static irqreturn_t pvcalls_front_event_handler(int irq, void *dev_id) pvcalls_exit(); return IRQ_HANDLED; } + if (READ_ONCE(bedata->disabled)) { + pvcalls_exit(); + return IRQ_HANDLED; + } again: while (RING_HAS_UNCONSUMED_RESPONSES(&bedata->ring)) { rsp = RING_GET_RESPONSE(&bedata->ring, bedata->ring.rsp_cons); req_id = rsp->req_id; + if (req_id >= PVCALLS_NR_RSP_PER_RING) { + /* + * The backend supplied a req_id that would index + * bedata->rsp[] out of bounds: a protocol violation + * from a malicious or buggy backend. Log once, stop + * trusting this backend and disable the frontend rather + * than silently dropping the response and continuing. + */ + pr_err_once("pvcalls: backend sent out-of-range req_id %u, disabling frontend\n", + req_id); + WRITE_ONCE(bedata->disabled, true); + bedata->ring.rsp_cons++; + done = 1; + break; + } if (rsp->cmd == PVCALLS_POLL) { struct sock_mapping *map = (struct sock_mapping *)(uintptr_t) rsp->u.poll.id; @@ -217,7 +252,7 @@ static irqreturn_t pvcalls_front_event_handler(int irq, void *dev_id) } RING_FINAL_CHECK_FOR_RESPONSES(&bedata->ring, more); - if (more) + if (more && !READ_ONCE(bedata->disabled)) goto again; if (done) wake_up(&bedata->inflight_req); @@ -330,8 +365,11 @@ int pvcalls_front_socket(struct socket *sock) if (notify) notify_remote_via_irq(bedata->irq); - wait_event(bedata->inflight_req, - READ_ONCE(bedata->rsp[req_id].req_id) == req_id); + ret = pvcalls_front_wait_rsp(bedata, req_id); + if (ret) { + pvcalls_exit(); + return ret; + } /* read req_id, then the content */ smp_rmb(); @@ -477,8 +515,11 @@ int pvcalls_front_connect(struct socket *sock, struct sockaddr *addr, if (notify) notify_remote_via_irq(bedata->irq); - wait_event(bedata->inflight_req, - READ_ONCE(bedata->rsp[req_id].req_id) == req_id); + ret = pvcalls_front_wait_rsp(bedata, req_id); + if (ret) { + pvcalls_exit_sock(sock); + return ret; + } /* read req_id, then the content */ smp_rmb(); @@ -711,8 +752,11 @@ int pvcalls_front_bind(struct socket *sock, struct sockaddr *addr, int addr_len) if (notify) notify_remote_via_irq(bedata->irq); - wait_event(bedata->inflight_req, - READ_ONCE(bedata->rsp[req_id].req_id) == req_id); + ret = pvcalls_front_wait_rsp(bedata, req_id); + if (ret) { + pvcalls_exit_sock(sock); + return ret; + } /* read req_id, then the content */ smp_rmb(); @@ -761,8 +805,11 @@ int pvcalls_front_listen(struct socket *sock, int backlog) if (notify) notify_remote_via_irq(bedata->irq); - wait_event(bedata->inflight_req, - READ_ONCE(bedata->rsp[req_id].req_id) == req_id); + ret = pvcalls_front_wait_rsp(bedata, req_id); + if (ret) { + pvcalls_exit_sock(sock); + return ret; + } /* read req_id, then the content */ smp_rmb(); @@ -820,6 +867,14 @@ int pvcalls_front_accept(struct socket *sock, struct socket *newsock, } } + if (READ_ONCE(bedata->disabled)) { + clear_bit(PVCALLS_FLAG_ACCEPT_INFLIGHT, + (void *)&map->passive.flags); + wake_up(&map->passive.inflight_accept_req); + pvcalls_exit_sock(sock); + return -EIO; + } + map2 = kzalloc_obj(*map2); if (map2 == NULL) { clear_bit(PVCALLS_FLAG_ACCEPT_INFLIGHT, @@ -880,10 +935,18 @@ int pvcalls_front_accept(struct socket *sock, struct socket *newsock, } if (wait_event_interruptible(bedata->inflight_req, - READ_ONCE(bedata->rsp[req_id].req_id) == req_id)) { + READ_ONCE(bedata->rsp[req_id].req_id) == req_id || + READ_ONCE(bedata->disabled))) { pvcalls_exit_sock(sock); return -EINTR; } + if (READ_ONCE(bedata->disabled)) { + clear_bit(PVCALLS_FLAG_ACCEPT_INFLIGHT, + (void *)&map->passive.flags); + wake_up(&map->passive.inflight_accept_req); + pvcalls_exit_sock(sock); + return -EIO; + } /* read req_id, then the content */ smp_rmb(); @@ -1054,7 +1117,8 @@ int pvcalls_front_release(struct socket *sock) notify_remote_via_irq(bedata->irq); wait_event(bedata->inflight_req, - READ_ONCE(bedata->rsp[req_id].req_id) == req_id); + READ_ONCE(bedata->rsp[req_id].req_id) == req_id || + READ_ONCE(bedata->disabled)); if (map->active_socket) { /* From 26d060ba39354eec0345fb73b5f8309edd6ec82c Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 19 Jun 2026 13:45:47 +0200 Subject: [PATCH 0215/1054] xen: Replace __ASSEMBLY__ with __ASSEMBLER__ in header files While the GCC and Clang compilers already define __ASSEMBLER__ automatically when compiling assembly code, __ASSEMBLY__ is a macro that only gets defined by the Makefiles in the kernel. This can be very confusing when switching between userspace and kernelspace coding, or when dealing with uapi headers that rather should use __ASSEMBLER__ instead. So let's standardize now on the __ASSEMBLER__ macro that is provided by the compilers. This is a completely mechanical patch (done with a simple "sed -i" statement). Signed-off-by: Thomas Huth Reviewed-by: Juergen Gross Signed-off-by: Juergen Gross Message-ID: <20260619114547.159637-1-thuth@redhat.com> --- include/xen/interface/xen-mca.h | 4 ++-- include/xen/interface/xen.h | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/xen/interface/xen-mca.h b/include/xen/interface/xen-mca.h index 1c9afbe8cc26..8f5815f1d3ab 100644 --- a/include/xen/interface/xen-mca.h +++ b/include/xen/interface/xen-mca.h @@ -50,7 +50,7 @@ /* OUT: There was no machine check data to fetch. */ #define XEN_MC_NODATA 0x2 -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ /* vIRQ injected to Dom0 */ #define VIRQ_MCA VIRQ_ARCH_0 @@ -388,5 +388,5 @@ struct xen_mce_log { #define MCE_GET_LOG_LEN _IOR('M', 2, int) #define MCE_GETCLEAR_FLAGS _IOR('M', 3, int) -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* __XEN_PUBLIC_ARCH_X86_MCA_H__ */ diff --git a/include/xen/interface/xen.h b/include/xen/interface/xen.h index 0ca23eca2a9c..40c9793e9880 100644 --- a/include/xen/interface/xen.h +++ b/include/xen/interface/xen.h @@ -337,7 +337,7 @@ #define MMUEXT_MARK_SUPER 19 #define MMUEXT_UNMARK_SUPER 20 -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ struct mmuext_op { unsigned int cmd; union { @@ -415,7 +415,7 @@ DEFINE_GUEST_HANDLE_STRUCT(mmuext_op); #define MAX_VMASST_TYPE 5 -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ typedef uint16_t domid_t; @@ -760,11 +760,11 @@ struct tmem_op { DEFINE_GUEST_HANDLE(u64); -#else /* __ASSEMBLY__ */ +#else /* __ASSEMBLER__ */ /* In assembly code we cannot use C numeric constant suffixes. */ #define mk_unsigned_long(x) x -#endif /* !__ASSEMBLY__ */ +#endif /* !__ASSEMBLER__ */ #endif /* __XEN_PUBLIC_XEN_H__ */ From 45ca1afe2fd14c04e37227e79d3f8455831d8408 Mon Sep 17 00:00:00 2001 From: Wentao Liang Date: Mon, 22 Jun 2026 19:25:41 +0800 Subject: [PATCH 0216/1054] xen/gntdev: fix error handling in ioctl When gntdev_ioctl_map_grant_ref() fails to copy the operation result back to userspace after successfully adding the mapping to the list, the error path returns -EFAULT without releasing the reference acquired by gntdev_alloc_map(). The mapping remains in priv->maps with a refcount of 1, causing a memory leak and a dangling list entry. Additionally, gntdev_add_map() may modify map->index to avoid overlap with existing mappings. Therefore, the index returned to userspace must be obtained after gntdev_add_map() completes. Fix this by holding the mutex across gntdev_add_map(), retrieving the correct index, and copy_to_user(). If copy_to_user() fails, remove the mapping from the list and release the reference while still holding the lock. Cc: stable@vger.kernel.org Fix these issues by properly handling all error cases. Fixes: 1401c00e59ea ("xen/gntdev: convert priv->lock to a mutex") Fixes: 68b025c813c2 ("xen-gntdev: Add reference counting to maps") Signed-off-by: Wentao Liang Reviewed-by: Juergen Gross Signed-off-by: Juergen Gross Message-ID: <20260622112541.38194-1-vulab@iscas.ac.cn> --- drivers/xen/gntdev.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/xen/gntdev.c b/drivers/xen/gntdev.c index 61ea855c4508..1dcc4675580e 100644 --- a/drivers/xen/gntdev.c +++ b/drivers/xen/gntdev.c @@ -670,11 +670,15 @@ static long gntdev_ioctl_map_grant_ref(struct gntdev_priv *priv, mutex_lock(&priv->lock); gntdev_add_map(priv, map); op.index = map->index << PAGE_SHIFT; - mutex_unlock(&priv->lock); - if (copy_to_user(u, &op, sizeof(op)) != 0) + if (copy_to_user(u, &op, sizeof(op)) != 0) { + list_del(&map->next); + mutex_unlock(&priv->lock); + gntdev_put_map(priv, map); return -EFAULT; + } + mutex_unlock(&priv->lock); return 0; } From 678d59219ce0ae883f04c96936222c6168ef1164 Mon Sep 17 00:00:00 2001 From: Yousef Alhouseen Date: Mon, 29 Jun 2026 18:05:17 +0200 Subject: [PATCH 0217/1054] xen/front-pgdir-shbuf: free grant reference head on errors grant_references() allocates a private grant-reference head before claiming references for the page directory and, for guest-owned buffers, the data pages. The success path frees the remaining head, but claim failures and grant_refs_for_buffer() errors return immediately. Unwind through a common exit path so the private grant-reference head is released even when granting fails part-way through setup. The caller still tears down any references already stored in buf->grefs. Signed-off-by: Yousef Alhouseen Reviewed-by: Stefano Stabellini Signed-off-by: Juergen Gross Message-ID: <20260629160517.29340-1-alhouseenyousef@gmail.com> --- drivers/xen/xen-front-pgdir-shbuf.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/xen/xen-front-pgdir-shbuf.c b/drivers/xen/xen-front-pgdir-shbuf.c index 9c7d8af6e6a1..428187edf85d 100644 --- a/drivers/xen/xen-front-pgdir-shbuf.c +++ b/drivers/xen/xen-front-pgdir-shbuf.c @@ -445,8 +445,10 @@ static int grant_references(struct xen_front_pgdir_shbuf *buf) unsigned long frame; cur_ref = gnttab_claim_grant_reference(&priv_gref_head); - if (cur_ref < 0) - return cur_ref; + if (cur_ref < 0) { + ret = cur_ref; + goto out_free_refs; + } frame = xen_page_to_gfn(virt_to_page(buf->directory + PAGE_SIZE * i)); @@ -457,11 +459,13 @@ static int grant_references(struct xen_front_pgdir_shbuf *buf) if (buf->ops->grant_refs_for_buffer) { ret = buf->ops->grant_refs_for_buffer(buf, &priv_gref_head, j); if (ret) - return ret; + goto out_free_refs; } + ret = 0; +out_free_refs: gnttab_free_grant_references(priv_gref_head); - return 0; + return ret; } /* From 2d044049421dd48212b28646a850749d4a2d57fa Mon Sep 17 00:00:00 2001 From: Yiyang Chen Date: Tue, 23 Jun 2026 06:23:13 +0000 Subject: [PATCH 0218/1054] HID: bpf: Fix hid_bpf_get_data() range check hid_bpf_get_data() returns a pointer into the HID-BPF context data when the caller-provided offset and size fit inside ctx->allocated_size. The current check adds rdwr_buf_size and offset before comparing the result against ctx->allocated_size. Since both values are unsigned, a very large size can wrap the sum below ctx->allocated_size and make the helper return a pointer even though the requested range is not contained in the backing buffer. Use check_add_overflow() to reject wrapped range ends before comparing the requested range end against ctx->allocated_size. Fixes: 658ee5a64fcf ("HID: bpf: allocate data memory for device_event BPF programs") Signed-off-by: Yiyang Chen Signed-off-by: Benjamin Tissoires --- drivers/hid/bpf/hid_bpf_dispatch.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/hid/bpf/hid_bpf_dispatch.c b/drivers/hid/bpf/hid_bpf_dispatch.c index d0130658091b..536f6d01fd14 100644 --- a/drivers/hid/bpf/hid_bpf_dispatch.c +++ b/drivers/hid/bpf/hid_bpf_dispatch.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "hid_bpf_dispatch.h" const struct hid_ops *hid_ops; @@ -296,10 +297,12 @@ __bpf_kfunc __u8 * hid_bpf_get_data(struct hid_bpf_ctx *ctx, unsigned int offset, const size_t rdwr_buf_size) { struct hid_bpf_ctx_kern *ctx_kern; + size_t end; ctx_kern = container_of(ctx, struct hid_bpf_ctx_kern, ctx); - if (rdwr_buf_size + offset > ctx->allocated_size) + if (check_add_overflow(rdwr_buf_size, offset, &end) || + end > ctx->allocated_size) return NULL; return ctx_kern->data + offset; From 5aad55011a37d999cf99d14bafb6a093a1a70466 Mon Sep 17 00:00:00 2001 From: Yiyang Chen Date: Tue, 23 Jun 2026 06:23:14 +0000 Subject: [PATCH 0219/1054] selftests/hid: Load only requested struct_ops maps The HID selftest skeleton contains several struct_ops maps, but each test usually wants to load only the programs named by that test. load_programs() disabled auto-attach for all maps, but left struct_ops autocreate enabled. libbpf can enable autoload for programs referenced by autocreated struct_ops maps, so an unrelated program can be loaded and fail even when the current test does not use it. Disable autocreate for all struct_ops maps by default, then re-enable it only for the maps selected by the test before loading the skeleton. Signed-off-by: Yiyang Chen Fixes: f64c1a459339 ("selftests/hid: disable struct_ops auto-attach") Signed-off-by: Benjamin Tissoires --- tools/testing/selftests/hid/hid_bpf.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/tools/testing/selftests/hid/hid_bpf.c b/tools/testing/selftests/hid/hid_bpf.c index 1e979fb3542b..269256e1decd 100644 --- a/tools/testing/selftests/hid/hid_bpf.c +++ b/tools/testing/selftests/hid/hid_bpf.c @@ -86,6 +86,20 @@ static void load_programs(const struct test_program programs[], self->skel = hid__open(); ASSERT_OK_PTR(self->skel) TEARDOWN_LOG("Error while calling hid__open"); + /* + * Disable all struct_ops maps by default so libbpf does not autoload + * programs referenced by maps that are unrelated to the current test. + */ + bpf_object__for_each_map(iter_map, *self->skel->skeleton->obj) { + if (bpf_map__type(iter_map) == BPF_MAP_TYPE_STRUCT_OPS) { + err = bpf_map__set_autocreate(iter_map, false); + ASSERT_OK(err) TH_LOG("can not disable struct_ops map '%s'", + bpf_map__name(iter_map)); + } + + bpf_map__set_autoattach(iter_map, false); + } + for (int i = 0; i < progs_count; i++) { struct bpf_program *prog; struct bpf_map *map; @@ -102,6 +116,10 @@ static void load_programs(const struct test_program programs[], ASSERT_OK_PTR(map) TH_LOG("can not find struct_ops by name '%s'", programs[i].name + 4); + err = bpf_map__set_autocreate(map, true); + ASSERT_OK(err) TH_LOG("can not enable struct_ops map '%s'", + programs[i].name + 4); + /* hid_id is the first field of struct hid_bpf_ops */ ops_hid_id = bpf_map__initial_value(map, NULL); ASSERT_OK_PTR(ops_hid_id) TH_LOG("unable to retrieve struct_ops data"); @@ -109,13 +127,6 @@ static void load_programs(const struct test_program programs[], *ops_hid_id = self->hid.hid_id; } - /* we disable the auto-attach feature of all maps because we - * only want the tested one to be manually attached in the next - * call to bpf_map__attach_struct_ops() - */ - bpf_object__for_each_map(iter_map, *self->skel->skeleton->obj) - bpf_map__set_autoattach(iter_map, false); - err = hid__load(self->skel); ASSERT_OK(err) TH_LOG("hid_skel_load failed: %d", err); From eebbef7c468a5cb58c4772849ee5066441166cf0 Mon Sep 17 00:00:00 2001 From: Yiyang Chen Date: Tue, 23 Jun 2026 06:23:15 +0000 Subject: [PATCH 0220/1054] selftests/hid: Cover hid_bpf_get_data() size overflow Add a HID-BPF regression check for hid_bpf_get_data() requests whose size would overflow when added to the offset. The new rdesc fixup callback asks for offset 2 and size ~0ULL, then records whether the helper returns NULL. A vulnerable kernel returns a non-NULL pointer because the runtime check wraps the addition. A fixed kernel rejects the request. The callback records the helper result without dereferencing any returned pointer. The callback reports the helper result through BSS and returns 0 intentionally. hid_rdesc_fixup return values are consumed as report descriptor fixup results, so a positive test-result value would be interpreted as a replacement report descriptor size. Also add KHDR_INCLUDES to the HID selftest build so hid_bpf.c sees the current kernel UAPI HID definitions on systems whose installed headers do not provide enum hid_report_type. Fixes: 658ee5a64fcf ("HID: bpf: allocate data memory for device_event BPF programs") Signed-off-by: Yiyang Chen Signed-off-by: Benjamin Tissoires --- tools/testing/selftests/hid/Makefile | 2 +- tools/testing/selftests/hid/hid_bpf.c | 11 +++++++++++ tools/testing/selftests/hid/progs/hid.c | 15 +++++++++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/hid/Makefile b/tools/testing/selftests/hid/Makefile index 96071b4800e8..2f423de83147 100644 --- a/tools/testing/selftests/hid/Makefile +++ b/tools/testing/selftests/hid/Makefile @@ -24,7 +24,7 @@ CXX ?= $(CROSS_COMPILE)g++ HOSTPKG_CONFIG := pkg-config -CFLAGS += -g -O0 -rdynamic -Wall -Werror -I$(OUTPUT) +CFLAGS += -g -O0 -rdynamic -Wall -Werror -I$(OUTPUT) $(KHDR_INCLUDES) CFLAGS += -I$(OUTPUT)/tools/include LDLIBS += -lelf -lz -lrt -lpthread diff --git a/tools/testing/selftests/hid/hid_bpf.c b/tools/testing/selftests/hid/hid_bpf.c index 269256e1decd..b851339308c2 100644 --- a/tools/testing/selftests/hid/hid_bpf.c +++ b/tools/testing/selftests/hid/hid_bpf.c @@ -898,6 +898,17 @@ TEST_F(hid_bpf, test_rdesc_fixup) ASSERT_EQ(rpt_desc.value[4], 0x42); } +TEST_F(hid_bpf, test_rdesc_fixup_get_data_overflow) +{ + const struct test_program progs[] = { + { .name = "hid_rdesc_fixup_get_data_overflow" }, + }; + + LOAD_PROGRAMS(progs); + + ASSERT_EQ(self->skel->bss->get_data_overflow_check, 1); +} + static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) { diff --git a/tools/testing/selftests/hid/progs/hid.c b/tools/testing/selftests/hid/progs/hid.c index 5ecc845ef792..b21fbb13c926 100644 --- a/tools/testing/selftests/hid/progs/hid.c +++ b/tools/testing/selftests/hid/progs/hid.c @@ -13,6 +13,7 @@ struct attach_prog_args { __u64 callback_check = 52; __u64 callback2_check = 52; +__u64 get_data_overflow_check; SEC("?struct_ops/hid_device_event") int BPF_PROG(hid_first_event, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type) @@ -240,6 +241,20 @@ struct hid_bpf_ops rdesc_fixup = { .hid_rdesc_fixup = (void *)hid_rdesc_fixup, }; +SEC("?struct_ops.s/hid_rdesc_fixup") +int BPF_PROG(hid_rdesc_fixup_get_data_overflow, struct hid_bpf_ctx *hid_ctx) +{ + if (!hid_bpf_get_data(hid_ctx, 2 /* offset */, ~0ULL /* size */)) + get_data_overflow_check = 1; + + return 0; +} + +SEC(".struct_ops.link") +struct hid_bpf_ops rdesc_fixup_get_data_overflow = { + .hid_rdesc_fixup = (void *)hid_rdesc_fixup_get_data_overflow, +}; + SEC("?struct_ops/hid_device_event") int BPF_PROG(hid_test_insert1, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type) { From 51d111301a3ad8e5655687cb6462182e5804fa02 Mon Sep 17 00:00:00 2001 From: Yousef Alhouseen Date: Sat, 27 Jun 2026 00:38:04 +0200 Subject: [PATCH 0221/1054] xen/gntalloc: make grant counters unsigned The module limit and current allocation count cannot validly be negative. Give both variables unsigned types so their representation matches the u32 grant count supplied through the ioctl and negative module parameter values are rejected by parameter parsing. This also prepares the limit check for overflow-safe unsigned arithmetic. Signed-off-by: Yousef Alhouseen Reviewed-by: Juergen Gross Signed-off-by: Juergen Gross Message-ID: <20260626223805.43781-2-alhouseenyousef@gmail.com> --- drivers/xen/gntalloc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/xen/gntalloc.c b/drivers/xen/gntalloc.c index eadedd1e963e..9279f1521b6f 100644 --- a/drivers/xen/gntalloc.c +++ b/drivers/xen/gntalloc.c @@ -70,14 +70,14 @@ #include #include -static int limit = 1024; -module_param(limit, int, 0644); +static unsigned int limit = 1024; +module_param(limit, uint, 0644); MODULE_PARM_DESC(limit, "Maximum number of grants that may be allocated by " "the gntalloc device"); static LIST_HEAD(gref_list); static DEFINE_MUTEX(gref_mutex); -static int gref_size; +static unsigned int gref_size; struct notify_info { uint16_t pgoff:12; /* Bits 0-11: Offset of the byte to clear */ From 2299822f3f466b5dcad2377bf63986199f881a6b Mon Sep 17 00:00:00 2001 From: Yousef Alhouseen Date: Sat, 27 Jun 2026 00:38:05 +0200 Subject: [PATCH 0222/1054] xen/gntalloc: validate grant count before allocation gntalloc_ioctl_alloc() allocates the grant-id array before checking whether the requested count fits within the global grant limit. Counts above that limit cannot succeed, so reject them before the user-controlled allocation reaches kcalloc(). Use a subtraction-based check while holding gref_mutex so adding the requested count cannot wrap. Also cast the count before advancing the per-file index so the page-size multiplication is performed in 64-bit arithmetic. Signed-off-by: Yousef Alhouseen Reviewed-by: Juergen Gross Signed-off-by: Juergen Gross Message-ID: <20260626223805.43781-3-alhouseenyousef@gmail.com> --- drivers/xen/gntalloc.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/xen/gntalloc.c b/drivers/xen/gntalloc.c index 9279f1521b6f..3218686be45b 100644 --- a/drivers/xen/gntalloc.c +++ b/drivers/xen/gntalloc.c @@ -272,6 +272,7 @@ static long gntalloc_ioctl_alloc(struct gntalloc_file_private_data *priv, int rc = 0; struct ioctl_gntalloc_alloc_gref op; uint32_t *gref_ids; + unsigned int limit_snapshot; pr_debug("%s: priv %p\n", __func__, priv); @@ -280,6 +281,12 @@ static long gntalloc_ioctl_alloc(struct gntalloc_file_private_data *priv, goto out; } + limit_snapshot = READ_ONCE(limit); + if (op.count > limit_snapshot) { + rc = -ENOSPC; + goto out; + } + gref_ids = kcalloc(op.count, sizeof(gref_ids[0]), GFP_KERNEL); if (!gref_ids) { rc = -ENOMEM; @@ -292,14 +299,16 @@ static long gntalloc_ioctl_alloc(struct gntalloc_file_private_data *priv, * are about to enforce, removing them here is a good idea. */ do_cleanup(); - if (gref_size + op.count > limit) { + limit_snapshot = READ_ONCE(limit); + if (gref_size > limit_snapshot || + op.count > limit_snapshot - gref_size) { mutex_unlock(&gref_mutex); rc = -ENOSPC; goto out_free; } gref_size += op.count; op.index = priv->index; - priv->index += op.count * PAGE_SIZE; + priv->index += (uint64_t)op.count * PAGE_SIZE; mutex_unlock(&gref_mutex); rc = add_grefs(&op, gref_ids, priv); From cbbef43bdc083892a2d4787245c249502c215bb8 Mon Sep 17 00:00:00 2001 From: Yousef Alhouseen Date: Sat, 27 Jun 2026 00:37:38 +0200 Subject: [PATCH 0223/1054] xenbus: reject unterminated directory replies split_strings() walks each directory entry with strlen(). Although the transport adds a terminator after the reply buffer, a malformed reply without a final NUL inside its advertised length would let that walk cross the protocol payload boundary. Reject such replies before counting the strings. Report the protocol violation once and return -EIO to the caller. Signed-off-by: Yousef Alhouseen Reviewed-by: Juergen Gross Signed-off-by: Juergen Gross Message-ID: <20260626223738.43742-1-alhouseenyousef@gmail.com> --- drivers/xen/xenbus/xenbus_xs.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/xen/xenbus/xenbus_xs.c b/drivers/xen/xenbus/xenbus_xs.c index c202e7c553a6..d1cca4acb6f3 100644 --- a/drivers/xen/xenbus/xenbus_xs.c +++ b/drivers/xen/xenbus/xenbus_xs.c @@ -417,6 +417,12 @@ static char **split_strings(char *strings, unsigned int len, unsigned int *num) { char *p, **ret; + if (len && strings[len - 1]) { + pr_err_once("malformed XS_DIRECTORY reply\n"); + kfree(strings); + return ERR_PTR(-EIO); + } + /* Count the strings. */ *num = count_strings(strings, len); From 6769087fd856889a32caf09590703813e3763575 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 25 Jun 2026 15:58:31 +0200 Subject: [PATCH 0224/1054] xfs: open code xfs_buf_ioend_fail in xfs_buf_submit This better integrates with the other failure handling in xfs_buf_submit, and prepares for a better API in xfs_buf_ioend_fail. Signed-off-by: Christoph Hellwig Reviewed-by: Carlos Maiolino Signed-off-by: Carlos Maiolino --- fs/xfs/xfs_buf.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c index 2a7d696d394a..a108d31996f2 100644 --- a/fs/xfs/xfs_buf.c +++ b/fs/xfs/xfs_buf.c @@ -1383,10 +1383,8 @@ xfs_buf_submit( * state here rather than mount state to avoid corrupting the log tail * on shutdown. */ - if (bp->b_mount->m_log && xlog_is_shutdown(bp->b_mount->m_log)) { - xfs_buf_ioend_fail(bp); - return; - } + if (bp->b_mount->m_log && xlog_is_shutdown(bp->b_mount->m_log)) + goto ioerror; if (bp->b_flags & XBF_WRITE) xfs_buf_wait_unpin(bp); @@ -1399,17 +1397,22 @@ xfs_buf_submit( if ((bp->b_flags & XBF_WRITE) && !xfs_buf_verify_write(bp)) { xfs_force_shutdown(bp->b_mount, SHUTDOWN_CORRUPT_INCORE); - xfs_buf_ioend(bp); - return; + goto end_io; } /* In-memory targets are directly mapped, no I/O required. */ - if (xfs_buftarg_is_mem(bp->b_target)) { - xfs_buf_ioend(bp); - return; - } + if (xfs_buftarg_is_mem(bp->b_target)) + goto end_io; xfs_buf_submit_bio(bp); + return; + +ioerror: + bp->b_flags &= ~XBF_DONE; + xfs_buf_stale(bp); + xfs_buf_ioerror(bp, -EIO); +end_io: + xfs_buf_ioend(bp); } /* From 0b434b552ecd19f33e2f85ea8e55dbb65352810d Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 25 Jun 2026 15:58:32 +0200 Subject: [PATCH 0225/1054] xfs: also mark the buffer stale on verifier failure in xfs_buf_submit We should treat the buffer that caused a shutdown the same as handling buffers after a shutdown, so use the same stale && !DONE logic here. Signed-off-by: Christoph Hellwig Reviewed-by: Carlos Maiolino Reviewed-by: "Darrick J. Wong" Signed-off-by: Carlos Maiolino --- fs/xfs/xfs_buf.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c index a108d31996f2..0061abffcbb5 100644 --- a/fs/xfs/xfs_buf.c +++ b/fs/xfs/xfs_buf.c @@ -1383,8 +1383,10 @@ xfs_buf_submit( * state here rather than mount state to avoid corrupting the log tail * on shutdown. */ - if (bp->b_mount->m_log && xlog_is_shutdown(bp->b_mount->m_log)) + if (bp->b_mount->m_log && xlog_is_shutdown(bp->b_mount->m_log)) { + xfs_buf_ioerror(bp, -EIO); goto ioerror; + } if (bp->b_flags & XBF_WRITE) xfs_buf_wait_unpin(bp); @@ -1396,8 +1398,9 @@ xfs_buf_submit( bp->b_error = 0; if ((bp->b_flags & XBF_WRITE) && !xfs_buf_verify_write(bp)) { + /* ->verify_write should have set b_error already */ xfs_force_shutdown(bp->b_mount, SHUTDOWN_CORRUPT_INCORE); - goto end_io; + goto ioerror; } /* In-memory targets are directly mapped, no I/O required. */ @@ -1410,7 +1413,6 @@ xfs_buf_submit( ioerror: bp->b_flags &= ~XBF_DONE; xfs_buf_stale(bp); - xfs_buf_ioerror(bp, -EIO); end_io: xfs_buf_ioend(bp); } From 0c1b3a823a22af623d55f225fe2ac7e8b9052821 Mon Sep 17 00:00:00 2001 From: Yingjie Gao Date: Thu, 25 Jun 2026 21:16:23 +0800 Subject: [PATCH 0226/1054] xfs: release dquot buffer after dqflush failure xfs_qm_dqpurge() gets a locked buffer from xfs_dquot_use_attached_buf(). If xfs_qm_dqflush() fails, the error path skips xfs_buf_relse() and then calls xfs_dquot_detach_buf(), which tries to lock the same buffer again. Release the buffer after xfs_qm_dqflush() returns so the error path drops the caller hold and unlocks the buffer before the dquot is detached, matching the other dqflush callers. Fixes: a40fe30868ba ("xfs: separate dquot buffer reads from xfs_dqflush") Cc: stable@vger.kernel.org # v6.13+ Signed-off-by: Yingjie Gao Reviewed-by: "Darrick J. Wong" Signed-off-by: Carlos Maiolino --- fs/xfs/xfs_qm.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c index aa0d2976f1c3..896b24f87ac9 100644 --- a/fs/xfs/xfs_qm.c +++ b/fs/xfs/xfs_qm.c @@ -166,10 +166,9 @@ xfs_qm_dqpurge( * does it on success. */ error = xfs_qm_dqflush(dqp, bp); - if (!error) { + if (!error) error = xfs_bwrite(bp); - xfs_buf_relse(bp); - } + xfs_buf_relse(bp); xfs_dqflock(dqp); } xfs_dquot_detach_buf(dqp); From 45de375b25060edf46e20abb36521ba530336ceb Mon Sep 17 00:00:00 2001 From: Dawei Feng Date: Sat, 27 Jun 2026 14:04:02 +0800 Subject: [PATCH 0227/1054] xfs: fix memory leak in xfs_dqinode_metadir_create() If xfs_metadir_create() fails in xfs_dqinode_metadir_create(), the current code returns directly, leaking the allocated update and transaction state. If the subsequent commit fails, the caller-owned inode reference is left behind. Fix this memory leak by routing the create failure path through xfs_metadir_cancel(). For both create and commit failures, finish and release any inode returned to the caller, mirroring the unwind pattern in xfs_metadir_mkdir(). 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.1. An x86_64 allyesconfig build showed no new warnings. Runtime validation used kprobe fault injection during `mount -o uquota` on a metadir XFS image. Injecting xfs_metadir_create() reproduced the old active-update path that left mount stuck later in mount setup; after this change, the same injection reported cancel_hits=1 and irele_hits=1. Injecting xfs_metadir_commit() exercised the old inode-reference leak path; after this change, it reported irele_hits=1. Fixes: e80fbe1ad8ef ("xfs: use metadir for quota inodes") Cc: stable@vger.kernel.org # v6.13 Signed-off-by: Dawei Feng Reviewed-by: "Darrick J. Wong" Signed-off-by: Carlos Maiolino --- fs/xfs/libxfs/xfs_dquot_buf.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/fs/xfs/libxfs/xfs_dquot_buf.c b/fs/xfs/libxfs/xfs_dquot_buf.c index ce767b40482f..bbada0d3cc08 100644 --- a/fs/xfs/libxfs/xfs_dquot_buf.c +++ b/fs/xfs/libxfs/xfs_dquot_buf.c @@ -436,17 +436,27 @@ xfs_dqinode_metadir_create( error = xfs_metadir_create(&upd, S_IFREG); if (error) - return error; + goto out_cancel; xfs_trans_log_inode(upd.tp, upd.ip, XFS_ILOG_CORE); error = xfs_metadir_commit(&upd); if (error) - return error; + goto out_irele; xfs_finish_inode_setup(upd.ip); *ipp = upd.ip; return 0; + +out_cancel: + xfs_metadir_cancel(&upd, error); +out_irele: + /* Have to finish setting up the inode to ensure it's deleted. */ + if (upd.ip) { + xfs_finish_inode_setup(upd.ip); + xfs_irele(upd.ip); + } + return error; } #ifndef __KERNEL__ From cc9af5e461ea5f6e37738f3f1e41c45a9b7f45d6 Mon Sep 17 00:00:00 2001 From: Yousef Alhouseen Date: Tue, 30 Jun 2026 12:06:07 +0200 Subject: [PATCH 0228/1054] xfs: use null daddr for unset first bad log block xlog_do_recovery_pass() may return before setting first_bad. The caller must distinguish that case from an error at a valid log block, including block zero after the log wraps. Initialize first_bad to XFS_BUF_DADDR_NULL and test it explicitly before treating the error as a torn write. Fixes: 7088c4136fa1 ("xfs: detect and trim torn writes during log recovery") Suggested-by: Darrick J. Wong Reported-by: syzbot+b7dfbed0c6c2b5e9fd34@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=b7dfbed0c6c2b5e9fd34 Cc: stable@vger.kernel.org # v4.5 Signed-off-by: Yousef Alhouseen Reviewed-by: "Darrick J. Wong" Signed-off-by: Carlos Maiolino --- fs/xfs/xfs_log_recover.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c index 09e6678ca487..5f984bf5698a 100644 --- a/fs/xfs/xfs_log_recover.c +++ b/fs/xfs/xfs_log_recover.c @@ -1028,7 +1028,7 @@ xlog_verify_head( { struct xlog_rec_header *tmp_rhead; char *tmp_buffer; - xfs_daddr_t first_bad; + xfs_daddr_t first_bad = XFS_BUF_DADDR_NULL; xfs_daddr_t tmp_rhead_blk; int found; int error; @@ -1057,7 +1057,8 @@ xlog_verify_head( */ error = xlog_do_recovery_pass(log, *head_blk, tmp_rhead_blk, XLOG_RECOVER_CRCPASS, &first_bad); - if ((error == -EFSBADCRC || error == -EFSCORRUPTED) && first_bad) { + if ((error == -EFSBADCRC || error == -EFSCORRUPTED) && + first_bad != XFS_BUF_DADDR_NULL) { /* * We've hit a potential torn write. Reset the error and warn * about it. @@ -3575,4 +3576,3 @@ xlog_recover_cancel( if (xlog_recovery_needed(log)) xlog_recover_cancel_intents(log); } - From 613059875958e7b217b250ed14c3b189f9488421 Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Mon, 22 Jun 2026 17:03:58 +0300 Subject: [PATCH 0229/1054] drm/dp_mst: Handle torn-down topology gracefully in drm_dp_mst_topology_queue_probe() A hotplug or link-loss event can tear down the MST topology (setting mgr->mst_state = false and mgr->mst_primary = NULL) concurrently with a caller invoking drm_dp_mst_topology_queue_probe(). Since the check is already performed under mgr->lock, the condition is not a programming error but a valid race -- the topology was valid when the caller decided to call this function, but was torn down before the lock was acquired. Replace the drm_WARN_ON() with a graceful early return. This eliminates spurious kernel warnings and the resulting compositor crashes observed when connecting/disconnecting DP MST monitors, while keeping the correct behavior of doing nothing when MST is not active. A drm_dbg_mst() trace is added so the skipped probe remains observable under MST debug logging. The existing WARN_ON(mgr->mst_primary) in drm_dp_mst_topology_mgr_set_mst() already catches the case where the topology is initialized twice, so no diagnostic coverage is lost. Fixes: dbaeef363ea5 ("drm/dp_mst: Add a helper to queue a topology probe") Cc: Imre Deak Cc: Lyude Paul Cc: stable@vger.kernel.org Cc: intel-gfx@lists.freedesktop.org Cc: dri-devel@lists.freedesktop.org Signed-off-by: Jonas Emilsson Signed-off-by: Luca Coelho Link: https://lore.kernel.org/all/20260503034533.1023686-1-jonas.emilsson@gmail.com Acked-by: Imre Deak Link: https://patch.msgid.link/20260622140532.526722-1-luciano.coelho@intel.com Signed-off-by: Maarten Lankhorst --- drivers/gpu/drm/display/drm_dp_mst_topology.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/display/drm_dp_mst_topology.c b/drivers/gpu/drm/display/drm_dp_mst_topology.c index 4de36fda0544..7ce9e212770a 100644 --- a/drivers/gpu/drm/display/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/display/drm_dp_mst_topology.c @@ -3740,8 +3740,10 @@ void drm_dp_mst_topology_queue_probe(struct drm_dp_mst_topology_mgr *mgr) { mutex_lock(&mgr->lock); - if (drm_WARN_ON(mgr->dev, !mgr->mst_state || !mgr->mst_primary)) + if (!mgr->mst_state || !mgr->mst_primary) { + drm_dbg_kms(mgr->dev, "queue_probe skipped: topology torn down\n"); goto out_unlock; + } drm_dp_mst_topology_mgr_invalidate_mstb(mgr->mst_primary); drm_dp_mst_queue_probe_work(mgr); From 9825cf2cb59fac7480e7fac9eee13ab9af3f1ea8 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 30 Jun 2026 18:03:03 +0200 Subject: [PATCH 0230/1054] ACPICA: Define acpi_ut_safe_strncpy() as strscpy_pad() alias Commit 292db66afd20 ("ACPICA: Unbreak tools build after switching over to strscpy_pad()") added an #ifdef based on a __KERNEL__ check which is sort of nasty to the acpi_ut_safe_strncpy() definition to unbreak ACPICA tools builds broken by commit 97f7d3f9c9ac ("ACPICA: Replace strncpy() with strscpy_pad() in acpi_ut_safe_strncpy()"). However, that #ifdef effectively produces dead code when tools are built because they don't call acpi_ut_safe_strncpy(). Accordingly, drop the existing definition of acpi_ut_safe_strncpy() and define it as a strscpy_pad() alias. Fixes: 292db66afd20 ("ACPICA: Unbreak tools build after switching over to strscpy_pad()") Signed-off-by: Rafael J. Wysocki [ rjw: Tweak the changelog ] Link: https://patch.msgid.link/12941764.O9o76ZdvQC@rafael.j.wysocki Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/acutils.h | 2 -- drivers/acpi/acpica/utnonansi.c | 16 ---------------- include/acpi/platform/aclinuxex.h | 1 + 3 files changed, 1 insertion(+), 18 deletions(-) diff --git a/drivers/acpi/acpica/acutils.h b/drivers/acpi/acpica/acutils.h index 9a18cdbfd60f..9049bfee409c 100644 --- a/drivers/acpi/acpica/acutils.h +++ b/drivers/acpi/acpica/acutils.h @@ -626,8 +626,6 @@ void acpi_ut_repair_name(char *name); #if defined (ACPI_DEBUGGER) || defined (ACPI_APPLICATION) || defined (ACPI_DEBUG_OUTPUT) u8 acpi_ut_safe_strcpy(char *dest, acpi_size dest_size, char *source); -void acpi_ut_safe_strncpy(char *dest, char *source, acpi_size dest_size); - u8 acpi_ut_safe_strcat(char *dest, acpi_size dest_size, char *source); u8 diff --git a/drivers/acpi/acpica/utnonansi.c b/drivers/acpi/acpica/utnonansi.c index 93867ad7f342..a465e5a1d309 100644 --- a/drivers/acpi/acpica/utnonansi.c +++ b/drivers/acpi/acpica/utnonansi.c @@ -164,20 +164,4 @@ acpi_ut_safe_strncat(char *dest, return (FALSE); } -void acpi_ut_safe_strncpy(char *dest, char *source, acpi_size dest_size) -{ - /* Always terminate destination string */ - -#ifdef __KERNEL__ - strscpy_pad(dest, source, dest_size); -#else - /* - * strscpy_pad() is not defined in ACPICA tools builds, so use strncpy() - * and directly NUL-terminate the destination string in that case. - */ - strncpy(dest, source, dest_size); - dest[dest_size - 1] = 0; -#endif -} - #endif diff --git a/include/acpi/platform/aclinuxex.h b/include/acpi/platform/aclinuxex.h index aeb74e2f9d4f..760e1ded325c 100644 --- a/include/acpi/platform/aclinuxex.h +++ b/include/acpi/platform/aclinuxex.h @@ -134,6 +134,7 @@ static inline void acpi_os_terminate_debugger(void) /* * OSL interfaces added by Linux */ +#define acpi_ut_safe_strncpy strscpy_pad #endif /* __KERNEL__ */ From 4c8b46d832bd98eab914e2bbcd32447b584b03a7 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 25 Jun 2026 15:58:33 +0200 Subject: [PATCH 0231/1054] xfs: improve the xfs_buf_ioend_fail calling convention Move setting the ASYNC flag into xfs_buf_ioend_fail, assert that the buffer is locked as expected, and drop the confusing _ioend in the name. Signed-off-by: Christoph Hellwig Reviewed-by: Carlos Maiolino Reviewed-by: "Darrick J. Wong" Signed-off-by: Carlos Maiolino --- fs/xfs/xfs_buf.c | 13 +++++++------ fs/xfs/xfs_buf.h | 2 +- fs/xfs/xfs_buf_item.c | 3 +-- fs/xfs/xfs_inode.c | 3 +-- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c index 0061abffcbb5..aa4317793c57 100644 --- a/fs/xfs/xfs_buf.c +++ b/fs/xfs/xfs_buf.c @@ -1192,17 +1192,18 @@ xfs_buf_ioerror_alert( } /* - * To simulate an I/O failure, the buffer must be locked and held with at least - * two references. + * Fail a locked and referenced buffer outside the I/O path. * - * The buf item reference is dropped via ioend processing. The second reference - * is owned by the caller and is dropped on I/O completion if the buffer is - * XBF_ASYNC. + * The caller transfers a reference which will be released after processing the + * error. */ void -xfs_buf_ioend_fail( +xfs_buf_fail( struct xfs_buf *bp) { + ASSERT(xfs_buf_islocked(bp)); + + bp->b_flags |= XBF_ASYNC; bp->b_flags &= ~XBF_DONE; xfs_buf_stale(bp); xfs_buf_ioerror(bp, -EIO); diff --git a/fs/xfs/xfs_buf.h b/fs/xfs/xfs_buf.h index b3cd1c7029f1..79cc9c3f0254 100644 --- a/fs/xfs/xfs_buf.h +++ b/fs/xfs/xfs_buf.h @@ -290,7 +290,7 @@ extern void __xfs_buf_ioerror(struct xfs_buf *bp, int error, xfs_failaddr_t failaddr); #define xfs_buf_ioerror(bp, err) __xfs_buf_ioerror((bp), (err), __this_address) extern void xfs_buf_ioerror_alert(struct xfs_buf *bp, xfs_failaddr_t fa); -void xfs_buf_ioend_fail(struct xfs_buf *); +void xfs_buf_fail(struct xfs_buf *bp); void __xfs_buf_mark_corrupt(struct xfs_buf *bp, xfs_failaddr_t fa); #define xfs_buf_mark_corrupt(bp) __xfs_buf_mark_corrupt((bp), __this_address) diff --git a/fs/xfs/xfs_buf_item.c b/fs/xfs/xfs_buf_item.c index 8487635579e5..1f055cd6732e 100644 --- a/fs/xfs/xfs_buf_item.c +++ b/fs/xfs/xfs_buf_item.c @@ -549,8 +549,7 @@ xfs_buf_item_unpin( * wait for the lock and then run the IO failure completion. */ xfs_buf_lock(bp); - bp->b_flags |= XBF_ASYNC; - xfs_buf_ioend_fail(bp); + xfs_buf_fail(bp); return; } diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index 317eb57f989f..15279d22a894 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -2646,8 +2646,7 @@ xfs_iflush_cluster( * inode cluster buffers. */ xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE); - bp->b_flags |= XBF_ASYNC; - xfs_buf_ioend_fail(bp); + xfs_buf_fail(bp); return error; } From b53177d418225b15d23b1817fac1b5c668e56c2f Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 25 Jun 2026 15:58:34 +0200 Subject: [PATCH 0232/1054] xfs: remove xfs_buf_ioend There are two callers of xfs_buf_ioend, one of which always has the XBF_ASYNC flag set. Open code the logic in both callers to prepare for a bug fix. Signed-off-by: Christoph Hellwig Reviewed-by: Carlos Maiolino Reviewed-by: "Darrick J. Wong" Signed-off-by: Carlos Maiolino --- fs/xfs/xfs_buf.c | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c index aa4317793c57..f5e23c8b07b6 100644 --- a/fs/xfs/xfs_buf.c +++ b/fs/xfs/xfs_buf.c @@ -1146,18 +1146,6 @@ __xfs_buf_ioend( return true; } -static void -xfs_buf_ioend( - struct xfs_buf *bp) -{ - if (!__xfs_buf_ioend(bp)) - return; - if (bp->b_flags & XBF_ASYNC) - xfs_buf_relse(bp); - else - complete(&bp->b_iowait); -} - static void xfs_buf_ioend_work( struct work_struct *work) @@ -1207,7 +1195,8 @@ xfs_buf_fail( bp->b_flags &= ~XBF_DONE; xfs_buf_stale(bp); xfs_buf_ioerror(bp, -EIO); - xfs_buf_ioend(bp); + if (__xfs_buf_ioend(bp)) + xfs_buf_relse(bp); } int @@ -1415,7 +1404,12 @@ xfs_buf_submit( bp->b_flags &= ~XBF_DONE; xfs_buf_stale(bp); end_io: - xfs_buf_ioend(bp); + if (!__xfs_buf_ioend(bp)) + return; + if (bp->b_flags & XBF_ASYNC) + xfs_buf_relse(bp); + else + complete(&bp->b_iowait); } /* From 93e21ef2a819348fce899bd1bd1303979bba3f1d Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 25 Jun 2026 15:58:35 +0200 Subject: [PATCH 0233/1054] xfs: fix handling of synchronous errors in xfs_buf_submit Synchronous readers and writers already run __xfs_buf_ioend from xfs_buf_iowait after being woken through bp->b_iowait, so we should not call it here, which can lead to double completions. Fixes: 4b90de5bc0f5 ("xfs: reduce context switches for synchronous buffered I/O") Signed-off-by: Christoph Hellwig Reviewed-by: Carlos Maiolino Reviewed-by: "Darrick J. Wong" Signed-off-by: Carlos Maiolino --- fs/xfs/xfs_buf.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c index f5e23c8b07b6..f18e53b60f54 100644 --- a/fs/xfs/xfs_buf.c +++ b/fs/xfs/xfs_buf.c @@ -1404,12 +1404,12 @@ xfs_buf_submit( bp->b_flags &= ~XBF_DONE; xfs_buf_stale(bp); end_io: - if (!__xfs_buf_ioend(bp)) - return; - if (bp->b_flags & XBF_ASYNC) - xfs_buf_relse(bp); - else + if (bp->b_flags & XBF_ASYNC) { + if (__xfs_buf_ioend(bp)) + xfs_buf_relse(bp); + } else { complete(&bp->b_iowait); + } } /* From e4281086ae6caf006b6ef0670479eb5f96880fb9 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 25 Jun 2026 15:58:36 +0200 Subject: [PATCH 0234/1054] xfs: simplify __xfs_buf_ioend __xfs_buf_ioend can only resubmit the buffer for asynchronous writes, which means the retry handling xfs_buf_iowait is not needed. Because of this can stop returning a value from __xfs_buf_ioend and just release the buffer for async I/O that does not require retries. Also drop the __-prefix now that the semantics are straight forward. Signed-off-by: Christoph Hellwig Reviewed-by: Carlos Maiolino Reviewed-by: "Darrick J. Wong" Signed-off-by: Carlos Maiolino --- fs/xfs/xfs_buf.c | 51 ++++++++++++++++++++++++------------------------ 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c index f18e53b60f54..e1465e950acc 100644 --- a/fs/xfs/xfs_buf.c +++ b/fs/xfs/xfs_buf.c @@ -1098,11 +1098,17 @@ xfs_buf_ioend_handle_error( return false; } -/* returns false if the caller needs to resubmit the I/O, else true */ -static bool -__xfs_buf_ioend( +/* + * Complete a buffer read or write. + * + * Releases the buffer if the I/O was asynchronous. + */ +static void +xfs_buf_ioend( struct xfs_buf *bp) { + bool async = bp->b_flags & XBF_ASYNC; + trace_xfs_buf_iodone(bp, _RET_IP_); if (bp->b_flags & XBF_READ) { @@ -1116,14 +1122,16 @@ __xfs_buf_ioend( if (bp->b_flags & XBF_READ_AHEAD) percpu_counter_dec(&bp->b_target->bt_readahead_count); } else { - if (!bp->b_error) { + if (unlikely(bp->b_error)) { + if (xfs_buf_ioend_handle_error(bp)) { + ASSERT(async); + return; + } + } else { bp->b_flags &= ~XBF_WRITE_FAIL; bp->b_flags |= XBF_DONE; } - if (unlikely(bp->b_error) && xfs_buf_ioend_handle_error(bp)) - return false; - /* clear the retry state */ bp->b_last_error = 0; bp->b_retries = 0; @@ -1143,18 +1151,15 @@ __xfs_buf_ioend( bp->b_flags &= ~(XBF_READ | XBF_WRITE | XBF_READ_AHEAD | _XBF_LOGRECOVERY); - return true; + if (async) + xfs_buf_relse(bp); } static void xfs_buf_ioend_work( struct work_struct *work) { - struct xfs_buf *bp = - container_of(work, struct xfs_buf, b_ioend_work); - - if (__xfs_buf_ioend(bp)) - xfs_buf_relse(bp); + xfs_buf_ioend(container_of(work, struct xfs_buf, b_ioend_work)); } void @@ -1195,8 +1200,7 @@ xfs_buf_fail( bp->b_flags &= ~XBF_DONE; xfs_buf_stale(bp); xfs_buf_ioerror(bp, -EIO); - if (__xfs_buf_ioend(bp)) - xfs_buf_relse(bp); + xfs_buf_ioend(bp); } int @@ -1305,12 +1309,11 @@ xfs_buf_iowait( { ASSERT(!(bp->b_flags & XBF_ASYNC)); - do { - trace_xfs_buf_iowait(bp, _RET_IP_); - wait_for_completion(&bp->b_iowait); - trace_xfs_buf_iowait_done(bp, _RET_IP_); - } while (!__xfs_buf_ioend(bp)); + trace_xfs_buf_iowait(bp, _RET_IP_); + wait_for_completion(&bp->b_iowait); + trace_xfs_buf_iowait_done(bp, _RET_IP_); + xfs_buf_ioend(bp); return bp->b_error; } @@ -1404,12 +1407,10 @@ xfs_buf_submit( bp->b_flags &= ~XBF_DONE; xfs_buf_stale(bp); end_io: - if (bp->b_flags & XBF_ASYNC) { - if (__xfs_buf_ioend(bp)) - xfs_buf_relse(bp); - } else { + if (bp->b_flags & XBF_ASYNC) + xfs_buf_ioend(bp); + else complete(&bp->b_iowait); - } } /* From 3dd63dba8f9cb6990a40af7ed66ee0159f475819 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Wed, 1 Jul 2026 17:21:55 +0900 Subject: [PATCH 0235/1054] block: avoid potential deadlock on zone revalidation failure If revalidating the zones of a zoned block device with blk_revalidate_disk_zones() fails during a SCSI disk rescan, the following lockdep splat is thrown: [ 347.251859] [ T11230] sda: failed to revalidate zones [ 347.261380] [ T11230] ====================================================== [ 347.263882] [ T11230] WARNING: possible circular locking dependency detected [ 347.266353] [ T11230] 7.1.0+ #1194 Not tainted [ 347.268052] [ T11230] ------------------------------------------------------ [ 347.270537] [ T11230] tcsh/11230 is trying to acquire lock: [ 347.272555] [ T11230] ffffffff8f91d400 (wq_pool_mutex){+.+.}-{4:4}, at: destroy_workqueue+0x15d/0x8d0 [ 347.275914] [ T11230] but task is already holding lock: [ 347.278646] [ T11230] ffff88812fa1bcc0 (&q->q_usage_counter(io)#5){++++}-{0:0}, at: blk_mq_freeze_queue_nomemsave+0x16/0x30 [ 347.282503] [ T11230] which lock already depends on the new lock. [ 347.286239] [ T11230] the existing dependency chain (in reverse order) is: [ 347.289408] [ T11230] -> #2 (&q->q_usage_counter(io)#5){++++}-{0:0}: [ 347.292437] [ T11230] blk_alloc_queue+0x5ca/0x750 [ 347.294379] [ T11230] blk_mq_alloc_queue+0x14c/0x240 [ 347.296375] [ T11230] scsi_alloc_sdev+0x871/0xd10 [scsi_mod] [ 347.298619] [ T11230] scsi_probe_and_add_lun+0x600/0xc50 [scsi_mod] [ 347.301056] [ T11230] __scsi_scan_target+0x187/0x3b0 [scsi_mod] [ 347.303385] [ T11230] scsi_scan_channel+0xf2/0x180 [scsi_mod] [ 347.305651] [ T11230] scsi_scan_host_selected+0x20b/0x2d0 [scsi_mod] [ 347.308119] [ T11230] do_scan_async+0x42/0x420 [scsi_mod] [ 347.310276] [ T11230] async_run_entry_fn+0x94/0x5a0 [ 347.312284] [ T11230] process_one_work+0x8da/0x1690 [ 347.314287] [ T11230] worker_thread+0x5fe/0x1010 [ 347.316216] [ T11230] kthread+0x358/0x450 [ 347.317675] [ T11230] ret_from_fork+0x5b9/0x8e0 [ 347.319181] [ T11230] ret_from_fork_asm+0x11/0x20 [ 347.320778] [ T11230] -> #1 (fs_reclaim){+.+.}-{0:0}: [ 347.322890] [ T11230] fs_reclaim_acquire+0xd5/0x120 [ 347.324464] [ T11230] __kmalloc_cache_node_noprof+0x39/0x620 [ 347.326223] [ T11230] init_rescuer+0x19b/0x560 [ 347.327697] [ T11230] workqueue_init+0x33b/0x6a0 [ 347.329224] [ T11230] kernel_init_freeable+0x2eb/0x600 [ 347.330881] [ T11230] kernel_init+0x1c/0x140 [ 347.332334] [ T11230] ret_from_fork+0x5b9/0x8e0 [ 347.333847] [ T11230] ret_from_fork_asm+0x11/0x20 [ 347.335360] [ T11230] -> #0 (wq_pool_mutex){+.+.}-{4:4}: [ 347.337510] [ T11230] __lock_acquire+0xdea/0x2260 [ 347.339030] [ T11230] lock_acquire+0x187/0x2f0 [ 347.340495] [ T11230] __mutex_lock+0x1ab/0x2600 [ 347.341464] [ T11230] destroy_workqueue+0x15d/0x8d0 [ 347.342485] [ T11230] disk_free_zone_resources+0xd5/0x560 [ 347.343577] [ T11230] blk_revalidate_disk_zones+0x620/0xac7 [ 347.344723] [ T11230] sd_zbc_revalidate_zones+0x1dd/0x790 [sd_mod] [ 347.345938] [ T11230] sd_revalidate_disk+0xc66/0x8e60 [sd_mod] [ 347.347112] [ T11230] scsi_rescan_device+0x1f9/0x310 [scsi_mod] [ 347.348318] [ T11230] store_rescan_field+0x19/0x20 [scsi_mod] [ 347.349507] [ T11230] kernfs_fop_write_iter+0x3d2/0x5e0 [ 347.350565] [ T11230] vfs_write+0x469/0x1000 [ 347.351484] [ T11230] ksys_write+0x116/0x250 [ 347.352403] [ T11230] do_syscall_64+0xf0/0x6e0 [ 347.353361] [ T11230] entry_SYSCALL_64_after_hwframe+0x4b/0x53 [ 347.354533] [ T11230] other info that might help us debug this: [ 347.356432] [ T11230] Chain exists of: wq_pool_mutex --> fs_reclaim --> &q->q_usage_counter(io)#5 [ 347.358919] [ T11230] Possible unsafe locking scenario: [ 347.360307] [ T11230] CPU0 CPU1 [ 347.361327] [ T11230] ---- ---- [ 347.362340] [ T11230] lock(&q->q_usage_counter(io)#5); [ 347.363344] [ T11230] lock(fs_reclaim); [ 347.364526] [ T11230] lock(&q->q_usage_counter(io)#5); [ 347.365968] [ T11230] lock(wq_pool_mutex); [ 347.366811] [ T11230] *** DEADLOCK *** This happens because SCSI disk rescan is executed from a work context and a failure of blk_revalidate_disk_zones() causes a call to disk_free_zone_resources() which will free the disk zone write plug workqueue. Avoid this by delaying the destruction of the disk zone write plug workqueue to disk_release(). Do this by introducing the function disk_release_zone_resources() and using this new function from disk_release(). This new function destroys the zone write plugs workqueue and calls disk_free_zone_resources(), thus allowing to remove the call to destroy_workqueue() from disk_free_zone_resources(). disk_alloc_zone_resources() is modified to not create the disk zone write plug work queue if it already exists. Fixes: a8f59e5a5dea ("block: use a per disk workqueue for zone write plugging") Cc: stable@vger.kernek.org Signed-off-by: Damien Le Moal Reviewed-by: Christoph Hellwig Reviewed-by: Hannes Reinecke Link: https://patch.msgid.link/20260701082155.1369996-1-dlemoal@kernel.org Signed-off-by: Jens Axboe --- block/blk-zoned.c | 42 ++++++++++++++++++++++++++++-------------- block/blk.h | 4 ++-- block/genhd.c | 2 +- 3 files changed, 31 insertions(+), 17 deletions(-) diff --git a/block/blk-zoned.c b/block/blk-zoned.c index bea817f3de56..ca30caec838e 100644 --- a/block/blk-zoned.c +++ b/block/blk-zoned.c @@ -1923,11 +1923,20 @@ static int disk_alloc_zone_resources(struct gendisk *disk, if (!disk->zone_wplugs_pool) goto free_hash; - disk->zone_wplugs_wq = - alloc_workqueue("%s_zwplugs", WQ_MEM_RECLAIM | WQ_HIGHPRI | WQ_PERCPU, - pool_size, disk->disk_name); - if (!disk->zone_wplugs_wq) - goto destroy_pool; + /* + * We may already have a zone write plug workqueue as this function may + * be called after disk_free_zone_resources(), which does not destroy + * the workqueue (the zone write plugs workqueue is destroyed at + * disk_release() time). + */ + if (!disk->zone_wplugs_wq) { + disk->zone_wplugs_wq = + alloc_workqueue("%s_zwplugs", + WQ_MEM_RECLAIM | WQ_HIGHPRI | WQ_PERCPU, + pool_size, disk->disk_name); + if (!disk->zone_wplugs_wq) + goto destroy_pool; + } disk->zone_wplugs_worker = kthread_create(disk_zone_wplugs_worker, disk, @@ -1935,15 +1944,12 @@ static int disk_alloc_zone_resources(struct gendisk *disk, if (IS_ERR(disk->zone_wplugs_worker)) { ret = PTR_ERR(disk->zone_wplugs_worker); disk->zone_wplugs_worker = NULL; - goto destroy_wq; + goto destroy_pool; } wake_up_process(disk->zone_wplugs_worker); return 0; -destroy_wq: - destroy_workqueue(disk->zone_wplugs_wq); - disk->zone_wplugs_wq = NULL; destroy_pool: mempool_destroy(disk->zone_wplugs_pool); disk->zone_wplugs_pool = NULL; @@ -1999,7 +2005,7 @@ static void disk_set_zones_cond_array(struct gendisk *disk, u8 *zones_cond) kfree_rcu_mightsleep(zones_cond); } -void disk_free_zone_resources(struct gendisk *disk) +static void disk_free_zone_resources(struct gendisk *disk) { if (disk->zone_wplugs_worker) { kthread_stop(disk->zone_wplugs_worker); @@ -2007,10 +2013,8 @@ void disk_free_zone_resources(struct gendisk *disk) } WARN_ON_ONCE(!list_empty(&disk->zone_wplugs_list)); - if (disk->zone_wplugs_wq) { - destroy_workqueue(disk->zone_wplugs_wq); - disk->zone_wplugs_wq = NULL; - } + if (disk->zone_wplugs_wq) + drain_workqueue(disk->zone_wplugs_wq); disk_destroy_zone_wplugs_hash_table(disk); @@ -2020,6 +2024,16 @@ void disk_free_zone_resources(struct gendisk *disk) disk->nr_zones = 0; } +void disk_release_zone_resources(struct gendisk *disk) +{ + if (disk->zone_wplugs_wq) { + destroy_workqueue(disk->zone_wplugs_wq); + disk->zone_wplugs_wq = NULL; + } + + disk_free_zone_resources(disk); +} + struct blk_revalidate_zone_args { struct gendisk *disk; u8 *zones_cond; diff --git a/block/blk.h b/block/blk.h index 25af8ac5ef0f..fb95d3c58950 100644 --- a/block/blk.h +++ b/block/blk.h @@ -528,7 +528,7 @@ static inline void ioc_clear_queue(struct request_queue *q) #ifdef CONFIG_BLK_DEV_ZONED void disk_init_zone_resources(struct gendisk *disk); -void disk_free_zone_resources(struct gendisk *disk); +void disk_release_zone_resources(struct gendisk *disk); static inline bool bio_zone_write_plugging(struct bio *bio) { return bio_flagged(bio, BIO_ZONE_WRITE_PLUGGING); @@ -581,7 +581,7 @@ int blkdev_zone_mgmt_ioctl(struct block_device *bdev, blk_mode_t mode, static inline void disk_init_zone_resources(struct gendisk *disk) { } -static inline void disk_free_zone_resources(struct gendisk *disk) +static inline void disk_release_zone_resources(struct gendisk *disk) { } static inline bool bio_zone_write_plugging(struct bio *bio) diff --git a/block/genhd.c b/block/genhd.c index f84b6a355b57..30ac0ffe6517 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -1300,7 +1300,7 @@ static void disk_release(struct device *dev) disk_release_events(disk); kfree(disk->random); - disk_free_zone_resources(disk); + disk_release_zone_resources(disk); xa_destroy(&disk->part_tbl); kobject_put(&disk->queue_kobj); From df645b745941ea829b39134ac342f730f4d9d978 Mon Sep 17 00:00:00 2001 From: Yi Xie Date: Tue, 30 Jun 2026 17:12:06 +0800 Subject: [PATCH 0236/1054] io_uring/memmap: return -EINVAL from get_unmapped_area() on bad mmap get_unmapped_area() returns -ENOMEM when io_uring_validate_mmap_request() fails, but validation errors are -EINVAL. Propagate that errno to userspace, like io_uring_mmap() already does. Signed-off-by: Yi Xie Link: https://patch.msgid.link/20260630091206.126206-1-xieyi@kylinos.cn Signed-off-by: Jens Axboe --- io_uring/memmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/io_uring/memmap.c b/io_uring/memmap.c index da1f6c5d07f8..23e8a85111bc 100644 --- a/io_uring/memmap.c +++ b/io_uring/memmap.c @@ -337,7 +337,7 @@ unsigned long io_uring_get_unmapped_area(struct file *filp, unsigned long addr, ptr = io_uring_validate_mmap_request(filp, pgoff); if (IS_ERR(ptr)) - return -ENOMEM; + return PTR_ERR(ptr); /* * Some architectures have strong cache aliasing requirements. From 15cd3ccf9b179f0f76948d0901be3b15028bc768 Mon Sep 17 00:00:00 2001 From: Melbin K Mathew Date: Wed, 1 Jul 2026 10:11:45 +0200 Subject: [PATCH 0237/1054] io_uring/msg_ring: reject CQE32 flag pass-through to normal rings IORING_OP_MSG_RING with IORING_MSG_RING_FLAGS_PASS allows a sender to pass completion flags through sqe->file_index. If the sender sets IORING_CQE_F_32 in file_index, the target-side completion path treats it as a 32b CQE and writes big_cqe[0] and big_cqe[1] into the CQ ring regardless of whether the target ring was created with IORING_SETUP_CQE32 or IORING_SETUP_CQE_MIXED. On a normal 16b CQE ring, this writes 16 extra bytes (two u64 big_cqe fields) into the next CQE slot in the ring buffer. As the receiving ring doesn't understand 32b CQEs, this is incorrect and they should be rejected. Fixes: cbeb47a7b5f0 ("io_uring/msg_ring: Pass custom flags to the cqe") Signed-off-by: Melbin K Mathew Link: https://patch.msgid.link/20260701081145.196730-1-mlbnkm1@gmail.com [axboe: edit commit message] Signed-off-by: Jens Axboe --- io_uring/msg_ring.c | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/io_uring/msg_ring.c b/io_uring/msg_ring.c index 3ff9098573db..3067c9343991 100644 --- a/io_uring/msg_ring.c +++ b/io_uring/msg_ring.c @@ -93,19 +93,38 @@ static void io_msg_remote_post(struct io_ring_ctx *ctx, struct io_kiocb *req, io_req_task_work_add_remote(req, IOU_F_TWQ_LAZY_WAKE); } +static int io_msg_ring_cqe_flags(struct io_ring_ctx *target_ctx, + const struct io_msg *msg, u32 *flags) +{ + *flags = 0; + + if (!(msg->flags & IORING_MSG_RING_FLAGS_PASS)) + return 0; + + *flags = msg->cqe_flags; + if ((*flags & IORING_CQE_F_32) && + !(target_ctx->flags & (IORING_SETUP_CQE32 | + IORING_SETUP_CQE_MIXED))) + return -EINVAL; + + return 0; +} + static int io_msg_data_remote(struct io_ring_ctx *target_ctx, struct io_msg *msg) { struct io_kiocb *target; - u32 flags = 0; + u32 flags; + int ret; - target = kmem_cache_alloc(req_cachep, GFP_KERNEL | __GFP_NOWARN | __GFP_ZERO) ; + ret = io_msg_ring_cqe_flags(target_ctx, msg, &flags); + if (ret) + return ret; + + target = kmem_cache_alloc(req_cachep, GFP_KERNEL | __GFP_NOWARN | __GFP_ZERO); if (unlikely(!target)) return -ENOMEM; - if (msg->flags & IORING_MSG_RING_FLAGS_PASS) - flags = msg->cqe_flags; - io_msg_remote_post(target_ctx, target, msg->len, flags, msg->user_data); return 0; } @@ -130,8 +149,9 @@ static int __io_msg_ring_data(struct io_ring_ctx *target_ctx, if (io_msg_need_remote(target_ctx)) return io_msg_data_remote(target_ctx, msg); - if (msg->flags & IORING_MSG_RING_FLAGS_PASS) - flags = msg->cqe_flags; + ret = io_msg_ring_cqe_flags(target_ctx, msg, &flags); + if (ret) + return ret; ret = -EOVERFLOW; if (target_ctx->flags & IORING_SETUP_IOPOLL) { From 147996e7e7c9e8339c0e04f6fa7ccb3e4d448ff7 Mon Sep 17 00:00:00 2001 From: HyeongJun An Date: Wed, 1 Jul 2026 18:52:31 +0900 Subject: [PATCH 0238/1054] ALSA: usx2y: us144mkii: fix work UAF on disconnect tascam_disconnect() cancels capture_work and midi_in_work before usb_kill_anchored_urbs() kills the capture/MIDI-in URBs. Those URBs self-resubmit, and their completion handlers reschedule the work. A URB that completes in the small window between cancel_work_sync() and usb_kill_anchored_urbs() therefore re-arms the work after its only cancel. Nothing cancels it again before snd_card_free() frees the card-private tascam structure, so the work handler then runs on freed memory. Kill the anchored URBs before cancelling the work; once the work is cancelled no remaining URB can complete to re-arm it. Fixes: c1bb0c13e430 ("ALSA: usb-audio: us144mkii: Implement audio capture and decoding") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: HyeongJun An Link: https://patch.msgid.link/20260701095231.1020811-1-sammiee5311@gmail.com Signed-off-by: Takashi Iwai --- sound/usb/usx2y/us144mkii.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/sound/usb/usx2y/us144mkii.c b/sound/usb/usx2y/us144mkii.c index 94553b61013c..58ef23146f20 100644 --- a/sound/usb/usx2y/us144mkii.c +++ b/sound/usb/usx2y/us144mkii.c @@ -585,19 +585,24 @@ static void tascam_disconnect(struct usb_interface *intf) return; if (intf->cur_altsetting->desc.bInterfaceNumber == 0) { - /* Ensure all deferred work is complete before freeing resources */ snd_card_disconnect(tascam->card); - cancel_work_sync(&tascam->stop_work); - cancel_work_sync(&tascam->capture_work); - cancel_work_sync(&tascam->midi_in_work); - cancel_work_sync(&tascam->midi_out_work); - cancel_work_sync(&tascam->stop_pcm_work); + /* + * Kill the URBs before cancelling the work, so a late URB + * completion cannot re-arm a work that then runs after + * snd_card_free(). + */ usb_kill_anchored_urbs(&tascam->playback_anchor); usb_kill_anchored_urbs(&tascam->capture_anchor); usb_kill_anchored_urbs(&tascam->feedback_anchor); usb_kill_anchored_urbs(&tascam->midi_in_anchor); usb_kill_anchored_urbs(&tascam->midi_out_anchor); + + cancel_work_sync(&tascam->stop_work); + cancel_work_sync(&tascam->capture_work); + cancel_work_sync(&tascam->midi_in_work); + cancel_work_sync(&tascam->midi_out_work); + cancel_work_sync(&tascam->stop_pcm_work); timer_delete_sync(&tascam->error_timer); tascam_free_urbs(tascam); snd_card_free(tascam->card); From 95edf2dbb492f3ea2420111e9c0044c7dec9113c Mon Sep 17 00:00:00 2001 From: Yousef Alhouseen Date: Sun, 28 Jun 2026 02:03:29 +0200 Subject: [PATCH 0239/1054] ASoC: SOF: validate probe info element counts Probe information replies contain a firmware-provided element count. IPC3 uses that count to copy an array, then returns the unchecked count to its caller. A short reply can therefore make the caller walk beyond the copied array. IPC4 similarly uses the count both to allocate the destination array and to walk the reply. On 32-bit systems the allocation size can wrap, while on all systems an excessive count reads beyond the reply payload. Validate each count against the actual reply size before copying or allocating the array, and use kcalloc() for the IPC4 allocation. Signed-off-by: Yousef Alhouseen Link: https://patch.msgid.link/20260628000329.18606-1-alhouseenyousef@gmail.com Signed-off-by: Mark Brown --- sound/soc/sof/sof-client-probes-ipc3.c | 23 +++++++++++++++++++---- sound/soc/sof/sof-client-probes-ipc4.c | 11 ++++++++++- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/sound/soc/sof/sof-client-probes-ipc3.c b/sound/soc/sof/sof-client-probes-ipc3.c index a78ec0954a61..a3e382d6161f 100644 --- a/sound/soc/sof/sof-client-probes-ipc3.c +++ b/sound/soc/sof/sof-client-probes-ipc3.c @@ -107,7 +107,7 @@ static int ipc3_probes_info(struct sof_client_dev *cdev, unsigned int cmd, struct device *dev = &cdev->auxdev.dev; struct sof_ipc_probe_info_params msg = {{{0}}}; struct sof_ipc_probe_info_params *reply; - size_t bytes; + size_t bytes, elem_size, payload_size; int ret; *params = NULL; @@ -128,14 +128,29 @@ static int ipc3_probes_info(struct sof_client_dev *cdev, unsigned int cmd, if (ret < 0 || reply->rhdr.error < 0) goto exit; + payload_size = reply->rhdr.hdr.size; + if (payload_size < offsetof(struct sof_ipc_probe_info_params, dma)) { + ret = -EINVAL; + goto exit; + } + if (!reply->num_elems) goto exit; if (cmd == SOF_IPC_PROBE_DMA_INFO) - bytes = sizeof(reply->dma[0]); + elem_size = sizeof(reply->dma[0]); else - bytes = sizeof(reply->desc[0]); - bytes *= reply->num_elems; + elem_size = sizeof(reply->desc[0]); + + payload_size -= offsetof(struct sof_ipc_probe_info_params, dma); + if (reply->num_elems > payload_size / elem_size) { + dev_err(dev, "%s: invalid probe info element count %u\n", + __func__, reply->num_elems); + ret = -EINVAL; + goto exit; + } + + bytes = reply->num_elems * elem_size; *params = kmemdup(&reply->dma[0], bytes, GFP_KERNEL); if (!*params) { ret = -ENOMEM; diff --git a/sound/soc/sof/sof-client-probes-ipc4.c b/sound/soc/sof/sof-client-probes-ipc4.c index 88397c7dc4c3..2eef32b55395 100644 --- a/sound/soc/sof/sof-client-probes-ipc4.c +++ b/sound/soc/sof/sof-client-probes-ipc4.c @@ -248,10 +248,19 @@ static int ipc4_probes_points_info(struct sof_client_dev *cdev, return ret; } info = msg.data_ptr; + if (msg.data_size < sizeof(*info) || + info->num_elems > (msg.data_size - sizeof(*info)) / + sizeof(info->points[0])) { + dev_err(dev, "%s: invalid probe info element count %u\n", + __func__, info->num_elems); + kfree(msg.data_ptr); + return -EINVAL; + } + *num_desc = info->num_elems; dev_dbg(dev, "%s: got %zu probe points", __func__, *num_desc); - *desc = kzalloc(*num_desc * sizeof(**desc), GFP_KERNEL); + *desc = kcalloc(*num_desc, sizeof(**desc), GFP_KERNEL); if (!*desc) { kfree(msg.data_ptr); return -ENOMEM; From f5ad2ead846e3a00d040d64c7eaf67b65629f51d Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 12 Jun 2026 22:15:59 +0200 Subject: [PATCH 0240/1054] RDMa/mlx5: Avoid frame overflow warning Building mlx5 on s390 shows a rather high stack usage that can exceed the warning limit when that is set to a lower but still reasonable value: drivers/infiniband/hw/mlx5/wr.c:1051:5: error: stack frame size (1328) exceeds limit (1280) in 'mlx5_ib_post_send' [-Werror,-Wframe-larger-than] The problem here is 'struct ib_reg_wr' on the stack of handle_reg_mr_integrity(), which gets inlined into mlx5_ib_post_send() along with a number of smaller functions. Keeping the inner function out of line like gcc does avoids the warning and reduces the total stack usage in other functions called from mlx5_ib_post_send(), though handle_reg_mr_integrity() itself still has the same problem as before. Signed-off-by: Arnd Bergmann Link: https://patch.msgid.link/20260612201611.4127750-1-arnd@kernel.org Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/mlx5/wr.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/infiniband/hw/mlx5/wr.c b/drivers/infiniband/hw/mlx5/wr.c index 9947feb7fb8a..fca9e1d9d5e9 100644 --- a/drivers/infiniband/hw/mlx5/wr.c +++ b/drivers/infiniband/hw/mlx5/wr.c @@ -840,13 +840,15 @@ static int handle_psv(struct mlx5_ib_dev *dev, struct mlx5_ib_qp *qp, return err; } -static int handle_reg_mr_integrity(struct mlx5_ib_dev *dev, - struct mlx5_ib_qp *qp, - const struct ib_send_wr *wr, - struct mlx5_wqe_ctrl_seg **ctrl, void **seg, - int *size, void **cur_edge, - unsigned int *idx, int nreq, u8 fence, - u8 next_fence) +static noinline_for_stack int handle_reg_mr_integrity(struct mlx5_ib_dev *dev, + struct mlx5_ib_qp *qp, + const struct ib_send_wr *wr, + struct mlx5_wqe_ctrl_seg **ctrl, + void **seg, + int *size, void **cur_edge, + unsigned int *idx, int nreq, + u8 fence, + u8 next_fence) { struct mlx5_ib_mr *mr; struct mlx5_ib_mr *pi_mr; From 1ca22c6aa006b05143367268066fb74e32cfe66b Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Mon, 18 May 2026 02:58:37 +0000 Subject: [PATCH 0241/1054] reset: spacemit: k3: fix USB2 ahb reset According to SpacemiT K3's updated docs, the USB2 ahb reset and USB2 bus clock enable bit was wrongly swapped, the correct one should be: Register : APMU_USB_CLK_RES_CTRL bit[1] : usb2_port_bus_clk_en bit[0] : usb2_port_ahb_rstn Fixes: a0e0c2f8c5f3 ("reset: spacemit: k3: Decouple composite reset lines") Reported-by: Junzhong Pan Signed-off-by: Yixun Lan Reviewed-by: Philipp Zabel Signed-off-by: Philipp Zabel --- drivers/reset/spacemit/reset-spacemit-k3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/reset/spacemit/reset-spacemit-k3.c b/drivers/reset/spacemit/reset-spacemit-k3.c index 9841f5e057b2..2e87f320cf11 100644 --- a/drivers/reset/spacemit/reset-spacemit-k3.c +++ b/drivers/reset/spacemit/reset-spacemit-k3.c @@ -112,7 +112,7 @@ static const struct ccu_reset_data k3_apmu_resets[] = { [RESET_APMU_SDH0] = RESET_DATA(APMU_SDH0_CLK_RES_CTRL, 0, BIT(1)), [RESET_APMU_SDH1] = RESET_DATA(APMU_SDH1_CLK_RES_CTRL, 0, BIT(1)), [RESET_APMU_SDH2] = RESET_DATA(APMU_SDH2_CLK_RES_CTRL, 0, BIT(1)), - [RESET_APMU_USB2_AHB] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(1)), + [RESET_APMU_USB2_AHB] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(0)), [RESET_APMU_USB2_VCC] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(2)), [RESET_APMU_USB2_PHY] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(3)), [RESET_APMU_USB3_A_AHB] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(5)), From ab45ecfab540653f1ff4a8d2f2da055c82cf1640 Mon Sep 17 00:00:00 2001 From: Tanmay Kathpalia Date: Sat, 27 Jun 2026 13:14:46 -0700 Subject: [PATCH 0242/1054] dt-bindings: reset: altr: add COMBOPHY_RESET for Agilex5 Add COMBOPHY_RESET definition at index 38 for the combo PHY reset control on Altera Agilex5 SoCs. This reset is used by peripherals such as the SD/eMMC controller that share the combo PHY. Signed-off-by: Tanmay Kathpalia Acked-by: Conor Dooley Reviewed-by: Philipp Zabel Signed-off-by: Philipp Zabel --- include/dt-bindings/reset/altr,rst-mgr-s10.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/dt-bindings/reset/altr,rst-mgr-s10.h b/include/dt-bindings/reset/altr,rst-mgr-s10.h index 04c4d0c6fd34..c2505b9eb63e 100644 --- a/include/dt-bindings/reset/altr,rst-mgr-s10.h +++ b/include/dt-bindings/reset/altr,rst-mgr-s10.h @@ -22,7 +22,7 @@ #define USB0_RESET 35 #define USB1_RESET 36 #define NAND_RESET 37 -/* 38 is empty */ +#define COMBOPHY_RESET 38 #define SDMMC_RESET 39 #define EMAC0_OCP_RESET 40 #define EMAC1_OCP_RESET 41 From 1a8c89f8c112c75e84ff9a140f969e372aed0c9a Mon Sep 17 00:00:00 2001 From: Zhao Dongdong Date: Wed, 17 Jun 2026 11:16:27 +0800 Subject: [PATCH 0243/1054] reset: sunxi: fix memory region leak on ioremap failure In sunxi_reset_init(), when ioremap() fails, the memory region obtained via request_mem_region() is not released, leading to a resource leak. Add an err_mem_region label to properly release the memory region before freeing the data structure. Fixes: 8f1ae77f4666 ("reset: Add Allwinner SoCs Reset Controller Driver") Cc: stable@vger.kernel.org Signed-off-by: Zhao Dongdong Reviewed-by: Philipp Zabel Acked-by: Jernej Skrabec Signed-off-by: Philipp Zabel --- drivers/reset/reset-sunxi.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/reset/reset-sunxi.c b/drivers/reset/reset-sunxi.c index 2544de6576e4..2f6df7707cad 100644 --- a/drivers/reset/reset-sunxi.c +++ b/drivers/reset/reset-sunxi.c @@ -44,7 +44,7 @@ static int sunxi_reset_init(struct device_node *np) data->membase = ioremap(res.start, size); if (!data->membase) { ret = -ENOMEM; - goto err_alloc; + goto err_mem_region; } spin_lock_init(&data->lock); @@ -57,6 +57,8 @@ static int sunxi_reset_init(struct device_node *np) return reset_controller_register(&data->rcdev); +err_mem_region: + release_mem_region(res.start, size); err_alloc: kfree(data); return ret; From 71827776667f4e4677a4fa806bcfb24d4b8dd9d7 Mon Sep 17 00:00:00 2001 From: Robby Cai Date: Fri, 19 Jun 2026 15:31:15 +0800 Subject: [PATCH 0244/1054] reset: imx7: Correct polarity of MIPI CSI resets on i.MX8MQ On i.MX8MQ, the MIPI CSI reset lines are active-low and not self-clearing. Writing '0' asserts reset and it remains asserted until explicitly deasserted by software. This driver previously treated the MIPI CSI reset signals as active-high, which led to incorrect reset assert/deassert sequencing. This issue was exposed by commit 6d79bb8fd2aa ("media: imx8mq-mipi-csi2: Explicitly release reset"). Fix this by reflecting the correct reset polarity and ensuring proper reset handling. Fixes: c979dbf59987 ("reset: imx7: Add support for i.MX8MQ IP block variant") Cc: stable@vger.kernel.org # 6d79bb8fd2aa: media: imx8mq-mipi-csi2: Explicitly release reset Reviewed-by: Philipp Zabel Signed-off-by: Robby Cai Reviewed-by: Guoniu Zhou Reviewed-by: Frank Li Signed-off-by: Philipp Zabel --- drivers/reset/reset-imx7.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/reset/reset-imx7.c b/drivers/reset/reset-imx7.c index dd01fe11c5cb..a3cb8244d76a 100644 --- a/drivers/reset/reset-imx7.c +++ b/drivers/reset/reset-imx7.c @@ -236,6 +236,12 @@ static int imx8mq_reset_set(struct reset_controller_dev *rcdev, case IMX8MQ_RESET_PCIE_CTRL_APPS_EN: case IMX8MQ_RESET_PCIE2_CTRL_APPS_EN: + case IMX8MQ_RESET_MIPI_CSI1_CORE_RESET: + case IMX8MQ_RESET_MIPI_CSI1_PHY_REF_RESET: + case IMX8MQ_RESET_MIPI_CSI1_ESC_RESET: + case IMX8MQ_RESET_MIPI_CSI2_CORE_RESET: + case IMX8MQ_RESET_MIPI_CSI2_PHY_REF_RESET: + case IMX8MQ_RESET_MIPI_CSI2_ESC_RESET: case IMX8MQ_RESET_MIPI_DSI_PCLK_RESET_N: case IMX8MQ_RESET_MIPI_DSI_ESC_RESET_N: case IMX8MQ_RESET_MIPI_DSI_DPI_RESET_N: From f3cf725cd284b7912d5522babb44721bf38c8887 Mon Sep 17 00:00:00 2001 From: Nan Li Date: Mon, 22 Jun 2026 10:08:35 +0100 Subject: [PATCH 0245/1054] afs: handle CB.InitCallBackState3 requests without a server record The cache manager callback path now attaches the server record to an incoming call through the rxrpc peer's app data. That association is not guaranteed to exist for every callback request, and most callback handlers already tolerate that case. Make CB.InitCallBackState3 follow the same pattern by checking whether a server record was attached before using it. If the peer is not mapped to a server record, trace the request and ignore it, matching the existing behaviour for other unmatched callback requests. This keeps the callback handler consistent with the rest of the cache manager service and avoids depending on peer state that may not be available for a given request. Fixes: 40e8b52fe8c8 ("afs: Use the per-peer app data provided by rxrpc") Cc: stable@kernel.org Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Xin Liu Signed-off-by: Nan Li Signed-off-by: Ren Wei Signed-off-by: David Howells Link: https://patch.msgid.link/20260622090856.2746629-2-dhowells@redhat.com cc: Marc Dionne cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner (Amutable) --- fs/afs/cmservice.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fs/afs/cmservice.c b/fs/afs/cmservice.c index 5540ae1cad59..263c60c811a5 100644 --- a/fs/afs/cmservice.c +++ b/fs/afs/cmservice.c @@ -364,6 +364,11 @@ static int afs_deliver_cb_init_call_back_state3(struct afs_call *call) if (!afs_check_call_state(call, AFS_CALL_SV_REPLYING)) return afs_io_error(call, afs_io_error_cm_reply); + if (!call->server) { + trace_afs_cm_no_server_u(call, call->request); + return 0; + } + if (memcmp(call->request, &call->server->_uuid, sizeof(call->server->_uuid)) != 0) { pr_notice("Callback UUID does not match fileserver UUID\n"); trace_afs_cm_no_server_u(call, call->request); From 539dce1144651f7976fa418e618b0b574bf15eeb Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 15 Jun 2026 14:52:18 +0200 Subject: [PATCH 0246/1054] fs: refuse O_TMPFILE creation with an unmapped fsuid or fsgid vfs_tmpfile() never checked that the caller's fsuid and fsgid map into the filesystem. On an idmapped mount whose idmapping does not cover the caller's fs{u,g}id, the ->tmpfile() instance initializes the new inode through inode_init_owner(), where mapped_fsuid()/mapped_fsgid() return INVALID_UID/INVALID_GID, and the tmpfile ends up owned by (uid_t)-1. Every other creation path already refuses this: may_o_create() (O_CREAT) and may_create_dentry() (mkdir, mknod, symlink, link) bail out with -EOVERFLOW via fsuidgid_has_mapping() precisely so that an object cannot be created with an owner the filesystem cannot represent. An O_TMPFILE is no exception: it is created I_LINKABLE and linkat(2) can splice it into the namespace afterwards, so the same guarantee must hold. Add the missing fsuidgid_has_mapping() check to vfs_tmpfile(). On a non-idmapped mount the caller's fs{u,g}id always map in the superblock's user namespace, so this is a no-op there and only takes effect on an idmapped mount that does not map the caller. It applies to every filesystem that sets FS_ALLOW_IDMAP and implements ->tmpfile() (tmpfs, ext4, btrfs, xfs, f2fs, ...), and to overlayfs, whose upper-layer tmpfile creation funnels through vfs_tmpfile() via backing_tmpfile_open(). Fixes: 8e5389132ab4 ("fs: introduce fsuidgid_has_mapping() helper") Link: https://patch.msgid.link/20260615-work-idmapped-tmpfile-v1-1-754a94d81f83@kernel.org Reviewed-by: Jan Kara Signed-off-by: Christian Brauner (Amutable) --- fs/namei.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/namei.c b/fs/namei.c index 5cc9f0f466b8..19ce43c9a6e6 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -4736,6 +4736,10 @@ int vfs_tmpfile(struct mnt_idmap *idmap, int error; int open_flag = file->f_flags; + /* A tmpfile is I_LINKABLE, so guard its owner like may_o_create(). */ + if (!fsuidgid_has_mapping(dir->i_sb, idmap)) + return -EOVERFLOW; + /* we want directory to be writable */ error = inode_permission(idmap, dir, MAY_WRITE | MAY_EXEC); if (error) From 4897cb71d4ab1f7e1a214adb1e4b80176702368d Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 22 Jun 2026 10:08:36 +0100 Subject: [PATCH 0247/1054] afs: Fix error code in afs_extract_vl_addrs() The error codes on these paths are only set on the first iteration through the loop. Set the correct error code on every iteration. Fixes: 0a5143f2f89c ("afs: Implement VL server rotation") Signed-off-by: Dan Carpenter Signed-off-by: David Howells Link: https://patch.msgid.link/20260622090856.2746629-3-dhowells@redhat.com cc: Marc Dionne cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner (Amutable) --- fs/afs/vl_list.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/afs/vl_list.c b/fs/afs/vl_list.c index 3e4966915ea4..003889cf0f18 100644 --- a/fs/afs/vl_list.c +++ b/fs/afs/vl_list.c @@ -92,7 +92,7 @@ static struct afs_addr_list *afs_extract_vl_addrs(struct afs_net *net, { struct afs_addr_list *alist; const u8 *b = *_b; - int ret = -EINVAL; + int ret; alist = afs_alloc_addrlist(nr_addrs); if (!alist) @@ -110,6 +110,7 @@ static struct afs_addr_list *afs_extract_vl_addrs(struct afs_net *net, case DNS_ADDRESS_IS_IPV4: if (end - b < 4) { _leave(" = -EINVAL [short inet]"); + ret = -EINVAL; goto error; } memcpy(x, b, 4); @@ -122,6 +123,7 @@ static struct afs_addr_list *afs_extract_vl_addrs(struct afs_net *net, case DNS_ADDRESS_IS_IPV6: if (end - b < 16) { _leave(" = -EINVAL [short inet6]"); + ret = -EINVAL; goto error; } memcpy(x, b, 16); From d943e68edc5cb98192d38e31373bb6b6a73230c6 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 15 Jun 2026 14:52:19 +0200 Subject: [PATCH 0248/1054] selftests/filesystems: test O_TMPFILE creation on idmapped mounts Add a regression test for the fsuidgid_has_mapping() check in vfs_tmpfile(). It idmaps a detached tmpfs mount so that the caller-visible id range [0, 10000) maps onto the on-disk range [10000, 20000) and checks that: - a caller whose fsuid/fsgid fall outside that range cannot create an O_TMPFILE through the mount and gets -EOVERFLOW instead of an inode owned by (uid_t)-1; - a mapped caller can create an O_TMPFILE, link it into the namespace, and the ownership round-trips through the mount idmap: it is reported as 0 through the mount and stored as 10000 on the underlying tmpfs. The test runs entirely as root and uses setfsuid()/setfsgid() to become the unmapped caller, so it needs no helper user. The layer directory is world-writable so that an unmapped caller still clears the directory permission check and reaches the fsuidgid_has_mapping() test. Link: https://patch.msgid.link/20260615-work-idmapped-tmpfile-v1-2-754a94d81f83@kernel.org Reviewed-by: Jan Kara Signed-off-by: Christian Brauner (Amutable) --- .../testing/selftests/filesystems/.gitignore | 1 + tools/testing/selftests/filesystems/Makefile | 4 + .../selftests/filesystems/idmapped_tmpfile.c | 168 ++++++++++++++++++ 3 files changed, 173 insertions(+) create mode 100644 tools/testing/selftests/filesystems/idmapped_tmpfile.c diff --git a/tools/testing/selftests/filesystems/.gitignore b/tools/testing/selftests/filesystems/.gitignore index 64ac0dfa46b7..a78f894157de 100644 --- a/tools/testing/selftests/filesystems/.gitignore +++ b/tools/testing/selftests/filesystems/.gitignore @@ -5,3 +5,4 @@ fclog file_stressor anon_inode_test kernfs_test +idmapped_tmpfile diff --git a/tools/testing/selftests/filesystems/Makefile b/tools/testing/selftests/filesystems/Makefile index 85427d7f19b9..a7ec2ba2dd83 100644 --- a/tools/testing/selftests/filesystems/Makefile +++ b/tools/testing/selftests/filesystems/Makefile @@ -2,6 +2,10 @@ CFLAGS += $(KHDR_INCLUDES) TEST_GEN_PROGS := devpts_pts file_stressor anon_inode_test kernfs_test fclog +TEST_GEN_PROGS += idmapped_tmpfile TEST_GEN_PROGS_EXTENDED := dnotify_test include ../lib.mk + +$(OUTPUT)/idmapped_tmpfile: LDLIBS += -lcap +$(OUTPUT)/idmapped_tmpfile: utils.c diff --git a/tools/testing/selftests/filesystems/idmapped_tmpfile.c b/tools/testing/selftests/filesystems/idmapped_tmpfile.c new file mode 100644 index 000000000000..bc411ab8281e --- /dev/null +++ b/tools/testing/selftests/filesystems/idmapped_tmpfile.c @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: GPL-2.0 +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "kselftest_harness.h" +#include "wrappers.h" +#include "utils.h" + +/* + * The test mount maps caller-visible ids [0, MAP_RANGE) onto the on-disk range + * [MAP_HOST, MAP_HOST + MAP_RANGE). An id outside [0, MAP_RANGE) therefore has + * no mapping in the mount and is not representable in the filesystem. + */ +#define MAP_HOST 10000 +#define MAP_RANGE 10000 +#define UNMAPPED 50000 + +#ifndef MOUNT_ATTR_IDMAP +#define MOUNT_ATTR_IDMAP 0x00100000 +#endif + +#ifndef __NR_mount_setattr +#define __NR_mount_setattr 442 +#endif + +static inline int sys_mount_setattr(int dfd, const char *path, + unsigned int flags, + struct mount_attr *attr, size_t size) +{ + return syscall(__NR_mount_setattr, dfd, path, flags, attr, size); +} + +/* + * Clone @path into a detached mount idmapped so that caller-visible ids + * [0, MAP_RANGE) map onto the on-disk ids [MAP_HOST, MAP_HOST + MAP_RANGE). + * Returns the mount fd, or -1 if idmapped mounts are not available. + */ +static int idmapped_clone(const char *path) +{ + struct mount_attr attr = { + .attr_set = MOUNT_ATTR_IDMAP, + }; + int fd_tree, userns_fd, ret; + + fd_tree = sys_open_tree(AT_FDCWD, path, + OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC); + if (fd_tree < 0) + return -1; + + userns_fd = get_userns_fd(MAP_HOST, 0, MAP_RANGE); + if (userns_fd < 0) { + close(fd_tree); + return -1; + } + + attr.userns_fd = userns_fd; + ret = sys_mount_setattr(fd_tree, "", AT_EMPTY_PATH, &attr, sizeof(attr)); + close(userns_fd); + if (ret) { + close(fd_tree); + return -1; + } + + return fd_tree; +} + +FIXTURE(idmapped_tmpfile) { + char dir[64]; /* non-idmapped path to the layer directory */ +}; + +FIXTURE_SETUP(idmapped_tmpfile) +{ + /* Private mount namespace so test mounts need no cleanup. */ + ASSERT_EQ(unshare(CLONE_NEWNS), 0); + ASSERT_EQ(sys_mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL), 0); + ASSERT_EQ(sys_mount("tmpfs", "/tmp", "tmpfs", 0, NULL), 0); + + snprintf(self->dir, sizeof(self->dir), "/tmp/d"); + ASSERT_EQ(mkdir(self->dir, 0777), 0); + /* World-writable so an unmapped caller still passes permission(). */ + ASSERT_EQ(chmod(self->dir, 0777), 0); +} + +FIXTURE_TEARDOWN(idmapped_tmpfile) +{ +} + +/* + * A caller whose fsuid/fsgid have no mapping in the idmapped mount must not be + * able to create an O_TMPFILE. Without the check in vfs_tmpfile() the inode + * would be created owned by (uid_t)-1 and could then be linked into the + * namespace. + */ +TEST_F(idmapped_tmpfile, unmapped_caller_is_refused) +{ + int mfd, fd; + + mfd = idmapped_clone(self->dir); + if (mfd < 0) + SKIP(return, "idmapped mounts not supported"); + + /* Become a caller outside the mount's [0, MAP_RANGE) range. */ + setfsgid(UNMAPPED); + setfsuid(UNMAPPED); + ASSERT_EQ(setfsuid(-1), UNMAPPED); + + fd = openat(mfd, ".", O_TMPFILE | O_WRONLY, 0644); + ASSERT_LT(fd, 0); + EXPECT_EQ(errno, EOVERFLOW); + if (fd >= 0) + close(fd); + + EXPECT_EQ(close(mfd), 0); +} + +/* + * A mapped caller can create an O_TMPFILE and link it into the namespace; the + * ownership round-trips through the mount idmap. This is what makes refusing + * the unmapped case above necessary in the first place. + */ +TEST_F(idmapped_tmpfile, mapped_caller_creates_and_links) +{ + char path[PATH_MAX]; + struct stat st; + int mfd, fd; + + mfd = idmapped_clone(self->dir); + if (mfd < 0) + SKIP(return, "idmapped mounts not supported"); + + /* Caller is uid/gid 0, which maps to MAP_HOST through the mount. */ + fd = openat(mfd, ".", O_TMPFILE | O_RDWR, 0600); + ASSERT_GE(fd, 0); + + ASSERT_EQ(fstat(fd, &st), 0); + EXPECT_EQ(st.st_uid, 0); + EXPECT_EQ(st.st_gid, 0); + + /* The tmpfile is linkable: splice it into the directory. */ + ASSERT_EQ(linkat(fd, "", mfd, "linked", AT_EMPTY_PATH), 0); + EXPECT_EQ(close(fd), 0); + + ASSERT_EQ(fstatat(mfd, "linked", &st, 0), 0); + EXPECT_EQ(st.st_uid, 0); + EXPECT_EQ(st.st_gid, 0); + + /* On the underlying, non-idmapped tmpfs it is stored as MAP_HOST. */ + snprintf(path, sizeof(path), "%s/linked", self->dir); + ASSERT_EQ(stat(path, &st), 0); + EXPECT_EQ(st.st_uid, MAP_HOST); + EXPECT_EQ(st.st_gid, MAP_HOST); + + EXPECT_EQ(close(mfd), 0); +} + +TEST_HARNESS_MAIN From 0b70716081c6462be9b2928ad736d0d527b09678 Mon Sep 17 00:00:00 2001 From: Matvey Kovalev Date: Mon, 22 Jun 2026 10:08:37 +0100 Subject: [PATCH 0249/1054] afs: fix NULL pointer dereference in afs_get_tree() afs_alloc_sbi() uses kzalloc for memory allocation. And, if ctx->dyn_root is not null, as->cell and as->volume are null. In trace_afs_get_tree() they are dereferenced. KASAN error message: KASAN: null-ptr-deref in range [0x0000000000000000-0x0000000000000007] CPU: 2 PID: 18478 Comm: syz-executor.7 Not tainted 5.10.246-syzkaller #0 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.12.0-1 04/01/2014 RIP: 0010:perf_trace_afs_get_tree+0x1d9/0x550 include/trace/events/afs.h:1365 Call Trace: trace_afs_get_tree include/trace/events/afs.h:1365 [inline] afs_get_tree+0x922/0x1350 fs/afs/super.c:599 vfs_get_tree+0x8e/0x300 fs/super.c:1572 do_new_mount fs/namespace.c:3011 [inline] path_mount+0x14a5/0x2220 fs/namespace.c:3341 do_mount fs/namespace.c:3354 [inline] __do_sys_mount fs/namespace.c:3562 [inline] __se_sys_mount fs/namespace.c:3539 [inline] __x64_sys_mount+0x283/0x300 fs/namespace.c:3539 do_syscall_64+0x33/0x50 arch/x86/entry/common.c:46 entry_SYSCALL_64_after_hwframe+0x67/0xd1 Found by Linux Verification Center (linuxtesting.org) with Syzkaller. Fixes: 80548b03991f5 ("afs: Add more tracepoints") Cc: stable@vger.kernel.org Signed-off-by: Matvey Kovalev Signed-off-by: David Howells Link: https://patch.msgid.link/20260622090856.2746629-4-dhowells@redhat.com cc: Marc Dionne cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner (Amutable) --- fs/afs/super.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/afs/super.c b/fs/afs/super.c index 942f3e9800d7..dec091e569c4 100644 --- a/fs/afs/super.c +++ b/fs/afs/super.c @@ -587,7 +587,8 @@ static int afs_get_tree(struct fs_context *fc) } fc->root = dget(sb->s_root); - trace_afs_get_tree(as->cell, as->volume); + if (!ctx->dyn_root) + trace_afs_get_tree(as->cell, as->volume); _leave(" = 0 [%p]", sb); return 0; From 733a984a4ee7345325e47efb505eebfe67b299bc Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 22 Jun 2026 10:08:38 +0100 Subject: [PATCH 0250/1054] afs: Fix double netfs initialisation in afs_root_iget() Fix afs_root_iget() to leave initialisation of the netfs_inode part of the afs_vnode to afs_inode_init_from_status(). Fixes: bc899ee1c898 ("netfs: Add a netfs inode context") Closes: https://sashiko.dev/#/patchset/20260609081738.770127-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260622090856.2746629-5-dhowells@redhat.com cc: Marc Dionne cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner (Amutable) --- fs/afs/inode.c | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/afs/inode.c b/fs/afs/inode.c index 3f48458694ba..a88995629d72 100644 --- a/fs/afs/inode.c +++ b/fs/afs/inode.c @@ -566,7 +566,6 @@ struct inode *afs_root_iget(struct super_block *sb, struct key *key) vnode = AFS_FS_I(inode); vnode->cb_v_check = atomic_read(&as->volume->cb_v_break); - afs_set_netfs_context(vnode); op = afs_alloc_operation(key, as->volume); if (IS_ERR(op)) { From 81e985b4c3a6cbcc443fcdcd3ebda7fcc845d459 Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 22 Jun 2026 10:08:39 +0100 Subject: [PATCH 0251/1054] afs: Remove setting of AS_RELEASE_ALWAYS for symlinks and mountpoints Regular AFS files correctly use afs_file_aops which have release_folio set as netfs_release_folio, so AS_RELEASE_ALWAYS is valid for them when fscache is enabled (set via afs_vnode_set_cache()). Symlinks and mountpoints in AFS use afs_dir_aops, which does not provide a release_folio callback. However, afs_apply_status() unconditionally calls mapping_set_release_always() for these. In such case when memory management code attempts to release folios, filemap_release_folio() checks folio_needs_release() which returns true due to AS_RELEASE_ALWAYS being set. Since there is no release_folio callback, it falls through to try_to_free_buffers(), which at present expects buffer_heads to be not null. For symlinks and mountpoints without buffer_heads, this causes pointer dereference. [dh: Added more bits that were missed] Fixes: eae9e78951bb ("afs: Use netfslib for symlinks, allowing them to be cached") Signed-off-by: Deepakkumar Karn Signed-off-by: David Howells Link: https://patch.msgid.link/20260622090856.2746629-6-dhowells@redhat.com cc: Marc Dionne cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner (Amutable) --- fs/afs/inode.c | 7 +++---- fs/afs/internal.h | 2 -- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/fs/afs/inode.c b/fs/afs/inode.c index a88995629d72..54ac6ec21daf 100644 --- a/fs/afs/inode.c +++ b/fs/afs/inode.c @@ -52,9 +52,9 @@ static noinline void dump_vnode(struct afs_vnode *vnode, struct afs_vnode *paren /* * Set parameters for the netfs library */ -static void afs_set_netfs_context(struct afs_vnode *vnode) +static void afs_set_netfs_context(struct afs_vnode *vnode, bool is_file) { - netfs_inode_init(&vnode->netfs, &afs_req_ops, true); + netfs_inode_init(&vnode->netfs, &afs_req_ops, is_file); } /* @@ -126,7 +126,6 @@ static int afs_inode_init_from_status(struct afs_operation *op, } inode->i_mapping->a_ops = &afs_symlink_aops; inode_nohighmem(inode); - mapping_set_release_always(inode->i_mapping); break; default: dump_vnode(vnode, op->file[0].vnode != vnode ? op->file[0].vnode : NULL); @@ -136,7 +135,7 @@ static int afs_inode_init_from_status(struct afs_operation *op, i_size_write(inode, status->size); inode_set_bytes(inode, status->size); - afs_set_netfs_context(vnode); + afs_set_netfs_context(vnode, status->type == AFS_FTYPE_FILE); vnode->invalid_before = status->data_version; trace_afs_set_dv(vnode, status->data_version); diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 0b72a8566299..785c646856d7 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -750,8 +750,6 @@ static inline void afs_vnode_set_cache(struct afs_vnode *vnode, { #ifdef CONFIG_AFS_FSCACHE vnode->netfs.cache = cookie; - if (cookie) - mapping_set_release_always(vnode->netfs.inode.i_mapping); #endif } From 35b177ef541ae8eefbfbf679c3476bc3fb1eb83c Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 22 Jun 2026 10:08:40 +0100 Subject: [PATCH 0252/1054] afs: Fix directory inode initialisation order Fix afs_inode_init_from_status() to call afs_set_netfs_context() before the switch to do file type-specific initialisation because local directory changes don't get uploaded to the server, only stored in the cache. This requires that the file size be set before, so move that up too. Without this, NETFS_ICTX_SINGLE_NO_UPLOAD as set on directories gets clobbered. Closes: https://sashiko.dev/#/patchset/20260618074903.2374756-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260622090856.2746629-7-dhowells@redhat.com Fixes: 6dd80936618c ("afs: Use netfslib for directories") cc: Marc Dionne cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner (Amutable) --- fs/afs/inode.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/afs/inode.c b/fs/afs/inode.c index 54ac6ec21daf..51c28f148845 100644 --- a/fs/afs/inode.c +++ b/fs/afs/inode.c @@ -93,6 +93,10 @@ static int afs_inode_init_from_status(struct afs_operation *op, inode->i_gid = make_kgid(&init_user_ns, status->group); set_nlink(&vnode->netfs.inode, status->nlink); + i_size_write(inode, status->size); + inode_set_bytes(inode, status->size); + afs_set_netfs_context(vnode, status->type == AFS_FTYPE_FILE); + switch (status->type) { case AFS_FTYPE_FILE: inode->i_mode = S_IFREG | (status->mode & S_IALLUGO); @@ -133,10 +137,6 @@ static int afs_inode_init_from_status(struct afs_operation *op, return afs_protocol_error(NULL, afs_eproto_file_type); } - i_size_write(inode, status->size); - inode_set_bytes(inode, status->size); - afs_set_netfs_context(vnode, status->type == AFS_FTYPE_FILE); - vnode->invalid_before = status->data_version; trace_afs_set_dv(vnode, status->data_version); inode_set_iversion_raw(&vnode->netfs.inode, status->data_version); From cb39654926f8e7a08ecc1dcb3941628855275940 Mon Sep 17 00:00:00 2001 From: Zilin Guan Date: Mon, 22 Jun 2026 10:08:41 +0100 Subject: [PATCH 0253/1054] afs: use kvfree() to free memory allocated by kvcalloc() op->more_files is allocated with kvcalloc() but released via afs_put_operation(), which uses kfree() internally. This mismach prevents the resource from being released properly and may lead to undefined behavior. Fix this by using kvfree() to free op->more_files to match its allocation method. Fixes: e49c7b2f6de7 ("afs: Build an abstraction around an "operation" concept") Signed-off-by: Zilin Guan Signed-off-by: David Howells Link: https://patch.msgid.link/20260622090856.2746629-8-dhowells@redhat.com cc: Marc Dionne cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner (Amutable) --- fs/afs/fs_operation.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/afs/fs_operation.c b/fs/afs/fs_operation.c index c0dbbc6d3716..20801b29521d 100644 --- a/fs/afs/fs_operation.c +++ b/fs/afs/fs_operation.c @@ -348,7 +348,7 @@ int afs_put_operation(struct afs_operation *op) for (i = 0; i < op->nr_files - 2; i++) if (op->more_files[i].put_vnode) iput(&op->more_files[i].vnode->netfs.inode); - kfree(op->more_files); + kvfree(op->more_files); } if (op->estate) { From a58edda50a3ec08e6adac1d04dc3e488494e412d Mon Sep 17 00:00:00 2001 From: Li RongQing Date: Mon, 22 Jun 2026 10:08:42 +0100 Subject: [PATCH 0254/1054] afs: Remove erroneous seq |= 1 in volume lookup loop The `seq |= 1` operation in the volume lookup loop is incorrect because: seq is already incremented at start, making it odd in next iteration which triggers lock, but The `|= 1` operation causes seq to be even and unintended lockless operation Remove this erroneous operation to maintain proper lock sequencing. Fixes: 32222f09782f ("afs: Apply server breaks to mmap'd files in the call processor") Signed-off-by: Li RongQing Signed-off-by: David Howells Link: https://patch.msgid.link/20260622090856.2746629-9-dhowells@redhat.com Reviewed-by: Oleg Nesterov cc: Marc Dionne cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner (Amutable) --- fs/afs/callback.c | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/afs/callback.c b/fs/afs/callback.c index 894d2bad6b6c..833ac3178ddc 100644 --- a/fs/afs/callback.c +++ b/fs/afs/callback.c @@ -140,7 +140,6 @@ static struct afs_volume *afs_lookup_volume_rcu(struct afs_cell *cell, break; if (!need_seqretry(&cell->volume_lock, seq)) break; - seq |= 1; /* Want a lock next time */ } done_seqretry(&cell->volume_lock, seq); From 680ba02073415962446e79b10e15ad3b8c87fec5 Mon Sep 17 00:00:00 2001 From: Yuto Ohnuki Date: Mon, 22 Jun 2026 10:08:43 +0100 Subject: [PATCH 0255/1054] afs: check for duplicate servers in VL server list The DNS response may contain the same server more than once. Check for duplicates by name and port before inserting into the list to avoid duplicate entries. Addresses the TODO comment in afs_extract_vlserver_list(). Signed-off-by: Yuto Ohnuki Signed-off-by: David Howells Link: https://patch.msgid.link/20260622090856.2746629-10-dhowells@redhat.com cc: Marc Dionne cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner (Amutable) --- fs/afs/vl_list.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/fs/afs/vl_list.c b/fs/afs/vl_list.c index 003889cf0f18..8e1cf6cdcf71 100644 --- a/fs/afs/vl_list.c +++ b/fs/afs/vl_list.c @@ -289,8 +289,20 @@ struct afs_vlserver_list *afs_extract_vlserver_list(struct afs_cell *cell, afs_put_addrlist(old, afs_alist_trace_put_vlserver_old); } + /* Check for duplicates in the server list */ + for (j = 0; j < vllist->nr_servers; j++) { + struct afs_vlserver *s = vllist->servers[j].server; - /* TODO: Might want to check for duplicates */ + if (s->name_len == server->name_len && + s->port == server->port && + strncasecmp(s->name, server->name, server->name_len) == 0) { + afs_put_vlserver(cell->net, server); + server = NULL; + break; + } + } + if (!server) + continue; /* Insertion-sort by priority and weight */ for (j = 0; j < vllist->nr_servers; j++) { From 2f79d1b93c62470fe02dbdc24770f1ae5a9e1be6 Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 22 Jun 2026 10:08:44 +0100 Subject: [PATCH 0256/1054] afs: Fix bulk lookup malfunction due to change in dir_emit() API afs_do_lookup() and afs_do_lookup_one() use the same directory parsing code as afs_readdir() and were supplying alternative dir_context actors to retrieve dirents, but because lookup needs the vnode's uniquifier as part of the reference, but not the DT flags, the uniquifier was being passed in the dt flags argument to the lookup actors. Unfortunately, commit c644bce62b9c, added to fix overlayfs with fuse, broke this by masking off part of the uniquifier. This doesn't matter enough to be directly noticeable, instead causing bulk advance inode lookups to fail (which are retried later) and may cause dir revalidation to malfunction if the uniquifier is changed by masking. Fix this by making the afs directory parsing code take special ->actor values of AFS_LOOKUP or AFS_LOOKUP_ONE instead that tell it to call afs_lookup_filldir() or afs_lookup_one_filldir() directly rather than going through dir_emit(). dir_emit() is still used for readdir. Fixes: c644bce62b9c ("readdir: require opt-in for d_type flags") Reported-by: Marc Dionne Signed-off-by: David Howells Link: https://patch.msgid.link/20260622090856.2746629-11-dhowells@redhat.com cc: Amir Goldstein cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner (Amutable) --- fs/afs/dir.c | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/fs/afs/dir.c b/fs/afs/dir.c index 498b99ccdf0e..6df56fe9163f 100644 --- a/fs/afs/dir.c +++ b/fs/afs/dir.c @@ -28,9 +28,11 @@ static int afs_d_revalidate(struct inode *dir, const struct qstr *name, static int afs_d_delete(const struct dentry *dentry); static void afs_d_iput(struct dentry *dentry, struct inode *inode); static bool afs_lookup_one_filldir(struct dir_context *ctx, const char *name, int nlen, - loff_t fpos, u64 ino, unsigned dtype); + u64 ino, u32 uniquifier); +#define AFS_LOOKUP_ONE ((filldir_t)0x123UL) static bool afs_lookup_filldir(struct dir_context *ctx, const char *name, int nlen, - loff_t fpos, u64 ino, unsigned dtype); + u64 ino, u32 uniquifier); +#define AFS_LOOKUP ((filldir_t)0x137UL) static int afs_create(struct mnt_idmap *idmap, struct inode *dir, struct dentry *dentry, umode_t mode, bool excl); static struct dentry *afs_mkdir(struct mnt_idmap *idmap, struct inode *dir, @@ -421,11 +423,18 @@ static int afs_dir_iterate_block(struct afs_vnode *dvnode, } /* found the next entry */ - if (!dir_emit(ctx, dire->u.name, nlen, - ntohl(dire->u.vnode), - (ctx->actor == afs_lookup_filldir || - ctx->actor == afs_lookup_one_filldir)? - ntohl(dire->u.unique) : DT_UNKNOWN)) { + if (ctx->actor == AFS_LOOKUP) { + if (!afs_lookup_filldir(ctx, dire->u.name, nlen, + ntohl(dire->u.vnode), + ntohl(dire->u.unique))) + return 0; + } else if (ctx->actor == AFS_LOOKUP_ONE) { + if (!afs_lookup_one_filldir(ctx, dire->u.name, nlen, + ntohl(dire->u.vnode), + ntohl(dire->u.unique))) + return 0; + } else if (!dir_emit(ctx, dire->u.name, nlen, + ntohl(dire->u.vnode), DT_UNKNOWN)) { _leave(" = 0 [full]"); return 0; } @@ -545,6 +554,7 @@ static int afs_readdir(struct file *file, struct dir_context *ctx) { afs_dataversion_t dir_version; + ctx->dt_flags_mask = UINT_MAX; return afs_dir_iterate(file_inode(file), ctx, file, &dir_version); } @@ -554,14 +564,14 @@ static int afs_readdir(struct file *file, struct dir_context *ctx) * uniquifier through dtype */ static bool afs_lookup_one_filldir(struct dir_context *ctx, const char *name, - int nlen, loff_t fpos, u64 ino, unsigned dtype) + int nlen, u64 ino, u32 uniquifier) { struct afs_lookup_one_cookie *cookie = container_of(ctx, struct afs_lookup_one_cookie, ctx); _enter("{%s,%u},%s,%u,,%llu,%u", cookie->name.name, cookie->name.len, name, nlen, - (unsigned long long) ino, dtype); + (unsigned long long) ino, uniquifier); /* insanity checks first */ BUILD_BUG_ON(sizeof(union afs_xdr_dir_block) != 2048); @@ -574,7 +584,7 @@ static bool afs_lookup_one_filldir(struct dir_context *ctx, const char *name, } cookie->fid.vnode = ino; - cookie->fid.unique = dtype; + cookie->fid.unique = uniquifier; cookie->found = 1; _leave(" = false [found]"); @@ -591,7 +601,7 @@ static int afs_do_lookup_one(struct inode *dir, const struct qstr *name, { struct afs_super_info *as = dir->i_sb->s_fs_info; struct afs_lookup_one_cookie cookie = { - .ctx.actor = afs_lookup_one_filldir, + .ctx.actor = AFS_LOOKUP_ONE, .name = *name, .fid.vid = as->volume->vid }; @@ -622,14 +632,14 @@ static int afs_do_lookup_one(struct inode *dir, const struct qstr *name, * uniquifier through dtype */ static bool afs_lookup_filldir(struct dir_context *ctx, const char *name, - int nlen, loff_t fpos, u64 ino, unsigned dtype) + int nlen, u64 ino, u32 uniquifier) { struct afs_lookup_cookie *cookie = container_of(ctx, struct afs_lookup_cookie, ctx); _enter("{%s,%u},%s,%u,,%llu,%u", cookie->name.name, cookie->name.len, name, nlen, - (unsigned long long) ino, dtype); + (unsigned long long) ino, uniquifier); /* insanity checks first */ BUILD_BUG_ON(sizeof(union afs_xdr_dir_block) != 2048); @@ -637,7 +647,7 @@ static bool afs_lookup_filldir(struct dir_context *ctx, const char *name, if (cookie->nr_fids < 50) { cookie->fids[cookie->nr_fids].vnode = ino; - cookie->fids[cookie->nr_fids].unique = dtype; + cookie->fids[cookie->nr_fids].unique = uniquifier; cookie->nr_fids++; } @@ -778,7 +788,7 @@ static struct inode *afs_do_lookup(struct inode *dir, struct dentry *dentry) for (i = 0; i < ARRAY_SIZE(cookie->fids); i++) cookie->fids[i].vid = dvnode->fid.vid; - cookie->ctx.actor = afs_lookup_filldir; + cookie->ctx.actor = AFS_LOOKUP; cookie->name = dentry->d_name; cookie->nr_fids = 2; /* slot 1 is saved for the fid we actually want * and slot 0 for the directory */ From c9c3b615a462a4023bd148f02c564e175ed10502 Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 22 Jun 2026 10:08:45 +0100 Subject: [PATCH 0257/1054] afs: Fix misplaced inc of net->cells_outstanding Fix net->cells_outstanding being incremented before the check for failure of idr_alloc_cyclic(), leaving the count incremented on error. Fixes: 88c853c3f5c0 ("afs: Fix cell refcounting by splitting the usage counter") Reported-by: Hillf Danton Signed-off-by: David Howells Link: https://patch.msgid.link/20260622090856.2746629-12-dhowells@redhat.com cc: Marc Dionne cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner (Amutable) --- fs/afs/cell.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/afs/cell.c b/fs/afs/cell.c index 9738684dbdd2..e0fab1609f27 100644 --- a/fs/afs/cell.c +++ b/fs/afs/cell.c @@ -205,11 +205,11 @@ static struct afs_cell *afs_alloc_cell(struct afs_net *net, cell->dns_source = vllist->source; cell->dns_status = vllist->status; smp_store_release(&cell->dns_lookup_count, 1); /* vs source/status */ - atomic_inc(&net->cells_outstanding); ret = idr_alloc_cyclic(&net->cells_dyn_ino, cell, 2, INT_MAX / 2, GFP_KERNEL); if (ret < 0) goto error; + atomic_inc(&net->cells_outstanding); cell->dynroot_ino = ret; cell->debug_id = atomic_inc_return(&cell_debug_id); From 5597fbd1e7c161914f20315a726e54025b0fdadb Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 22 Jun 2026 10:08:46 +0100 Subject: [PATCH 0258/1054] afs: Fix reinitialisation of the inode, in particular ->lock_work It seems that initalising afs_vnode::lock_work a single time in the slab's init function isn't sufficient for work_structs. This results in the DEBUG_OBJECTS debugging stuff producing a warning occasionally when running the generic/131 xfstest: ODEBUG: activate not available (active state 0) object: 0000000016d8760f object type: work_struct hint: afs_lock_work+0x0/0x220 WARNING: lib/debugobjects.c:629 at debug_print_object+0x4b/0x90, CPU#3: locktest/7695 ... CPU: 3 UID: 0 PID: 7695 Comm: locktest Tainted: G S 7.1.0-build3+ #2771 PREEMPT ... RIP: 0010:debug_print_object+0x65/0x90 ... Call Trace: ? __pfx_afs_lock_work+0x10/0x10 debug_object_activate+0x122/0x170 insert_work+0x25/0x60 __queue_work+0x2e0/0x340 queue_delayed_work_on+0x48/0x70 afs_fl_release_private+0x57/0x70 locks_release_private+0x5c/0xa0 locks_free_lock+0xe/0x20 posix_lock_inode+0x55f/0x5b0 locks_lock_inode_wait+0x81/0x140 ? file_write_and_wait_range+0x50/0x70 afs_lock+0xcd/0x110 fcntl_setlk+0x10d/0x260 do_fcntl+0x24e/0x5b0 __do_sys_fcntl+0x6a/0x90 do_syscall_64+0x11e/0x310 entry_SYSCALL_64_after_hwframe+0x71/0x79 Fix this by reinitialising ->lock_work after allocating an inode. Also, flush ->lock_work when the inode is being evicted to make sure it's not still running. Fixes: e8d6c554126b ("AFS: implement file locking") Signed-off-by: David Howells Link: https://patch.msgid.link/20260622090856.2746629-13-dhowells@redhat.com cc: Marc Dionne cc: Thomas Gleixner cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner (Amutable) --- fs/afs/inode.c | 1 + fs/afs/super.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/afs/inode.c b/fs/afs/inode.c index 51c28f148845..14f39a9bea6c 100644 --- a/fs/afs/inode.c +++ b/fs/afs/inode.c @@ -680,6 +680,7 @@ void afs_evict_inode(struct inode *inode) inode->i_mapping->a_ops->writepages(inode->i_mapping, &wbc); } + flush_delayed_work(&vnode->lock_work); netfs_wait_for_outstanding_io(inode); truncate_inode_pages_final(&inode->i_data); netfs_free_folioq_buffer(vnode->directory); diff --git a/fs/afs/super.c b/fs/afs/super.c index dec091e569c4..82bb713825a0 100644 --- a/fs/afs/super.c +++ b/fs/afs/super.c @@ -660,7 +660,6 @@ static void afs_i_init_once(void *_vnode) INIT_LIST_HEAD(&vnode->wb_keys); INIT_LIST_HEAD(&vnode->pending_locks); INIT_LIST_HEAD(&vnode->granted_locks); - INIT_DELAYED_WORK(&vnode->lock_work, afs_lock_work); INIT_LIST_HEAD(&vnode->cb_mmap_link); seqlock_init(&vnode->cb_lock); } @@ -694,6 +693,7 @@ static struct inode *afs_alloc_inode(struct super_block *sb) init_rwsem(&vnode->rmdir_lock); INIT_WORK(&vnode->cb_work, afs_invalidate_mmap_work); + INIT_DELAYED_WORK(&vnode->lock_work, afs_lock_work); _leave(" = %p", &vnode->netfs.inode); return &vnode->netfs.inode; From 0f36469d7ce98b362934113c550d08bb0c784231 Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 22 Jun 2026 10:08:47 +0100 Subject: [PATCH 0259/1054] afs: Fix callback service message parsers to pass through -EAGAIN The AFS filesystem client uses an rxrpc server to listen for callback notifications. Each callback call type handler has a delivery function that parses the incoming request stream, and this should return -EAGAIN the last packet hasn't yet been seen, but all currently queued received data is consumed. afs_extract_data() does this, but the -EAGAIN return is switched to 0 inadvertantly Fix callback service message parsers to pass through -EAGAIN Fixes: d001648ec7cf ("rxrpc: Don't expose skbs to in-kernel users [ver #2]") Closes: https://sashiko.dev/#/patchset/20260609081738.770127-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260622090856.2746629-14-dhowells@redhat.com cc: Marc Dionne cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner (Amutable) --- fs/afs/cmservice.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/fs/afs/cmservice.c b/fs/afs/cmservice.c index 263c60c811a5..db394f101fc6 100644 --- a/fs/afs/cmservice.c +++ b/fs/afs/cmservice.c @@ -334,7 +334,6 @@ static int afs_deliver_cb_init_call_back_state3(struct afs_call *call) ret = afs_extract_data(call, false); switch (ret) { case 0: break; - case -EAGAIN: return 0; default: return ret; } @@ -456,7 +455,6 @@ static int afs_deliver_cb_probe_uuid(struct afs_call *call) ret = afs_extract_data(call, false); switch (ret) { case 0: break; - case -EAGAIN: return 0; default: return ret; } From 3b1601471a88f86082fc1f1c2475645cdf59f7d8 Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 22 Jun 2026 10:08:48 +0100 Subject: [PATCH 0260/1054] afs: Use scoped_seqlock_read() rather than manually doing seqlock stuff This is an addendum to the patch to remove the erroneous seq |= 1 in volume lookup loop. Switch to using scoped_seqlock_read() as suggested by Oleg Nesterov[1]. Signed-off-by: David Howells Link: https://lore.kernel.org/r/aifaeKvz3KemfzaS@redhat.com/ [1] Link: https://patch.msgid.link/20260622090856.2746629-15-dhowells@redhat.com Reviewed-by: Oleg Nesterov cc: Marc Dionne cc: Li RongQing cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner (Amutable) --- fs/afs/callback.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/fs/afs/callback.c b/fs/afs/callback.c index 833ac3178ddc..dd7a407ea368 100644 --- a/fs/afs/callback.c +++ b/fs/afs/callback.c @@ -113,16 +113,12 @@ static struct afs_volume *afs_lookup_volume_rcu(struct afs_cell *cell, { struct afs_volume *volume = NULL; struct rb_node *p; - int seq = 1; - for (;;) { + scoped_seqlock_read(&cell->volume_lock, ss_lock) { /* Unfortunately, rbtree walking doesn't give reliable results * under just the RCU read lock, so we have to check for * changes. */ - seq++; /* 2 on the 1st/lockless path, otherwise odd */ - read_seqbegin_or_lock(&cell->volume_lock, &seq); - p = rcu_dereference_raw(cell->volumes.rb_node); while (p) { volume = rb_entry(p, struct afs_volume, cell_node); @@ -138,11 +134,8 @@ static struct afs_volume *afs_lookup_volume_rcu(struct afs_cell *cell, if (volume && afs_try_get_volume(volume, afs_volume_trace_get_callback)) break; - if (!need_seqretry(&cell->volume_lock, seq)) - break; } - done_seqretry(&cell->volume_lock, seq); return volume; } From 794a01110390c1b76f59ece773fb0fbfd89c6f5c Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 22 Jun 2026 10:08:49 +0100 Subject: [PATCH 0261/1054] afs: Fix missing NULL pointer check in afs_break_some_callbacks() Fix afs_break_some_callbacks() to check to see if afs_lookup_volume_rcu() returned NULL (e.g. the specified volume is unknown). Fixes: 8230fd8217b7 ("afs: Make callback processing more efficient.") Closes: https://sashiko.dev/#/patchset/20260609081738.770127-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260622090856.2746629-16-dhowells@redhat.com cc: Marc Dionne cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner (Amutable) --- fs/afs/callback.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fs/afs/callback.c b/fs/afs/callback.c index dd7a407ea368..74853e0d0435 100644 --- a/fs/afs/callback.c +++ b/fs/afs/callback.c @@ -213,7 +213,11 @@ static void afs_break_some_callbacks(struct afs_server *server, rcu_read_lock(); volume = afs_lookup_volume_rcu(server->cell, vid); - if (cbb->fid.vnode == 0 && cbb->fid.unique == 0) { + if (!volume) { + /* Ignore breaks on unknown volumes. */ + rcu_read_unlock(); + *_count = 0; + } else if (cbb->fid.vnode == 0 && cbb->fid.unique == 0) { afs_break_volume_callback(server, volume); *_count -= 1; if (*_count) From d672c276f685a540ed2b2a8bafaed4650a89022c Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 22 Jun 2026 10:08:50 +0100 Subject: [PATCH 0262/1054] afs: Fix leak of ungot volume Fix afs_lookup_volume_rcu() so that it doesn't leak a dying volume if afs_try_get_volume() fails. Fixes: 32222f09782f ("afs: Apply server breaks to mmap'd files in the call processor") Closes: https://sashiko.dev/#/patchset/20260609081738.770127-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260622090856.2746629-17-dhowells@redhat.com cc: Marc Dionne cc: Deepakkumar Karn cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner (Amutable) --- fs/afs/callback.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/afs/callback.c b/fs/afs/callback.c index 74853e0d0435..61354003c006 100644 --- a/fs/afs/callback.c +++ b/fs/afs/callback.c @@ -134,6 +134,7 @@ static struct afs_volume *afs_lookup_volume_rcu(struct afs_cell *cell, if (volume && afs_try_get_volume(volume, afs_volume_trace_get_callback)) break; + volume = NULL; } return volume; From fc10c0ecf06f2981af5d04357612b00051e03e9e Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 22 Jun 2026 10:08:51 +0100 Subject: [PATCH 0263/1054] afs: Fix vllist leak Fix a leak of the new vllist in afs_update_cell() in the event that it is an empty list (nr_servers == 0), in which case the old list isn't displaced unless the old list is also empty. Fixes: d5c32c89b208 ("afs: Fix cell DNS lookup") Closes: https://sashiko.dev/#/patchset/20260609081738.770127-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260622090856.2746629-18-dhowells@redhat.com cc: Marc Dionne cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner (Amutable) --- fs/afs/cell.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/afs/cell.c b/fs/afs/cell.c index e0fab1609f27..fbb8a43aa7cd 100644 --- a/fs/afs/cell.c +++ b/fs/afs/cell.c @@ -547,6 +547,8 @@ static int afs_update_cell(struct afs_cell *cell) rcu_assign_pointer(cell->vl_servers, vllist); cell->dns_source = vllist->source; old = p; + } else { + old = vllist; } write_unlock(&cell->vl_servers_lock); afs_put_vlserverlist(cell->net, old); From 55e841836c6f4646490f7b0347192b7a92d431ba Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 22 Jun 2026 10:08:52 +0100 Subject: [PATCH 0264/1054] afs: Fix lack of locking around modifications of net->cells_dyn_ino Fix the lack of locking around modifications of net->cells_dyn_ino by taking net->cells_lock exclusively. This also requires to cell to be removed from net->cells_dyn_ino in afs_destroy_cell_work() rather than in afs_cell_destroy() as the latter runs in RCU cleanup context and sleeping locks cannot be taken there. Fixes: 1d0b929fc070 ("afs: Change dynroot to create contents on demand") Closes: https://sashiko.dev/#/patchset/20260618074903.2374756-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260622090856.2746629-19-dhowells@redhat.com cc: Marc Dionne cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner (Amutable) --- fs/afs/cell.c | 8 +++++++- fs/afs/dynroot.c | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/fs/afs/cell.c b/fs/afs/cell.c index fbb8a43aa7cd..9d8937ae24e2 100644 --- a/fs/afs/cell.c +++ b/fs/afs/cell.c @@ -205,8 +205,10 @@ static struct afs_cell *afs_alloc_cell(struct afs_net *net, cell->dns_source = vllist->source; cell->dns_status = vllist->status; smp_store_release(&cell->dns_lookup_count, 1); /* vs source/status */ + down_write(&net->cells_lock); ret = idr_alloc_cyclic(&net->cells_dyn_ino, cell, 2, INT_MAX / 2, GFP_KERNEL); + up_write(&net->cells_lock); if (ret < 0) goto error; atomic_inc(&net->cells_outstanding); @@ -579,7 +581,6 @@ static void afs_cell_destroy(struct rcu_head *rcu) afs_put_vlserverlist(net, rcu_access_pointer(cell->vl_servers)); afs_unuse_cell(cell->alias_of, afs_cell_trace_unuse_alias); key_put(cell->anonymous_key); - idr_remove(&net->cells_dyn_ino, cell->dynroot_ino); kfree(cell->name - 1); kfree(cell); @@ -594,6 +595,11 @@ static void afs_destroy_cell_work(struct work_struct *work) afs_see_cell(cell, afs_cell_trace_destroy); timer_delete_sync(&cell->management_timer); cancel_work_sync(&cell->manager); + + down_write(&cell->net->cells_lock); + idr_remove(&cell->net->cells_dyn_ino, cell->dynroot_ino); + up_write(&cell->net->cells_lock); + call_rcu(&cell->rcu, afs_cell_destroy); } diff --git a/fs/afs/dynroot.c b/fs/afs/dynroot.c index 1d5e33bc7502..6e3c8c691ba9 100644 --- a/fs/afs/dynroot.c +++ b/fs/afs/dynroot.c @@ -278,7 +278,7 @@ static struct dentry *afs_lookup_atcell(struct inode *dir, struct dentry *dentry } /* - * Transcribe the cell database into readdir content under the RCU read lock. + * Transcribe the cell database into readdir content under net->cells_lock. * Each cell produces two entries, one prefixed with a dot and one not. */ static int afs_dynroot_readdir_cells(struct afs_net *net, struct dir_context *ctx) From 26f17ce6fa3f05cb5965790499c1839094260de4 Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 22 Jun 2026 10:08:53 +0100 Subject: [PATCH 0265/1054] afs: Fix premature cell exposure through /afs AFS cell records are prematurely exposured through the /afs dynamic root by virtue of adding them immediately to the net->cells_dyn_ino IDR when the cell is allocated rather than when it is added to the lookup tree. This allows a candidate record to be accessed, even if it's actually a duplicate or not published yet. Fix this by not adding the cell to cells_dyn_ino until it's confirmed non-duplicate and is being published. A flag is then used to record whether it is added to the IDR to make removal from the IDR conditional. Closes: https://sashiko.dev/#/patchset/20260618155141.2513212-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260622090856.2746629-20-dhowells@redhat.com Fixes: 1d0b929fc070 ("afs: Change dynroot to create contents on demand") cc: Marc Dionne cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner (Amutable) --- fs/afs/cell.c | 27 +++++++++++++++++---------- fs/afs/internal.h | 1 + 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/fs/afs/cell.c b/fs/afs/cell.c index 9d8937ae24e2..47a2645768d7 100644 --- a/fs/afs/cell.c +++ b/fs/afs/cell.c @@ -205,14 +205,7 @@ static struct afs_cell *afs_alloc_cell(struct afs_net *net, cell->dns_source = vllist->source; cell->dns_status = vllist->status; smp_store_release(&cell->dns_lookup_count, 1); /* vs source/status */ - down_write(&net->cells_lock); - ret = idr_alloc_cyclic(&net->cells_dyn_ino, cell, - 2, INT_MAX / 2, GFP_KERNEL); - up_write(&net->cells_lock); - if (ret < 0) - goto error; atomic_inc(&net->cells_outstanding); - cell->dynroot_ino = ret; cell->debug_id = atomic_inc_return(&cell_debug_id); trace_afs_cell(cell->debug_id, 1, 0, afs_cell_trace_alloc); @@ -306,6 +299,13 @@ struct afs_cell *afs_lookup_cell(struct afs_net *net, goto cell_already_exists; } + ret = idr_alloc_cyclic(&net->cells_dyn_ino, candidate, + 2, INT_MAX / 2, GFP_KERNEL); + if (ret < 0) + goto cant_alloc_ino; + candidate->dynroot_ino = ret; + set_bit(AFS_CELL_FL_HAVE_INO, &candidate->flags); + cell = candidate; candidate = NULL; afs_use_cell(cell, trace); @@ -380,6 +380,11 @@ struct afs_cell *afs_lookup_cell(struct afs_net *net, _leave(" = %p [cell]", cell); return cell; +cant_alloc_ino: + up_write(&net->cells_lock); + afs_put_cell(candidate, afs_cell_trace_put_candidate); + goto error_noput; + cell_already_exists: _debug("cell exists"); cell = cursor; @@ -596,9 +601,11 @@ static void afs_destroy_cell_work(struct work_struct *work) timer_delete_sync(&cell->management_timer); cancel_work_sync(&cell->manager); - down_write(&cell->net->cells_lock); - idr_remove(&cell->net->cells_dyn_ino, cell->dynroot_ino); - up_write(&cell->net->cells_lock); + if (test_bit(AFS_CELL_FL_HAVE_INO, &cell->flags)) { + down_write(&cell->net->cells_lock); + idr_remove(&cell->net->cells_dyn_ino, cell->dynroot_ino); + up_write(&cell->net->cells_lock); + } call_rcu(&cell->rcu, afs_cell_destroy); } diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 785c646856d7..601f01e5c15f 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -388,6 +388,7 @@ struct afs_cell { #define AFS_CELL_FL_NO_GC 0 /* The cell was added manually, don't auto-gc */ #define AFS_CELL_FL_DO_LOOKUP 1 /* DNS lookup requested */ #define AFS_CELL_FL_CHECK_ALIAS 2 /* Need to check for aliases */ +#define AFS_CELL_FL_HAVE_INO 3 /* Have dynroot_ino */ enum afs_cell_state state; short error; enum dns_record_source dns_source:8; /* Latest source of data from lookup */ From 56b4e4b26f84411d880f968a539207b0a8889c8c Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 22 Jun 2026 10:08:54 +0100 Subject: [PATCH 0266/1054] afs: Fix the volume AFS_VOLUME_RM_TREE is set on Fix afs_insert_volume_into_cell() to set AFS_VOLUME_RM_TREE on the volume replaced, not the new volume, as it's now removed from the cell's volume tree. This will cause the old volume to be removed from the tree twice and the new volume never to be removed. Fixes: 9a6b294ab496 ("afs: Fix use-after-free due to get/remove race in volume tree") Closes: https://sashiko.dev/#/patchset/20260618074903.2374756-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260622090856.2746629-21-dhowells@redhat.com cc: Marc Dionne cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner (Amutable) --- fs/afs/volume.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/afs/volume.c b/fs/afs/volume.c index 9ae5c8ad2e04..4f79d25ec37f 100644 --- a/fs/afs/volume.c +++ b/fs/afs/volume.c @@ -40,7 +40,7 @@ static struct afs_volume *afs_insert_volume_into_cell(struct afs_cell *cell, goto found; } - set_bit(AFS_VOLUME_RM_TREE, &volume->flags); + set_bit(AFS_VOLUME_RM_TREE, &p->flags); rb_replace_node_rcu(&p->cell_node, &volume->cell_node, &cell->volumes); } } From 903d37c97228258da71e092f8b4ab260ce81497d Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 22 Jun 2026 10:08:55 +0100 Subject: [PATCH 0267/1054] afs: Fix unchecked-length string display in debug statement Fix afs_extract_vlserver_list() to limit the length of the displayed string in a debug statement(). Fixes: 0a5143f2f89c ("afs: Implement VL server rotation") Closes: https://sashiko.dev/#/patchset/20260618074903.2374756-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260622090856.2746629-22-dhowells@redhat.com cc: Marc Dionne cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner (Amutable) --- fs/afs/vl_list.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fs/afs/vl_list.c b/fs/afs/vl_list.c index 8e1cf6cdcf71..c1dac5dbed0d 100644 --- a/fs/afs/vl_list.c +++ b/fs/afs/vl_list.c @@ -200,6 +200,8 @@ struct afs_vlserver_list *afs_extract_vlserver_list(struct afs_cell *cell, b += sizeof(*hdr); while (end - b >= sizeof(bs)) { + int nlen; + bs.name_len = afs_extract_le16(&b); bs.priority = afs_extract_le16(&b); bs.weight = afs_extract_le16(&b); @@ -209,10 +211,12 @@ struct afs_vlserver_list *afs_extract_vlserver_list(struct afs_cell *cell, bs.protocol = *b++; bs.nr_addrs = *b++; + nlen = min3(bs.name_len, end - b, 255); + _debug("extract %u %u %u %u %u %u %*.*s", bs.name_len, bs.priority, bs.weight, bs.port, bs.protocol, bs.nr_addrs, - bs.name_len, bs.name_len, b); + bs.name_len, nlen, b); if (end - b < bs.name_len) break; From ebebef925281a336ed1d4bbbefaa5d3b00877f28 Mon Sep 17 00:00:00 2001 From: Jori Koolstra Date: Sun, 14 Jun 2026 21:10:40 +0200 Subject: [PATCH 0268/1054] MAINTAINERS: take over vboxsf from Hans de Goede I talked to Hans de Goede about two weeks ago in person. He expressed he would rather have someone else maintain vboxsf and was thinking about orphaning it. Since I am already doing filesystem stuff anyway, I am fine with doing this. (vboxsf is a thin layer between the vfs and the Virtual Box guest device driver). I have no major plans for vboxsf, but I do want to support passing physical addresses to the host; the communication protocol seems to allow for it and it would mean we can get rid of some kmap calls. Signed-off-by: Jori Koolstra Link: https://patch.msgid.link/20260614191040.3007723-1-jkoolstra@xs4all.nl Acked-by: Hans de Goede Signed-off-by: Christian Brauner (Amutable) --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 15011f5752a9..a6f463d20328 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -28725,7 +28725,7 @@ F: include/linux/vbox_utils.h F: include/uapi/linux/vbox*.h VIRTUAL BOX SHARED FOLDER VFS DRIVER -M: Hans de Goede +M: Jori Koolstra L: linux-fsdevel@vger.kernel.org S: Maintained F: fs/vboxsf/* From 681e452683b69a8e1a571cba0f238f8ceacf55d2 Mon Sep 17 00:00:00 2001 From: Fengnan Chang Date: Fri, 12 Jun 2026 12:40:41 +0800 Subject: [PATCH 0269/1054] iomap: release pages on atomic dio size mismatch If bio_iov_iter_get_pages() or the bounce helper succeeds but builds a short bio, the REQ_ATOMIC size check rejects it before submission. The old error path only dropped the bio reference, leaving any pages already attached to the bio unreleased. Release or unbounce the pages before falling through to out_put_bio on this error path. This bug was reported by sashiko: https://sashiko.dev/#/patchset/20260608073134.95964-1-changfengnan%40bytedance.com Fixes: 9e0933c21c12 ("fs: iomap: Atomic write support") Signed-off-by: Fengnan Chang Link: https://patch.msgid.link/20260612044041.10677-1-changfengnan@bytedance.com Reviewed-by: Christoph Hellwig Signed-off-by: Christian Brauner (Amutable) --- fs/iomap/direct-io.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/fs/iomap/direct-io.c b/fs/iomap/direct-io.c index b485e3b191da..e2cd5f92babe 100644 --- a/fs/iomap/direct-io.c +++ b/fs/iomap/direct-io.c @@ -369,7 +369,7 @@ static ssize_t iomap_dio_bio_iter_one(struct iomap_iter *iter, */ if ((op & REQ_ATOMIC) && WARN_ON_ONCE(ret != iomap_length(iter))) { ret = -EINVAL; - goto out_put_bio; + goto out_bio_release_pages; } if (iter->iomap.flags & IOMAP_F_INTEGRITY) { @@ -393,6 +393,11 @@ static ssize_t iomap_dio_bio_iter_one(struct iomap_iter *iter, iomap_dio_submit_bio(iter, dio, bio, pos); return ret; +out_bio_release_pages: + if (dio->flags & IOMAP_DIO_BOUNCE) + bio_iov_iter_unbounce(bio, true, false); + else + bio_release_pages(bio, false); out_put_bio: bio_put(bio); return ret; From 16b02eb4b9b272c221255c20d34ccd5db53a3ed3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Wilczy=C5=84ski?= Date: Sat, 13 Jun 2026 21:10:05 +0000 Subject: [PATCH 0270/1054] proc: only bump parent nlink when registering directories MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit proc_register() increments the parent directory's link count for every entry it registers, while remove_proc_entry() and remove_proc_subtree() decrement it only when the removed entry is a directory. Regular files thus inflate the parent's count while they exist, and leak one link permanently on every create and remove cycle. For example, /proc/bus/pci/00 with twenty-two device files and no subdirectories reports nlink 24 instead of 2, and SR-IOV VF enable and disable cycles, each creating and removing the VF config space entries under /proc/bus/pci/, inflate the link count of that directory without bound. Before commit e06689bf5701 ("proc: change ->nlink under proc_subdir_lock"), the increment lived in proc_mkdir_data() and proc_create_mount_point(), and was therefore applied only to directories. Moving it into proc_register() to bring it under proc_subdir_lock dropped the S_ISDIR check. Thus, move the nlink accounting into pde_subdir_insert() and pde_erase(), only updating it for directories in both, so the link count is always changed together with the directory entry itself. Fixes: e06689bf5701 ("proc: change ->nlink under proc_subdir_lock") Cc: stable@vger.kernel.org # v5.5+ Signed-off-by: Krzysztof WilczyÅ„ski Link: https://patch.msgid.link/20260613211005.921692-1-kwilczynski@kernel.org Signed-off-by: Christian Brauner (Amutable) --- fs/proc/generic.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/fs/proc/generic.c b/fs/proc/generic.c index adc9b9a092b0..26086a283672 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c @@ -112,6 +112,8 @@ static bool pde_subdir_insert(struct proc_dir_entry *dir, /* Add new node and rebalance tree. */ rb_link_node(&de->subdir_node, parent, new); rb_insert_color(&de->subdir_node, root); + if (S_ISDIR(de->mode)) + dir->nlink++; return true; } @@ -404,7 +406,6 @@ struct proc_dir_entry *proc_register(struct proc_dir_entry *dir, write_unlock(&proc_subdir_lock); goto out_free_inum; } - dir->nlink++; write_unlock(&proc_subdir_lock); return dp; @@ -706,6 +707,8 @@ static void pde_erase(struct proc_dir_entry *pde, struct proc_dir_entry *parent) { rb_erase(&pde->subdir_node, &parent->subdir); RB_CLEAR_NODE(&pde->subdir_node); + if (S_ISDIR(pde->mode)) + parent->nlink--; } /* @@ -731,8 +734,6 @@ void remove_proc_entry(const char *name, struct proc_dir_entry *parent) de = NULL; } else { pde_erase(de, parent); - if (S_ISDIR(de->mode)) - parent->nlink--; } } write_unlock(&proc_subdir_lock); @@ -791,8 +792,6 @@ int remove_proc_subtree(const char *name, struct proc_dir_entry *parent) continue; } next = de->parent; - if (S_ISDIR(de->mode)) - next->nlink--; write_unlock(&proc_subdir_lock); proc_entry_rundown(de); From e348eecd4d8fa8d18a5157ff59f7be1dc59c5928 Mon Sep 17 00:00:00 2001 From: Souvik Banerjee Date: Fri, 1 May 2026 23:27:35 +0000 Subject: [PATCH 0271/1054] ovl: use linked upper dentry in copy-up tmpfile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ovl_copy_up_tmpfile() stores the disconnected O_TMPFILE dentry as the overlay's upper dentry reference via ovl_inode_update(). vfs_tmpfile() allocated this dentry via d_alloc(parentpath->dentry, &slash_name), so d_name is "/" and d_parent is c->workdir. Local upper filesystems (ext4, btrfs, xfs, ...) immediately rename it to "#" via d_mark_tmpfile() inside their ->tmpfile() op; FUSE and virtiofs do not, so both fields stay that way. Neither identifies the destination directory and filename where ovl_do_link() actually linked the file. When the upper filesystem implements ->d_revalidate() (e.g. FUSE or virtiofs), ovl_revalidate_real() calls it with the dentry's parent inode and a snapshot of d_name. The server tries to look up "/" inside c->workdir, fails, and overlayfs reports -ESTALE. This causes persistent ESTALE errors for any file that was copied up via the tmpfile path, breaking dpkg, apt, and other tools that do rename-over-existing on overlayfs with a FUSE/virtiofs upper. Before commit 6b52243f633e ("ovl: fold copy-up helpers into callers"), the tmpfile copy-up path used a dedicated helper ovl_link_tmpfile() that captured the linked destination dentry returned by ovl_do_link(): err = ovl_do_link(temp, udir, upper); ... if (!err) *newdentry = dget(upper); and published it via ovl_inode_update(d_inode(c->dentry), newdentry). The fold inlined ovl_do_link() into ovl_copy_up_tmpfile() but dropped the dget(upper) capture, and rewrote the publish line as ovl_inode_update(d_inode(c->dentry), dget(temp)) — where temp is the disconnected O_TMPFILE dentry. Fix by keeping a reference to the linked destination dentry after ovl_do_link() succeeds, and publishing that dentry at the existing ovl_inode_update() call site. The non-tmpfile/workdir path continues to publish the renamed temporary dentry. Reproducer: - Mount overlayfs with virtiofs (or a FUSE fs whose server advertises FUSE_TMPFILE) as upper - Run: dpkg -i - Observe: "error installing new file '...': Stale file handle" Fixes: 6b52243f633e ("ovl: fold copy-up helpers into callers") Cc: stable@vger.kernel.org # v4.20+ Signed-off-by: Souvik Banerjee Link: https://patch.msgid.link/20260501232735.2610824-1-souvik@amlalabs.com Reviewed-by: Amir Goldstein Reviewed-by: Miklos Szeredi Signed-off-by: Christian Brauner (Amutable) --- fs/overlayfs/copy_up.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c index 13cb60b52bd6..e963701b4c87 100644 --- a/fs/overlayfs/copy_up.c +++ b/fs/overlayfs/copy_up.c @@ -853,7 +853,7 @@ static int ovl_copy_up_tmpfile(struct ovl_copy_up_ctx *c) { struct ovl_fs *ofs = OVL_FS(c->dentry->d_sb); struct inode *udir = d_inode(c->destdir); - struct dentry *temp, *upper; + struct dentry *temp, *upper, *newdentry = NULL; struct file *tmpfile; int err; @@ -889,6 +889,14 @@ static int ovl_copy_up_tmpfile(struct ovl_copy_up_ctx *c) err = PTR_ERR(upper); if (!IS_ERR(upper)) { err = ovl_do_link(ofs, temp, udir, upper); + if (!err) { + /* + * Record the linked dentry -- not the disconnected + * O_TMPFILE dentry -- so that ->d_revalidate() on + * the upper fs sees the real parent/name. + */ + newdentry = dget(upper); + } end_creating(upper); } @@ -903,7 +911,7 @@ static int ovl_copy_up_tmpfile(struct ovl_copy_up_ctx *c) if (!c->metacopy) ovl_set_upperdata(d_inode(c->dentry)); - ovl_inode_update(d_inode(c->dentry), dget(temp)); + ovl_inode_update(d_inode(c->dentry), newdentry); out: ovl_end_write(c->dentry); From fb3e566cafc38fe3ba35e6843a2d529a3748870c Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Thu, 18 Jun 2026 10:39:22 -0400 Subject: [PATCH 0272/1054] minix: avoid overflow in bitmap block count calculation minix_check_superblock() uses minix_blocks_needed() to verify that the on-disk imap and zmap block counts are large enough for the advertised inode and zone counts. The helper currently performs DIV_ROUND_UP() in unsigned int arithmetic. A Minix v3 image can set s_ninodes or s_zones near UINT_MAX so the addition inside DIV_ROUND_UP() wraps to zero. That makes a zero imap/zmap block count look valid, after which minix_fill_super() can dereference s_imap[0] or s_zmap[0] even though no bitmap buffers were allocated. Impact: mounting a crafted Minix v3 image whose s_ninodes or s_zones is near UINT_MAX makes minix_check_superblock() accept a zero bitmap-block count and minix_fill_super() dereference s_imap[0]/s_zmap[0], panicking the kernel. The divisor is the bitmap capacity in bits, blocksize * 8, which is always a power of two: minix_fill_super() obtains the block size through sb_set_blocksize(), and blk_validate_block_size() rejects any size that is not a power of two. Use DIV_ROUND_UP_POW2(), which divides before adding the round-up term and so cannot overflow for a power-of-two divisor. Fixes: 8c97a6ddc956 ("minix: Add required sanity checking to minix_check_superblock()") Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Michael Bommarito Link: https://patch.msgid.link/20260618143922.3066874-1-michael.bommarito@gmail.com Reviewed-by: Jan Kara Signed-off-by: Christian Brauner (Amutable) --- fs/minix/minix.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/minix/minix.h b/fs/minix/minix.h index f2025c9b5825..9e52d4302f0d 100644 --- a/fs/minix/minix.h +++ b/fs/minix/minix.h @@ -97,7 +97,7 @@ static inline struct minix_inode_info *minix_i(struct inode *inode) static inline unsigned minix_blocks_needed(unsigned bits, unsigned blocksize) { - return DIV_ROUND_UP(bits, blocksize * 8); + return DIV_ROUND_UP_POW2(bits, blocksize * 8); } #if defined(CONFIG_MINIX_FS_NATIVE_ENDIAN) && \ From 8c256fba2b46020004201c500b2a1fbc707a33ef Mon Sep 17 00:00:00 2001 From: Hongling Zeng Date: Wed, 17 Jun 2026 16:50:49 +0800 Subject: [PATCH 0273/1054] cachefiles: Fix double unlock in nomem_d_alloc error path When start_creating() fails and returns -ENOMEM, it has already released the parent directory lock in __start_dirop(): static struct dentry *__start_dirop(...) { ... inode_lock_nested(dir, I_MUTEX_PARENT); dentry = lookup_one_qstr_excl(name, parent, lookup_flags); if (IS_ERR(dentry)) inode_unlock(dir); <-- Lock released on error return dentry; } However, the nomem_d_alloc error path in cachefiles_get_directory() unconditionally calls inode_unlock(d_inode(dir)) again, causing a double unlock that corrupts the rwsem state. This is a leftover from commit 7ab96df840e60 which replaced manual locking with start_creating() but failed to update the nomem_d_alloc path (while correctly updating mkdir_error and lookup_error paths). Fixes: 7ab96df840e6 ("VFS/nfsd/cachefiles/ovl: add start_creating() and end_creating()") Signed-off-by: Hongling Zeng Link: https://patch.msgid.link/20260617085049.730789-1-zenghongling@kylinos.cn Signed-off-by: Christian Brauner (Amutable) --- fs/cachefiles/namei.c | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/cachefiles/namei.c b/fs/cachefiles/namei.c index 2937db690b40..2c46f0decb02 100644 --- a/fs/cachefiles/namei.c +++ b/fs/cachefiles/namei.c @@ -209,7 +209,6 @@ struct dentry *cachefiles_get_directory(struct cachefiles_cache *cache, return ERR_PTR(ret); nomem_d_alloc: - inode_unlock(d_inode(dir)); _leave(" = -ENOMEM"); return ERR_PTR(-ENOMEM); } From fd5637a2fe6dd4448392738691d63e5559fafb12 Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Tue, 9 Jun 2026 20:46:56 +0200 Subject: [PATCH 0274/1054] ovl: fix comment about locking order Forgot to update the comment when we changed the locking order. Fixes: 162d06444070c ("ovl: reorder ovl_want_write() after ovl_inode_lock()") Signed-off-by: Amir Goldstein Link: https://patch.msgid.link/20260609184656.1916631-1-amir73il@gmail.com Signed-off-by: Christian Brauner (Amutable) --- fs/overlayfs/inode.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index 00c69707bda9..bc71231cad53 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -783,8 +783,8 @@ static const struct address_space_operations ovl_aops = { * * This chain is valid: * - inode->i_rwsem (inode_lock[2]) - * - upper_mnt->mnt_sb->s_writers (ovl_want_write[0]) * - OVL_I(inode)->lock (ovl_inode_lock[2]) + * - upper_mnt->mnt_sb->s_writers (ovl_want_write[0]) * - OVL_I(lowerinode)->lock (ovl_inode_lock[1]) * * And this chain is valid: @@ -797,8 +797,8 @@ static const struct address_space_operations ovl_aops = { * held, because it is in reverse order of the non-nested case using the same * upper fs: * - inode->i_rwsem (inode_lock[1]) - * - upper_mnt->mnt_sb->s_writers (ovl_want_write[0]) * - OVL_I(inode)->lock (ovl_inode_lock[1]) + * - upper_mnt->mnt_sb->s_writers (ovl_want_write[0]) */ #define OVL_MAX_NESTING FILESYSTEM_MAX_STACK_DEPTH From 6a2875517c778ac1111b6920e94cbab91cda8724 Mon Sep 17 00:00:00 2001 From: Matteo Croce Date: Tue, 16 Jun 2026 18:33:46 +0200 Subject: [PATCH 0275/1054] fat: stop reading directory entries past the end-of-directory marker The FAT specification[1] (FAT Directory Structure -> "DIR_Name[0]") states: If DIR_Name[0] == 0x00, then the directory entry is free (same as for 0xE5), and there are no allocated directory entries after this one (all of the DIR_Name[0] bytes in all of the entries after this one are also set to 0). The special 0 value, rather than the 0xE5 value, indicates to FAT file system driver code that the rest of the entries in this directory do not need to be examined because they are all free. Linux did not honour this. fat_get_entry() kept advancing past the 0x00 terminator; if the trailing on-disk slots were not zero-filled (buggy formatters, read-only media written by other operating systems, on-disk corruption) the driver surfaced arbitrary bytes as real directory entries. On a typical affected image, `ls /mnt` returns ~150 bogus entries with random binary names, multi-gigabyte sizes, dates ranging from 1980 to 2106, and a flood of -EIO from stat(). Earlier attempts (v1..v3, see [2][3][4]) added `de->name[0] == 0` guards at each call site. As Hirofumi pointed out on v3, those guards reject the entry but fat_get_entry() has already advanced *pos past it; the next readdir() resumes after the marker and walks straight back into the garbage. His suggestion was to centralise the check. This patch: * Adds fat_get_entry_eod(), a small wrapper around fat_get_entry() that returns -1 when name[0] == 0 and seeks *pos to dir->i_size. Per spec every slot after the 0x00 marker is also zero, so jumping to the end of the directory is correct: subsequent reads return -1 from fat_bmap() without re-fetching trailing zero slots, and callers persisting *pos across invocations (notably readdir's ctx->pos) keep reporting end-of-directory on re-entry. * Converts the read/search paths to use the new wrapper: fat_parse_long(), fat_search_long(), __fat_readdir(), and fat_get_short_entry() -- the last covers fat_get_dotdot_entry(), fat_dir_empty(), fat_subdirs(), fat_scan(), and fat_scan_logstart() transitively. * Leaves fat_add_entries() and __fat_remove_entries() on raw fat_get_entry(): the write paths legitimately need to operate on free/zero slots. fat_add_entries() additionally detects an allocated entry past a 0x00 marker (the spec violation that produces the garbage) and treats it as filesystem corruption: fat_fs_error_ratelimit() is called -- which honours the configured errors= mount option (panic / remount-ro / continue) -- and the operation returns -EIO so we don't write fresh entries into an already-corrupt directory. [1] https://download.microsoft.com/download/1/6/1/161ba512-40e2-4cc9-843a-923143f3456c/fatgen103.doc [2] https://lore.kernel.org/lkml/20181207013410.7050-1-mcroce@redhat.com/ [3] https://lore.kernel.org/lkml/20181216231510.26854-1-mcroce@redhat.com/ [4] https://lore.kernel.org/lkml/20190201001408.7453-1-mcroce@redhat.com/ Reported-by: Timothy Redaelli Suggested-by: OGAWA Hirofumi Signed-off-by: Matteo Croce Link: https://patch.msgid.link/20260616163346.32603-1-technoboy85@gmail.com Acked-by: OGAWA Hirofumi Signed-off-by: Christian Brauner (Amutable) --- fs/fat/dir.c | 44 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/fs/fat/dir.c b/fs/fat/dir.c index 4f6f42f33613..c6cca5d00ffd 100644 --- a/fs/fat/dir.c +++ b/fs/fat/dir.c @@ -130,6 +130,31 @@ static inline int fat_get_entry(struct inode *dir, loff_t *pos, return fat__get_entry(dir, pos, bh, de); } +/* + * Like fat_get_entry(), but honour the FAT end-of-directory marker: + * a dirent whose first name byte is NUL terminates iteration per the + * spec, which also guarantees that every following slot is zeroed. + * Skip straight to the end of the directory so the next call returns + * -1 from fat_bmap() without re-reading the trailing zero slots, and + * so callers that persist *pos across invocations (e.g. readdir's + * ctx->pos) keep reporting EOD. Release *bh and set it to NULL to + * match fat_get_entry()'s contract that *bh is NULL on the -1 return. + */ +static int fat_get_entry_eod(struct inode *dir, loff_t *pos, + struct buffer_head **bh, + struct msdos_dir_entry **de) +{ + int err = fat_get_entry(dir, pos, bh, de); + + if (err == 0 && (*de)->name[0] == 0) { + brelse(*bh); + *bh = NULL; + *pos = dir->i_size; + return -1; + } + return err; +} + /* * Convert Unicode 16 to UTF-8, translated Unicode, or ASCII. * If uni_xlate is enabled and we can't get a 1:1 conversion, use a @@ -327,7 +352,7 @@ static int fat_parse_long(struct inode *dir, loff_t *pos, if (ds->id & 0x40) (*unicode)[offset + 13] = 0; - if (fat_get_entry(dir, pos, bh, de) < 0) + if (fat_get_entry_eod(dir, pos, bh, de) < 0) return PARSE_EOF; if (slot == 0) break; @@ -489,7 +514,7 @@ int fat_search_long(struct inode *inode, const unsigned char *name, err = -ENOENT; while (1) { - if (fat_get_entry(inode, &cpos, &bh, &de) == -1) + if (fat_get_entry_eod(inode, &cpos, &bh, &de) == -1) goto end_of_dir; parse_record: nr_slots = 0; @@ -601,7 +626,7 @@ static int __fat_readdir(struct inode *inode, struct file *file, bh = NULL; get_new: - if (fat_get_entry(inode, &cpos, &bh, &de) == -1) + if (fat_get_entry_eod(inode, &cpos, &bh, &de) == -1) goto end_of_dir; parse_record: nr_slots = 0; @@ -885,7 +910,7 @@ static int fat_get_short_entry(struct inode *dir, loff_t *pos, struct buffer_head **bh, struct msdos_dir_entry **de) { - while (fat_get_entry(dir, pos, bh, de) >= 0) { + while (fat_get_entry_eod(dir, pos, bh, de) >= 0) { /* free entry or long name entry or volume label */ if (!IS_FREE((*de)->name) && !((*de)->attr & ATTR_VOLUME)) return 0; @@ -1302,6 +1327,7 @@ int fat_add_entries(struct inode *dir, void *slots, int nr_slots, struct msdos_dir_entry *de; int err, free_slots, i, nr_bhs; loff_t pos; + bool saw_eod; sinfo->nr_slots = nr_slots; @@ -1310,12 +1336,15 @@ int fat_add_entries(struct inode *dir, void *slots, int nr_slots, bh = prev = NULL; pos = 0; err = -ENOSPC; + saw_eod = false; while (fat_get_entry(dir, &pos, &bh, &de) > -1) { /* check the maximum size of directory */ if (pos >= FAT_MAX_DIR_SIZE) goto error; if (IS_FREE(de->name)) { + if (de->name[0] == 0) + saw_eod = true; if (prev != bh) { get_bh(bh); bhs[nr_bhs] = prev = bh; @@ -1325,6 +1354,13 @@ int fat_add_entries(struct inode *dir, void *slots, int nr_slots, if (free_slots == nr_slots) goto found; } else { + if (saw_eod) { + fat_fs_error_ratelimit(sb, + "allocated dir entry found after end-of-directory marker (i_pos %lld)", + MSDOS_I(dir)->i_pos); + err = -EIO; + goto error; + } for (i = 0; i < nr_bhs; i++) brelse(bhs[i]); prev = NULL; From 704d48d81dc41470e108811c32c577ada66192d4 Mon Sep 17 00:00:00 2001 From: Farhad Alemi Date: Mon, 1 Jun 2026 20:10:08 -0700 Subject: [PATCH 0276/1054] freevxfs: don't BUG() on unknown typed-extent type vxfs_bmap_typed() handles four typed-extent types and calls BUG() in its default case, so an on-disk typed extent with any other type value crashes the kernel. It is reachable from ioctl(FIBMAP) on a regular file: kernel BUG at fs/freevxfs/vxfs_bmap.c:230! RIP: vxfs_bmap_typed fs/freevxfs/vxfs_bmap.c:230 [inline] vxfs_bmap1+0x128a/0x12d0 fs/freevxfs/vxfs_bmap.c:257 Replace the BUG() with WARN_ON_ONCE() and return 0 -- the value vxfs_bmap_typed() already returns on failure (and from the DEV4 case above); vxfs_getblk() maps 0 to -EIO, so the ioctl fails cleanly. Reported-by: Farhad Alemi Signed-off-by: Farhad Alemi Link: https://patch.msgid.link/CA+0ovChveuAwv=t15dr2m09E32bM48hHJxvfeEYZOhdNiEc9Tw@mail.gmail.com Reviewed-by: Christoph Hellwig Signed-off-by: Christian Brauner (Amutable) --- fs/freevxfs/vxfs_bmap.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/freevxfs/vxfs_bmap.c b/fs/freevxfs/vxfs_bmap.c index e85222892038..1b8216eb1d90 100644 --- a/fs/freevxfs/vxfs_bmap.c +++ b/fs/freevxfs/vxfs_bmap.c @@ -227,7 +227,8 @@ vxfs_bmap_typed(struct inode *ip, long iblock) return 0; } default: - BUG(); + WARN_ON_ONCE(1); + return 0; } } From 18227a6bc98bd0ba96ed3ce9d5b28776a5a28dfc Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Fri, 19 Jun 2026 04:38:20 -0500 Subject: [PATCH 0277/1054] orangefs: keep the readdir entry size 64-bit in fill_from_part() fill_from_part() computes the size of a directory entry in size_t but stores it in a __u32. An entry length near U32_MAX wraps it to a small value, bypasses the bounds check, and is then used to index the entry, reading far past the directory part -- an out-of-bounds read that oopses the kernel. Compute the size as a u64 so it cannot truncate; the bounds check then rejects the entry. The trailer is supplied by the userspace client. Fixes: 480e3e532e31 ("orangefs: support very large directories") Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas Link: https://patch.msgid.link/20260619-b4-disp-50d2bd59-v1-1-ce332969b4a2@proton.me Signed-off-by: Christian Brauner (Amutable) --- fs/orangefs/dir.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/fs/orangefs/dir.c b/fs/orangefs/dir.c index 6e2ebc8b9867..115b2c2f5269 100644 --- a/fs/orangefs/dir.c +++ b/fs/orangefs/dir.c @@ -191,7 +191,8 @@ static int fill_from_part(struct orangefs_dir_part *part, { const int offset = sizeof(struct orangefs_readdir_response_s); struct orangefs_khandle *khandle; - __u32 *len, padlen; + __u32 *len; + u64 padlen; loff_t i; char *s; i = ctx->pos & ~PART_MASK; @@ -215,8 +216,8 @@ static int fill_from_part(struct orangefs_dir_part *part, * len is the size of the string itself. padlen is the * total size of the encoded string. */ - padlen = (sizeof *len + *len + 1) + - (8 - (sizeof *len + *len + 1)%8)%8; + padlen = (u64)sizeof *len + *len + 1; + padlen += (8 - padlen % 8) % 8; if (part->len < i + padlen + sizeof *khandle) goto next; s = (void *)part + offset + i + sizeof *len; From 3f8c65b06fafc3f779abda5f7b81707411d05d4c Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Tue, 23 Jun 2026 11:32:27 +0200 Subject: [PATCH 0278/1054] bpf: have bpf_real_data_inode() take a struct file bpf_real_data_inode() must be usable from the bprm_check_security, mmap_file and file_mprotect hooks for systemd's RestrictFilesystemAccess BPF LSM program, so have it take a struct file instead of a dentry. Amir Goldstein suggests: While doing so, rename it from bpf_real_inode() to bpf_real_data_inode(). For a regular file on a union/overlay filesystem it resolves to the underlying inode that hosts the data, but for a non-regular file it returns the overlay inode. The new name makes the "inode hosting the data" intent explicit and avoids the ambiguity of "the real inode backing a file". Document the non-regular-file behavior in the kfunc too. Both the signature change and the rename are safe because the kfunc landed this cycle and has no released users. Link: https://patch.msgid.link/20260623-work-bpf-real_inode-v2-1-8e8b57dd25f7@kernel.org Fixes: 9af8c8a54f6e ("bpf: add bpf_real_inode() kfunc") Reviewed-by: Amir Goldstein Signed-off-by: Christian Brauner (Amutable) --- fs/bpf_fs_kfuncs.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/fs/bpf_fs_kfuncs.c b/fs/bpf_fs_kfuncs.c index 768aca2dc0f0..f1863a891db6 100644 --- a/fs/bpf_fs_kfuncs.c +++ b/fs/bpf_fs_kfuncs.c @@ -360,18 +360,23 @@ __bpf_kfunc int bpf_cgroup_read_xattr(struct cgroup *cgroup, const char *name__s #endif /* CONFIG_CGROUPS */ /** - * bpf_real_inode - get the real inode backing a dentry - * @dentry: dentry to resolve + * bpf_real_data_inode - get the real inode hosting a file's data + * @file: file to resolve * - * If the dentry is on a union/overlay filesystem, return the underlying, real - * inode that hosts the data. Otherwise return the inode attached to the - * dentry itself. + * Resolve @file to the inode that hosts its data. For a regular file on a + * union/overlay filesystem this is the underlying (upper or lower) inode that + * stores the data, not the overlay inode. * - * Return: The real inode backing the dentry, or NULL for a negative dentry. + * Data resolution only applies to regular files. For a non-regular file (e.g. + * a device node, fifo or socket) on a union/overlay filesystem the overlay + * inode itself is returned; for any file on a non-union filesystem the inode + * attached to @file is returned. + * + * Return: The inode hosting @file's data, or NULL. */ -__bpf_kfunc struct inode *bpf_real_inode(struct dentry *dentry) +__bpf_kfunc struct inode *bpf_real_data_inode(struct file *file) { - return d_real_inode(dentry); + return d_real_inode(file_dentry(file)); } __bpf_kfunc_end_defs(); @@ -384,7 +389,7 @@ BTF_ID_FLAGS(func, bpf_get_dentry_xattr, KF_SLEEPABLE) BTF_ID_FLAGS(func, bpf_get_file_xattr, KF_SLEEPABLE) BTF_ID_FLAGS(func, bpf_set_dentry_xattr, KF_SLEEPABLE) BTF_ID_FLAGS(func, bpf_remove_dentry_xattr, KF_SLEEPABLE) -BTF_ID_FLAGS(func, bpf_real_inode, KF_SLEEPABLE | KF_RET_NULL) +BTF_ID_FLAGS(func, bpf_real_data_inode, KF_SLEEPABLE | KF_RET_NULL) BTF_KFUNCS_END(bpf_fs_kfunc_set_ids) static int bpf_fs_kfuncs_filter(const struct bpf_prog *prog, u32 kfunc_id) From 597a7bc7630035580e941a548cb646618c1c5933 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Tue, 16 Jun 2026 16:08:17 +0200 Subject: [PATCH 0279/1054] xfs: fix the error unwind in xfs_open_devices() Since the rt and log block devices are closed in xfs_free_buftarg() the buftarg owns the device file. The error unwind does not respect that: when the log buftarg allocation fails, out_free_rtdev_targ frees the rt buftarg - releasing rtdev_file - and then falls through to out_close_rtdev and releases it a second time. The unwind also leaves mp->m_rtdev_targp and mp->m_ddev_targp pointing to the freed buftargs. The failed mount continues into deactivate_locked_super() -> xfs_kill_sb() -> xfs_mount_free(), which frees them again. Clear the buftarg pointers once the unwind freed them and clear rtdev_file once the rt buftarg owns it, so nothing is released twice. Reachable when a buftarg allocation fails after the data buftarg was set up: an I/O error in sync_blockdev() or an allocation failure in xfs_init_buftarg() while mounting with external rt and log devices. Link: https://patch.msgid.link/20260616-work-super-bdev_holder_global-v2-1-7df6b864028e@kernel.org Fixes: 41233576e9a4 ("xfs: close the RT and log block devices in xfs_free_buftarg") Signed-off-by: Christian Brauner (Amutable) --- fs/xfs/xfs_super.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c index eac7f9503805..8531d526fc44 100644 --- a/fs/xfs/xfs_super.c +++ b/fs/xfs/xfs_super.c @@ -534,8 +534,11 @@ xfs_open_devices( out_free_rtdev_targ: if (mp->m_rtdev_targp) xfs_free_buftarg(mp->m_rtdev_targp); + mp->m_rtdev_targp = NULL; + rtdev_file = NULL; /* released by xfs_free_buftarg() */ out_free_ddev_targ: xfs_free_buftarg(mp->m_ddev_targp); + mp->m_ddev_targp = NULL; out_close_rtdev: if (rtdev_file) bdev_fput(rtdev_file); From 55ec50d046c03b3724741957f7b007856e36dbe7 Mon Sep 17 00:00:00 2001 From: Morduan Zang Date: Wed, 24 Jun 2026 14:26:22 +0800 Subject: [PATCH 0280/1054] iomap: guard io_size EOF trim against concurrent truncate underflow iomap: fix zero padding data issue in concurrent append writes changed ioend accounting so that io_size tracks only valid data within EOF. This trims io_size when a writeback range extends past end_pos: ioend->io_size += map_len; if (ioend->io_offset + ioend->io_size > end_pos) ioend->io_size = end_pos - ioend->io_offset; However, if end_pos ends up below ioend->io_offset, the subtraction becomes negative and is stored in size_t io_size, causing an unsigned wrap to a huge value. This can happen when writeback continues past byte-level EOF up to a block-aligned range, or when a concurrent truncate shrinks the file after end_pos was sampled in iomap_writeback_handle_eof(). A wrapped io_size can mislead append detection and corrupt completion-time size handling, since filesystem end_io paths consume io_size for decisions such as on-disk EOF updates and unwritten/COW completion ranges. Fix this by clamping io_size to zero when EOF has moved to or before the ioend start offset. This preserves the original intent of trimming io_size to valid in-EOF data while avoiding the underflow. Fixes: 51d20d1dacbe ("iomap: fix zero padding data issue in concurrent append writes") Suggested-by: Christoph Hellwig Signed-off-by: Morduan Zang Link: https://patch.msgid.link/9E38E2659B47DC2A+20260624062622.337469-1-zhangdandan@uniontech.com Reviewed-by: Christoph Hellwig Signed-off-by: Christian Brauner (Amutable) --- fs/iomap/ioend.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/fs/iomap/ioend.c b/fs/iomap/ioend.c index f7c3e0c70fd7..0565328764c1 100644 --- a/fs/iomap/ioend.c +++ b/fs/iomap/ioend.c @@ -298,8 +298,12 @@ ssize_t iomap_add_to_ioend(struct iomap_writepage_ctx *wpc, struct folio *folio, * appending writes. */ ioend->io_size += map_len; - if (ioend->io_offset + ioend->io_size > end_pos) - ioend->io_size = end_pos - ioend->io_offset; + if (ioend->io_offset + ioend->io_size > end_pos) { + if (ioend->io_offset >= end_pos) + ioend->io_size = 0; + else + ioend->io_size = end_pos - ioend->io_offset; + } wbc_account_cgroup_owner(wpc->wbc, folio, map_len); return map_len; From f718c9fa87bec45eca57189aa05647741ae9eb14 Mon Sep 17 00:00:00 2001 From: Alan Urmancheev Date: Tue, 23 Jun 2026 01:23:22 -0400 Subject: [PATCH 0281/1054] exec: fix off-by-one in binfmt max rewrite depth comment The loop in exec_binprm() permits depth values 0 through 5, up to 5 successive binfmt rewrites (setting bprm->interpreter) until the 6th one would fail on depth > 5 and return -ELOOP. The comment claimed 4 levels, which was wrong. Adjusting the code to allow only 4 rewrites would be breaking userland, so fix the comment and not the code. Reproducer (a chain of shebanged scripts followed by an ELF binary): #!/bin/sh tmp=$(mktemp -d) echo $tmp cd $tmp mk () { echo $2 > $1; chmod +x $1; } for i in $(seq 4); do mk $i "#!$((i + 1))" done mk 5 '#!/bin/true' ./1 && echo '5 binfmt rewrites OK (1 -> 2 -> 3 -> 4 -> 5 -> /bin/true)' mk 5 '#!6' mk 6 '#!/bin/true' ./1 || echo '6 binfmt rewrites KO (1 -> 2 -> 3 -> 4 -> 5 -> 6 -> /bin/true)' Signed-off-by: Alan Urmancheev Link: https://patch.msgid.link/20260623052322.74711-1-alan.urman@gmail.com Signed-off-by: Christian Brauner (Amutable) --- fs/exec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/exec.c b/fs/exec.c index b92fe7db176c..d5993cedc829 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1717,7 +1717,7 @@ static int exec_binprm(struct linux_binprm *bprm) old_vpid = task_pid_nr_ns(current, task_active_pid_ns(current->parent)); rcu_read_unlock(); - /* This allows 4 levels of binfmt rewrites before failing hard. */ + /* This allows 5 levels of binfmt rewrites before failing hard. */ for (depth = 0;; depth++) { struct file *exec; if (depth > 5) From b61cbeadaa83a712afb2f759aa7e65d43cdef322 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 25 Jun 2026 15:06:19 +0100 Subject: [PATCH 0282/1054] netfs: Fix decision whether to disallow write-streaming due to fscache use netfs_perform_write() buffers data by writing it into the pagecache for later writeback. If the folio it wants to write to isn't present, it uses "write streaming" in which is will store partial data in a non-uptodate, but dirty folio. However, when fscache is in use, this is a potential problem as writes to the cache have to be aligned to the cache backend's DIO granularity, and so netfs_perform_write() attempts to suppress write-streaming in such a case, requiring the folio content to be fetched first unless the entire folio is going to be overwritten. This allows the content to be written to the cache too. Unfortunately, the test netfs_perform_write() uses isn't correct because it doesn't take into account the fact that the object lookup is asynchronous and farmed off to a work queue, so there's a short window in which the cache is doing a lookup but the test fails because the answer is undefined. This can be triggered by the generic/464 xfstest, and causes a warning to be emitted in cachefiles (in code not yet upstream) because it sees a write that doesn't have its bounds rounded out to DIO alignment. Fix this by changing the condition to whether FSCACHE_COOKIE_IS_CACHING is set on a cookie rather than whether the cookie is marked enabled. Note that this is really just a hint as to whether we allow write streaming or not and no other aspects of the cookie or cache object are accessed. Also apply the same fix to netfs_write_begin(). Reported-by: Marc Dionne Signed-off-by: David Howells Link: https://patch.msgid.link/20260625140640.3116900-2-dhowells@redhat.com cc: Paulo Alcantara cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner (Amutable) --- fs/netfs/buffered_read.c | 2 +- fs/netfs/buffered_write.c | 2 +- fs/netfs/internal.h | 12 ++++++++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c index 76d0f6a29aba..24a8a5418e31 100644 --- a/fs/netfs/buffered_read.c +++ b/fs/netfs/buffered_read.c @@ -659,7 +659,7 @@ int netfs_write_begin(struct netfs_inode *ctx, * within the cache granule containing the EOF, in which case we need * to preload the granule. */ - if (!netfs_is_cache_enabled(ctx) && + if (!netfs_is_cache_maybe_enabled(ctx) && netfs_skip_folio_read(folio, pos, len, false)) { netfs_stat(&netfs_n_rh_write_zskip); goto have_folio_no_wait; diff --git a/fs/netfs/buffered_write.c b/fs/netfs/buffered_write.c index 6bde3320bcec..2cdb68e6b16f 100644 --- a/fs/netfs/buffered_write.c +++ b/fs/netfs/buffered_write.c @@ -277,7 +277,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, * caching service temporarily because the backing store got * culled. */ - if (netfs_is_cache_enabled(ctx)) { + if (netfs_is_cache_maybe_enabled(ctx)) { if (finfo) { netfs_stat(&netfs_n_wh_wstream_conflict); goto flush_content; diff --git a/fs/netfs/internal.h b/fs/netfs/internal.h index 645996ecfc80..d889caa401dc 100644 --- a/fs/netfs/internal.h +++ b/fs/netfs/internal.h @@ -239,6 +239,18 @@ static inline bool netfs_is_cache_enabled(struct netfs_inode *ctx) #endif } +static inline bool netfs_is_cache_maybe_enabled(struct netfs_inode *ctx) +{ +#if IS_ENABLED(CONFIG_FSCACHE) + struct fscache_cookie *cookie = ctx->cache; + + return fscache_cookie_valid(cookie) && + test_bit(FSCACHE_COOKIE_IS_CACHING, &cookie->flags); +#else + return false; +#endif +} + /* * Get a ref on a netfs group attached to a dirty page (e.g. a ceph snap). */ From dbd6f56d975b23241b7bbb11bb8f562af548a0aa Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 25 Jun 2026 15:06:20 +0100 Subject: [PATCH 0283/1054] netfs: Fix netfs_create_write_req() to handle async cache object creation netfs_create_write_req() will skip caching if the fscache cookie is disabled, but this is a problem because async cache object creation might not have got far enough yet that has been enabled - thereby causing the call to fscache_begin_write_operation() to be skipped. Fix this by removing the checks on the cookie and delegating this to fscache_begin_write_operation(). Fixes: 7b589a9b45ae ("netfs: Fix handling of USE_PGPRIV2 and WRITE_TO_CACHE flags") Closes: https://sashiko.dev/#/patchset/20260624115737.2964520-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260625140640.3116900-3-dhowells@redhat.com cc: Paulo Alcantara cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner (Amutable) --- fs/netfs/write_issue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/netfs/write_issue.c b/fs/netfs/write_issue.c index c03c7cc45e47..4f55228f0fd4 100644 --- a/fs/netfs/write_issue.c +++ b/fs/netfs/write_issue.c @@ -106,7 +106,7 @@ struct netfs_io_request *netfs_create_write_req(struct address_space *mapping, _enter("R=%x", wreq->debug_id); ictx = netfs_inode(wreq->inode); - if (is_cacheable && netfs_is_cache_enabled(ictx)) + if (is_cacheable) fscache_begin_write_operation(&wreq->cache_resources, netfs_i_cookie(ictx)); if (rolling_buffer_init(&wreq->buffer, wreq->debug_id, ITER_SOURCE) < 0) goto nomem; From af6830cc12dfe86c832dccc9c9878a93aaa22f83 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 25 Jun 2026 15:06:21 +0100 Subject: [PATCH 0284/1054] cachefiles: Fix double fput Fix a double fput() in error handling in cachefiles_create_tmpfile(). Link: https://sashiko.dev/#/patchset/20260608145432.681865-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260625140640.3116900-4-dhowells@redhat.com cc: Paulo Alcantara cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner (Amutable) --- fs/cachefiles/namei.c | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/cachefiles/namei.c b/fs/cachefiles/namei.c index 2c46f0decb02..67793898148b 100644 --- a/fs/cachefiles/namei.c +++ b/fs/cachefiles/namei.c @@ -466,7 +466,6 @@ struct file *cachefiles_create_tmpfile(struct cachefiles_object *object) ret = -EINVAL; if (unlikely(!file->f_op->read_iter) || unlikely(!file->f_op->write_iter)) { - fput(file); pr_notice("Cache does not support read_iter and write_iter\n"); goto err_unuse; } From 511a018ed2afd8d415edd307ce7ad2048506f6a1 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 25 Jun 2026 15:06:22 +0100 Subject: [PATCH 0285/1054] cachefiles: Fix file burial to take lock when unsetting S_KERNEL_FILE Fix cachefiles_bury_object() to lock the inode of the file being buried whilst it unsets the S_KERNEL_FILE flag. Fixes: 07a90e97400c ("cachefiles: Implement culling daemon commands") Closes: https://sashiko.dev/#/patchset/20260616100821.2062304-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260625140640.3116900-5-dhowells@redhat.com cc: Paulo Alcantara cc: NeilBrown cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner (Amutable) --- fs/cachefiles/namei.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/cachefiles/namei.c b/fs/cachefiles/namei.c index 67793898148b..8a9f6be15828 100644 --- a/fs/cachefiles/namei.c +++ b/fs/cachefiles/namei.c @@ -374,7 +374,7 @@ int cachefiles_bury_object(struct cachefiles_cache *cache, "Rename failed with error %d", ret); } - __cachefiles_unmark_inode_in_use(object, d_inode(rep)); + cachefiles_do_unmark_inode_in_use(object, d_inode(rep)); end_renaming(&rd); _leave(" = 0"); return 0; From 55f4bb9373ca4a521f3b0119366db92715a39b81 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 25 Jun 2026 15:06:23 +0100 Subject: [PATCH 0286/1054] iov_iter: Fix potential underflow in iov_iter_extract_xarray_pages() In iov_iter_extract_xarray_pages(), if no pages are extracted because there's a hole (or something otherwise unextractable) in the xarray, then the calculation of maxsize at the end can go wrong if the starting offset is not zero. Fix this by returning 0 in such a case and freeing the page array if allocated here rather than being passed in. Note that in the near future, ITER_XARRAY should be removed. Fixes: 7d58fe731028 ("iov_iter: Add a function to extract a page list from an iterator") Link: https://sashiko.dev/#/patchset/20260608145432.681865-1-dhowells%40redhat.com Link: https://sashiko.dev/#/patchset/20260616100821.2062304-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260625140640.3116900-6-dhowells@redhat.com Reviewed-by: Christoph Hellwig cc: Paulo Alcantara cc: Matthew Wilcox cc: Christoph Hellwig cc: Jens Axboe cc: Mike Marshall cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner (Amutable) --- lib/iov_iter.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/iov_iter.c b/lib/iov_iter.c index 273919b16161..0f320b4e82a8 100644 --- a/lib/iov_iter.c +++ b/lib/iov_iter.c @@ -1568,6 +1568,7 @@ static ssize_t iov_iter_extract_xarray_pages(struct iov_iter *i, struct folio *folio; unsigned int nr = 0, offset; loff_t pos = i->xarray_start + i->iov_offset; + bool will_alloc = !*pages; XA_STATE(xas, i->xarray, pos >> PAGE_SHIFT); offset = pos & ~PAGE_MASK; @@ -1595,6 +1596,14 @@ static ssize_t iov_iter_extract_xarray_pages(struct iov_iter *i, } rcu_read_unlock(); + if (!nr) { + if (will_alloc) { + kvfree(*pages); + *pages = NULL; + } + return 0; + } + maxsize = min_t(size_t, nr * PAGE_SIZE - offset, maxsize); iov_iter_advance(i, maxsize); return maxsize; From 70531f4f3a143f81baf549da7f59a24a9f87a65c Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 25 Jun 2026 15:06:24 +0100 Subject: [PATCH 0287/1054] iov_iter: Fix missing alloc fail check in iov_iter_extract_bvec_pages() Fix iov_iter_extract_bvec_pages() to check if want_pages_array() fails and, if so, return -ENOMEM appropriately. Fixes: e4e535bff2bc ("iov_iter: don't require contiguous pages in iov_iter_extract_bvec_pages") Link: https://sashiko.dev/#/patchset/20260608145432.681865-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260625140640.3116900-7-dhowells@redhat.com Reviewed-by: Christoph Hellwig cc: Ming Lei cc: Paulo Alcantara cc: Matthew Wilcox cc: Christoph Hellwig cc: Jens Axboe cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner (Amutable) --- lib/iov_iter.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/iov_iter.c b/lib/iov_iter.c index 0f320b4e82a8..3dfad70328eb 100644 --- a/lib/iov_iter.c +++ b/lib/iov_iter.c @@ -1637,6 +1637,8 @@ static ssize_t iov_iter_extract_bvec_pages(struct iov_iter *i, bi.bi_bvec_done = skip; maxpages = want_pages_array(pages, maxsize, skip, maxpages); + if (!maxpages) + return -ENOMEM; while (bi.bi_size && bi.bi_idx < i->nr_segs) { struct bio_vec bv = bvec_iter_bvec(i->bvec, bi); From 72698020e15db16fc141e191b460bc335263b0ad Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 25 Jun 2026 15:06:25 +0100 Subject: [PATCH 0288/1054] iov_iter: Fix a memory leak in iov_iter_extract_user_pages() There's a potential memory leak in callers of iov_iter_extract_user_pages() whereby if a pages array is allocated in function, it isn't freed before returning of an error or 0. Now, it's not a leak per se in iov_iter_extract_user_pages() as, if an array is allocated, it's returned through *pages, so it's incumbent on the caller to free it. However, not all callers do. Fix this by freeing the table and clearing *pages before returning an error or 0. Note that iov_iter_extract_pages() and its subfunctions are allowed to return 0 without returning an array (for instance if the iterator count is 0). Fixes: 7d58fe731028 ("iov_iter: Add a function to extract a page list from an iterator") Closes: https://sashiko.dev/#/patchset/20260616100821.2062304-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260625140640.3116900-8-dhowells@redhat.com Reviewed-by: Christoph Hellwig cc: Paulo Alcantara cc: Matthew Wilcox cc: Christoph Hellwig cc: Jens Axboe cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner (Amutable) --- lib/iov_iter.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/iov_iter.c b/lib/iov_iter.c index 3dfad70328eb..c2484551a4e8 100644 --- a/lib/iov_iter.c +++ b/lib/iov_iter.c @@ -1756,6 +1756,7 @@ static ssize_t iov_iter_extract_user_pages(struct iov_iter *i, unsigned long addr; unsigned int gup_flags = 0; size_t offset; + bool will_alloc = !*pages; int res; if (i->data_source == ITER_DEST) @@ -1772,8 +1773,14 @@ static ssize_t iov_iter_extract_user_pages(struct iov_iter *i, if (!maxpages) return -ENOMEM; res = pin_user_pages_fast(addr, maxpages, gup_flags, *pages); - if (unlikely(res <= 0)) + if (unlikely(res <= 0)) { + if (will_alloc) { + kvfree(*pages); + *pages = NULL; + } return res; + } + maxsize = min_t(size_t, maxsize, res * PAGE_SIZE - offset); iov_iter_advance(i, maxsize); return maxsize; From 0442e23a5f72c74ba18882e4a2eed305c687009d Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 25 Jun 2026 15:06:26 +0100 Subject: [PATCH 0289/1054] iov_iter: Remove unused variable in kunit_iov_iter.c Remove the no longer used variable 'b' from iov_kunit_copy_to_bvec(). The variable is initialised and incremented, but nothing now makes use of the value. Signed-off-by: David Howells Link: https://patch.msgid.link/20260625140640.3116900-9-dhowells@redhat.com Reviewed-by: Christoph Hellwig cc: Ming Lei cc: Paulo Alcantara cc: Matthew Wilcox cc: Christoph Hellwig cc: Jens Axboe cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner (Amutable) --- lib/tests/kunit_iov_iter.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/tests/kunit_iov_iter.c b/lib/tests/kunit_iov_iter.c index 1e6fce9cb255..d9690ba1db88 100644 --- a/lib/tests/kunit_iov_iter.c +++ b/lib/tests/kunit_iov_iter.c @@ -283,7 +283,7 @@ static void __init iov_kunit_copy_to_bvec(struct kunit *test) struct page **spages, **bpages; u8 *scratch, *buffer; size_t bufsize, npages, size, copied; - int i, b, patt; + int i, patt; bufsize = 0x100000; npages = bufsize / PAGE_SIZE; @@ -306,10 +306,9 @@ static void __init iov_kunit_copy_to_bvec(struct kunit *test) KUNIT_EXPECT_EQ(test, iter.nr_segs, 0); /* Build the expected image in the scratch buffer. */ - b = 0; patt = 0; memset(scratch, 0, bufsize); - for (pr = bvec_test_ranges; pr->from >= 0; pr++, b++) { + for (pr = bvec_test_ranges; pr->from >= 0; pr++) { u8 *p = scratch + pr->page * PAGE_SIZE; for (i = pr->from; i < pr->to; i++) From 2bcd3ab3728752425ff5ab1e4be1698eba13d0d8 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 25 Jun 2026 15:06:27 +0100 Subject: [PATCH 0290/1054] scatterlist: Fix offset in folio calc in extract_xarray_to_sg() Fix the calculation of the offset in the folio being extracted in extract_xarray_to_sg(). Note that in the near future, ITER_XARRAY should be removed. Fixes: f5f82cd18732 ("Move netfs_extract_iter_to_sg() to lib/scatterlist.c") Link: https://sashiko.dev/#/patchset/20260608145432.681865-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260625140640.3116900-10-dhowells@redhat.com Reviewed-by: Christoph Hellwig cc: Paulo Alcantara cc: Matthew Wilcox cc: Christoph Hellwig cc: Jens Axboe cc: Mike Marshall cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner (Amutable) --- lib/scatterlist.c | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/scatterlist.c b/lib/scatterlist.c index b7fe91ef35b8..6ea40d2e6247 100644 --- a/lib/scatterlist.c +++ b/lib/scatterlist.c @@ -1366,6 +1366,7 @@ static ssize_t extract_xarray_to_sg(struct iov_iter *iter, sg_max--; maxsize -= len; + start += len; ret += len; if (maxsize <= 0 || sg_max == 0) break; From fa746e23d1094f9a68afe5973746b0e32078fd8b Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 25 Jun 2026 15:06:28 +0100 Subject: [PATCH 0291/1054] netfs: Fix kdoc warning Fix a kdoc warning due to a misnamed parameter in the description. Reported-by: Matthew Wilcox Signed-off-by: David Howells Link: https://patch.msgid.link/20260625140640.3116900-11-dhowells@redhat.com cc: Paulo Alcantara cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner (Amutable) --- include/linux/netfs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/netfs.h b/include/linux/netfs.h index 243c0f737938..bdc270e84b30 100644 --- a/include/linux/netfs.h +++ b/include/linux/netfs.h @@ -753,7 +753,7 @@ static inline void netfs_inode_init(struct netfs_inode *ctx, /** * netfs_resize_file - Note that a file got resized - * @ctx: The netfs inode being resized + * @ictx: The netfs inode being resized * @new_i_size: The new file size * @changed_on_server: The change was applied to the server * From 41376400c4717fed43490030902f9e4c9062b285 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 25 Jun 2026 15:06:29 +0100 Subject: [PATCH 0292/1054] netfs: Replace wb_lock with a bit lock for asynchronicity The netfs_inode::wb_lock mutex is used to prevent multiple simultaneous writebacks from fighting each other (a writeback thread will write multiple discontiguous regions within the same request). The mutex, however, only serialises the issuing of subrequests; it doesn't serialise the collection of results, and, in particular, the updating of file size information and fscache populatedness data. Unfortunately, the mutex cannot be held around the entire process as it has to be unlocked in the same thread in which it is locked - and we don't want to hold up the allocator whilst we complete the writeback. Fix this by replacing the mutex with a bit flag and a list of lock waiters so that the lock can be dropped in the collector thread after collection is complete. Link: https://sashiko.dev/#/patchset/20260608145432.681865-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260625140640.3116900-12-dhowells@redhat.com cc: Paulo Alcantara cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner (Amutable) --- fs/afs/symlink.c | 4 +- fs/netfs/locking.c | 95 ++++++++++++++++++++++++++++++++++++++++ fs/netfs/write_collect.c | 10 +++++ fs/netfs/write_issue.c | 37 +++++----------- include/linux/netfs.h | 11 ++++- 5 files changed, 126 insertions(+), 31 deletions(-) diff --git a/fs/afs/symlink.c b/fs/afs/symlink.c index ed5868369f37..16b4823cb7b7 100644 --- a/fs/afs/symlink.c +++ b/fs/afs/symlink.c @@ -255,11 +255,11 @@ int afs_symlink_writepages(struct address_space *mapping, } if (ret == 0) { - mutex_lock(&vnode->netfs.wb_lock); + netfs_wb_begin(&vnode->netfs, false); netfs_free_folioq_buffer(vnode->directory); vnode->directory = NULL; vnode->directory_size = 0; - mutex_unlock(&vnode->netfs.wb_lock); + netfs_wb_end(&vnode->netfs); } else if (ret == 1) { ret = 0; /* Skipped write due to lock conflict. */ } diff --git a/fs/netfs/locking.c b/fs/netfs/locking.c index 2249ecd09d0a..4e3be2b81504 100644 --- a/fs/netfs/locking.c +++ b/fs/netfs/locking.c @@ -9,6 +9,11 @@ #include #include "internal.h" +struct netfs_wb_waiter { + struct list_head link; /* Link in ictx->wb_queue */ + struct task_struct *waiter; /* Waiter task; cleared when lock granted */ +}; + /* * inode_dio_wait_interruptible - wait for outstanding DIO requests to finish * @inode: inode to wait for @@ -203,3 +208,93 @@ void netfs_end_io_direct(struct inode *inode) up_read(&inode->i_rwsem); } EXPORT_SYMBOL(netfs_end_io_direct); + +/* + * Wait to have exclusive access to writeback. + */ +static bool netfs_wb_begin_wait(struct netfs_inode *ictx) +{ + struct netfs_wb_waiter waiter = {}; + struct task_struct *tsk = current; + bool got = false; + + spin_lock(&ictx->lock); + + if (test_and_set_bit_lock(NETFS_ICTX_WB_LOCK, &ictx->flags)) { + get_task_struct(tsk); + waiter.waiter = tsk; + list_add_tail(&waiter.link, &ictx->wb_queue); + } else { + got = true; + } + spin_unlock(&ictx->lock); + + if (!got) { + for (;;) { + set_current_state(TASK_UNINTERRUPTIBLE); + /* Read waiter before accessing inode state. */ + if (smp_load_acquire(&waiter.waiter) == NULL) + break; + schedule(); + } + } + __set_current_state(TASK_RUNNING); + return true; +} + +/** + * netfs_wb_begin - Begin writeback, waiting if need be + * @ictx: The inode to get writeback access on + * @nowait: Return failure immediately rather than waiting if true + * + * Begin writeback to an inode, waiting for exclusive access if @nowait is + * false. This prevents collection from being done out of order with respect + * to the issuance of write subrequests. + * + * Note that writeback may be ended in a different process (e.g. the collection + * function on a workqueue) than started it. + * + * Return: True if can proceed, false if denied. + */ +bool netfs_wb_begin(struct netfs_inode *ictx, bool nowait) +{ + if (!test_and_set_bit_lock(NETFS_ICTX_WB_LOCK, &ictx->flags)) + return true; + if (nowait) { + netfs_stat(&netfs_n_wb_lock_skip); + return false; + } + netfs_stat(&netfs_n_wb_lock_wait); + return netfs_wb_begin_wait(ictx); +} +EXPORT_SYMBOL(netfs_wb_begin); + +/* netfs_wb_end - End writeback + * @ictx: The inode we have writeback access to + * + * End writeback access on an inode, waking up the next writeback request. + */ +void netfs_wb_end(struct netfs_inode *ictx) +{ + struct netfs_wb_waiter *waiter; + struct task_struct *tsk; + + WARN_ON_ONCE(!test_bit(NETFS_ICTX_WB_LOCK, &ictx->flags)); + + spin_lock(&ictx->lock); + + waiter = list_first_entry_or_null(&ictx->wb_queue, struct netfs_wb_waiter, link); + if (waiter) { + list_del(&waiter->link); + tsk = waiter->waiter; + /* Write inode state before clearing waiter. */ + smp_store_release(&waiter->waiter, NULL); + wake_up_process(tsk); + put_task_struct(tsk); + } else { + clear_bit_unlock(NETFS_ICTX_WB_LOCK, &ictx->flags); + } + + spin_unlock(&ictx->lock); +} +EXPORT_SYMBOL(netfs_wb_end); diff --git a/fs/netfs/write_collect.c b/fs/netfs/write_collect.c index 24fc2bb2f8a4..210eb8f3958d 100644 --- a/fs/netfs/write_collect.c +++ b/fs/netfs/write_collect.c @@ -408,6 +408,16 @@ bool netfs_write_collection(struct netfs_io_request *wreq) netfs_wake_rreq_flag(wreq, NETFS_RREQ_IN_PROGRESS, netfs_rreq_trace_wake_ip); /* As we cleared NETFS_RREQ_IN_PROGRESS, we acquired its ref. */ + switch (wreq->origin) { + case NETFS_WRITEBACK: + case NETFS_WRITEBACK_SINGLE: + case NETFS_WRITETHROUGH: + netfs_wb_end(ictx); + break; + default: + break; + } + if (wreq->iocb) { size_t written = min(wreq->transferred, wreq->len); wreq->iocb->ki_pos += written; diff --git a/fs/netfs/write_issue.c b/fs/netfs/write_issue.c index 4f55228f0fd4..2473bce37649 100644 --- a/fs/netfs/write_issue.c +++ b/fs/netfs/write_issue.c @@ -551,14 +551,8 @@ int netfs_writepages(struct address_space *mapping, struct folio *folio; int error = 0; - if (!mutex_trylock(&ictx->wb_lock)) { - if (wbc->sync_mode == WB_SYNC_NONE) { - netfs_stat(&netfs_n_wb_lock_skip); - return 0; - } - netfs_stat(&netfs_n_wb_lock_wait); - mutex_lock(&ictx->wb_lock); - } + if (!netfs_wb_begin(ictx, wbc->sync_mode == WB_SYNC_NONE)) + return 0; /* Need the first folio to be able to set up the op. */ folio = writeback_iter(mapping, wbc, NULL, &error); @@ -593,8 +587,6 @@ int netfs_writepages(struct address_space *mapping, } while ((folio = writeback_iter(mapping, wbc, folio, &error))); netfs_end_issue_write(wreq); - - mutex_unlock(&ictx->wb_lock); netfs_wake_collector(wreq); netfs_put_request(wreq, netfs_rreq_trace_put_return); @@ -604,7 +596,7 @@ int netfs_writepages(struct address_space *mapping, couldnt_start: netfs_kill_dirty_pages(mapping, wbc, folio); out: - mutex_unlock(&ictx->wb_lock); + netfs_wb_end(ictx); _leave(" = %d", error); return error; } @@ -618,12 +610,12 @@ struct netfs_io_request *netfs_begin_writethrough(struct kiocb *iocb, size_t len struct netfs_io_request *wreq = NULL; struct netfs_inode *ictx = netfs_inode(file_inode(iocb->ki_filp)); - mutex_lock(&ictx->wb_lock); + netfs_wb_begin(ictx, false); wreq = netfs_create_write_req(iocb->ki_filp->f_mapping, iocb->ki_filp, iocb->ki_pos, NETFS_WRITETHROUGH); if (IS_ERR(wreq)) { - mutex_unlock(&ictx->wb_lock); + netfs_wb_end(ictx); return wreq; } @@ -685,7 +677,6 @@ int netfs_advance_writethrough(struct netfs_io_request *wreq, struct writeback_c ssize_t netfs_end_writethrough(struct netfs_io_request *wreq, struct writeback_control *wbc, struct folio *writethrough_cache) { - struct netfs_inode *ictx = netfs_inode(wreq->inode); ssize_t ret; _enter("R=%x", wreq->debug_id); @@ -699,8 +690,6 @@ ssize_t netfs_end_writethrough(struct netfs_io_request *wreq, struct writeback_c netfs_end_issue_write(wreq); - mutex_unlock(&ictx->wb_lock); - if (wreq->iocb) ret = -EIOCBQUEUED; else @@ -847,15 +836,10 @@ int netfs_writeback_single(struct address_space *mapping, if (WARN_ON_ONCE(!iov_iter_is_folioq(iter))) return -EIO; - if (!mutex_trylock(&ictx->wb_lock)) { - if (wbc->sync_mode == WB_SYNC_NONE) { - /* The VFS will have undirtied the inode. */ - netfs_single_mark_inode_dirty(&ictx->inode); - netfs_stat(&netfs_n_wb_lock_skip); - return 1; - } - netfs_stat(&netfs_n_wb_lock_wait); - mutex_lock(&ictx->wb_lock); + if (!netfs_wb_begin(ictx, wbc->sync_mode == WB_SYNC_NONE)) { + /* The VFS will have undirtied the inode. */ + netfs_single_mark_inode_dirty(&ictx->inode); + return 1; } wreq = netfs_create_write_req(mapping, NULL, 0, NETFS_WRITEBACK_SINGLE); @@ -893,7 +877,6 @@ int netfs_writeback_single(struct address_space *mapping, smp_wmb(); /* Write lists before ALL_QUEUED. */ set_bit(NETFS_RREQ_ALL_QUEUED, &wreq->flags); - mutex_unlock(&ictx->wb_lock); netfs_wake_collector(wreq); netfs_put_request(wreq, netfs_rreq_trace_put_return); @@ -901,7 +884,7 @@ int netfs_writeback_single(struct address_space *mapping, return ret; couldnt_start: - mutex_unlock(&ictx->wb_lock); + netfs_wb_end(ictx); _leave(" = %d", ret); return ret; } diff --git a/include/linux/netfs.h b/include/linux/netfs.h index bdc270e84b30..1bc120d61c5b 100644 --- a/include/linux/netfs.h +++ b/include/linux/netfs.h @@ -61,14 +61,16 @@ struct netfs_inode { #if IS_ENABLED(CONFIG_FSCACHE) struct fscache_cookie *cache; #endif - struct mutex wb_lock; /* Writeback serialisation */ + struct list_head wb_queue; /* Queue of processes wanting to do writeback */ loff_t _remote_i_size; /* Size of the remote file */ loff_t _zero_point; /* Size after which we assume there's no data * on the server */ + spinlock_t lock; /* Lock covering wb_queue */ atomic_t io_count; /* Number of outstanding reqs */ unsigned long flags; #define NETFS_ICTX_ODIRECT 0 /* The file has DIO in progress */ #define NETFS_ICTX_UNBUFFERED 1 /* I/O should not use the pagecache */ +#define NETFS_ICTX_WB_LOCK 2 /* Writeback serialisation lock */ #define NETFS_ICTX_MODIFIED_ATTR 3 /* Indicate change in mtime/ctime */ #define NETFS_ICTX_SINGLE_NO_UPLOAD 4 /* Monolithic payload, cache but no upload */ }; @@ -462,6 +464,10 @@ int netfs_alloc_folioq_buffer(struct address_space *mapping, size_t *_cur_size, ssize_t size, gfp_t gfp); void netfs_free_folioq_buffer(struct folio_queue *fq); +/* Writeback exclusion API. */ +bool netfs_wb_begin(struct netfs_inode *ictx, bool nowait); +void netfs_wb_end(struct netfs_inode *ictx); + /** * netfs_inode - Get the netfs inode context from the inode * @inode: The inode to query @@ -743,7 +749,8 @@ static inline void netfs_inode_init(struct netfs_inode *ctx, #if IS_ENABLED(CONFIG_FSCACHE) ctx->cache = NULL; #endif - mutex_init(&ctx->wb_lock); + INIT_LIST_HEAD(&ctx->wb_queue); + spin_lock_init(&ctx->lock); /* ->releasepage() drives zero_point */ if (use_zero_point) { ctx->_zero_point = ctx->_remote_i_size; From ba6a9f6533c77c628eef0c0c5c19cd316e2be1b4 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 25 Jun 2026 15:06:30 +0100 Subject: [PATCH 0293/1054] netfs: Fix writethrough to use collection offload Fix writethrough write to set NETFS_RREQ_OFFLOAD_COLLECTION on the request so that collection is processed asynchronously rather than only right at the end - and also so that asynchronous O_SYNC writes get collected at all. Fixes: 288ace2f57c9 ("netfs: New writeback implementation") Closes: https://sashiko.dev/#/patchset/20260616100821.2062304-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260625140640.3116900-13-dhowells@redhat.com cc: Paulo Alcantara cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner (Amutable) --- fs/netfs/write_issue.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/netfs/write_issue.c b/fs/netfs/write_issue.c index 2473bce37649..3b363ce12f3f 100644 --- a/fs/netfs/write_issue.c +++ b/fs/netfs/write_issue.c @@ -620,6 +620,7 @@ struct netfs_io_request *netfs_begin_writethrough(struct kiocb *iocb, size_t len } wreq->io_streams[0].avail = true; + __set_bit(NETFS_RREQ_OFFLOAD_COLLECTION, &wreq->flags); trace_netfs_write(wreq, netfs_write_trace_writethrough); return wreq; } From ac5f95ac5d6d0f4c567b8b642825705a2bf0d79e Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 25 Jun 2026 15:06:31 +0100 Subject: [PATCH 0294/1054] netfs: Fix writeback error handling Fix the error handling in writeback_iter() loop. If an error occurs, writeback_iter() needs to be called again with *error set to the error so that it can clean up iteration state. Further, the current folio needs unlocking and redirtying. Fixes: 288ace2f57c9 ("netfs: New writeback implementation") Link: https://sashiko.dev/#/patchset/20260619140646.2633762-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260625140640.3116900-14-dhowells@redhat.com cc: Paulo Alcantara cc: Matthew Wilcox cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner (Amutable) --- fs/netfs/write_issue.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/fs/netfs/write_issue.c b/fs/netfs/write_issue.c index 3b363ce12f3f..3682896c3fdf 100644 --- a/fs/netfs/write_issue.c +++ b/fs/netfs/write_issue.c @@ -582,8 +582,6 @@ int netfs_writepages(struct address_space *mapping, } error = netfs_write_folio(wreq, wbc, folio); - if (error < 0) - break; } while ((folio = writeback_iter(mapping, wbc, folio, &error))); netfs_end_issue_write(wreq); @@ -594,7 +592,14 @@ int netfs_writepages(struct address_space *mapping, return error; couldnt_start: - netfs_kill_dirty_pages(mapping, wbc, folio); + if (error == -ENOMEM) { + folio_redirty_for_writepage(wbc, folio); + folio_unlock(folio); + folio = writeback_iter(mapping, wbc, folio, &error); + WARN_ON_ONCE(folio != NULL); + } else { + netfs_kill_dirty_pages(mapping, wbc, folio); + } out: netfs_wb_end(ictx); _leave(" = %d", error); From b6a713fd34b9498ee2164d5d3e8460732a392efc Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 25 Jun 2026 15:06:32 +0100 Subject: [PATCH 0295/1054] netfs: Fix folio state after ENOMEM whilst under writeback iteration Fix the state of the current folio when ENOMEM occurs during writeback iteration. The folio needs to be redirtied and unlocked before the terminal writeback_iter() is invoked. Fixes: 06fa229ceb36 ("netfs: Abstract out a rolling folio buffer implementation") Link: https://sashiko.dev/#/patchset/20260619140646.2633762-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260625140640.3116900-15-dhowells@redhat.com cc: Paulo Alcantara cc: Matthew Wilcox cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner (Amutable) --- fs/netfs/write_issue.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/netfs/write_issue.c b/fs/netfs/write_issue.c index 3682896c3fdf..f2761c99795a 100644 --- a/fs/netfs/write_issue.c +++ b/fs/netfs/write_issue.c @@ -582,6 +582,10 @@ int netfs_writepages(struct address_space *mapping, } error = netfs_write_folio(wreq, wbc, folio); + if (error == -ENOMEM) { + folio_redirty_for_writepage(wbc, folio); + folio_unlock(folio); + } } while ((folio = writeback_iter(mapping, wbc, folio, &error))); netfs_end_issue_write(wreq); From 64f04f9789237728be4e1836151848af350d1374 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 25 Jun 2026 15:06:33 +0100 Subject: [PATCH 0296/1054] netfs: Fix DIO write retry for filesystems without a ->prepare_write() Fix netfs_unbuffered_write() so that it doesn't re-issue a write twice when the filesystem doesn't have a ->prepare_write(). The resetting of the iterator and the call to netfs_reissue_write() should just be removed as almost everything it does is done again when the loop it's in goes back to the top. It does, however, still need the IN_PROGRESS flag setting, so that (and the stat inc) are moved out of the if-statement. Further, the MADE_PROGRESS flags should be cleared and wreq->transferred should be updated, so fix those too. Reported-by: syzbot+3c74b1f0c372e98efc32@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=3c74b1f0c372e98efc32 Signed-off-by: David Howells Link: https://patch.msgid.link/20260625140640.3116900-16-dhowells@redhat.com cc: Paulo Alcantara cc: hongao cc: ChenXiaoSong cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner (Amutable) --- fs/netfs/direct_write.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/fs/netfs/direct_write.c b/fs/netfs/direct_write.c index 25f8ceb15fad..c16fbad286a1 100644 --- a/fs/netfs/direct_write.c +++ b/fs/netfs/direct_write.c @@ -166,13 +166,16 @@ static int netfs_unbuffered_write(struct netfs_io_request *wreq) */ subreq->error = -EAGAIN; trace_netfs_sreq(subreq, netfs_sreq_trace_retry); - if (subreq->transferred > 0) + if (subreq->transferred > 0) { iov_iter_advance(&wreq->buffer.iter, subreq->transferred); + wreq->transferred += subreq->transferred; + } if (stream->source == NETFS_UPLOAD_TO_SERVER && wreq->netfs_ops->retry_request) wreq->netfs_ops->retry_request(wreq, stream); + __clear_bit(NETFS_SREQ_MADE_PROGRESS, &subreq->flags); __clear_bit(NETFS_SREQ_NEED_RETRY, &subreq->flags); __clear_bit(NETFS_SREQ_BOUNDARY, &subreq->flags); __clear_bit(NETFS_SREQ_FAILED, &subreq->flags); @@ -186,17 +189,10 @@ static int netfs_unbuffered_write(struct netfs_io_request *wreq) netfs_get_subrequest(subreq, netfs_sreq_trace_get_resubmit); - if (stream->prepare_write) { + if (stream->prepare_write) stream->prepare_write(subreq); - __set_bit(NETFS_SREQ_IN_PROGRESS, &subreq->flags); - netfs_stat(&netfs_n_wh_retry_write_subreq); - } else { - struct iov_iter source; - - netfs_reset_iter(subreq); - source = subreq->io_iter; - netfs_reissue_write(stream, subreq, &source); - } + __set_bit(NETFS_SREQ_IN_PROGRESS, &subreq->flags); + netfs_stat(&netfs_n_wh_retry_write_subreq); } netfs_unbuffered_write_done(wreq); From 6c732471740bc2ac9b0946134f9f551dc75f4369 Mon Sep 17 00:00:00 2001 From: David Lee Date: Wed, 1 Jul 2026 11:44:28 +0000 Subject: [PATCH 0297/1054] fhandle: reject detached mounts in capable_wrt_mount() The recent fhandle RCU fix moved the mount namespace capability check into capable_wrt_mount(), so a non-NULL mnt_namespace survives the ns_capable() dereference. The helper still assumes the later READ_ONCE(mount->mnt_ns) must be non-NULL because may_decode_fh() checked is_mounted() first. That assumption is not stable. A detached mount from open_tree(..., OPEN_TREE_CLONE) can be dissolved on fput while open_by_handle_at() is between those checks, and umount_tree() can clear mount->mnt_ns. If the helper observes NULL, it dereferences mnt_ns->user_ns and panics. Return false when the RCU read observes a detached mount. This keeps the relaxed permission path conservative: a mount no longer attached to a namespace cannot authorize open_by_handle_at() access. Fixes: 620c266f3949 ("fhandle: relax open_by_handle_at() permission checks") Cc: stable@vger.kernel.org Signed-off-by: David Lee Assisted-by: LLM Link: https://patch.msgid.link/20260701114438.24431-1-david.lee@trailofbits.com Reviewed-by: Jeff Layton Signed-off-by: Christian Brauner (Amutable) --- fs/fhandle.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/fhandle.c b/fs/fhandle.c index 1ca7eb3a6cb5..f8829231e3d7 100644 --- a/fs/fhandle.c +++ b/fs/fhandle.c @@ -295,7 +295,7 @@ static bool capable_wrt_mount(struct mount *mount) */ guard(rcu)(); mnt_ns = READ_ONCE(mount->mnt_ns); - return ns_capable(mnt_ns->user_ns, CAP_SYS_ADMIN); + return mnt_ns && ns_capable(mnt_ns->user_ns, CAP_SYS_ADMIN); } static inline int may_decode_fh(struct handle_to_path_ctx *ctx, From 044472d5ee7d71f918fa3f61bd65e4933a0c006e Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 29 Jun 2026 14:17:38 +0200 Subject: [PATCH 0298/1054] iomap: consolidate bio submission Add a iomap_bio_submit_read_endio helper factored out of iomap_bio_submit_read to that all ->submit_read implementations for iomap_read_ops that use iomap_bio_read_folio_range can shared the logic. Right now that logic is mostly trivial, but already has a bug for XFS because the XFS version is too trivial: file system integrity validation needs a workqueue context and thus can't happen from the default iomap bi_end_io I/O handler. Unfortunately the iomap refactoring just before fs integrity landed moved code around here and the call go misplaced, meaning it never got called. The PI information still is verified by the block layer, but the offloading is less efficient (and the future userspace interface can't get at it). Fixes: 0b10a370529c ("iomap: support T10 protection information") Cc: stable@vger.kernel.org # v7.1 Signed-off-by: Christoph Hellwig Link: https://patch.msgid.link/20260629121750.3392300-2-hch@lst.de Acked-by: Namjae Jeon Reviewed-by: "Darrick J. Wong" Reviewed-by: Joanne Koong Signed-off-by: Christian Brauner (Amutable) --- fs/exfat/iomap.c | 5 +---- fs/iomap/bio.c | 13 ++++++++++--- fs/ntfs/aops.c | 6 ++---- fs/ntfs3/inode.c | 5 +---- fs/xfs/xfs_aops.c | 3 +-- include/linux/iomap.h | 2 ++ 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/fs/exfat/iomap.c b/fs/exfat/iomap.c index 1aac38e63fe6..190fc6471f84 100644 --- a/fs/exfat/iomap.c +++ b/fs/exfat/iomap.c @@ -253,10 +253,7 @@ static void exfat_iomap_read_end_io(struct bio *bio) static void exfat_iomap_bio_submit_read(const struct iomap_iter *iter, struct iomap_read_folio_ctx *ctx) { - struct bio *bio = ctx->read_ctx; - - bio->bi_end_io = exfat_iomap_read_end_io; - submit_bio(bio); + iomap_bio_submit_read_endio(iter, ctx, exfat_iomap_read_end_io); } const struct iomap_read_ops exfat_iomap_bio_read_ops = { diff --git a/fs/iomap/bio.c b/fs/iomap/bio.c index 4504f4633f17..0f31e35567b4 100644 --- a/fs/iomap/bio.c +++ b/fs/iomap/bio.c @@ -78,15 +78,23 @@ u32 iomap_finish_ioend_buffered_read(struct iomap_ioend *ioend) return __iomap_read_end_io(&ioend->io_bio, ioend->io_error); } -static void iomap_bio_submit_read(const struct iomap_iter *iter, - struct iomap_read_folio_ctx *ctx) +void iomap_bio_submit_read_endio(const struct iomap_iter *iter, + struct iomap_read_folio_ctx *ctx, bio_end_io_t end_io) { struct bio *bio = ctx->read_ctx; + bio->bi_end_io = end_io; if (iter->iomap.flags & IOMAP_F_INTEGRITY) fs_bio_integrity_alloc(bio); submit_bio(bio); } +EXPORT_SYMBOL_GPL(iomap_bio_submit_read_endio); + +static void iomap_bio_submit_read(const struct iomap_iter *iter, + struct iomap_read_folio_ctx *ctx) +{ + return iomap_bio_submit_read_endio(iter, ctx, iomap_read_end_io); +} static struct bio_set *iomap_read_bio_set(struct iomap_read_folio_ctx *ctx) { @@ -127,7 +135,6 @@ static void iomap_read_alloc_bio(const struct iomap_iter *iter, if (ctx->rac) bio->bi_opf |= REQ_RAHEAD; bio->bi_iter.bi_sector = iomap_sector(iomap, iter->pos); - bio->bi_end_io = iomap_read_end_io; bio_add_folio_nofail(bio, folio, plen, offset_in_folio(folio, iter->pos)); ctx->read_ctx = bio; diff --git a/fs/ntfs/aops.c b/fs/ntfs/aops.c index 1fbf832ad165..f2bb56506046 100644 --- a/fs/ntfs/aops.c +++ b/fs/ntfs/aops.c @@ -38,11 +38,9 @@ static void ntfs_iomap_read_end_io(struct bio *bio) } static void ntfs_iomap_bio_submit_read(const struct iomap_iter *iter, - struct iomap_read_folio_ctx *ctx) + struct iomap_read_folio_ctx *ctx) { - struct bio *bio = ctx->read_ctx; - bio->bi_end_io = ntfs_iomap_read_end_io; - submit_bio(bio); + iomap_bio_submit_read_endio(iter, ctx, ntfs_iomap_read_end_io); } static const struct iomap_read_ops ntfs_iomap_bio_read_ops = { diff --git a/fs/ntfs3/inode.c b/fs/ntfs3/inode.c index c43101cc064d..0c9bd669117d 100644 --- a/fs/ntfs3/inode.c +++ b/fs/ntfs3/inode.c @@ -608,10 +608,7 @@ static void ntfs_iomap_read_end_io(struct bio *bio) static void ntfs_iomap_bio_submit_read(const struct iomap_iter *iter, struct iomap_read_folio_ctx *ctx) { - struct bio *bio = ctx->read_ctx; - - bio->bi_end_io = ntfs_iomap_read_end_io; - submit_bio(bio); + iomap_bio_submit_read_endio(iter, ctx, ntfs_iomap_read_end_io); } static const struct iomap_read_ops ntfs_iomap_bio_read_ops = { diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c index 2a0c54256e93..51293b6f331f 100644 --- a/fs/xfs/xfs_aops.c +++ b/fs/xfs/xfs_aops.c @@ -764,8 +764,7 @@ xfs_bio_submit_read( /* defer read completions to the ioend workqueue */ iomap_init_ioend(iter->inode, bio, ctx->read_ctx_file_offset, 0); - bio->bi_end_io = xfs_end_bio; - submit_bio(bio); + iomap_bio_submit_read_endio(iter, ctx, xfs_end_bio); } static const struct iomap_read_ops xfs_iomap_read_ops = { diff --git a/include/linux/iomap.h b/include/linux/iomap.h index 3582ed1fe236..56b43d594e6e 100644 --- a/include/linux/iomap.h +++ b/include/linux/iomap.h @@ -622,6 +622,8 @@ extern struct bio_set iomap_ioend_bioset; #ifdef CONFIG_BLOCK int iomap_bio_read_folio_range(const struct iomap_iter *iter, struct iomap_read_folio_ctx *ctx, size_t plen); +void iomap_bio_submit_read_endio(const struct iomap_iter *iter, + struct iomap_read_folio_ctx *ctx, bio_end_io_t end_io); extern const struct iomap_read_ops iomap_bio_read_ops; From 3372eb0384b791faf133806da287819f5bfaad76 Mon Sep 17 00:00:00 2001 From: Joanne Koong Date: Mon, 29 Jun 2026 14:17:39 +0200 Subject: [PATCH 0299/1054] fuse: call fuse_send_readpages explicitly from fuse_readahead Move the call to fuse_send_readpages from the iomap ->submit_read method to the fuse readahead implementation. fuse_read_folio() does not need to call fuse_send_readpages() because it always does reads synchronously (the iomap->submit_read method for this was a no-op since data->ia is always NULL for fuse_read_folio()). This prepares for an iomap fix that will call ->submit_read after each iomap. Signed-off-by: Joanne Koong Signed-off-by: Christoph Hellwig Link: https://patch.msgid.link/20260629121750.3392300-3-hch@lst.de Reviewed-by: "Darrick J. Wong" Signed-off-by: Christian Brauner (Amutable) --- fs/fuse/file.c | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/fs/fuse/file.c b/fs/fuse/file.c index e052a0d44dee..ceada75310b8 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -981,19 +981,8 @@ static int fuse_iomap_read_folio_range_async(const struct iomap_iter *iter, return ret; } -static void fuse_iomap_submit_read(const struct iomap_iter *iter, - struct iomap_read_folio_ctx *ctx) -{ - struct fuse_fill_read_data *data = ctx->read_ctx; - - if (data->ia) - fuse_send_readpages(data->ia, data->file, data->nr_bytes, - data->fc->async_read); -} - static const struct iomap_read_ops fuse_iomap_read_ops = { .read_folio_range = fuse_iomap_read_folio_range_async, - .submit_read = fuse_iomap_submit_read, }; static int fuse_read_folio(struct file *file, struct folio *folio) @@ -1116,6 +1105,9 @@ static void fuse_readahead(struct readahead_control *rac) return; iomap_readahead(&fuse_iomap_ops, &ctx, NULL); + if (data.ia) + fuse_send_readpages(data.ia, data.file, data.nr_bytes, + fc->async_read); } static ssize_t fuse_cache_read_iter(struct kiocb *iocb, struct iov_iter *to) From c1fb97d31782f5a8c66d127624626accbb0dd8bc Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 29 Jun 2026 14:17:40 +0200 Subject: [PATCH 0300/1054] iomap: submit read bio after each extent Currently the iomap buffered read path tries to build up read context (i.e. bios for the typical block based case) over multiple iomaps as long as the sector matches. This does not take into account files that can map to multiple different devices. While this could be fixed by a bdev check in iomap_bio_read_folio_range, the building up of I/O over iomaps actually was a problem for the not yet merged ext2 iomap port, as that does want to send out I/O at the end of an indirect block mapped range. So instead of adding more checks move over to a model where a bio only spans a single iomap. Change ->submit_read to be called after each iteration so that the bio based users submit the bio after each iomap. Fuse is unchanged because the previous commit stopped using ->submit_read for it. Fixes: dfeab2e95a75 ("erofs: add multiple device support") Reported-by: Kelu Ye Reported-by: Yifan Zhao Signed-off-by: Christoph Hellwig Link: https://patch.msgid.link/20260629121750.3392300-4-hch@lst.de Tested-by: Yifan Zhao Reviewed-by: "Darrick J. Wong" Signed-off-by: Christian Brauner (Amutable) --- fs/iomap/bio.c | 2 ++ fs/iomap/buffered-io.c | 16 ++++++++-------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/fs/iomap/bio.c b/fs/iomap/bio.c index 0f31e35567b4..dc8ac7e370a5 100644 --- a/fs/iomap/bio.c +++ b/fs/iomap/bio.c @@ -87,6 +87,8 @@ void iomap_bio_submit_read_endio(const struct iomap_iter *iter, if (iter->iomap.flags & IOMAP_F_INTEGRITY) fs_bio_integrity_alloc(bio); submit_bio(bio); + + ctx->read_ctx = NULL; } EXPORT_SYMBOL_GPL(iomap_bio_submit_read_endio); diff --git a/fs/iomap/buffered-io.c b/fs/iomap/buffered-io.c index 8d4806dc46d4..276720bc18dc 100644 --- a/fs/iomap/buffered-io.c +++ b/fs/iomap/buffered-io.c @@ -642,12 +642,12 @@ void iomap_read_folio(const struct iomap_ops *ops, fsverity_readahead(ctx->vi, folio->index, folio_nr_pages(folio)); - while ((ret = iomap_iter(&iter, ops)) > 0) + while ((ret = iomap_iter(&iter, ops)) > 0) { iter.status = iomap_read_folio_iter(&iter, ctx, &bytes_submitted); - - if (ctx->read_ctx && ctx->ops->submit_read) - ctx->ops->submit_read(&iter, ctx); + if (ctx->read_ctx && ctx->ops->submit_read) + ctx->ops->submit_read(&iter, ctx); + } if (ctx->cur_folio) iomap_read_end(ctx->cur_folio, bytes_submitted); @@ -718,12 +718,12 @@ void iomap_readahead(const struct iomap_ops *ops, fsverity_readahead(ctx->vi, readahead_index(rac), readahead_count(rac)); - while (iomap_iter(&iter, ops) > 0) + while (iomap_iter(&iter, ops) > 0) { iter.status = iomap_readahead_iter(&iter, ctx, &cur_bytes_submitted); - - if (ctx->read_ctx && ctx->ops->submit_read) - ctx->ops->submit_read(&iter, ctx); + if (ctx->read_ctx && ctx->ops->submit_read) + ctx->ops->submit_read(&iter, ctx); + } if (ctx->cur_folio) iomap_read_end(ctx->cur_folio, cur_bytes_submitted); From 754e9e49b76fd5be339172aa98544182ed3ca75e Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Tue, 23 Jun 2026 16:20:31 +0200 Subject: [PATCH 0301/1054] pkey: Move keytype check from pkey api to handler The PKEY_VERIFYPROTK ioctl takes data from user-space and verifies the contained protected key. While checking the integrity of the ioctl request structure is the responsibility of the generic pkey_api code, the verification of the contained protected key is the responsibility of the pkey handler. The keytype verification (based on the calculated bitsize of the key) is part of the protected key verification and therefore the responsibility of the pkey handler (which already verifies it). Therefore the keytype verification is removed from the generic pkey_api code. As the calculation of the key bitsize is currently wrong, the removal of the keytype check in pkey_api also removes this wrong calculation. For this reason, the commit is flagged with the Fixes: tag. Cc: stable@kernel.org # 6.12+ Fixes: 8fcc231ce3be ("s390/pkey: Introduce pkey base with handler registry and handler modules") Reviewed-by: Ingo Franzki Reviewed-by: Harald Freudenberger Signed-off-by: Holger Dengler Signed-off-by: Alexander Gordeev Signed-off-by: Vasily Gorbik --- drivers/s390/crypto/pkey_api.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/drivers/s390/crypto/pkey_api.c b/drivers/s390/crypto/pkey_api.c index 28e1007005f2..5d8f63f390a8 100644 --- a/drivers/s390/crypto/pkey_api.c +++ b/drivers/s390/crypto/pkey_api.c @@ -327,7 +327,6 @@ static int pkey_ioctl_verifyprotk(struct pkey_verifyprotk __user *uvp) { struct pkey_verifyprotk kvp; struct protaeskeytoken *t; - u32 keytype; u8 *tmpbuf; int rc; @@ -341,14 +340,6 @@ static int pkey_ioctl_verifyprotk(struct pkey_verifyprotk __user *uvp) return -EINVAL; } - keytype = pkey_aes_bitsize_to_keytype(8 * kvp.protkey.len); - if (!keytype) { - PKEY_DBF_ERR("%s unknown/unsupported protkey length %u\n", - __func__, kvp.protkey.len); - memzero_explicit(&kvp, sizeof(kvp)); - return -EINVAL; - } - /* build a 'protected key token' from the raw protected key */ tmpbuf = kzalloc(sizeof(*t), GFP_KERNEL); if (!tmpbuf) { @@ -358,7 +349,7 @@ static int pkey_ioctl_verifyprotk(struct pkey_verifyprotk __user *uvp) t = (struct protaeskeytoken *)tmpbuf; t->type = TOKTYPE_NON_CCA; t->version = TOKVER_PROTECTED_KEY; - t->keytype = keytype; + t->keytype = kvp.protkey.type; t->len = kvp.protkey.len; memcpy(t->protkey, kvp.protkey.protkey, kvp.protkey.len); From ed0abc8be27e23aa65716bcaab8976ada2503cab Mon Sep 17 00:00:00 2001 From: John Madieu Date: Wed, 10 Jun 2026 16:47:04 +0000 Subject: [PATCH 0302/1054] ASoC: rsnd: adg: make rsnd_adg_clk_control() idempotent rsnd_adg_clk_control() is asymmetric on the disable path: the clkin clocks are guarded by clkin_rate[], but the "adg" clock is disabled unconditionally. If an enable attempt fails (for example a clkin failing to turn on during resume), the error path correctly rolls everything back, but rsnd_resume() ignores the return value, so the following system suspend calls rsnd_adg_clk_disable() again and underflows the "adg" clock enable count: adg_0_clks1 already disabled WARNING: drivers/clk/clk.c:1188 clk_core_disable+0xa4/0xac Call trace: clk_core_disable+0xa4/0xac (P) clk_disable+0x30/0x4c rsnd_adg_clk_control+0x9c/0x2cc rsnd_suspend+0x20/0x74 device_suspend+0x140/0x3ec dpm_suspend+0x168/0x270 Track the enable state explicitly and bail out of redundant enable/disable calls, mirroring what is already done for the per-SSI clock prepare state. A failed enable leaves the state as disabled, so the next suspend becomes a no-op and the next resume retries cleanly. Fixes: 47899d53f86f ("ASoC: rsnd: adg: Add per-SSI ADG and SSIF supply clock management") Signed-off-by: John Madieu Acked-by: Kuninori Morimoto Link: https://patch.msgid.link/20260610164704.2211321-1-john.madieu.xa@bp.renesas.com Signed-off-by: Mark Brown --- sound/soc/renesas/rcar/adg.c | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/sound/soc/renesas/rcar/adg.c b/sound/soc/renesas/rcar/adg.c index 5479cefb6dbe..53efd1be5139 100644 --- a/sound/soc/renesas/rcar/adg.c +++ b/sound/soc/renesas/rcar/adg.c @@ -45,6 +45,7 @@ struct rsnd_adg { struct rsnd_mod mod; int clkin_rate[CLKINMAX]; bool ssi_clk_prepared; + bool clk_enabled; int clkin_size; int clkout_size; u32 ckr; @@ -463,6 +464,22 @@ int rsnd_adg_clk_control(struct rsnd_priv *priv, int enable) struct clk *clk; int ret = 0, i; + /* + * rsnd_adg_clk_enable() and rsnd_adg_clk_disable() can be called + * redundantly, for example when system suspend follows a resume + * whose enable failed. Make this function idempotent so that the + * "adg" clock, which has no clkin_rate[] style guard, is never + * disabled twice. + */ + if (enable) { + if (adg->clk_enabled) + return 0; + } else { + if (!adg->clk_enabled) + return 0; + adg->clk_enabled = false; + } + if (enable) { ret = clk_prepare_enable(adg->adg); if (ret < 0) @@ -520,12 +537,22 @@ int rsnd_adg_clk_control(struct rsnd_priv *priv, int enable) * rsnd_adg_clk_enable() might return error (_disable() will not). * We need to rollback in such case */ - if (ret < 0) + if (ret < 0) { + /* + * Mark as enabled so that the rollback below is not + * short-circuited by the idempotency guard. It clears + * the flag again on its way through. + */ + adg->clk_enabled = true; rsnd_adg_clk_disable(priv); + return ret; + } /* disable adg */ if (!enable) clk_disable_unprepare(adg->adg); + else + adg->clk_enabled = true; return ret; } From d42df9dce7b374079c5c41691bd62d8765768a80 Mon Sep 17 00:00:00 2001 From: Rodrigo Vivi Date: Fri, 12 Jun 2026 12:24:15 -0400 Subject: [PATCH 0303/1054] drm/xe: wedge from the timeout handler only after releasing the queue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A kernel job that exhausts its recovery attempts called xe_device_declare_wedged() directly from guc_exec_queue_timedout_job(), while the handler still owned the timed-out job and the queue scheduler (sched = &q->guc->sched, stopped at the top of the handler). In the default wedged mode (XE_WEDGED_MODE_UPON_CRITICAL_ERROR), xe_device_declare_wedged() takes the destructive path in xe_guc_submit_wedge(): guc_submit_reset_prepare(), xe_guc_submit_stop() - which calls guc_exec_queue_stop() on every queue, including this one - softreset and pause-abort. That tears submission down, signals the in-flight fences and restarts the schedulers. This is the correct behaviour when the wedge originates outside the TDR, but not when the TDR itself triggers it: every queue should be torn down except the one the TDR is currently operating on, which it still owns. Control then returned to the handler, which kept using the now stale job and scheduler: xe_sched_job_set_error(job, err); drm_sched_for_each_pending_job(tmp_job, &sched->base, NULL) xe_sched_job_set_error(to_xe_sched_job(tmp_job), -ECANCELED); drm_sched_for_each_pending_job() warns because the scheduler is no longer stopped (WARN_ON(!drm_sched_is_stopped())) and the iteration then dereferences a freed job, faulting on the slab poison: Oops: general protection fault ... 0x6b6b6b6b6b6b6c3b RIP: guc_exec_queue_timedout_job+... Defer the wedge until the handler has finished operating on the queue, right before returning DRM_GPU_SCHED_STAT_NO_HANG, so the teardown no longer races with this handler's use of @q. Fixes: 770031ec2312 ("drm/xe: fix job timeout recovery for unstarted jobs and kernel queues") Suggested-by: Matthew Brost Cc: Matthew Brost Cc: Thomas Hellström Cc: Himal Prasad Ghimiray Cc: Sanjay Yadav Assisted-by: GitHub-Copilot:claude-opus-4.8 Reviewed-by: Matthew Brost Link: https://patch.msgid.link/20260612162414.287971-2-rodrigo.vivi@intel.com Signed-off-by: Rodrigo Vivi (cherry picked from commit a889e9b06bfdb375fc88b3b2a4b143f621f930c6) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_guc_submit.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_guc_submit.c b/drivers/gpu/drm/xe/xe_guc_submit.c index 12a410458df6..c54dc84cfe60 100644 --- a/drivers/gpu/drm/xe/xe_guc_submit.c +++ b/drivers/gpu/drm/xe/xe_guc_submit.c @@ -1493,7 +1493,7 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job) struct xe_device *xe = guc_to_xe(guc); int err = -ETIME; pid_t pid = -1; - bool wedged = false, skip_timeout_check; + bool wedged = false, wedge_device = false, skip_timeout_check; xe_gt_assert(guc_to_gt(guc), !exec_queue_destroyed(q)); @@ -1638,7 +1638,7 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job) } if (q->flags & EXEC_QUEUE_FLAG_KERNEL) { xe_gt_WARN(q->gt, true, "Kernel-submitted job timed out\n"); - xe_device_declare_wedged(gt_to_xe(q->gt)); + wedge_device = true; } } else if (q->flags & EXEC_QUEUE_FLAG_VM && !exec_queue_killed(q)) { xe_gt_WARN(q->gt, true, "VM job timed out on non-killed execqueue\n"); @@ -1658,6 +1658,9 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job) xe_guc_exec_queue_trigger_cleanup(q); } + if (wedge_device) + xe_device_declare_wedged(gt_to_xe(q->gt)); + /* * We want the job added back to the pending list so it gets freed; this * is what DRM_GPU_SCHED_STAT_NO_HANG does. From 3feeb667197bd58a17f4edfdbcad249ffcb3c864 Mon Sep 17 00:00:00 2001 From: Francois Dugast Date: Tue, 16 Jun 2026 10:17:56 +0200 Subject: [PATCH 0304/1054] drm/xe/pt: Fix NULL pointer dereference in xe_pt_zap_ptes_entry() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The page-table walk framework may pass a NULL *child pointer for unpopulated entries. xe_pt_zap_ptes_entry() called container_of(*child) before checking for NULL, then dereferenced the result, causing a crash. Move the container_of() call after a NULL guard, so the function returns early instead of proceeding with an invalid pointer. XE_WARN_ON is kept to help root cause the issue, but we now bail instead of crashing the driver. v2: Comment that triggering XE_WARN_ON is unexpected behavior (Matt Brost) Fixes: dd08ebf6c352 ("drm/xe: Introduce a new DRM driver for Intel GPUs") Cc: Matthew Brost Cc: Thomas Hellström Reviewed-by: Matthew Brost Link: https://lore.kernel.org/r/20260616081756.286918-1-francois.dugast@intel.com Signed-off-by: Francois Dugast (cherry picked from commit b9297d19d9df5d4b6c994648570c5dcd1cac68ff) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_pt.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_pt.c b/drivers/gpu/drm/xe/xe_pt.c index 18a98667c0e6..234ea175c5e3 100644 --- a/drivers/gpu/drm/xe/xe_pt.c +++ b/drivers/gpu/drm/xe/xe_pt.c @@ -885,12 +885,20 @@ static int xe_pt_zap_ptes_entry(struct xe_ptw *parent, pgoff_t offset, { struct xe_pt_zap_ptes_walk *xe_walk = container_of(walk, typeof(*xe_walk), base); - struct xe_pt *xe_child = container_of(*child, typeof(*xe_child), base); + struct xe_pt *xe_child; pgoff_t end_offset; - XE_WARN_ON(!*child); XE_WARN_ON(!level); + /* + * Below would be unexpected behavior that needs to be root caused + * but better warn and bail than crash the driver. + */ + if (XE_WARN_ON(!*child)) + return 0; + + xe_child = container_of(*child, typeof(*xe_child), base); + /* * Note that we're called from an entry callback, and we're dealing * with the child of that entry rather than the parent, so need to From 334c1ce4253d55082be684178a0a5de66ee4199f Mon Sep 17 00:00:00 2001 From: Lu Yao Date: Wed, 17 Jun 2026 09:25:16 +0800 Subject: [PATCH 0305/1054] drm/xe: Remove redundant exec_queue_suspended() check in submit_exec_queue() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There already has a check for exec_queue_suspended(q) that returns early if suspended. Fixes: 65280af331aa ("drm/xe/multi_queue: skip submit when primary queue is suspended") Signed-off-by: Lu Yao Reviewed-by: Rodrigo Vivi Link: https://patch.msgid.link/20260617012516.19930-1-yaolu@kylinos.cn Signed-off-by: Rodrigo Vivi (cherry picked from commit 173202a5a3a9e6590194ce0f5880d1529a71ade7) Signed-off-by: Thomas Hellström --- 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 c54dc84cfe60..f5c3d8a97ec6 100644 --- a/drivers/gpu/drm/xe/xe_guc_submit.c +++ b/drivers/gpu/drm/xe/xe_guc_submit.c @@ -1163,7 +1163,7 @@ static void submit_exec_queue(struct xe_exec_queue *q, struct xe_sched_job *job) if (exec_queue_suspended(q)) return; - if (!exec_queue_enabled(q) && !exec_queue_suspended(q)) { + if (!exec_queue_enabled(q)) { action[len++] = XE_GUC_ACTION_SCHED_CONTEXT_MODE_SET; action[len++] = q->guc->id; action[len++] = GUC_CONTEXT_ENABLE; From e70086a3a06d276b4a5d9a2c51c9330c6cf72780 Mon Sep 17 00:00:00 2001 From: Ashutosh Dixit Date: Mon, 15 Jun 2026 15:42:19 -0700 Subject: [PATCH 0306/1054] drm/xe/rtp: Add RING_FORCE_TO_NONPRIV_DENY to OA whitelists MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Unconditionally whitelisting OA registers is a security violation. Set RING_FORCE_TO_NONPRIV_DENY bit in OA nonpriv slots, so that OA registers don't get whitelisted by default after probe, gt reset, resume and engine reset. Fixes: 828a8eaf37c3 ("drm/xe/oa: Add MMIO trigger support") Cc: stable@vger.kernel.org # v6.12+ Suggested-by: Umesh Nerlige Ramappa Signed-off-by: Ashutosh Dixit Reviewed-by: Umesh Nerlige Ramappa Link: https://patch.msgid.link/20260615224227.34880-2-ashutosh.dixit@intel.com (cherry picked from commit 90511bdcfda97211c01f1d945d4ea616578d8fca) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_reg_whitelist.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_reg_whitelist.c b/drivers/gpu/drm/xe/xe_reg_whitelist.c index fb65940848d7..d3bfc05949ae 100644 --- a/drivers/gpu/drm/xe/xe_reg_whitelist.c +++ b/drivers/gpu/drm/xe/xe_reg_whitelist.c @@ -104,10 +104,12 @@ static const struct xe_rtp_entry_sr register_whitelist[] = { RING_FORCE_TO_NONPRIV_ACCESS_RW)) }, +#define WHITELIST_DENY(r, f) WHITELIST(r, (f) | RING_FORCE_TO_NONPRIV_DENY) + #define WHITELIST_OA_MMIO_TRG(trg, status, head) \ - WHITELIST(trg, RING_FORCE_TO_NONPRIV_ACCESS_RW), \ - WHITELIST(status, RING_FORCE_TO_NONPRIV_ACCESS_RD), \ - WHITELIST(head, RING_FORCE_TO_NONPRIV_ACCESS_RD | RING_FORCE_TO_NONPRIV_RANGE_4) + WHITELIST_DENY(trg, RING_FORCE_TO_NONPRIV_ACCESS_RW), \ + WHITELIST_DENY(status, RING_FORCE_TO_NONPRIV_ACCESS_RD), \ + WHITELIST_DENY(head, RING_FORCE_TO_NONPRIV_ACCESS_RD | RING_FORCE_TO_NONPRIV_RANGE_4) #define WHITELIST_OAG_MMIO_TRG \ WHITELIST_OA_MMIO_TRG(OAG_MMIOTRIGGER, OAG_OASTATUS, OAG_OAHEADPTR) From aff079bdce65f6d085e4b0091fdf87fffa95b0d9 Mon Sep 17 00:00:00 2001 From: Jakob Linke Date: Wed, 17 Jun 2026 08:24:15 +0200 Subject: [PATCH 0307/1054] drm/amdgpu/soc24: reset dGPU if suspend got aborted For SOC24 ASICs (RDNA4 / Navi 4x dGPUs) re-enabling PM features fails if an S3 suspend got aborted, the same issue already handled for SOC21 and SOC15: commit df3c7dc5c58b ("drm/amdgpu: Reset dGPU if suspend got aborted") commit 38e8ca3e4b6d ("amdgpu/soc15: enable asic reset for dGPU in case of suspend abort") The aborted resume fails with: amdgpu: SMU: No response msg_reg: 6 resp_reg: 0 amdgpu: Failed to enable requested dpm features! amdgpu: resume of IP block failed -62 Apply the same workaround for soc24: detect the aborted-suspend state at resume via the sign-of-life register and reset the device before re-init. This is a workaround till a proper solution is finalized. Fixes: 98b912c50e44 ("drm/amdgpu: Add soc24 common ip block (v2)") Signed-off-by: Jakob Linke Signed-off-by: Alex Deucher (cherry picked from commit fed5bdbfe1d4a19a26c70f7fc58017dc88be1c18) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/soc24.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/soc24.c b/drivers/gpu/drm/amd/amdgpu/soc24.c index 265db9331d0b..9dce30d2bb8d 100644 --- a/drivers/gpu/drm/amd/amdgpu/soc24.c +++ b/drivers/gpu/drm/amd/amdgpu/soc24.c @@ -496,8 +496,36 @@ static int soc24_common_suspend(struct amdgpu_ip_block *ip_block) return soc24_common_hw_fini(ip_block); } +static bool soc24_need_reset_on_resume(struct amdgpu_device *adev) +{ + u32 sol_reg1, sol_reg2; + + /* Will reset for the following suspend abort cases. + * 1) Only reset dGPU side. + * 2) S3 suspend got aborted and TOS is active. + * As for dGPU suspend abort cases the SOL value + * will be kept as zero at this resume point. + */ + if (!(adev->flags & AMD_IS_APU) && adev->in_s3) { + sol_reg1 = RREG32_SOC15(MP0, 0, regMPASP_SMN_C2PMSG_81); + msleep(100); + sol_reg2 = RREG32_SOC15(MP0, 0, regMPASP_SMN_C2PMSG_81); + + return (sol_reg1 != sol_reg2); + } + + return false; +} + static int soc24_common_resume(struct amdgpu_ip_block *ip_block) { + struct amdgpu_device *adev = ip_block->adev; + + if (soc24_need_reset_on_resume(adev)) { + dev_info(adev->dev, "S3 suspend aborted, resetting..."); + soc24_asic_reset(adev); + } + return soc24_common_hw_init(ip_block); } From 84a1a8a952ab4b8c23c5dd1f2eea4049cb4914f5 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 15 Jun 2026 18:17:59 -0400 Subject: [PATCH 0308/1054] drm/amdgpu/gfx8: drop unecessary BUG_ON() There's no need to crash the kernel for this case. Reviewed-by: Vitaly Prosyak Signed-off-by: Alex Deucher (cherry picked from commit 4d7c25208ca612b754f3bf39e9f16e725b828891) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c index 130196859ff3..70ba81e6b4d4 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c @@ -6256,9 +6256,6 @@ static void gfx_v8_0_ring_emit_fence_compute(struct amdgpu_ring *ring, static void gfx_v8_0_ring_emit_fence_kiq(struct amdgpu_ring *ring, u64 addr, u64 seq, unsigned int flags) { - /* we only allocate 32bit for each seq wb address */ - BUG_ON(flags & AMDGPU_FENCE_FLAG_64BIT); - /* write fence seq to the "addr" */ amdgpu_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3)); amdgpu_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) | From 6302be10b521f5106ce01eb5a724b9e7945a5061 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 15 Jun 2026 18:14:59 -0400 Subject: [PATCH 0309/1054] drm/amdgpu/gfx9: replace BUG_ON() with WARN_ON() There's no need to crash the kernel for these cases. Reviewed-by: Vitaly Prosyak Signed-off-by: Alex Deucher (cherry picked from commit b71604f8685b0eba07866f4e8dc30f93e1931054) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c index 81a759a98725..3370f542e990 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c @@ -1183,7 +1183,7 @@ static void gfx_v9_0_wait_reg_mem(struct amdgpu_ring *ring, int eng_sel, WAIT_REG_MEM_ENGINE(eng_sel))); if (mem_space) - BUG_ON(addr0 & 0x3); /* Dword align */ + WARN_ON(addr0 & 0x3); /* Dword align */ amdgpu_ring_write(ring, addr0); amdgpu_ring_write(ring, addr1); amdgpu_ring_write(ring, ref); @@ -5474,7 +5474,7 @@ static void gfx_v9_0_ring_emit_ib_gfx(struct amdgpu_ring *ring, } amdgpu_ring_write(ring, header); - BUG_ON(ib->gpu_addr & 0x3); /* Dword align */ + WARN_ON(ib->gpu_addr & 0x3); /* Dword align */ amdgpu_ring_write(ring, #ifdef __BIG_ENDIAN (2 << 0) | @@ -5570,7 +5570,7 @@ static void gfx_v9_0_ring_emit_ib_compute(struct amdgpu_ring *ring, } amdgpu_ring_write(ring, PACKET3(PACKET3_INDIRECT_BUFFER, 2)); - BUG_ON(ib->gpu_addr & 0x3); /* Dword align */ + WARN_ON(ib->gpu_addr & 0x3); /* Dword align */ amdgpu_ring_write(ring, #ifdef __BIG_ENDIAN (2 << 0) | @@ -5611,9 +5611,9 @@ static void gfx_v9_0_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, * aligned if only send 32bit data low (discard data high) */ if (write64bit) - BUG_ON(addr & 0x7); + WARN_ON(addr & 0x7); else - BUG_ON(addr & 0x3); + WARN_ON(addr & 0x3); amdgpu_ring_write(ring, lower_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(addr)); amdgpu_ring_write(ring, lower_32_bits(seq)); From 00f4050f7c367d7bdce347ca279ce467c434cf15 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 15 Jun 2026 18:42:35 -0400 Subject: [PATCH 0310/1054] drm/amdgpu/gfx9.4.3: replace BUG_ON() with WARN_ON() There's no need to crash the kernel for these cases. Reviewed-by: Vitaly Prosyak Signed-off-by: Alex Deucher (cherry picked from commit 5676593d08998d7a6d9e2d51d6b54b3820e3755c) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c b/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c index 510266ba0c38..2a36647b975a 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c @@ -405,7 +405,7 @@ static void gfx_v9_4_3_wait_reg_mem(struct amdgpu_ring *ring, int eng_sel, WAIT_REG_MEM_ENGINE(eng_sel))); if (mem_space) - BUG_ON(addr0 & 0x3); /* Dword align */ + WARN_ON(addr0 & 0x3); /* Dword align */ amdgpu_ring_write(ring, addr0); amdgpu_ring_write(ring, addr1); amdgpu_ring_write(ring, ref); @@ -2944,7 +2944,7 @@ static void gfx_v9_4_3_ring_emit_ib_compute(struct amdgpu_ring *ring, } amdgpu_ring_write(ring, PACKET3(PACKET3_INDIRECT_BUFFER, 2)); - BUG_ON(ib->gpu_addr & 0x3); /* Dword align */ + WARN_ON(ib->gpu_addr & 0x3); /* Dword align */ amdgpu_ring_write(ring, #ifdef __BIG_ENDIAN (2 << 0) | @@ -2978,9 +2978,9 @@ static void gfx_v9_4_3_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, * aligned if only send 32bit data low (discard data high) */ if (write64bit) - BUG_ON(addr & 0x7); + WARN_ON(addr & 0x7); else - BUG_ON(addr & 0x3); + WARN_ON(addr & 0x3); amdgpu_ring_write(ring, lower_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(addr)); amdgpu_ring_write(ring, lower_32_bits(seq)); @@ -3040,9 +3040,6 @@ static void gfx_v9_4_3_ring_emit_fence_kiq(struct amdgpu_ring *ring, u64 addr, { struct amdgpu_device *adev = ring->adev; - /* we only allocate 32bit for each seq wb address */ - BUG_ON(flags & AMDGPU_FENCE_FLAG_64BIT); - /* write fence seq to the "addr" */ amdgpu_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3)); amdgpu_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) | From d06c4173a7c38c7a39e98859f839ce714c7af2c9 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 15 Jun 2026 18:19:52 -0400 Subject: [PATCH 0311/1054] drm/amdgpu/gfx10: replace BUG_ON() with WARN_ON() There's no need to crash the kernel for these cases. Reviewed-by: Vitaly Prosyak Signed-off-by: Alex Deucher (cherry picked from commit ac6f00beb658239bced4aaed9efbb04a35348d48) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c index 0780c5e5de4f..b4b27e4c495d 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c @@ -4022,7 +4022,7 @@ static void gfx_v10_0_wait_reg_mem(struct amdgpu_ring *ring, int eng_sel, WAIT_REG_MEM_ENGINE(eng_sel))); if (mem_space) - BUG_ON(addr0 & 0x3); /* Dword align */ + WARN_ON(addr0 & 0x3); /* Dword align */ amdgpu_ring_write(ring, addr0); amdgpu_ring_write(ring, addr1); amdgpu_ring_write(ring, ref); @@ -8658,7 +8658,7 @@ static void gfx_v10_0_ring_emit_ib_gfx(struct amdgpu_ring *ring, } amdgpu_ring_write(ring, header); - BUG_ON(ib->gpu_addr & 0x3); /* Dword align */ + WARN_ON(ib->gpu_addr & 0x3); /* Dword align */ amdgpu_ring_write(ring, #ifdef __BIG_ENDIAN (2 << 0) | @@ -8693,7 +8693,7 @@ static void gfx_v10_0_ring_emit_ib_compute(struct amdgpu_ring *ring, } amdgpu_ring_write(ring, PACKET3(PACKET3_INDIRECT_BUFFER, 2)); - BUG_ON(ib->gpu_addr & 0x3); /* Dword align */ + WARN_ON(ib->gpu_addr & 0x3); /* Dword align */ amdgpu_ring_write(ring, #ifdef __BIG_ENDIAN (2 << 0) | @@ -8726,9 +8726,9 @@ static void gfx_v10_0_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, * aligned if only send 32bit data low (discard data high) */ if (write64bit) - BUG_ON(addr & 0x7); + WARN_ON(addr & 0x7); else - BUG_ON(addr & 0x3); + WARN_ON(addr & 0x3); amdgpu_ring_write(ring, lower_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(addr)); amdgpu_ring_write(ring, lower_32_bits(seq)); @@ -8776,9 +8776,6 @@ static void gfx_v10_0_ring_emit_fence_kiq(struct amdgpu_ring *ring, u64 addr, { struct amdgpu_device *adev = ring->adev; - /* we only allocate 32bit for each seq wb address */ - BUG_ON(flags & AMDGPU_FENCE_FLAG_64BIT); - /* write fence seq to the "addr" */ amdgpu_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3)); amdgpu_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) | From 0eebcab1ea2a77f086a04108f386f82ee3496022 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 15 Jun 2026 18:20:55 -0400 Subject: [PATCH 0312/1054] drm/amdgpu/gfx11: replace BUG_ON() with WARN_ON() There's no need to crash the kernel for these cases. Reviewed-by: Vitaly Prosyak Signed-off-by: Alex Deucher (cherry picked from commit daa62107452d2451787c4248ca38fa2d1a0cbefd) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c index f856b0cf5bec..92c16392b916 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c @@ -546,7 +546,7 @@ static void gfx_v11_0_wait_reg_mem(struct amdgpu_ring *ring, int eng_sel, WAIT_REG_MEM_ENGINE(eng_sel))); if (mem_space) - BUG_ON(addr0 & 0x3); /* Dword align */ + WARN_ON(addr0 & 0x3); /* Dword align */ amdgpu_ring_write(ring, addr0); amdgpu_ring_write(ring, addr1); amdgpu_ring_write(ring, ref); @@ -5997,7 +5997,7 @@ static void gfx_v11_0_ring_emit_ib_gfx(struct amdgpu_ring *ring, } amdgpu_ring_write(ring, header); - BUG_ON(ib->gpu_addr & 0x3); /* Dword align */ + WARN_ON(ib->gpu_addr & 0x3); /* Dword align */ amdgpu_ring_write(ring, #ifdef __BIG_ENDIAN (2 << 0) | @@ -6032,7 +6032,7 @@ static void gfx_v11_0_ring_emit_ib_compute(struct amdgpu_ring *ring, } amdgpu_ring_write(ring, PACKET3(PACKET3_INDIRECT_BUFFER, 2)); - BUG_ON(ib->gpu_addr & 0x3); /* Dword align */ + WARN_ON(ib->gpu_addr & 0x3); /* Dword align */ amdgpu_ring_write(ring, #ifdef __BIG_ENDIAN (2 << 0) | @@ -6065,9 +6065,9 @@ static void gfx_v11_0_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, * aligned if only send 32bit data low (discard data high) */ if (write64bit) - BUG_ON(addr & 0x7); + WARN_ON(addr & 0x7); else - BUG_ON(addr & 0x3); + WARN_ON(addr & 0x3); amdgpu_ring_write(ring, lower_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(addr)); amdgpu_ring_write(ring, lower_32_bits(seq)); @@ -6121,9 +6121,6 @@ static void gfx_v11_0_ring_emit_fence_kiq(struct amdgpu_ring *ring, u64 addr, { struct amdgpu_device *adev = ring->adev; - /* we only allocate 32bit for each seq wb address */ - BUG_ON(flags & AMDGPU_FENCE_FLAG_64BIT); - /* write fence seq to the "addr" */ amdgpu_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3)); amdgpu_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) | From cd3b3efa1ced05528d9128755338baa62a6b562d Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 15 Jun 2026 18:21:58 -0400 Subject: [PATCH 0313/1054] drm/amdgpu/gfx12: replace BUG_ON() with WARN_ON() There's no need to crash the kernel for these cases. Reviewed-by: Vitaly Prosyak Signed-off-by: Alex Deucher (cherry picked from commit f952076f76d62f783e8ba4995a7c400d39354ccf) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c index f66293fc675e..989c8e2baf6a 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c @@ -440,7 +440,7 @@ static void gfx_v12_0_wait_reg_mem(struct amdgpu_ring *ring, int eng_sel, WAIT_REG_MEM_ENGINE(eng_sel))); if (mem_space) - BUG_ON(addr0 & 0x3); /* Dword align */ + WARN_ON(addr0 & 0x3); /* Dword align */ amdgpu_ring_write(ring, addr0); amdgpu_ring_write(ring, addr1); amdgpu_ring_write(ring, ref); @@ -4493,7 +4493,7 @@ static void gfx_v12_0_ring_emit_ib_gfx(struct amdgpu_ring *ring, control |= ib->length_dw | (vmid << 24); amdgpu_ring_write(ring, header); - BUG_ON(ib->gpu_addr & 0x3); /* Dword align */ + WARN_ON(ib->gpu_addr & 0x3); /* Dword align */ amdgpu_ring_write(ring, #ifdef __BIG_ENDIAN (2 << 0) | @@ -4512,7 +4512,7 @@ static void gfx_v12_0_ring_emit_ib_compute(struct amdgpu_ring *ring, u32 control = INDIRECT_BUFFER_VALID | ib->length_dw | (vmid << 24); amdgpu_ring_write(ring, PACKET3(PACKET3_INDIRECT_BUFFER, 2)); - BUG_ON(ib->gpu_addr & 0x3); /* Dword align */ + WARN_ON(ib->gpu_addr & 0x3); /* Dword align */ amdgpu_ring_write(ring, #ifdef __BIG_ENDIAN (2 << 0) | @@ -4543,9 +4543,9 @@ static void gfx_v12_0_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, * aligned if only send 32bit data low (discard data high) */ if (write64bit) - BUG_ON(addr & 0x7); + WARN_ON(addr & 0x7); else - BUG_ON(addr & 0x3); + WARN_ON(addr & 0x3); amdgpu_ring_write(ring, lower_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(addr)); amdgpu_ring_write(ring, lower_32_bits(seq)); @@ -4593,9 +4593,6 @@ static void gfx_v12_0_ring_emit_fence_kiq(struct amdgpu_ring *ring, u64 addr, { struct amdgpu_device *adev = ring->adev; - /* we only allocate 32bit for each seq wb address */ - BUG_ON(flags & AMDGPU_FENCE_FLAG_64BIT); - /* write fence seq to the "addr" */ amdgpu_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3)); amdgpu_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) | From 6560e6bd76127844e39f09fa591c2791dc7932e8 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 15 Jun 2026 18:22:53 -0400 Subject: [PATCH 0314/1054] drm/amdgpu/gfx12.1: replace BUG_ON() with WARN_ON() There's no need to crash the kernel for these cases. Reviewed-by: Vitaly Prosyak Signed-off-by: Alex Deucher (cherry picked from commit e4d99e04b2e9b13b97d3b17804c735f62689db23) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/gfx_v12_1.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v12_1.c b/drivers/gpu/drm/amd/amdgpu/gfx_v12_1.c index 61c3577f829f..02c9cda186ee 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v12_1.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v12_1.c @@ -248,7 +248,7 @@ static void gfx_v12_1_wait_reg_mem(struct amdgpu_ring *ring, int eng_sel, PACKET3_WAIT_REG_MEM__FUNCTION(3))); /* equal */ if (mem_space) - BUG_ON(addr0 & 0x3); /* Dword align */ + WARN_ON(addr0 & 0x3); /* Dword align */ amdgpu_ring_write(ring, addr0); amdgpu_ring_write(ring, addr1); amdgpu_ring_write(ring, ref); @@ -3433,7 +3433,7 @@ static void gfx_v12_1_ring_emit_ib_compute(struct amdgpu_ring *ring, } amdgpu_ring_write(ring, PACKET3(PACKET3_INDIRECT_BUFFER, 2)); - BUG_ON(ib->gpu_addr & 0x3); /* Dword align */ + WARN_ON(ib->gpu_addr & 0x3); /* Dword align */ amdgpu_ring_write(ring, #ifdef __BIG_ENDIAN (2 << 0) | @@ -3466,9 +3466,9 @@ static void gfx_v12_1_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, * aligned if only send 32bit data low (discard data high) */ if (write64bit) - BUG_ON(addr & 0x7); + WARN_ON(addr & 0x7); else - BUG_ON(addr & 0x3); + WARN_ON(addr & 0x3); amdgpu_ring_write(ring, lower_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(addr)); amdgpu_ring_write(ring, lower_32_bits(seq)); @@ -3515,9 +3515,6 @@ static void gfx_v12_1_ring_emit_fence_kiq(struct amdgpu_ring *ring, u64 addr, { struct amdgpu_device *adev = ring->adev; - /* we only allocate 32bit for each seq wb address */ - BUG_ON(flags & AMDGPU_FENCE_FLAG_64BIT); - /* write fence seq to the "addr" */ amdgpu_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3)); amdgpu_ring_write(ring, (PACKET3_WRITE_DATA__DST_SEL(5) | PACKET3_WRITE_DATA__WR_CONFIRM(1))); From 40cdbe9fa424cc6264a7aed93a04bd7d69109d9e Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 15 Jun 2026 18:44:11 -0400 Subject: [PATCH 0315/1054] drm/amdgpu/sdma4.4.2: replace BUG_ON() with WARN_ON() There's no need to crash the kernel for these cases. Reviewed-by: Vitaly Prosyak Signed-off-by: Alex Deucher (cherry picked from commit fa4f86a148271e325e95287630a3a15a9cd35fdc) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c b/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c index 88428b88e00f..8652928861ad 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c @@ -457,7 +457,7 @@ static void sdma_v4_4_2_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 /* write the fence */ amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_FENCE)); /* zero in first two bits */ - BUG_ON(addr & 0x3); + WARN_ON(addr & 0x3); amdgpu_ring_write(ring, lower_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(addr)); amdgpu_ring_write(ring, lower_32_bits(seq)); @@ -467,7 +467,7 @@ static void sdma_v4_4_2_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 addr += 4; amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_FENCE)); /* zero in first two bits */ - BUG_ON(addr & 0x3); + WARN_ON(addr & 0x3); amdgpu_ring_write(ring, lower_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(seq)); From 9e98ed3113943257ad6e5c1e6beddbdb482a70ad Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 15 Jun 2026 18:26:28 -0400 Subject: [PATCH 0316/1054] drm/amdgpu/sdma5.0: replace BUG_ON() with WARN_ON() There's no need to crash the kernel for these cases. Reviewed-by: Vitaly Prosyak Signed-off-by: Alex Deucher (cherry picked from commit 8d144a0eb09537055841af48c9e7c2d4cd48e84d) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c index fa02907217e0..b809942b1eb7 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c @@ -527,7 +527,7 @@ static void sdma_v5_0_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 se amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_FENCE) | SDMA_PKT_FENCE_HEADER_MTYPE(0x3)); /* Ucached(UC) */ /* zero in first two bits */ - BUG_ON(addr & 0x3); + WARN_ON(addr & 0x3); amdgpu_ring_write(ring, lower_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(addr)); amdgpu_ring_write(ring, lower_32_bits(seq)); @@ -538,7 +538,7 @@ static void sdma_v5_0_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 se amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_FENCE) | SDMA_PKT_FENCE_HEADER_MTYPE(0x3)); /* zero in first two bits */ - BUG_ON(addr & 0x3); + WARN_ON(addr & 0x3); amdgpu_ring_write(ring, lower_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(seq)); From b9dd618a635d39fbb211454b6e8837b2a7f10fb0 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 15 Jun 2026 18:27:15 -0400 Subject: [PATCH 0317/1054] drm/amdgpu/sdma5.2: replace BUG_ON() with WARN_ON() There's no need to crash the kernel for these cases. Reviewed-by: Vitaly Prosyak Signed-off-by: Alex Deucher (cherry picked from commit ae658afc7f47f6147371ec42cc6b1a793dfdb5af) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/sdma_v5_2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v5_2.c b/drivers/gpu/drm/amd/amdgpu/sdma_v5_2.c index f6ecbc524c9b..87c1e29fd298 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v5_2.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v5_2.c @@ -377,7 +377,7 @@ static void sdma_v5_2_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 se amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_FENCE) | SDMA_PKT_FENCE_HEADER_MTYPE(0x3)); /* Ucached(UC) */ /* zero in first two bits */ - BUG_ON(addr & 0x3); + WARN_ON(addr & 0x3); amdgpu_ring_write(ring, lower_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(addr)); amdgpu_ring_write(ring, lower_32_bits(seq)); @@ -388,7 +388,7 @@ static void sdma_v5_2_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 se amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_FENCE) | SDMA_PKT_FENCE_HEADER_MTYPE(0x3)); /* zero in first two bits */ - BUG_ON(addr & 0x3); + WARN_ON(addr & 0x3); amdgpu_ring_write(ring, lower_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(seq)); From ec42c96c322e5cc48099ab5e67b5cbe236cb1949 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 15 Jun 2026 18:27:54 -0400 Subject: [PATCH 0318/1054] drm/amdgpu/sdma6.0: replace BUG_ON() with WARN_ON() There's no need to crash the kernel for these cases. Reviewed-by: Vitaly Prosyak Signed-off-by: Alex Deucher (cherry picked from commit c17a508a7d652da3728f8bbc481bfffe96d65a87) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c index d894b7599c18..d7537888e60c 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c @@ -361,7 +361,7 @@ static void sdma_v6_0_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 se amdgpu_ring_write(ring, SDMA_PKT_COPY_LINEAR_HEADER_OP(SDMA_OP_FENCE) | SDMA_PKT_FENCE_HEADER_MTYPE(0x3)); /* Ucached(UC) */ /* zero in first two bits */ - BUG_ON(addr & 0x3); + WARN_ON(addr & 0x3); amdgpu_ring_write(ring, lower_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(addr)); amdgpu_ring_write(ring, lower_32_bits(seq)); @@ -372,7 +372,7 @@ static void sdma_v6_0_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 se amdgpu_ring_write(ring, SDMA_PKT_COPY_LINEAR_HEADER_OP(SDMA_OP_FENCE) | SDMA_PKT_FENCE_HEADER_MTYPE(0x3)); /* zero in first two bits */ - BUG_ON(addr & 0x3); + WARN_ON(addr & 0x3); amdgpu_ring_write(ring, lower_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(seq)); From e80e28f398f5d9f6e361ffb56382d2e74fc87556 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 15 Jun 2026 18:28:29 -0400 Subject: [PATCH 0319/1054] drm/amdgpu/sdma7.0: replace BUG_ON() with WARN_ON() There's no need to crash the kernel for these cases. Reviewed-by: Vitaly Prosyak Signed-off-by: Alex Deucher (cherry picked from commit 9723a8bed3aa251a26bee4583bac9d8fb064dd44) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c index f154b68dda70..49c57a38151b 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c @@ -363,7 +363,7 @@ static void sdma_v7_0_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 se amdgpu_ring_write(ring, SDMA_PKT_COPY_LINEAR_HEADER_OP(SDMA_OP_FENCE) | SDMA_PKT_FENCE_HEADER_MTYPE(0x3)); /* Ucached(UC) */ /* zero in first two bits */ - BUG_ON(addr & 0x3); + WARN_ON(addr & 0x3); amdgpu_ring_write(ring, lower_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(addr)); amdgpu_ring_write(ring, lower_32_bits(seq)); @@ -374,7 +374,7 @@ static void sdma_v7_0_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 se amdgpu_ring_write(ring, SDMA_PKT_COPY_LINEAR_HEADER_OP(SDMA_OP_FENCE) | SDMA_PKT_FENCE_HEADER_MTYPE(0x3)); /* zero in first two bits */ - BUG_ON(addr & 0x3); + WARN_ON(addr & 0x3); amdgpu_ring_write(ring, lower_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(seq)); From 767648c18d7872bbf54481ba846e055f7e1c0213 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 15 Jun 2026 18:29:00 -0400 Subject: [PATCH 0320/1054] drm/amdgpu/sdma7.1: replace BUG_ON() with WARN_ON() There's no need to crash the kernel for these cases. Reviewed-by: Vitaly Prosyak Signed-off-by: Alex Deucher (cherry picked from commit c4f230b51cf2d3e7e8b1c800331f3dbed2a9e3f5) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c b/drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c index cd9668605a50..b06001f6b536 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c @@ -331,7 +331,7 @@ static void sdma_v7_1_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 se amdgpu_ring_write(ring, SDMA_PKT_COPY_LINEAR_HEADER_OP(SDMA_OP_FENCE) | SDMA_PKT_FENCE_HEADER_MTYPE(0x3)); /* Ucached(UC) */ /* zero in first two bits */ - BUG_ON(addr & 0x3); + WARN_ON(addr & 0x3); amdgpu_ring_write(ring, lower_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(addr)); amdgpu_ring_write(ring, lower_32_bits(seq)); @@ -342,7 +342,7 @@ static void sdma_v7_1_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 se amdgpu_ring_write(ring, SDMA_PKT_COPY_LINEAR_HEADER_OP(SDMA_OP_FENCE) | SDMA_PKT_FENCE_HEADER_MTYPE(0x3)); /* zero in first two bits */ - BUG_ON(addr & 0x3); + WARN_ON(addr & 0x3); amdgpu_ring_write(ring, lower_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(seq)); From 53c78ab388bfc1a4d72e756815d0db0a842c812e Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Fri, 12 Jun 2026 10:55:09 +0800 Subject: [PATCH 0321/1054] drm/amd/pm: make pp_features read-only when scpm is enabled SCPM owns power feature control when enabled. Make pp_features read-only during sysfs setup by clearing its write bits and store callback. Signed-off-by: Yang Wang Reviewed-by: Asad Kamal Signed-off-by: Alex Deucher (cherry picked from commit 6a5786e191fdce36c5db170e5209cf609e8f0087) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/pm/amdgpu_pm.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/gpu/drm/amd/pm/amdgpu_pm.c b/drivers/gpu/drm/amd/pm/amdgpu_pm.c index f43d09769320..2703f95d3d98 100644 --- a/drivers/gpu/drm/amd/pm/amdgpu_pm.c +++ b/drivers/gpu/drm/amd/pm/amdgpu_pm.c @@ -2696,6 +2696,11 @@ static int default_attr_update(struct amdgpu_device *adev, struct amdgpu_device_ gc_ver != IP_VERSION(9, 4, 3)) || gc_ver < IP_VERSION(9, 0, 0)) *states = ATTR_STATE_UNSUPPORTED; + + if (adev->scpm_enabled) { + dev_attr->attr.mode &= ~S_IWUGO; + dev_attr->store = NULL; + } } else if (DEVICE_ATTR_IS(gpu_metrics)) { if (gc_ver < IP_VERSION(9, 1, 0)) *states = ATTR_STATE_UNSUPPORTED; From 238baca26a6279e688d1a156bd031390b82eb578 Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Thu, 18 Jun 2026 12:54:14 +0800 Subject: [PATCH 0322/1054] drm/amd/pm: fix amdgpu_pm_info power display units amdgpu_pm_info displayed power sensor readings with the wrong fractional unit. It treated the low byte of the raw sensor value as the decimal part of watts, while that field represents milliwatts in the decoded value. As a result, debugfs could report misleading SoC power when the remainder was not already a two-digit centiwatt value. Example with query = 0x00000354: raw field value --------------------- query >> 8 3 W query & 0xff 84 mW decoded power 3084 mW output value --------------------- before 3.84 W after 3.08 W Fixes: f0b8f65b4825 ("drm/amd/amdgpu: fix the GPU power print error in pm info") Signed-off-by: Yang Wang Reviewed-by: Asad Kamal Signed-off-by: Alex Deucher (cherry picked from commit 01992b121fb652c753d37e0c1427a2d1a557d2b1) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/pm/amdgpu_pm.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/amd/pm/amdgpu_pm.c b/drivers/gpu/drm/amd/pm/amdgpu_pm.c index 2703f95d3d98..97da01aff76c 100644 --- a/drivers/gpu/drm/amd/pm/amdgpu_pm.c +++ b/drivers/gpu/drm/amd/pm/amdgpu_pm.c @@ -41,6 +41,8 @@ #define DEVICE_ATTR_IS(_name) (attr_id == device_attr_id__##_name) +#define power_2_mwatt(power) (((power) >> 8) * 1000 + ((power) & 0xff)) + struct od_attribute { struct kobj_attribute attribute; struct list_head entry; @@ -3354,7 +3356,6 @@ static int amdgpu_hwmon_get_power(struct device *dev, enum amd_pp_sensors sensor) { struct amdgpu_device *adev = dev_get_drvdata(dev); - unsigned int uw; u32 query = 0; int r; @@ -3363,9 +3364,7 @@ static int amdgpu_hwmon_get_power(struct device *dev, return r; /* convert to microwatts */ - uw = (query >> 8) * 1000000 + (query & 0xff) * 1000; - - return uw; + return power_2_mwatt(query) * 1000; } static ssize_t amdgpu_hwmon_show_power_avg(struct device *dev, @@ -4908,7 +4907,7 @@ static int amdgpu_debugfs_pm_info_pp(struct seq_file *m, struct amdgpu_device *a { uint32_t mp1_ver = amdgpu_ip_version(adev, MP1_HWIP, 0); uint32_t gc_ver = amdgpu_ip_version(adev, GC_HWIP, 0); - uint32_t value; + uint32_t value, mwatt, centiwatt; uint64_t value64 = 0; uint32_t query = 0; int size; @@ -4933,17 +4932,21 @@ static int amdgpu_debugfs_pm_info_pp(struct seq_file *m, struct amdgpu_device *a seq_printf(m, "\t%u mV (VDDNB)\n", value); size = sizeof(uint32_t); if (!amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_GPU_AVG_POWER, (void *)&query, &size)) { + mwatt = power_2_mwatt(query); + centiwatt = DIV_ROUND_CLOSEST(mwatt, 10); if (adev->flags & AMD_IS_APU) - seq_printf(m, "\t%u.%02u W (average SoC including CPU)\n", query >> 8, query & 0xff); + seq_printf(m, "\t%u.%02u W (average SoC including CPU)\n", centiwatt / 100, centiwatt % 100); else - seq_printf(m, "\t%u.%02u W (average SoC)\n", query >> 8, query & 0xff); + seq_printf(m, "\t%u.%02u W (average SoC)\n", centiwatt / 100, centiwatt % 100); } size = sizeof(uint32_t); if (!amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_GPU_INPUT_POWER, (void *)&query, &size)) { + mwatt = power_2_mwatt(query); + centiwatt = DIV_ROUND_CLOSEST(mwatt, 10); if (adev->flags & AMD_IS_APU) - seq_printf(m, "\t%u.%02u W (current SoC including CPU)\n", query >> 8, query & 0xff); + seq_printf(m, "\t%u.%02u W (current SoC including CPU)\n", centiwatt / 100, centiwatt % 100); else - seq_printf(m, "\t%u.%02u W (current SoC)\n", query >> 8, query & 0xff); + seq_printf(m, "\t%u.%02u W (current SoC)\n", centiwatt / 100, centiwatt % 100); } size = sizeof(value); seq_printf(m, "\n"); From da353a6b30086674c77bdbbfd86e9e0c7416ba99 Mon Sep 17 00:00:00 2001 From: Leorize Date: Mon, 18 May 2026 20:06:19 -0700 Subject: [PATCH 0323/1054] drm/amd/display: set MSA MISC1 bit 6 when using VSC SDP for DCE 11.x When BT.2020 colorimetry is selected, the driver sends information using VSC SDP but does not set "ignore MSA colorimetry" bit on older GPUs with DCE-based IPs. This causes certain sinks to prefer colorimetry information in DP MSA, resulting in terrible color rendering ("dull" colors) when HDR is enabled. This commit wires up the MISC1 bit 6 for GPUs with DCE 11.x based IPs to correctly configure sinks to ignore colorimetry information in MSA, resolving the color rendering issue. Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/4849 Assisted-by: oh-my-pi:GPT-5.5 Signed-off-by: Leorize Signed-off-by: Alex Deucher (cherry picked from commit 323a09e56c1d549ce47d4f110de77b0051b4a8bf) Cc: stable@vger.kernel.org --- .../drm/amd/display/dc/dce/dce_stream_encoder.c | 15 ++++++++++++++- .../drm/amd/display/dc/dce/dce_stream_encoder.h | 3 ++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.c b/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.c index ed407e779c12..2c3a20d35fe9 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.c @@ -271,7 +271,6 @@ static void dce110_stream_encoder_dp_set_stream_attribute( bool use_vsc_sdp_for_colorimetry, uint32_t enable_sdp_splitting) { - (void)use_vsc_sdp_for_colorimetry; (void)enable_sdp_splitting; uint32_t h_active_start; uint32_t v_active_start; @@ -334,6 +333,16 @@ static void dce110_stream_encoder_dp_set_stream_attribute( if (REG(DP_MSA_MISC)) misc1 = REG_READ(DP_MSA_MISC); + /* For YCbCr420 and BT2020 Colorimetry Formats, VSC SDP shall be used. + * When MISC1, bit 6, is Set to 1, a Source device uses a VSC SDP to indicate the + * Pixel Encoding/Colorimetry Format and that a Sink device shall ignore MISC1, bit 7, + * and MISC0, bits 7:1 (MISC1, bit 7, and MISC0, bits 7:1, become "don't care"). + */ + if (use_vsc_sdp_for_colorimetry) + misc1 = misc1 | 0x40; + else + misc1 = misc1 & ~0x40; + /* set color depth */ switch (hw_crtc_timing.display_color_depth) { @@ -499,6 +508,10 @@ static void dce110_stream_encoder_dp_set_stream_attribute( hw_crtc_timing.h_addressable + hw_crtc_timing.h_border_right, DP_MSA_VHEIGHT, hw_crtc_timing.v_border_top + hw_crtc_timing.v_addressable + hw_crtc_timing.v_border_bottom); + } else { + /* DCE-only path */ + if (REG(DP_MSA_MISC)) + REG_WRITE(DP_MSA_MISC, misc1); /* MSA_MISC1 */ } } diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.h b/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.h index 342c0afe6a94..88d6044904d1 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.h +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.h @@ -96,7 +96,8 @@ #define SE_COMMON_REG_LIST(id)\ SE_COMMON_REG_LIST_DCE_BASE(id), \ - SRI(AFMT_CNTL, DIG, id) + SRI(AFMT_CNTL, DIG, id), \ + SRI(DP_MSA_MISC, DP, id) #define SE_DCN_REG_LIST(id)\ SE_COMMON_REG_LIST_BASE(id),\ From 0c01c811be47e6b146552dd59bfedbea8f09b8f4 Mon Sep 17 00:00:00 2001 From: Boyuan Zhang Date: Tue, 12 May 2026 10:29:36 -0400 Subject: [PATCH 0324/1054] drm/amdgpu: fix division by zero with invalid uvd dimensions When width or height is less than 16, width_in_mb or height_in_mb becomes 0, leading to fs_in_mb being 0. This causes a division by zero when calculating num_dpb_buffer in H264 and H264 Perf decode paths. Add validation to reject frames with width < 16 or height < 16 before performing any calculations that depend on these values. V2: Format change - move up all vaiable definitions. V3: Use warn_once to avoid spam. Signed-off-by: Boyuan Zhang Reviewed-by: Leo Liu Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit 3e41d26c70b0a459d041cc19482a226c4b7423cb) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c index 480bf88def46..23383ac5323f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c @@ -655,6 +655,14 @@ static int amdgpu_uvd_cs_msg_decode(struct amdgpu_device *adev, uint32_t *msg, unsigned int image_size, tmp, min_dpb_size, num_dpb_buffer; unsigned int min_ctx_size = ~0; + /* Reject invalid dimensions to prevent division by zero */ + if (width < 16 || height < 16) { + dev_WARN_ONCE(adev->dev, 1, + "Invalid UVD decoding dimensions (%dx%d)!\n", + width, height); + return -EINVAL; + } + image_size = width * height; image_size += image_size / 2; image_size = ALIGN(image_size, 1024); From 3b4082fabc67c9780b06eb959e59dd92fa79c0f0 Mon Sep 17 00:00:00 2001 From: Boyuan Zhang Date: Thu, 21 May 2026 09:59:37 -0400 Subject: [PATCH 0325/1054] drm/amdgpu/vcn4: avoid rereading IB param length Reuse the parameter length returned by vcn_v4_0_enc_find_ib_param() instead of rereading it from the IB. This avoids a potential TOCTOU issue if the IB contents change between reads. Signed-off-by: Boyuan Zhang Reviewed-by: David Rosca Signed-off-by: Alex Deucher (cherry picked from commit dbb02b4755f8c1f3773263f2d779872c1c0c073a) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c index ff7269bafae8..894780669f9c 100644 --- a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c @@ -1927,14 +1927,17 @@ static int vcn_v4_0_dec_msg(struct amdgpu_cs_parser *p, struct amdgpu_job *job, #define RENCODE_IB_PARAM_SESSION_INIT 0x00000003 /* return the offset in ib if id is found, -1 otherwise */ -static int vcn_v4_0_enc_find_ib_param(struct amdgpu_ib *ib, uint32_t id, int start) +static int vcn_v4_0_enc_find_ib_param(struct amdgpu_ib *ib, uint32_t id, int start, uint32_t *length) { int i; uint32_t len; for (i = start; (len = amdgpu_ib_get_value(ib, i)) >= 8; i += len / 4) { - if (amdgpu_ib_get_value(ib, i + 1) == id) + if (amdgpu_ib_get_value(ib, i + 1) == id) { + if (length) + *length = len; return i; + } } return -1; } @@ -1944,14 +1947,14 @@ static int vcn_v4_0_ring_patch_cs_in_place(struct amdgpu_cs_parser *p, struct amdgpu_ib *ib) { struct amdgpu_ring *ring = amdgpu_job_ring(job); - uint32_t val; + uint32_t val, len; int idx = 0, sidx; /* The first instance can decode anything */ if (!ring->me) return 0; - while ((idx = vcn_v4_0_enc_find_ib_param(ib, RADEON_VCN_ENGINE_INFO, idx)) >= 0) { + while ((idx = vcn_v4_0_enc_find_ib_param(ib, RADEON_VCN_ENGINE_INFO, idx, &len)) >= 0) { val = amdgpu_ib_get_value(ib, idx + 2); /* RADEON_VCN_ENGINE_TYPE */ if (val == RADEON_VCN_ENGINE_TYPE_DECODE) { uint32_t valid_buf_flag = amdgpu_ib_get_value(ib, idx + 6); @@ -1964,12 +1967,12 @@ static int vcn_v4_0_ring_patch_cs_in_place(struct amdgpu_cs_parser *p, amdgpu_ib_get_value(ib, idx + 8); return vcn_v4_0_dec_msg(p, job, msg_buffer_addr); } else if (val == RADEON_VCN_ENGINE_TYPE_ENCODE) { - sidx = vcn_v4_0_enc_find_ib_param(ib, RENCODE_IB_PARAM_SESSION_INIT, idx); + sidx = vcn_v4_0_enc_find_ib_param(ib, RENCODE_IB_PARAM_SESSION_INIT, idx, NULL); if (sidx >= 0 && amdgpu_ib_get_value(ib, sidx + 2) == RENCODE_ENCODE_STANDARD_AV1) return vcn_v4_0_limit_sched(p, job); } - idx += amdgpu_ib_get_value(ib, idx) / 4; + idx += len / 4; } return 0; } From 186bfdc4e26d019b2e7570cb121964a1d89b2e5b Mon Sep 17 00:00:00 2001 From: Boyuan Zhang Date: Mon, 25 May 2026 11:34:27 -0400 Subject: [PATCH 0326/1054] drm/amdgpu/vce: fix integer overflow in image size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix a security vulnerability where malicious VCE command streams with oversized dimensions (e.g. 65536×65536) cause 32-bit integer overflow, wrapping the calculated buffer size to 0. This bypasses validation and allows GPU firmware to perform out-of-bound memory access. The fix uses 64-bit arithmetic to detect overflow and rejects invalid dimensions before they reach the hardware. V2: remove redundant check V3: modify max height value V4: remove size64 Signed-off-by: Boyuan Zhang Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit cbe408dba581755ad1279a487ec786d8927d778d) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c index efdebd9c0a1f..eef3c9853a5c 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c @@ -877,9 +877,20 @@ int amdgpu_vce_ring_parse_cs(struct amdgpu_cs_parser *p, goto out; } - *size = amdgpu_ib_get_value(ib, idx + 8) * - amdgpu_ib_get_value(ib, idx + 10) * - 8 * 3 / 2; + uint32_t width, height; + width = amdgpu_ib_get_value(ib, idx + 8); + height = amdgpu_ib_get_value(ib, idx + 10); + + if (width == 0 || height == 0 || + width > 4096 || height > 2304) { + DRM_ERROR("invalid VCE image size: %ux%u\n", + width, height); + r = -EINVAL; + goto out; + } + + *size = width * height * 8 * 3 / 2; + break; case 0x04000001: /* config extension */ From 8cd2ea7bab77b7aa087b1a6cc26d2df03c2a6ed9 Mon Sep 17 00:00:00 2001 From: Xiaogang Chen Date: Tue, 16 Jun 2026 17:18:59 -0500 Subject: [PATCH 0327/1054] drm/amdkfd: Guard m->cp_hqd_eop_control setting by q->eop_ring_buffer_size To avoid wraparound if the value is 0. Signed-off-by: Xiaogang Chen Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit c0cae35661868af207077a4306bc42c7c972947c) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v10.c | 4 ++-- drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c | 4 ++-- drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12.c | 4 ++-- drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12_1.c | 4 ++-- drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_vi.c | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v10.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v10.c index 8e8ec266ca46..e034da638c07 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v10.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v10.c @@ -203,8 +203,8 @@ static void update_mqd(struct mqd_manager *mm, void *mqd, * more than (EOP entry count - 1) so a queue size of 0x800 dwords * is safe, giving a maximum field value of 0xA. */ - m->cp_hqd_eop_control = min(0xA, - ffs(q->eop_ring_buffer_size / sizeof(unsigned int)) - 1 - 1); + m->cp_hqd_eop_control = q->eop_ring_buffer_size ? min(0xA, + ffs(q->eop_ring_buffer_size / sizeof(unsigned int)) - 1 - 1) : 0; m->cp_hqd_eop_base_addr_lo = lower_32_bits(q->eop_ring_buffer_address >> 8); m->cp_hqd_eop_base_addr_hi = 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 fff137e00b5e..350fcbbba4b2 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c @@ -241,8 +241,8 @@ static void update_mqd(struct mqd_manager *mm, void *mqd, * more than (EOP entry count - 1) so a queue size of 0x800 dwords * is safe, giving a maximum field value of 0xA. */ - m->cp_hqd_eop_control = min(0xA, - ffs(q->eop_ring_buffer_size / sizeof(unsigned int)) - 1 - 1); + m->cp_hqd_eop_control = q->eop_ring_buffer_size ? min(0xA, + ffs(q->eop_ring_buffer_size / sizeof(unsigned int)) - 1 - 1) : 0; m->cp_hqd_eop_base_addr_lo = lower_32_bits(q->eop_ring_buffer_address >> 8); m->cp_hqd_eop_base_addr_hi = diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12.c index 8c815f129614..7c387fa90076 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12.c @@ -216,8 +216,8 @@ static void update_mqd(struct mqd_manager *mm, void *mqd, * more than (EOP entry count - 1) so a queue size of 0x800 dwords * is safe, giving a maximum field value of 0xA. */ - m->cp_hqd_eop_control = min(0xA, - ffs(q->eop_ring_buffer_size / sizeof(unsigned int)) - 1 - 1); + m->cp_hqd_eop_control = q->eop_ring_buffer_size ? min(0xA, + ffs(q->eop_ring_buffer_size / sizeof(unsigned int)) - 1 - 1) : 0; m->cp_hqd_eop_base_addr_lo = lower_32_bits(q->eop_ring_buffer_address >> 8); m->cp_hqd_eop_base_addr_hi = diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12_1.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12_1.c index 475589b924e9..431a940f91f3 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12_1.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12_1.c @@ -294,8 +294,8 @@ static void update_mqd(struct mqd_manager *mm, void *mqd, * more than (EOP entry count - 1) so a queue size of 0x800 dwords * is safe, giving a maximum field value of 0xA. */ - m->cp_hqd_eop_control = min(0xA, - ffs(q->eop_ring_buffer_size / sizeof(unsigned int)) - 1 - 1); + m->cp_hqd_eop_control = q->eop_ring_buffer_size ? min(0xA, + ffs(q->eop_ring_buffer_size / sizeof(unsigned int)) - 1 - 1) : 0; m->cp_hqd_eop_base_addr_lo = lower_32_bits(q->eop_ring_buffer_address >> 8); m->cp_hqd_eop_base_addr_hi = diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_vi.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_vi.c index c86779af323b..60b87a500698 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_vi.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_vi.c @@ -214,8 +214,8 @@ static void __update_mqd(struct mqd_manager *mm, void *mqd, * more than (EOP entry count - 1) so a queue size of 0x800 dwords * is safe, giving a maximum field value of 0xA. */ - m->cp_hqd_eop_control |= min(0xA, - order_base_2(q->eop_ring_buffer_size / 4) - 1); + m->cp_hqd_eop_control |= q->eop_ring_buffer_size ? min(0xA, + order_base_2(q->eop_ring_buffer_size / 4) - 1) : 0; m->cp_hqd_eop_base_addr_lo = lower_32_bits(q->eop_ring_buffer_address >> 8); m->cp_hqd_eop_base_addr_hi = From 923425ac7cf7a4f9e088b2d58d390e7d25c3effa Mon Sep 17 00:00:00 2001 From: Matthew Stewart Date: Thu, 4 Jun 2026 11:36:09 -0400 Subject: [PATCH 0328/1054] drm/amd/display: Fix DCN42 null registers & register masks [why] The register lists used on DCN42 variants are different. Some reused codepaths are trying to access registers not used. [how] Add DISPCLK_FREQ_CHANGECNTL, HUBPREQ_DEBUG, and HDMISTREAMCLK_CNTL to the register lists. Reviewed-by: Ovidiu (Ovi) Bunea Signed-off-by: Matthew Stewart Signed-off-by: George Zhang Tested-by: Dan Wheeler Signed-off-by: Alex Deucher (cherry picked from commit 64142f9d51aff32f4130d916cb8f044a072ad27d) --- drivers/gpu/drm/amd/display/dc/dccg/dcn42/dcn42_dccg.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/dccg/dcn42/dcn42_dccg.h b/drivers/gpu/drm/amd/display/dc/dccg/dcn42/dcn42_dccg.h index 2076565b1caa..d45e3af77aad 100644 --- a/drivers/gpu/drm/amd/display/dc/dccg/dcn42/dcn42_dccg.h +++ b/drivers/gpu/drm/amd/display/dc/dccg/dcn42/dcn42_dccg.h @@ -46,6 +46,7 @@ DCCG_SF(DISPCLK_FREQ_CHANGE_CNTL, DCCG_FIFO_ERRDET_STATE, mask_sh),\ DCCG_SF(DISPCLK_FREQ_CHANGE_CNTL, DCCG_FIFO_ERRDET_OVR_EN, mask_sh),\ DCCG_SF(DISPCLK_FREQ_CHANGE_CNTL, DISPCLK_CHG_FWD_CORR_DISABLE, mask_sh),\ + DCCG_SF(DISPCLK_FREQ_CHANGE_CNTL, RESYNC_FIFO_LEVEL_ADJUST_EN, mask_sh),\ DCCG_SF(DPPCLK0_DTO_PARAM, DPPCLK0_DTO_PHASE, mask_sh),\ DCCG_SF(DPPCLK0_DTO_PARAM, DPPCLK0_DTO_MODULO, mask_sh),\ DCCG_SF(HDMICHARCLK0_CLOCK_CNTL, HDMICHARCLK0_EN, mask_sh),\ @@ -239,8 +240,7 @@ DCCG_SF(SYMCLKE_CLOCK_ENABLE, SYMCLKE_SRC_SEL, mask_sh),\ DCCG_SF(SYMCLKE_CLOCK_ENABLE, SYMCLKE_CLOCK_ENABLE, mask_sh),\ DCCG_SF(SYMCLKE_CLOCK_ENABLE, SYMCLKE_FE_EN, mask_sh),\ - DCCG_SF(SYMCLKE_CLOCK_ENABLE, SYMCLKE_FE_SRC_SEL, mask_sh),\ - DCCG_SF(DISPCLK_FREQ_CHANGE_CNTL, RESYNC_FIFO_LEVEL_ADJUST_EN, mask_sh) + DCCG_SF(SYMCLKE_CLOCK_ENABLE, SYMCLKE_FE_SRC_SEL, mask_sh) void dccg42_otg_add_pixel(struct dccg *dccg, From 5b609a2a29540dfadd44610f4af397b75768871c Mon Sep 17 00:00:00 2001 From: Matthew Stewart Date: Fri, 5 Jun 2026 15:05:46 -0400 Subject: [PATCH 0329/1054] drm/amd/display: Remove DCCG registers not needed in DCN42 [why] Some resources that exist in the DCN block are not needed and shouldn't be used. [how] Remove defines from register lists. Reviewed-by: Ovidiu (Ovi) Bunea Signed-off-by: Matthew Stewart Signed-off-by: George Zhang Tested-by: Dan Wheeler Signed-off-by: Alex Deucher (cherry picked from commit dac8aa629a45e34027444f74d3b86b6f104b024c) --- .../amd/display/dc/dccg/dcn42/dcn42_dccg.h | 62 +++++++++---------- 1 file changed, 30 insertions(+), 32 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/dccg/dcn42/dcn42_dccg.h b/drivers/gpu/drm/amd/display/dc/dccg/dcn42/dcn42_dccg.h index d45e3af77aad..a2b17ed11bdb 100644 --- a/drivers/gpu/drm/amd/display/dc/dccg/dcn42/dcn42_dccg.h +++ b/drivers/gpu/drm/amd/display/dc/dccg/dcn42/dcn42_dccg.h @@ -57,34 +57,24 @@ DCCG_SF(PHYBSYMCLK_CLOCK_CNTL, PHYBSYMCLK_SRC_SEL, mask_sh),\ DCCG_SF(PHYCSYMCLK_CLOCK_CNTL, PHYCSYMCLK_EN, mask_sh),\ DCCG_SF(PHYCSYMCLK_CLOCK_CNTL, PHYCSYMCLK_SRC_SEL, mask_sh),\ - DCCG_SF(PHYDSYMCLK_CLOCK_CNTL, PHYDSYMCLK_EN, mask_sh),\ - DCCG_SF(PHYDSYMCLK_CLOCK_CNTL, PHYDSYMCLK_SRC_SEL, mask_sh),\ DCCG_SF(DPSTREAMCLK_CNTL, DPSTREAMCLK0_EN, mask_sh),\ DCCG_SF(DPSTREAMCLK_CNTL, DPSTREAMCLK1_EN, mask_sh),\ DCCG_SF(DPSTREAMCLK_CNTL, DPSTREAMCLK2_EN, mask_sh),\ - DCCG_SF(DPSTREAMCLK_CNTL, DPSTREAMCLK3_EN, mask_sh),\ DCCG_SF(DPSTREAMCLK_CNTL, DPSTREAMCLK0_SRC_SEL, mask_sh),\ DCCG_SF(DPSTREAMCLK_CNTL, DPSTREAMCLK1_SRC_SEL, mask_sh),\ DCCG_SF(DPSTREAMCLK_CNTL, DPSTREAMCLK2_SRC_SEL, mask_sh),\ - DCCG_SF(DPSTREAMCLK_CNTL, DPSTREAMCLK3_SRC_SEL, mask_sh),\ DCCG_SF(HDMISTREAMCLK_CNTL, HDMISTREAMCLK0_EN, mask_sh),\ DCCG_SF(HDMISTREAMCLK_CNTL, HDMISTREAMCLK0_SRC_SEL, mask_sh),\ DCCG_SF(SYMCLK32_SE_CNTL, SYMCLK32_SE0_SRC_SEL, mask_sh),\ DCCG_SF(SYMCLK32_SE_CNTL, SYMCLK32_SE1_SRC_SEL, mask_sh),\ DCCG_SF(SYMCLK32_SE_CNTL, SYMCLK32_SE2_SRC_SEL, mask_sh),\ - DCCG_SF(SYMCLK32_SE_CNTL, SYMCLK32_SE3_SRC_SEL, mask_sh),\ DCCG_SF(SYMCLK32_SE_CNTL, SYMCLK32_SE0_EN, mask_sh),\ DCCG_SF(SYMCLK32_SE_CNTL, SYMCLK32_SE1_EN, mask_sh),\ DCCG_SF(SYMCLK32_SE_CNTL, SYMCLK32_SE2_EN, mask_sh),\ - DCCG_SF(SYMCLK32_SE_CNTL, SYMCLK32_SE3_EN, mask_sh),\ DCCG_SF(SYMCLK32_LE_CNTL, SYMCLK32_LE0_SRC_SEL, mask_sh),\ DCCG_SF(SYMCLK32_LE_CNTL, SYMCLK32_LE1_SRC_SEL, mask_sh),\ - DCCG_SF(SYMCLK32_LE_CNTL, SYMCLK32_LE2_SRC_SEL, mask_sh),\ - DCCG_SF(SYMCLK32_LE_CNTL, SYMCLK32_LE3_SRC_SEL, mask_sh),\ DCCG_SF(SYMCLK32_LE_CNTL, SYMCLK32_LE0_EN, mask_sh),\ DCCG_SF(SYMCLK32_LE_CNTL, SYMCLK32_LE1_EN, mask_sh),\ - DCCG_SF(SYMCLK32_LE_CNTL, SYMCLK32_LE2_EN, mask_sh),\ - DCCG_SF(SYMCLK32_LE_CNTL, SYMCLK32_LE3_EN, mask_sh),\ DCCG_SFII(OTG, PIXEL_RATE_CNTL, PIPE, DTO_SRC_SEL, 0, mask_sh),\ DCCG_SFII(OTG, PIXEL_RATE_CNTL, PIPE, DTO_SRC_SEL, 1, mask_sh),\ DCCG_SFII(OTG, PIXEL_RATE_CNTL, PIPE, DTO_SRC_SEL, 2, mask_sh),\ @@ -122,7 +112,6 @@ DCCG_SF(DCCG_GATE_DISABLE_CNTL2, PHYASYMCLK_ROOT_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL2, PHYBSYMCLK_ROOT_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL2, PHYCSYMCLK_ROOT_GATE_DISABLE, mask_sh),\ - DCCG_SF(DCCG_GATE_DISABLE_CNTL2, PHYDSYMCLK_ROOT_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GLOBAL_FGCG_REP_CNTL, DCCG_GLOBAL_FGCG_REP_DIS, mask_sh),\ DCCG_SFII(OTG, PIXEL_RATE_CNTL, DP_DTO, ENABLE, 0, mask_sh),\ DCCG_SFII(OTG, PIXEL_RATE_CNTL, DP_DTO, ENABLE, 1, mask_sh),\ @@ -135,7 +124,6 @@ DCCG_SF(DSCCLK_DTO_CTRL, DSCCLK0_EN, mask_sh),\ DCCG_SF(DSCCLK_DTO_CTRL, DSCCLK1_EN, mask_sh),\ DCCG_SF(DSCCLK_DTO_CTRL, DSCCLK2_EN, mask_sh),\ - DCCG_SF(DSCCLK_DTO_CTRL, DSCCLK3_EN, mask_sh),\ DCCG_SF(DSCCLK0_DTO_PARAM, DSCCLK0_DTO_PHASE, mask_sh),\ DCCG_SF(DSCCLK0_DTO_PARAM, DSCCLK0_DTO_MODULO, mask_sh),\ DCCG_SF(DSCCLK1_DTO_PARAM, DSCCLK1_DTO_PHASE, mask_sh),\ @@ -148,36 +136,26 @@ DCCG_SF(DCCG_GATE_DISABLE_CNTL2, SYMCLKA_FE_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL2, SYMCLKB_FE_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL2, SYMCLKC_FE_GATE_DISABLE, mask_sh),\ - DCCG_SF(DCCG_GATE_DISABLE_CNTL2, SYMCLKD_FE_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL2, SYMCLKA_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL2, SYMCLKB_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL2, SYMCLKC_GATE_DISABLE, mask_sh),\ - DCCG_SF(DCCG_GATE_DISABLE_CNTL2, SYMCLKD_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL2, PHYASYMCLK_ROOT_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL2, PHYBSYMCLK_ROOT_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL2, PHYCSYMCLK_ROOT_GATE_DISABLE, mask_sh),\ - DCCG_SF(DCCG_GATE_DISABLE_CNTL2, PHYDSYMCLK_ROOT_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_ROOT_SE0_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_ROOT_SE1_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_ROOT_SE2_GATE_DISABLE, mask_sh),\ - DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_ROOT_SE3_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_ROOT_LE0_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_ROOT_LE1_GATE_DISABLE, mask_sh),\ - DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_ROOT_LE2_GATE_DISABLE, mask_sh),\ - DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_ROOT_LE3_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_SE0_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_SE1_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_SE2_GATE_DISABLE, mask_sh),\ - DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_SE3_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_LE0_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_LE1_GATE_DISABLE, mask_sh),\ - DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_LE2_GATE_DISABLE, mask_sh),\ - DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_LE3_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL4, HDMICHARCLK0_ROOT_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL4, PHYA_REFCLK_ROOT_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL4, PHYB_REFCLK_ROOT_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL4, PHYC_REFCLK_ROOT_GATE_DISABLE, mask_sh),\ - DCCG_SF(DCCG_GATE_DISABLE_CNTL4, PHYD_REFCLK_ROOT_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL5, DTBCLK_P0_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL5, DTBCLK_P1_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL5, DTBCLK_P2_GATE_DISABLE, mask_sh),\ @@ -185,19 +163,15 @@ DCCG_SF(DCCG_GATE_DISABLE_CNTL5, SYMCLKA_FE_ROOT_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL5, SYMCLKB_FE_ROOT_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL5, SYMCLKC_FE_ROOT_GATE_DISABLE, mask_sh),\ - DCCG_SF(DCCG_GATE_DISABLE_CNTL5, SYMCLKD_FE_ROOT_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL5, SYMCLKA_ROOT_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL5, SYMCLKB_ROOT_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL5, SYMCLKC_ROOT_GATE_DISABLE, mask_sh),\ - DCCG_SF(DCCG_GATE_DISABLE_CNTL5, SYMCLKD_ROOT_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL5, DPSTREAMCLK0_ROOT_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL5, DPSTREAMCLK1_ROOT_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL5, DPSTREAMCLK2_ROOT_GATE_DISABLE, mask_sh),\ - DCCG_SF(DCCG_GATE_DISABLE_CNTL5, DPSTREAMCLK3_ROOT_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL5, DPSTREAMCLK0_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL5, DPSTREAMCLK1_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL5, DPSTREAMCLK2_GATE_DISABLE, mask_sh),\ - DCCG_SF(DCCG_GATE_DISABLE_CNTL5, DPSTREAMCLK3_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL6, DSCCLK0_ROOT_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL6, DSCCLK1_ROOT_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL6, DSCCLK2_ROOT_GATE_DISABLE, mask_sh),\ @@ -209,26 +183,38 @@ DCCG_SF(SYMCLKA_CLOCK_ENABLE, SYMCLKA_CLOCK_ENABLE, mask_sh),\ DCCG_SF(SYMCLKB_CLOCK_ENABLE, SYMCLKB_CLOCK_ENABLE, mask_sh),\ DCCG_SF(SYMCLKC_CLOCK_ENABLE, SYMCLKC_CLOCK_ENABLE, mask_sh),\ - DCCG_SF(SYMCLKD_CLOCK_ENABLE, SYMCLKD_CLOCK_ENABLE, mask_sh),\ DCCG_SF(SYMCLKA_CLOCK_ENABLE, SYMCLKA_FE_EN, mask_sh),\ DCCG_SF(SYMCLKB_CLOCK_ENABLE, SYMCLKB_FE_EN, mask_sh),\ DCCG_SF(SYMCLKC_CLOCK_ENABLE, SYMCLKC_FE_EN, mask_sh),\ - DCCG_SF(SYMCLKD_CLOCK_ENABLE, SYMCLKD_FE_EN, mask_sh),\ DCCG_SF(SYMCLKA_CLOCK_ENABLE, SYMCLKA_FE_SRC_SEL, mask_sh),\ DCCG_SF(SYMCLKB_CLOCK_ENABLE, SYMCLKB_FE_SRC_SEL, mask_sh),\ - DCCG_SF(SYMCLKC_CLOCK_ENABLE, SYMCLKC_FE_SRC_SEL, mask_sh),\ - DCCG_SF(SYMCLKD_CLOCK_ENABLE, SYMCLKD_FE_SRC_SEL, mask_sh) + DCCG_SF(SYMCLKC_CLOCK_ENABLE, SYMCLKC_FE_SRC_SEL, mask_sh) #define DCCG_MASK_SH_LIST_DCN42(mask_sh) \ DCCG_MASK_SH_LIST_DCN42_COMMON(mask_sh),\ + DCCG_SF(PHYDSYMCLK_CLOCK_CNTL, PHYDSYMCLK_EN, mask_sh),\ + DCCG_SF(PHYDSYMCLK_CLOCK_CNTL, PHYDSYMCLK_SRC_SEL, mask_sh),\ DCCG_SF(PHYESYMCLK_CLOCK_CNTL, PHYESYMCLK_EN, mask_sh),\ DCCG_SF(PHYESYMCLK_CLOCK_CNTL, PHYESYMCLK_SRC_SEL, mask_sh),\ DCCG_SF(HDMISTREAMCLK0_DTO_PARAM, HDMISTREAMCLK0_DTO_PHASE, mask_sh),\ DCCG_SF(HDMISTREAMCLK0_DTO_PARAM, HDMISTREAMCLK0_DTO_MODULO, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL2, PHYDSYMCLK_ROOT_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL2, PHYESYMCLK_ROOT_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL2, SYMCLKD_FE_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL2, SYMCLKD_GATE_DISABLE, mask_sh),\ DCCG_SF(DSCCLK3_DTO_PARAM, DSCCLK3_DTO_PHASE, mask_sh),\ DCCG_SF(DSCCLK3_DTO_PARAM, DSCCLK3_DTO_MODULO, mask_sh),\ - DCCG_SF(DCCG_GATE_DISABLE_CNTL2, PHYESYMCLK_ROOT_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_ROOT_SE3_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_ROOT_LE2_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_ROOT_LE3_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_SE3_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_LE2_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_LE3_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL5, SYMCLKD_FE_ROOT_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL5, SYMCLKD_ROOT_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL5, DPSTREAMCLK3_ROOT_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL5, DPSTREAMCLK3_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL4, PHYD_REFCLK_ROOT_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL4, PHYE_REFCLK_ROOT_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL5, SYMCLKE_FE_ROOT_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL5, SYMCLKE_ROOT_GATE_DISABLE, mask_sh),\ @@ -237,10 +223,22 @@ DCCG_SF(SYMCLKB_CLOCK_ENABLE, SYMCLKB_SRC_SEL, mask_sh),\ DCCG_SF(SYMCLKC_CLOCK_ENABLE, SYMCLKC_SRC_SEL, mask_sh),\ DCCG_SF(SYMCLKD_CLOCK_ENABLE, SYMCLKD_SRC_SEL, mask_sh),\ + DCCG_SF(SYMCLKD_CLOCK_ENABLE, SYMCLKD_CLOCK_ENABLE, mask_sh),\ + DCCG_SF(SYMCLKD_CLOCK_ENABLE, SYMCLKD_FE_EN, mask_sh),\ + DCCG_SF(SYMCLKD_CLOCK_ENABLE, SYMCLKD_FE_SRC_SEL, mask_sh),\ DCCG_SF(SYMCLKE_CLOCK_ENABLE, SYMCLKE_SRC_SEL, mask_sh),\ DCCG_SF(SYMCLKE_CLOCK_ENABLE, SYMCLKE_CLOCK_ENABLE, mask_sh),\ DCCG_SF(SYMCLKE_CLOCK_ENABLE, SYMCLKE_FE_EN, mask_sh),\ - DCCG_SF(SYMCLKE_CLOCK_ENABLE, SYMCLKE_FE_SRC_SEL, mask_sh) + DCCG_SF(SYMCLKE_CLOCK_ENABLE, SYMCLKE_FE_SRC_SEL, mask_sh),\ + DCCG_SF(SYMCLK32_SE_CNTL, SYMCLK32_SE3_SRC_SEL, mask_sh),\ + DCCG_SF(SYMCLK32_SE_CNTL, SYMCLK32_SE3_EN, mask_sh),\ + DCCG_SF(SYMCLK32_LE_CNTL, SYMCLK32_LE2_SRC_SEL, mask_sh),\ + DCCG_SF(SYMCLK32_LE_CNTL, SYMCLK32_LE3_SRC_SEL, mask_sh),\ + DCCG_SF(SYMCLK32_LE_CNTL, SYMCLK32_LE2_EN, mask_sh),\ + DCCG_SF(SYMCLK32_LE_CNTL, SYMCLK32_LE3_EN, mask_sh),\ + DCCG_SF(DPSTREAMCLK_CNTL, DPSTREAMCLK3_EN, mask_sh),\ + DCCG_SF(DPSTREAMCLK_CNTL, DPSTREAMCLK3_SRC_SEL, mask_sh),\ + DCCG_SF(DSCCLK_DTO_CTRL, DSCCLK3_EN, mask_sh) void dccg42_otg_add_pixel(struct dccg *dccg, From f87f926395690449dc748a8bbc6e378ff180e6a7 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 11 Jun 2026 15:01:19 +0200 Subject: [PATCH 0330/1054] drm/amd/display: avoid large stack allocation in commit_planes_do_stream_update_sequence The function has two arrays on the stack to hold temporary dsc_optc_config and dsc_config objects. The combination blows through common stack frame warning limits in combination with the other local variables: drivers/gpu/drm/amd/amdgpu/../display/dc/core/dc.c:4070:22: error: stack frame size (1352) exceeds limit (1280) in 'commit_planes_do_stream_update_sequence' [-Werror,-Wframe-larger-than] Since neither array is initialized or used outside of the add_link_update_dsc_config_sequence() function, there is no actual need to keep each element around. Replace the arrays with a single instance each to reduce the stack usage to less than half. Fixes: 9f49d3cd7e71 ("drm/amd/display: Implement block sequencing infrastructure for modular hardware operations.") Signed-off-by: Arnd Bergmann Tested-by: Dan Wheeler Acked-by: George Zhang Signed-off-by: Alex Deucher (cherry picked from commit 9e0896fa6f7dbe9ca3dbbd3b593fa91670f4820b) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/display/dc/core/dc.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c index bcdbf3471039..72762c4fa392 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc.c @@ -4077,8 +4077,6 @@ static void commit_planes_do_stream_update_sequence(struct dc *dc, { int j; struct block_sequence_state seq_state = { .steps = block_sequence, .num_steps = num_steps }; - struct dsc_config dsc_cfgs[MAX_PIPES]; - struct dsc_optc_config dsc_optc_cfgs[MAX_PIPES]; unsigned int dsc_cfg_index = 0; *num_steps = 0; // Initialize to 0 @@ -4150,11 +4148,13 @@ static void commit_planes_do_stream_update_sequence(struct dc *dc, if (stream_update->dsc_config) if (dsc_cfg_index < MAX_PIPES) { + struct dsc_config dsc_cfg; + struct dsc_optc_config dsc_optc_cfg; + add_link_update_dsc_config_sequence(&seq_state, pipe_ctx, - &dsc_cfgs[dsc_cfg_index], - &dsc_optc_cfgs[dsc_cfg_index]); - dsc_cfg_index++; + &dsc_cfg, + &dsc_optc_cfg); } if (stream_update->mst_bw_update) { From ea772a440d56b285f4d491affac50ecd41f6b402 Mon Sep 17 00:00:00 2001 From: Asad Kamal Date: Sun, 14 Jun 2026 12:50:28 +0800 Subject: [PATCH 0331/1054] drm/amdgpu: fix aperture mapping leak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit amdgpu_pci_remove() calls drm_dev_unplug() before invoking the driver fini routines. This causes drm_dev_enter() in amdgpu_ttm_fini() to always return false, so iounmap(aper_base_kaddr) never runs on normal driver unload, leaving an orphaned entry in the x86 PAT interval tree. On connected_to_cpu hardware, the aperture is mapped write-back (WB) via ioremap_cache(). On reload, IP discovery calls memremap(..., MEMREMAP_WC) over the same range. The WC vs WB conflict causes: ioremap error for 0x..., requested 0x1, got 0x0 amdgpu: discovery failed: -2 Fix by switching to devres-managed mappings so cleanup is guaranteed regardless of drm_dev_enter() state: - connected_to_cpu path: devm_memremap(MEMREMAP_WB). For IORESOURCE_SYSTEM_RAM ranges this takes the try_ram_remap() shortcut, returning __va(offset) from the existing kernel direct map. No new ioremap VA or PAT entry is created, so there is nothing to orphan. - dGPU path: devm_ioremap_wc() registers iounmap() as a devres action, guaranteeing cleanup at device_del() time. Also remove iounmap(aper_base_kaddr) from amdgpu_device_unmap_mmio() since the mapping is now devres-owned. v2: Remove redundant x86_64 guard (Lijo) Fixes: 9d0af8b4def0 ("drm/amdgpu: pre-map device buffer as cached for A+A config") Signed-off-by: Asad Kamal Reviewed-by: Christian König Reviewed-by: Lijo Lazar Signed-off-by: Alex Deucher (cherry picked from commit d871e99879cb5fd1fa798b006b4888887e63a17a) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 2 -- drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c | 36 ++++++++++------------ 2 files changed, 16 insertions(+), 22 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index 211d30f03d25..8d6502a94306 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -4184,8 +4184,6 @@ static void amdgpu_device_unmap_mmio(struct amdgpu_device *adev) iounmap(adev->rmmio); adev->rmmio = NULL; - if (adev->mman.aper_base_kaddr) - iounmap(adev->mman.aper_base_kaddr); adev->mman.aper_base_kaddr = NULL; /* Memory manager related */ diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c index 16c060badaee..00b5317f77f8 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c @@ -2118,18 +2118,23 @@ int amdgpu_ttm_init(struct amdgpu_device *adev) /* Change the size here instead of the init above so only lpfn is affected */ amdgpu_ttm_disable_buffer_funcs(adev); #ifdef CONFIG_64BIT -#ifdef CONFIG_X86 - if (adev->gmc.xgmi.connected_to_cpu) - adev->mman.aper_base_kaddr = ioremap_cache(adev->gmc.aper_base, - adev->gmc.visible_vram_size); - - else if (adev->gmc.is_app_apu) + if (adev->gmc.xgmi.connected_to_cpu) { + void *kaddr = devm_memremap(adev->dev, adev->gmc.aper_base, + adev->gmc.visible_vram_size, + MEMREMAP_WB); + if (IS_ERR(kaddr)) + return PTR_ERR(kaddr); + adev->mman.aper_base_kaddr = (__force void __iomem *)kaddr; + } else if (adev->gmc.is_app_apu) { DRM_DEBUG_DRIVER( "No need to ioremap when real vram size is 0\n"); - else -#endif - adev->mman.aper_base_kaddr = ioremap_wc(adev->gmc.aper_base, - adev->gmc.visible_vram_size); + } else { + adev->mman.aper_base_kaddr = devm_ioremap_wc(adev->dev, + adev->gmc.aper_base, + adev->gmc.visible_vram_size); + if (!adev->mman.aper_base_kaddr) + return -ENOMEM; + } #endif amdgpu_ttm_init_vram_resv_regions(adev); @@ -2246,8 +2251,6 @@ int amdgpu_ttm_init(struct amdgpu_device *adev) */ void amdgpu_ttm_fini(struct amdgpu_device *adev) { - int idx; - if (!adev->mman.initialized) return; @@ -2270,14 +2273,7 @@ void amdgpu_ttm_fini(struct amdgpu_device *adev) amdgpu_ttm_unmark_vram_reserved(adev, AMDGPU_RESV_FW_VRAM_USAGE); amdgpu_ttm_unmark_vram_reserved(adev, AMDGPU_RESV_DRV_VRAM_USAGE); - if (drm_dev_enter(adev_to_drm(adev), &idx)) { - - if (adev->mman.aper_base_kaddr) - iounmap(adev->mman.aper_base_kaddr); - adev->mman.aper_base_kaddr = NULL; - - drm_dev_exit(idx); - } + adev->mman.aper_base_kaddr = NULL; if (!adev->gmc.is_app_apu) amdgpu_vram_mgr_fini(adev); From 426ffae6ecc7ec77d32bf8be065c21a1b881b084 Mon Sep 17 00:00:00 2001 From: Yongqiang Sun Date: Tue, 2 Jun 2026 09:47:19 -0400 Subject: [PATCH 0332/1054] drm/amdkfd: clamp v9 CRIU control stack checkpoint copy to BO size CRIU checkpoint copies the MQD control stack using cp_hqd_cntl_stack_size from hardware without bounding it to the allocated BO region. If the HW field is larger than the queue's control stack allocation, memcpy reads past the BO into adjacent GTT memory and can leak kernel data to userspace. Store the page-aligned control stack BO size in mqd_manager and clamp checkpoint copies and reported checkpoint sizes to min(cp_hqd_cntl_stack_size, mm->ctl_stack_size). Apply the same bound for multi-XCC v9.4.3 checkpoint layout. Signed-off-by: Yongqiang Sun Reviewed-by: David Francis Signed-off-by: Alex Deucher (cherry picked from commit 6c2abd0ec09e86c6323010673766f76050e28aa3) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.h | 1 + .../gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c | 25 ++++++++++++++++--- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.h b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.h index 06ca6235ff1b..63ea70e5c0e6 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.h +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.h @@ -127,6 +127,7 @@ struct mqd_manager { struct mutex mqd_mutex; struct kfd_node *dev; uint32_t mqd_size; + uint32_t ctl_stack_size; }; struct mqd_user_context_save_area_header { diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c index 17bfb419b202..be99f0d53b18 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c @@ -27,6 +27,7 @@ #include #include "kfd_priv.h" #include "kfd_mqd_manager.h" +#include "kfd_topology.h" #include "v9_structs.h" #include "gc/gc_9_0_offset.h" #include "gc/gc_9_0_sh_mask.h" @@ -411,8 +412,11 @@ static int get_wave_state(struct mqd_manager *mm, void *mqd, static int get_checkpoint_info(struct mqd_manager *mm, void *mqd, u32 *ctl_stack_size) { struct v9_mqd *m = get_mqd(mqd); + u32 per_xcc_size; - if (check_mul_overflow(m->cp_hqd_cntl_stack_size, NUM_XCC(mm->dev->xcc_mask), ctl_stack_size)) + per_xcc_size = min_t(u32, m->cp_hqd_cntl_stack_size, mm->ctl_stack_size); + + if (check_mul_overflow(per_xcc_size, NUM_XCC(mm->dev->xcc_mask), ctl_stack_size)) return -EINVAL; return 0; @@ -421,13 +425,15 @@ static int get_checkpoint_info(struct mqd_manager *mm, void *mqd, u32 *ctl_stack static void checkpoint_mqd(struct mqd_manager *mm, void *mqd, void *mqd_dst, void *ctl_stack_dst) { struct v9_mqd *m; + u32 ctl_stack_copy_size; /* Control stack is located one page after MQD. */ void *ctl_stack = (void *)((uintptr_t)mqd + AMDGPU_GPU_PAGE_SIZE); m = get_mqd(mqd); + ctl_stack_copy_size = min_t(u32, m->cp_hqd_cntl_stack_size, mm->ctl_stack_size); memcpy(mqd_dst, m, sizeof(struct v9_mqd)); - memcpy(ctl_stack_dst, ctl_stack, m->cp_hqd_cntl_stack_size); + memcpy(ctl_stack_dst, ctl_stack, ctl_stack_copy_size); } static void checkpoint_mqd_v9_4_3(struct mqd_manager *mm, @@ -436,15 +442,19 @@ static void checkpoint_mqd_v9_4_3(struct mqd_manager *mm, void *ctl_stack_dst) { struct v9_mqd *m; + u32 ctl_stack_stride; int xcc; uint64_t size = get_mqd(mqd)->cp_mqd_stride_size; + ctl_stack_stride = min_t(u32, get_mqd(mqd)->cp_hqd_cntl_stack_size, + mm->ctl_stack_size); + for (xcc = 0; xcc < NUM_XCC(mm->dev->xcc_mask); xcc++) { m = get_mqd(mqd + size * xcc); checkpoint_mqd(mm, m, (uint8_t *)mqd_dst + sizeof(*m) * xcc, - (uint8_t *)ctl_stack_dst + m->cp_hqd_cntl_stack_size * xcc); + (uint8_t *)ctl_stack_dst + ctl_stack_stride * xcc); } } @@ -998,6 +1008,15 @@ struct mqd_manager *mqd_manager_init_v9(enum KFD_MQD_TYPE type, mqd->is_occupied = kfd_is_occupied_cp; mqd->get_checkpoint_info = get_checkpoint_info; mqd->mqd_size = sizeof(struct v9_mqd); + if (dev->kfd->cwsr_enabled) { + struct kfd_topology_device *topo_dev; + + topo_dev = kfd_topology_device_by_id(dev->id); + if (topo_dev) + mqd->ctl_stack_size = + ALIGN(topo_dev->node_props.ctl_stack_size, + AMDGPU_GPU_PAGE_SIZE); + } mqd->mqd_stride = mqd_stride_v9; #if defined(CONFIG_DEBUG_FS) mqd->debugfs_show_mqd = debugfs_show_mqd; From 0a3d35460320baf8744c7dcc3e287e07fbaf6d36 Mon Sep 17 00:00:00 2001 From: Jesse Zhang Date: Thu, 11 Jun 2026 10:14:32 +0800 Subject: [PATCH 0333/1054] drm/amdgpu/gfx11: fix EOP interrupt routing for KQ and userq Try KQ by ring_id first (KCQ and UQ never share a HW slot); fall back to amdgpu_userq_process_fence_irq() on miss, since KQ EOPs were misrouted into the userq fence path when enable_mes is true. Require a strict (me,pipe,queue) match in the gfx case, then userq gfx EOPs fall through to amdgpu_userq_process_fence_irq(). Suggested-by: Alex Deucher Signed-off-by: Jesse Zhang Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit 88e589cc811ba907209a426c426c469bcb4bb894) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c | 43 +++++++++++++++++--------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c index 92c16392b916..e60ae566b5f8 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c @@ -6507,25 +6507,33 @@ static int gfx_v11_0_eop_irq(struct amdgpu_device *adev, struct amdgpu_iv_entry *entry) { u32 doorbell_offset = entry->src_data[0]; - u8 me_id, pipe_id, queue_id; - struct amdgpu_ring *ring; - int i; DRM_DEBUG("IH: CP EOP\n"); - if (adev->enable_mes && doorbell_offset) { - amdgpu_userq_process_fence_irq(adev, doorbell_offset); - } else { - me_id = (entry->ring_id & 0x0c) >> 2; - pipe_id = (entry->ring_id & 0x03) >> 0; - queue_id = (entry->ring_id & 0x70) >> 4; + if (!adev->gfx.disable_kq) { + u8 me_id = (entry->ring_id & 0x0c) >> 2; + u8 pipe_id = (entry->ring_id & 0x03) >> 0; + u8 queue_id = (entry->ring_id & 0x70) >> 4; + struct amdgpu_ring *ring; + int i; switch (me_id) { case 0: - if (pipe_id == 0) - amdgpu_fence_process(&adev->gfx.gfx_ring[0]); - else - amdgpu_fence_process(&adev->gfx.gfx_ring[1]); + /* + * MES splits gfx HQDs per (me,pipe): KGQ owns queue=0, + * userq gfx owns queue>=1 (see amdgpu_mes_get_hqd_mask). + * Require a strict (me,pipe,queue) match so userq gfx + * EOPs fall through to amdgpu_userq_process_fence_irq(). + */ + for (i = 0; i < adev->gfx.num_gfx_rings; i++) { + ring = &adev->gfx.gfx_ring[i]; + if ((ring->me == me_id) && + (ring->pipe == pipe_id) && + (ring->queue == queue_id)) { + amdgpu_fence_process(ring); + return 0; + } + } break; case 1: case 2: @@ -6537,13 +6545,20 @@ static int gfx_v11_0_eop_irq(struct amdgpu_device *adev, */ if ((ring->me == me_id) && (ring->pipe == pipe_id) && - (ring->queue == queue_id)) + (ring->queue == queue_id)) { amdgpu_fence_process(ring); + return 0; + } } break; + default: + break; } } + if (adev->enable_mes && doorbell_offset) + amdgpu_userq_process_fence_irq(adev, doorbell_offset); + return 0; } From 128abbbfa913e7e099b75ae652cc90cfd66c6d6b Mon Sep 17 00:00:00 2001 From: Jesse Zhang Date: Thu, 11 Jun 2026 10:26:04 +0800 Subject: [PATCH 0334/1054] drm/amdgpu/gfx12: fix EOP interrupt routing for KQ and userq Try KQ by ring_id first (KCQ and UQ never share a HW slot); fall back to amdgpu_userq_process_fence_irq() on miss, since KCQ EOPs were misrouted into the userq fence path when enable_mes is true. Require a strict (me,pipe,queue) match in the gfx case, then userq gfx EOPs fall through to amdgpu_userq_process_fence_irq(). Suggested-by: Alex Deucher Signed-off-by: Jesse Zhang Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit 6c1f4f7ff08448e0e18cd7fc4e59d6c96a36f25d) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c | 43 +++++++++++++++++--------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c index 989c8e2baf6a..3f3b1754c038 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c @@ -4835,25 +4835,33 @@ static int gfx_v12_0_eop_irq(struct amdgpu_device *adev, struct amdgpu_iv_entry *entry) { u32 doorbell_offset = entry->src_data[0]; - u8 me_id, pipe_id, queue_id; - struct amdgpu_ring *ring; - int i; DRM_DEBUG("IH: CP EOP\n"); - if (adev->enable_mes && doorbell_offset) { - amdgpu_userq_process_fence_irq(adev, doorbell_offset); - } else { - me_id = (entry->ring_id & 0x0c) >> 2; - pipe_id = (entry->ring_id & 0x03) >> 0; - queue_id = (entry->ring_id & 0x70) >> 4; + if (!adev->gfx.disable_kq) { + u8 me_id = (entry->ring_id & 0x0c) >> 2; + u8 pipe_id = (entry->ring_id & 0x03) >> 0; + u8 queue_id = (entry->ring_id & 0x70) >> 4; + struct amdgpu_ring *ring; + int i; switch (me_id) { case 0: - if (pipe_id == 0) - amdgpu_fence_process(&adev->gfx.gfx_ring[0]); - else - amdgpu_fence_process(&adev->gfx.gfx_ring[1]); + /* + * MES splits gfx HQDs per (me,pipe): KGQ owns queue=0, + * userq gfx owns queue>=1 (see amdgpu_mes_get_hqd_mask). + * Require a strict (me,pipe,queue) match so userq gfx + * EOPs fall through to amdgpu_userq_process_fence_irq(). + */ + for (i = 0; i < adev->gfx.num_gfx_rings; i++) { + ring = &adev->gfx.gfx_ring[i]; + if ((ring->me == me_id) && + (ring->pipe == pipe_id) && + (ring->queue == queue_id)) { + amdgpu_fence_process(ring); + return 0; + } + } break; case 1: case 2: @@ -4865,13 +4873,20 @@ static int gfx_v12_0_eop_irq(struct amdgpu_device *adev, */ if ((ring->me == me_id) && (ring->pipe == pipe_id) && - (ring->queue == queue_id)) + (ring->queue == queue_id)) { amdgpu_fence_process(ring); + return 0; + } } break; + default: + break; } } + if (adev->enable_mes && doorbell_offset) + amdgpu_userq_process_fence_irq(adev, doorbell_offset); + return 0; } From ac11060c6d4959e2d4ceada037d2e1e1bfcf6645 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Wed, 10 Jun 2026 17:18:17 +0200 Subject: [PATCH 0335/1054] drm/amd/display: Handle struct drm_plane_state.ignore_damage_clips The mode-setting pipeline can disabled damage clippings for a commit by setting ignore_damage_clips in struct drm_plane_state. The commit will then do a full display update. Test the flag in DCN code and do a full update in DCN code if it has been set. Commit 35ed38d58257 ("drm: Allow drivers to indicate the damage helpers to ignore damage clips") introduced ignore_damage_clips to selectively ignore damage clipping in certain framebuffer changes. This driver does not do that, but DRM's damage iterator will soon rely on the flag. Therefore supporting it here as well make sense for consistency. Signed-off-by: Thomas Zimmermann Fixes: 35ed38d58257 ("drm: Allow drivers to indicate the damage helpers to ignore damage clips") Cc: Javier Martinez Canillas Cc: Thomas Zimmermann Cc: Zack Rusin Cc: dri-devel@lists.freedesktop.org Reviewed-by: Javier Martinez Canillas Reviewed-by: Harry Wentland Signed-off-by: Alex Deucher (cherry picked from commit a24019f6480fad5c077b5956eed942c8960323d6) Cc: # v6.8+ --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index d3a8d681227a..18145d78334f 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -6614,8 +6614,8 @@ static void fill_dc_dirty_rects(struct drm_plane *plane, { struct dm_crtc_state *dm_crtc_state = to_dm_crtc_state(crtc_state); struct rect *dirty_rects = flip_addrs->dirty_rects; - u32 num_clips; - struct drm_mode_rect *clips; + u32 num_clips = 0; + struct drm_mode_rect *clips = NULL; bool bb_changed; bool fb_changed; u32 i = 0; @@ -6631,8 +6631,10 @@ static void fill_dc_dirty_rects(struct drm_plane *plane, if (new_plane_state->rotation != DRM_MODE_ROTATE_0) goto ffu; - num_clips = drm_plane_get_damage_clips_count(new_plane_state); - clips = drm_plane_get_damage_clips(new_plane_state); + if (!new_plane_state->ignore_damage_clips) { + num_clips = drm_plane_get_damage_clips_count(new_plane_state); + clips = drm_plane_get_damage_clips(new_plane_state); + } if (num_clips && (!amdgpu_damage_clips || (amdgpu_damage_clips < 0 && is_psr_su))) From a609b6278bf3cde17eeee6620091465521e4b02c Mon Sep 17 00:00:00 2001 From: Zhu Lingshan Date: Wed, 24 Jun 2026 15:52:35 +0800 Subject: [PATCH 0336/1054] drm/amdgpu: reject mapping a reserved doorbell to a new queue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When creating an user-queue, the user space provides a doorbell BO handle and an offset within the bo to obtain a doorbell. However current implementation using xa_store_irq() to store a doorbell, which allows a later queue created with the same BO and offset parameters to overwrite an existing queue and doorbell mapping. This can cause problems like misrouting fence IRQ processing to a wrong queue, and mislead the cleanup process of one queue erasing the mapping of another queue. This commit fixes this issue by replacing xa_store_irq with xa_insert_irq, which rejects mapping a reserved doorbell to a newly created queue Signed-off-by: Zhu Lingshan Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit 6244eae22966350db52faf9c1369d3b2ffc5de4e) Cc: stable@vger.kernel.org --- 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 91554e7c092c..ef3f0213cc46 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c @@ -680,8 +680,8 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args) /* Update VM owner at userq submit-time for page-fault attribution. */ amdgpu_vm_set_task_info(&fpriv->vm); - r = xa_err(xa_store_irq(&adev->userq_doorbell_xa, index, queue, - GFP_KERNEL)); + r = xa_insert_irq(&adev->userq_doorbell_xa, index, queue, + GFP_KERNEL); if (r) goto clean_mqd; From 020da7c5aac5b86bad8a1571f6eda6b8cff9331d Mon Sep 17 00:00:00 2001 From: Ce Sun Date: Mon, 22 Jun 2026 23:05:09 +0800 Subject: [PATCH 0337/1054] drm/amdgpu: fix resource leak on ACP reset timeout When ACP soft reset poll times out, original code returns early without cleanup, leaking MFD child devices, genpd links and all ACP heap allocations. Replace direct early return with goto out to force run all cleanup logic regardless of reset success, preserve timeout error code for caller. Signed-off-by: Ce Sun Reviewed-by: Tao Zhou Signed-off-by: Alex Deucher (cherry picked from commit 98073e4328d7a8d75d03696ab27f6de70ef1aeda) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c index 4c732e0f776e..f04b2d63c59a 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c @@ -508,6 +508,7 @@ static int acp_hw_fini(struct amdgpu_ip_block *ip_block) u32 val = 0; u32 count = 0; struct amdgpu_device *adev = ip_block->adev; + int ret = 0; /* return early if no ACP */ if (!adev->acp.acp_genpd) { @@ -529,7 +530,8 @@ static int acp_hw_fini(struct amdgpu_ip_block *ip_block) break; if (--count == 0) { dev_err(&adev->pdev->dev, "Failed to reset ACP\n"); - return -ETIMEDOUT; + ret = -ETIMEDOUT; + goto out; } udelay(100); } @@ -546,11 +548,12 @@ static int acp_hw_fini(struct amdgpu_ip_block *ip_block) break; if (--count == 0) { dev_err(&adev->pdev->dev, "Failed to reset ACP\n"); - return -ETIMEDOUT; + ret = -ETIMEDOUT; + goto out; } udelay(100); } - +out: device_for_each_child(adev->acp.parent, NULL, acp_genpd_remove_device); @@ -560,7 +563,7 @@ static int acp_hw_fini(struct amdgpu_ip_block *ip_block) kfree(adev->acp.acp_genpd); kfree(adev->acp.acp_cell); - return 0; + return ret; } static int acp_suspend(struct amdgpu_ip_block *ip_block) From 28c9b3c5dc35cc790d11e26ca3fc6e068be63998 Mon Sep 17 00:00:00 2001 From: Ce Sun Date: Mon, 22 Jun 2026 22:58:16 +0800 Subject: [PATCH 0338/1054] drm/amdgpu: invoke pm_genpd_remove() before freeing genpd Call pm_genpd_remove() to unregister from global list prior to releasing acp_genpd memory, and clear the pointer after free. Signed-off-by: Ce Sun Reviewed-by: Tao Zhou Signed-off-by: Alex Deucher (cherry picked from commit cd8650d7a91ee8b768e202354672553faa5cc1f2) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c index f04b2d63c59a..9014678d75ab 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c @@ -560,7 +560,9 @@ static int acp_hw_fini(struct amdgpu_ip_block *ip_block) mfd_remove_devices(adev->acp.parent); kfree(adev->acp.i2s_pdata); kfree(adev->acp.acp_res); + pm_genpd_remove(&adev->acp.acp_genpd->gpd); kfree(adev->acp.acp_genpd); + adev->acp.acp_genpd = NULL; kfree(adev->acp.acp_cell); return ret; From 75050390151a14802be433c3856ddcb483cecd24 Mon Sep 17 00:00:00 2001 From: Honglei Huang Date: Thu, 25 Jun 2026 16:23:47 +0800 Subject: [PATCH 0339/1054] drm/amd/display: use kvzalloc to allocate struct dc struct dc has grown large over time (most of it the two inlined dc_scratch_space copies) and now sits close to the page allocator's 4 MiB contiguous allocation limit. Its actual size is not fixed by the source alone, it also depends on the compiler and the .config, so it can easily cross 4 MiB, e.g. with a newer GCC or a config change. dc_create() allocates it with kzalloc(). Once struct dc exceeds 4 MiB the request is rounded up to order 11 (8 MiB), which is above MAX_PAGE_ORDER, so the page allocator warns and returns NULL. dc_create() then fails, DM init fails and amdgpu probe aborts with -EINVAL: WARNING: mm/page_alloc.c:5197 at __alloc_frozen_pages_noprof+0x2f9/0x380 dc_create+0x38/0x660 [amdgpu] amdgpu_dm_init+0x2d9/0x510 [amdgpu] dm_hw_init+0x1b/0x90 [amdgpu] amdgpu_device_init.cold+0x150d/0x1e13 [amdgpu] amdgpu_driver_load_kms+0x19/0x80 [amdgpu] amdgpu_pci_probe+0x1e2/0x4c0 [amdgpu] dc_create() then returns NULL and DM init fails, which aborts the whole GPU init and makes amdgpu probe fail with -EINVAL ("hw_init of IP block failed -22"), leaving the display unusable. The subsequent amdgpu_irq_put() warnings during teardown are just fallout of unwinding a half-initialized device. struct dc is a software-only bookkeeping structure that is never handed to hardware DMA and is only ever kept as an opaque pointer, so it does not require physically contiguous memory. Allocate it with kvzalloc() (and free it with kvfree()) so that the allocator can fall back to vmalloc() when a contiguous allocation of that size is not available, which also avoids the MAX_PAGE_ORDER warning entirely. v2: - Rebase to amd-staging-drm-next. Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/5406 Reviewed-by: Mario Limonciello (AMD) Signed-off-by: Honglei Huang Signed-off-by: Alex Deucher (cherry picked from commit 991e0516a8072f2292681c6ae98a924ab0e32575) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/display/dc/core/dc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c index 72762c4fa392..175106cce5a4 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc.c @@ -1509,7 +1509,7 @@ static void disable_vbios_mode_if_required( struct dc *dc_create(const struct dc_init_data *init_params) { - struct dc *dc = kzalloc_obj(*dc); + struct dc *dc = kvzalloc_obj(*dc); unsigned int full_pipe_count; if (!dc) @@ -1557,7 +1557,7 @@ struct dc *dc_create(const struct dc_init_data *init_params) destruct_dc: dc_destruct(dc); - kfree(dc); + kvfree(dc); return NULL; } @@ -1606,7 +1606,7 @@ void dc_deinit_callbacks(struct dc *dc) void dc_destroy(struct dc **dc) { dc_destruct(*dc); - kfree(*dc); + kvfree(*dc); *dc = NULL; } From 93c8fe6d56037f284be7116d0c8155847c6d7fbe Mon Sep 17 00:00:00 2001 From: Harry Wentland Date: Tue, 16 Jun 2026 12:17:45 -0400 Subject: [PATCH 0340/1054] drm/amd/display: guard against overflow in HDCP message dump [Why] mod_hdcp_dump_binary_message() computed target_size (a uint32_t) as roughly byte_size * msg_size and gated the whole write on buf_size >= target_size. A large msg_size can overflow target_size, wrapping it to a small value that passes the check while the loop still writes byte_size * msg_size bytes into buf. All current callers pass small constants so this is not reachable today, but the unchecked arithmetic should be hardened. [How] Drop the overflow-prone target_size precomputation and instead bounds-check the output position on every iteration, stopping once the next entry would not leave room for the trailing terminator. This cannot overflow and, for oversized messages, dumps as much as fits rather than printing nothing. Fixes: 4c283fdac08a ("drm/amd/display: Add HDCP module") Assisted-by: Copilot:claude-opus-4.8 Reviewed-by: Alex Hung Signed-off-by: Harry Wentland Signed-off-by: George Zhang Signed-off-by: Alex Deucher (cherry picked from commit d0a775e5d70b376696245a14c09e3aa6dde0023a) Cc: stable@vger.kernel.org --- .../drm/amd/display/modules/hdcp/hdcp_log.c | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_log.c b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_log.c index 1164fd96b714..f0f8e280ed30 100644 --- a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_log.c +++ b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_log.c @@ -33,22 +33,28 @@ void mod_hdcp_dump_binary_message(uint8_t *msg, uint32_t msg_size, byte_size = 3, newline_size = 1, terminator_size = 1; - uint32_t line_count = msg_size / bytes_per_line, - trailing_bytes = msg_size % bytes_per_line; - uint32_t target_size = (byte_size * bytes_per_line + newline_size) * line_count + - byte_size * trailing_bytes + newline_size + terminator_size; uint32_t buf_pos = 0; uint32_t i = 0; - if (buf_size >= target_size) { - for (i = 0; i < msg_size; i++) { - if (i % bytes_per_line == 0) - buf[buf_pos++] = '\n'; - sprintf((char *)&buf[buf_pos], "%02X ", msg[i]); - buf_pos += byte_size; - } - buf[buf_pos++] = '\0'; + /* Need room for at least the terminator. */ + if (buf_size < terminator_size) + return; + + for (i = 0; i < msg_size; i++) { + uint32_t needed = byte_size + terminator_size; + + if (i % bytes_per_line == 0) + needed += newline_size; + + if (buf_pos + needed > buf_size) + break; + + if (i % bytes_per_line == 0) + buf[buf_pos++] = '\n'; + sprintf((char *)&buf[buf_pos], "%02X ", msg[i]); + buf_pos += byte_size; } + buf[buf_pos++] = '\0'; } void mod_hdcp_log_ddc_trace(struct mod_hdcp *hdcp) From 5be7f6720a0ff93cf224c9bc81d1f493bf3fe632 Mon Sep 17 00:00:00 2001 From: Natalie Vock Date: Fri, 29 May 2026 17:30:50 +0200 Subject: [PATCH 0341/1054] drm/amdgpu: Only set bo->moved when the BO was actually moved MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "moved" VM state is a bit unfortunately named, because BOs can end up in this state without being physically moved. While we need to invalidate every mapping when BOs are physically moved, in some other cases like PRT binds/unbinds there is no need to refresh mappings except those affected by the bind. Full invalidation of all BO mappings manifested as severe regressions in PRT bind performance, which this patch fixes. The offending patch is 4cdbba5a16aa ("drm/amdgpu: restructure VM state machine v4") in the amd-staging-drm-next tree, although it has not yet propagated anywhere else. Fixes: 4cdbba5a16aa ("drm/amdgpu: restructure VM state machine v4") Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/5437 Signed-off-by: Natalie Vock Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit 0b2fa33b4235991a100dd799c891cf5c242aaed1) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c index fee4c94c2585..3f3369d427a1 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c @@ -232,7 +232,6 @@ static void amdgpu_vm_bo_moved(struct amdgpu_vm_bo_base *vm_bo) vm_bo->moved = false; list_move(&vm_bo->vm_status, &lists->idle); } else { - vm_bo->moved = true; list_move(&vm_bo->vm_status, &lists->moved); } amdgpu_vm_bo_unlock_lists(vm_bo); @@ -608,6 +607,7 @@ int amdgpu_vm_validate(struct amdgpu_device *adev, struct amdgpu_vm *vm, return r; vm->update_funcs->map_table(to_amdgpu_bo_vm(bo_base->bo)); + bo_base->moved = true; amdgpu_vm_bo_moved(bo_base); } @@ -625,6 +625,7 @@ int amdgpu_vm_validate(struct amdgpu_device *adev, struct amdgpu_vm *vm, if (r) return r; + bo_base->moved = true; amdgpu_vm_bo_moved(bo_base); } @@ -645,6 +646,7 @@ int amdgpu_vm_validate(struct amdgpu_device *adev, struct amdgpu_vm *vm, if (r) return r; + bo_base->moved = true; amdgpu_vm_bo_moved(bo_base); /* It's a bit inefficient to always jump back to the start, but @@ -2284,6 +2286,7 @@ void amdgpu_vm_bo_invalidate(struct amdgpu_bo *bo, bool evicted) if (bo_base->moved) continue; + bo_base->moved = true; amdgpu_vm_bo_moved(bo_base); } } From d4dbcb11eaaa85611ee28f92438361a0e1245adb Mon Sep 17 00:00:00 2001 From: Natalie Vock Date: Fri, 29 May 2026 17:30:51 +0200 Subject: [PATCH 0342/1054] drm/amdgpu: Rename moved state to needs_update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This state can be reached via other means than physical moves, like PRT bindings. Make the name match the actual purpose of the state. Signed-off-by: Natalie Vock Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit 1f7a795fb9f8186bd81ca9c4a80f75482db53c9e) --- drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c | 2 +- drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c | 53 +++++++++++++------------- drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h | 9 +++-- 3 files changed, 33 insertions(+), 31 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c index c2e6495a28bc..e714cee2997a 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c @@ -1322,7 +1322,7 @@ static int amdgpu_cs_submit(struct amdgpu_cs_parser *p, e->range = NULL; } - if (r || !list_empty(&vm->individual.moved)) { + if (r || !list_empty(&vm->individual.needs_update)) { r = -EAGAIN; mutex_unlock(&p->adev->notifier_lock); return r; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c index 3f3369d427a1..f317f888b59f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c @@ -142,7 +142,7 @@ static void amdgpu_vm_assert_locked(struct amdgpu_vm *vm) static void amdgpu_vm_bo_status_init(struct amdgpu_vm_bo_status *lists) { INIT_LIST_HEAD(&lists->evicted); - INIT_LIST_HEAD(&lists->moved); + INIT_LIST_HEAD(&lists->needs_update); INIT_LIST_HEAD(&lists->idle); } @@ -211,14 +211,14 @@ static void amdgpu_vm_bo_evicted(struct amdgpu_vm_bo_base *vm_bo) amdgpu_vm_bo_unlock_lists(vm_bo); } /** - * amdgpu_vm_bo_moved - vm_bo is moved + * amdgpu_vm_bo_needs_update - vm_bo needs pagetable update * - * @vm_bo: vm_bo which is moved + * @vm_bo: vm_bo which is out of date * - * State for vm_bo objects meaning the underlying BO was moved but the new - * location not yet reflected in the page tables. + * State for vm_bo objects meaning the underlying BO had mapping changes (move, PRT bind/unbind) + * but the new location is not yet reflected in the page tables. */ -static void amdgpu_vm_bo_moved(struct amdgpu_vm_bo_base *vm_bo) +static void amdgpu_vm_bo_needs_update(struct amdgpu_vm_bo_base *vm_bo) { struct amdgpu_vm_bo_status *lists; struct amdgpu_bo *bo = vm_bo->bo; @@ -232,7 +232,7 @@ static void amdgpu_vm_bo_moved(struct amdgpu_vm_bo_base *vm_bo) vm_bo->moved = false; list_move(&vm_bo->vm_status, &lists->idle); } else { - list_move(&vm_bo->vm_status, &lists->moved); + list_move(&vm_bo->vm_status, &lists->needs_update); } amdgpu_vm_bo_unlock_lists(vm_bo); } @@ -273,14 +273,14 @@ static void amdgpu_vm_bo_reset_state_machine(struct amdgpu_vm *vm) */ amdgpu_vm_assert_locked(vm); list_for_each_entry_safe(vm_bo, tmp, &vm->kernel.idle, vm_status) - amdgpu_vm_bo_moved(vm_bo); + amdgpu_vm_bo_needs_update(vm_bo); list_for_each_entry_safe(vm_bo, tmp, &vm->always_valid.idle, vm_status) - amdgpu_vm_bo_moved(vm_bo); + amdgpu_vm_bo_needs_update(vm_bo); spin_lock(&vm->individual_lock); list_for_each_entry_safe(vm_bo, tmp, &vm->individual.idle, vm_status) { vm_bo->moved = true; - list_move(&vm_bo->vm_status, &vm->individual.moved); + list_move(&vm_bo->vm_status, &vm->individual.needs_update); } spin_unlock(&vm->individual_lock); } @@ -435,7 +435,7 @@ void amdgpu_vm_bo_base_init(struct amdgpu_vm_bo_base *base, */ if (bo->preferred_domains & amdgpu_mem_type_to_domain(bo->tbo.resource->mem_type)) - amdgpu_vm_bo_moved(base); + amdgpu_vm_bo_needs_update(base); else amdgpu_vm_bo_evicted(base); } @@ -608,7 +608,7 @@ int amdgpu_vm_validate(struct amdgpu_device *adev, struct amdgpu_vm *vm, vm->update_funcs->map_table(to_amdgpu_bo_vm(bo_base->bo)); bo_base->moved = true; - amdgpu_vm_bo_moved(bo_base); + amdgpu_vm_bo_needs_update(bo_base); } /* @@ -626,7 +626,7 @@ int amdgpu_vm_validate(struct amdgpu_device *adev, struct amdgpu_vm *vm, return r; bo_base->moved = true; - amdgpu_vm_bo_moved(bo_base); + amdgpu_vm_bo_needs_update(bo_base); } if (!ticket) @@ -647,7 +647,7 @@ int amdgpu_vm_validate(struct amdgpu_device *adev, struct amdgpu_vm *vm, return r; bo_base->moved = true; - amdgpu_vm_bo_moved(bo_base); + amdgpu_vm_bo_needs_update(bo_base); /* It's a bit inefficient to always jump back to the start, but * we would need to re-structure the KFD for properly fixing @@ -981,7 +981,7 @@ int amdgpu_vm_update_pdes(struct amdgpu_device *adev, amdgpu_vm_assert_locked(vm); - if (list_empty(&vm->kernel.moved)) + if (list_empty(&vm->kernel.needs_update)) return 0; if (!drm_dev_enter(adev_to_drm(adev), &idx)) @@ -997,7 +997,7 @@ int amdgpu_vm_update_pdes(struct amdgpu_device *adev, if (r) goto error; - list_for_each_entry(entry, &vm->kernel.moved, vm_status) { + list_for_each_entry(entry, &vm->kernel.needs_update, vm_status) { /* vm_flush_needed after updating moved PDEs */ flush_tlb_needed |= entry->moved; @@ -1013,7 +1013,8 @@ int amdgpu_vm_update_pdes(struct amdgpu_device *adev, if (flush_tlb_needed) atomic64_inc(&vm->tlb_seq); - list_for_each_entry_safe(entry, tmp, &vm->kernel.moved, vm_status) + list_for_each_entry_safe(entry, tmp, &vm->kernel.needs_update, + vm_status) amdgpu_vm_bo_idle(entry); error: @@ -1617,7 +1618,7 @@ int amdgpu_vm_handle_moved(struct amdgpu_device *adev, bool clear, unlock; int r; - list_for_each_entry_safe(bo_va, tmp, &vm->always_valid.moved, + list_for_each_entry_safe(bo_va, tmp, &vm->always_valid.needs_update, base.vm_status) { /* Per VM BOs never need to bo cleared in the page tables */ r = amdgpu_vm_bo_update(adev, bo_va, false); @@ -1626,8 +1627,8 @@ int amdgpu_vm_handle_moved(struct amdgpu_device *adev, } spin_lock(&vm->individual_lock); - while (!list_empty(&vm->individual.moved)) { - bo_va = list_first_entry(&vm->individual.moved, + while (!list_empty(&vm->individual.needs_update)) { + bo_va = list_first_entry(&vm->individual.needs_update, typeof(*bo_va), base.vm_status); bo = bo_va->base.bo; resv = bo->tbo.base.resv; @@ -1788,7 +1789,7 @@ static void amdgpu_vm_bo_insert_map(struct amdgpu_device *adev, amdgpu_vm_prt_get(adev); if (amdgpu_vm_is_bo_always_valid(vm, bo) && !bo_va->base.moved) - amdgpu_vm_bo_moved(&bo_va->base); + amdgpu_vm_bo_needs_update(&bo_va->base); trace_amdgpu_vm_bo_map(bo_va, mapping); } @@ -2097,7 +2098,7 @@ int amdgpu_vm_bo_clear_mappings(struct amdgpu_device *adev, if (amdgpu_vm_is_bo_always_valid(vm, bo) && !before->bo_va->base.moved) - amdgpu_vm_bo_moved(&before->bo_va->base); + amdgpu_vm_bo_needs_update(&before->bo_va->base); } else { kfree(before); } @@ -2112,7 +2113,7 @@ int amdgpu_vm_bo_clear_mappings(struct amdgpu_device *adev, if (amdgpu_vm_is_bo_always_valid(vm, bo) && !after->bo_va->base.moved) - amdgpu_vm_bo_moved(&after->bo_va->base); + amdgpu_vm_bo_needs_update(&after->bo_va->base); } else { kfree(after); } @@ -2287,7 +2288,7 @@ void amdgpu_vm_bo_invalidate(struct amdgpu_bo *bo, bool evicted) if (bo_base->moved) continue; bo_base->moved = true; - amdgpu_vm_bo_moved(bo_base); + amdgpu_vm_bo_needs_update(bo_base); } } @@ -3101,7 +3102,7 @@ static void amdgpu_debugfs_vm_bo_status_info(struct seq_file *m, id = 0; seq_puts(m, "\tMoved BOs:\n"); - list_for_each_entry(base, &lists->moved, vm_status) { + list_for_each_entry(base, &lists->needs_update, vm_status) { if (!base->bo) continue; @@ -3110,7 +3111,7 @@ static void amdgpu_debugfs_vm_bo_status_info(struct seq_file *m, id = 0; seq_puts(m, "\tIdle BOs:\n"); - list_for_each_entry(base, &lists->moved, vm_status) { + list_for_each_entry(base, &lists->needs_update, vm_status) { if (!base->bo) continue; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h index b32f51a78cd8..5822836fa4a3 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h @@ -212,7 +212,8 @@ struct amdgpu_vm_bo_base { * protected by vm BO being reserved */ bool shared; - /* protected by the BO being reserved */ + /* if the BO was moved and all mappings are invalid + * protected by the BO being reserved */ bool moved; }; @@ -220,14 +221,14 @@ struct amdgpu_vm_bo_base { * The following status lists contain amdgpu_vm_bo_base objects for * either PD/PTs, per VM BOs or BOs with individual resv object. * - * The state transits are: evicted -> moved -> idle + * The state transits are: evicted -> needs_update -> idle */ struct amdgpu_vm_bo_status { /* BOs evicted which need to move into place again */ struct list_head evicted; - /* BOs which moved but new location hasn't been updated in the PDs/PTs */ - struct list_head moved; + /* BOs whose mappings changed but PDs/PTs haven't been updated */ + struct list_head needs_update; /* BOs done with the state machine and need no further action */ struct list_head idle; From efcedeececcf995fcf717b21e39aa7c446fa3bf7 Mon Sep 17 00:00:00 2001 From: Boyuan Zhang Date: Wed, 24 Jun 2026 09:50:01 -0400 Subject: [PATCH 0343/1054] drm/amdgpu/jpeg: fix jpeg_v5_0_1_is_idle detection jpeg_v5_0_1_is_idle() initializes ret to false and then accumulates ring idle status using &=. Since false & condition always remains false, the function can never report the JPEG block as idle. Initialize ret to true so the function returns true only when all JPEG rings report RB_JOB_DONE. Signed-off-by: Boyuan Zhang Reviewed-by: David (Ming Qiang) Wu Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit 680adf5faeeabb4585f7aeb53681719e2d6c2f41) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_1.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_1.c index 250316704dfa..ae3afc7ab326 100644 --- a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_1.c +++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_1.c @@ -657,7 +657,7 @@ static void jpeg_v5_0_1_dec_ring_set_wptr(struct amdgpu_ring *ring) static bool jpeg_v5_0_1_is_idle(struct amdgpu_ip_block *ip_block) { struct amdgpu_device *adev = ip_block->adev; - bool ret = false; + bool ret = true; int i, j; for (i = 0; i < adev->jpeg.num_jpeg_inst; ++i) { From 52f650963d8825e97a0ccdd2b616f8a01d9d3d38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20K=C3=B6nig?= Date: Wed, 24 Jun 2026 16:00:41 +0200 Subject: [PATCH 0344/1054] drm/amdgpu: fix check in amdgpu_hmm_invalidate_gfx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For a short moment during alloc/free the userptr BO is not part of his VM, so bo->vm_bo can be NULL. Keep a reference to the VM root PD as parent of the userptr BO so that we can always use that to wait for all submissions of the VM instead of only the one involving the userptr BO. Signed-off-by: Christian König Fixes: 91250893cbaa ("drm/amdgpu: fix waiting for all submissions for userptrs") Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/5399 Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit 631849ff5d603841e74f19f4a5e30fe1f7d7cf30) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c | 1 + drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c index 76da3f932f24..6a0699746fbc 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c @@ -535,6 +535,7 @@ int amdgpu_gem_userptr_ioctl(struct drm_device *dev, void *data, bo = gem_to_amdgpu_bo(gobj); bo->preferred_domains = AMDGPU_GEM_DOMAIN_GTT; bo->allowed_domains = AMDGPU_GEM_DOMAIN_GTT; + bo->parent = amdgpu_bo_ref(fpriv->vm.root.bo); r = amdgpu_ttm_tt_set_userptr(&bo->tbo, args->addr, args->flags); if (r) goto release_object; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c index 99bc9ad67d5b..a7d13e337d84 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c @@ -67,7 +67,6 @@ 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)) @@ -78,7 +77,7 @@ 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(vm_root->tbo.base.resv, + r = dma_resv_wait_timeout(bo->parent->tbo.base.resv, DMA_RESV_USAGE_BOOKKEEP, false, MAX_SCHEDULE_TIMEOUT); mutex_unlock(&adev->notifier_lock); From 96f222efc9e798165079def83d7f94f22ca9c384 Mon Sep 17 00:00:00 2001 From: Prike Liang Date: Thu, 25 Jun 2026 10:31:00 +0800 Subject: [PATCH 0345/1054] drm/amdgpu/mes11: set doorbell offset for suspending userq MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Updating the union MESAPI__SUSPEND and union MESAPI__RESUME to add the doorbell offset for suspending userq. Signed-off-by: Prike Liang Acked-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit 30af09db33696f7e0de5c0c505cbb0cb92b6e25b) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/mes_v11_0.c | 2 ++ drivers/gpu/drm/amd/include/mes_v11_api_def.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c b/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c index ac6d4f277336..4d133c481b26 100644 --- a/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c @@ -559,6 +559,7 @@ static int mes_v11_0_suspend_gang(struct amdgpu_mes *mes, mes_suspend_gang_pkt.gang_context_addr = input->gang_context_addr; mes_suspend_gang_pkt.suspend_fence_addr = input->suspend_fence_addr; mes_suspend_gang_pkt.suspend_fence_value = input->suspend_fence_value; + mes_suspend_gang_pkt.doorbell_offset = input->doorbell_offset; return mes_v11_0_submit_pkt_and_poll_completion(mes, &mes_suspend_gang_pkt, sizeof(mes_suspend_gang_pkt), @@ -578,6 +579,7 @@ static int mes_v11_0_resume_gang(struct amdgpu_mes *mes, mes_resume_gang_pkt.resume_all_gangs = input->resume_all_gangs; mes_resume_gang_pkt.gang_context_addr = input->gang_context_addr; + mes_resume_gang_pkt.doorbell_offset = input->doorbell_offset; return mes_v11_0_submit_pkt_and_poll_completion(mes, &mes_resume_gang_pkt, sizeof(mes_resume_gang_pkt), diff --git a/drivers/gpu/drm/amd/include/mes_v11_api_def.h b/drivers/gpu/drm/amd/include/mes_v11_api_def.h index f9629d42ada2..7808147ada38 100644 --- a/drivers/gpu/drm/amd/include/mes_v11_api_def.h +++ b/drivers/gpu/drm/amd/include/mes_v11_api_def.h @@ -427,6 +427,7 @@ union MESAPI__SUSPEND { uint32_t suspend_fence_value; struct MES_API_STATUS api_status; + uint32_t doorbell_offset; }; uint32_t max_dwords_in_api[API_FRAME_SIZE_IN_DWORDS]; @@ -444,6 +445,7 @@ union MESAPI__RESUME { uint64_t gang_context_addr; struct MES_API_STATUS api_status; + uint32_t doorbell_offset; }; uint32_t max_dwords_in_api[API_FRAME_SIZE_IN_DWORDS]; From 218c4929236d33413e5ecc6003c5185018f830fc Mon Sep 17 00:00:00 2001 From: Prike Liang Date: Thu, 25 Jun 2026 10:42:27 +0800 Subject: [PATCH 0346/1054] drm/amdgpu/mes12: set doorbell offset for suspending userq MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Updating the union MESAPI__SUSPEND and union MESAPI__RESUME to add the doorbell offset for suspending userq. Signed-off-by: Prike Liang Acked-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit 5b58a2c120063544869d0284d3b355527f9f04f5) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/mes_v12_0.c | 2 ++ drivers/gpu/drm/amd/amdgpu/mes_v12_1.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/mes_v12_0.c b/drivers/gpu/drm/amd/amdgpu/mes_v12_0.c index 7453fb11289e..b6cbc25e1ab4 100644 --- a/drivers/gpu/drm/amd/amdgpu/mes_v12_0.c +++ b/drivers/gpu/drm/amd/amdgpu/mes_v12_0.c @@ -592,6 +592,7 @@ static int mes_v12_0_suspend_gang(struct amdgpu_mes *mes, mes_suspend_gang_pkt.gang_context_addr = input->gang_context_addr; mes_suspend_gang_pkt.suspend_fence_addr = input->suspend_fence_addr; mes_suspend_gang_pkt.suspend_fence_value = input->suspend_fence_value; + mes_suspend_gang_pkt.doorbell_offset = input->doorbell_offset; return mes_v12_0_submit_pkt_and_poll_completion(mes, AMDGPU_MES_SCHED_PIPE, &mes_suspend_gang_pkt, sizeof(mes_suspend_gang_pkt), @@ -611,6 +612,7 @@ static int mes_v12_0_resume_gang(struct amdgpu_mes *mes, mes_resume_gang_pkt.resume_all_gangs = input->resume_all_gangs; mes_resume_gang_pkt.gang_context_addr = input->gang_context_addr; + mes_resume_gang_pkt.doorbell_offset = input->doorbell_offset; return mes_v12_0_submit_pkt_and_poll_completion(mes, AMDGPU_MES_SCHED_PIPE, &mes_resume_gang_pkt, sizeof(mes_resume_gang_pkt), diff --git a/drivers/gpu/drm/amd/amdgpu/mes_v12_1.c b/drivers/gpu/drm/amd/amdgpu/mes_v12_1.c index 8a90ad5a51b8..e13535d94c51 100644 --- a/drivers/gpu/drm/amd/amdgpu/mes_v12_1.c +++ b/drivers/gpu/drm/amd/amdgpu/mes_v12_1.c @@ -484,6 +484,7 @@ static int mes_v12_1_suspend_gang(struct amdgpu_mes *mes, mes_suspend_gang_pkt.gang_context_addr = input->gang_context_addr; mes_suspend_gang_pkt.suspend_fence_addr = input->suspend_fence_addr; mes_suspend_gang_pkt.suspend_fence_value = input->suspend_fence_value; + mes_suspend_gang_pkt.doorbell_offset = input->doorbell_offset; /* Suspend gang is handled by master MES */ return mes_v12_1_submit_pkt_and_poll_completion(mes, input->xcc_id, AMDGPU_MES_SCHED_PIPE, @@ -504,6 +505,7 @@ static int mes_v12_1_resume_gang(struct amdgpu_mes *mes, mes_resume_gang_pkt.resume_all_gangs = input->resume_all_gangs; mes_resume_gang_pkt.gang_context_addr = input->gang_context_addr; + mes_resume_gang_pkt.doorbell_offset = input->doorbell_offset; /* Resume gang is handled by master MES */ return mes_v12_1_submit_pkt_and_poll_completion(mes, input->xcc_id, AMDGPU_MES_SCHED_PIPE, From b181bf68d11f034efe27ae1377a0f659605f040f Mon Sep 17 00:00:00 2001 From: Prike Liang Date: Wed, 17 Jun 2026 14:20:16 +0800 Subject: [PATCH 0347/1054] drm/amdgpu: add the doorbell index input for suspending userq MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It requires inputing the doorbell offset for MES firmware preempts the userq, and adding the doorbell offset also keep aliging with the union MESAPI__SUSPEND in MES firmware. Signed-off-by: Prike Liang Acked-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit bc434335ab3c096a33a9e88c7951b4ac574db458) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_mes.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.h index fdd06a17520a..1aae49f4df49 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.h @@ -302,12 +302,14 @@ struct mes_suspend_gang_input { uint64_t gang_context_addr; uint64_t suspend_fence_addr; uint32_t suspend_fence_value; + uint32_t doorbell_offset; }; struct mes_resume_gang_input { uint32_t xcc_id; bool resume_all_gangs; uint64_t gang_context_addr; + uint32_t doorbell_offset; }; struct mes_reset_queue_input { From ff8cb5cee095f9d5ec4dfa0dd970cfa89bf7d3af Mon Sep 17 00:00:00 2001 From: Granthali Vinodkumar Dhandar Date: Wed, 17 Jun 2026 17:39:58 +0530 Subject: [PATCH 0348/1054] drm/amdgpu: add support for GC IP version 11.7.0 Initialize GC IP 11_7_0 Signed-off-by: Granthali Vinodkumar Dhandar Reviewed-by: Mario Limonciello Signed-off-by: Alex Deucher (cherry picked from commit cf591e67c095542a16475df293ec7bc9a118e4ee) --- drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c | 6 ++++ drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c | 1 + drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c | 12 +++++++- drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c | 2 ++ drivers/gpu/drm/amd/amdgpu/imu_v11_0.c | 1 + drivers/gpu/drm/amd/amdgpu/mes_v11_0.c | 2 ++ drivers/gpu/drm/amd/amdgpu/soc21.c | 28 +++++++++++++++++++ drivers/gpu/drm/amd/amdkfd/kfd_crat.c | 1 + drivers/gpu/drm/amd/amdkfd/kfd_device.c | 5 ++++ 9 files changed, 57 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c index be5069642a90..3b93c264c60e 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c @@ -2119,6 +2119,7 @@ static int amdgpu_discovery_set_common_ip_blocks(struct amdgpu_device *adev) case IP_VERSION(11, 5, 3): case IP_VERSION(11, 5, 4): case IP_VERSION(11, 5, 6): + case IP_VERSION(11, 7, 0): amdgpu_device_ip_block_add(adev, &soc21_common_ip_block); break; case IP_VERSION(12, 0, 0): @@ -2180,6 +2181,7 @@ static int amdgpu_discovery_set_gmc_ip_blocks(struct amdgpu_device *adev) case IP_VERSION(11, 5, 3): case IP_VERSION(11, 5, 4): case IP_VERSION(11, 5, 6): + case IP_VERSION(11, 7, 0): amdgpu_device_ip_block_add(adev, &gmc_v11_0_ip_block); break; case IP_VERSION(12, 0, 0): @@ -2506,6 +2508,7 @@ static int amdgpu_discovery_set_gc_ip_blocks(struct amdgpu_device *adev) case IP_VERSION(11, 5, 3): case IP_VERSION(11, 5, 4): case IP_VERSION(11, 5, 6): + case IP_VERSION(11, 7, 0): amdgpu_device_ip_block_add(adev, &gfx_v11_0_ip_block); break; case IP_VERSION(12, 0, 0): @@ -2719,6 +2722,7 @@ static int amdgpu_discovery_set_mes_ip_blocks(struct amdgpu_device *adev) case IP_VERSION(11, 5, 3): case IP_VERSION(11, 5, 4): case IP_VERSION(11, 5, 6): + case IP_VERSION(11, 7, 0): amdgpu_device_ip_block_add(adev, &mes_v11_0_ip_block); adev->enable_mes = true; adev->enable_mes_kiq = true; @@ -3127,6 +3131,7 @@ int amdgpu_discovery_set_ip_blocks(struct amdgpu_device *adev) case IP_VERSION(11, 5, 3): case IP_VERSION(11, 5, 4): case IP_VERSION(11, 5, 6): + case IP_VERSION(11, 7, 0): adev->family = AMDGPU_FAMILY_GC_11_5_0; break; case IP_VERSION(12, 0, 0): @@ -3156,6 +3161,7 @@ int amdgpu_discovery_set_ip_blocks(struct amdgpu_device *adev) case IP_VERSION(11, 5, 3): case IP_VERSION(11, 5, 4): case IP_VERSION(11, 5, 6): + case IP_VERSION(11, 7, 0): adev->flags |= AMD_IS_APU; break; default: diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c index 5f7745143f56..d4ca889be416 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c @@ -977,6 +977,7 @@ void amdgpu_gmc_tmz_set(struct amdgpu_device *adev) case IP_VERSION(11, 5, 3): case IP_VERSION(11, 5, 4): case IP_VERSION(11, 5, 6): + case IP_VERSION(11, 7, 0): /* Don't enable it by default yet. */ if (amdgpu_tmz < 1) { diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c index e60ae566b5f8..6004750cd9b0 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c @@ -133,6 +133,10 @@ MODULE_FIRMWARE("amdgpu/gc_11_5_6_pfp.bin"); MODULE_FIRMWARE("amdgpu/gc_11_5_6_me.bin"); MODULE_FIRMWARE("amdgpu/gc_11_5_6_mec.bin"); MODULE_FIRMWARE("amdgpu/gc_11_5_6_rlc.bin"); +MODULE_FIRMWARE("amdgpu/gc_11_7_0_pfp.bin"); +MODULE_FIRMWARE("amdgpu/gc_11_7_0_me.bin"); +MODULE_FIRMWARE("amdgpu/gc_11_7_0_mec.bin"); +MODULE_FIRMWARE("amdgpu/gc_11_7_0_rlc.bin"); static const struct amdgpu_hwip_reg_entry gc_reg_list_11_0[] = { SOC15_REG_ENTRY_STR(GC, 0, regGRBM_STATUS), @@ -1128,6 +1132,7 @@ static int gfx_v11_0_gpu_early_init(struct amdgpu_device *adev) case IP_VERSION(11, 5, 3): case IP_VERSION(11, 5, 4): case IP_VERSION(11, 5, 6): + case IP_VERSION(11, 7, 0): adev->gfx.config.max_hw_contexts = 8; adev->gfx.config.sc_prim_fifo_size_frontend = 0x20; adev->gfx.config.sc_prim_fifo_size_backend = 0x100; @@ -1612,6 +1617,7 @@ static int gfx_v11_0_sw_init(struct amdgpu_ip_block *ip_block) case IP_VERSION(11, 5, 3): case IP_VERSION(11, 5, 4): case IP_VERSION(11, 5, 6): + case IP_VERSION(11, 7, 0): adev->gfx.me.num_me = 1; adev->gfx.me.num_pipe_per_me = 1; adev->gfx.me.num_queue_per_pipe = 2; @@ -3085,7 +3091,8 @@ static int gfx_v11_0_wait_for_rlc_autoload_complete(struct amdgpu_device *adev) amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(11, 5, 2) || amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(11, 5, 3) || amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(11, 5, 4) || - amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(11, 5, 6)) + amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(11, 5, 6) || + amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(11, 7, 0)) bootload_status = RREG32_SOC15(GC, 0, regRLC_RLCS_BOOTLOAD_STATUS_gc_11_0_1); else @@ -5758,6 +5765,7 @@ static void gfx_v11_cntl_power_gating(struct amdgpu_device *adev, bool enable) case IP_VERSION(11, 5, 3): case IP_VERSION(11, 5, 4): case IP_VERSION(11, 5, 6): + case IP_VERSION(11, 7, 0): WREG32_SOC15(GC, 0, regRLC_PG_DELAY_3, RLC_PG_DELAY_3_DEFAULT_GC_11_0_1); break; default: @@ -5798,6 +5806,7 @@ static int gfx_v11_0_set_powergating_state(struct amdgpu_ip_block *ip_block, case IP_VERSION(11, 5, 3): case IP_VERSION(11, 5, 4): case IP_VERSION(11, 5, 6): + case IP_VERSION(11, 7, 0): if (!enable) amdgpu_gfx_off_ctrl(adev, false); @@ -5834,6 +5843,7 @@ static int gfx_v11_0_set_clockgating_state(struct amdgpu_ip_block *ip_block, case IP_VERSION(11, 5, 3): case IP_VERSION(11, 5, 4): case IP_VERSION(11, 5, 6): + case IP_VERSION(11, 7, 0): gfx_v11_0_update_gfx_clock_gating(adev, state == AMD_CG_STATE_GATE); break; diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c index 8eb9847d9e1e..8a0a88551461 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c @@ -606,6 +606,7 @@ static void gmc_v11_0_set_gfxhub_funcs(struct amdgpu_device *adev) case IP_VERSION(11, 5, 3): case IP_VERSION(11, 5, 4): case IP_VERSION(11, 5, 6): + case IP_VERSION(11, 7, 0): adev->gfxhub.funcs = &gfxhub_v11_5_0_funcs; break; default: @@ -781,6 +782,7 @@ static int gmc_v11_0_sw_init(struct amdgpu_ip_block *ip_block) case IP_VERSION(11, 5, 3): case IP_VERSION(11, 5, 4): case IP_VERSION(11, 5, 6): + case IP_VERSION(11, 7, 0): set_bit(AMDGPU_GFXHUB(0), adev->vmhubs_mask); set_bit(AMDGPU_MMHUB0(0), adev->vmhubs_mask); /* diff --git a/drivers/gpu/drm/amd/amdgpu/imu_v11_0.c b/drivers/gpu/drm/amd/amdgpu/imu_v11_0.c index f5927c3553ce..177d702e612a 100644 --- a/drivers/gpu/drm/amd/amdgpu/imu_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/imu_v11_0.c @@ -43,6 +43,7 @@ MODULE_FIRMWARE("amdgpu/gc_11_5_2_imu.bin"); MODULE_FIRMWARE("amdgpu/gc_11_5_3_imu.bin"); MODULE_FIRMWARE("amdgpu/gc_11_5_4_imu.bin"); MODULE_FIRMWARE("amdgpu/gc_11_5_6_imu.bin"); +MODULE_FIRMWARE("amdgpu/gc_11_7_0_imu.bin"); static int imu_v11_0_init_microcode(struct amdgpu_device *adev) { diff --git a/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c b/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c index 4d133c481b26..3ca2ee3e9202 100644 --- a/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c @@ -60,6 +60,8 @@ MODULE_FIRMWARE("amdgpu/gc_11_5_4_mes_2.bin"); MODULE_FIRMWARE("amdgpu/gc_11_5_4_mes1.bin"); MODULE_FIRMWARE("amdgpu/gc_11_5_6_mes_2.bin"); MODULE_FIRMWARE("amdgpu/gc_11_5_6_mes1.bin"); +MODULE_FIRMWARE("amdgpu/gc_11_7_0_mes_2.bin"); +MODULE_FIRMWARE("amdgpu/gc_11_7_0_mes1.bin"); static int mes_v11_0_hw_init(struct amdgpu_ip_block *ip_block); static int mes_v11_0_hw_fini(struct amdgpu_ip_block *ip_block); diff --git a/drivers/gpu/drm/amd/amdgpu/soc21.c b/drivers/gpu/drm/amd/amdgpu/soc21.c index 963659deeaff..9b9b13e327d8 100644 --- a/drivers/gpu/drm/amd/amdgpu/soc21.c +++ b/drivers/gpu/drm/amd/amdgpu/soc21.c @@ -838,6 +838,34 @@ static int soc21_common_early_init(struct amdgpu_ip_block *ip_block) adev->pg_flags = 0; adev->external_rev_id = adev->rev_id + 0xd0; break; + case IP_VERSION(11, 7, 0): + adev->cg_flags = AMD_CG_SUPPORT_VCN_MGCG | + AMD_CG_SUPPORT_JPEG_MGCG | + AMD_CG_SUPPORT_GFX_CGCG | + AMD_CG_SUPPORT_GFX_CGLS | + AMD_CG_SUPPORT_GFX_MGCG | + AMD_CG_SUPPORT_GFX_FGCG | + AMD_CG_SUPPORT_REPEATER_FGCG | + AMD_CG_SUPPORT_GFX_PERF_CLK | + AMD_CG_SUPPORT_GFX_3D_CGCG | + AMD_CG_SUPPORT_GFX_3D_CGLS | + AMD_CG_SUPPORT_MC_MGCG | + AMD_CG_SUPPORT_MC_LS | + AMD_CG_SUPPORT_HDP_LS | + AMD_CG_SUPPORT_HDP_DS | + AMD_CG_SUPPORT_HDP_SD | + AMD_CG_SUPPORT_ATHUB_MGCG | + AMD_CG_SUPPORT_ATHUB_LS | + AMD_CG_SUPPORT_IH_CG | + AMD_CG_SUPPORT_BIF_MGCG | + AMD_CG_SUPPORT_BIF_LS; + adev->pg_flags = AMD_PG_SUPPORT_VCN_DPG | + AMD_PG_SUPPORT_VCN | + AMD_PG_SUPPORT_JPEG_DPG | + AMD_PG_SUPPORT_JPEG | + AMD_PG_SUPPORT_GFX_PG; + adev->external_rev_id = adev->rev_id + 0xF; + break; default: /* FIXME: not supported yet */ return -EINVAL; diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_crat.c b/drivers/gpu/drm/amd/amdkfd/kfd_crat.c index f28259d13818..a6a7888c7a8d 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_crat.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_crat.c @@ -1715,6 +1715,7 @@ int kfd_get_gpu_cache_info(struct kfd_node *kdev, struct kfd_gpu_cache_info **pc case IP_VERSION(11, 5, 3): case IP_VERSION(11, 5, 4): case IP_VERSION(11, 5, 6): + case IP_VERSION(11, 7, 0): /* Cacheline size not available in IP discovery for gc11. * kfd_fill_gpu_cache_info_from_gfx_config to hard code it */ diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device.c b/drivers/gpu/drm/amd/amdkfd/kfd_device.c index 5eb863dec8f4..47de7702c39e 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device.c @@ -169,6 +169,7 @@ static void kfd_device_info_set_event_interrupt_class(struct kfd_dev *kfd) case IP_VERSION(11, 5, 3): case IP_VERSION(11, 5, 4): case IP_VERSION(11, 5, 6): + case IP_VERSION(11, 7, 0): kfd->device_info.event_interrupt_class = &event_interrupt_class_v11; break; case IP_VERSION(12, 0, 0): @@ -451,6 +452,10 @@ struct kfd_dev *kgd2kfd_probe(struct amdgpu_device *adev, bool vf) gfx_target_version = 110504; f2g = &gfx_v11_kfd2kgd; break; + case IP_VERSION(11, 7, 0): + gfx_target_version = 110700; + f2g = &gfx_v11_kfd2kgd; + break; case IP_VERSION(12, 0, 0): gfx_target_version = 120000; f2g = &gfx_v12_kfd2kgd; From 166e1100c175093729fd048efef3cd3108e6bfb2 Mon Sep 17 00:00:00 2001 From: Granthali Vinodkumar Dhandar Date: Wed, 17 Jun 2026 18:04:28 +0530 Subject: [PATCH 0349/1054] drm/amdgpu: add support for GC IP version 11.7.1 Initialize GC IP 11_7_1 Signed-off-by: Granthali Vinodkumar Dhandar Reviewed-by: Mario Limonciello Signed-off-by: Alex Deucher (cherry picked from commit a928d8d81ec5cdb5a8944d08136720811efad0f6) --- drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c | 6 ++++ drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c | 1 + drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c | 12 +++++++- drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c | 2 ++ drivers/gpu/drm/amd/amdgpu/imu_v11_0.c | 1 + drivers/gpu/drm/amd/amdgpu/mes_v11_0.c | 2 ++ drivers/gpu/drm/amd/amdgpu/psp_v15_0.c | 2 ++ drivers/gpu/drm/amd/amdgpu/soc21.c | 28 +++++++++++++++++++ drivers/gpu/drm/amd/amdkfd/kfd_crat.c | 1 + drivers/gpu/drm/amd/amdkfd/kfd_device.c | 5 ++++ 10 files changed, 59 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c index 3b93c264c60e..853365dee2a7 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c @@ -2120,6 +2120,7 @@ static int amdgpu_discovery_set_common_ip_blocks(struct amdgpu_device *adev) case IP_VERSION(11, 5, 4): case IP_VERSION(11, 5, 6): case IP_VERSION(11, 7, 0): + case IP_VERSION(11, 7, 1): amdgpu_device_ip_block_add(adev, &soc21_common_ip_block); break; case IP_VERSION(12, 0, 0): @@ -2182,6 +2183,7 @@ static int amdgpu_discovery_set_gmc_ip_blocks(struct amdgpu_device *adev) case IP_VERSION(11, 5, 4): case IP_VERSION(11, 5, 6): case IP_VERSION(11, 7, 0): + case IP_VERSION(11, 7, 1): amdgpu_device_ip_block_add(adev, &gmc_v11_0_ip_block); break; case IP_VERSION(12, 0, 0): @@ -2509,6 +2511,7 @@ static int amdgpu_discovery_set_gc_ip_blocks(struct amdgpu_device *adev) case IP_VERSION(11, 5, 4): case IP_VERSION(11, 5, 6): case IP_VERSION(11, 7, 0): + case IP_VERSION(11, 7, 1): amdgpu_device_ip_block_add(adev, &gfx_v11_0_ip_block); break; case IP_VERSION(12, 0, 0): @@ -2723,6 +2726,7 @@ static int amdgpu_discovery_set_mes_ip_blocks(struct amdgpu_device *adev) case IP_VERSION(11, 5, 4): case IP_VERSION(11, 5, 6): case IP_VERSION(11, 7, 0): + case IP_VERSION(11, 7, 1): amdgpu_device_ip_block_add(adev, &mes_v11_0_ip_block); adev->enable_mes = true; adev->enable_mes_kiq = true; @@ -3132,6 +3136,7 @@ int amdgpu_discovery_set_ip_blocks(struct amdgpu_device *adev) case IP_VERSION(11, 5, 4): case IP_VERSION(11, 5, 6): case IP_VERSION(11, 7, 0): + case IP_VERSION(11, 7, 1): adev->family = AMDGPU_FAMILY_GC_11_5_0; break; case IP_VERSION(12, 0, 0): @@ -3162,6 +3167,7 @@ int amdgpu_discovery_set_ip_blocks(struct amdgpu_device *adev) case IP_VERSION(11, 5, 4): case IP_VERSION(11, 5, 6): case IP_VERSION(11, 7, 0): + case IP_VERSION(11, 7, 1): adev->flags |= AMD_IS_APU; break; default: diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c index d4ca889be416..5d6149ba7ab7 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c @@ -978,6 +978,7 @@ void amdgpu_gmc_tmz_set(struct amdgpu_device *adev) case IP_VERSION(11, 5, 4): case IP_VERSION(11, 5, 6): case IP_VERSION(11, 7, 0): + case IP_VERSION(11, 7, 1): /* Don't enable it by default yet. */ if (amdgpu_tmz < 1) { diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c index 6004750cd9b0..3b12eb27a253 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c @@ -137,6 +137,10 @@ MODULE_FIRMWARE("amdgpu/gc_11_7_0_pfp.bin"); MODULE_FIRMWARE("amdgpu/gc_11_7_0_me.bin"); MODULE_FIRMWARE("amdgpu/gc_11_7_0_mec.bin"); MODULE_FIRMWARE("amdgpu/gc_11_7_0_rlc.bin"); +MODULE_FIRMWARE("amdgpu/gc_11_7_1_pfp.bin"); +MODULE_FIRMWARE("amdgpu/gc_11_7_1_me.bin"); +MODULE_FIRMWARE("amdgpu/gc_11_7_1_mec.bin"); +MODULE_FIRMWARE("amdgpu/gc_11_7_1_rlc.bin"); static const struct amdgpu_hwip_reg_entry gc_reg_list_11_0[] = { SOC15_REG_ENTRY_STR(GC, 0, regGRBM_STATUS), @@ -1133,6 +1137,7 @@ static int gfx_v11_0_gpu_early_init(struct amdgpu_device *adev) case IP_VERSION(11, 5, 4): case IP_VERSION(11, 5, 6): case IP_VERSION(11, 7, 0): + case IP_VERSION(11, 7, 1): adev->gfx.config.max_hw_contexts = 8; adev->gfx.config.sc_prim_fifo_size_frontend = 0x20; adev->gfx.config.sc_prim_fifo_size_backend = 0x100; @@ -1618,6 +1623,7 @@ static int gfx_v11_0_sw_init(struct amdgpu_ip_block *ip_block) case IP_VERSION(11, 5, 4): case IP_VERSION(11, 5, 6): case IP_VERSION(11, 7, 0): + case IP_VERSION(11, 7, 1): adev->gfx.me.num_me = 1; adev->gfx.me.num_pipe_per_me = 1; adev->gfx.me.num_queue_per_pipe = 2; @@ -3092,7 +3098,8 @@ static int gfx_v11_0_wait_for_rlc_autoload_complete(struct amdgpu_device *adev) amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(11, 5, 3) || amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(11, 5, 4) || amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(11, 5, 6) || - amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(11, 7, 0)) + amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(11, 7, 0) || + amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(11, 7, 1)) bootload_status = RREG32_SOC15(GC, 0, regRLC_RLCS_BOOTLOAD_STATUS_gc_11_0_1); else @@ -5766,6 +5773,7 @@ static void gfx_v11_cntl_power_gating(struct amdgpu_device *adev, bool enable) case IP_VERSION(11, 5, 4): case IP_VERSION(11, 5, 6): case IP_VERSION(11, 7, 0): + case IP_VERSION(11, 7, 1): WREG32_SOC15(GC, 0, regRLC_PG_DELAY_3, RLC_PG_DELAY_3_DEFAULT_GC_11_0_1); break; default: @@ -5807,6 +5815,7 @@ static int gfx_v11_0_set_powergating_state(struct amdgpu_ip_block *ip_block, case IP_VERSION(11, 5, 4): case IP_VERSION(11, 5, 6): case IP_VERSION(11, 7, 0): + case IP_VERSION(11, 7, 1): if (!enable) amdgpu_gfx_off_ctrl(adev, false); @@ -5844,6 +5853,7 @@ static int gfx_v11_0_set_clockgating_state(struct amdgpu_ip_block *ip_block, case IP_VERSION(11, 5, 4): case IP_VERSION(11, 5, 6): case IP_VERSION(11, 7, 0): + case IP_VERSION(11, 7, 1): gfx_v11_0_update_gfx_clock_gating(adev, state == AMD_CG_STATE_GATE); break; diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c index 8a0a88551461..c40d9c467204 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c @@ -607,6 +607,7 @@ static void gmc_v11_0_set_gfxhub_funcs(struct amdgpu_device *adev) case IP_VERSION(11, 5, 4): case IP_VERSION(11, 5, 6): case IP_VERSION(11, 7, 0): + case IP_VERSION(11, 7, 1): adev->gfxhub.funcs = &gfxhub_v11_5_0_funcs; break; default: @@ -783,6 +784,7 @@ static int gmc_v11_0_sw_init(struct amdgpu_ip_block *ip_block) case IP_VERSION(11, 5, 4): case IP_VERSION(11, 5, 6): case IP_VERSION(11, 7, 0): + case IP_VERSION(11, 7, 1): set_bit(AMDGPU_GFXHUB(0), adev->vmhubs_mask); set_bit(AMDGPU_MMHUB0(0), adev->vmhubs_mask); /* diff --git a/drivers/gpu/drm/amd/amdgpu/imu_v11_0.c b/drivers/gpu/drm/amd/amdgpu/imu_v11_0.c index 177d702e612a..05b164f38c97 100644 --- a/drivers/gpu/drm/amd/amdgpu/imu_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/imu_v11_0.c @@ -44,6 +44,7 @@ MODULE_FIRMWARE("amdgpu/gc_11_5_3_imu.bin"); MODULE_FIRMWARE("amdgpu/gc_11_5_4_imu.bin"); MODULE_FIRMWARE("amdgpu/gc_11_5_6_imu.bin"); MODULE_FIRMWARE("amdgpu/gc_11_7_0_imu.bin"); +MODULE_FIRMWARE("amdgpu/gc_11_7_1_imu.bin"); static int imu_v11_0_init_microcode(struct amdgpu_device *adev) { diff --git a/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c b/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c index 3ca2ee3e9202..1b071a3de173 100644 --- a/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c @@ -62,6 +62,8 @@ MODULE_FIRMWARE("amdgpu/gc_11_5_6_mes_2.bin"); MODULE_FIRMWARE("amdgpu/gc_11_5_6_mes1.bin"); MODULE_FIRMWARE("amdgpu/gc_11_7_0_mes_2.bin"); MODULE_FIRMWARE("amdgpu/gc_11_7_0_mes1.bin"); +MODULE_FIRMWARE("amdgpu/gc_11_7_1_mes_2.bin"); +MODULE_FIRMWARE("amdgpu/gc_11_7_1_mes1.bin"); static int mes_v11_0_hw_init(struct amdgpu_ip_block *ip_block); static int mes_v11_0_hw_fini(struct amdgpu_ip_block *ip_block); diff --git a/drivers/gpu/drm/amd/amdgpu/psp_v15_0.c b/drivers/gpu/drm/amd/amdgpu/psp_v15_0.c index 2a8582e87f2b..2a4d91368ac6 100644 --- a/drivers/gpu/drm/amd/amdgpu/psp_v15_0.c +++ b/drivers/gpu/drm/amd/amdgpu/psp_v15_0.c @@ -33,6 +33,8 @@ MODULE_FIRMWARE("amdgpu/psp_15_0_0_toc.bin"); MODULE_FIRMWARE("amdgpu/psp_15_0_0_ta.bin"); +MODULE_FIRMWARE("amdgpu/psp_15_0_9_toc.bin"); +MODULE_FIRMWARE("amdgpu/psp_15_0_9_ta.bin"); static int psp_v15_0_0_init_microcode(struct psp_context *psp) { diff --git a/drivers/gpu/drm/amd/amdgpu/soc21.c b/drivers/gpu/drm/amd/amdgpu/soc21.c index 9b9b13e327d8..1677e88a4e36 100644 --- a/drivers/gpu/drm/amd/amdgpu/soc21.c +++ b/drivers/gpu/drm/amd/amdgpu/soc21.c @@ -866,6 +866,34 @@ static int soc21_common_early_init(struct amdgpu_ip_block *ip_block) AMD_PG_SUPPORT_GFX_PG; adev->external_rev_id = adev->rev_id + 0xF; break; + case IP_VERSION(11, 7, 1): + adev->cg_flags = AMD_CG_SUPPORT_VCN_MGCG | + AMD_CG_SUPPORT_JPEG_MGCG | + AMD_CG_SUPPORT_GFX_CGCG | + AMD_CG_SUPPORT_GFX_CGLS | + AMD_CG_SUPPORT_GFX_MGCG | + AMD_CG_SUPPORT_GFX_FGCG | + AMD_CG_SUPPORT_REPEATER_FGCG | + AMD_CG_SUPPORT_GFX_PERF_CLK | + AMD_CG_SUPPORT_GFX_3D_CGCG | + AMD_CG_SUPPORT_GFX_3D_CGLS | + AMD_CG_SUPPORT_MC_MGCG | + AMD_CG_SUPPORT_MC_LS | + AMD_CG_SUPPORT_HDP_LS | + AMD_CG_SUPPORT_HDP_DS | + AMD_CG_SUPPORT_HDP_SD | + AMD_CG_SUPPORT_ATHUB_MGCG | + AMD_CG_SUPPORT_ATHUB_LS | + AMD_CG_SUPPORT_IH_CG | + AMD_CG_SUPPORT_BIF_MGCG | + AMD_CG_SUPPORT_BIF_LS; + adev->pg_flags = AMD_PG_SUPPORT_VCN_DPG | + AMD_PG_SUPPORT_VCN | + AMD_PG_SUPPORT_JPEG_DPG | + AMD_PG_SUPPORT_JPEG | + AMD_PG_SUPPORT_GFX_PG; + adev->external_rev_id = adev->rev_id + 0x40; + break; default: /* FIXME: not supported yet */ return -EINVAL; diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_crat.c b/drivers/gpu/drm/amd/amdkfd/kfd_crat.c index a6a7888c7a8d..2a239f45fc24 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_crat.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_crat.c @@ -1716,6 +1716,7 @@ int kfd_get_gpu_cache_info(struct kfd_node *kdev, struct kfd_gpu_cache_info **pc case IP_VERSION(11, 5, 4): case IP_VERSION(11, 5, 6): case IP_VERSION(11, 7, 0): + case IP_VERSION(11, 7, 1): /* Cacheline size not available in IP discovery for gc11. * kfd_fill_gpu_cache_info_from_gfx_config to hard code it */ diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device.c b/drivers/gpu/drm/amd/amdkfd/kfd_device.c index 47de7702c39e..008a0719fe1f 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device.c @@ -170,6 +170,7 @@ static void kfd_device_info_set_event_interrupt_class(struct kfd_dev *kfd) case IP_VERSION(11, 5, 4): case IP_VERSION(11, 5, 6): case IP_VERSION(11, 7, 0): + case IP_VERSION(11, 7, 1): kfd->device_info.event_interrupt_class = &event_interrupt_class_v11; break; case IP_VERSION(12, 0, 0): @@ -456,6 +457,10 @@ struct kfd_dev *kgd2kfd_probe(struct amdgpu_device *adev, bool vf) gfx_target_version = 110700; f2g = &gfx_v11_kfd2kgd; break; + case IP_VERSION(11, 7, 1): + gfx_target_version = 110701; + f2g = &gfx_v11_kfd2kgd; + break; case IP_VERSION(12, 0, 0): gfx_target_version = 120000; f2g = &gfx_v12_kfd2kgd; From 9c8b85f95c1d4736b967e17b8eb4a463c055bea3 Mon Sep 17 00:00:00 2001 From: David Francis Date: Thu, 25 Jun 2026 10:09:13 -0400 Subject: [PATCH 0350/1054] drm/amdkfd: Use kvcalloc to allocate arrays There were a few instances in kfd_chardev.c of kvzalloc being used to allocate memory for an array. Switch those to kvcalloc, which - is the standard way of allocating a zero-initialized array - does a check for the mul overflowing Signed-off-by: David Francis Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit 60b048c93f7a3add39757ad65fe2bb6e58eeae23) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdkfd/kfd_chardev.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c index 531e20748198..c7edebd2fd8a 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c @@ -1914,13 +1914,13 @@ static int criu_checkpoint_devices(struct kfd_process *p, struct kfd_criu_device_bucket *device_buckets = NULL; int ret = 0, i; - device_buckets = kvzalloc(num_devices * sizeof(*device_buckets), GFP_KERNEL); + device_buckets = kvcalloc(num_devices, sizeof(*device_buckets), GFP_KERNEL); if (!device_buckets) { ret = -ENOMEM; goto exit; } - device_priv = kvzalloc(num_devices * sizeof(*device_priv), GFP_KERNEL); + device_priv = kvcalloc(num_devices, sizeof(*device_priv), GFP_KERNEL); if (!device_priv) { ret = -ENOMEM; goto exit; @@ -2040,17 +2040,17 @@ static int criu_checkpoint_bos(struct kfd_process *p, int ret = 0, pdd_index, bo_index = 0, id; void *mem; - bo_buckets = kvzalloc(num_bos * sizeof(*bo_buckets), GFP_KERNEL); + bo_buckets = kvcalloc(num_bos, sizeof(*bo_buckets), GFP_KERNEL); if (!bo_buckets) return -ENOMEM; - bo_privs = kvzalloc(num_bos * sizeof(*bo_privs), GFP_KERNEL); + bo_privs = kvcalloc(num_bos, sizeof(*bo_privs), GFP_KERNEL); if (!bo_privs) { ret = -ENOMEM; goto exit; } - files = kvzalloc(num_bos * sizeof(struct file *), GFP_KERNEL); + files = kvcalloc(num_bos, sizeof(struct file *), GFP_KERNEL); if (!files) { ret = -ENOMEM; goto exit; @@ -2581,7 +2581,7 @@ static int criu_restore_bos(struct kfd_process *p, if (!bo_buckets) return -ENOMEM; - files = kvzalloc(args->num_bos * sizeof(struct file *), GFP_KERNEL); + files = kvcalloc(args->num_bos, sizeof(struct file *), GFP_KERNEL); if (!files) { ret = -ENOMEM; goto exit; From 8b7033c0c5dcc3b3bd8403453d2793ec4514ae62 Mon Sep 17 00:00:00 2001 From: Donet Tom Date: Thu, 25 Jun 2026 13:22:06 +0530 Subject: [PATCH 0351/1054] drm/amdgpu: Fix AMDGPU_GTT_MAX_TRANSFER_SIZE for non-4K systems Running RCCL unit tests on a system with a 64K PAGE_SIZE triggers the following warning and causes the test to terminate on latest upstream kernel: WARNING: drivers/gpu/drm/amd/amdgpu/amdgpu_object.c:1335 at amdgpu_bo_release_notify+0x1bc/0x280 [amdgpu], CPU#18: rccl-UnitTests/33151 Call trace: amdgpu_bo_release_notify ttm_bo_release amdgpu_gem_object_free drm_gem_object_free amdgpu_bo_unref amdgpu_bo_create amdgpu_bo_create_user amdgpu_gem_object_create amdgpu_amdkfd_gpuvm_alloc_memory_of_gpu kfd_ioctl_alloc_memory_of_gpu kfd_ioctl sys_ioctl The warning is triggered because amdgpu_ttm_next_clear_entity() returns NULL when a clear buffer operation is requested. This happens because the GART window allocation for the default_entity, clear_entity and move_entity fails during initialization. Commit [1] introduced separate GART windows for the default_entity, clear_entity and move_entity of each SDMA instance. Their sizes are derived from AMDGPU_GTT_MAX_TRANSFER_SIZE, which is currently defined as 1024 pages. This implicitly assumes a 4K PAGE_SIZE, where 1024 pages correspond to a 4MB transfer. On a 64K PAGE_SIZE system, however, the same value expands to 64MB. The default_entity and clear_entity each allocate one AMDGPU_GTT_MAX_TRANSFER_SIZE GART window, while the move_entity allocates two such windows. This results in 16MB of GART space per SDMA instance on a 4K PAGE_SIZE system, but 256MB per SDMA instance on a 64K PAGE_SIZE system. On an MI210 system with five SDMA instances and a 512MB GART aperture, the total GART space required becomes 1.25GB, exceeding the available GART aperture. Consequently, GART window allocation fails, amdgpu_ttm_next_clear_entity() returns NULL, and the above warning is triggered. Redefine AMDGPU_GTT_MAX_TRANSFER_SIZE in bytes instead of page units. Where a page count is required, convert it using PAGE_SHIFT. This preserves the existing 4MB transfer size across all PAGE_SIZE configurations while keeping GART window allocations within the available GART aperture. [1] https://lore.kernel.org/all/20260408100327.1372-3-pierre-eric.pelloux-prayer@amd.com/#t Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/5435 Fixes: 897ee11ec020 ("drm/amdgpu: create multiple clear/move ttm entities") Signed-off-by: Donet Tom Signed-off-by: Alex Deucher (cherry picked from commit 27213b776a666d3030de5acc3cd75278197b0494) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c | 10 ++++++---- drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h | 2 +- drivers/gpu/drm/amd/amdkfd/kfd_migrate.c | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c index 00b5317f77f8..025625e7e800 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c @@ -208,9 +208,10 @@ static int amdgpu_ttm_map_buffer(struct amdgpu_ttm_buffer_entity *entity, void *cpu_addr; uint64_t flags; int r; + const u64 GTT_MAX_PAGES = (AMDGPU_GTT_MAX_TRANSFER_SIZE >> PAGE_SHIFT); BUG_ON(adev->mman.buffer_funcs->copy_max_bytes < - AMDGPU_GTT_MAX_TRANSFER_SIZE * 8); + GTT_MAX_PAGES * AMDGPU_GPU_PAGES_IN_CPU_PAGE * 8); if (WARN_ON(mem->mem_type == AMDGPU_PL_PREEMPT)) return -EINVAL; @@ -230,7 +231,7 @@ static int amdgpu_ttm_map_buffer(struct amdgpu_ttm_buffer_entity *entity, offset = mm_cur->start & ~PAGE_MASK; num_pages = PFN_UP(*size + offset); - num_pages = min_t(uint32_t, num_pages, AMDGPU_GTT_MAX_TRANSFER_SIZE); + num_pages = min_t(uint32_t, num_pages, GTT_MAX_PAGES); *size = min(*size, (uint64_t)num_pages * PAGE_SIZE - offset); @@ -2033,6 +2034,7 @@ static int amdgpu_ttm_buffer_entity_init(struct amdgpu_gtt_mgr *mgr, u32 num_gart_windows) { int i, r, num_pages; + const u64 GTT_MAX_PAGES = (AMDGPU_GTT_MAX_TRANSFER_SIZE >> PAGE_SHIFT); r = drm_sched_entity_init(&entity->base, prio, scheds, num_schedulers, NULL); if (r) @@ -2045,7 +2047,7 @@ static int amdgpu_ttm_buffer_entity_init(struct amdgpu_gtt_mgr *mgr, if (num_gart_windows == 0) return 0; - num_pages = num_gart_windows * AMDGPU_GTT_MAX_TRANSFER_SIZE; + num_pages = num_gart_windows * GTT_MAX_PAGES; r = amdgpu_gtt_mgr_alloc_entries(mgr, &entity->gart_node, num_pages, DRM_MM_INSERT_BEST); if (r) { @@ -2056,7 +2058,7 @@ static int amdgpu_ttm_buffer_entity_init(struct amdgpu_gtt_mgr *mgr, for (i = 0; i < num_gart_windows; i++) { entity->gart_window_offs[i] = amdgpu_gtt_node_to_byte_offset(&entity->gart_node) + - i * AMDGPU_GTT_MAX_TRANSFER_SIZE * PAGE_SIZE; + i * GTT_MAX_PAGES * PAGE_SIZE; } return 0; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h index 2d72fa217274..b5d938b31383 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h @@ -39,7 +39,7 @@ #define AMDGPU_PL_MMIO_REMAP (TTM_PL_PRIV + 5) #define __AMDGPU_PL_NUM (TTM_PL_PRIV + 6) -#define AMDGPU_GTT_MAX_TRANSFER_SIZE 1024 +#define AMDGPU_GTT_MAX_TRANSFER_SIZE (1ULL << 22) extern const struct attribute_group amdgpu_vram_mgr_attr_group; extern const struct attribute_group amdgpu_gtt_mgr_attr_group; diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c b/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c index 226e76ae0be7..7cd236c1ff75 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c @@ -128,7 +128,7 @@ svm_migrate_copy_memory_gart(struct amdgpu_device *adev, dma_addr_t *sys, enum MIGRATION_COPY_DIR direction, struct dma_fence **mfence) { - const u64 GTT_MAX_PAGES = AMDGPU_GTT_MAX_TRANSFER_SIZE; + const u64 GTT_MAX_PAGES = (AMDGPU_GTT_MAX_TRANSFER_SIZE >> PAGE_SHIFT); struct amdgpu_ring *ring; struct amdgpu_ttm_buffer_entity *entity; u64 gart_s, gart_d; From 67a654b41cfa73c3b83402c4a01b2689cad5b9bc Mon Sep 17 00:00:00 2001 From: Perry Yuan Date: Thu, 25 Jun 2026 13:57:56 +0800 Subject: [PATCH 0352/1054] drm/amdgpu: flush pending RCU callbacks on module unload MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Call rcu_barrier() in module exit to wait for outstanding call_rcu() callbacks before freeing module text, preventing late callback execution in freed memory. BUG: unable to handle page fault for address: ffffffffc1d59c40 PGD 6a12067 P4D 6a12067 PUD 6a14067 PMD 13698b067 PTE 0 Oops: 0010 [#1] SMP NOPTI RIP: 0010:0xffffffffc1d59c40 Code: Unable to access opcode bytes at RIP 0xffffffffc1d59c16. RSP: 0018:ffffc900198c0f28 EFLAGS: 00010286 RAX: ffffffffc1d59c40 RBX: ffff897c7d6b61c0 RCX: ffff88826aff4590 RDX: ffff8884d8b35490 RSI: ffffc900198c0f30 RDI: ffff88812af67290 RBP: 000000000000000a (DONE segment entries) R08: 0000000000000000 R09: 0000000000000100 R10: 0000000000000000 R11: ffffffff82a06100 R12: ffff88811a4e3700 R13: 0000000000000000 R14: ffff897c7d6b6270 R15: 0000000000000000 FS: 0000000000000000(0000) GS:ffff897c7d680000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: ffffffffc1d59c16 CR3: 00000104a980a001 CR4: 0000000002770ee0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000fffe07f0 DR7: 0000000000000400 PKRU: 55555554 Call Trace: ? rcu_do_batch+0x163/0x450 ? rcu_core+0x177/0x1c0 ? __do_softirq+0xc1/0x280 ? asm_call_irq_on_stack+0xf/0x20 ? do_softirq_own_stack+0x37/0x50 ? irq_exit_rcu+0xc4/0x100 ? sysvec_apic_timer_interrupt+0x36/0x80 ? asm_sysvec_apic_timer_interrupt+0x12/0x20 ? cpuidle_enter_state+0xd4/0x360 ? cpuidle_enter+0x29/0x40 ? cpuidle_idle_call+0x108/0x1a0 ? do_idle+0x77/0xf0 ? cpu_startup_entry+0x19/0x20 ? secondary_startup_64_no_verify+0xbf/0xcb Signed-off-by: Perry Yuan Reviewed-by: Yifan Zhang Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit feaa5039f6c12acc9aa934c2d45dcd251a12c69f) --- drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c index bf4260269681..4c0c77eafbd1 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c @@ -3196,6 +3196,14 @@ static void __exit amdgpu_exit(void) amdgpu_sync_fini(); mmu_notifier_synchronize(); amdgpu_xcp_drv_release(); + + /* + * Flush outstanding call_rcu() callbacks before the + * module text is freed. Otherwise a grace period elapsing after + * unload invokes a callback in already-freed module memory and + * faults in rcu_do_batch(). + */ + rcu_barrier(); } module_init(amdgpu_init); From 220f22e1d66c1cfb63387eb1c4210f92a357c2d9 Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Wed, 1 Jul 2026 09:11:15 +0800 Subject: [PATCH 0353/1054] drm/amd/pm: fix smu13 power limit range calculation SMU13 reports SocketPowerLimitAc/Dc as the default power limit, but MsgLimits.Power may carry a different firmware bound for the same PPT throttler. Using only the socket limit for both min and max can therefore expose an incorrect power range. Keep the socket limit as the default, but derive the range from both values: use the lower value for the min base and the higher value for the max base before applying OD percentages. Keep the current limit query independent from the cap calculation. Fixes: 1eaf26db9590 ("drm/amd/pm: fix smu13 power limit default/cap calculation") Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/5419 Signed-off-by: Yang Wang Reviewed-by: Kenneth Feng Signed-off-by: Alex Deucher (cherry picked from commit f45bbf0f62f266ed8422d84f347d75d5fca846a7) Cc: stable@vger.kernel.org --- .../gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c | 11 +++++++---- .../gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c | 15 ++++++++------- 2 files changed, 15 insertions(+), 11 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 7f8d4bb47d02..acbd7046d8a5 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 @@ -2403,11 +2403,14 @@ static int smu_v13_0_0_get_power_limit(struct smu_context *smu, 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; + uint32_t msg_limit = skutable->MsgLimits.Power[PPT_THROTTLER_PPT0][POWER_SOURCE_AC]; + uint32_t min_limit = min_t(uint32_t, pp_limit, msg_limit); + uint32_t max_limit = max_t(uint32_t, pp_limit, msg_limit); + uint32_t 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); + ret = smu_v13_0_get_current_power_limit(smu, current_power_limit); if (ret) *current_power_limit = pp_limit; } @@ -2430,12 +2433,12 @@ static int smu_v13_0_0_get_power_limit(struct smu_context *smu, od_percent_upper, od_percent_lower, pp_limit); if (max_power_limit) { - *max_power_limit = pp_limit * (100 + od_percent_upper); + *max_power_limit = max_limit * (100 + od_percent_upper); *max_power_limit /= 100; } if (min_power_limit) { - *min_power_limit = pp_limit * (100 - od_percent_lower); + *min_power_limit = min_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 0f774b0920ce..42c9ceeb4f7d 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 @@ -2385,15 +2385,16 @@ static int smu_v13_0_7_get_power_limit(struct smu_context *smu, 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; + uint32_t msg_limit = skutable->MsgLimits.Power[PPT_THROTTLER_PPT0][POWER_SOURCE_AC]; + uint32_t min_limit = min_t(uint32_t, pp_limit, msg_limit); + uint32_t max_limit = max_t(uint32_t, pp_limit, msg_limit); + uint32_t 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); + ret = smu_v13_0_get_current_power_limit(smu, current_power_limit); if (ret) - power_limit = pp_limit; - - *current_power_limit = power_limit; + *current_power_limit = pp_limit; } if (default_power_limit) @@ -2414,12 +2415,12 @@ static int smu_v13_0_7_get_power_limit(struct smu_context *smu, od_percent_upper, od_percent_lower, pp_limit); if (max_power_limit) { - *max_power_limit = pp_limit * (100 + od_percent_upper); + *max_power_limit = max_limit * (100 + od_percent_upper); *max_power_limit /= 100; } if (min_power_limit) { - *min_power_limit = pp_limit * (100 - od_percent_lower); + *min_power_limit = min_limit * (100 - od_percent_lower); *min_power_limit /= 100; } From a6e14b976be48eebd8769cb5b883a6af7fc5ade1 Mon Sep 17 00:00:00 2001 From: WenTao Liang Date: Fri, 26 Jun 2026 20:45:55 +0800 Subject: [PATCH 0354/1054] drm/amd/display: detect_link_and_local_sink: DP alt mode timeout path leaks prev_sink reference prev_sink is unconditionally retained via dc_sink_retain at function entry, but the DP alt mode timeout path inside SIGNAL_TYPE_DISPLAY_PORT returns false without releasing prev_sink. All other return paths in the function correctly call dc_sink_release(prev_sink), making this the only missing cleanup. Fixes: 54618888d1ea ("drm/amd/display: break down dc_link.c") Signed-off-by: WenTao Liang Reviewed-by: Mario Limonciello (AMD) Link: https://patch.msgid.link/20260626124555.36910-1-vulab@iscas.ac.cn Signed-off-by: Mario Limonciello Signed-off-by: Alex Deucher (cherry picked from commit 45510cf662dcf46b5d8926d454f338809f107b9d) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/display/dc/link/link_detection.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/dc/link/link_detection.c b/drivers/gpu/drm/amd/display/dc/link/link_detection.c index a3212fd151d1..7d8951fecd57 100644 --- a/drivers/gpu/drm/amd/display/dc/link/link_detection.c +++ b/drivers/gpu/drm/amd/display/dc/link/link_detection.c @@ -1164,8 +1164,11 @@ static bool detect_link_and_local_sink(struct dc_link *link, link->link_enc->features.flags.bits.DP_IS_USB_C == 1) { /* if alt mode times out, return false */ - if (!wait_for_entering_dp_alt_mode(link)) + if (!wait_for_entering_dp_alt_mode(link)) { + if (prev_sink) + dc_sink_release(prev_sink); return false; + } } if (!detect_dp(link, &sink_caps, reason)) { From a279bd143b3c184358b658e43a057e31ee8c4de5 Mon Sep 17 00:00:00 2001 From: Harish Kasiviswanathan Date: Fri, 26 Jun 2026 12:21:54 -0400 Subject: [PATCH 0355/1054] drm/amdgpu: Fix kernel panic during driver load failure Avoid kernel panic if MES init fails during driver load. The KIQ ring is falsely marked as ready as ASICs that use MES, KIQ is owned by MES. BUG: kernel NULL pointer dereference, address: 0000000000000000 RIP: 0010:gfx_v12_1_wait_reg_mem+0x5a/0x1f0 [amdgpu] Call Trace: gfx_v12_1_ring_emit_reg_write_reg_wait+0x1f/0x30 [amdgpu] amdgpu_gmc_fw_reg_write_reg_wait+0xb2/0x190 [amdgpu] amdgpu_gmc_flush_gpu_tlb+0x1cc/0x230 [amdgpu] amdgpu_gart_invalidate_tlb+0x81/0xa0 [amdgpu] amdgpu_gart_unbind+0x72/0x90 [amdgpu] amdgpu_ttm_backend_unbind+0xa4/0xb0 [amdgpu] amdgpu_ttm_tt_unpopulate+0x13/0xd0 [amdgpu] amdttm_tt_unpopulate+0x29/0x70 [amdttm] ttm_bo_put+0x1eb/0x360 [amdttm] amdgpu_bo_free_kernel+0xf9/0x1f0 [amdgpu] amdgpu_ih_ring_fini+0x5a/0x90 [amdgpu] amdgpu_irq_fini_hw+0x58/0x80 [amdgpu] amdgpu_device_fini_hw+0x4e0/0x5b0 [amdgpu] amdgpu_driver_load_kms+0x60/0xa0 [amdgpu] amdgpu_pci_probe+0x28e/0x6d0 [amdgpu] pci_device_probe+0x19f/0x220 really_probe+0x1ed/0x340 driver_probe_device+0x1e/0x80 __driver_attach+0xd3/0x1a0 bus_for_each_dev+0x68/0xa0 bus_add_driver+0x19f/0x270 driver_register+0x5d/0xf0 do_one_initcall+0xac/0x200 do_init_module+0x1ec/0x280 __se_sys_finit_module+0x2de/0x310 do_syscall_64+0x6a/0x250 entry_SYSCALL_64_after_hwframe+0x4b/0x53 Signed-off-by: Harish Kasiviswanathan Reviewed-by: Kent Russell Signed-off-by: Alex Deucher (cherry picked from commit 4623b958dd6da0f4c3026afdf330626a09ecb0f0) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c | 13 +++++++++++-- drivers/gpu/drm/amd/amdgpu/gfx_v12_1.c | 13 +++++++++++-- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c index 3f3b1754c038..da668a8d6abd 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c @@ -3519,10 +3519,19 @@ static int gfx_v12_0_cp_resume(struct amdgpu_device *adev) gfx_v12_0_cp_gfx_enable(adev, true); } - if (adev->enable_mes_kiq && adev->mes.kiq_hw_init) + if (adev->enable_mes_kiq && adev->mes.kiq_hw_init) { r = amdgpu_mes_kiq_hw_init(adev, 0); - else + /* + * With MES, GFX KIQ ring is owned by the MES and is never + * initialized/used directly by the driver, so it must + * not be left flagged as ready. mes_v12_0_hw_init() clears + * but clear here if MES init fails + */ + if (r) + adev->gfx.kiq[0].ring.sched.ready = false; + } else { r = gfx_v12_0_kiq_resume(adev); + } if (r) return r; diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v12_1.c b/drivers/gpu/drm/amd/amdgpu/gfx_v12_1.c index 02c9cda186ee..e7e9f11b9754 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v12_1.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v12_1.c @@ -2547,10 +2547,19 @@ static int gfx_v12_1_xcc_cp_resume(struct amdgpu_device *adev, uint16_t xcc_mask gfx_v12_1_xcc_cp_compute_enable(adev, true, xcc_id); - if (adev->enable_mes_kiq && adev->mes.kiq_hw_init) + if (adev->enable_mes_kiq && adev->mes.kiq_hw_init) { r = amdgpu_mes_kiq_hw_init(adev, xcc_id); - else + /* + * With MES, GFX KIQ ring is owned by the MES and is never + * initialized/used directly by the driver, so it must + * not be left flagged as ready. mes_v12_0_hw_init() clears + * but clear here if MES init fails + */ + if (r) + adev->gfx.kiq[xcc_id].ring.sched.ready = false; + } else { r = gfx_v12_1_xcc_kiq_resume(adev, xcc_id); + } if (r) return r; From c44af3810fc8b3adf6910a332038aa566560c8fa Mon Sep 17 00:00:00 2001 From: Boyuan Zhang Date: Fri, 26 Jun 2026 10:39:26 -0400 Subject: [PATCH 0356/1054] drm/amdgpu/jpeg: fix jpeg_v4_0_3_is_idle detection jpeg_v4_0_3_is_idle() initializes ret to false and then accumulates ring idle status using &=. Since false & condition always remains false, the function can never report the JPEG block as idle. Initialize ret to true so the function returns true only when all JPEG rings report RB_JOB_DONE. Signed-off-by: Boyuan Zhang Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit e9df8e9d04e0593d17ddb069f3b7958991cd18c9) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_3.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_3.c index 0c746580de11..d8204fbc198d 100644 --- a/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_3.c +++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_3.c @@ -1010,7 +1010,7 @@ void jpeg_v4_0_3_dec_ring_nop(struct amdgpu_ring *ring, uint32_t count) static bool jpeg_v4_0_3_is_idle(struct amdgpu_ip_block *ip_block) { struct amdgpu_device *adev = ip_block->adev; - bool ret = false; + bool ret = true; int i, j; for (i = 0; i < adev->jpeg.num_jpeg_inst; ++i) { From 8813b0612275cc61fe9e6603d0ee019247ade6be Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Thu, 2 Jul 2026 00:13:19 +0700 Subject: [PATCH 0357/1054] HID: multitouch: fix out-of-bounds bit access on mt_io_flags mt_io_flags is a single unsigned long, but mt_process_slot(), mt_release_pending_palms() and mt_release_contacts() use it as a per-slot bitmap indexed by the slot number. That slot number is only bounded by td->maxcontacts, which is taken from the device's ContactCountMaximum feature report and can be up to 255, not by BITS_PER_LONG. As a result, a multitouch device that advertises a large contact count makes set_bit()/clear_bit() operate past the mt_io_flags word and corrupt the adjacent members of struct mt_device. The sticky-fingers release timer is the easiest way to reach this. mt_release_contacts() runs for (i = 0; i < mt->num_slots; i++) clear_bit(i, &td->mt_io_flags); with num_slots == maxcontacts. For maxcontacts around 250 the loop clears the bits that overlap td->applications.next, zeroing that list head, and the list_for_each_entry() that immediately follows then dereferences NULL. The kernel panics from timer (softirq) context. On a KASAN build this shows up as a general protection fault in mt_release_contacts() with a null-ptr-deref at offset 0x58, which is offsetof(struct mt_application, num_received). The state is reachable from an untrusted USB or Bluetooth HID multitouch device; no local privileges are required. Store the per-slot active state in a separately allocated bitmap sized for maxcontacts, the same pattern already used for pending_palm_slots, and keep only MT_IO_FLAGS_RUNNING in mt_io_flags. The two "mt_io_flags & MT_IO_SLOTS_MASK" arming checks become bitmap_empty(td->active_slots, td->maxcontacts). Move MT_IO_FLAGS_RUNNING back to bit 0. It was bumped to bit 32 by the same commit to leave the low byte for the slot bits; with the slot bits gone it fits in bit 0 again, which also keeps it within the unsigned long on 32-bit. Fixes: 46f781e0d151 ("HID: multitouch: fix sticky fingers") Cc: stable@vger.kernel.org Signed-off-by: Trung Nguyen Signed-off-by: Benjamin Tissoires --- drivers/hid/hid-multitouch.c | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 0495152091e3..edb37b4c867e 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -31,6 +31,7 @@ * [1] https://gitlab.freedesktop.org/libevdev/hid-tools */ +#include #include #include #include @@ -97,8 +98,7 @@ enum report_mode { TOUCHPAD_REPORT_ALL = TOUCHPAD_REPORT_BUTTONS | TOUCHPAD_REPORT_CONTACTS, }; -#define MT_IO_SLOTS_MASK GENMASK(7, 0) /* reserve first 8 bits for slot tracking */ -#define MT_IO_FLAGS_RUNNING 32 +#define MT_IO_FLAGS_RUNNING 0 static const bool mtrue = true; /* default for true */ static const bool mfalse; /* default for false */ @@ -174,10 +174,9 @@ struct mt_device { struct timer_list release_timer; /* to release sticky fingers */ struct hid_haptic_device *haptic; /* haptic related configuration */ struct hid_device *hdev; /* hid_device we're attached to */ - unsigned long mt_io_flags; /* mt flags (MT_IO_FLAGS_RUNNING) - * first 8 bits are reserved for keeping the slot - * states, this is fine because we only support up - * to 250 slots (MT_MAX_MAXCONTACT) + unsigned long mt_io_flags; /* mt flags (MT_IO_FLAGS_RUNNING) */ + unsigned long *active_slots; /* bitmap of slots with an active + * contact, sized for maxcontacts */ __u8 inputmode_value; /* InputMode HID feature value */ __u8 maxcontacts; @@ -1036,7 +1035,7 @@ static void mt_release_pending_palms(struct mt_device *td, for_each_set_bit(slotnum, app->pending_palm_slots, td->maxcontacts) { clear_bit(slotnum, app->pending_palm_slots); - clear_bit(slotnum, &td->mt_io_flags); + clear_bit(slotnum, td->active_slots); input_mt_slot(input, slotnum); input_mt_report_slot_inactive(input); @@ -1247,9 +1246,9 @@ static int mt_process_slot(struct mt_device *td, struct input_dev *input, input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, major); input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, minor); - set_bit(slotnum, &td->mt_io_flags); + set_bit(slotnum, td->active_slots); } else { - clear_bit(slotnum, &td->mt_io_flags); + clear_bit(slotnum, td->active_slots); } return 0; @@ -1384,7 +1383,7 @@ static void mt_touch_report(struct hid_device *hid, * defect. */ if (app->quirks & MT_QUIRK_STICKY_FINGERS) { - if (td->mt_io_flags & MT_IO_SLOTS_MASK) + if (!bitmap_empty(td->active_slots, td->maxcontacts)) mod_timer(&td->release_timer, jiffies + msecs_to_jiffies(100)); else @@ -1443,6 +1442,15 @@ static int mt_touch_input_configured(struct hid_device *hdev, if (td->is_pressurepad) __set_bit(INPUT_PROP_PRESSUREPAD, input->propbit); + if (!td->active_slots) { + td->active_slots = devm_kcalloc(&td->hdev->dev, + BITS_TO_LONGS(td->maxcontacts), + sizeof(long), + GFP_KERNEL); + if (!td->active_slots) + return -ENOMEM; + } + app->pending_palm_slots = devm_kcalloc(&hi->input->dev, BITS_TO_LONGS(td->maxcontacts), sizeof(long), @@ -2062,7 +2070,7 @@ static void mt_release_contacts(struct hid_device *hid) for (i = 0; i < mt->num_slots; i++) { input_mt_slot(input_dev, i); input_mt_report_slot_inactive(input_dev); - clear_bit(i, &td->mt_io_flags); + clear_bit(i, td->active_slots); } input_mt_sync_frame(input_dev); input_sync(input_dev); @@ -2085,7 +2093,7 @@ static void mt_expired_timeout(struct timer_list *t) */ if (test_and_set_bit_lock(MT_IO_FLAGS_RUNNING, &td->mt_io_flags)) return; - if (td->mt_io_flags & MT_IO_SLOTS_MASK) + if (!bitmap_empty(td->active_slots, td->maxcontacts)) mt_release_contacts(hdev); clear_bit_unlock(MT_IO_FLAGS_RUNNING, &td->mt_io_flags); } From b6eb022890c78285f55381589c1536bd66b8eaeb Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Thu, 2 Jul 2026 00:13:20 +0700 Subject: [PATCH 0358/1054] selftests/hid: multitouch: test a large ContactCountMaximum Add a regression test for the out-of-bounds bit operations on struct mt_device.mt_io_flags. A HID multitouch device can advertise a ContactCountMaximum far larger than the number of contacts a single report describes, up to 255. The driver used to keep the per-slot active state in the bits of a single unsigned long and index set_bit()/clear_bit() by the slot number, so such a device drove those operations out of bounds. The sticky-fingers release timer made it fatal: mt_release_contacts() cleared one bit per slot and overwrote the adjacent members of struct mt_device. The new device advertises a ContactCountMaximum of 250 while exposing only a few finger collections (a large contact count cannot be expressed with one finger collection per contact within the HID descriptor size limit). The test sends a single contact and lets the 100ms sticky-fingers timer release it. A kernel without the fix panics in mt_release_contacts(); a fixed kernel reports the release cleanly. Signed-off-by: Trung Nguyen Signed-off-by: Benjamin Tissoires --- .../selftests/hid/tests/test_multitouch.py | 114 ++++++++++++++++++ 1 file changed, 114 insertions(+) diff --git a/tools/testing/selftests/hid/tests/test_multitouch.py b/tools/testing/selftests/hid/tests/test_multitouch.py index fa4fb2054bd4..7897340118b4 100644 --- a/tools/testing/selftests/hid/tests/test_multitouch.py +++ b/tools/testing/selftests/hid/tests/test_multitouch.py @@ -513,6 +513,79 @@ class SmartTechDigitizer(Digitizer): return absinfo is not None and absinfo.resolution == 3 +class MinWin8TSParallelBigContactMax(Digitizer): + """A parallel Win8 touchscreen that advertises a ContactCountMaximum much + larger than the number of contacts it actually reports. + + Such firmware makes the driver allocate that many input slots (up to 255) + while the input report only carries a few contacts. This is what used to + drive the per-slot bit operations on mt_io_flags out of bounds. The number + of contacts a HID report can describe is limited by the descriptor size, + so a large ContactCountMaximum can only be expressed this way, decoupled + from the number of finger collections.""" + + def __init__(self, n_fingers=5, contact_max=250): + self.phys_max = 120, 90 + rdesc_finger_str = f""" + Usage Page (Digitizers) + Usage (Finger) + Collection (Logical) + Report Size (1) + Report Count (1) + Logical Minimum (0) + Logical Maximum (1) + Usage (Tip Switch) + Input (Data,Var,Abs) + Report Size (7) + Logical Maximum (127) + Input (Cnst,Var,Abs) + Report Size (8) + Logical Maximum (255) + Usage (Contact Id) + Input (Data,Var,Abs) + Report Size (16) + Unit Exponent (-1) + Unit (SILinear: cm) + Logical Maximum (4095) + Physical Minimum (0) + Physical Maximum ({self.phys_max[0]}) + Usage Page (Generic Desktop) + Usage (X) + Input (Data,Var,Abs) + Physical Maximum ({self.phys_max[1]}) + Usage (Y) + Input (Data,Var,Abs) + End Collection +""" + rdesc_str = f""" + Usage Page (Digitizers) + Usage (Touch Screen) + Collection (Application) + Report ID (1) + {rdesc_finger_str * n_fingers} + Unit Exponent (-4) + Unit (SILinear: s) + Logical Maximum (65535) + Physical Maximum (65535) + Usage Page (Digitizers) + Usage (Scan Time) + Input (Data,Var,Abs) + Report Size (8) + Logical Maximum (255) + Usage (Contact Count) + Input (Data,Var,Abs) + Report ID (2) + Logical Maximum ({contact_max}) + Usage (Contact Max) + Feature (Data,Var,Abs) + End Collection + {Digitizer.msCertificationBlob(68)} +""" + super().__init__( + f"uhid test parallel big contact max {contact_max}", rdesc_str + ) + + class BaseTest: class TestMultitouch(base.BaseTestCase.TestUhid): kernel_modules = [KERNEL_MODULE] @@ -1735,6 +1808,47 @@ class TestMinWin8TSParallel(BaseTest.TestWin8Multitouch): return MinWin8TSParallel(10) +class TestMinWin8TSParallelBigContactMax(base.BaseTestCase.TestUhid): + """Regression test for the out-of-bounds bit operations on + struct mt_device.mt_io_flags. + + A Win8 touchscreen may advertise a ContactCountMaximum much larger than + the number of contacts it reports. The driver used to keep the per-slot + active state in the bits of a single unsigned long while indexing + set_bit()/clear_bit() by the slot number, so such a device drove those bit + operations out of bounds. The sticky-fingers release timer made it fatal: + mt_release_contacts() cleared one bit per slot, overwrote the adjacent + struct mt_device members and panicked the kernel. + + Send a single contact, let the 100ms sticky-fingers timer release it, and + check that the kernel reports the release cleanly instead of crashing.""" + + kernel_modules = [KERNEL_MODULE] + + def create_device(self): + return MinWin8TSParallelBigContactMax() + + def test_sticky_fingers_release_big_contact_max(self): + uhdev = self.uhdev + evdev = uhdev.get_evdev() + + assert evdev.num_slots == uhdev.max_contacts + + t0 = Touch(1, 5, 10) + r = uhdev.event([t0]) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 0 + + # do not release the contact; the sticky-fingers timer must do it + # after 100ms, which is where the out-of-bounds release used to hit + time.sleep(0.2) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 0) in events + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1 + + class TestMinWin8TSHybrid(BaseTest.TestWin8Multitouch): def create_device(self): return MinWin8TSHybrid() From 12272cb1b23e3032e5c627fb52f183a61913a88b Mon Sep 17 00:00:00 2001 From: Sen Wang Date: Tue, 30 Jun 2026 13:31:20 -0500 Subject: [PATCH 0359/1054] ASoC: codecs: tas675x: use READ_ONCE for params to be used concurrently active_playback_dais and active_capture_dais are written atomically via set_bit()/clear_bit() and can be read concurrently from the fault_check_work delayed work handler. fault_check_work already uses READ_ONCE; extend the same guard to all other reads in tas675x_hw_params() and tas675x_mute_stream(). Fixes: 133c81f84471 ("ASoC: codecs: Add TAS67524 quad-channel audio amplifier driver") Signed-off-by: Sen Wang Link: https://patch.msgid.link/20260630183126.2588322-2-sen@ti.com Signed-off-by: Mark Brown --- sound/soc/codecs/tas675x.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/tas675x.c b/sound/soc/codecs/tas675x.c index 6f89a422f3c6..f1d2bbc85008 100644 --- a/sound/soc/codecs/tas675x.c +++ b/sound/soc/codecs/tas675x.c @@ -1133,7 +1133,7 @@ static int tas675x_hw_params(struct snd_pcm_substream *substream, * Single clock domain: SDIN and SDOUT share one SCLK/FSYNC pair, * so all active DAIs must use the same sample rate. */ - if ((tas->active_playback_dais || tas->active_capture_dais) && + if ((READ_ONCE(tas->active_playback_dais) || READ_ONCE(tas->active_capture_dais)) && tas->rate && tas->rate != rate) { dev_err(component->dev, "Rate %u conflicts with active rate %u\n", @@ -1397,14 +1397,14 @@ static int tas675x_mute_stream(struct snd_soc_dai *dai, int mute, int direction) set_bit(dai->id, &tas->active_playback_dais); /* Last playback stream */ - if (mute && !tas->active_playback_dais) { + if (mute && !READ_ONCE(tas->active_playback_dais)) { ret = tas675x_set_state_all(tas, TAS675X_STATE_SLEEP_BOTH); regmap_read(tas->regmap, TAS675X_CLK_FAULT_LATCHED_REG, &discard); return ret; } return tas675x_set_state_all(tas, - tas->active_playback_dais ? + READ_ONCE(tas->active_playback_dais) ? TAS675X_STATE_PLAY_BOTH : TAS675X_STATE_SLEEP_BOTH); } From a044f99d000dca7e1d3e8fc847d9ad60467b6793 Mon Sep 17 00:00:00 2001 From: Sen Wang Date: Tue, 30 Jun 2026 13:31:21 -0500 Subject: [PATCH 0360/1054] ASoC: codecs: tas675x: Fix CHx temperature range register bit fields The initial merged patch mixed up the bits for temp reg with LDG report, now fixing to the right bits according to TRM (SLOU589A). Fixes: 133c81f84471 ("ASoC: codecs: Add TAS67524 quad-channel audio amplifier driver") Signed-off-by: Sen Wang Link: https://patch.msgid.link/20260630183126.2588322-3-sen@ti.com Signed-off-by: Mark Brown --- sound/soc/codecs/tas675x.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/tas675x.c b/sound/soc/codecs/tas675x.c index f1d2bbc85008..82526362de7b 100644 --- a/sound/soc/codecs/tas675x.c +++ b/sound/soc/codecs/tas675x.c @@ -954,10 +954,10 @@ static const struct snd_kcontrol_new tas675x_snd_controls[] = { /* Temperature and Voltage Monitoring */ SOC_SINGLE_RO("PVDD Sense", TAS675X_PVDD_SENSE_REG, 0, 0xFF), SOC_SINGLE_RO("Global Temperature", TAS675X_TEMP_GLOBAL_REG, 0, 0xFF), - SOC_SINGLE_RO("CH1 Temperature Range", TAS675X_TEMP_CH1_CH2_REG, 6, 3), - SOC_SINGLE_RO("CH2 Temperature Range", TAS675X_TEMP_CH1_CH2_REG, 4, 3), - SOC_SINGLE_RO("CH3 Temperature Range", TAS675X_TEMP_CH3_CH4_REG, 2, 3), - SOC_SINGLE_RO("CH4 Temperature Range", TAS675X_TEMP_CH3_CH4_REG, 0, 3), + SOC_SINGLE_RO("CH1 Temperature Range", TAS675X_TEMP_CH1_CH2_REG, 0, 7), + SOC_SINGLE_RO("CH2 Temperature Range", TAS675X_TEMP_CH1_CH2_REG, 3, 7), + SOC_SINGLE_RO("CH3 Temperature Range", TAS675X_TEMP_CH3_CH4_REG, 0, 7), + SOC_SINGLE_RO("CH4 Temperature Range", TAS675X_TEMP_CH3_CH4_REG, 3, 7), /* Speaker Protection & Detection */ SOC_SINGLE("Tweeter Detection Switch", TAS675X_TWEETER_DETECT_CTRL_REG, 0, 1, 1), From c34a4be8b846c7a220fe56442ecca27f6ab91943 Mon Sep 17 00:00:00 2001 From: Sen Wang Date: Tue, 30 Jun 2026 13:31:22 -0500 Subject: [PATCH 0361/1054] Documentation: sound: tas675x: Fix temperature range and impedance documentation Two corrections against the TRM (SLOU589A): - Corrected channel temperature range - Corrected conversion formula for global temperature Fixes: ba46edca354e ("Documentation: sound: Add TAS675x codec mixer controls documentation") Signed-off-by: Sen Wang Link: https://patch.msgid.link/20260630183126.2588322-4-sen@ti.com Signed-off-by: Mark Brown --- Documentation/sound/codecs/tas675x.rst | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Documentation/sound/codecs/tas675x.rst b/Documentation/sound/codecs/tas675x.rst index c08b0e392306..2d9e009b46b1 100644 --- a/Documentation/sound/codecs/tas675x.rst +++ b/Documentation/sound/codecs/tas675x.rst @@ -281,7 +281,7 @@ Global Temperature :Description: Global die temperature sense register. :Type: Integer (read-only) :Range: 0 to 255 -:Conversion: (value × 0.5 °C) − 50 °C +:Conversion: value × 2.19 K; subtract 273.15 for °C :Register: 0x75 CHx Temperature Range @@ -289,10 +289,11 @@ CHx Temperature Range :Description: Per-channel coarse temperature range indicator (x = 1, 2, 3, 4). :Type: Integer (read-only) -:Range: 0 to 3 -:Mapping: 0 = <80 °C, 1 = 80–100 °C, 2 = 100–120 °C, 3 = >120 °C -:Register: 0xBB bits [7:6] (CH1), bits [5:4] (CH2), - 0xBC bits [3:2] (CH3), bits [1:0] (CH4) +:Range: 0 to 7 +:Mapping: 0 = <95 °C, 1 = 95–110 °C, 2 = 110–125 °C, 3 = 125–135 °C, + 4 = 135–145 °C, 5 = 145–155 °C, 6 = 155–165 °C, 7 = >165 °C +:Register: 0xBB bits [2:0] (CH1), bits [5:3] (CH2), + 0xBC bits [2:0] (CH3), bits [5:3] (CH4) Load Diagnostics ================ From e23fafb8594ea886ee03e005cc32dfda24f417cf Mon Sep 17 00:00:00 2001 From: Gustavo Sousa Date: Mon, 1 Jun 2026 13:09:47 -0700 Subject: [PATCH 0362/1054] drm/xe/rtp: Add struct types for RTP tables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We currently have a mixture of styles for our RTP tables with respect of how we define the number of entries: * xe_rtp_process_to_sr() expects to receive the number of entries as arguments; * xe_rtp_process() expects the array to have a sentinel at the end of the array; * in xe_rtp_test.c, even though xe_rtp_process_to_sr() does not require a sentinel value, we need to rely on that technique to be able to count xe_rtp_entry_sr entries because simply using ARRAY_SIZE() is not possible. The style used by xe_rtp_process_to_sr() makes it hard to share the tables with other compilation units (e.g. kunit tests), since the number of entries is calculated with ARRAY_SIZE(), which is done at compile time. Since we use the size of the tables to create some bitmasks, using a sentinel style doesn't seem great either. A way to reconcile things into a single style is to have a struct type that would hold the entries array and the number of entries. Since we have xe_rtp_entry and xe_rtp_entry_sr, we would have one type for each. The advantage of the proposed approach is that now we have a nice way to share the tables directly to kunit tests with information about their size. v6: - Removed sentinels that are not needed v5: - Removed added code from conflict resolution issues v4: - Removed conflicts with main branch v3: - No changes v2: - Add compatibility with new xe_rtp_table_sr format for "bad-mcr-reg-forced-to-regular" and "bad-regular-reg-forced-to-mcr" Fixes: 828a8eaf37c3 ("drm/xe/oa: Add MMIO trigger support") Cc: stable@vger.kernel.org # v6.12+ Reviewed-by: Matt Roper Signed-off-by: Gustavo Sousa Signed-off-by: Violet Monti Link: https://patch.msgid.link/20260601200947.2032784-7-violet.monti@intel.com Signed-off-by: Matt Roper (cherry picked from commit 5ff004fdc7377905f2fe5264b8829d35e14608b8) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/tests/xe_rtp_test.c | 103 ++++++++++--------------- drivers/gpu/drm/xe/xe_hw_engine.c | 14 ++-- drivers/gpu/drm/xe/xe_reg_whitelist.c | 7 +- drivers/gpu/drm/xe/xe_rtp.c | 31 ++++---- drivers/gpu/drm/xe/xe_rtp.h | 16 +++- drivers/gpu/drm/xe/xe_rtp_types.h | 10 +++ drivers/gpu/drm/xe/xe_tuning.c | 45 +++++------ drivers/gpu/drm/xe/xe_wa.c | 89 +++++++++++---------- 8 files changed, 156 insertions(+), 159 deletions(-) diff --git a/drivers/gpu/drm/xe/tests/xe_rtp_test.c b/drivers/gpu/drm/xe/tests/xe_rtp_test.c index 642f6e090ad0..3d0688d058d9 100644 --- a/drivers/gpu/drm/xe/tests/xe_rtp_test.c +++ b/drivers/gpu/drm/xe/tests/xe_rtp_test.c @@ -54,13 +54,13 @@ struct rtp_to_sr_test_case { unsigned long expected_count_sr_entries; unsigned int expected_sr_errors; unsigned long expected_active; - const struct xe_rtp_entry_sr *entries; + const struct xe_rtp_table_sr table; }; struct rtp_test_case { const char *name; unsigned long expected_active; - const struct xe_rtp_entry *entries; + const struct xe_rtp_table table; }; static bool fake_xe_gt_mcr_check_reg(struct xe_gt *gt, struct xe_reg reg) @@ -289,7 +289,7 @@ static const struct rtp_to_sr_test_case rtp_to_sr_cases[] = { .expected_active = BIT(0) | BIT(1), .expected_count_sr_entries = 1, /* Different bits on the same register: create a single entry */ - .entries = (const struct xe_rtp_entry_sr[]) { + .table = XE_RTP_TABLE_SR( { XE_RTP_NAME("basic-1"), XE_RTP_RULES(FUNC(match_yes)), XE_RTP_ACTIONS(SET(REGULAR_REG1, REG_BIT(0))) @@ -298,8 +298,7 @@ static const struct rtp_to_sr_test_case rtp_to_sr_cases[] = { XE_RTP_RULES(FUNC(match_yes)), XE_RTP_ACTIONS(SET(REGULAR_REG1, REG_BIT(1))) }, - {} - }, + ), }, { .name = "no-match-no-add", @@ -309,7 +308,7 @@ static const struct rtp_to_sr_test_case rtp_to_sr_cases[] = { .expected_active = BIT(0), .expected_count_sr_entries = 1, /* Don't coalesce second entry since rules don't match */ - .entries = (const struct xe_rtp_entry_sr[]) { + .table = XE_RTP_TABLE_SR( { XE_RTP_NAME("basic-1"), XE_RTP_RULES(FUNC(match_yes)), XE_RTP_ACTIONS(SET(REGULAR_REG1, REG_BIT(0))) @@ -318,8 +317,7 @@ static const struct rtp_to_sr_test_case rtp_to_sr_cases[] = { XE_RTP_RULES(FUNC(match_no)), XE_RTP_ACTIONS(SET(REGULAR_REG1, REG_BIT(1))) }, - {} - }, + ), }, { .name = "two-regs-two-entries", @@ -329,7 +327,7 @@ static const struct rtp_to_sr_test_case rtp_to_sr_cases[] = { .expected_active = BIT(0) | BIT(1), .expected_count_sr_entries = 2, /* Same bits on different registers are not coalesced */ - .entries = (const struct xe_rtp_entry_sr[]) { + .table = XE_RTP_TABLE_SR( { XE_RTP_NAME("basic-1"), XE_RTP_RULES(FUNC(match_yes)), XE_RTP_ACTIONS(SET(REGULAR_REG1, REG_BIT(0))) @@ -338,8 +336,7 @@ static const struct rtp_to_sr_test_case rtp_to_sr_cases[] = { XE_RTP_RULES(FUNC(match_yes)), XE_RTP_ACTIONS(SET(REGULAR_REG2, REG_BIT(0))) }, - {} - }, + ), }, { .name = "clr-one-set-other", @@ -349,7 +346,7 @@ static const struct rtp_to_sr_test_case rtp_to_sr_cases[] = { .expected_active = BIT(0) | BIT(1), .expected_count_sr_entries = 1, /* Check clr vs set actions on different bits */ - .entries = (const struct xe_rtp_entry_sr[]) { + .table = XE_RTP_TABLE_SR( { XE_RTP_NAME("basic-1"), XE_RTP_RULES(FUNC(match_yes)), XE_RTP_ACTIONS(SET(REGULAR_REG1, REG_BIT(0))) @@ -358,8 +355,7 @@ static const struct rtp_to_sr_test_case rtp_to_sr_cases[] = { XE_RTP_RULES(FUNC(match_yes)), XE_RTP_ACTIONS(CLR(REGULAR_REG1, REG_BIT(1))) }, - {} - }, + ), }, { #define TEMP_MASK REG_GENMASK(10, 8) @@ -371,14 +367,13 @@ static const struct rtp_to_sr_test_case rtp_to_sr_cases[] = { .expected_active = BIT(0), .expected_count_sr_entries = 1, /* Check FIELD_SET works */ - .entries = (const struct xe_rtp_entry_sr[]) { + .table = XE_RTP_TABLE_SR( { XE_RTP_NAME("basic-1"), XE_RTP_RULES(FUNC(match_yes)), XE_RTP_ACTIONS(FIELD_SET(REGULAR_REG1, TEMP_MASK, TEMP_FIELD)) }, - {} - }, + ), #undef TEMP_MASK #undef TEMP_FIELD }, @@ -390,7 +385,7 @@ static const struct rtp_to_sr_test_case rtp_to_sr_cases[] = { .expected_active = BIT(0) | BIT(1), .expected_count_sr_entries = 1, .expected_sr_errors = 1, - .entries = (const struct xe_rtp_entry_sr[]) { + .table = XE_RTP_TABLE_SR( { XE_RTP_NAME("basic-1"), XE_RTP_RULES(FUNC(match_yes)), XE_RTP_ACTIONS(SET(REGULAR_REG1, REG_BIT(0))) @@ -400,8 +395,7 @@ static const struct rtp_to_sr_test_case rtp_to_sr_cases[] = { XE_RTP_RULES(FUNC(match_yes)), XE_RTP_ACTIONS(SET(REGULAR_REG1, REG_BIT(0))) }, - {} - }, + ), }, { .name = "conflict-not-disjoint", @@ -411,7 +405,7 @@ static const struct rtp_to_sr_test_case rtp_to_sr_cases[] = { .expected_active = BIT(0) | BIT(1), .expected_count_sr_entries = 1, .expected_sr_errors = 1, - .entries = (const struct xe_rtp_entry_sr[]) { + .table = XE_RTP_TABLE_SR( { XE_RTP_NAME("basic-1"), XE_RTP_RULES(FUNC(match_yes)), XE_RTP_ACTIONS(SET(REGULAR_REG1, REG_BIT(0))) @@ -421,8 +415,7 @@ static const struct rtp_to_sr_test_case rtp_to_sr_cases[] = { XE_RTP_RULES(FUNC(match_yes)), XE_RTP_ACTIONS(CLR(REGULAR_REG1, REG_GENMASK(1, 0))) }, - {} - }, + ), }, { .name = "conflict-reg-type", @@ -432,7 +425,7 @@ static const struct rtp_to_sr_test_case rtp_to_sr_cases[] = { .expected_active = BIT(0) | BIT(1) | BIT(2), .expected_count_sr_entries = 1, .expected_sr_errors = 2, - .entries = (const struct xe_rtp_entry_sr[]) { + .table = XE_RTP_TABLE_SR( { XE_RTP_NAME("basic-1"), XE_RTP_RULES(FUNC(match_yes)), XE_RTP_ACTIONS(SET(REGULAR_REG1, REG_BIT(0))) @@ -447,8 +440,7 @@ static const struct rtp_to_sr_test_case rtp_to_sr_cases[] = { XE_RTP_RULES(FUNC(match_yes)), XE_RTP_ACTIONS(SET(MASKED_REG1, REG_BIT(0))) }, - {} - }, + ), }, { .name = "bad-mcr-reg-forced-to-regular", @@ -458,13 +450,12 @@ static const struct rtp_to_sr_test_case rtp_to_sr_cases[] = { .expected_active = BIT(0), .expected_count_sr_entries = 1, .expected_sr_errors = 1, - .entries = (const struct xe_rtp_entry_sr[]) { + .table = XE_RTP_TABLE_SR( { XE_RTP_NAME("bad-mcr-regular-reg"), XE_RTP_RULES(FUNC(match_yes)), XE_RTP_ACTIONS(SET(BAD_MCR_REG4, REG_BIT(0))) }, - {} - }, + ), }, { .name = "bad-regular-reg-forced-to-mcr", @@ -474,13 +465,12 @@ static const struct rtp_to_sr_test_case rtp_to_sr_cases[] = { .expected_active = BIT(0), .expected_count_sr_entries = 1, .expected_sr_errors = 1, - .entries = (const struct xe_rtp_entry_sr[]) { + .table = XE_RTP_TABLE_SR( { XE_RTP_NAME("bad-regular-reg"), XE_RTP_RULES(FUNC(match_yes)), XE_RTP_ACTIONS(SET(BAD_REGULAR_REG5, REG_BIT(0))) }, - {} - }, + ), }, }; @@ -492,16 +482,12 @@ static void xe_rtp_process_to_sr_tests(struct kunit *test) struct xe_reg_sr *reg_sr = >->reg_sr; const struct xe_reg_sr_entry *sre, *sr_entry = NULL; struct xe_rtp_process_ctx ctx = XE_RTP_PROCESS_CTX_INITIALIZER(gt); - unsigned long idx, count_sr_entries = 0, count_rtp_entries = 0, active = 0; + unsigned long idx, count_sr_entries = 0, active = 0; xe_reg_sr_init(reg_sr, "xe_rtp_to_sr_tests", xe); - while (param->entries[count_rtp_entries].rules) - count_rtp_entries++; - - xe_rtp_process_ctx_enable_active_tracking(&ctx, &active, count_rtp_entries); - xe_rtp_process_to_sr(&ctx, param->entries, count_rtp_entries, - reg_sr, false); + xe_rtp_process_ctx_enable_active_tracking(&ctx, &active, param->table.n_entries); + xe_rtp_process_to_sr(&ctx, ¶m->table, reg_sr, false); xa_for_each(®_sr->xa, idx, sre) { if (idx == param->expected_reg.addr) @@ -534,56 +520,52 @@ static const struct rtp_test_case rtp_cases[] = { { .name = "active1", .expected_active = BIT(0), - .entries = (const struct xe_rtp_entry[]) { + .table = XE_RTP_TABLE( { XE_RTP_NAME("r1"), XE_RTP_RULES(FUNC(match_yes)), }, - {} - }, + ), }, { .name = "active2", .expected_active = BIT(0) | BIT(1), - .entries = (const struct xe_rtp_entry[]) { + .table = XE_RTP_TABLE( { XE_RTP_NAME("r1"), XE_RTP_RULES(FUNC(match_yes)), }, { XE_RTP_NAME("r2"), XE_RTP_RULES(FUNC(match_yes)), }, - {} - }, + ), }, { .name = "active-inactive", .expected_active = BIT(0), - .entries = (const struct xe_rtp_entry[]) { + .table = XE_RTP_TABLE( { XE_RTP_NAME("r1"), XE_RTP_RULES(FUNC(match_yes)), }, { XE_RTP_NAME("r2"), XE_RTP_RULES(FUNC(match_no)), }, - {} - }, + ), }, { .name = "inactive-active", .expected_active = BIT(1), - .entries = (const struct xe_rtp_entry[]) { + .table = XE_RTP_TABLE( { XE_RTP_NAME("r1"), XE_RTP_RULES(FUNC(match_no)), }, { XE_RTP_NAME("r2"), XE_RTP_RULES(FUNC(match_yes)), }, - {} - }, + ), }, { .name = "inactive-active-inactive", .expected_active = BIT(1), - .entries = (const struct xe_rtp_entry[]) { + .table = XE_RTP_TABLE( { XE_RTP_NAME("r1"), XE_RTP_RULES(FUNC(match_no)), }, @@ -593,13 +575,12 @@ static const struct rtp_test_case rtp_cases[] = { { XE_RTP_NAME("r3"), XE_RTP_RULES(FUNC(match_no)), }, - {} - }, + ), }, { .name = "inactive-inactive-inactive", .expected_active = 0, - .entries = (const struct xe_rtp_entry[]) { + .table = XE_RTP_TABLE( { XE_RTP_NAME("r1"), XE_RTP_RULES(FUNC(match_no)), }, @@ -609,8 +590,7 @@ static const struct rtp_test_case rtp_cases[] = { { XE_RTP_NAME("r3"), XE_RTP_RULES(FUNC(match_no)), }, - {} - }, + ), }, }; @@ -620,13 +600,10 @@ static void xe_rtp_process_tests(struct kunit *test) struct xe_device *xe = test->priv; struct xe_gt *gt = xe_device_get_root_tile(xe)->primary_gt; struct xe_rtp_process_ctx ctx = XE_RTP_PROCESS_CTX_INITIALIZER(gt); - unsigned long count_rtp_entries = 0, active = 0; + unsigned long active = 0; - while (param->entries[count_rtp_entries].rules) - count_rtp_entries++; - - xe_rtp_process_ctx_enable_active_tracking(&ctx, &active, count_rtp_entries); - xe_rtp_process(&ctx, param->entries); + xe_rtp_process_ctx_enable_active_tracking(&ctx, &active, param->table.n_entries); + xe_rtp_process(&ctx, ¶m->table); KUNIT_EXPECT_EQ(test, active, param->expected_active); } diff --git a/drivers/gpu/drm/xe/xe_hw_engine.c b/drivers/gpu/drm/xe/xe_hw_engine.c index 8c66ff6f3d3c..98265293f2dc 100644 --- a/drivers/gpu/drm/xe/xe_hw_engine.c +++ b/drivers/gpu/drm/xe/xe_hw_engine.c @@ -346,7 +346,7 @@ hw_engine_setup_default_lrc_state(struct xe_hw_engine *hwe) u32 blit_cctl_val = REG_FIELD_PREP(BLIT_CCTL_DST_MOCS_MASK, mocs_write_idx) | REG_FIELD_PREP(BLIT_CCTL_SRC_MOCS_MASK, mocs_read_idx); struct xe_rtp_process_ctx ctx = XE_RTP_PROCESS_CTX_INITIALIZER(hwe); - const struct xe_rtp_entry_sr lrc_setup[] = { + const struct xe_rtp_table_sr lrc_setup = XE_RTP_TABLE_SR( /* * Some blitter commands do not have a field for MOCS, those * commands will use MOCS index pointed by BLIT_CCTL. @@ -369,10 +369,9 @@ hw_engine_setup_default_lrc_state(struct xe_hw_engine *hwe) PREEMPT_GPGPU_THREAD_GROUP_LEVEL)), XE_RTP_ENTRY_FLAG(FOREACH_ENGINE) }, - }; + ); - xe_rtp_process_to_sr(&ctx, lrc_setup, ARRAY_SIZE(lrc_setup), - &hwe->reg_lrc, true); + xe_rtp_process_to_sr(&ctx, &lrc_setup, &hwe->reg_lrc, true); } void xe_hw_engine_setup_reg_lrc(struct xe_hw_engine *hwe) @@ -408,7 +407,7 @@ hw_engine_setup_default_state(struct xe_hw_engine *hwe) u32 ring_cmd_cctl_val = REG_FIELD_PREP(CMD_CCTL_WRITE_OVERRIDE_MASK, mocs_write_idx) | REG_FIELD_PREP(CMD_CCTL_READ_OVERRIDE_MASK, mocs_read_idx); struct xe_rtp_process_ctx ctx = XE_RTP_PROCESS_CTX_INITIALIZER(hwe); - const struct xe_rtp_entry_sr engine_entries[] = { + const struct xe_rtp_table_sr engine_sr = XE_RTP_TABLE_SR( { XE_RTP_NAME("RING_CMD_CCTL_default_MOCS"), XE_RTP_RULES(FUNC(xe_rtp_match_always)), XE_RTP_ACTIONS(FIELD_SET(RING_CMD_CCTL(0), @@ -465,10 +464,9 @@ hw_engine_setup_default_state(struct xe_hw_engine *hwe) XE_RTP_ACTIONS(SET(GFX_MODE(0), GFX_MSIX_INTERRUPT_ENABLE, XE_RTP_ACTION_FLAG(ENGINE_BASE))) }, - }; + ); - xe_rtp_process_to_sr(&ctx, engine_entries, ARRAY_SIZE(engine_entries), - &hwe->reg_sr, false); + xe_rtp_process_to_sr(&ctx, &engine_sr, &hwe->reg_sr, false); } static const struct engine_info *find_engine_info(enum xe_engine_class class, int instance) diff --git a/drivers/gpu/drm/xe/xe_reg_whitelist.c b/drivers/gpu/drm/xe/xe_reg_whitelist.c index d3bfc05949ae..2d8ddb57412c 100644 --- a/drivers/gpu/drm/xe/xe_reg_whitelist.c +++ b/drivers/gpu/drm/xe/xe_reg_whitelist.c @@ -41,7 +41,7 @@ static bool match_multi_queue_class(const struct xe_device *xe, return xe_gt_supports_multi_queue(gt, hwe->class); } -static const struct xe_rtp_entry_sr register_whitelist[] = { +static const struct xe_rtp_table_sr register_whitelist = XE_RTP_TABLE_SR( { XE_RTP_NAME("WaAllowPMDepthAndInvocationCountAccessFromUMD, 1408556865"), XE_RTP_RULES(GRAPHICS_VERSION_RANGE(1200, 1210), ENGINE_CLASS(RENDER)), XE_RTP_ACTIONS(WHITELIST(PS_INVOCATION_COUNT, @@ -156,7 +156,7 @@ static const struct xe_rtp_entry_sr register_whitelist[] = { XE_RTP_RULES(FUNC(match_has_mert), ENGINE_CLASS(COPY)), XE_RTP_ACTIONS(WHITELIST_OA_MERT_MMIO_TRG) }, -}; +); static void whitelist_apply_to_hwe(struct xe_hw_engine *hwe) { @@ -204,8 +204,7 @@ void xe_reg_whitelist_process_engine(struct xe_hw_engine *hwe) { struct xe_rtp_process_ctx ctx = XE_RTP_PROCESS_CTX_INITIALIZER(hwe); - xe_rtp_process_to_sr(&ctx, register_whitelist, ARRAY_SIZE(register_whitelist), - &hwe->reg_whitelist, false); + xe_rtp_process_to_sr(&ctx, ®ister_whitelist, &hwe->reg_whitelist, false); whitelist_apply_to_hwe(hwe); } diff --git a/drivers/gpu/drm/xe/xe_rtp.c b/drivers/gpu/drm/xe/xe_rtp.c index dec9d94e6fb0..83a40e1f9528 100644 --- a/drivers/gpu/drm/xe/xe_rtp.c +++ b/drivers/gpu/drm/xe/xe_rtp.c @@ -326,8 +326,7 @@ static void rtp_mark_active(struct xe_device *xe, * xe_rtp_process_to_sr - Process all rtp @entries, adding the matching ones to * the save-restore argument. * @ctx: The context for processing the table, with one of device, gt or hwe - * @entries: Table with RTP definitions - * @n_entries: Number of entries to process, usually ARRAY_SIZE(entries) + * @table: Table with RTP definitions * @sr: Save-restore struct where matching rules execute the action. This can be * viewed as the "coalesced view" of multiple the tables. The bits for each * register set are expected not to collide with previously added entries @@ -339,12 +338,10 @@ static void rtp_mark_active(struct xe_device *xe, * used to calculate the right register offset */ void xe_rtp_process_to_sr(struct xe_rtp_process_ctx *ctx, - const struct xe_rtp_entry_sr *entries, - size_t n_entries, + const struct xe_rtp_table_sr *table, struct xe_reg_sr *sr, bool process_in_vf) { - const struct xe_rtp_entry_sr *entry; struct xe_hw_engine *hwe = NULL; struct xe_gt *gt = NULL; struct xe_device *xe = NULL; @@ -354,9 +351,10 @@ void xe_rtp_process_to_sr(struct xe_rtp_process_ctx *ctx, if (!process_in_vf && IS_SRIOV_VF(xe)) return; - xe_assert(xe, entries); + xe_assert(xe, table->entries); - for (entry = entries; entry - entries < n_entries; entry++) { + for (size_t i = 0; i < table->n_entries; i++) { + const struct xe_rtp_entry_sr *entry = &table->entries[i]; bool match = false; if (entry->flags & XE_RTP_ENTRY_FLAG_FOREACH_ENGINE) { @@ -371,37 +369,40 @@ void xe_rtp_process_to_sr(struct xe_rtp_process_ctx *ctx, } if (match) - rtp_mark_active(xe, ctx, entry - entries); + rtp_mark_active(xe, ctx, i); } } EXPORT_SYMBOL_IF_KUNIT(xe_rtp_process_to_sr); /** - * xe_rtp_process - Process all rtp @entries, without running any action + * xe_rtp_process - Process all entries in rtp @table, without running any action * @ctx: The context for processing the table, with one of device, gt or hwe - * @entries: Table with RTP definitions + * @table: Table with RTP definitions * - * Walk the table pointed by @entries (with an empty sentinel), executing the + * Walk the table pointed by @table, executing the * rules. One difference from xe_rtp_process_to_sr(): there is no action * associated with each entry since this uses struct xe_rtp_entry. Its main use * is for marking active workarounds via * xe_rtp_process_ctx_enable_active_tracking(). */ void xe_rtp_process(struct xe_rtp_process_ctx *ctx, - const struct xe_rtp_entry *entries) + const struct xe_rtp_table *table) { - const struct xe_rtp_entry *entry; struct xe_hw_engine *hwe; struct xe_gt *gt; struct xe_device *xe; rtp_get_context(ctx, &hwe, >, &xe); - for (entry = entries; entry && entry->rules; entry++) { + xe_assert(xe, table->entries); + + for (size_t i = 0; i < table->n_entries; i++) { + const struct xe_rtp_entry *entry = &table->entries[i]; + if (!rule_matches(xe, gt, hwe, entry->rules, entry->n_rules)) continue; - rtp_mark_active(xe, ctx, entry - entries); + rtp_mark_active(xe, ctx, i); } } EXPORT_SYMBOL_IF_KUNIT(xe_rtp_process); diff --git a/drivers/gpu/drm/xe/xe_rtp.h b/drivers/gpu/drm/xe/xe_rtp.h index e4f1930ca1c3..4e3cfd69f922 100644 --- a/drivers/gpu/drm/xe/xe_rtp.h +++ b/drivers/gpu/drm/xe/xe_rtp.h @@ -461,6 +461,16 @@ struct xe_reg_sr; XE_RTP_PASTE_FOREACH(ACTION_, COMMA, (__VA_ARGS__)) \ } +#define XE_RTP_TABLE_SR(...) { \ + .entries = (const struct xe_rtp_entry_sr[]){__VA_ARGS__}, \ + .n_entries = ARRAY_SIZE(((const struct xe_rtp_entry_sr[]){__VA_ARGS__})), \ +} + +#define XE_RTP_TABLE(...) { \ + .entries = (const struct xe_rtp_entry[]){__VA_ARGS__}, \ + .n_entries = ARRAY_SIZE(((const struct xe_rtp_entry[]){__VA_ARGS__})), \ +} + #define XE_RTP_PROCESS_CTX_INITIALIZER(arg__) _Generic((arg__), \ struct xe_hw_engine * : (struct xe_rtp_process_ctx){ { (void *)(arg__) }, XE_RTP_PROCESS_TYPE_ENGINE }, \ struct xe_gt * : (struct xe_rtp_process_ctx){ { (void *)(arg__) }, XE_RTP_PROCESS_TYPE_GT }, \ @@ -471,12 +481,12 @@ void xe_rtp_process_ctx_enable_active_tracking(struct xe_rtp_process_ctx *ctx, size_t n_entries); void xe_rtp_process_to_sr(struct xe_rtp_process_ctx *ctx, - const struct xe_rtp_entry_sr *entries, - size_t n_entries, struct xe_reg_sr *sr, + const struct xe_rtp_table_sr *table, + struct xe_reg_sr *sr, bool process_in_vf); void xe_rtp_process(struct xe_rtp_process_ctx *ctx, - const struct xe_rtp_entry *entries); + const struct xe_rtp_table *table); /* Match functions to be used with XE_RTP_MATCH_FUNC */ diff --git a/drivers/gpu/drm/xe/xe_rtp_types.h b/drivers/gpu/drm/xe/xe_rtp_types.h index 0265c16d2762..58018ae4f8cc 100644 --- a/drivers/gpu/drm/xe/xe_rtp_types.h +++ b/drivers/gpu/drm/xe/xe_rtp_types.h @@ -112,6 +112,16 @@ struct xe_rtp_entry { u8 n_rules; }; +struct xe_rtp_table_sr { + const struct xe_rtp_entry_sr *entries; + size_t n_entries; +}; + +struct xe_rtp_table { + const struct xe_rtp_entry *entries; + size_t n_entries; +}; + enum xe_rtp_process_type { XE_RTP_PROCESS_TYPE_DEVICE, XE_RTP_PROCESS_TYPE_GT, diff --git a/drivers/gpu/drm/xe/xe_tuning.c b/drivers/gpu/drm/xe/xe_tuning.c index 9a1b3862e192..bf3fad9cdbef 100644 --- a/drivers/gpu/drm/xe/xe_tuning.c +++ b/drivers/gpu/drm/xe/xe_tuning.c @@ -20,7 +20,7 @@ #undef XE_REG_MCR #define XE_REG_MCR(...) XE_REG(__VA_ARGS__, .mcr = 1) -static const struct xe_rtp_entry_sr gt_tunings[] = { +static const struct xe_rtp_table_sr gt_tunings = XE_RTP_TABLE_SR( { XE_RTP_NAME("Tuning: Blend Fill Caching Optimization Disable"), XE_RTP_RULES(PLATFORM(DG2)), XE_RTP_ACTIONS(SET(XEHP_L3SCQREG7, BLEND_FILL_CACHING_OPT_DIS)) @@ -100,9 +100,9 @@ static const struct xe_rtp_entry_sr gt_tunings[] = { XE_RTP_ACTIONS(FIELD_SET(GAMSTLB_CTRL, BANK_HASH_MODE, BANK_HASH_4KB_MODE)) }, -}; +); -static const struct xe_rtp_entry_sr engine_tunings[] = { +static const struct xe_rtp_table_sr engine_tunings = XE_RTP_TABLE_SR( { XE_RTP_NAME("Tuning: L3 Hashing Mask"), XE_RTP_RULES(GRAPHICS_VERSION_RANGE(1200, 1210), FUNC(xe_rtp_match_first_render_or_compute)), @@ -129,9 +129,9 @@ static const struct xe_rtp_entry_sr engine_tunings[] = { FUNC(xe_rtp_match_first_render_or_compute)), XE_RTP_ACTIONS(SET(TDL_TSL_CHICKEN2, TILEY_LOCALID)) }, -}; +); -static const struct xe_rtp_entry_sr lrc_tunings[] = { +static const struct xe_rtp_table_sr lrc_tunings = XE_RTP_TABLE_SR( { XE_RTP_NAME("Tuning: Windower HW Filtering"), XE_RTP_RULES(GRAPHICS_VERSION_RANGE(3000, 3599), ENGINE_CLASS(RENDER)), XE_RTP_ACTIONS(SET(XEHP_COMMON_SLICE_CHICKEN4, HW_FILTERING)) @@ -171,7 +171,7 @@ static const struct xe_rtp_entry_sr lrc_tunings[] = { XE_RTP_ACTIONS(FIELD_SET(FF_MODE, VS_HIT_MAX_VALUE_MASK, REG_FIELD_PREP(VS_HIT_MAX_VALUE_MASK, 0x3f))) }, -}; +); /** * xe_tuning_init - initialize gt with tunings bookkeeping @@ -185,9 +185,9 @@ int xe_tuning_init(struct xe_gt *gt) size_t n_lrc, n_engine, n_gt, total; unsigned long *p; - n_gt = BITS_TO_LONGS(ARRAY_SIZE(gt_tunings)); - n_engine = BITS_TO_LONGS(ARRAY_SIZE(engine_tunings)); - n_lrc = BITS_TO_LONGS(ARRAY_SIZE(lrc_tunings)); + n_gt = BITS_TO_LONGS(gt_tunings.n_entries); + n_engine = BITS_TO_LONGS(engine_tunings.n_entries); + n_lrc = BITS_TO_LONGS(lrc_tunings.n_entries); total = n_gt + n_engine + n_lrc; p = drmm_kzalloc(&xe->drm, sizeof(*p) * total, GFP_KERNEL); @@ -210,9 +210,8 @@ void xe_tuning_process_gt(struct xe_gt *gt) xe_rtp_process_ctx_enable_active_tracking(&ctx, gt->tuning_active.gt, - ARRAY_SIZE(gt_tunings)); - xe_rtp_process_to_sr(&ctx, gt_tunings, ARRAY_SIZE(gt_tunings), - >->reg_sr, false); + gt_tunings.n_entries); + xe_rtp_process_to_sr(&ctx, >_tunings, >->reg_sr, false); } EXPORT_SYMBOL_IF_KUNIT(xe_tuning_process_gt); @@ -222,9 +221,8 @@ void xe_tuning_process_engine(struct xe_hw_engine *hwe) xe_rtp_process_ctx_enable_active_tracking(&ctx, hwe->gt->tuning_active.engine, - ARRAY_SIZE(engine_tunings)); - xe_rtp_process_to_sr(&ctx, engine_tunings, ARRAY_SIZE(engine_tunings), - &hwe->reg_sr, false); + engine_tunings.n_entries); + xe_rtp_process_to_sr(&ctx, &engine_tunings, &hwe->reg_sr, false); } EXPORT_SYMBOL_IF_KUNIT(xe_tuning_process_engine); @@ -242,9 +240,8 @@ void xe_tuning_process_lrc(struct xe_hw_engine *hwe) xe_rtp_process_ctx_enable_active_tracking(&ctx, hwe->gt->tuning_active.lrc, - ARRAY_SIZE(lrc_tunings)); - xe_rtp_process_to_sr(&ctx, lrc_tunings, ARRAY_SIZE(lrc_tunings), - &hwe->reg_lrc, true); + lrc_tunings.n_entries); + xe_rtp_process_to_sr(&ctx, &lrc_tunings, &hwe->reg_lrc, true); } /** @@ -259,18 +256,18 @@ int xe_tuning_dump(struct xe_gt *gt, struct drm_printer *p) size_t idx; drm_printf(p, "GT Tunings\n"); - for_each_set_bit(idx, gt->tuning_active.gt, ARRAY_SIZE(gt_tunings)) - drm_printf_indent(p, 1, "%s\n", gt_tunings[idx].name); + for_each_set_bit(idx, gt->tuning_active.gt, gt_tunings.n_entries) + drm_printf_indent(p, 1, "%s\n", gt_tunings.entries[idx].name); drm_puts(p, "\n"); drm_printf(p, "Engine Tunings\n"); - for_each_set_bit(idx, gt->tuning_active.engine, ARRAY_SIZE(engine_tunings)) - drm_printf_indent(p, 1, "%s\n", engine_tunings[idx].name); + for_each_set_bit(idx, gt->tuning_active.engine, engine_tunings.n_entries) + drm_printf_indent(p, 1, "%s\n", engine_tunings.entries[idx].name); drm_puts(p, "\n"); drm_printf(p, "LRC Tunings\n"); - for_each_set_bit(idx, gt->tuning_active.lrc, ARRAY_SIZE(lrc_tunings)) - drm_printf_indent(p, 1, "%s\n", lrc_tunings[idx].name); + for_each_set_bit(idx, gt->tuning_active.lrc, lrc_tunings.n_entries) + drm_printf_indent(p, 1, "%s\n", lrc_tunings.entries[idx].name); return 0; } diff --git a/drivers/gpu/drm/xe/xe_wa.c b/drivers/gpu/drm/xe/xe_wa.c index cb811f8a7781..b9d9fe0801aa 100644 --- a/drivers/gpu/drm/xe/xe_wa.c +++ b/drivers/gpu/drm/xe/xe_wa.c @@ -130,7 +130,7 @@ __diag_push(); __diag_ignore_all("-Woverride-init", "Allow field overrides in table"); -static const struct xe_rtp_entry_sr gt_was[] = { +static const struct xe_rtp_table_sr gt_was = XE_RTP_TABLE_SR( /* Workarounds applying over a range of IPs */ { XE_RTP_NAME("14011060649"), @@ -306,9 +306,9 @@ static const struct xe_rtp_entry_sr gt_was[] = { XE_RTP_RULES(GRAPHICS_VERSION(3510), GRAPHICS_STEP(A0, B0)), XE_RTP_ACTIONS(SET(GUC_INTR_CHICKEN, DISABLE_SIGNALING_ENGINES)) }, -}; +); -static const struct xe_rtp_entry_sr engine_was[] = { +static const struct xe_rtp_table_sr engine_was = XE_RTP_TABLE_SR( /* Workarounds applying over a range of IPs */ { XE_RTP_NAME("22010931296, 18011464164, 14010919138"), @@ -614,9 +614,9 @@ static const struct xe_rtp_entry_sr engine_was[] = { FUNC(xe_rtp_match_first_render_or_compute)), XE_RTP_ACTIONS(SET(TDL_CHICKEN, BIT_APQ_OPT_DIS)) }, -}; +); -static const struct xe_rtp_entry_sr lrc_was[] = { +static const struct xe_rtp_table_sr lrc_was = XE_RTP_TABLE_SR( { XE_RTP_NAME("16011163337"), XE_RTP_RULES(GRAPHICS_VERSION_RANGE(1200, 1210), ENGINE_CLASS(RENDER)), /* read verification is ignored due to 1608008084. */ @@ -794,21 +794,29 @@ static const struct xe_rtp_entry_sr lrc_was[] = { ENGINE_CLASS(RENDER)), XE_RTP_ACTIONS(SET(CHICKEN_RASTER_1, DIS_CLIP_NEGATIVE_BOUNDING_BOX)) }, -}; +); -static __maybe_unused const struct xe_rtp_entry oob_was[] = { +static const struct xe_rtp_entry oob_was_entries[] = { #include - {} }; -static_assert(ARRAY_SIZE(oob_was) - 1 == _XE_WA_OOB_COUNT); +static_assert(ARRAY_SIZE(oob_was_entries) == _XE_WA_OOB_COUNT); -static __maybe_unused const struct xe_rtp_entry device_oob_was[] = { +static __maybe_unused const struct xe_rtp_table oob_was = { + .entries = oob_was_entries, + .n_entries = ARRAY_SIZE(oob_was_entries), +}; + +static const struct xe_rtp_entry device_oob_was_entries[] = { #include - {} }; -static_assert(ARRAY_SIZE(device_oob_was) - 1 == _XE_DEVICE_WA_OOB_COUNT); +static_assert(ARRAY_SIZE(device_oob_was_entries) == _XE_DEVICE_WA_OOB_COUNT); + +static __maybe_unused const struct xe_rtp_table device_oob_was = { + .entries = device_oob_was_entries, + .n_entries = ARRAY_SIZE(device_oob_was_entries), +}; __diag_pop(); @@ -824,10 +832,10 @@ void xe_wa_process_device_oob(struct xe_device *xe) { struct xe_rtp_process_ctx ctx = XE_RTP_PROCESS_CTX_INITIALIZER(xe); - xe_rtp_process_ctx_enable_active_tracking(&ctx, xe->wa_active.oob, ARRAY_SIZE(device_oob_was)); + xe_rtp_process_ctx_enable_active_tracking(&ctx, xe->wa_active.oob, device_oob_was.n_entries); xe->wa_active.oob_initialized = true; - xe_rtp_process(&ctx, device_oob_was); + xe_rtp_process(&ctx, &device_oob_was); } /** @@ -842,9 +850,9 @@ void xe_wa_process_gt_oob(struct xe_gt *gt) struct xe_rtp_process_ctx ctx = XE_RTP_PROCESS_CTX_INITIALIZER(gt); xe_rtp_process_ctx_enable_active_tracking(&ctx, gt->wa_active.oob, - ARRAY_SIZE(oob_was)); + oob_was.n_entries); gt->wa_active.oob_initialized = true; - xe_rtp_process(&ctx, oob_was); + xe_rtp_process(&ctx, &oob_was); } /** @@ -859,9 +867,8 @@ void xe_wa_process_gt(struct xe_gt *gt) struct xe_rtp_process_ctx ctx = XE_RTP_PROCESS_CTX_INITIALIZER(gt); xe_rtp_process_ctx_enable_active_tracking(&ctx, gt->wa_active.gt, - ARRAY_SIZE(gt_was)); - xe_rtp_process_to_sr(&ctx, gt_was, ARRAY_SIZE(gt_was), - >->reg_sr, false); + gt_was.n_entries); + xe_rtp_process_to_sr(&ctx, >_was, >->reg_sr, false); } EXPORT_SYMBOL_IF_KUNIT(xe_wa_process_gt); @@ -878,9 +885,8 @@ void xe_wa_process_engine(struct xe_hw_engine *hwe) struct xe_rtp_process_ctx ctx = XE_RTP_PROCESS_CTX_INITIALIZER(hwe); xe_rtp_process_ctx_enable_active_tracking(&ctx, hwe->gt->wa_active.engine, - ARRAY_SIZE(engine_was)); - xe_rtp_process_to_sr(&ctx, engine_was, ARRAY_SIZE(engine_was), - &hwe->reg_sr, false); + engine_was.n_entries); + xe_rtp_process_to_sr(&ctx, &engine_was, &hwe->reg_sr, false); } /** @@ -896,9 +902,8 @@ void xe_wa_process_lrc(struct xe_hw_engine *hwe) struct xe_rtp_process_ctx ctx = XE_RTP_PROCESS_CTX_INITIALIZER(hwe); xe_rtp_process_ctx_enable_active_tracking(&ctx, hwe->gt->wa_active.lrc, - ARRAY_SIZE(lrc_was)); - xe_rtp_process_to_sr(&ctx, lrc_was, ARRAY_SIZE(lrc_was), - &hwe->reg_lrc, true); + lrc_was.n_entries); + xe_rtp_process_to_sr(&ctx, &lrc_was, &hwe->reg_lrc, true); } /** @@ -912,7 +917,7 @@ int xe_wa_device_init(struct xe_device *xe) unsigned long *p; p = drmm_kzalloc(&xe->drm, - sizeof(*p) * BITS_TO_LONGS(ARRAY_SIZE(device_oob_was)), + sizeof(*p) * BITS_TO_LONGS(device_oob_was.n_entries), GFP_KERNEL); if (!p) @@ -935,10 +940,10 @@ int xe_wa_gt_init(struct xe_gt *gt) size_t n_oob, n_lrc, n_engine, n_gt, total; unsigned long *p; - n_gt = BITS_TO_LONGS(ARRAY_SIZE(gt_was)); - n_engine = BITS_TO_LONGS(ARRAY_SIZE(engine_was)); - n_lrc = BITS_TO_LONGS(ARRAY_SIZE(lrc_was)); - n_oob = BITS_TO_LONGS(ARRAY_SIZE(oob_was)); + n_gt = BITS_TO_LONGS(gt_was.n_entries); + n_engine = BITS_TO_LONGS(engine_was.n_entries); + n_lrc = BITS_TO_LONGS(lrc_was.n_entries); + n_oob = BITS_TO_LONGS(oob_was.n_entries); total = n_gt + n_engine + n_lrc + n_oob; p = drmm_kzalloc(&xe->drm, sizeof(*p) * total, GFP_KERNEL); @@ -962,9 +967,9 @@ void xe_wa_device_dump(struct xe_device *xe, struct drm_printer *p) size_t idx; drm_printf(p, "Device OOB Workarounds\n"); - for_each_set_bit(idx, xe->wa_active.oob, ARRAY_SIZE(device_oob_was)) - if (device_oob_was[idx].name) - drm_printf_indent(p, 1, "%s\n", device_oob_was[idx].name); + for_each_set_bit(idx, xe->wa_active.oob, device_oob_was.n_entries) + if (device_oob_was.entries[idx].name) + drm_printf_indent(p, 1, "%s\n", device_oob_was.entries[idx].name); } /** @@ -979,24 +984,24 @@ int xe_wa_gt_dump(struct xe_gt *gt, struct drm_printer *p) size_t idx; drm_printf(p, "GT Workarounds\n"); - for_each_set_bit(idx, gt->wa_active.gt, ARRAY_SIZE(gt_was)) - drm_printf_indent(p, 1, "%s\n", gt_was[idx].name); + for_each_set_bit(idx, gt->wa_active.gt, gt_was.n_entries) + drm_printf_indent(p, 1, "%s\n", gt_was.entries[idx].name); drm_puts(p, "\n"); drm_printf(p, "Engine Workarounds\n"); - for_each_set_bit(idx, gt->wa_active.engine, ARRAY_SIZE(engine_was)) - drm_printf_indent(p, 1, "%s\n", engine_was[idx].name); + for_each_set_bit(idx, gt->wa_active.engine, engine_was.n_entries) + drm_printf_indent(p, 1, "%s\n", engine_was.entries[idx].name); drm_puts(p, "\n"); drm_printf(p, "LRC Workarounds\n"); - for_each_set_bit(idx, gt->wa_active.lrc, ARRAY_SIZE(lrc_was)) - drm_printf_indent(p, 1, "%s\n", lrc_was[idx].name); + for_each_set_bit(idx, gt->wa_active.lrc, lrc_was.n_entries) + drm_printf_indent(p, 1, "%s\n", lrc_was.entries[idx].name); drm_puts(p, "\n"); drm_printf(p, "OOB Workarounds\n"); - for_each_set_bit(idx, gt->wa_active.oob, ARRAY_SIZE(oob_was)) - if (oob_was[idx].name) - drm_printf_indent(p, 1, "%s\n", oob_was[idx].name); + for_each_set_bit(idx, gt->wa_active.oob, oob_was.n_entries) + if (oob_was.entries[idx].name) + drm_printf_indent(p, 1, "%s\n", oob_was.entries[idx].name); return 0; } From fc16126cc11d9f507130bf84ab137ee0938c900e Mon Sep 17 00:00:00 2001 From: Reinette Chatre Date: Tue, 9 Jun 2026 14:02:27 -0700 Subject: [PATCH 0363/1054] x86,fs/resctrl: Prevent out-of-bounds access while offlining CPU when SNC enabled The architecture updates the cpu_mask in a domain's header to track which online CPUs are associated with the domain. When this mask becomes empty the architecture initiates offline of the domain that includes calling on resctrl fs to offline the domain. If it is a monitoring domain in which LLC occupancy is tracked resctrl fs forces the limbo handler to clear all busy RMID state associated with the domain. The limbo handler always reads the current event value associated with a busy RMID irrespective of it being checked as part of regular "is it still busy" check or whether it will be forced released anyway. When reading an RMID on a system with SNC enabled the "logical RMID" is converted to the "physical RMID" and this conversion requires the NUMA node ID of the resctrl monitoring domain that is in turn determined by querying the NUMA node ID of any CPU belonging to the monitoring domain. When the monitoring domain is going offline its cpu_mask is empty causing the NUMA node ID query via cpu_to_node() to be done with "nr_cpu_ids" as argument resulting in an out-of-bounds access. Refactor the limbo handler to skip reading the RMID when the RMID will just be forced to no longer be dirty in the domain anyway. Add a safety check to the architecture's RMID reader to protect against this scenario. Fixes: e13db55b5a0d ("x86/resctrl: Introduce snc_nodes_per_l3_cache") Closes: https://sashiko.dev/#/patchset/cover.1780456704.git.reinette.chatre%40intel.com?part=9 Reported-by: Sashiko Signed-off-by: Reinette Chatre Signed-off-by: Borislav Petkov (AMD) Cc: Link: https://patch.msgid.link/16137433df42f85013b2f7a53626795cbd6637b9.1781029125.git.reinette.chatre@intel.com --- arch/x86/kernel/cpu/resctrl/monitor.c | 5 ++++ fs/resctrl/monitor.c | 37 +++++++++++++++------------ 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/arch/x86/kernel/cpu/resctrl/monitor.c b/arch/x86/kernel/cpu/resctrl/monitor.c index 03ee6102ab07..569894d6e5c8 100644 --- a/arch/x86/kernel/cpu/resctrl/monitor.c +++ b/arch/x86/kernel/cpu/resctrl/monitor.c @@ -259,6 +259,11 @@ int resctrl_arch_rmid_read(struct rdt_resource *r, struct rdt_domain_hdr *hdr, if (!domain_header_is_valid(hdr, RESCTRL_MON_DOMAIN, RDT_RESOURCE_L3)) return -EINVAL; + if (cpumask_empty(&hdr->cpu_mask)) { + pr_warn_once("Domain %d has no CPUs\n", hdr->id); + return -EINVAL; + } + d = container_of(hdr, struct rdt_l3_mon_domain, hdr); hw_dom = resctrl_to_arch_mon_dom(d); cpu = cpumask_any(&hdr->cpu_mask); diff --git a/fs/resctrl/monitor.c b/fs/resctrl/monitor.c index 0e6a389a16bf..a932a1fea818 100644 --- a/fs/resctrl/monitor.c +++ b/fs/resctrl/monitor.c @@ -135,10 +135,10 @@ void __check_limbo(struct rdt_l3_mon_domain *d, bool force_free) struct rdt_resource *r = resctrl_arch_get_resource(RDT_RESOURCE_L3); u32 idx_limit = resctrl_arch_system_num_rmid_idx(); struct rmid_entry *entry; + bool rmid_dirty = true; u32 idx, cur_idx = 1; void *arch_mon_ctx; void *arch_priv; - bool rmid_dirty; u64 val = 0; arch_priv = mon_event_all[QOS_L3_OCCUP_EVENT_ID].arch_priv; @@ -161,22 +161,27 @@ void __check_limbo(struct rdt_l3_mon_domain *d, bool force_free) break; entry = __rmid_entry(idx); - if (resctrl_arch_rmid_read(r, &d->hdr, entry->closid, entry->rmid, - QOS_L3_OCCUP_EVENT_ID, arch_priv, &val, - arch_mon_ctx)) { - rmid_dirty = true; - } else { - rmid_dirty = (val >= resctrl_rmid_realloc_threshold); + if (!force_free) { + if (resctrl_arch_rmid_read(r, &d->hdr, entry->closid, + entry->rmid, QOS_L3_OCCUP_EVENT_ID, + arch_priv, &val, arch_mon_ctx)) { + rmid_dirty = true; + } else { + rmid_dirty = (val >= resctrl_rmid_realloc_threshold); - /* - * x86's CLOSID and RMID are independent numbers, so the entry's - * CLOSID is an empty CLOSID (X86_RESCTRL_EMPTY_CLOSID). On Arm the - * RMID (PMG) extends the CLOSID (PARTID) space with bits that aren't - * used to select the configuration. It is thus necessary to track both - * CLOSID and RMID because there may be dependencies between them - * on some architectures. - */ - trace_mon_llc_occupancy_limbo(entry->closid, entry->rmid, d->hdr.id, val); + /* + * x86's CLOSID and RMID are independent numbers, + * so the entry's CLOSID is an empty CLOSID + * (X86_RESCTRL_EMPTY_CLOSID). On Arm the RMID + * (PMG) extends the CLOSID (PARTID) space with + * bits that aren't used to select the configuration. + * It is thus necessary to track both CLOSID and + * RMID because there may be dependencies between + * them on some architectures. + */ + trace_mon_llc_occupancy_limbo(entry->closid, entry->rmid, + d->hdr.id, val); + } } if (force_free || !rmid_dirty) { From 44c74d27d1b9aaa99fa8a83640c1223575262b80 Mon Sep 17 00:00:00 2001 From: Stephen Smalley Date: Thu, 18 Jun 2026 13:55:14 -0400 Subject: [PATCH 0364/1054] selinux: check connect-related permissions on TCP Fast Open Similar to Landlock, SELinux was not updated when TCP Fast Open support was introduced to ensure connect-related permissions are checked when using TCP Fast Open. Update its socket_sendmsg() hook to call selinux_socket_connect() when MSG_FASTOPEN is passed. Cc: stable@vger.kernel.org Link: https://lore.kernel.org/linux-security-module/20260616201615.275032-1-hexlabsecurity@proton.me/ Link: https://lore.kernel.org/linux-security-module/20260617180526.15627-2-matthieu@buffet.re/ Reported-by: Bryam Vargas Reported-by: Matthieu Buffet Reported-by: Mikhail Ivanov Signed-off-by: Stephen Smalley Tested-by: Bryam Vargas Signed-off-by: Paul Moore --- security/selinux/hooks.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 1a713d96206f..c4677ce89399 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -5262,7 +5262,24 @@ static int selinux_socket_accept(struct socket *sock, struct socket *newsock) static int selinux_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size) { - return sock_has_perm(sock->sk, SOCKET__WRITE); + int rc; + struct sockaddr *const addr = msg->msg_name; + const int addrlen = msg->msg_namelen; + + rc = sock_has_perm(sock->sk, SOCKET__WRITE); + if (rc) + return rc; + + if (addr && (msg->msg_flags & MSG_FASTOPEN) && + (sk_is_tcp(sock->sk) || + (sk_is_inet(sock->sk) && sock->sk->sk_type == SOCK_STREAM && + sock->sk->sk_protocol == IPPROTO_MPTCP))) { + rc = selinux_socket_connect(sock, addr, addrlen); + if (rc) + return rc; + } + + return 0; } static int selinux_socket_recvmsg(struct socket *sock, struct msghdr *msg, From 63a76e3a587c4143e8e24e8a6b0c232fa0676034 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Thu, 25 Jun 2026 13:42:59 +0800 Subject: [PATCH 0365/1054] iio: temperature: Build mlx90635 with CONFIG_MLX90635 drivers/iio/temperature/Kconfig has a dedicated MLX90635 option, but the Makefile currently builds mlx90635.o under CONFIG_MLX90632. This means enabling CONFIG_MLX90635 alone does not carry its provider object into the build, while enabling CONFIG_MLX90632 unexpectedly also builds mlx90635.o. Gate mlx90635.o on the matching generated Kconfig symbol. Fixes: a1d1ba5e1c28 ("iio: temperature: mlx90635 MLX90635 IR Temperature sensor") Cc: stable@vger.kernel.org Signed-off-by: Pengpeng Hou Reviewed-by: Andy Shevchenko Acked-by: Crt Mori Signed-off-by: Jonathan Cameron --- drivers/iio/temperature/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/temperature/Makefile b/drivers/iio/temperature/Makefile index 07d6e65709f7..0850bf691820 100644 --- a/drivers/iio/temperature/Makefile +++ b/drivers/iio/temperature/Makefile @@ -13,7 +13,7 @@ obj-$(CONFIG_MAX31865) += max31865.o obj-$(CONFIG_MCP9600) += mcp9600.o obj-$(CONFIG_MLX90614) += mlx90614.o obj-$(CONFIG_MLX90632) += mlx90632.o -obj-$(CONFIG_MLX90632) += mlx90635.o +obj-$(CONFIG_MLX90635) += mlx90635.o obj-$(CONFIG_TMP006) += tmp006.o obj-$(CONFIG_TMP007) += tmp007.o obj-$(CONFIG_TMP117) += tmp117.o From 7dc4de2aa6316f1d044cde21f5acfec5f3ec6b47 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Thu, 25 Jun 2026 13:44:07 +0800 Subject: [PATCH 0366/1054] iio: adc: ti-ads124s08: Return reset GPIO lookup errors devm_gpiod_get_optional() returns NULL when the optional GPIO is absent, but returns an ERR_PTR when the GPIO provider lookup fails, including probe deferral. Probe currently logs the ERR_PTR case as if the reset GPIO were simply absent and keeps the error pointer in reset_gpio. Later ads124s_reset() treats any non-NULL reset_gpio as a valid descriptor and passes it to gpiod_set_value_cansleep(). Return the lookup error instead of retaining the ERR_PTR. Fixes: e717f8c6dfec ("iio: adc: Add the TI ads124s08 ADC code") Cc: stable@vger.kernel.org Reviewed-by: Joshua Crofts Signed-off-by: Pengpeng Hou Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-ads124s08.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/ti-ads124s08.c b/drivers/iio/adc/ti-ads124s08.c index 8ea1269f74db..57eed8554bd9 100644 --- a/drivers/iio/adc/ti-ads124s08.c +++ b/drivers/iio/adc/ti-ads124s08.c @@ -321,7 +321,8 @@ static int ads124s_probe(struct spi_device *spi) ads124s_priv->reset_gpio = devm_gpiod_get_optional(&spi->dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(ads124s_priv->reset_gpio)) - dev_info(&spi->dev, "Reset GPIO not defined\n"); + return dev_err_probe(&spi->dev, PTR_ERR(ads124s_priv->reset_gpio), + "Failed to get reset GPIO\n"); ads124s_priv->chip_info = &ads124s_chip_info_tbl[spi_id->driver_data]; From ec3304ddfd99adf531244be3a35c77b52583d5d3 Mon Sep 17 00:00:00 2001 From: Lizhi Hou Date: Wed, 1 Jul 2026 08:55:56 -0700 Subject: [PATCH 0367/1054] accel/amdxdna: Fix use-after-free in debug BO command handling When a debug BO command completes, job->drv_cmd may already have been freed. Accessing it from aie2_sched_drvcmd_resp_handler() can result in a use-after-free and memory corruption. Fix this by introducing reference counting for drv_cmd objects and transferring ownership to the job while it is in flight. This ensures that the command remains valid until the completion handler finishes processing it. Fixes: 7ea046838021 ("accel/amdxdna: Support firmware debug buffer") Reviewed-by: Mario Limonciello (AMD) Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/20260701155556.663541-1-lizhi.hou@amd.com --- drivers/accel/amdxdna/aie2_ctx.c | 68 +++++++++++++++++++++-------- drivers/accel/amdxdna/amdxdna_ctx.h | 1 + 2 files changed, 51 insertions(+), 18 deletions(-) diff --git a/drivers/accel/amdxdna/aie2_ctx.c b/drivers/accel/amdxdna/aie2_ctx.c index e9fbd8c14364..54486960cbf5 100644 --- a/drivers/accel/amdxdna/aie2_ctx.c +++ b/drivers/accel/amdxdna/aie2_ctx.c @@ -59,6 +59,18 @@ static bool aie2_tdr_detect(struct amdxdna_dev *xdna) return false; } +static void aie2_cmd_release(struct kref *ref) +{ + struct amdxdna_drv_cmd *drv_cmd = container_of(ref, struct amdxdna_drv_cmd, refcnt); + + kfree(drv_cmd); +} + +static void aie2_cmd_put(struct amdxdna_drv_cmd *drv_cmd) +{ + kref_put(&drv_cmd->refcnt, aie2_cmd_release); +} + static void aie2_job_release(struct kref *ref) { struct amdxdna_sched_job *job; @@ -70,6 +82,8 @@ static void aie2_job_release(struct kref *ref) wake_up(&job->hwctx->priv->job_free_wq); if (job->out_fence) dma_fence_put(job->out_fence); + if (job->drv_cmd) + aie2_cmd_put(job->drv_cmd); kfree(job->aie2_job_health); kfree(job); } @@ -901,7 +915,7 @@ static int aie2_hwctx_cfg_debug_bo(struct amdxdna_hwctx *hwctx, u32 bo_hdl, { struct amdxdna_client *client = hwctx->client; struct amdxdna_dev *xdna = client->xdna; - struct amdxdna_drv_cmd cmd = { 0 }; + struct amdxdna_drv_cmd *cmd; struct amdxdna_gem_obj *abo; u64 seq; int ret; @@ -912,32 +926,39 @@ static int aie2_hwctx_cfg_debug_bo(struct amdxdna_hwctx *hwctx, u32 bo_hdl, return -EINVAL; } + cmd = kzalloc_obj(*cmd); + if (!cmd) { + ret = -ENOMEM; + goto put_obj; + } + kref_init(&cmd->refcnt); + if (attach) { if (abo->assigned_hwctx != AMDXDNA_INVALID_CTX_HANDLE) { ret = -EBUSY; - goto put_obj; + goto put_cmd; } - cmd.opcode = ATTACH_DEBUG_BO; + cmd->opcode = ATTACH_DEBUG_BO; } else { if (abo->assigned_hwctx != hwctx->id) { ret = -EINVAL; - goto put_obj; + goto put_cmd; } - cmd.opcode = DETACH_DEBUG_BO; + cmd->opcode = DETACH_DEBUG_BO; } - ret = amdxdna_cmd_submit(client, &cmd, AMDXDNA_INVALID_BO_HANDLE, + ret = amdxdna_cmd_submit(client, cmd, AMDXDNA_INVALID_BO_HANDLE, &bo_hdl, 1, hwctx->id, &seq); if (ret) { XDNA_ERR(xdna, "Submit command failed"); - goto put_obj; + goto put_cmd; } aie2_cmd_wait(hwctx, seq); - if (cmd.result) { - XDNA_ERR(xdna, "Response failure 0x%x", cmd.result); + if (cmd->result) { + XDNA_ERR(xdna, "Response failure 0x%x", cmd->result); ret = -EINVAL; - goto put_obj; + goto put_cmd; } if (attach) @@ -947,6 +968,8 @@ static int aie2_hwctx_cfg_debug_bo(struct amdxdna_hwctx *hwctx, u32 bo_hdl, XDNA_DBG(xdna, "Config debug BO %d to %s", bo_hdl, hwctx->name); +put_cmd: + aie2_cmd_put(cmd); put_obj: amdxdna_gem_put_obj(abo); return ret; @@ -974,25 +997,32 @@ int aie2_hwctx_sync_debug_bo(struct amdxdna_hwctx *hwctx, u32 debug_bo_hdl) { struct amdxdna_client *client = hwctx->client; struct amdxdna_dev *xdna = client->xdna; - struct amdxdna_drv_cmd cmd = { 0 }; + struct amdxdna_drv_cmd *cmd; u64 seq; int ret; - cmd.opcode = SYNC_DEBUG_BO; - ret = amdxdna_cmd_submit(client, &cmd, AMDXDNA_INVALID_BO_HANDLE, + cmd = kzalloc_obj(*cmd); + if (!cmd) + return -ENOMEM; + kref_init(&cmd->refcnt); + + cmd->opcode = SYNC_DEBUG_BO; + ret = amdxdna_cmd_submit(client, cmd, AMDXDNA_INVALID_BO_HANDLE, &debug_bo_hdl, 1, hwctx->id, &seq); if (ret) { XDNA_ERR(xdna, "Submit command failed"); - return ret; + goto put_cmd; } aie2_cmd_wait(hwctx, seq); - if (cmd.result) { - XDNA_ERR(xdna, "Response failure 0x%x", cmd.result); - return -EINVAL; + if (cmd->result) { + XDNA_ERR(xdna, "Response failure 0x%x", cmd->result); + ret = -EINVAL; } - return 0; +put_cmd: + aie2_cmd_put(cmd); + return ret; } static int aie2_populate_range(struct amdxdna_gem_obj *abo) @@ -1142,6 +1172,8 @@ int aie2_cmd_submit(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job, dma_resv_add_fence(job->bos[i]->resv, job->out_fence, DMA_RESV_USAGE_WRITE); job->seq = hwctx->priv->seq++; kref_get(&job->refcnt); + if (job->drv_cmd) + kref_get(&job->drv_cmd->refcnt); drm_sched_entity_push_job(&job->base); *seq = job->seq; diff --git a/drivers/accel/amdxdna/amdxdna_ctx.h b/drivers/accel/amdxdna/amdxdna_ctx.h index aaae16430466..b6bef3af7dab 100644 --- a/drivers/accel/amdxdna/amdxdna_ctx.h +++ b/drivers/accel/amdxdna/amdxdna_ctx.h @@ -132,6 +132,7 @@ enum amdxdna_job_opcode { struct amdxdna_drv_cmd { enum amdxdna_job_opcode opcode; u32 result; + struct kref refcnt; }; struct app_health_report; From 56acfeb10019e200ab6787d01f8d7cbe0f01526f Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Thu, 25 Jun 2026 23:53:36 +0000 Subject: [PATCH 0368/1054] selinux: avoid sk_socket dereference in selinux_sctp_bind_connect() selinux_sctp_bind_connect() dereferences sk->sk_socket to pass a struct socket * to selinux_socket_bind() and selinux_socket_connect_helper(). However, when the hook is invoked from the ASCONF softirq path (sctp_process_asconf), there is no file reference guaranteeing that sk->sk_socket is non-NULL. The setsockopt callers (bindx, connectx, set_primary, sendmsg connect) hold a file reference and are not affected. Both selinux_socket_bind() and selinux_socket_connect_helper() immediately resolve sock->sk, never using the struct socket * for anything else. Refactor the inner logic into helpers that take a struct sock * directly so that selinux_sctp_bind_connect() never needs to touch sk->sk_socket at all. Cc: stable@vger.kernel.org Fixes: d452930fd3b9 ("selinux: Add SCTP support") Suggested-by: Stephen Smalley Signed-off-by: Tristan Madani Reviewed-by: Stephen Smalley Tested-by: Stephen Smalley Signed-off-by: Paul Moore --- security/selinux/hooks.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index c4677ce89399..70a3388c047d 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -4994,9 +4994,8 @@ static int selinux_socket_socketpair(struct socket *socka, Need to determine whether we should perform a name_bind permission check between the socket and the port number. */ -static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen) +static int __selinux_socket_bind(struct sock *sk, struct sockaddr *address, int addrlen) { - struct sock *sk = sock->sk; struct sk_security_struct *sksec = selinux_sock(sk); u16 family; int err; @@ -5126,13 +5125,17 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in return -EAFNOSUPPORT; } +static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen) +{ + return __selinux_socket_bind(sock->sk, address, addrlen); +} + /* This supports connect(2) and SCTP connect services such as sctp_connectx(3) * and sctp_sendmsg(3) as described in Documentation/security/SCTP.rst */ -static int selinux_socket_connect_helper(struct socket *sock, +static int selinux_socket_connect_helper(struct sock *sk, struct sockaddr *address, int addrlen) { - struct sock *sk = sock->sk; struct sk_security_struct *sksec = selinux_sock(sk); int err; @@ -5221,7 +5224,7 @@ static int selinux_socket_connect(struct socket *sock, int err; struct sock *sk = sock->sk; - err = selinux_socket_connect_helper(sock, address, addrlen); + err = selinux_socket_connect_helper(sk, address, addrlen); if (err) return err; @@ -5723,13 +5726,11 @@ static int selinux_sctp_bind_connect(struct sock *sk, int optname, int len, err = 0, walk_size = 0; void *addr_buf; struct sockaddr *addr; - struct socket *sock; if (!selinux_policycap_extsockclass()) return 0; /* Process one or more addresses that may be IPv4 or IPv6 */ - sock = sk->sk_socket; addr_buf = address; while (walk_size < addrlen) { @@ -5758,14 +5759,14 @@ static int selinux_sctp_bind_connect(struct sock *sk, int optname, case SCTP_PRIMARY_ADDR: case SCTP_SET_PEER_PRIMARY_ADDR: case SCTP_SOCKOPT_BINDX_ADD: - err = selinux_socket_bind(sock, addr, len); + err = __selinux_socket_bind(sk, addr, len); break; /* Connect checks */ case SCTP_SOCKOPT_CONNECTX: case SCTP_PARAM_SET_PRIMARY: case SCTP_PARAM_ADD_IP: case SCTP_SENDMSG_CONNECT: - err = selinux_socket_connect_helper(sock, addr, len); + err = selinux_socket_connect_helper(sk, addr, len); if (err) return err; From 1e56f30a73f304fe26a272742c398aedd88a1a6c Mon Sep 17 00:00:00 2001 From: Guzebing Date: Sun, 21 Jun 2026 09:40:30 +0800 Subject: [PATCH 0369/1054] block: Make WBT latency writes honor enable state queue/wbt_lat_usec controls both the stored WBT latency target and the effective WBT enable state. The old no-op check skipped updates whenever the converted latency matched the stored min_lat_nsec. That check ignored whether the current WBT state already matched the state requested by the write. For a queue disabled by default, attempting to enable WBT by writing the default value through sysfs could return success while the enable state was left unchanged. Treat a write as a no-op only when both the stored latency and the effective WBT enabled state already match the converted value. Signed-off-by: Guzebing Link: https://patch.msgid.link/20260621014030.1625306-1-guzebing1612@gmail.com Signed-off-by: Jens Axboe --- block/blk-wbt.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/block/blk-wbt.c b/block/blk-wbt.c index dcc2438ca16d..953d400fd013 100644 --- a/block/blk-wbt.c +++ b/block/blk-wbt.c @@ -813,6 +813,21 @@ static void wbt_queue_depth_changed(struct rq_qos *rqos) wbt_update_limits(RQWB(rqos)); } +static bool wbt_set_lat_changed(struct request_queue *q, u64 val) +{ + struct rq_qos *rqos = wbt_rq_qos(q); + struct rq_wb *rwb; + + if (!rqos) + return true; + + rwb = RQWB(rqos); + if (rwb->min_lat_nsec != val) + return true; + + return rwb_enabled(rwb) != !!val; +} + static void wbt_exit(struct rq_qos *rqos) { struct rq_wb *rwb = RQWB(rqos); @@ -1005,8 +1020,12 @@ int wbt_set_lat(struct gendisk *disk, s64 val) else if (val >= 0) val *= 1000ULL; - if (wbt_get_min_lat(q) == val) + mutex_lock(&disk->rqos_state_mutex); + if (!wbt_set_lat_changed(q, val)) { + mutex_unlock(&disk->rqos_state_mutex); goto out; + } + mutex_unlock(&disk->rqos_state_mutex); blk_mq_quiesce_queue(q); From 7ad2bcf2441430bb2e918fb3ef9a90d775a6e422 Mon Sep 17 00:00:00 2001 From: Zihan Xi Date: Sun, 28 Jun 2026 17:19:24 +0800 Subject: [PATCH 0370/1054] smb: client: harden POSIX SID length parsing posix_info_sid_size() reads sid[1] to obtain the subauthority count, but its existing boundary check still accepts buffers with only one remaining byte. Require two bytes before reading sid[1] so all client paths that reuse the helper reject truncated POSIX SIDs safely. Fixes: 349e13ad30b4 ("cifs: add smb2 POSIX info level") Cc: stable@vger.kernel.org Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Xin Liu Assisted-by: Codex:gpt-5.4 Signed-off-by: Zihan Xi Signed-off-by: Ren Wei Signed-off-by: Steve French --- fs/smb/client/smb2pdu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index d058584b8f05..77738900e75e 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -5405,7 +5405,7 @@ int posix_info_sid_size(const void *beg, const void *end) size_t subauth; int total; - if (beg + 1 > end) + if (beg + 2 > end) return -1; subauth = *(u8 *)(beg+1); From b86467cd2691192ad4809a5a6e922fc24b8e9839 Mon Sep 17 00:00:00 2001 From: Zihan Xi Date: Wed, 1 Jul 2026 18:23:21 +0800 Subject: [PATCH 0371/1054] smb: client: use unaligned reads in parse_posix_ctxt() The server controls create-context DataOffset, so the POSIX context data pointer may be misaligned on strict-alignment architectures. Use get_unaligned_le32() when reading nlink, reparse_tag, and mode. Fixes: 69dda3059e7a ("cifs: add SMB2_open() arg to return POSIX data") Cc: stable@vger.kernel.org Signed-off-by: Zihan Xi Signed-off-by: Ren Wei Signed-off-by: Steve French --- fs/smb/client/smb2pdu.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index 77738900e75e..95c0efe9d43b 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -2396,9 +2396,9 @@ parse_posix_ctxt(struct create_context *cc, struct smb2_file_all_info *info, memset(posix, 0, sizeof(*posix)); - posix->nlink = le32_to_cpu(*(__le32 *)(beg + 0)); - posix->reparse_tag = le32_to_cpu(*(__le32 *)(beg + 4)); - posix->mode = le32_to_cpu(*(__le32 *)(beg + 8)); + posix->nlink = get_unaligned_le32(beg); + posix->reparse_tag = get_unaligned_le32(beg + 4); + posix->mode = get_unaligned_le32(beg + 8); sid = beg + 12; sid_len = posix_info_sid_size(sid, end); From 1b7a6da1d617876fbccd98da9bf1c2368e4f9424 Mon Sep 17 00:00:00 2001 From: Steve French Date: Thu, 18 Jun 2026 21:23:06 -0500 Subject: [PATCH 0372/1054] cifs: update internal module version number to 2.60 Signed-off-by: Steve French --- fs/smb/client/cifsfs.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/smb/client/cifsfs.h b/fs/smb/client/cifsfs.h index 901e1340c986..854e672a4e37 100644 --- a/fs/smb/client/cifsfs.h +++ b/fs/smb/client/cifsfs.h @@ -166,6 +166,6 @@ extern const struct export_operations cifs_export_ops; #endif /* CONFIG_CIFS_NFSD_EXPORT */ /* when changing internal version - update following two lines at same time */ -#define SMB3_PRODUCT_BUILD 60 -#define CIFS_VERSION "2.60" +#define SMB3_PRODUCT_BUILD 61 +#define CIFS_VERSION "2.61" #endif /* _CIFSFS_H */ From 4b0363cb1f3ec42b0b1346e5ab0b8a3dceeee9be Mon Sep 17 00:00:00 2001 From: Sarthak Sharma Date: Mon, 8 Jun 2026 16:02:24 +0530 Subject: [PATCH 0373/1054] selftests/mm: fix ksft_process_madv.sh test category ksft_process_madv.sh currently runs run_vmtests.sh with the mmap category. Update it to run the process_madv category, since ksft_mmap.sh already runs the mmap category tests. This avoids running mmap tests twice and ensures that process_madv tests are run through the kselftest harness. Link: https://lore.kernel.org/20260608103224.344101-1-sarthak.sharma@arm.com Fixes: 6ce964c02f1c ("selftests/mm: have the harness run each test category separately") Signed-off-by: Sarthak Sharma Reviewed-by: Mark Brown Reviewed-by: Dev Jain Acked-by: David Hildenbrand (Arm) Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Mark Brown Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/ksft_process_madv.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/mm/ksft_process_madv.sh b/tools/testing/selftests/mm/ksft_process_madv.sh index 2c3137ae8bc8..edad2d2d888f 100755 --- a/tools/testing/selftests/mm/ksft_process_madv.sh +++ b/tools/testing/selftests/mm/ksft_process_madv.sh @@ -1,4 +1,4 @@ #!/bin/sh -e # SPDX-License-Identifier: GPL-2.0 -./run_vmtests.sh -t mmap +./run_vmtests.sh -t process_madv From 65476d31d8056e859c48580f82295ce159196ffe Mon Sep 17 00:00:00 2001 From: Qi Zheng Date: Wed, 17 Jun 2026 16:56:58 +0800 Subject: [PATCH 0374/1054] mm: shrinker: fix shrinker_info teardown race with expansion expand_shrinker_info() iterates all visible memcgs under shrinker_mutex, including memcgs that have not finished ->css_online() yet. Once pn->shrinker_info has been published, teardown must stay serialized with expand_shrinker_info() until that memcg is either fully online or no longer visible to iteration. Today alloc_shrinker_info() breaks that rule by dropping shrinker_mutex before freeing a partially initialized shrinker_info array, which may cause the following race: CPU0 CPU1 ==== ==== css_create --> list_add_tail_rcu(&css->sibling, &parent_css->children); online_css --> mem_cgroup_css_online --> alloc_shrinker_info --> alloc node0 info rcu_assign_pointer(C->node0->shrinker_info, old0) alloc node1 info -> FAIL -> goto err mutex_unlock(shrinker_mutex) shrinker_alloc() --> shrinker_memcg_alloc --> mutex_lock(shrinker_mutex) expand_shrinker_info --> mem_cgroup_iter see the memcg expand_one_shrinker_info --> old0 = C->node0->shrinker_info memcpy(new->unit, old0->unit, ...); free_shrinker_info --> kvfree(old0); /* double free !! */ kvfree_rcu(old0, rcu); The same problem exists later in mem_cgroup_css_online(). If alloc_shrinker_info() succeeds but a subsequent objcg allocation fails, the free_objcg -> free_shrinker_info() unwind path tears down the already published pn->shrinker_info arrays without shrinker_mutex. The expand_one_shrinker_info() can race with that teardown in the same way, leading to use-after-free or double-free of the old shrinker_info. Fix this by serializing shrinker_info teardown with shrinker_mutex, and by keeping alloc_shrinker_info() error cleanup inside the locked section. Link: https://lore.kernel.org/20260617085658.27096-1-qi.zheng@linux.dev Fixes: 307bececcd12 ("mm: shrinker: add a secondary array for shrinker_info::{map, nr_deferred}") Signed-off-by: Qi Zheng Acked-by: Muchun Song Cc: Dave Chinner Cc: Qi Zheng Cc: Roman Gushchin Cc: Signed-off-by: Andrew Morton --- mm/shrinker.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/mm/shrinker.c b/mm/shrinker.c index 7082d01c8c9d..a70aab124a0e 100644 --- a/mm/shrinker.c +++ b/mm/shrinker.c @@ -59,12 +59,14 @@ static inline int shrinker_unit_alloc(struct shrinker_info *new, return 0; } -void free_shrinker_info(struct mem_cgroup *memcg) +static void __free_shrinker_info(struct mem_cgroup *memcg) { struct mem_cgroup_per_node *pn; struct shrinker_info *info; int nid; + lockdep_assert_held(&shrinker_mutex); + for_each_node(nid) { pn = memcg->nodeinfo[nid]; info = rcu_dereference_protected(pn->shrinker_info, true); @@ -74,6 +76,13 @@ void free_shrinker_info(struct mem_cgroup *memcg) } } +void free_shrinker_info(struct mem_cgroup *memcg) +{ + mutex_lock(&shrinker_mutex); + __free_shrinker_info(memcg); + mutex_unlock(&shrinker_mutex); +} + int alloc_shrinker_info(struct mem_cgroup *memcg) { int nid, ret = 0; @@ -98,8 +107,8 @@ int alloc_shrinker_info(struct mem_cgroup *memcg) return ret; err: + __free_shrinker_info(memcg); mutex_unlock(&shrinker_mutex); - free_shrinker_info(memcg); return -ENOMEM; } From e30453c61e185e914fde83c650e268067b140218 Mon Sep 17 00:00:00 2001 From: Qi Zheng Date: Wed, 17 Jun 2026 17:00:52 +0800 Subject: [PATCH 0375/1054] mm: shrinker: fix NULL pointer dereference in debugfs shrinker_debugfs_add() creates both "count" and "scan" debugfs files unconditionally. That assumes every shrinker implements both count_objects() and scan_objects(), which is not guaranteed. For example, the xen-backend shrinker sets count_objects() but leaves scan_objects() NULL, so writing to its scan file calls through a NULL function pointer and panics the kernel: BUG: kernel NULL pointer dereference, address: 0000000000000000 RIP: 0010:0x0 Code: Unable to access opcode bytes at 0xffffffffffffffd6. Call Trace: shrinker_debugfs_scan_write+0x12e/0x270 full_proxy_write+0x5f/0x90 vfs_write+0xde/0x420 ? filp_flush+0x75/0x90 ? filp_close+0x1d/0x30 ? do_dup2+0xb8/0x120 ksys_write+0x68/0xf0 ? filp_flush+0x75/0x90 do_syscall_64+0xb3/0x5b0 entry_SYSCALL_64_after_hwframe+0x76/0x7e The count path has the same issue in principle if a shrinker omits count_objects(). To fix it, only create "count" and "scan" debugfs files when the corresponding callbacks are present. Link: https://lore.kernel.org/20260617090052.27325-1-qi.zheng@linux.dev Fixes: bbf535fd6f06 ("mm: shrinkers: add scan interface for shrinker debugfs") Signed-off-by: Qi Zheng Reviewed-by: Muchun Song Cc: Dave Chinner Cc: Qi Zheng Cc: Roman Gushchin Cc: Signed-off-by: Andrew Morton --- mm/shrinker_debug.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/mm/shrinker_debug.c b/mm/shrinker_debug.c index cda4e86428c8..cafb56630132 100644 --- a/mm/shrinker_debug.c +++ b/mm/shrinker_debug.c @@ -183,10 +183,12 @@ int shrinker_debugfs_add(struct shrinker *shrinker) } shrinker->debugfs_entry = entry; - debugfs_create_file("count", 0440, entry, shrinker, - &shrinker_debugfs_count_fops); - debugfs_create_file("scan", 0220, entry, shrinker, - &shrinker_debugfs_scan_fops); + if (shrinker->count_objects) + debugfs_create_file("count", 0440, entry, shrinker, + &shrinker_debugfs_count_fops); + if (shrinker->scan_objects) + debugfs_create_file("scan", 0220, entry, shrinker, + &shrinker_debugfs_scan_fops); return 0; } From d58fdbe37a829fd2e5803dd4e5a72992dd8c5368 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Wed, 17 Jun 2026 17:56:47 -0700 Subject: [PATCH 0376/1054] mm/damon/sysfs-schemes: fix dir put orders in access_pattern_add_dirs() Patch series "mm/damon/sysfs-schemes: fix wrong directories put orders in error paths". Error paths of damon_sysfs_access_pattern_add_dirs() and damon_sysfs_scheme_add_dirs() functions put references to directories in wrong orders. As a result, uninitialized memory dereference and/or memory leak can happen. Fix those. This patch (of 2): In access_pattern_add_dirs(), error handling path puts references starting from setup failed directories. If the failure happpened from the initial allication in the setup functions, uninitialized memory dereference happen. The allocation failures will not commonly happen, but the consequence is quite bad. Fix the wrong reference put orders. The issue was discovered [1] by Sashiko. Link: https://lore.kernel.org/20260618005650.83868-2-sj@kernel.org Link: https://lore.kernel.org/20260617060005.86852-1-sj@kernel.org [1] Fixes: 7e84b1f8212a ("mm/damon/sysfs: support DAMON-based Operation Schemes") Signed-off-by: SeongJae Park Cc: # 5.18.x Signed-off-by: Andrew Morton --- mm/damon/sysfs-schemes.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/mm/damon/sysfs-schemes.c b/mm/damon/sysfs-schemes.c index 329cfd0bbe9f..7c00aa78b2f5 100644 --- a/mm/damon/sysfs-schemes.c +++ b/mm/damon/sysfs-schemes.c @@ -1993,22 +1993,19 @@ static int damon_sysfs_access_pattern_add_dirs( err = damon_sysfs_access_pattern_add_range_dir(access_pattern, &access_pattern->sz, "sz"); if (err) - goto put_sz_out; + return err; err = damon_sysfs_access_pattern_add_range_dir(access_pattern, &access_pattern->nr_accesses, "nr_accesses"); if (err) - goto put_nr_accesses_sz_out; + goto put_sz_out; err = damon_sysfs_access_pattern_add_range_dir(access_pattern, &access_pattern->age, "age"); if (err) - goto put_age_nr_accesses_sz_out; + goto put_nr_accesses_sz_out; return 0; -put_age_nr_accesses_sz_out: - kobject_put(&access_pattern->age->kobj); - access_pattern->age = NULL; put_nr_accesses_sz_out: kobject_put(&access_pattern->nr_accesses->kobj); access_pattern->nr_accesses = NULL; From 05ea83ee88ca70f8932906d9f2617ff996f45b50 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Wed, 17 Jun 2026 17:56:48 -0700 Subject: [PATCH 0377/1054] mm/damon/sysfs-schemes: put stats for scheme_add_dirs() internal error damon_sysfs_scheme_add_dirs() setup the tried_regions directory after the stats directory setup is completed. When the tried_regions directory setup is failed, the setup function ensures the reference for the tried regions directory is released. Hence the error path should put references on setup succeeded directory objects, starting from the stats directory. However, the error path is putting the tried_regions directory instead of the stats directory. As a direct result, the stats directory object is leaked. Worse yet, if the tried_regions directory setup failed from the initial allocation, the scheme->tried_regions field remains uninitialized. The following kobject_put(&scheme->tried_regions->kobj) call in the error path will dereference the uninitialized memory. The setup failures should not be common. But once it happens, the consequence is quite bad. Fix this issue by correctly putting the stats directory instead of the tried_regions directory. The issue was discovered [1] by Sashiko. Link: https://lore.kernel.org/20260618005650.83868-3-sj@kernel.org Link: https://lore.kernel.org/20260617005223.96813-1-sj@kernel.org [1] Fixes: 5181b75f438d ("mm/damon/sysfs-schemes: implement schemes/tried_regions directory") Signed-off-by: SeongJae Park Cc: # 6.2.x Signed-off-by: Andrew Morton --- mm/damon/sysfs-schemes.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mm/damon/sysfs-schemes.c b/mm/damon/sysfs-schemes.c index 7c00aa78b2f5..0134111c3c1f 100644 --- a/mm/damon/sysfs-schemes.c +++ b/mm/damon/sysfs-schemes.c @@ -2513,12 +2513,12 @@ static int damon_sysfs_scheme_add_dirs(struct damon_sysfs_scheme *scheme) goto put_filters_watermarks_quotas_access_pattern_out; err = damon_sysfs_scheme_set_tried_regions(scheme); if (err) - goto put_tried_regions_out; + goto put_stats_out; return 0; -put_tried_regions_out: - kobject_put(&scheme->tried_regions->kobj); - scheme->tried_regions = NULL; +put_stats_out: + kobject_put(&scheme->stats->kobj); + scheme->stats = NULL; put_filters_watermarks_quotas_access_pattern_out: kobject_put(&scheme->ops_filters->kobj); scheme->ops_filters = NULL; From 7da7d599b8a83271c464adfd5ef160202b470570 Mon Sep 17 00:00:00 2001 From: Zi Yan Date: Mon, 22 Jun 2026 11:30:42 -0400 Subject: [PATCH 0378/1054] mm/compaction: handle free_pages_prepare() properly in compaction_free() free_pages_prepare() can fail but compaction_free() does not handle the failure case. Failed pages should not be added back to cc->freepages for future use, since they can be either PageHWPoison or free_page_is_bad() and might cause data corruption. Link: https://lore.kernel.org/20260622-handle_free_pages_prepare_in_compaction_free-v1-1-fcf3b14abcf7@nvidia.com Fixes: 733aea0b3a7b ("mm/compaction: add support for >0 order folio memory compaction.") Signed-off-by: Zi Yan Reviewed-by: Vlastimil Babka (SUSE) Acked-by: Johannes Weiner Reviewed-by: Baolin Wang Reviewed-by: Lance Yang Cc: Brendan Jackman Cc: Jiaqi Yan Cc: Michal Hocko Cc: Suren Baghdasaryan Cc: Signed-off-by: Andrew Morton --- mm/compaction.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/mm/compaction.c b/mm/compaction.c index b776f35ad020..f08765ade014 100644 --- a/mm/compaction.c +++ b/mm/compaction.c @@ -1875,15 +1875,14 @@ static void compaction_free(struct folio *dst, unsigned long data) int order = folio_order(dst); struct page *page = &dst->page; - if (folio_put_testzero(dst)) { - free_pages_prepare(page, order); + if (folio_put_testzero(dst) && free_pages_prepare(page, order)) { list_add(&dst->lru, &cc->freepages[order]); cc->nr_freepages += 1 << order; } cc->nr_migratepages += 1 << order; /* - * someone else has referenced the page, we cannot take it back to our - * free list. + * someone else has referenced the page or free_pages_prepare() fails, + * we cannot take it back to our free list. */ } From 1cb6cf6f2b38d56f9e5e9e7c80c5d482c51874f3 Mon Sep 17 00:00:00 2001 From: Lorenzo Stoakes Date: Mon, 22 Jun 2026 16:59:13 +0100 Subject: [PATCH 0379/1054] MAINTAINERS: add Lance as an rmap reviewer Lance has been doing excellent work reviewing rmap series and has proven himself to be a great member of the community in general, so add him as an rmap reviewer. Link: https://lore.kernel.org/20260622155913.280355-1-ljs@kernel.org Signed-off-by: Lorenzo Stoakes Acked-by: Vlastimil Babka (SUSE) Acked-by: David Hildenbrand (Arm) Acked-by: SeongJae Park Acked-by: Harry Yoo (Oracle) Acked-by: Dev Jain Acked-by: Barry Song Acked-by: Lance Yang Cc: Jann Horn Cc: Liam R. Howlett Cc: Rik van Riel Signed-off-by: Andrew Morton --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 15011f5752a9..eed632c30e01 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17205,6 +17205,7 @@ R: Liam R. Howlett R: Vlastimil Babka R: Harry Yoo R: Jann Horn +R: Lance Yang L: linux-mm@kvack.org S: Maintained F: include/linux/rmap.h From 35d4a3cf70a855b50e53189ac2f8463e20a02046 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Tue, 23 Jun 2026 06:58:31 -0700 Subject: [PATCH 0380/1054] mm/damon/ops-common: handle extreme intervals in damon_hot_score() Fix three issues in damon_hot_score() that comes from wrong handling of extreme (zero or too high) monitoring intervals user setup. When the user sets sampling interval zero, damon_max_nr_accesses(), which is called from damon_hot_score(), causes a divide-by-zero. Needless to say, it is a problem. When the user sets the aggregation interval zero, the function returns zero. It is wrong, since the real maximum nr_acceses in the setup should be one. Worse yet, it can cause another divide-by-zero from its caller, damon_hot_score(), since it uses damon_max_nr_accesses() return value as a denominator. When the user sets the aggregation interval very high, damon_hot_score() could return a value out of [0, DAMOS_MAX_SCORE] range. Since the return value is used as an index to the regions_score_histogram array, which is DAMOS_MAX_SCORE+1 size, it causes out of bounds array access. The issues can be relatively easily reproduced like below. The sysfs write permission is required, though. # ./damo start --damos_action lru_prio --damos_quota_space 100M \ --damos_quota_interval 1s # cd /sys/kernel/mm/damon/admin/kdamonds/0 # echo 0 > contexts/0/monitoring_attrs/intervals/sample_us # echo 0 > contexts/0/monitoring_attrs/intervals/aggr_us # echo commit > state # dmesg [...] [ 131.329762] Oops: divide error: 0000 [#1] SMP NOPTI [...] [ 131.336089] RIP: 0010:damon_hot_score+0x27/0xd0 [...] Fix the divide-by-zero intervals problems by explicitly handling the zero intervals in damon_max_nr_accesses(). Fix the out-of-bound array access by applying [0, DAMOS_MAX_SCORE] bounds before returning from damon_hot_score(). The issue was discovered [1] by Sashiko. Link: https://lore.kernel.org/20260623135834.67189-1-sj@kernel.org Link: https://lore.kernel.org/20260619202459.145010-1-sj@kernel.org [1] Fixes: 198f0f4c58b9 ("mm/damon/vaddr,paddr: support pageout prioritization") Signed-off-by: SeongJae Park Cc: # 5.16.x Signed-off-by: Andrew Morton --- include/linux/damon.h | 8 ++++++-- mm/damon/ops-common.c | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/include/linux/damon.h b/include/linux/damon.h index 6f7edb3590ef..888570f55b41 100644 --- a/include/linux/damon.h +++ b/include/linux/damon.h @@ -1065,9 +1065,13 @@ static inline bool damon_target_has_pid(const struct damon_ctx *ctx) static inline unsigned int damon_max_nr_accesses(const struct damon_attrs *attrs) { - /* {aggr,sample}_interval are unsigned long, hence could overflow */ - return min(attrs->aggr_interval / attrs->sample_interval, + unsigned long sample_interval; + unsigned long max_nr_accesses; + + sample_interval = attrs->sample_interval ? : 1; + max_nr_accesses = min(attrs->aggr_interval / sample_interval, (unsigned long)UINT_MAX); + return max_nr_accesses ? : 1; } diff --git a/mm/damon/ops-common.c b/mm/damon/ops-common.c index 5c93ef2bb8a9..d1842e2b00ef 100644 --- a/mm/damon/ops-common.c +++ b/mm/damon/ops-common.c @@ -143,6 +143,7 @@ int damon_hot_score(struct damon_ctx *c, struct damon_region *r, * Transform it to fit in [0, DAMOS_MAX_SCORE] */ hotness = hotness * DAMOS_MAX_SCORE / DAMON_MAX_SUBSCORE; + hotness = max(min(hotness, DAMOS_MAX_SCORE), 0); return hotness; } From ffd017237cfe99e6e5602ab14179b0e6878a0840 Mon Sep 17 00:00:00 2001 From: Ketan Date: Tue, 23 Jun 2026 02:48:04 +0530 Subject: [PATCH 0381/1054] mm: page_ext: add count limit to page_ext_iter_next to prevent invalid PFN access The page_ext iteration API does not validate if the PFN still belongs to a valid section while advancing the iterator. When dynamically adding memory in the hotplug path, it can lead to a NULL pointer dereference during page_ext_lookup at the boundary of the last valid section when iterator count equals __pgcount. The for_each_page_ext() macro calls page_ext_iter_next() as its loop increment. for_each_page_ext() does a "__page_ext = page_ext_iter_next(&__iter)" at the end. This causes page_ext_iter_next() to increment iter->index past __pgcount and call page_ext_lookup(start_pfn + __pgcount). During memory hotplug (online), the PFN at start_pfn + __pgcount may belong to a section that has not yet been initialized, causing page_ext_lookup() to trigger a NULL pointer dereference. [ 14.555124][ T846] Call trace: [ 14.555125][ T846] lookup_page_ext+0x6c/0x108 (P) [ 14.555127][ T846] page_ext_lookup+0x30/0x3c [ 14.555129][ T846] __reset_page_owner+0x11c/0x260 [ 14.571201][ T846] __free_pages_ok+0x5e8/0x8e0 [ 14.571204][ T846] __free_pages_core+0x78/0xf0 [ 14.571206][ T846] generic_online_page+0x14/0x24 [ 14.597782][ T846] online_pages+0x178/0x30c [ 14.597784][ T846] memory_block_change_state+0x284/0x32c [ 14.597787][ T846] memory_subsys_online+0x4c/0x64 [ 14.597789][ T846] device_online+0x88/0xb0 [ 14.597791][ T846] online_memory_block+0x30/0x40 [ 14.597793][ T846] walk_memory_blocks+0xac/0xe8 [ 14.597794][ T846] add_memory_resource+0x280/0x298 [ 14.656161][ T846] add_memory+0x60/0x98 Move the iteration boundary enforcement inside the iterator functions, so callers cannot inadvertently access beyond the requested range. Link: https://lore.kernel.org/20260623-page_ext-v3-1-a89799a5367c@oss.qualcomm.com Fixes: 9039b9096ea2 ("mm: page_ext: add an iteration API for page extensions") Signed-off-by: Ketan Kishore Suggested-by: David Hildenbrand Suggested-by: Matthew Wilcox Acked-by: Zi Yan Acked-by: David Hildenbrand (Arm) Cc: Brendan Jackman Cc: Johannes Weiner Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Luiz Capitulino Cc: Michal Hocko Cc: Mike Rapoport Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Signed-off-by: Andrew Morton --- include/linux/page_ext.h | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/include/linux/page_ext.h b/include/linux/page_ext.h index 61e876e255e8..f23d4b218da0 100644 --- a/include/linux/page_ext.h +++ b/include/linux/page_ext.h @@ -120,14 +120,18 @@ struct page_ext_iter { * page_ext_iter_begin() - Prepare for iterating through page extensions. * @iter: page extension iterator. * @pfn: PFN of the page we're interested in. + * @count: maximum number of page extensions to return. * * Must be called with RCU read lock taken. * * Return: NULL if no page_ext exists for this page. */ static inline struct page_ext *page_ext_iter_begin(struct page_ext_iter *iter, - unsigned long pfn) + unsigned long pfn, unsigned long count) { + if (!count) + return NULL; + iter->index = 0; iter->start_pfn = pfn; iter->page_ext = page_ext_lookup(pfn); @@ -138,19 +142,22 @@ static inline struct page_ext *page_ext_iter_begin(struct page_ext_iter *iter, /** * page_ext_iter_next() - Get next page extension * @iter: page extension iterator. + * @count: maximum number of page extensions to return. * * Must be called with RCU read lock taken. * * Return: NULL if no next page_ext exists. */ -static inline struct page_ext *page_ext_iter_next(struct page_ext_iter *iter) +static inline struct page_ext *page_ext_iter_next(struct page_ext_iter *iter, + unsigned long count) { unsigned long pfn; if (WARN_ON_ONCE(!iter->page_ext)) return NULL; - iter->index++; + if (++iter->index >= count) + return NULL; pfn = iter->start_pfn + iter->index; if (page_ext_iter_next_fast_possible(pfn)) @@ -183,9 +190,9 @@ static inline struct page_ext *page_ext_iter_get(const struct page_ext_iter *ite * IMPORTANT: must be called with RCU read lock taken. */ #define for_each_page_ext(__page, __pgcount, __page_ext, __iter) \ - for (__page_ext = page_ext_iter_begin(&__iter, page_to_pfn(__page));\ - __page_ext && __iter.index < __pgcount; \ - __page_ext = page_ext_iter_next(&__iter)) + for (__page_ext = page_ext_iter_begin(&__iter, page_to_pfn(__page), __pgcount); \ + __page_ext; \ + __page_ext = page_ext_iter_next(&__iter, __pgcount)) #else /* !CONFIG_PAGE_EXTENSION */ struct page_ext; From 81401cebfc1598306b0a981b5f9ee5b58c1aac52 Mon Sep 17 00:00:00 2001 From: Jinjiang Tu Date: Fri, 26 Jun 2026 09:32:52 +0800 Subject: [PATCH 0382/1054] fs/proc: fix KPF_KSM reported for all anonymous pages Reading /proc/kpageflags for any anonymous page returns KPF_KSM set, even when KSM is not in use. As a result, tools misclassify all anonymous pages as KSM merged. In stable_page_flags(), if the page is anonymous, then use (mapping & FOLIO_MAPPING_KSM) check to identify if the anonymous page is KSM page. However, FOLIO_MAPPING_KSM is FOLIO_MAPPING_ANON | FOLIO_MAPPING_ANON_KSM, (mapping & FOLIO_MAPPING_KSM) check returns true for all anonymous pages. To fix it, use FOLIO_MAPPING_ANON_KSM instead. Link: https://lore.kernel.org/20260629033122.774318-1-tujinjiang@huawei.com Link: https://lore.kernel.org/20260626013252.2846774-1-tujinjiang@huawei.com Fixes: dee3d0bef2b0 ("proc: rewrite stable_page_flags()") Signed-off-by: Jinjiang Tu Acked-by: David Hildenbrand (Arm) Acked-by: Zi Yan Reviewed-by: Xu Xin Cc: Chengming Zhou Cc: Kefeng Wang Cc: Luiz Capitulino Cc: Matthew Wilcox (Oracle) Cc: Miaohe Lin Cc: Nanyong Sun Cc: Svetly Todorov Cc: Signed-off-by: Andrew Morton --- fs/proc/page.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/proc/page.c b/fs/proc/page.c index f9b2c2c906cd..7d9387143435 100644 --- a/fs/proc/page.c +++ b/fs/proc/page.c @@ -173,7 +173,7 @@ u64 stable_page_flags(const struct page *page) u |= 1 << KPF_MMAP; if (is_anon) { u |= 1 << KPF_ANON; - if (mapping & FOLIO_MAPPING_KSM) + if ((mapping & FOLIO_MAPPING_FLAGS) == FOLIO_MAPPING_KSM) u |= 1 << KPF_KSM; } From dccf636bf1e68c3fda92f0c9e1018ab7e0ac8b2c Mon Sep 17 00:00:00 2001 From: Zenghui Yu Date: Sun, 28 Jun 2026 18:11:18 +0800 Subject: [PATCH 0383/1054] selftests/mm: pagemap_ioctl: use the correct page size for transact_test() There are several places in transact_test() where we use the hardcoded 0x1000 (4k) as page size, which is not always correct for architectures supporting multiple page sizes. Switch to use the correct page size. Otherwise ./ksft_pagemap.sh on a 16k-page-size arm64 box fails with $ ./ksft_pagemap.sh [...] # ok 96 mprotect_tests Both pages written after remap and mprotect # ok 97 mprotect_tests Clear and make the pages written # Bail out! ioctl failed # # Planned tests != run tests (117 != 97) # # Totals: pass:97 fail:0 xfail:0 xpass:0 skip:0 error:0 # [FAIL] not ok 1 pagemap_ioctl # exit=1 # SUMMARY: PASS=0 SKIP=0 FAIL=1 1..1 Link: https://lore.kernel.org/20260628101118.35861-1-zenghui.yu@linux.dev Fixes: 46fd75d4a3c9 ("selftests: mm: add pagemap ioctl tests") Signed-off-by: Zenghui Yu Cc: Muhammad Usama Anjum Cc: David Hildenbrand Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zenghui Yu Cc: Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/pagemap_ioctl.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/testing/selftests/mm/pagemap_ioctl.c b/tools/testing/selftests/mm/pagemap_ioctl.c index 762306177ad8..6f8971d5b3ce 100644 --- a/tools/testing/selftests/mm/pagemap_ioctl.c +++ b/tools/testing/selftests/mm/pagemap_ioctl.c @@ -1368,7 +1368,7 @@ void *thread_proc(void *mem) ksft_exit_fail_msg("pthread_barrier_wait\n"); for (i = 0; i < access_per_thread; ++i) - __atomic_add_fetch(m + i * (0x1000 / sizeof(*m)), 1, __ATOMIC_SEQ_CST); + __atomic_add_fetch(m + i * (page_size / sizeof(*m)), 1, __ATOMIC_SEQ_CST); ret = pthread_barrier_wait(&end_barrier); if (ret && ret != PTHREAD_BARRIER_SERIAL_THREAD) @@ -1403,15 +1403,15 @@ static void transact_test(int page_size) if (pthread_barrier_init(&end_barrier, NULL, nthreads + 1)) ksft_exit_fail_msg("pthread_barrier_init\n"); - mem = mmap(NULL, 0x1000 * nthreads * pages_per_thread, PROT_READ | PROT_WRITE, + mem = mmap(NULL, page_size * nthreads * pages_per_thread, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); if (mem == MAP_FAILED) ksft_exit_fail_msg("Error mmap %s.\n", strerror(errno)); - wp_init(mem, 0x1000 * nthreads * pages_per_thread); - wp_addr_range(mem, 0x1000 * nthreads * pages_per_thread); + wp_init(mem, page_size * nthreads * pages_per_thread); + wp_addr_range(mem, page_size * nthreads * pages_per_thread); - memset(mem, 0, 0x1000 * nthreads * pages_per_thread); + memset(mem, 0, page_size * nthreads * pages_per_thread); count = get_dirty_pages_reset(mem, nthreads * pages_per_thread, 1, page_size); ksft_test_result(count > 0, "%s count %u\n", __func__, count); @@ -1420,7 +1420,7 @@ static void transact_test(int page_size) finish = 0; for (i = 0; i < nthreads; ++i) - pthread_create(&th, NULL, thread_proc, mem + 0x1000 * i * pages_per_thread); + pthread_create(&th, NULL, thread_proc, mem + page_size * i * pages_per_thread); extra_pages = 0; for (i = 0; i < iter_count; ++i) { From fd5295afae916fb300890875ca53c527537d0c06 Mon Sep 17 00:00:00 2001 From: Zenghui Yu Date: Sun, 28 Jun 2026 22:31:11 +0800 Subject: [PATCH 0384/1054] selftests/mm: hmm-tests: include linux/mman.h to access MADV_COLLAPSE The following compilation error occurs with an old version of glibc due to a recent commit adding MADV_COLLAPSE testing: [root@localhost mm]# getconf GNU_LIBC_VERSION glibc 2.34 [root@localhost mm]# make CC hmm-tests hmm-tests.c: In function 'hmm_migrate_anon_huge_fault': hmm-tests.c:2355:27: error: 'MADV_COLLAPSE' undeclared (first use in this function); did you mean 'MADV_COLD'? 2355 | ret = madvise(map, size, MADV_COLLAPSE); | ^~~~~~~~~~~~~ | MADV_COLD hmm-tests.c:2355:27: note: each undeclared identifier is reported only once for each function it appears in make: *** [../lib.mk:225: /root/code/linux/tools/testing/selftests/mm/hmm-tests] Error 1 Include linux/mman.h (which provides the definition of MADV_COLLAPSE) to fix the build error. Link: https://lore.kernel.org/20260628143111.36863-1-zenghui.yu@linux.dev Fixes: e3d8707358ea ("selftests/mm/hmm-tests: test pagemap reads of PMD device-private entries") Signed-off-by: Zenghui Yu Reviewed-by: Lorenzo Stoakes Reviewed-by: Dev Jain Cc: David Hildenbrand Cc: Jason Gunthorpe Cc: Leon Romanovsky Cc: Liam R. Howlett Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/hmm-tests.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/testing/selftests/mm/hmm-tests.c b/tools/testing/selftests/mm/hmm-tests.c index e4c49699f3f7..2f2b9879d100 100644 --- a/tools/testing/selftests/mm/hmm-tests.c +++ b/tools/testing/selftests/mm/hmm-tests.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include From b9faea04ac04ec81588022c08cda877749291109 Mon Sep 17 00:00:00 2001 From: Radu Rendec Date: Sun, 28 Jun 2026 11:02:03 -0400 Subject: [PATCH 0385/1054] mailmap: add entries for Radu Rendec I have used multiple email addresses for my kernel contributions, and some of them are no longer active. Add all to .mailmap for clarity. Link: https://lore.kernel.org/20260628150203.4105796-1-radu@rendec.net Signed-off-by: Radu Rendec Cc: Jakub Kicinski Signed-off-by: Andrew Morton --- .mailmap | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.mailmap b/.mailmap index 23eb9a4b04f4..be1f61db17a8 100644 --- a/.mailmap +++ b/.mailmap @@ -708,6 +708,10 @@ Qi Zheng Quentin Monnet Quentin Monnet Quentin Perret +Radu Rendec +Radu Rendec +Radu Rendec +Radu Rendec Rae Moar Rafael J. Wysocki Rajeev Nandan From 48a926cb6788afa2a528c2f59cead87f1e3d6e30 Mon Sep 17 00:00:00 2001 From: SJ Park Date: Sun, 28 Jun 2026 15:08:03 -0700 Subject: [PATCH 0386/1054] mm/damon: add a kernel-doc comment for damon_ctx->probes The two fields of damon_ctx struct dont have their kernel-doc comments. That causes kernel document builds to warn. Fix those. This patch (of 2): Fix below document build warning: WARNING: ../include/linux/damon.h:909 struct member 'probes' not described in 'damon_ctx' Link: https://lore.kernel.org/20260628220808.98931-1-sj@kernel.org Link: https://lore.kernel.org/20260628220808.98931-2-sj@kernel.org Fixes: 18c777859f28 ("mm/damon/core: embed damon_probe objects in damon_ctx") Signed-off-by: SJ Park Reported-by: Randy Dunlap Closes: https://lore.kernel.org/4df95955-b255-4e5a-90c4-35db02f3111f@infradead.org Signed-off-by: Andrew Morton --- include/linux/damon.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/damon.h b/include/linux/damon.h index 888570f55b41..fdac40cd55d4 100644 --- a/include/linux/damon.h +++ b/include/linux/damon.h @@ -843,6 +843,7 @@ struct damon_attrs { * including damon_call() and damos_walk(). * * @ops: Set of monitoring operations for given use cases. + * @probes: Head of probes (&damon_probe) list. * @addr_unit: Scale factor for core to ops address conversion. * @min_region_sz: Minimum region size. * @pause: Pause kdamond main loop. From 968a672b99387c75585f5dfa623ac405f3e351e5 Mon Sep 17 00:00:00 2001 From: SJ Park Date: Sun, 28 Jun 2026 15:08:04 -0700 Subject: [PATCH 0387/1054] mm/damon: add a kernel-doc comment for damon_ctx->rnd_state Fix below kernel document build warning: WARNING: ../include/linux/damon.h:909 struct member 'rnd_state' not described in 'damon_ctx' Link: https://lore.kernel.org/20260628220808.98931-3-sj@kernel.org Fixes: 9012c4e647df ("mm/damon: replace damon_rand() with a per-ctx lockless PRNG") Signed-off-by: SJ Park Reported-by: Randy Dunlap Closes: https://lore.kernel.org/4df95955-b255-4e5a-90c4-35db02f3111f@infradead.org Signed-off-by: Andrew Morton --- include/linux/damon.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/damon.h b/include/linux/damon.h index fdac40cd55d4..02ac34537df9 100644 --- a/include/linux/damon.h +++ b/include/linux/damon.h @@ -849,6 +849,7 @@ struct damon_attrs { * @pause: Pause kdamond main loop. * @adaptive_targets: Head of monitoring targets (&damon_target) list. * @schemes: Head of schemes (&damos) list. + * @rnd_state: Per-ctx PRNG state for damon_rand(). */ struct damon_ctx { struct damon_attrs attrs; @@ -906,7 +907,6 @@ struct damon_ctx { struct list_head adaptive_targets; struct list_head schemes; - /* Per-ctx PRNG state for damon_rand(); kdamond is the sole consumer. */ struct rnd_state rnd_state; }; From 45a9591ec5c236d0eb2cf08e540d85392c0df773 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Mon, 29 Jun 2026 15:59:28 +0200 Subject: [PATCH 0388/1054] mm: a second pagecache maintainer As MM is slowly transitioning towards a more distributed maintainership model, we agreed with Matthew that I will be a co-maintainer in case he is not available. Link: https://lore.kernel.org/20260629135927.2586391-2-jack@suse.cz Signed-off-by: Jan Kara Acked-by: David Hildenbrand (Arm) Acked-by: Matthew Wilcox (Oracle) Acked-by: Lorenzo Stoakes Cc: Christian Brauner Signed-off-by: Andrew Morton --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index eed632c30e01..c760f7d889e3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20406,7 +20406,7 @@ F: kernel/padata.c PAGE CACHE M: Matthew Wilcox (Oracle) -R: Jan Kara +M: Jan Kara L: linux-fsdevel@vger.kernel.org L: linux-mm@kvack.org S: Supported From 7746d72c64054976887928d64d2caf25c5a6dcc0 Mon Sep 17 00:00:00 2001 From: Zenghui Yu Date: Mon, 29 Jun 2026 07:44:31 -0700 Subject: [PATCH 0389/1054] samples/damon/mtier: fail early if address range parameters are invalid The comment on top of `struct damon_region` clearly says that For any use case, @ar should be non-zero positive size. which is now verified in damon_verify_new_region() if the kernel is built with DAMON_DEBUG_SANITY. The WARN_ONCE() can be triggered if the mtier sample module is enabled before node{0,1}_{start,end}_addr have been properly initialized, which is obviously not good. ------------[ cut here ]------------ start 0 >= end 0 WARNING: mm/damon/core.c:217 at damon_new_region+0xf4/0x118, CPU#59: bash/341468 Call trace: damon_new_region+0xf4/0x118 (P) damon_set_regions+0xfc/0x3c0 damon_sample_mtier_build_ctx+0xe8/0x3a8 damon_sample_mtier_start+0x1c/0x90 damon_sample_mtier_enable_store+0x98/0xb0 param_attr_store+0xb4/0x128 module_attr_store+0x2c/0x50 sysfs_kf_write+0x58/0x90 kernfs_fop_write_iter+0x16c/0x238 vfs_write+0x2c0/0x370 ksys_write+0x74/0x118 __arm64_sys_write+0x24/0x38 invoke_syscall+0xa8/0x118 el0_svc_common.constprop.0+0x48/0xf0 do_el0_svc+0x24/0x38 el0_svc+0x54/0x370 el0t_64_sync_handler+0xa0/0xe8 el0t_64_sync+0x1ac/0x1b0 ---[ end trace 0000000000000000 ]--- Note that the same issue can happen if detect_node_addresses is true, and node 0 or 1 is memoryless. Fix it together by checking the validity of parameters right before damon_new_region() and fail early if they're invalid. Link: https://lore.kernel.org/20260629144432.133962-1-sj@kernel.org Fixes: 82a08bde3cf7 ("samples/damon: implement a DAMON module for memory tiering") Signed-off-by: Zenghui Yu Signed-off-by: SJ Park Reviewed-by: SJ Park Cc: # 6.16.x Signed-off-by: Andrew Morton --- samples/damon/mtier.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/samples/damon/mtier.c b/samples/damon/mtier.c index eb1143de8df1..3785b0c7ffb1 100644 --- a/samples/damon/mtier.c +++ b/samples/damon/mtier.c @@ -120,6 +120,9 @@ static struct damon_ctx *damon_sample_mtier_build_ctx(bool promote) addr.end = promote ? node1_end_addr : node0_end_addr; } + if (addr.start >= addr.end) + goto free_out; + range.start = addr.start; range.end = addr.end; From e187bc02f8fa4226d62814592cf064ee4557c470 Mon Sep 17 00:00:00 2001 From: Pedro Falcato Date: Thu, 25 Jun 2026 16:38:53 +0100 Subject: [PATCH 0390/1054] mm: do file ownership checks with the proper mount idmap Ever since idmapped mounts were introduced, inode ownership checks (for side-channel protection) in mincore() and madvise(MADV_PAGEOUT) were done against the nop_mnt_idmap, which completely ignores the file's mount's idmap. This results in odd edgecases like: 1) mount/bind-mount with an idmap userA:userB:1 2) userB runs an owner_or_capable() check on file that is owned by userA on-disk/in-memory, but owned by userB after idmap translation 3) owner_or_capable() mysteriously fails as the correct idmap wasn't supplied In the case of mincore/madvise MADV_PAGEOUT, this is usually benign, because file_permission(file, MAY_WRITE) will probably succeed, as it uses the proper idmap internally, but it does not need to be the case on e.g a 0444 file where even the owner itself doesn't have permissions to write to it. Since this is clearly not trivial to get right, introduce a file_owner_or_capable() that can carry the correct semantics, and switch the various users in mm to it. The issue was found by manual code inspection & an off-list discussion with Jan Kara. Link: https://lore.kernel.org/20260625153853.913949-1-pfalcato@suse.de Fixes: 9caccd41541a ("fs: introduce MOUNT_ATTR_IDMAP") Signed-off-by: Pedro Falcato Reviewed-by: Jan Kara Reviewed-by: Christian Brauner (Amutable) Acked-by: David Hildenbrand (Arm) Cc: Al Viro Cc: Jann Horn Cc: Liam R. Howlett Cc: Matthew Wilcox (Oracle) Cc: Vlastimil Babka Cc: Signed-off-by: Andrew Morton --- include/linux/fs.h | 5 +++++ mm/filemap.c | 2 +- mm/madvise.c | 3 +-- mm/mincore.c | 3 +-- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/include/linux/fs.h b/include/linux/fs.h index d10897b3a1e3..50ce731a2b78 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2444,6 +2444,11 @@ static inline struct mnt_idmap *file_mnt_idmap(const struct file *file) return mnt_idmap(file->f_path.mnt); } +static inline bool file_owner_or_capable(const struct file *file) +{ + return inode_owner_or_capable(file_mnt_idmap(file), file_inode(file)); +} + /** * is_idmapped_mnt - check whether a mount is mapped * @mnt: the mount to check diff --git a/mm/filemap.c b/mm/filemap.c index 5af62e6abca5..58eb9d240643 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -4704,7 +4704,7 @@ static inline bool can_do_cachestat(struct file *f) { if (f->f_mode & FMODE_WRITE) return true; - if (inode_owner_or_capable(file_mnt_idmap(f), file_inode(f))) + if (file_owner_or_capable(f)) return true; return file_permission(f, MAY_WRITE) == 0; } diff --git a/mm/madvise.c b/mm/madvise.c index cd9bb077072c..77552b03d318 100644 --- a/mm/madvise.c +++ b/mm/madvise.c @@ -336,8 +336,7 @@ static inline bool can_do_file_pageout(struct vm_area_struct *vma) * otherwise we'd be including shared non-exclusive mappings, which * opens a side channel. */ - return inode_owner_or_capable(&nop_mnt_idmap, - file_inode(vma->vm_file)) || + return file_owner_or_capable(vma->vm_file) || file_permission(vma->vm_file, MAY_WRITE) == 0; } diff --git a/mm/mincore.c b/mm/mincore.c index 296f2e3922b5..c8757c5085bf 100644 --- a/mm/mincore.c +++ b/mm/mincore.c @@ -227,8 +227,7 @@ static inline bool can_do_mincore(struct vm_area_struct *vma) * for writing; otherwise we'd be including shared non-exclusive * mappings, which opens a side channel. */ - return inode_owner_or_capable(&nop_mnt_idmap, - file_inode(vma->vm_file)) || + return file_owner_or_capable(vma->vm_file) || file_permission(vma->vm_file, MAY_WRITE) == 0; } From 74a21a2db6aecff5b01cf6b3a52144dd805d51ff Mon Sep 17 00:00:00 2001 From: Yichong Chen Date: Mon, 29 Jun 2026 10:21:23 +0800 Subject: [PATCH 0391/1054] tools/virtio: add missing compat definitions for vhost_net_test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Patch series "tools: Fix tools/virtio test build", v2. This series fixes build failures hit by: make -C tools/virtio test Patch 1 adds tools/virtio compatibility definitions needed by current virtio headers when building the tools/virtio tests. Patch 2 makes tools/include/linux/overflow.h include stdint.h for SIZE_MAX, which is used by its size helper functions. With the series applied, make -C tools/virtio test builds virtio_test, vringh_test and vhost_net_test successfully. Tested on x86_64 and arm64 with: make -C tools/virtio clean make -C tools/virtio test This patch (of 2): vhost_net_test builds virtio_ring.c in userspace. Recent virtio headers pull in helper headers that are not provided by the tools/virtio compatibility layer, including asm/percpu_types.h, linux/completion.h, linux/mod_devicetable.h and linux/virtio_features.h. Add the missing compat definitions and the DMA attribute used by the current virtio ring code. Link: https://lore.kernel.org/20260629022124.131894-1-chenyichong@uniontech.com Link: https://lore.kernel.org/20260629022124.131894-2-chenyichong@uniontech.com Signed-off-by: Yichong Chen Acked-by: Eugenio Pérez Cc: chenyichong Cc: Jason Wang Cc: Lorenzo Stoakes Cc: "Michael S. Tsirkin" Cc: Mike Rapoport Cc: Paolo Abeni Cc: Xuan Zhuo Signed-off-by: Andrew Morton --- tools/virtio/asm/percpu_types.h | 7 +++ tools/virtio/linux/completion.h | 9 ++++ tools/virtio/linux/device.h | 1 + tools/virtio/linux/dma-mapping.h | 1 + tools/virtio/linux/mod_devicetable.h | 14 +++++ tools/virtio/linux/virtio_features.h | 79 ++++++++++++++++++++++++++++ 6 files changed, 111 insertions(+) create mode 100644 tools/virtio/asm/percpu_types.h create mode 100644 tools/virtio/linux/completion.h create mode 100644 tools/virtio/linux/mod_devicetable.h create mode 100644 tools/virtio/linux/virtio_features.h diff --git a/tools/virtio/asm/percpu_types.h b/tools/virtio/asm/percpu_types.h new file mode 100644 index 000000000000..4eb53d93c099 --- /dev/null +++ b/tools/virtio/asm/percpu_types.h @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_PERCPU_TYPES_H +#define _ASM_PERCPU_TYPES_H + +#define __percpu_qual + +#endif /* _ASM_PERCPU_TYPES_H */ diff --git a/tools/virtio/linux/completion.h b/tools/virtio/linux/completion.h new file mode 100644 index 000000000000..5e54b679721b --- /dev/null +++ b/tools/virtio/linux/completion.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LINUX_COMPLETION_H +#define _LINUX_COMPLETION_H + +struct completion { + unsigned int done; +}; + +#endif /* _LINUX_COMPLETION_H */ diff --git a/tools/virtio/linux/device.h b/tools/virtio/linux/device.h index 075c2140d975..abf100cb0023 100644 --- a/tools/virtio/linux/device.h +++ b/tools/virtio/linux/device.h @@ -1,4 +1,5 @@ #ifndef LINUX_DEVICE_H +#define LINUX_DEVICE_H struct device { void *parent; diff --git a/tools/virtio/linux/dma-mapping.h b/tools/virtio/linux/dma-mapping.h index 8d1a16cb20db..b9fc5e8338e3 100644 --- a/tools/virtio/linux/dma-mapping.h +++ b/tools/virtio/linux/dma-mapping.h @@ -61,5 +61,6 @@ enum dma_data_direction { #define DMA_MAPPING_ERROR (~(dma_addr_t)0) #define DMA_ATTR_CPU_CACHE_CLEAN (1UL << 11) +#define DMA_ATTR_DEBUGGING_IGNORE_CACHELINES 0 #endif diff --git a/tools/virtio/linux/mod_devicetable.h b/tools/virtio/linux/mod_devicetable.h new file mode 100644 index 000000000000..3ba594b8229d --- /dev/null +++ b/tools/virtio/linux/mod_devicetable.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LINUX_MOD_DEVICETABLE_H +#define _LINUX_MOD_DEVICETABLE_H + +#include + +struct virtio_device_id { + __u32 device; + __u32 vendor; +}; + +#define VIRTIO_DEV_ANY_ID 0xffffffff + +#endif /* _LINUX_MOD_DEVICETABLE_H */ diff --git a/tools/virtio/linux/virtio_features.h b/tools/virtio/linux/virtio_features.h new file mode 100644 index 000000000000..04cbb9622ec7 --- /dev/null +++ b/tools/virtio/linux/virtio_features.h @@ -0,0 +1,79 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LINUX_VIRTIO_FEATURES_H +#define _LINUX_VIRTIO_FEATURES_H + +#include +#include +#include + +#define VIRTIO_FEATURES_U64S 2 +#define VIRTIO_FEATURES_BITS (VIRTIO_FEATURES_U64S * 64) + +#define VIRTIO_BIT(b) (1ULL << ((b) & 0x3f)) +#define VIRTIO_U64(b) ((b) >> 6) + +#define VIRTIO_DECLARE_FEATURES(name) \ + union { \ + u64 name; \ + u64 name##_array[VIRTIO_FEATURES_U64S];\ + } + +static inline bool virtio_features_chk_bit(unsigned int bit) +{ + return bit < VIRTIO_FEATURES_BITS; +} + +static inline bool virtio_features_test_bit(const u64 *features, + unsigned int bit) +{ + return virtio_features_chk_bit(bit) && + !!(features[VIRTIO_U64(bit)] & VIRTIO_BIT(bit)); +} + +static inline void virtio_features_set_bit(u64 *features, unsigned int bit) +{ + if (virtio_features_chk_bit(bit)) + features[VIRTIO_U64(bit)] |= VIRTIO_BIT(bit); +} + +static inline void virtio_features_clear_bit(u64 *features, unsigned int bit) +{ + if (virtio_features_chk_bit(bit)) + features[VIRTIO_U64(bit)] &= ~VIRTIO_BIT(bit); +} + +static inline void virtio_features_zero(u64 *features) +{ + memset(features, 0, sizeof(features[0]) * VIRTIO_FEATURES_U64S); +} + +static inline void virtio_features_from_u64(u64 *features, u64 from) +{ + virtio_features_zero(features); + features[0] = from; +} + +static inline bool virtio_features_equal(const u64 *f1, const u64 *f2) +{ + int i; + + for (i = 0; i < VIRTIO_FEATURES_U64S; ++i) + if (f1[i] != f2[i]) + return false; + return true; +} + +static inline void virtio_features_copy(u64 *to, const u64 *from) +{ + memcpy(to, from, sizeof(to[0]) * VIRTIO_FEATURES_U64S); +} + +static inline void virtio_features_andnot(u64 *to, const u64 *f1, const u64 *f2) +{ + int i; + + for (i = 0; i < VIRTIO_FEATURES_U64S; i++) + to[i] = f1[i] & ~f2[i]; +} + +#endif /* _LINUX_VIRTIO_FEATURES_H */ From 039892c35f9d8f5ea00d7c2ed1c25224f28b11d7 Mon Sep 17 00:00:00 2001 From: Yichong Chen Date: Mon, 29 Jun 2026 10:21:24 +0800 Subject: [PATCH 0392/1054] tools/include: include stdint.h for SIZE_MAX in overflow.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit tools/include/linux/overflow.h uses SIZE_MAX in its size helper functions. Include stdint.h so tools users that include overflow.h without another SIZE_MAX provider can build. Link: https://lore.kernel.org/20260629022124.131894-3-chenyichong@uniontech.com Signed-off-by: Yichong Chen Acked-by: Eugenio Pérez Cc: Jason Wang Cc: Lorenzo Stoakes Cc: "Michael S. Tsirkin" Cc: Mike Rapoport Cc: Paolo Abeni Cc: Xuan Zhuo Signed-off-by: Andrew Morton --- tools/include/linux/overflow.h | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/include/linux/overflow.h b/tools/include/linux/overflow.h index 3427d7880326..98963688143f 100644 --- a/tools/include/linux/overflow.h +++ b/tools/include/linux/overflow.h @@ -1,4 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 OR MIT */ +#include #ifndef __LINUX_OVERFLOW_H #define __LINUX_OVERFLOW_H From fcd245ea7528d50fddffc0fd1308941a9180f5b3 Mon Sep 17 00:00:00 2001 From: Jan Beulich Date: Thu, 2 Jul 2026 08:11:22 +0200 Subject: [PATCH 0393/1054] x86/Xen: correct commentary and parameter naming of xen_exchange_memory() As documented in comments in struct xen_memory_exchange, the input to the hypercall is a set of MFNs which are to be removed from the domain, plus a set of PFNs where the newly allocated MFNs are to appear. Present comment and parameter naming don't correctly reflect that. Signed-off-by: Jan Beulich Reviewed-by: Juergen Gross Signed-off-by: Juergen Gross Message-ID: <7e0c8795-cc60-4b78-8601-6a999739467a@suse.com> --- arch/x86/xen/mmu_pv.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/arch/x86/xen/mmu_pv.c b/arch/x86/xen/mmu_pv.c index aab5f70d407c..820af6f0aa57 100644 --- a/arch/x86/xen/mmu_pv.c +++ b/arch/x86/xen/mmu_pv.c @@ -2291,18 +2291,19 @@ static void xen_remap_exchanged_ptes(unsigned long vaddr, int order, } /* - * Perform the hypercall to exchange a region of our pfns to point to - * memory with the required contiguous alignment. Takes the pfns as - * input, and populates mfns as output. + * Perform the hypercall to exchange a region of our pages to point to memory + * with the required contiguous alignment. Takes as input the mfns to trade + * in (mfns_in) and the pfns where the new pages are to appear (fns_inout), + * and populates mfns as output (fns_inout). * * Returns a success code indicating whether the hypervisor was able to * satisfy the request or not. */ static int xen_exchange_memory(unsigned long extents_in, unsigned int order_in, - unsigned long *pfns_in, + unsigned long *mfns_in, unsigned long extents_out, unsigned int order_out, - unsigned long *mfns_out, + unsigned long *fns_inout, unsigned int address_bits) { long rc; @@ -2312,13 +2313,13 @@ static int xen_exchange_memory(unsigned long extents_in, unsigned int order_in, .in = { .nr_extents = extents_in, .extent_order = order_in, - .extent_start = pfns_in, + .extent_start = mfns_in, .domid = DOMID_SELF }, .out = { .nr_extents = extents_out, .extent_order = order_out, - .extent_start = mfns_out, + .extent_start = fns_inout, .address_bits = address_bits, .domid = DOMID_SELF } From c283e9ada7fcb7dd4b10592623086b2e6d2f9925 Mon Sep 17 00:00:00 2001 From: "Xiang Mei (Microsoft)" Date: Sat, 27 Jun 2026 02:40:23 +0000 Subject: [PATCH 0394/1054] xfrm: fix sk_dst_cache double-free in xfrm_user_policy() xfrm_user_policy() clears the socket dst cache with __sk_dst_reset(), i.e. the non-atomic __sk_dst_set(sk, NULL): it reads sk_dst_cache with rcu_dereference_protected(), stores NULL and dst_release()s the old dst. That is only safe if no other thread modifies sk_dst_cache concurrently. For a connected UDP socket that does not hold: the transmit fast path (udp_sendmsg -> sk_dst_check -> sk_dst_reset) resets the cache locklessly with an atomic xchg(). A per-socket policy change racing a send can make both sides observe the same old dst and each dst_release() it, dropping the socket's single reference twice and freeing the xfrm_dst bundle while it is still referenced: BUG: KASAN: slab-use-after-free in dst_release Write of size 4 at addr ffff88801897b6c0 by task exploit/155 Call Trace: ... dst_release (... ./include/linux/rcuref.h:109) xfrm_user_policy (./include/net/sock.h:2239 ./include/net/sock.h:2256 net/xfrm/xfrm_state.c:3053) do_ip_setsockopt (net/ipv4/ip_sockglue.c:1347) ip_setsockopt (net/ipv4/ip_sockglue.c:1417) do_sock_setsockopt (net/socket.c:2368) __sys_setsockopt (net/socket.c:2393) __x64_sys_setsockopt (net/socket.c:2396) do_syscall_64 (arch/x86/entry/syscall_64.c:94) entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121) Reachable by an unprivileged user via a user+network namespace. Use the atomic sk_dst_reset() so the cache is cleared and released with a single xchg(): whichever side wins releases the dst once, the other sees NULL and does nothing. Behaviour is otherwise unchanged. Fixes: 2b06cdf3e688 ("xfrm: Clear sk_dst_cache when applying per-socket policy.") Fixes: be8f8284cd89 ("net: xfrm: allow clearing socket xfrm policies.") Reported-by: AutonomousCodeSecurity@microsoft.com Signed-off-by: Xiang Mei (Microsoft) Signed-off-by: Steffen Klassert --- net/xfrm/xfrm_state.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index c58cd024e3c6..08ba6805ddb3 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -3010,7 +3010,7 @@ int xfrm_user_policy(struct sock *sk, int optname, sockptr_t optval, int optlen) if (sockptr_is_null(optval) && !optlen) { xfrm_sk_policy_insert(sk, XFRM_POLICY_IN, NULL); xfrm_sk_policy_insert(sk, XFRM_POLICY_OUT, NULL); - __sk_dst_reset(sk); + sk_dst_reset(sk); return 0; } @@ -3050,7 +3050,7 @@ int xfrm_user_policy(struct sock *sk, int optname, sockptr_t optval, int optlen) if (err >= 0) { xfrm_sk_policy_insert(sk, err, pol); xfrm_pol_put(pol); - __sk_dst_reset(sk); + sk_dst_reset(sk); err = 0; } From c4a5f0071cc6d378a0e7151b60d85986aefac1f3 Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Sat, 27 Jun 2026 11:50:58 +0800 Subject: [PATCH 0395/1054] xfrm: cache the offload ifindex for netlink dumps copy_to_user_state_extra() only holds a reference to the outer xfrm_state. That does not pin x->xso.dev. NETDEV_DOWN and NETDEV_UNREGISTER can race through xfrm_dev_state_flush(), xfrm_state_delete(), and xfrm_dev_state_free(), which clears xso->dev and drops the netdev reference before the GETSA dump reaches xso_to_xuo() and reads xso->dev->ifindex. The buggy scenario involves two paths, with each column showing the order within that path: XFRM_MSG_GETSA dump path: NETDEV teardown path: 1. xfrm_get_sa() gets xfrm_state 1. xfrm_dev_state_flush() finds x 2. copy_to_user_state_extra() sees 2. xfrm_state_delete() removes x x->xso.dev from the SAD 3. copy_user_offload() calls 3. xfrm_dev_state_free() clears xso_to_xuo() xso->dev 4. xso->dev->ifindex dereferences 4. netdev_put() drops the device a detached net_device reference Avoid following the live net_device from the dump paths. Cache the attached ifindex in xfrm_dev_offload when state or policy offload is bound to a device, and serialize that snapshot instead. This preserves the user-visible XFRMA_OFFLOAD_DEV value without depending on the embedded net_device lifetime. Validation reproduced this kernel report: Oops: general protection fault Call Trace: copy_to_user_state_extra+0xb8d/0x1370 [xfrm_user] ? __pfx_copy_to_user_state_extra+0x10/0x10 [xfrm_user] ? __asan_memset+0x23/0x50 ? srso_alias_return_thunk+0x5/0xfbef5 ? __alloc_skb+0x342/0x960 ? srso_alias_return_thunk+0x5/0xfbef5 ? __asan_memset+0x23/0x50 ? srso_alias_return_thunk+0x5/0xfbef5 ? __nlmsg_put+0x147/0x1b0 dump_one_state+0x1c7/0x3e0 [xfrm_user] xfrm_state_netlink+0xcb/0x130 [xfrm_user] ? __pfx_xfrm_state_netlink+0x10/0x10 [xfrm_user] ? srso_alias_return_thunk+0x5/0xfbef5 ? xfrm_user_state_lookup.constprop.0+0x230/0x310 [xfrm_user] xfrm_get_sa+0x102/0x250 [xfrm_user] ? __pfx_xfrm_get_sa+0x10/0x10 [xfrm_user] xfrm_user_rcv_msg+0x504/0xaa0 [xfrm_user] ? __pfx_xfrm_user_rcv_msg+0x10/0x10 [xfrm_user] ? srso_alias_return_thunk+0x5/0xfbef5 ? stack_trace_save+0x8e/0xc0 ? __pfx_stack_trace_save+0x10/0x10 netlink_rcv_skb+0x11f/0x350 ? __pfx_xfrm_user_rcv_msg+0x10/0x10 [xfrm_user] ? __pfx_netlink_rcv_skb+0x10/0x10 ? __pfx_mutex_lock+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 xfrm_netlink_rcv+0x65/0x80 [xfrm_user] netlink_unicast+0x600/0x870 ? __pfx_netlink_unicast+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? __pfx_stack_trace_save+0x10/0x10 netlink_sendmsg+0x75d/0xc10 ? __pfx_netlink_sendmsg+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ____sys_sendmsg+0x77a/0x900 ? srso_alias_return_thunk+0x5/0xfbef5 ? __pfx_____sys_sendmsg+0x10/0x10 ? __pfx_copy_msghdr_from_user+0x10/0x10 ? release_sock+0x1a/0x1d0 ? srso_alias_return_thunk+0x5/0xfbef5 ? netlink_insert+0x143/0xec0 ___sys_sendmsg+0xff/0x180 ? __pfx____sys_sendmsg+0x10/0x10 ? _raw_spin_lock_irqsave+0x85/0xe0 ? do_getsockname+0xf9/0x170 ? srso_alias_return_thunk+0x5/0xfbef5 ? fdget+0x53/0x3b0 __sys_sendmsg+0x111/0x1a0 ? __pfx___sys_sendmsg+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? __sys_getsockname+0x8c/0x100 do_syscall_64+0x102/0x5a0 entry_SYSCALL_64_after_hwframe+0x77/0x7f Fixes: 07b87f9eea0c ("xfrm: Fix unregister netdevice hang on hardware offload.") Assisted-by: Codex:gpt-5.5 Signed-off-by: Cen Zhang Signed-off-by: Steffen Klassert --- include/net/xfrm.h | 2 ++ net/xfrm/xfrm_device.c | 1 + net/xfrm/xfrm_state.c | 1 + net/xfrm/xfrm_user.c | 38 +++++++++++++++++++++++++++++--------- 4 files changed, 33 insertions(+), 9 deletions(-) diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 519a0156a05c..a6d69aaa6cd2 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -162,6 +162,8 @@ struct xfrm_dev_offload { */ struct net_device *real_dev; unsigned long offload_handle; + /* Snapshot the attached device index for dump paths. */ + int ifindex; u8 dir : 2; u8 type : 2; u8 flags : 2; diff --git a/net/xfrm/xfrm_device.c b/net/xfrm/xfrm_device.c index aec1e1184a71..f153bf695b9d 100644 --- a/net/xfrm/xfrm_device.c +++ b/net/xfrm/xfrm_device.c @@ -321,6 +321,7 @@ int xfrm_dev_state_add(struct net *net, struct xfrm_state *x, } xso->dev = dev; + xso->ifindex = dev->ifindex; netdev_tracker_alloc(dev, &xso->dev_tracker, GFP_ATOMIC); if (xuo->flags & XFRM_OFFLOAD_INBOUND) diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 08ba6805ddb3..10e5a1a95fe3 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -1547,6 +1547,7 @@ xfrm_state_find(const xfrm_address_t *daddr, const xfrm_address_t *saddr, xso->type = XFRM_DEV_OFFLOAD_PACKET; xso->dir = xdo->dir; xso->dev = dev; + xso->ifindex = dev->ifindex; xso->flags = XFRM_DEV_OFFLOAD_FLAG_ACQ; netdev_hold(dev, &xso->dev_tracker, GFP_ATOMIC); error = dev->xfrmdev_ops->xdo_dev_state_add(dev, x, diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 6384795ee6b2..0eb87fc998d1 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -1201,17 +1201,26 @@ static int copy_sec_ctx(struct xfrm_sec_ctx *s, struct sk_buff *skb) return 0; } -static void xso_to_xuo(const struct xfrm_dev_offload *xso, - struct xfrm_user_offload *xuo) +static void xso_to_xuo_ifindex(const struct xfrm_dev_offload *xso, int ifindex, + struct xfrm_user_offload *xuo) { - xuo->ifindex = xso->dev->ifindex; + xuo->ifindex = ifindex; if (xso->dir == XFRM_DEV_OFFLOAD_IN) xuo->flags = XFRM_OFFLOAD_INBOUND; if (xso->type == XFRM_DEV_OFFLOAD_PACKET) xuo->flags |= XFRM_OFFLOAD_PACKET; } -static int copy_user_offload(struct xfrm_dev_offload *xso, struct sk_buff *skb) +#ifdef CONFIG_XFRM_MIGRATE +static void xso_to_xuo(const struct xfrm_dev_offload *xso, + struct xfrm_user_offload *xuo) +{ + xso_to_xuo_ifindex(xso, xso->dev->ifindex, xuo); +} +#endif + +static int copy_user_offload_ifindex(const struct xfrm_dev_offload *xso, + int ifindex, struct sk_buff *skb) { struct xfrm_user_offload *xuo; struct nlattr *attr; @@ -1222,11 +1231,22 @@ static int copy_user_offload(struct xfrm_dev_offload *xso, struct sk_buff *skb) xuo = nla_data(attr); memset(xuo, 0, sizeof(*xuo)); - xso_to_xuo(xso, xuo); + xso_to_xuo_ifindex(xso, ifindex, xuo); return 0; } +static int copy_user_offload(struct xfrm_dev_offload *xso, struct sk_buff *skb) +{ + return copy_user_offload_ifindex(xso, xso->dev->ifindex, skb); +} + +static int copy_user_state_offload(const struct xfrm_dev_offload *xso, + struct sk_buff *skb) +{ + return copy_user_offload_ifindex(xso, READ_ONCE(xso->ifindex), skb); +} + static bool xfrm_redact(void) { return IS_ENABLED(CONFIG_SECURITY) && @@ -1433,8 +1453,8 @@ static int copy_to_user_state_extra(struct xfrm_state *x, &x->replay); if (ret) goto out; - if(x->xso.dev) - ret = copy_user_offload(&x->xso, skb); + if (READ_ONCE(x->xso.dev)) + ret = copy_user_state_offload(&x->xso, skb); if (ret) goto out; if (x->if_id) { @@ -4046,8 +4066,8 @@ static inline unsigned int xfrm_sa_len(struct xfrm_state *x) l += nla_total_size(sizeof(*x->coaddr)); if (x->props.extra_flags) l += nla_total_size(sizeof(x->props.extra_flags)); - if (x->xso.dev) - l += nla_total_size(sizeof(struct xfrm_user_offload)); + if (READ_ONCE(x->xso.dev)) + l += nla_total_size(sizeof(struct xfrm_user_offload)); if (x->props.smark.v | x->props.smark.m) { l += nla_total_size(sizeof(x->props.smark.v)); l += nla_total_size(sizeof(x->props.smark.m)); From ea528f18231ec0f33317be57f8866913b19aba6e Mon Sep 17 00:00:00 2001 From: Antony Antony Date: Sat, 27 Jun 2026 10:23:43 +0200 Subject: [PATCH 0396/1054] xfrm: reject optional IPTFS templates in outbound policies syzbot reported a stack-out-of-bounds read in xfrm_state_find() which flows from xfrm_tmpl_resolve_one(). Commit 3d776e31c841 ("xfrm: Reject optional tunnel/BEET mode templates in outbound policies") disallowed optional tunnel and BEET in outbound policies to prevent this. Later when IPTFS added, it was not covered by that fix and can still trigger the out-of-bounds read; Extend the check to disallow optional IPTFS in outbound policies as well. IPTFS should be identical to tunnel mode. IN and FWD policies are not affected: xfrm_tmpl_resolve_one() is only reachable via the outbound path. Reproducer, before: ip link add dummy0 type dummy ip link set dummy0 up ip addr add 10.1.1.1/24 dev dummy0 ip xfrm policy add src 10.1.1.1/32 dst 10.1.1.2/32 dir out tmpl src fc00::dead:1 dst fc00::dead:2 proto esp reqid 1 mode iptfs level use tmpl src fc00::dead:1 dst fc00::dead:2 proto esp reqid 2 mode transport ping -W 1 -c 1 10.1.1.2 PING 10.1.1.2 (10.1.1.2) 56(84) bytes of data. [ 64.168420] ================================================================== [ 64.169977] BUG: KASAN: stack-out-of-bounds in __xfrm6_addr_hash+0x11e/0x170 [ 64.169977] Read of size 4 at addr ffff88800e1ffd20 by task ping/2844 [ 64.169977] CPU: 2 UID: 0 PID: 2844 Comm: ping Not tainted 7.1.0-rc7-00180-geb23b588430a #98 PREEMPT(full) [ 64.169977] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014 [ 64.169977] Call Trace: [ 64.169977] [ 64.169977] dump_stack_lvl+0x47/0x70 [ 64.169977] ? __xfrm6_addr_hash+0x11e/0x170 [ 64.169977] print_report+0x152/0x4b0 [ 64.169977] ? ksys_mmap_pgoff+0x6d/0xa0 [ 64.169977] ? entry_SYSCALL_64_after_hwframe+0x76/0x7e [ 64.169977] ? rcu_read_unlock_sched+0xa/0x20 [ 64.169977] ? __virt_addr_valid+0x21b/0x230 [ 64.169977] ? __xfrm6_addr_hash+0x11e/0x170 [ 64.169977] kasan_report+0xa8/0xd0 [ 64.169977] ? __xfrm6_addr_hash+0x11e/0x170 [ 64.169977] __xfrm6_addr_hash+0x11e/0x170 [ 64.169977] __xfrm_dst_hash+0x24/0xc0 [ 64.169977] xfrm_state_find+0xa2d/0x2f90 [ 64.169977] ? __pfx_xfrm_state_find+0x10/0x10 [ 64.169977] ? __pfx_ftrace_graph_ret_addr+0x10/0x10 [ 64.169977] ? __pfx_ftrace_graph_ret_addr+0x10/0x10 [ 64.169977] xfrm_tmpl_resolve_one+0x210/0x570 [ 64.169977] ? __pfx_xfrm_tmpl_resolve_one+0x10/0x10 [ 64.169977] ? __pfx_stack_trace_consume_entry+0x10/0x10 [ 64.169977] ? kernel_text_address+0x5b/0x80 [ 64.169977] ? __kernel_text_address+0xe/0x30 [ 64.169977] ? unwind_get_return_address+0x5e/0x90 [ 64.169977] ? arch_stack_walk+0x8c/0xe0 [ 64.169977] xfrm_tmpl_resolve+0x130/0x200 [ 64.169977] ? __pfx_xfrm_tmpl_resolve+0x10/0x10 [ 64.169977] ? __pfx_xfrm_policy_inexact_lookup_rcu+0x10/0x10 [ 64.169977] ? __refcount_add_not_zero.constprop.0+0xb2/0x110 [ 64.169977] ? __pfx___refcount_add_not_zero.constprop.0+0x10/0x10 [ 64.169977] xfrm_resolve_and_create_bundle+0xd5/0x310 [ 64.169977] ? __pfx_xfrm_resolve_and_create_bundle+0x10/0x10 [ 64.169977] ? __pfx_xfrm_policy_lookup_bytype+0x10/0x10 [ 64.169977] ? __pfx_xfrm_policy_lookup_bytype+0x10/0x10 [ 64.169977] xfrm_lookup_with_ifid+0x3d8/0xb80 [ 64.169977] ? __pfx_xfrm_lookup_with_ifid+0x10/0x10 [ 64.169977] ? ip_route_output_key_hash+0xc6/0x110 [ 64.169977] ? kasan_save_track+0x10/0x30 [ 64.169977] xfrm_lookup_route+0x18/0xe0 [ 64.169977] ip4_datagram_release_cb+0x4c9/0x530 [ 64.169977] ? __pfx_ip4_datagram_release_cb+0x10/0x10 [ 64.169977] ? do_raw_spin_lock+0x71/0xc0 [ 64.169977] ? __pfx_do_raw_spin_lock+0x10/0x10 [ 64.169977] release_sock+0xb0/0x170 [ 64.169977] udp_connect+0x43/0x50 [ 64.169977] __sys_connect+0xa6/0x100 [ 64.169977] ? alloc_fd+0x2e9/0x300 [ 64.169977] ? __pfx___sys_connect+0x10/0x10 [ 64.169977] ? preempt_latency_start+0x1f/0x70 [ 64.169977] ? fd_install+0x7e/0x150 [ 64.169977] ? rcu_read_unlock_sched+0xa/0x20 [ 64.169977] ? __sys_socket+0xdf/0x130 [ 64.169977] ? __pfx___sys_socket+0x10/0x10 [ 64.169977] ? vma_refcount_put+0x43/0xa0 [ 64.169977] __x64_sys_connect+0x7e/0x90 [ 64.169977] do_syscall_64+0x11b/0x2b0 [ 64.169977] entry_SYSCALL_64_after_hwframe+0x76/0x7e [ 64.169977] RIP: 0033:0x7f4851ecb570 [ 64.169977] Code: 00 f7 d8 64 89 01 48 83 c8 ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 80 3d f9 ca 0d 00 00 74 17 b8 2a 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 58 c3 0f 1f 80 00 00 00 00 48 83 ec 18 89 54 [ 64.169977] RSP: 002b:00007ffc830e3498 EFLAGS: 00000202 ORIG_RAX: 000000000000002a [ 64.169977] RAX: ffffffffffffffda RBX: 00007ffc830e34d0 RCX: 00007f4851ecb570 [ 64.169977] RDX: 0000000000000010 RSI: 00007ffc830e34d0 RDI: 0000000000000005 [ 64.169977] RBP: 0000000000000000 R08: 0000000000000003 R09: 0000000000000000 [ 64.169977] R10: 0000000000000006 R11: 0000000000000202 R12: 0000000000000005 [ 64.169977] R13: 0000000000000000 R14: 00005619a863f340 R15: 0000000000000000 [ 64.169977] [ 64.169977] The buggy address belongs to stack of task ping/2844 [ 64.169977] and is located at offset 88 in frame: [ 64.169977] ip4_datagram_release_cb+0x0/0x530 [ 64.169977] This frame has 1 object: [ 64.169977] [32, 88) 'fl4' [ 64.169977] The buggy address belongs to the physical page: [ 64.169977] page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0xe1ff [ 64.169977] flags: 0x4000000000000000(zone=1) [ 64.169977] raw: 4000000000000000 0000000000000000 ffffea0000387fc8 0000000000000000 [ 64.169977] raw: 0000000000000000 0000000000000000 00000000ffffffff 0000000000000000 [ 64.169977] page dumped because: kasan: bad access detected [ 64.169977] Memory state around the buggy address: [ 64.169977] ffff88800e1ffc00: f2 f2 00 00 f3 f3 00 00 00 00 00 00 00 00 00 00 [ 64.169977] ffff88800e1ffc80: 00 00 00 00 00 00 00 00 00 f1 f1 f1 f1 00 00 00 [ 64.169977] >ffff88800e1ffd00: 00 00 00 00 f3 f3 f3 f3 f3 00 00 00 00 00 00 00 [ 64.169977] ^ [ 64.169977] ffff88800e1ffd80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 f1 f1 [ 64.169977] ffff88800e1ffe00: f1 f1 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 64.169977] ================================================================== [ 64.245153] Disabling lock debugging due to kernel taint After the fix: ip xfrm policy add src 10.1.1.1/32 dst 10.1.1.2/32 dir out tmpl \ src fc00::dead:1 dst fc00::dead:2 proto esp reqid 1 mode iptfs \ level use tmpl src fc00::dead:1 dst fc00::dead:2 proto esp reqid 2 \ mode transport Error: Mode in optional template not allowed in outbound policy. Fixes: d1716d5a44c3 ("xfrm: add generic iptfs defines and functionality") Reported-by: syzbot+0ac4d84afe1066a1f3e9@syzkaller.appspotmail.com Closes: https://lore.kernel.org/all/6a3ceb94.43b4ff68.30a095.0004.GAE@google.com/T/ Signed-off-by: Antony Antony Signed-off-by: Steffen Klassert --- net/xfrm/xfrm_user.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 0eb87fc998d1..d6db63304ba6 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -2124,13 +2124,12 @@ static int validate_tmpl(int nr, struct xfrm_user_tmpl *ut, u16 family, switch (ut[i].mode) { case XFRM_MODE_TUNNEL: case XFRM_MODE_BEET: + case XFRM_MODE_IPTFS: if (ut[i].optional && dir == XFRM_POLICY_OUT) { NL_SET_ERR_MSG(extack, "Mode in optional template not allowed in outbound policy"); return -EINVAL; } break; - case XFRM_MODE_IPTFS: - break; default: if (ut[i].family != prev_family) { NL_SET_ERR_MSG(extack, "Mode in template doesn't support a family change"); From 4af24c27a39ba147a613a09e10b9e0f7294524c0 Mon Sep 17 00:00:00 2001 From: Brajesh Gupta Date: Tue, 30 Jun 2026 21:10:07 +0530 Subject: [PATCH 0397/1054] drm/imagination: Fix double call to drm_sched_entity_fini() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Call sequence of double call: pvr_context_destroy   pvr_context_kill_queues     pvr_queue_kill       drm_sched_entity_destroy         drm_sched_entity_fini // here   pvr_context_put     kref_put(..., pvr_context_release)       pvr_context_destroy_queues         pvr_queue_destroy           drm_sched_entity_fini // here Call to drm_sched_entity_destroy() from pvr_context_kill_queues() calls drm_sched_entity_flush() + drm_sched_entity_fini(). drm_sched_entity_flush() ensures all pending jobs are completed and drm_sched_entity_fini() ensures no further submission is allowed as per expectation from pvr_context_kill_queues(). Double call to drm_sched_entity_fini() is misuse of the API so keep call only in pvr_context_create() failure path. Stack trace for issue with addition of refcounting for DRM entity stats in commit fd177135f0e6 ("drm/sched: Account entity GPU time"): [ 789.490527] ------------[ cut here ]------------ [ 789.490559] refcount_t: underflow; use-after-free. [ 789.490657] WARNING: lib/refcount.c:28 at refcount_warn_saturate+0xf4/0x144, CPU#0: kworker/u16:1/440 [ 789.490695] Modules linked in: powervr drm_gpuvm drm_exec gpu_sched drm_shmem_helper xhci_plat_hcd xhci_hcd dwc3 usbcore usb_common snd_soc_simple_card snd_soc_simple_card_utils sa2ul sha512 sha256 dwc3_am62 sha1 authenc rti_wdt libsha512 at24 sch_fq_codel fuse dm_mod ipv6 [ 789.490798] CPU: 0 UID: 0 PID: 440 Comm: kworker/u16:1 Not tainted 7.0.0-rc7-02049-g5e2c0700091b #22 PREEMPT [ 789.490809] Hardware name: Texas Instruments AM625 SK (DT) [ 789.490815] Workqueue: powervr-sched pvr_queue_fence_release_work [powervr] [ 789.490868] pstate: 60000005 (nZCv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--) [ 789.490876] pc : refcount_warn_saturate+0xf4/0x144 [ 789.490884] lr : refcount_warn_saturate+0xf4/0x144 [ 789.490892] sp : ffff8000822cbcc0 [ 789.490895] x29: ffff8000822cbcc0 x28: 0000000000000000 x27: 0000000000000000 [ 789.490909] x26: 0000000000000000 x25: ffff800081b1e338 x24: ffff000004541405 [ 789.490922] x23: ffff000004bea950 x22: ffff00000042e400 x21: ffff000007123e30 [ 789.490935] x20: ffff000007123000 x19: ffff000007a80d50 x18: fffffffffffe7768 [ 789.490948] x17: 74736574202c6e6f x16: 697461746e656d65 x15: ffff800081b269f0 [ 789.490962] x14: 0000000000000030 x13: ffff800081b26a70 x12: 0000000000000211 [ 789.490975] x11: 00000000000000c0 x10: 0000000000000b50 x9 : ffff8000822cbb30 [ 789.490988] x8 : ffff0000014e7bb0 x7 : ffff00007725e780 x6 : 0000000372a05f49 [ 789.491001] x5 : 0000000000000000 x4 : 0000000000000001 x3 : 0000000000000010 [ 789.491013] x2 : 0000000000000000 x1 : 0000000000000000 x0 : ffff0000014e7000 [ 789.491027] Call trace: [ 789.491032] refcount_warn_saturate+0xf4/0x144 (P) [ 789.491043] drm_sched_entity_fini+0x164/0x18c [gpu_sched] [ 789.491081] pvr_queue_destroy+0x64/0x134 [powervr] [ 789.491110] pvr_context_destroy_queues+0x34/0x64 [powervr] [ 789.491138] pvr_context_release+0x70/0xac [powervr] [ 789.491166] pvr_context_put.part.0+0x5c/0x7c [powervr] [ 789.491193] pvr_context_put+0x14/0x24 [powervr] [ 789.491221] pvr_queue_fence_release_work+0x20/0x38 [powervr] [ 789.491249] process_one_work+0x160/0x4c4 [ 789.491264] worker_thread+0x188/0x310 [ 789.491276] kthread+0x130/0x13c [ 789.491287] ret_from_fork+0x10/0x20 [ 789.491300] ---[ end trace 0000000000000000 ]--- Fixes: eaf01ee5ba28 ("drm/imagination: Implement job submission and scheduling") Cc: stable@vger.kernel.org Signed-off-by: Brajesh Gupta Reviewed-by: Alessio Belle Link: https://patch.msgid.link/20260630-b4-sched_fix-v7-1-71aa39c62627@imgtec.com Signed-off-by: Alessio Belle --- drivers/gpu/drm/imagination/pvr_context.c | 18 ++++++++++-------- drivers/gpu/drm/imagination/pvr_queue.c | 6 ++++-- drivers/gpu/drm/imagination/pvr_queue.h | 2 +- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/imagination/pvr_context.c b/drivers/gpu/drm/imagination/pvr_context.c index eba4694400b5..52e16c1e7af0 100644 --- a/drivers/gpu/drm/imagination/pvr_context.c +++ b/drivers/gpu/drm/imagination/pvr_context.c @@ -161,22 +161,24 @@ ctx_fw_data_init(void *cpu_ptr, void *priv) /** * pvr_context_destroy_queues() - Destroy all queues attached to a context. * @ctx: Context to destroy queues on. + * @cleanup_queue_entity: Whether to cleanup the queue entity e.g. context + * creation failure path. * * Should be called when the last reference to a context object is dropped. * It releases all resources attached to the queues bound to this context. */ -static void pvr_context_destroy_queues(struct pvr_context *ctx) +static void pvr_context_destroy_queues(struct pvr_context *ctx, bool cleanup_queue_entity) { switch (ctx->type) { case DRM_PVR_CTX_TYPE_RENDER: - pvr_queue_destroy(ctx->queues.fragment); - pvr_queue_destroy(ctx->queues.geometry); + pvr_queue_destroy(ctx->queues.fragment, cleanup_queue_entity); + pvr_queue_destroy(ctx->queues.geometry, cleanup_queue_entity); break; case DRM_PVR_CTX_TYPE_COMPUTE: - pvr_queue_destroy(ctx->queues.compute); + pvr_queue_destroy(ctx->queues.compute, cleanup_queue_entity); break; case DRM_PVR_CTX_TYPE_TRANSFER_FRAG: - pvr_queue_destroy(ctx->queues.transfer); + pvr_queue_destroy(ctx->queues.transfer, cleanup_queue_entity); break; } } @@ -240,7 +242,7 @@ static int pvr_context_create_queues(struct pvr_context *ctx, return -EINVAL; err_destroy_queues: - pvr_context_destroy_queues(ctx); + pvr_context_destroy_queues(ctx, true); return err; } @@ -349,7 +351,7 @@ int pvr_context_create(struct pvr_file *pvr_file, struct drm_pvr_ioctl_create_co pvr_fw_object_destroy(ctx->fw_obj); err_destroy_queues: - pvr_context_destroy_queues(ctx); + pvr_context_destroy_queues(ctx, true); err_free_ctx_id: /* @@ -384,7 +386,7 @@ pvr_context_release(struct kref *ref_count) spin_unlock(&pvr_dev->ctx_list_lock); xa_erase(&pvr_dev->ctx_ids, ctx->ctx_id); - pvr_context_destroy_queues(ctx); + pvr_context_destroy_queues(ctx, false); pvr_fw_object_destroy(ctx->fw_obj); kfree(ctx->data); pvr_vm_context_put(ctx->vm_ctx); diff --git a/drivers/gpu/drm/imagination/pvr_queue.c b/drivers/gpu/drm/imagination/pvr_queue.c index 7ed60e1c1a86..941c017399fc 100644 --- a/drivers/gpu/drm/imagination/pvr_queue.c +++ b/drivers/gpu/drm/imagination/pvr_queue.c @@ -1439,11 +1439,12 @@ void pvr_queue_kill(struct pvr_queue *queue) /** * pvr_queue_destroy() - Destroy a queue. * @queue: The queue to destroy. + * @cleanup_queue_entity: Whether to cleanup the queue entity. * * Cleanup the queue and free the resources attached to it. Should be * called from the context release function. */ -void pvr_queue_destroy(struct pvr_queue *queue) +void pvr_queue_destroy(struct pvr_queue *queue, bool cleanup_queue_entity) { if (!queue) return; @@ -1453,7 +1454,8 @@ void pvr_queue_destroy(struct pvr_queue *queue) mutex_unlock(&queue->ctx->pvr_dev->queues.lock); drm_sched_fini(&queue->scheduler); - drm_sched_entity_fini(&queue->entity); + if (cleanup_queue_entity) + drm_sched_entity_fini(&queue->entity); if (WARN_ON(queue->last_queued_job_scheduled_fence)) dma_fence_put(queue->last_queued_job_scheduled_fence); diff --git a/drivers/gpu/drm/imagination/pvr_queue.h b/drivers/gpu/drm/imagination/pvr_queue.h index 4aa72665ce25..149cc6d124bf 100644 --- a/drivers/gpu/drm/imagination/pvr_queue.h +++ b/drivers/gpu/drm/imagination/pvr_queue.h @@ -158,7 +158,7 @@ struct pvr_queue *pvr_queue_create(struct pvr_context *ctx, void pvr_queue_kill(struct pvr_queue *queue); -void pvr_queue_destroy(struct pvr_queue *queue); +void pvr_queue_destroy(struct pvr_queue *queue, bool cleanup_queue_entity); void pvr_queue_process(struct pvr_queue *queue); From d431b4012fd22920523dbd2806da663c1048e386 Mon Sep 17 00:00:00 2001 From: Brajesh Gupta Date: Wed, 1 Jul 2026 10:49:30 +0530 Subject: [PATCH 0398/1054] drm/imagination: Fix returned size for DRM_IOCTL_PVR_DEV_QUERY For a few subtypes of DRM_IOCTL_PVR_DEV_QUERY, driver was overriding the returned size unconditionally. This would have resulted in increase of reported size beyond the amount of data returned to userspace when args->size < size of query structure. Updated behaviour matches with the description of drm_pvr_ioctl_dev_query_args.size and written byte length. None of the structures of DRM_IOCTL_PVR_DEV_QUERY changed after addition, so change will not break any compatibility with earlier version. Fixes: f99f5f3ea7ef ("drm/imagination: Add GPU ID parsing and firmware loading") Fixes: ff5f643de0bf ("drm/imagination: Add GEM and VM related code") Signed-off-by: Brajesh Gupta Reviewed-by: Alessio Belle Link: https://patch.msgid.link/20260701-b4-b4-query-v2-1-a1b491387875@imgtec.com Signed-off-by: Alessio Belle --- drivers/gpu/drm/imagination/pvr_drv.c | 6 ++++-- drivers/gpu/drm/imagination/pvr_vm.c | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/imagination/pvr_drv.c b/drivers/gpu/drm/imagination/pvr_drv.c index b20c462bcba0..091e9b4873e1 100644 --- a/drivers/gpu/drm/imagination/pvr_drv.c +++ b/drivers/gpu/drm/imagination/pvr_drv.c @@ -515,7 +515,8 @@ pvr_dev_query_quirks_get(struct pvr_device *pvr_dev, if (err < 0) return err; - args->size = sizeof(query); + if (args->size > sizeof(query)) + args->size = sizeof(query); return 0; } @@ -596,7 +597,8 @@ pvr_dev_query_enhancements_get(struct pvr_device *pvr_dev, if (err < 0) return err; - args->size = sizeof(query); + if (args->size > sizeof(query)) + args->size = sizeof(query); return 0; } diff --git a/drivers/gpu/drm/imagination/pvr_vm.c b/drivers/gpu/drm/imagination/pvr_vm.c index e1ec60f34b6e..396d349fb6ce 100644 --- a/drivers/gpu/drm/imagination/pvr_vm.c +++ b/drivers/gpu/drm/imagination/pvr_vm.c @@ -1019,7 +1019,8 @@ pvr_static_data_areas_get(const struct pvr_device *pvr_dev, if (err < 0) return err; - args->size = sizeof(query); + if (args->size > sizeof(query)) + args->size = sizeof(query); return 0; } @@ -1069,7 +1070,8 @@ pvr_heap_info_get(const struct pvr_device *pvr_dev, if (err < 0) return err; - args->size = sizeof(query); + if (args->size > sizeof(query)) + args->size = sizeof(query); return 0; } From 8dc8f3f4c2382fb7d1b1986ba8f33a2466cd3d7a Mon Sep 17 00:00:00 2001 From: Shuvam Pandey Date: Wed, 1 Jul 2026 11:44:34 -0700 Subject: [PATCH 0399/1054] drm/imagination: Fix user array stride in pvr_set_uobj_array() pvr_set_uobj_array() copies an array of kernel objects to a userspace array whose element size is described by out->stride. When out->stride is different from the kernel object size, the slow path advances the userspace pointer by the kernel object size and the kernel pointer by the userspace stride. This reverses the intended layout. For larger userspace strides, later copies read from the wrong kernel addresses. For smaller userspace strides, later copies are written at the wrong userspace offsets. The padding clear is also done only for the first element instead of the padding area for each element. Advance the userspace pointer by out->stride and the kernel pointer by obj_size, and clear per-element padding while the current userspace pointer is still available. Fixes: f99f5f3ea7ef ("drm/imagination: Add GPU ID parsing and firmware loading") Cc: stable@vger.kernel.org # v6.8+ Reviewed-by: Alessio Belle Signed-off-by: Shuvam Pandey Link: https://patch.msgid.link/6a456012.eb165e5c.113c2a.b71d@mx.google.com Signed-off-by: Alessio Belle --- drivers/gpu/drm/imagination/pvr_drv.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/imagination/pvr_drv.c b/drivers/gpu/drm/imagination/pvr_drv.c index 091e9b4873e1..e8487fd22e15 100644 --- a/drivers/gpu/drm/imagination/pvr_drv.c +++ b/drivers/gpu/drm/imagination/pvr_drv.c @@ -1257,14 +1257,13 @@ pvr_set_uobj_array(const struct drm_pvr_obj_array *out, u32 min_stride, u32 obj_ if (copy_to_user(out_ptr, in_ptr, cpy_elem_size)) return -EFAULT; - out_ptr += obj_size; - in_ptr += out->stride; - } + if (out->stride > obj_size && + clear_user(out_ptr + cpy_elem_size, out->stride - obj_size)) { + return -EFAULT; + } - if (out->stride > obj_size && - clear_user(u64_to_user_ptr(out->array + obj_size), - out->stride - obj_size)) { - return -EFAULT; + out_ptr += out->stride; + in_ptr += obj_size; } } From 61596826b89af9dc20a53bae79b2b41e2bdc1fb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Hellstr=C3=B6m?= Date: Fri, 5 Jun 2026 11:33:05 +0200 Subject: [PATCH 0400/1054] drm/xe/rtp: Fix build error with clang < 21 and non-const initializers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clang < 21 treats const-qualified compound literals at function scope as having static storage duration, which requires all initializer elements to be compile-time constants. When xe_hw_engine.c initializes a local struct xe_rtp_table_sr using XE_RTP_TABLE_SR(), the compound literals in XE_RTP_TABLE_SR end up containing runtime values (e.g. blit_cctl_val derived from gt->mocs.uc_index), triggering: xe_hw_engine.c:361: error: initializer element is not a compile-time constant xe_hw_engine.c:416: error: initializer element is not a compile-time constant ARRAY_SIZE() cannot be used as a replacement because it expands through __must_be_array() -> __BUILD_BUG_ON_ZERO_MSG() -> _Static_assert inside sizeof(struct{}), which clang < 21 also rejects in the same context. Replace ARRAY_SIZE() with an open-coded sizeof(arr)/sizeof(elem) in XE_RTP_TABLE_SR and XE_RTP_TABLE to avoid both issues. Fixes: e23fafb8594e ("drm/xe/rtp: Add struct types for RTP tables") Cc: Matt Roper Cc: Gustavo Sousa Cc: Violet Monti Cc: Matthew Brost Cc: Thomas Hellström Cc: Rodrigo Vivi Cc: Ashutosh Dixit Cc: intel-xe@lists.freedesktop.org Reported-by: Mark Brown Closes: https://lore.kernel.org/intel-xe/bfb0dee8-b243-47ba-a89d-71472b0d51c5@sirena.org.uk/ Assisted-by: GitHub_Copilot:claude-sonnet-4.6 Signed-off-by: Thomas Hellström Reviewed-by: Gustavo Sousa Link: https://patch.msgid.link/20260605093305.110598-1-thomas.hellstrom@linux.intel.com (cherry picked from commit a57011eff45e7265dc42a7adad68b84605d8f828) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_rtp.h | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_rtp.h b/drivers/gpu/drm/xe/xe_rtp.h index 4e3cfd69f922..2cc65053cd07 100644 --- a/drivers/gpu/drm/xe/xe_rtp.h +++ b/drivers/gpu/drm/xe/xe_rtp.h @@ -461,14 +461,22 @@ struct xe_reg_sr; XE_RTP_PASTE_FOREACH(ACTION_, COMMA, (__VA_ARGS__)) \ } +/* + * Note: ARRAY_SIZE() cannot be used here because it expands through + * __must_be_array() -> __BUILD_BUG_ON_ZERO_MSG() -> _Static_assert inside + * sizeof(struct{}), which clang < 21 rejects when the compound literal + * contains non-compile-time-constant initializers. + */ #define XE_RTP_TABLE_SR(...) { \ .entries = (const struct xe_rtp_entry_sr[]){__VA_ARGS__}, \ - .n_entries = ARRAY_SIZE(((const struct xe_rtp_entry_sr[]){__VA_ARGS__})), \ + .n_entries = sizeof((const struct xe_rtp_entry_sr[]){__VA_ARGS__}) / \ + sizeof(struct xe_rtp_entry_sr), \ } #define XE_RTP_TABLE(...) { \ .entries = (const struct xe_rtp_entry[]){__VA_ARGS__}, \ - .n_entries = ARRAY_SIZE(((const struct xe_rtp_entry[]){__VA_ARGS__})), \ + .n_entries = sizeof((const struct xe_rtp_entry[]){__VA_ARGS__}) / \ + sizeof(struct xe_rtp_entry), \ } #define XE_RTP_PROCESS_CTX_INITIALIZER(arg__) _Generic((arg__), \ From 31e2437561621b4867c08efc890bf629d017df03 Mon Sep 17 00:00:00 2001 From: Ashutosh Dixit Date: Mon, 15 Jun 2026 15:42:20 -0700 Subject: [PATCH 0401/1054] drm/xe/rtp: Maintain OA whitelists separately MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OA registers are dynamically whitelisted (and again dewhitelisted) on OA stream open/close. Maintaining OA whitelists separately from non-OA register whitlists simplifies this management of OA register whitelisting/dewhitelisting. Fixes: 828a8eaf37c3 ("drm/xe/oa: Add MMIO trigger support") Cc: stable@vger.kernel.org # v6.12+ Signed-off-by: Ashutosh Dixit Reviewed-by: Umesh Nerlige Ramappa Link: https://patch.msgid.link/20260615224227.34880-3-ashutosh.dixit@intel.com (cherry picked from commit c478244a9e2d14b3f1f92e8bd293919e554622a5) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_gt_debugfs.c | 4 +++- drivers/gpu/drm/xe/xe_hw_engine.c | 2 ++ drivers/gpu/drm/xe/xe_hw_engine_types.h | 8 ++++++++ drivers/gpu/drm/xe/xe_reg_whitelist.c | 5 +++++ 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/xe/xe_gt_debugfs.c b/drivers/gpu/drm/xe/xe_gt_debugfs.c index f45306308cd6..c38bcacb27e4 100644 --- a/drivers/gpu/drm/xe/xe_gt_debugfs.c +++ b/drivers/gpu/drm/xe/xe_gt_debugfs.c @@ -149,8 +149,10 @@ static int register_save_restore(struct xe_gt *gt, struct drm_printer *p) drm_printf(p, "\n"); drm_printf(p, "Whitelist\n"); - for_each_hw_engine(hwe, gt, id) + for_each_hw_engine(hwe, gt, id) { xe_reg_whitelist_dump(&hwe->reg_whitelist, p); + xe_reg_whitelist_dump(&hwe->oa_whitelist, p); + } return 0; } diff --git a/drivers/gpu/drm/xe/xe_hw_engine.c b/drivers/gpu/drm/xe/xe_hw_engine.c index 98265293f2dc..55632ac4dfe7 100644 --- a/drivers/gpu/drm/xe/xe_hw_engine.c +++ b/drivers/gpu/drm/xe/xe_hw_engine.c @@ -572,6 +572,8 @@ static void hw_engine_init_early(struct xe_gt *gt, struct xe_hw_engine *hwe, hw_engine_setup_default_state(hwe); xe_reg_sr_init(&hwe->reg_whitelist, hwe->name, gt_to_xe(gt)); + xe_reg_sr_init(&hwe->oa_whitelist, hwe->name, gt_to_xe(gt)); + xe_reg_sr_init(&hwe->oa_sr, hwe->name, gt_to_xe(gt)); xe_reg_whitelist_process_engine(hwe); } diff --git a/drivers/gpu/drm/xe/xe_hw_engine_types.h b/drivers/gpu/drm/xe/xe_hw_engine_types.h index 2cf898e682f5..84c097da9b6f 100644 --- a/drivers/gpu/drm/xe/xe_hw_engine_types.h +++ b/drivers/gpu/drm/xe/xe_hw_engine_types.h @@ -130,6 +130,14 @@ struct xe_hw_engine { * @reg_whitelist: table with registers to be whitelisted */ struct xe_reg_sr reg_whitelist; + /** + * @oa_whitelist: oa registers to be whitelisted + */ + struct xe_reg_sr oa_whitelist; + /** + * @oa_sr: oa nonpriv whitelist registers, changed on oa stream open/close + */ + struct xe_reg_sr oa_sr; /** * @reg_lrc: LRC workaround registers */ diff --git a/drivers/gpu/drm/xe/xe_reg_whitelist.c b/drivers/gpu/drm/xe/xe_reg_whitelist.c index 2d8ddb57412c..6d642c2f6fd7 100644 --- a/drivers/gpu/drm/xe/xe_reg_whitelist.c +++ b/drivers/gpu/drm/xe/xe_reg_whitelist.c @@ -103,6 +103,9 @@ static const struct xe_rtp_table_sr register_whitelist = XE_RTP_TABLE_SR( WHITELIST(VFLSKPD, RING_FORCE_TO_NONPRIV_ACCESS_RW)) }, +); + +static const struct xe_rtp_table_sr oa_whitelist = XE_RTP_TABLE_SR( #define WHITELIST_DENY(r, f) WHITELIST(r, (f) | RING_FORCE_TO_NONPRIV_DENY) @@ -206,6 +209,8 @@ void xe_reg_whitelist_process_engine(struct xe_hw_engine *hwe) xe_rtp_process_to_sr(&ctx, ®ister_whitelist, &hwe->reg_whitelist, false); whitelist_apply_to_hwe(hwe); + + xe_rtp_process_to_sr(&ctx, &oa_whitelist, &hwe->oa_whitelist, false); } /** From 60d49ea28bb190a640bd8dc3f4c946e0811a948c Mon Sep 17 00:00:00 2001 From: Ashutosh Dixit Date: Mon, 15 Jun 2026 15:42:21 -0700 Subject: [PATCH 0402/1054] drm/xe/rtp: Keep track of non-OA nonpriv slots MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to dynamically whitelist/dewhitelist OA registers on OA stream open/close, we need to keep track of nonpriv slots occupied by non-OA register whitelists. Fixes: 828a8eaf37c3 ("drm/xe/oa: Add MMIO trigger support") Cc: stable@vger.kernel.org # v6.12+ Signed-off-by: Ashutosh Dixit Reviewed-by: Umesh Nerlige Ramappa Link: https://patch.msgid.link/20260615224227.34880-4-ashutosh.dixit@intel.com (cherry picked from commit 15739920b71ef3c56868973b4e7e3164a793d09d) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_reg_whitelist.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_reg_whitelist.c b/drivers/gpu/drm/xe/xe_reg_whitelist.c index 6d642c2f6fd7..b5ae7d26e5ba 100644 --- a/drivers/gpu/drm/xe/xe_reg_whitelist.c +++ b/drivers/gpu/drm/xe/xe_reg_whitelist.c @@ -161,7 +161,7 @@ static const struct xe_rtp_table_sr oa_whitelist = XE_RTP_TABLE_SR( }, ); -static void whitelist_apply_to_hwe(struct xe_hw_engine *hwe) +static int whitelist_apply_to_hwe(struct xe_hw_engine *hwe) { struct xe_reg_sr *sr = &hwe->reg_whitelist; struct xe_reg_sr_entry *entry; @@ -193,6 +193,8 @@ static void whitelist_apply_to_hwe(struct xe_hw_engine *hwe) slot++; } + + return slot; } /** @@ -206,9 +208,10 @@ static void whitelist_apply_to_hwe(struct xe_hw_engine *hwe) void xe_reg_whitelist_process_engine(struct xe_hw_engine *hwe) { struct xe_rtp_process_ctx ctx = XE_RTP_PROCESS_CTX_INITIALIZER(hwe); + int first_oa_slot; xe_rtp_process_to_sr(&ctx, ®ister_whitelist, &hwe->reg_whitelist, false); - whitelist_apply_to_hwe(hwe); + first_oa_slot = whitelist_apply_to_hwe(hwe); xe_rtp_process_to_sr(&ctx, &oa_whitelist, &hwe->oa_whitelist, false); } From 4fe2844b0f0c7cdc45ca4c4c62ca56b7f26c514c Mon Sep 17 00:00:00 2001 From: Ashutosh Dixit Date: Mon, 15 Jun 2026 15:42:22 -0700 Subject: [PATCH 0403/1054] drm/xe/rtp: Generalize whitelist_apply_to_hwe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Generalize whitelist_apply_to_hwe to construct both non-OA and OA whitelist nonpriv registers. Fixes: 828a8eaf37c3 ("drm/xe/oa: Add MMIO trigger support") Cc: stable@vger.kernel.org # v6.12+ Signed-off-by: Ashutosh Dixit Reviewed-by: Umesh Nerlige Ramappa Link: https://patch.msgid.link/20260615224227.34880-5-ashutosh.dixit@intel.com (cherry picked from commit c3ff77d7235ccef7a0883c2fd981f70ef3aafd21) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_reg_whitelist.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_reg_whitelist.c b/drivers/gpu/drm/xe/xe_reg_whitelist.c index b5ae7d26e5ba..e9d0a0b82527 100644 --- a/drivers/gpu/drm/xe/xe_reg_whitelist.c +++ b/drivers/gpu/drm/xe/xe_reg_whitelist.c @@ -161,9 +161,10 @@ static const struct xe_rtp_table_sr oa_whitelist = XE_RTP_TABLE_SR( }, ); -static int whitelist_apply_to_hwe(struct xe_hw_engine *hwe) +static int whitelist_apply_to_hwe(struct xe_hw_engine *hwe, struct xe_reg_sr *in, + struct xe_reg_sr *out, int first_slot) { - struct xe_reg_sr *sr = &hwe->reg_whitelist; + struct xe_reg_sr *sr = in; struct xe_reg_sr_entry *entry; struct drm_printer p; unsigned long reg; @@ -172,7 +173,7 @@ static int whitelist_apply_to_hwe(struct xe_hw_engine *hwe) xe_gt_dbg(hwe->gt, "Add %s whitelist to engine\n", sr->name); p = xe_gt_dbg_printer(hwe->gt); - slot = 0; + slot = first_slot; xa_for_each(&sr->xa, reg, entry) { struct xe_reg_sr_entry hwe_entry = { .reg = RING_FORCE_TO_NONPRIV(hwe->mmio_base, slot), @@ -189,7 +190,7 @@ static int whitelist_apply_to_hwe(struct xe_hw_engine *hwe) } xe_reg_whitelist_print_entry(&p, 0, reg, entry); - xe_reg_sr_add(&hwe->reg_sr, &hwe_entry, hwe->gt); + xe_reg_sr_add(out, &hwe_entry, hwe->gt); slot++; } @@ -211,7 +212,7 @@ void xe_reg_whitelist_process_engine(struct xe_hw_engine *hwe) int first_oa_slot; xe_rtp_process_to_sr(&ctx, ®ister_whitelist, &hwe->reg_whitelist, false); - first_oa_slot = whitelist_apply_to_hwe(hwe); + first_oa_slot = whitelist_apply_to_hwe(hwe, &hwe->reg_whitelist, &hwe->reg_sr, 0); xe_rtp_process_to_sr(&ctx, &oa_whitelist, &hwe->oa_whitelist, false); } From a19a83721a28ccaddace846da70da5c53d7dd052 Mon Sep 17 00:00:00 2001 From: Ashutosh Dixit Date: Mon, 15 Jun 2026 15:42:23 -0700 Subject: [PATCH 0404/1054] drm/xe/rtp: Save OA nonpriv registers to register save/restore lists MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now we can save OA whitelisting nonpriv registers to register save/restore lists. OA nonpriv registers are saved to both hwe->oa_sr as well as hwe->reg_sr. During probe, resume and gt-reset flows KMD will apply hwe->reg_sr, ensuring OA registers are de-whitelisted after these events. For engine-reset, hwe->reg_sr is registered with GuC and GuC will apply these registers, ensuring OA registers are de-whitelisted after engine resets. hwe->oa_sr is used for whitelisting or de-whitelisting OA registers during OA operation, by toggling the 'deny' bit on oa stream open/close. Fixes: 828a8eaf37c3 ("drm/xe/oa: Add MMIO trigger support") Cc: stable@vger.kernel.org # v6.12+ Signed-off-by: Ashutosh Dixit Reviewed-by: Umesh Nerlige Ramappa Link: https://patch.msgid.link/20260615224227.34880-6-ashutosh.dixit@intel.com (cherry picked from commit 3a3c3e56db2923daaf1a5353cd6463a4cdaf4ffa) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_reg_whitelist.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/gpu/drm/xe/xe_reg_whitelist.c b/drivers/gpu/drm/xe/xe_reg_whitelist.c index e9d0a0b82527..76ac23644a4d 100644 --- a/drivers/gpu/drm/xe/xe_reg_whitelist.c +++ b/drivers/gpu/drm/xe/xe_reg_whitelist.c @@ -215,6 +215,18 @@ void xe_reg_whitelist_process_engine(struct xe_hw_engine *hwe) first_oa_slot = whitelist_apply_to_hwe(hwe, &hwe->reg_whitelist, &hwe->reg_sr, 0); xe_rtp_process_to_sr(&ctx, &oa_whitelist, &hwe->oa_whitelist, false); + + /* + * Save oa nonpriv registers to hwe->oa_sr, from which oa registers are whitelisted + * or de-whitelisted, by toggling the 'deny' bit on oa stream open/close + */ + whitelist_apply_to_hwe(hwe, &hwe->oa_whitelist, &hwe->oa_sr, first_oa_slot); + + /* + * Also save oa nonpriv registers to hwe->reg_sr, to ensure oa registers are not + * whitelisted by default after probe, gt reset, resume and engine reset + */ + whitelist_apply_to_hwe(hwe, &hwe->oa_whitelist, &hwe->reg_sr, first_oa_slot); } /** From b422babd77fac2c96b92db484050e460899bddaf Mon Sep 17 00:00:00 2001 From: Ashutosh Dixit Date: Mon, 15 Jun 2026 15:42:24 -0700 Subject: [PATCH 0405/1054] drm/xe/rtp: Toggle 'deny' bit to (de-)whitelist OA regs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Whitelist or de-whitelist OA registers by setting or resetting the 'deny' bit in OA nonpriv registers and writing new register values to HW. Fixes: 828a8eaf37c3 ("drm/xe/oa: Add MMIO trigger support") Cc: stable@vger.kernel.org # v6.12+ Signed-off-by: Ashutosh Dixit Reviewed-by: Umesh Nerlige Ramappa Link: https://patch.msgid.link/20260615224227.34880-7-ashutosh.dixit@intel.com (cherry picked from commit aeaa7d2bb017272ab9e18759fe00bf758cd3299f) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_reg_whitelist.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/gpu/drm/xe/xe_reg_whitelist.c b/drivers/gpu/drm/xe/xe_reg_whitelist.c index 76ac23644a4d..7186998df498 100644 --- a/drivers/gpu/drm/xe/xe_reg_whitelist.c +++ b/drivers/gpu/drm/xe/xe_reg_whitelist.c @@ -229,6 +229,21 @@ void xe_reg_whitelist_process_engine(struct xe_hw_engine *hwe) whitelist_apply_to_hwe(hwe, &hwe->oa_whitelist, &hwe->reg_sr, first_oa_slot); } +__maybe_unused static void __whitelist_oa_regs(struct xe_hw_engine *hwe, bool whitelist) +{ + struct xe_reg_sr_entry *entry; + unsigned long reg; + + xa_for_each(&hwe->oa_sr.xa, reg, entry) { + if (whitelist) + entry->set_bits &= ~RING_FORCE_TO_NONPRIV_DENY; + else + entry->set_bits |= RING_FORCE_TO_NONPRIV_DENY; + } + + xe_reg_sr_apply_mmio(&hwe->oa_sr, hwe->gt); +} + /** * xe_reg_whitelist_print_entry - print one whitelist entry * @p: DRM printer From ebba7ce65252a4ab0e3794ff14854df2afca5c08 Mon Sep 17 00:00:00 2001 From: Ashutosh Dixit Date: Mon, 15 Jun 2026 15:42:25 -0700 Subject: [PATCH 0406/1054] drm/xe/rtp: (De-)whitelist OA registers for all hwe's for a gt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Whitelist or de-whitelist OA registers for all hwe's on the gt on which the OA stream is opened. This simplifies the case where an oa unit has 0 attached hwe's (but which monitors OA events on the associated GT). Fixes: 828a8eaf37c3 ("drm/xe/oa: Add MMIO trigger support") Cc: stable@vger.kernel.org # v6.12+ Signed-off-by: Ashutosh Dixit Reviewed-by: Umesh Nerlige Ramappa Link: https://patch.msgid.link/20260615224227.34880-8-ashutosh.dixit@intel.com (cherry picked from commit 6f73bf8fffa728aa5d5ee143ba318fa0744113a2) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_reg_whitelist.c | 32 ++++++++++++++++++++++++++- drivers/gpu/drm/xe/xe_reg_whitelist.h | 4 ++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/xe/xe_reg_whitelist.c b/drivers/gpu/drm/xe/xe_reg_whitelist.c index 7186998df498..b2e7aabd19d7 100644 --- a/drivers/gpu/drm/xe/xe_reg_whitelist.c +++ b/drivers/gpu/drm/xe/xe_reg_whitelist.c @@ -229,7 +229,7 @@ void xe_reg_whitelist_process_engine(struct xe_hw_engine *hwe) whitelist_apply_to_hwe(hwe, &hwe->oa_whitelist, &hwe->reg_sr, first_oa_slot); } -__maybe_unused static void __whitelist_oa_regs(struct xe_hw_engine *hwe, bool whitelist) +static void __whitelist_oa_regs(struct xe_hw_engine *hwe, bool whitelist) { struct xe_reg_sr_entry *entry; unsigned long reg; @@ -244,6 +244,36 @@ __maybe_unused static void __whitelist_oa_regs(struct xe_hw_engine *hwe, bool wh xe_reg_sr_apply_mmio(&hwe->oa_sr, hwe->gt); } +/** + * xe_reg_whitelist_oa_regs - whitelist oa registers for gt + * @gt: gt to whitelist oa registers for + * + * Whitelist OA registers by resetting RING_FORCE_TO_NONPRIV_DENY + */ +void xe_reg_whitelist_oa_regs(struct xe_gt *gt) +{ + struct xe_hw_engine *hwe; + enum xe_hw_engine_id id; + + for_each_hw_engine(hwe, gt, id) + __whitelist_oa_regs(hwe, true); +} + +/** + * xe_reg_dewhitelist_oa_regs - dewhitelist oa registers for gt + * @gt: gt to dewhitelist oa registers for + * + * Dewhitelist OA registers by setting RING_FORCE_TO_NONPRIV_DENY + */ +void xe_reg_dewhitelist_oa_regs(struct xe_gt *gt) +{ + struct xe_hw_engine *hwe; + enum xe_hw_engine_id id; + + for_each_hw_engine(hwe, gt, id) + __whitelist_oa_regs(hwe, false); +} + /** * xe_reg_whitelist_print_entry - print one whitelist entry * @p: DRM printer diff --git a/drivers/gpu/drm/xe/xe_reg_whitelist.h b/drivers/gpu/drm/xe/xe_reg_whitelist.h index 3b64b42fe96e..e1eb1b7d5480 100644 --- a/drivers/gpu/drm/xe/xe_reg_whitelist.h +++ b/drivers/gpu/drm/xe/xe_reg_whitelist.h @@ -9,12 +9,16 @@ #include struct drm_printer; +struct xe_gt; struct xe_hw_engine; struct xe_reg_sr; struct xe_reg_sr_entry; void xe_reg_whitelist_process_engine(struct xe_hw_engine *hwe); +void xe_reg_whitelist_oa_regs(struct xe_gt *gt); +void xe_reg_dewhitelist_oa_regs(struct xe_gt *gt); + void xe_reg_whitelist_print_entry(struct drm_printer *p, unsigned int indent, u32 reg, struct xe_reg_sr_entry *entry); From 63ddb3ad08ff4e89c108499dfec5e9be5ddc25c9 Mon Sep 17 00:00:00 2001 From: Ashutosh Dixit Date: Mon, 15 Jun 2026 15:42:26 -0700 Subject: [PATCH 0407/1054] drm/xe/oa: (De-)whitelist OA registers on OA stream open/release MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Whitelist OA registers on stream open and de-whitelist on stream close/release. Whitelisting is only done when 'stream->sample' is true. 'stream->sample' is only true when (a) xe_observation_paranoid is set to false by system admin, or (b) the process is perfmon_capable(). This therefore enforces the OA register whitelisting security requirements. Fixes: 828a8eaf37c3 ("drm/xe/oa: Add MMIO trigger support") Cc: stable@vger.kernel.org # v6.12+ Signed-off-by: Ashutosh Dixit Reviewed-by: Umesh Nerlige Ramappa Link: https://patch.msgid.link/20260615224227.34880-9-ashutosh.dixit@intel.com (cherry picked from commit f8e6874f46f19a6a2a0f24a81689f90641bb402a) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_oa.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/gpu/drm/xe/xe_oa.c b/drivers/gpu/drm/xe/xe_oa.c index 4bf4b1f65929..2dce6a47202c 100644 --- a/drivers/gpu/drm/xe/xe_oa.c +++ b/drivers/gpu/drm/xe/xe_oa.c @@ -37,6 +37,7 @@ #include "xe_oa.h" #include "xe_observation.h" #include "xe_pm.h" +#include "xe_reg_whitelist.h" #include "xe_sched_job.h" #include "xe_sriov.h" #include "xe_sync.h" @@ -885,6 +886,9 @@ static void xe_oa_stream_destroy(struct xe_oa_stream *stream) mutex_destroy(&stream->stream_lock); + if (stream->sample) + xe_reg_dewhitelist_oa_regs(stream->gt); + xe_oa_disable_metric_set(stream); xe_exec_queue_put(stream->k_exec_q); @@ -1885,6 +1889,9 @@ static int xe_oa_stream_open_ioctl_locked(struct xe_oa *oa, goto err_disable; } + if (stream->sample) + xe_reg_whitelist_oa_regs(stream->gt); + /* Hold a reference on the drm device till stream_fd is released */ drm_dev_get(&stream->oa->xe->drm); From ef78e2a22f72c892fd6663f0760abd208d49a3e2 Mon Sep 17 00:00:00 2001 From: Ashutosh Dixit Date: Mon, 15 Jun 2026 15:42:27 -0700 Subject: [PATCH 0408/1054] drm/xe/rtp: Ensure locking/ref counting for OA whitelists MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since multiple OA streams might be open in parallel on a gt, ensure that proper locking is in place. Also ensure that OA registers are whitelisted when the first OA stream is open and de-whitelisted after the last OA stream is closed. Fixes: 828a8eaf37c3 ("drm/xe/oa: Add MMIO trigger support") Cc: stable@vger.kernel.org # v6.12+ Signed-off-by: Ashutosh Dixit Reviewed-by: Umesh Nerlige Ramappa Link: https://patch.msgid.link/20260615224227.34880-10-ashutosh.dixit@intel.com (cherry picked from commit 645f1a2589bd4782e25490e5ecc05b7043c36cbf) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_oa_types.h | 3 +++ drivers/gpu/drm/xe/xe_reg_whitelist.c | 9 +++++++++ 2 files changed, 12 insertions(+) diff --git a/drivers/gpu/drm/xe/xe_oa_types.h b/drivers/gpu/drm/xe/xe_oa_types.h index 3d9ec8490899..e876e9be92ba 100644 --- a/drivers/gpu/drm/xe/xe_oa_types.h +++ b/drivers/gpu/drm/xe/xe_oa_types.h @@ -126,6 +126,9 @@ struct xe_oa_gt { /** @oa_unit: array of oa_units */ struct xe_oa_unit *oa_unit; + + /** @whitelist_count: number of open streams for which oa registers are whitelisted */ + u32 whitelist_count; }; /** diff --git a/drivers/gpu/drm/xe/xe_reg_whitelist.c b/drivers/gpu/drm/xe/xe_reg_whitelist.c index b2e7aabd19d7..3d9e3daab01a 100644 --- a/drivers/gpu/drm/xe/xe_reg_whitelist.c +++ b/drivers/gpu/drm/xe/xe_reg_whitelist.c @@ -255,6 +255,10 @@ void xe_reg_whitelist_oa_regs(struct xe_gt *gt) struct xe_hw_engine *hwe; enum xe_hw_engine_id id; + lockdep_assert_held(>->oa.gt_lock); + if (gt->oa.whitelist_count++) + return; + for_each_hw_engine(hwe, gt, id) __whitelist_oa_regs(hwe, true); } @@ -270,6 +274,11 @@ void xe_reg_dewhitelist_oa_regs(struct xe_gt *gt) struct xe_hw_engine *hwe; enum xe_hw_engine_id id; + lockdep_assert_held(>->oa.gt_lock); + xe_assert(gt_to_xe(gt), gt->oa.whitelist_count); + if (--gt->oa.whitelist_count) + return; + for_each_hw_engine(hwe, gt, id) __whitelist_oa_regs(hwe, false); } From 136fb61ba8571076dc5d49350a0e6d002d740b74 Mon Sep 17 00:00:00 2001 From: Matthew Brost Date: Wed, 17 Jun 2026 06:51:01 -0700 Subject: [PATCH 0409/1054] drm/xe: Return error on non-migratable faults requiring devmem MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Non-migratable faults that require devmem incorrectly jump to the 'out' label, which squashes the error code intended to be returned to the upper layers. Fix this by returning -EACCES instead. Reported-by: Sashiko Fixes: 4208fac3dce5 ("drm/xe: Add more SVM GT stats") Cc: stable@vger.kernel.org Signed-off-by: Matthew Brost Reviewed-by: Francois Dugast Link: https://patch.msgid.link/20260617135101.1245574-1-matthew.brost@intel.com (cherry picked from commit c4508edb2c723de93717272488ea65b165637eac) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_svm.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_svm.c b/drivers/gpu/drm/xe/xe_svm.c index e1651e70c8f0..b1e1ac26c66d 100644 --- a/drivers/gpu/drm/xe/xe_svm.c +++ b/drivers/gpu/drm/xe/xe_svm.c @@ -1248,10 +1248,8 @@ static int __xe_svm_handle_pagefault(struct xe_vm *vm, struct xe_vma *vma, xe_svm_range_fault_count_stats_incr(gt, range); - if (ctx.devmem_only && !range->base.pages.flags.migrate_devmem) { - err = -EACCES; - goto out; - } + if (ctx.devmem_only && !range->base.pages.flags.migrate_devmem) + return -EACCES; if (xe_svm_range_is_valid(range, tile, ctx.devmem_only, dpagemap)) { xe_svm_range_valid_fault_count_stats_incr(gt, range); From d472497265374e895e31cf2af8a2c5f650019889 Mon Sep 17 00:00:00 2001 From: Matthew Auld Date: Fri, 12 Jun 2026 18:05:02 +0100 Subject: [PATCH 0410/1054] drm/xe/display: skip FORCE_WC and vm_bound check for external dma-bufs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, xe_display_bo_framebuffer_init() unconditionally attempts to apply XE_BO_FLAG_FORCE_WC to the buffer and rejects the FB creation with -EINVAL if the BO is already VM_BINDed. However, for imported dma-bufs (ttm_bo_type_sg), this check doesn't seem to make much sense since CPU caching policy is entirely controlled by the exporter. Plus there is no place to set this flag, in the first place. Also this is not rejected if not yet vm_binded, but that seems arbitrary since setting or not setting FORCE_WC should a noop either way, at this stage, and whether it is currently VM_BINDed makes no difference. Currently if we run an app and offload rendering to an external dGPU, like NV or another xe device, the dma-buf passed back to the compositor (igpu) will be an actual external import from xe pov, and it will be missing FORCE_WC, and if the compositor side did a VM_BIND before turning into it into an fb the whole thing gets rejected. So it looks like we either need to reject outright, no matter what, or this usecase is valid and we need to loosen the restriction for sg buffers. Proposing here to loosen the restriction. Assisted-by: Gemini:gemini-3.1-pro-preview Link: https://gitlab.freedesktop.org/drm/xe/kernel/-/work_items/7919 Fixes: 44e694958b95 ("drm/xe/display: Implement display support") Signed-off-by: Matthew Auld Cc: Thomas Hellström Cc: Matthew Brost Cc: Maarten Lankhorst Cc: # v6.12+ Reviewed-by: Maarten Lankhorst Link: https://patch.msgid.link/20260612170501.550816-2-matthew.auld@intel.com (cherry picked from commit 3e493f88c84088ccd7b53cdd23ac5c875c9a60dd) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/display/xe_display_bo.c | 3 ++- drivers/gpu/drm/xe/display/xe_fb_pin.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/xe/display/xe_display_bo.c b/drivers/gpu/drm/xe/display/xe_display_bo.c index 7fbac223b097..8953da0136dc 100644 --- a/drivers/gpu/drm/xe/display/xe_display_bo.c +++ b/drivers/gpu/drm/xe/display/xe_display_bo.c @@ -48,7 +48,8 @@ static int xe_display_bo_framebuffer_init(struct drm_gem_object *obj, if (ret) goto err; - if (!(bo->flags & XE_BO_FLAG_FORCE_WC)) { + if (!(bo->flags & XE_BO_FLAG_FORCE_WC) && + bo->ttm.type != ttm_bo_type_sg) { /* * XE_BO_FLAG_FORCE_WC should ideally be set at creation, or is * automatically set when creating FB. We cannot change caching diff --git a/drivers/gpu/drm/xe/display/xe_fb_pin.c b/drivers/gpu/drm/xe/display/xe_fb_pin.c index f93c98bec5b5..5f4a0cd8deca 100644 --- a/drivers/gpu/drm/xe/display/xe_fb_pin.c +++ b/drivers/gpu/drm/xe/display/xe_fb_pin.c @@ -331,7 +331,8 @@ static struct i915_vma *__xe_pin_fb_vma(struct drm_gem_object *obj, bool is_dpt, int ret = 0; /* We reject creating !SCANOUT fb's, so this is weird.. */ - drm_WARN_ON(bo->ttm.base.dev, !(bo->flags & XE_BO_FLAG_FORCE_WC)); + drm_WARN_ON(bo->ttm.base.dev, !(bo->flags & XE_BO_FLAG_FORCE_WC) && + bo->ttm.type != ttm_bo_type_sg); if (!vma) return ERR_PTR(-ENODEV); From dca6e08c923a44d2d66b955e03dd57a3a38c2b94 Mon Sep 17 00:00:00 2001 From: Shuicheng Lin Date: Thu, 25 Jun 2026 21:56:15 +0000 Subject: [PATCH 0411/1054] drm/xe/userptr: Hold notifier_lock for write on inject test path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When CONFIG_DRM_XE_USERPTR_INVAL_INJECT=y, xe_pt_svm_userptr_pre_commit() runs vma_check_userptr() with the svm notifier_lock taken for read. The test injection causes vma_check_userptr() to call xe_vma_userptr_force_invalidate(), which feeds into xe_vma_userptr_do_inval() with drm_gpusvm_ctx.in_notifier=true. That flag tells drm_gpusvm_unmap_pages() the caller already holds notifier_lock for write and only asserts the mode. Because the caller actually holds it for read, the assertion fires: WARNING: drivers/gpu/drm/drm_gpusvm.c:1669 at \ drm_gpusvm_unmap_pages+0xd4/0x130 [drm_gpusvm_helper] Call Trace: xe_vma_userptr_do_inval+0x40d/0xfd0 [xe] xe_vma_userptr_invalidate_pass1+0x3e6/0x8d0 [xe] xe_vma_userptr_force_invalidate+0xde/0x290 [xe] vma_check_userptr.constprop.0+0x1c6/0x220 [xe] xe_pt_svm_userptr_pre_commit+0x6a3/0xc60 [xe] ... xe_vm_bind_ioctl+0x3a0a/0x4480 [xe] Acquire notifier_lock for write in pre-commit when the inject Kconfig is enabled, via new helpers xe_pt_svm_userptr_notifier_lock()/_unlock(). Rename xe_svm_assert_held_read() to xe_svm_assert_held_read_or_inject_write() so it asserts the correct mode under each build configuration. Production builds (CONFIG_DRM_XE_USERPTR_INVAL_INJECT=n) keep the existing read-mode behavior bit-for-bit. Fixes: 9e9787414882 ("drm/xe/userptr: replace xe_hmm with gpusvm") Assisted-by: Claude:claude-opus-4.7 Cc: Matthew Auld Cc: Zongyao Bai Reviewed-by: Matthew Brost Link: https://patch.msgid.link/20260625215615.3016892-1-shuicheng.lin@intel.com Signed-off-by: Shuicheng Lin (cherry picked from commit 80ccbd97ffee8ad2e73167d826fe7be548364365) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_pt.c | 43 ++++++++++++++++++++++++++++++------- drivers/gpu/drm/xe/xe_svm.h | 15 +++++++++++-- 2 files changed, 48 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_pt.c b/drivers/gpu/drm/xe/xe_pt.c index 234ea175c5e3..3380ce710a48 100644 --- a/drivers/gpu/drm/xe/xe_pt.c +++ b/drivers/gpu/drm/xe/xe_pt.c @@ -1086,7 +1086,7 @@ static void xe_pt_commit_locks_assert(struct xe_vma *vma) xe_pt_commit_prepare_locks_assert(vma); if (xe_vma_is_userptr(vma)) - xe_svm_assert_held_read(vm); + xe_svm_assert_held_read_or_inject_write(vm); } static void xe_pt_commit(struct xe_vma *vma, @@ -1406,6 +1406,33 @@ static int xe_pt_pre_commit(struct xe_migrate_pt_update *pt_update) pt_update_ops, rftree); } +/* + * Acquire/release the svm notifier_lock around xe_pt_svm_userptr_pre_commit() + * and the matching late release in xe_pt_update_ops_run(). Read mode by + * default; write mode when CONFIG_DRM_XE_USERPTR_INVAL_INJECT is on, + * because a userptr op in this critical section may invoke the injected + * xe_vma_userptr_force_invalidate() path that calls + * drm_gpusvm_unmap_pages() with ctx->in_notifier=true, which requires the + * lock held for write. + */ +static void xe_pt_svm_userptr_notifier_lock(struct xe_vm *vm) +{ +#if IS_ENABLED(CONFIG_DRM_XE_USERPTR_INVAL_INJECT) + down_write(&vm->svm.gpusvm.notifier_lock); +#else + xe_svm_notifier_lock(vm); +#endif +} + +static void xe_pt_svm_userptr_notifier_unlock(struct xe_vm *vm) +{ +#if IS_ENABLED(CONFIG_DRM_XE_USERPTR_INVAL_INJECT) + up_write(&vm->svm.gpusvm.notifier_lock); +#else + xe_svm_notifier_unlock(vm); +#endif +} + #if IS_ENABLED(CONFIG_DRM_GPUSVM) #ifdef CONFIG_DRM_XE_USERPTR_INVAL_INJECT @@ -1437,7 +1464,7 @@ static int vma_check_userptr(struct xe_vm *vm, struct xe_vma *vma, struct xe_userptr_vma *uvma; unsigned long notifier_seq; - xe_svm_assert_held_read(vm); + xe_svm_assert_held_read_or_inject_write(vm); if (!xe_vma_is_userptr(vma)) return 0; @@ -1467,7 +1494,7 @@ static int op_check_svm_userptr(struct xe_vm *vm, struct xe_vma_op *op, { int err = 0; - xe_svm_assert_held_read(vm); + xe_svm_assert_held_read_or_inject_write(vm); switch (op->base.op) { case DRM_GPUVA_OP_MAP: @@ -1539,12 +1566,12 @@ static int xe_pt_svm_userptr_pre_commit(struct xe_migrate_pt_update *pt_update) if (err) return err; - xe_svm_notifier_lock(vm); + xe_pt_svm_userptr_notifier_lock(vm); list_for_each_entry(op, &vops->list, link) { err = op_check_svm_userptr(vm, op, pt_update_ops); if (err) { - xe_svm_notifier_unlock(vm); + xe_pt_svm_userptr_notifier_unlock(vm); break; } } @@ -2403,7 +2430,7 @@ static void bind_op_commit(struct xe_vm *vm, struct xe_tile *tile, vma->tile_invalidated & ~BIT(tile->id)); vma->tile_staged &= ~BIT(tile->id); if (xe_vma_is_userptr(vma)) { - xe_svm_assert_held_read(vm); + xe_svm_assert_held_read_or_inject_write(vm); to_userptr_vma(vma)->userptr.initial_bind = true; } @@ -2439,7 +2466,7 @@ static void unbind_op_commit(struct xe_vm *vm, struct xe_tile *tile, if (!vma->tile_present) { list_del_init(&vma->combined_links.rebind); if (xe_vma_is_userptr(vma)) { - xe_svm_assert_held_read(vm); + xe_svm_assert_held_read_or_inject_write(vm); spin_lock(&vm->userptr.invalidated_lock); list_del_init(&to_userptr_vma(vma)->userptr.invalidate_link); @@ -2715,7 +2742,7 @@ xe_pt_update_ops_run(struct xe_tile *tile, struct xe_vma_ops *vops) } if (pt_update_ops->needs_svm_lock) - xe_svm_notifier_unlock(vm); + xe_pt_svm_userptr_notifier_unlock(vm); /* * The last fence is only used for zero bind queue idling; migrate diff --git a/drivers/gpu/drm/xe/xe_svm.h b/drivers/gpu/drm/xe/xe_svm.h index b7b8eeacf196..3ca46a6f98c7 100644 --- a/drivers/gpu/drm/xe/xe_svm.h +++ b/drivers/gpu/drm/xe/xe_svm.h @@ -394,8 +394,19 @@ static inline struct drm_pagemap *xe_drm_pagemap_from_fd(int fd, u32 region_inst #define xe_svm_assert_in_notifier(vm__) \ lockdep_assert_held_write(&(vm__)->svm.gpusvm.notifier_lock) -#define xe_svm_assert_held_read(vm__) \ +/* + * Assert the svm notifier_lock is held. Read mode by default; write mode + * when CONFIG_DRM_XE_USERPTR_INVAL_INJECT is on, because that path forces + * a userptr invalidation that ends in drm_gpusvm_unmap_pages() with + * ctx->in_notifier=true, which requires the lock held for write. + */ +#if IS_ENABLED(CONFIG_DRM_XE_USERPTR_INVAL_INJECT) +#define xe_svm_assert_held_read_or_inject_write(vm__) \ + lockdep_assert_held_write(&(vm__)->svm.gpusvm.notifier_lock) +#else +#define xe_svm_assert_held_read_or_inject_write(vm__) \ lockdep_assert_held_read(&(vm__)->svm.gpusvm.notifier_lock) +#endif #define xe_svm_notifier_lock(vm__) \ drm_gpusvm_notifier_lock(&(vm__)->svm.gpusvm) @@ -409,7 +420,7 @@ static inline struct drm_pagemap *xe_drm_pagemap_from_fd(int fd, u32 region_inst #else #define xe_svm_assert_in_notifier(...) do {} while (0) -static inline void xe_svm_assert_held_read(struct xe_vm *vm) +static inline void xe_svm_assert_held_read_or_inject_write(struct xe_vm *vm) { } From 0c56ea482aab1470b96a525ef53fa3eb8704f9a6 Mon Sep 17 00:00:00 2001 From: Shuicheng Lin Date: Thu, 25 Jun 2026 22:44:52 +0000 Subject: [PATCH 0412/1054] drm/xe/userptr: Drop bogus static from finish in force_invalidate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The local "finish" pointer in xe_vma_userptr_force_invalidate() is unconditionally written before each read, so the static storage class serves no purpose. Worse, it makes the variable a process-wide shared slot: the function's per-VM asserts do not exclude concurrent callers on different VMs, so two such callers can race on the slot and take the wrong if (finish) branch. The function is gated by CONFIG_DRM_XE_USERPTR_INVAL_INJECT (developer/test option, default n), so production builds are unaffected. Drop the static. Fixes: 18c4e536959e ("drm/xe/userptr: Convert invalidation to two-pass MMU notifier") Assisted-by: Claude:claude-opus-4.7 Cc: Thomas Hellström Cc: Matthew Brost Reviewed-by: Matthew Brost Reviewed-by: Zongyao Bai Link: https://patch.msgid.link/20260625224452.3243231-1-shuicheng.lin@intel.com Signed-off-by: Shuicheng Lin (cherry picked from commit ed382e3b07fae51a09d7290485bff0592f6b168b) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_userptr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/xe/xe_userptr.c b/drivers/gpu/drm/xe/xe_userptr.c index 6761005c0b90..6f71bc66b14e 100644 --- a/drivers/gpu/drm/xe/xe_userptr.c +++ b/drivers/gpu/drm/xe/xe_userptr.c @@ -269,7 +269,7 @@ static const struct mmu_interval_notifier_ops vma_userptr_notifier_ops = { */ void xe_vma_userptr_force_invalidate(struct xe_userptr_vma *uvma) { - static struct mmu_interval_notifier_finish *finish; + struct mmu_interval_notifier_finish *finish; struct xe_vm *vm = xe_vma_vm(&uvma->vma); /* Protect against concurrent userptr pinning */ From 7ac3cae7a251d28e9079de07a991bd4eb2bb7fd8 Mon Sep 17 00:00:00 2001 From: Shuicheng Lin Date: Fri, 26 Jun 2026 21:06:31 +0000 Subject: [PATCH 0413/1054] drm/xe/hw_engine: Fix double-free of managed BO in error path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The error path in hw_engine_init() explicitly frees a BO allocated with xe_managed_bo_create_pin_map() via xe_bo_unpin_map_no_vm(). Since the managed BO already has a devm cleanup action registered, this causes a double-free when devm unwinds during probe failure. Remove the explicit free and let devm handle it, consistent with all other xe_managed_bo_create_pin_map() callers. Fixes: 0e1a47fcabc8 ("drm/xe: Add a helper for DRM device-lifetime BO create") Assisted-by: Claude:claude-opus-4.6 Reviewed-by: Zongyao Bai Link: https://patch.msgid.link/20260626210631.3887291-1-shuicheng.lin@intel.com Signed-off-by: Shuicheng Lin (cherry picked from commit e459a3bdeb117be496d7f229e2ea1f6c9fe4080b) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_hw_engine.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_hw_engine.c b/drivers/gpu/drm/xe/xe_hw_engine.c index 55632ac4dfe7..0b193c451a11 100644 --- a/drivers/gpu/drm/xe/xe_hw_engine.c +++ b/drivers/gpu/drm/xe/xe_hw_engine.c @@ -628,7 +628,7 @@ static int hw_engine_init(struct xe_gt *gt, struct xe_hw_engine *hwe, hwe->exl_port = xe_execlist_port_create(xe, hwe); if (IS_ERR(hwe->exl_port)) { err = PTR_ERR(hwe->exl_port); - goto err_hwsp; + goto err_name; } } else { /* GSCCS has a special interrupt for reset */ @@ -648,8 +648,6 @@ static int hw_engine_init(struct xe_gt *gt, struct xe_hw_engine *hwe, return devm_add_action_or_reset(xe->drm.dev, hw_engine_fini, hwe); -err_hwsp: - xe_bo_unpin_map_no_vm(hwe->hwsp); err_name: hwe->name = NULL; From ed8b0d731892c68b41ecbd27c952af284816dec1 Mon Sep 17 00:00:00 2001 From: Michal Wajdeczko Date: Wed, 27 May 2026 20:37:35 +0200 Subject: [PATCH 0414/1054] drm/xe/pf: Don't attempt to process FAST_REQ or EVENT relays MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently defined VF/PF relay actions use regular REQUEST messages only and the PF shouldn't attempt to handle FAST_REQUEST nor EVENT messages as this would result in breaking the VFPF ABI protocol and also might trigger an assert on the PF side. Fixes: 98e62805921c ("drm/xe/pf: Add SR-IOV GuC Relay PF services") Signed-off-by: Michal Wajdeczko Reviewed-by: MichaÅ‚ Winiarski Link: https://patch.msgid.link/20260527183735.22616-1-michal.wajdeczko@intel.com (cherry picked from commit 1714d360fc5ae2e0886a69e979095d9c7ff3568a) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_guc_relay.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_guc_relay.c b/drivers/gpu/drm/xe/xe_guc_relay.c index 577a315854af..eed0a750d2eb 100644 --- a/drivers/gpu/drm/xe/xe_guc_relay.c +++ b/drivers/gpu/drm/xe/xe_guc_relay.c @@ -689,12 +689,17 @@ static int relay_action_handler(struct xe_guc_relay *relay, u32 origin, return relay_testloop_action_handler(relay, origin, msg, len, response, size); type = FIELD_GET(GUC_HXG_MSG_0_TYPE, msg[0]); + relay_assert(relay, guc_hxg_type_is_action(type)); - if (IS_SRIOV_PF(relay_to_xe(relay))) - ret = xe_gt_sriov_pf_service_process_request(gt, origin, msg, len, response, size); - else + if (IS_SRIOV_PF(relay_to_xe(relay))) { + if (type == GUC_HXG_TYPE_REQUEST) + ret = xe_gt_sriov_pf_service_process_request(gt, origin, msg, len, + response, size); + else + ret = -EOPNOTSUPP; + } else { ret = -EOPNOTSUPP; - + } if (type == GUC_HXG_TYPE_EVENT) relay_assert(relay, ret <= 0); From b5c55015d4164a0f206bcdcf2985da948b3c7837 Mon Sep 17 00:00:00 2001 From: Matthew Auld Date: Thu, 25 Jun 2026 16:20:56 +0100 Subject: [PATCH 0415/1054] drm/xe: fix NPD in bo_meminfo() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a buffer object is purged, its ttm.resource is set to NULL via the TTM pipeline gutting flow. However, the BO remains in the client's object list until userspace explicitly closes the GEM handle. If memory stats are queried during this time, accessing bo->ttm.resource->mem_type will result in a NULL pointer dereference. Fix this by safely skipping purged BOs in bo_meminfo, as they no longer consume any memory. User is getting NPD on device resume, and possible theory is that in bo_move(), if we need to evict something to SYSTEM to save the CCS state, but the BO is marked as dontneed, this won't trigger a move but will nuke the pages, leaving us with a NULL bo resource. And the meminfo() doesn't look ready to handle a NULL resource. v2 (Sashiko): - There could potentially be other cases where we might end up with a NULL resource, so make this a general NULL check for now. Closes: https://gitlab.freedesktop.org/drm/xe/kernel/-/work_items/8419 Fixes: ad9843aac91a ("drm/xe/madvise: Implement purgeable buffer object support") Assisted-by: Copilot:gemini-3.1-pro-preview Reported-by: Matthew Schwartz Signed-off-by: Matthew Auld Cc: Thomas Hellström Cc: Matthew Brost Cc: Arvind Yadav Reviewed-by: Matthew Brost Tested-by: Matthew Schwartz Link: https://patch.msgid.link/20260625152054.450125-6-matthew.auld@intel.com (cherry picked from commit c9a8e7daa0afe3161111e27fd92176e608c7f186) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_drm_client.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/xe/xe_drm_client.c b/drivers/gpu/drm/xe/xe_drm_client.c index 84b66147bf49..81020b4b344e 100644 --- a/drivers/gpu/drm/xe/xe_drm_client.c +++ b/drivers/gpu/drm/xe/xe_drm_client.c @@ -168,10 +168,20 @@ static void bo_meminfo(struct xe_bo *bo, struct drm_memory_stats stats[TTM_NUM_MEM_TYPES]) { u64 sz = xe_bo_size(bo); - u32 mem_type = bo->ttm.resource->mem_type; + u32 mem_type; xe_bo_assert_held(bo); + /* + * The resource can be NULL if the BO has been purged, plus maybe some + * other cases. Either way there shouldn't be any memory to account for, + * or a current resource to account this against, so skip for now. + */ + if (!bo->ttm.resource) + return; + + mem_type = bo->ttm.resource->mem_type; + if (drm_gem_object_is_shared_for_memory_stats(&bo->ttm.base)) stats[mem_type].shared += sz; else From 8a0fb57675be578c4db19deb4298ed08a70f0f1a Mon Sep 17 00:00:00 2001 From: Matthew Auld Date: Thu, 25 Jun 2026 16:20:58 +0100 Subject: [PATCH 0416/1054] drm/xe/pt: prevent invalid cursor access for purged BOs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit During a page table walk for binding, xe_pt_stage_bind() explicitly skips initializing the xe_res_cursor for purged BOs, treating them similarly to NULL VMAs by only setting the cursor size. However, xe_pt_hugepte_possible() and xe_pt_scan_64K() did not check if the BO was purged before attempting to walk the cursor using xe_res_dma() and xe_res_next(). Because the cursor was left uninitialized for purged BOs, this falls through and triggers warnings like: WARNING: drivers/gpu/drm/xe/xe_res_cursor.h:274 at xe_res_next Fix this by explicitly checking if the BO is purged in both xe_pt_hugepte_possible() and xe_pt_scan_64K(), returning early just as we do for NULL VMAs, avoiding the invalid cursor accesses entirely. As a precaution, also zero-initialize the cursor in xe_pt_stage_bind() to ensure we don't pass garbage data into the page table walkers if we ever hit a similar edge case in the future. Closes: https://gitlab.freedesktop.org/drm/xe/kernel/-/work_items/8418 Fixes: ad9843aac91a ("drm/xe/madvise: Implement purgeable buffer object support") Assisted-by: Copilot:gemini-3.1-pro-preview Reported-by: Matthew Schwartz Signed-off-by: Matthew Auld Cc: Thomas Hellström Cc: Matthew Brost Cc: Arvind Yadav Reviewed-by: Matthew Brost Tested-by: Matthew Schwartz Link: https://patch.msgid.link/20260625152054.450125-8-matthew.auld@intel.com (cherry picked from commit 4c7b9c6ece32440e5a435a92076d049450cd2d2e) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_pt.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_pt.c b/drivers/gpu/drm/xe/xe_pt.c index 3380ce710a48..670bc2206fea 100644 --- a/drivers/gpu/drm/xe/xe_pt.c +++ b/drivers/gpu/drm/xe/xe_pt.c @@ -433,6 +433,7 @@ xe_pt_insert_entry(struct xe_pt_stage_bind_walk *xe_walk, struct xe_pt *parent, static bool xe_pt_hugepte_possible(u64 addr, u64 next, unsigned int level, struct xe_pt_stage_bind_walk *xe_walk) { + struct xe_bo *bo = xe_vma_bo(xe_walk->vma); u64 size, dma; if (level > MAX_HUGEPTE_LEVEL) @@ -446,8 +447,8 @@ static bool xe_pt_hugepte_possible(u64 addr, u64 next, unsigned int level, if (next - xe_walk->va_curs_start > xe_walk->curs->size) return false; - /* null VMA's do not have dma addresses */ - if (xe_vma_is_null(xe_walk->vma)) + /* null VMA's and purged BO's do not have dma addresses */ + if (xe_vma_is_null(xe_walk->vma) || (bo && xe_bo_is_purged(bo))) return true; /* if we are clearing page table, no dma addresses*/ @@ -468,6 +469,7 @@ static bool xe_pt_hugepte_possible(u64 addr, u64 next, unsigned int level, static bool xe_pt_scan_64K(u64 addr, u64 next, struct xe_pt_stage_bind_walk *xe_walk) { + struct xe_bo *bo = xe_vma_bo(xe_walk->vma); struct xe_res_cursor curs = *xe_walk->curs; if (!IS_ALIGNED(addr, SZ_64K)) @@ -476,8 +478,8 @@ xe_pt_scan_64K(u64 addr, u64 next, struct xe_pt_stage_bind_walk *xe_walk) if (next > xe_walk->l0_end_addr) return false; - /* null VMA's do not have dma addresses */ - if (xe_vma_is_null(xe_walk->vma)) + /* null VMA's and purged BO's do not have dma addresses */ + if (xe_vma_is_null(xe_walk->vma) || (bo && xe_bo_is_purged(bo))) return true; xe_res_next(&curs, addr - xe_walk->va_curs_start); @@ -708,7 +710,7 @@ xe_pt_stage_bind(struct xe_tile *tile, struct xe_vma *vma, { struct xe_device *xe = tile_to_xe(tile); struct xe_bo *bo = xe_vma_bo(vma); - struct xe_res_cursor curs; + struct xe_res_cursor curs = {}; struct xe_vm *vm = xe_vma_vm(vma); struct xe_pt_stage_bind_walk xe_walk = { .base = { From 959b5016e4646b55fd2fd0438932e4c4e9ce171f Mon Sep 17 00:00:00 2001 From: Ashutosh Dixit Date: Mon, 29 Jun 2026 10:26:34 -0700 Subject: [PATCH 0417/1054] drm/xe/oa: Fix offset alignment for MERT WHITELIST_OA_MERT_MMIO_TRG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 'head' argument for WHITELIST_OA_MERT_MMIO_TRG was previously wrong (not multiple of 16). Fix this. Fixes: ec02e49f21bc ("drm/xe/rtp: Whitelist OAMERT MMIO trigger registers") Cc: stable@vger.kernel.org Reviewed-by: Umesh Nerlige Ramappa Signed-off-by: Ashutosh Dixit Link: https://patch.msgid.link/20260629172634.1100983-1-ashutosh.dixit@intel.com (cherry picked from commit f6c23e4589bdc69a5d2f79aed5c5bddd5d406cbe) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_reg_whitelist.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/xe/xe_reg_whitelist.c b/drivers/gpu/drm/xe/xe_reg_whitelist.c index 3d9e3daab01a..526907d2d824 100644 --- a/drivers/gpu/drm/xe/xe_reg_whitelist.c +++ b/drivers/gpu/drm/xe/xe_reg_whitelist.c @@ -129,7 +129,7 @@ static const struct xe_rtp_table_sr oa_whitelist = XE_RTP_TABLE_SR( OAM_HEAD_POINTER(XE_OAM_SCMI_1_BASE_ADJ)) #define WHITELIST_OA_MERT_MMIO_TRG \ - WHITELIST_OA_MMIO_TRG(OAMERT_MMIO_TRG, OAMERT_STATUS, OAMERT_HEAD_POINTER) + WHITELIST_OA_MMIO_TRG(OAMERT_MMIO_TRG, OAMERT_STATUS, OAMERT_TAIL_POINTER) { XE_RTP_NAME("oag_mmio_trg_rcs"), XE_RTP_RULES(GRAPHICS_VERSION_RANGE(1200, XE_RTP_END_VERSION_UNDEFINED), From 6666336d8fe5f3f0cd3f8da2865ca78110a6e170 Mon Sep 17 00:00:00 2001 From: Jia He Date: Tue, 30 Jun 2026 22:23:47 +0000 Subject: [PATCH 0418/1054] arm64/sysreg: Fix BWE field encoding in ID_AA64DFR2_EL1 Commit 93d7356e4b30 ("arm64: sysreg: Describe ID_AA64DFR2_EL1 fields") encodes the FEAT_BWE2 value of the BWE field as '0b0002'. Binary literals only accept the digits 0 and 1, so the intended value is 2, i.e. 0b0010. The macro generated by gen-sysreg.awk currently expands to #define ID_AA64DFR2_EL1_BWE_FEAT_BWE2 UL(0b0002) is not legal C and would fail to compile if any in-tree code referenced it. At present no caller uses this enum value, so the kernel still builds cleanly, but the bug is latent. Fix the typo by using the correct binary literal 0b0010. Cc: Bin Guo Fixes: 93d7356e4b30 ("arm64: sysreg: Describe ID_AA64DFR2_EL1 fields") Signed-off-by: Jia He Reviewed-by: Mark Brown Reviewed-by: Oliver Upton Acked-by: Marc Zyngier Signed-off-by: Will Deacon --- arch/arm64/tools/sysreg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/tools/sysreg b/arch/arm64/tools/sysreg index bc1788b1662b..7cb61aca3797 100644 --- a/arch/arm64/tools/sysreg +++ b/arch/arm64/tools/sysreg @@ -1806,7 +1806,7 @@ Res0 15:8 UnsignedEnum 7:4 BWE 0b0000 NI 0b0001 FEAT_BWE - 0b0002 FEAT_BWE2 + 0b0010 FEAT_BWE2 EndEnum UnsignedEnum 3:0 STEP 0b0000 NI From a52d6c7160f7e2f8c56adf29146385b8f2868d3d Mon Sep 17 00:00:00 2001 From: Wang Yan Date: Thu, 2 Jul 2026 09:52:42 +0800 Subject: [PATCH 0419/1054] selftests/arm64: fix spelling errors in comments Fix two spelling mistakes in arm64 selftest comments: - "whcih" -> "which" (arm64/gcs/libc-gcs.c) - "resutls" -> "results" (arm64/pauth/pac.c) Signed-off-by: Wang Yan Signed-off-by: Will Deacon --- tools/testing/selftests/arm64/gcs/libc-gcs.c | 2 +- tools/testing/selftests/arm64/pauth/pac.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/arm64/gcs/libc-gcs.c b/tools/testing/selftests/arm64/gcs/libc-gcs.c index 72e82bfbecc9..ddb0b2b20155 100644 --- a/tools/testing/selftests/arm64/gcs/libc-gcs.c +++ b/tools/testing/selftests/arm64/gcs/libc-gcs.c @@ -130,7 +130,7 @@ TEST(gcs_find_terminator) * We can access a GCS via ptrace * * This could usefully have a fixture but note that each test is - * fork()ed into a new child whcih causes issues. Might be better to + * fork()ed into a new child which causes issues. Might be better to * lift at least some of this out into a separate, non-harness, test * program. */ diff --git a/tools/testing/selftests/arm64/pauth/pac.c b/tools/testing/selftests/arm64/pauth/pac.c index 67d138057707..f4b859c75a5a 100644 --- a/tools/testing/selftests/arm64/pauth/pac.c +++ b/tools/testing/selftests/arm64/pauth/pac.c @@ -292,7 +292,7 @@ TEST(single_thread_different_keys) /* * fork() does not change keys. Only exec() does so call a worker program. - * Its only job is to sign a value and report back the resutls + * Its only job is to sign a value and report back the results */ TEST(exec_changed_keys) { From 18313833e2c6de222a4f6c072da759d0d5888528 Mon Sep 17 00:00:00 2001 From: Or Gerlitz Date: Wed, 17 Jun 2026 14:21:05 +0300 Subject: [PATCH 0420/1054] RDMA/cma: Fix hardware address comparison length in netevent callback The cited commit hardcoded the hardware address comparison len to ETH_ALEN. This breaks IPoIB, which uses 20-byte addresses. By truncating the memcmp, the CMA may incorrectly assume the target address is unchanged and fails to abort the stalled connection. Fix this by replacing ETH_ALEN with the dynamic neigh->dev->addr_len to correctly evaluate the full address regardless of the link layer. Fixes: 925d046e7e52 ("RDMA/core: Add a netevent notifier to cma") Signed-off-by: Or Gerlitz Link: https://patch.msgid.link/20260617-fix-cma-ipoib-v1-1-03f869344304@ddn.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/core/cma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c index 9480d1a51c11..e88d3efb967b 100644 --- a/drivers/infiniband/core/cma.c +++ b/drivers/infiniband/core/cma.c @@ -5270,7 +5270,7 @@ static int cma_netevent_callback(struct notifier_block *self, list_for_each_entry(current_id, &ips_node->id_list, id_list_entry) { if (!memcmp(current_id->id.route.addr.dev_addr.dst_dev_addr, - neigh->ha, ETH_ALEN)) + neigh->ha, neigh->dev->addr_len)) continue; cma_id_get(current_id); if (!queue_work(cma_wq, ¤t_id->id.net_work)) From 037a3c43edfb597665dd34457cd22b14692f2ba3 Mon Sep 17 00:00:00 2001 From: Taeyang Lee <0wn@theori.io> Date: Sun, 14 Jun 2026 23:22:18 +0900 Subject: [PATCH 0421/1054] perf/core: Detach event groups during remove_on_exec perf_event_remove_on_exec() removes events by calling perf_event_exit_event(). For top-level events, this removes the event from the context with DETACH_EXIT only. This can leave inconsistent group state when a removed event is a group leader and the group contains siblings without remove_on_exec. If the group was active, the surviving siblings can remain active and attached to the removed leader's sibling list, but are no longer represented by a valid group leader on the PMU context active lists. A later close of the removed leader uses DETACH_GROUP and can promote the still-active siblings from this stale group state. The next schedule-in can then add an already-linked active_list entry again, corrupting the PMU context active list. With DEBUG_LIST enabled, this is caught as a list_add double-add in merge_sched_in(). Fix this by detaching group relationships when remove_on_exec removes an event. This preserves the existing task-exit and revoke behavior, while ensuring surviving siblings are ungrouped before the removed event leaves the context. Fixes: 2e498d0a74e5 ("perf: Add support for event removal on exec") Signed-off-by: Taeyang Lee <0wn@theori.io> Signed-off-by: Peter Zijlstra (Intel) Link: https://patch.msgid.link/ai65GgZcC0LAlWLG@Taeyangs-MacBook-Pro.local --- kernel/events/core.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/kernel/events/core.c b/kernel/events/core.c index 954c36e28101..d7f3e2c2ecb1 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -4729,7 +4729,7 @@ static void perf_remove_from_owner(struct perf_event *event); static void perf_event_exit_event(struct perf_event *event, struct perf_event_context *ctx, struct task_struct *task, - bool revoke); + unsigned long detach_flags); /* * Removes all events from the current task that have been marked @@ -4756,7 +4756,7 @@ static void perf_event_remove_on_exec(struct perf_event_context *ctx) modified = true; - perf_event_exit_event(event, ctx, ctx->task, false); + perf_event_exit_event(event, ctx, ctx->task, DETACH_GROUP); } raw_spin_lock_irqsave(&ctx->lock, flags); @@ -12937,7 +12937,7 @@ static void __pmu_detach_event(struct pmu *pmu, struct perf_event *event, /* * De-schedule the event and mark it REVOKED. */ - perf_event_exit_event(event, ctx, ctx->task, true); + perf_event_exit_event(event, ctx, ctx->task, DETACH_REVOKE); /* * All _free_event() bits that rely on event->pmu: @@ -14525,12 +14525,13 @@ static void perf_event_exit_event(struct perf_event *event, struct perf_event_context *ctx, struct task_struct *task, - bool revoke) + unsigned long detach_flags) { struct perf_event *parent_event = event->parent; - unsigned long detach_flags = DETACH_EXIT; unsigned int attach_state; + detach_flags |= DETACH_EXIT; + if (parent_event) { /* * Do not destroy the 'original' grouping; because of the @@ -14553,8 +14554,8 @@ perf_event_exit_event(struct perf_event *event, sync_child_event(event, task); } - if (revoke) - detach_flags |= DETACH_GROUP | DETACH_REVOKE; + if (detach_flags & DETACH_REVOKE) + detach_flags |= DETACH_GROUP; perf_remove_from_context(event, detach_flags); /* @@ -14642,7 +14643,7 @@ static void perf_event_exit_task_context(struct task_struct *task, bool exit) perf_event_task(task, ctx, 0); list_for_each_entry_safe(child_event, next, &ctx->event_list, event_entry) - perf_event_exit_event(child_event, ctx, exit ? task : NULL, false); + perf_event_exit_event(child_event, ctx, exit ? task : NULL, 0); mutex_unlock(&ctx->mutex); From abf08854d224085e2ebb3ba660e7995909f47d6a Mon Sep 17 00:00:00 2001 From: David Windsor Date: Mon, 29 Jun 2026 20:13:33 -0400 Subject: [PATCH 0422/1054] x86/uprobes: Keep shadow stack in sync for emulated CALLs Uprobe CALL emulation updates the normal user stack, but not the CET user shadow stack. The subsequent RET then sees a stale shadow stack entry and raises #CP. Update the relative CALL emulation and XOL CALL fixup paths to keep the shadow stack in sync. Fixes: 488af8ea7131 ("x86/shstk: Wire in shadow stack interface") Signed-off-by: David Windsor Signed-off-by: Peter Zijlstra (Intel) Acked-by: Oleg Nesterov Acked-by: Jiri Olsa Tested-by: Jiri Olsa Link: https://patch.msgid.link/8b5b1c7407b98f31664ad7b6a6faf20d2d4a6cad.1782777969.git.dwindsor@gmail.com --- arch/x86/kernel/uprobes.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/arch/x86/kernel/uprobes.c b/arch/x86/kernel/uprobes.c index ebb1baf1eb1d..d74bb54543b6 100644 --- a/arch/x86/kernel/uprobes.c +++ b/arch/x86/kernel/uprobes.c @@ -1246,9 +1246,15 @@ static int default_post_xol_op(struct arch_uprobe *auprobe, struct pt_regs *regs long correction = utask->vaddr - utask->xol_vaddr; regs->ip += correction; } else if (auprobe->defparam.fixups & UPROBE_FIX_CALL) { + unsigned long retaddr = utask->vaddr + auprobe->defparam.ilen; + int err; + regs->sp += sizeof_long(regs); /* Pop incorrect return address */ - if (emulate_push_stack(regs, utask->vaddr + auprobe->defparam.ilen)) + if (emulate_push_stack(regs, retaddr)) return -ERESTART; + err = shstk_update_last_frame(retaddr); + if (err) + return err; } /* popf; tell the caller to not touch TF */ if (auprobe->defparam.fixups & UPROBE_FIX_SETF) @@ -1338,6 +1344,10 @@ static bool branch_emulate_op(struct arch_uprobe *auprobe, struct pt_regs *regs) */ if (emulate_push_stack(regs, new_ip)) return false; + if (shstk_push(new_ip) == -EFAULT) { + regs->sp += sizeof_long(regs); + return false; + } } else if (!check_jmp_cond(auprobe, regs)) { offs = 0; } From 5166973b20784b4627c7a657d546963d8c6e9b5a Mon Sep 17 00:00:00 2001 From: David Windsor Date: Mon, 29 Jun 2026 20:13:34 -0400 Subject: [PATCH 0423/1054] selftests/x86: Add shadow stack uprobe CALL test Add coverage for entry uprobes installed on CALL instructions while user shadow stack is enabled. The test puts an entry uprobe on a helper whose first instruction is a relative CALL, then verifies that the call/return sequence completes without SIGSEGV. This catches regressions where x86 uprobe CALL emulation updates the regular user stack but leaves the CET shadow stack stale. Signed-off-by: David Windsor Signed-off-by: Peter Zijlstra (Intel) Link: https://patch.msgid.link/b957039191118c5eba97d01d80c494b859f115a6.1782777969.git.dwindsor@gmail.com --- .../testing/selftests/x86/test_shadow_stack.c | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/tools/testing/selftests/x86/test_shadow_stack.c b/tools/testing/selftests/x86/test_shadow_stack.c index 21af54d5f4ea..3d6ca33edba4 100644 --- a/tools/testing/selftests/x86/test_shadow_stack.c +++ b/tools/testing/selftests/x86/test_shadow_stack.c @@ -873,6 +873,86 @@ static int test_uretprobe(void) return err; } +/* Keep the CALL first so the function address is exactly the probed CALL. */ +extern void uprobe_call_trigger(void); +asm (".pushsection .text\n" + ".global uprobe_call_target\n" + ".type uprobe_call_target, @function\n" + "uprobe_call_target:\n" + " ret\n" + ".size uprobe_call_target, .-uprobe_call_target\n" + + ".global uprobe_call_trigger\n" + ".type uprobe_call_trigger, @function\n" + "uprobe_call_trigger:\n" + " call uprobe_call_target\n" + " ret\n" + ".size uprobe_call_trigger, .-uprobe_call_trigger\n" + ".popsection\n" +); + +/* If CALL emulation misses the shadow stack update, this exits via SIGSEGV. */ +static int test_uprobe_call(void) +{ + const size_t attr_sz = sizeof(struct perf_event_attr); + const char *file = "/proc/self/exe"; + int fd = -1, type, err = 1; + struct perf_event_attr attr; + struct sigaction sa = {}; + ssize_t offset; + + type = determine_uprobe_perf_type(); + if (type < 0) { + if (type == -ENOENT) + printf("[SKIP]\tUprobe on CALL test, uprobes are not available\n"); + return 0; + } + + offset = get_uprobe_offset(uprobe_call_trigger); + if (offset < 0) + return 1; + + sa.sa_sigaction = segv_gp_handler; + sa.sa_flags = SA_SIGINFO; + if (sigaction(SIGSEGV, &sa, NULL)) + return 1; + + /* Setup entry uprobe through perf event interface. */ + memset(&attr, 0, attr_sz); + attr.size = attr_sz; + attr.type = type; + attr.config = 0; + attr.config1 = (__u64)(unsigned long)file; + attr.config2 = offset; + + fd = syscall(__NR_perf_event_open, &attr, 0 /* pid */, -1 /* cpu */, + -1 /* group_fd */, PERF_FLAG_FD_CLOEXEC); + if (fd < 0) + goto out; + + if (sigsetjmp(jmp_buffer, 1)) + goto out; + + if (ARCH_PRCTL(ARCH_SHSTK_ENABLE, ARCH_SHSTK_SHSTK)) + goto out; + + /* + * This either segfaults and goes through sigsetjmp above + * or succeeds and we're good. + */ + uprobe_call_trigger(); + + printf("[OK]\tUprobe on CALL test\n"); + err = 0; + +out: + ARCH_PRCTL(ARCH_SHSTK_DISABLE, ARCH_SHSTK_SHSTK); + signal(SIGSEGV, SIG_DFL); + if (fd >= 0) + close(fd); + return err; +} + void segv_handler_ptrace(int signum, siginfo_t *si, void *uc) { /* The SSP adjustment caused a segfault. */ @@ -1071,6 +1151,12 @@ int main(int argc, char *argv[]) goto out; } + if (test_uprobe_call()) { + ret = 1; + printf("[FAIL]\tuprobe on CALL test\n"); + goto out; + } + return ret; out: From 169328645663bae30e9abad4012d52441e085a71 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Wed, 1 Jul 2026 13:13:25 +0200 Subject: [PATCH 0424/1054] uprobes/x86: Use proper mm_struct in __in_uprobe_trampoline In the unregister path we use __in_uprobe_trampoline check with current->mm for the VMA lookup, which is wrong, because we are in the tracer context, not the traced process. Add mm_struct pointer argument to __in_uprobe_trampoline and changing related callers to pass proper mm_struct pointer. Fixes: ba2bfc97b462 ("uprobes/x86: Add support to optimize uprobes") Reported-by: syzbot+61ce80689253f42e6d80@syzkaller.appspotmail.com Signed-off-by: Jiri Olsa Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Oleg Nesterov Acked-by: Andrii Nakryiko Tested-by: syzbot+61ce80689253f42e6d80@syzkaller.appspotmail.com Link: https://patch.msgid.link/20260701111337.53943-2-jolsa@kernel.org --- arch/x86/kernel/uprobes.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/arch/x86/kernel/uprobes.c b/arch/x86/kernel/uprobes.c index d74bb54543b6..3af979fb41d3 100644 --- a/arch/x86/kernel/uprobes.c +++ b/arch/x86/kernel/uprobes.c @@ -761,9 +761,9 @@ void arch_uprobe_clear_state(struct mm_struct *mm) destroy_uprobe_trampoline(tramp); } -static bool __in_uprobe_trampoline(unsigned long ip) +static bool __in_uprobe_trampoline(struct mm_struct *mm, unsigned long ip) { - struct vm_area_struct *vma = vma_lookup(current->mm, ip); + struct vm_area_struct *vma = vma_lookup(mm, ip); return vma && vma_is_special_mapping(vma, &tramp_mapping); } @@ -776,14 +776,14 @@ static bool in_uprobe_trampoline(unsigned long ip) rcu_read_lock(); if (mmap_lock_speculate_try_begin(mm, &seq)) { - found = __in_uprobe_trampoline(ip); + found = __in_uprobe_trampoline(mm, ip); retry = mmap_lock_speculate_retry(mm, seq); } rcu_read_unlock(); if (retry) { mmap_read_lock(mm); - found = __in_uprobe_trampoline(ip); + found = __in_uprobe_trampoline(mm, ip); mmap_read_unlock(mm); } return found; @@ -1044,7 +1044,7 @@ static int copy_from_vaddr(struct mm_struct *mm, unsigned long vaddr, void *dst, return 0; } -static bool __is_optimized(uprobe_opcode_t *insn, unsigned long vaddr) +static bool __is_optimized(struct mm_struct *mm, uprobe_opcode_t *insn, unsigned long vaddr) { struct __packed __arch_relative_insn { u8 op; @@ -1053,7 +1053,7 @@ static bool __is_optimized(uprobe_opcode_t *insn, unsigned long vaddr) if (!is_call_insn(insn)) return false; - return __in_uprobe_trampoline(vaddr + 5 + call->raddr); + return __in_uprobe_trampoline(mm, vaddr + 5 + call->raddr); } static int is_optimized(struct mm_struct *mm, unsigned long vaddr) @@ -1064,7 +1064,7 @@ static int is_optimized(struct mm_struct *mm, unsigned long vaddr) err = copy_from_vaddr(mm, vaddr, &insn, 5); if (err) return err; - return __is_optimized((uprobe_opcode_t *)&insn, vaddr); + return __is_optimized(mm, (uprobe_opcode_t *)&insn, vaddr); } static bool should_optimize(struct arch_uprobe *auprobe) From 49df66b7993c80b80c7eb9a84ba5b3410c8296a0 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Thu, 2 Jul 2026 11:46:21 +0200 Subject: [PATCH 0425/1054] batman-adv: ensure minimal ethernet header on TX As documented in commit 8bd67ebb50c0 ("net: bridge: xmit: make sure we have at least eth header len bytes"), it is possible by for a local user with eBPF TC hook access to attach a tc filter which truncates the packet and redirects to an batadv interface. But the code assumes that at least ETH_HLEN bytes are available and thus might read outside of the available buffer. The batadv_interface_tx() must therefore always check itself if enough data is available for the ethernet header and don't rely on min_header_len. Cc: stable@vger.kernel.org Fixes: c6c8fea29769 ("net: Add batman-adv meshing protocol") Reported-by: Sashiko Signed-off-by: Sven Eckelmann --- net/batman-adv/mesh-interface.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/batman-adv/mesh-interface.c b/net/batman-adv/mesh-interface.c index 511f70e0706a..0b75234521b6 100644 --- a/net/batman-adv/mesh-interface.c +++ b/net/batman-adv/mesh-interface.c @@ -195,6 +195,9 @@ static netdev_tx_t batadv_interface_tx(struct sk_buff *skb, if (READ_ONCE(bat_priv->mesh_state) != BATADV_MESH_ACTIVE) goto dropped; + if (!pskb_may_pull(skb, ETH_HLEN)) + goto dropped; + /* reset control block to avoid left overs from previous users */ memset(skb->cb, 0, sizeof(struct batadv_skb_cb)); From a846aecb931b4d65d5eafa92a0623545af46d4f2 Mon Sep 17 00:00:00 2001 From: Jacob Moroni Date: Wed, 17 Jun 2026 14:19:36 +0000 Subject: [PATCH 0426/1054] RDMA/irdma: Prevent rereg_mr for non-mem regions When a QP/CQ/SRQ is created, a two step process is used where the buffer is allocated in userspace and explicitly registered with the normal reg_mr mechanism prior to creating the actual QP/CQ/SRQ object. These special registrations are indicated via an ABI field so the driver knows that they do not have a valid mkey and to skip the actual CQP command submission. Since these are real MR objects from the core's perspective, it is possible for a user application to invoke rereg_mr on them and cause a real CQP op to be emitted with the zero-initialized mkey value of 0. Fix this by preventing rereg_mr on these special regions. Fixes: 5ac388db27c4 ("RDMA/irdma: Add support to re-register a memory region") Signed-off-by: Jacob Moroni Reviewed-by: David Hu Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/irdma/verbs.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/infiniband/hw/irdma/verbs.c b/drivers/infiniband/hw/irdma/verbs.c index cb54c7c8fcd8..23ab8286dc12 100644 --- a/drivers/infiniband/hw/irdma/verbs.c +++ b/drivers/infiniband/hw/irdma/verbs.c @@ -3791,6 +3791,9 @@ 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); + if (iwmr->type != IRDMA_MEMREG_TYPE_MEM) + return ERR_PTR(-EINVAL); + ret = ib_umem_check_rereg(iwmr->region, flags, new_access); if (ret) return ERR_PTR(ret); From f01f5275feb77bac9fefbbf7cc584fe0b3850a92 Mon Sep 17 00:00:00 2001 From: Yousef Alhouseen Date: Tue, 30 Jun 2026 23:18:27 +0200 Subject: [PATCH 0427/1054] ublk: snapshot batch commands before preparing I/O The batch prepare path rereads its userspace element array when rolling back a partially prepared batch. Userspace can change an already processed tag before the second read, causing rollback to reject the replacement tag and leave earlier I/O slots prepared. The WARN_ON_ONCE() in the rollback path then fires. Copy the bounded batch into kernel memory before changing any I/O state and use the same snapshot for preparation and rollback. Commit and fetch batches retain the existing chunked userspace walk. Fixes: b256795b3606 ("ublk: handle UBLK_U_IO_PREP_IO_CMDS") Reported-by: syzbot+1a67ee1aa79484801ec6@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=1a67ee1aa79484801ec6 Signed-off-by: Yousef Alhouseen Reviewed-by: Ming Lei Link: https://patch.msgid.link/20260630211827.50475-1-alhouseenyousef@gmail.com Signed-off-by: Jens Axboe --- drivers/block/ublk_drv.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c index 4f6d9e652187..c2c11f2a01e7 100644 --- a/drivers/block/ublk_drv.c +++ b/drivers/block/ublk_drv.c @@ -3584,6 +3584,7 @@ ublk_batch_auto_buf_reg(const struct ublk_batch_io *uc, #define UBLK_CMD_BATCH_TMP_BUF_SZ (48 * 10) struct ublk_batch_io_iter { void __user *uaddr; + const u8 *kaddr; unsigned done, total; unsigned char elem_bytes; /* copy to this buffer from user space */ @@ -3632,7 +3633,10 @@ static int ublk_walk_cmd_buf(struct ublk_batch_io_iter *iter, while (iter->done < iter->total) { unsigned int len = min(sizeof(iter->buf), iter->total - iter->done); - if (copy_from_user(iter->buf, iter->uaddr + iter->done, len)) { + if (iter->kaddr) { + memcpy(iter->buf, iter->kaddr + iter->done, len); + } else if (copy_from_user(iter->buf, iter->uaddr + iter->done, + len)) { pr_warn("ublk%d: read batch cmd buffer failed\n", data->ub->dev_info.dev_id); return -EFAULT; @@ -3723,14 +3727,21 @@ static int ublk_handle_batch_prep_cmd(const struct ublk_batch_io_data *data) .total = uc->nr_elem * uc->elem_bytes, .elem_bytes = uc->elem_bytes, }; + void *cmd_buf; int ret; + cmd_buf = vmemdup_user(iter.uaddr, iter.total); + if (IS_ERR(cmd_buf)) + return PTR_ERR(cmd_buf); + iter.kaddr = cmd_buf; + mutex_lock(&data->ub->mutex); ret = ublk_walk_cmd_buf(&iter, data, ublk_batch_prep_io); if (ret && iter.done) ublk_batch_revert_prep_cmd(&iter, data); mutex_unlock(&data->ub->mutex); + kvfree(cmd_buf); return ret; } From c16b8c4cfb4fe2244cc33e469a93c1ab8684146b Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 2 Jul 2026 09:25:00 +0100 Subject: [PATCH 0428/1054] cifs: Fix missing credit release on failure in cifs_issue_read() Fix missing release of credits in the failure path in cifs_issue_read() lest retrying the subreq just overwrites the credits value. Fixes: 69c3c023af25 ("cifs: Implement netfslib hooks") Link: https://sashiko.dev/#/patchset/20260608145432.681865-1-dhowells%40redhat.com Signed-off-by: David Howells Acked-by: Paulo Alcantara (Red Hat) cc: linux-cifs@vger.kernel.org cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Steve French --- fs/smb/client/file.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c index 8b25d6c9ec5e..5a25635bc62a 100644 --- a/fs/smb/client/file.c +++ b/fs/smb/client/file.c @@ -241,6 +241,7 @@ static void cifs_issue_read(struct netfs_io_subrequest *subreq) return; failed: + add_credits_and_wake_if(rdata->server, &rdata->credits, 0); subreq->error = rc; netfs_read_subreq_terminated(subreq); } From 2995ccec260caa9e85b3301a4aba1e66ed80ad74 Mon Sep 17 00:00:00 2001 From: Gerald Schaefer Date: Tue, 23 Jun 2026 19:44:06 +0200 Subject: [PATCH 0429/1054] s390/monwriter: Reject buffer reuse with different data length When data buffers are reused, e.g. for interval sample records, the first record determines the data length, and the size of the buffer for user copy. Current monwriter code does not check if the data length was changed for subsequent records, which also would never happen for valid user programs. However, a malicious user could change the data length, resulting in out of bounds user copy to the kernel buffer, and memory corruption. By default, the monwriter misc device is created with root-only permissions, so practical impact is typically low. Fix this by checking for changed data length and rejecting such records. Cc: stable@vger.kernel.org Signed-off-by: Gerald Schaefer Reviewed-by: Christian Borntraeger Signed-off-by: Vasily Gorbik --- drivers/s390/char/monwriter.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/s390/char/monwriter.c b/drivers/s390/char/monwriter.c index eaeb4a6384d1..ecf121a87f88 100644 --- a/drivers/s390/char/monwriter.c +++ b/drivers/s390/char/monwriter.c @@ -122,6 +122,9 @@ static int monwrite_new_hdr(struct mon_private *monpriv) kfree(monbuf->data); kfree(monbuf); monbuf = NULL; + } else if (monbuf->hdr.datalen != monhdr->datalen) { + /* Data with buffer reuse must not change its length */ + return -EINVAL; } } else if (monhdr->mon_function != MONWRITE_STOP_INTERVAL) { if (mon_buf_count >= mon_max_bufs) From b9b0889071569d43623c260074e159cd8f26adb1 Mon Sep 17 00:00:00 2001 From: Jacob Moroni Date: Wed, 17 Jun 2026 16:40:13 +0000 Subject: [PATCH 0430/1054] RDMA/irdma: Prevent user-triggered null deref on QP create Previously, the user QP creation path would only attempt to populate iwqp->iwpbl if the user-provided req.user_wqe_bufs field was non-zero. The problem is that iwqp->iwpbl is unconditionally dereferenced later on in irdma_setup_virt_qp. While there was a check for iwqp->iwpbl != NULL, this check would only occur if req.user_wqe_bufs was non-zero. The end result is that a user could send a zero user_wqe_bufs value and trigger a null ptr deref. Fix this by unconditionally calling irdma_get_pbl and bailing if it fails, similar to the CQ and SRQ paths. Fixes: b48c24c2d710 ("RDMA/irdma: Implement device supported verb APIs") Link: https://patch.msgid.link/r/20260617164013.280790-1-jmoroni@google.com Signed-off-by: Jacob Moroni Reviewed-by: David Hu Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/irdma/verbs.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/drivers/infiniband/hw/irdma/verbs.c b/drivers/infiniband/hw/irdma/verbs.c index 23ab8286dc12..be8c5cf12f7f 100644 --- a/drivers/infiniband/hw/irdma/verbs.c +++ b/drivers/infiniband/hw/irdma/verbs.c @@ -633,17 +633,16 @@ static int irdma_setup_umode_qp(struct ib_udata *udata, iwqp->ctx_info.qp_compl_ctx = req.user_compl_ctx; iwqp->user_mode = 1; - if (req.user_wqe_bufs) { - spin_lock_irqsave(&ucontext->qp_reg_mem_list_lock, flags); - iwqp->iwpbl = irdma_get_pbl((unsigned long)req.user_wqe_bufs, - &ucontext->qp_reg_mem_list); - spin_unlock_irqrestore(&ucontext->qp_reg_mem_list_lock, flags); - if (!iwqp->iwpbl) { - ret = -ENODATA; - ibdev_dbg(&iwdev->ibdev, "VERBS: no pbl info\n"); - return ret; - } + spin_lock_irqsave(&ucontext->qp_reg_mem_list_lock, flags); + iwqp->iwpbl = irdma_get_pbl((unsigned long)req.user_wqe_bufs, + &ucontext->qp_reg_mem_list); + spin_unlock_irqrestore(&ucontext->qp_reg_mem_list_lock, flags); + + if (!iwqp->iwpbl) { + ret = -ENODATA; + ibdev_dbg(&iwdev->ibdev, "VERBS: no pbl info\n"); + return ret; } if (!ucontext->use_raw_attrs) { From f107c62bfc057b82758c233391ee0842f02a0582 Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Wed, 1 Jul 2026 21:21:46 +0200 Subject: [PATCH 0431/1054] iio: adc: ad4130: add missing `select IIO_TRIGGERED_BUFFER` to Kconfig The Kconfig entry is missing a `select IIO_TRIGGERED_BUFFER` parameter, causing potential build failures. Fixes: ec98c3b50157 ("iio: adc: ad4130: add new supported parts") Cc: stable@vger.kernel.org Signed-off-by: Joshua Crofts Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 6fb0766ca27a..4ed8ba9384db 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -108,6 +108,7 @@ config AD4130 depends on SPI depends on GPIOLIB select IIO_BUFFER + select IIO_TRIGGERED_BUFFER select IIO_KFIFO_BUF select REGMAP_SPI depends on COMMON_CLK From fd354554af1d2b33232ca6c8a3d79ed82413d715 Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Wed, 1 Jul 2026 21:21:47 +0200 Subject: [PATCH 0432/1054] iio: adc: ad7779: add missing 'select IIO_TRIGGERED_BUFFER' to Kconfig The Kconfig entry for the AD7779 is missing a 'select IIO_TRIGGERED_BUFFER' parameter, causing build failures. Fixes: c9a3f8c7bfcb ("drivers: iio: adc: add support for ad777x family") Cc: stable@vger.kernel.org Signed-off-by: Joshua Crofts Tested-by: Andy Shevchenko Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 4ed8ba9384db..3755a81c1efd 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -454,6 +454,7 @@ config AD7779 depends on SPI select CRC8 select IIO_BUFFER + select IIO_TRIGGERED_BUFFER select IIO_BACKEND help Say yes here to build support for Analog Devices AD777X family From 2815a277c53e9a84784d6410cd55a9da5b33068d Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Thu, 18 Jun 2026 12:17:51 +0800 Subject: [PATCH 0433/1054] RDMA/erdma: initialize ret for empty receive WR lists erdma_post_recv() returns ret after walking the receive work request list. If the caller passes an empty list, the loop is skipped and ret is not assigned. Initialize ret to 0 so an empty receive work request list returns success instead of stack data. Fixes: 155055771704 ("RDMA/erdma: Add verbs implementation") Link: https://patch.msgid.link/r/20260618041752.481193-1-ruoyuw560@gmail.com Signed-off-by: Ruoyu Wang Reviewed-by: Cheng Xu Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/erdma/erdma_qp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/erdma/erdma_qp.c b/drivers/infiniband/hw/erdma/erdma_qp.c index 25f6c49aec77..e002343832f7 100644 --- a/drivers/infiniband/hw/erdma/erdma_qp.c +++ b/drivers/infiniband/hw/erdma/erdma_qp.c @@ -734,7 +734,7 @@ int erdma_post_recv(struct ib_qp *ibqp, const struct ib_recv_wr *recv_wr, const struct ib_recv_wr *wr = recv_wr; struct erdma_qp *qp = to_eqp(ibqp); unsigned long flags; - int ret; + int ret = 0; spin_lock_irqsave(&qp->lock, flags); From 155fd5ce2382b0ffbec0d7ee7b3a6818a27a5aed Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Thu, 18 Jun 2026 12:17:52 +0800 Subject: [PATCH 0434/1054] RDMA/mana_ib: initialize err for empty send WR lists mana_ib_post_send() returns err after walking the send work request list. If the caller passes an empty list, the loop is skipped and err is not assigned. Initialize err to 0 so an empty send work request list returns success instead of stack data. Fixes: c8017f5b4856 ("RDMA/mana_ib: UD/GSI work requests") Link: https://patch.msgid.link/r/20260618041752.481193-2-ruoyuw560@gmail.com Signed-off-by: Ruoyu Wang Reviewed-by: Long Li Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/mana/wr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/mana/wr.c b/drivers/infiniband/hw/mana/wr.c index 1813567d3b16..36a1d506f08f 100644 --- a/drivers/infiniband/hw/mana/wr.c +++ b/drivers/infiniband/hw/mana/wr.c @@ -144,7 +144,7 @@ static int mana_ib_post_send_ud(struct mana_ib_qp *qp, const struct ib_ud_wr *wr int mana_ib_post_send(struct ib_qp *ibqp, const struct ib_send_wr *wr, const struct ib_send_wr **bad_wr) { - int err; + int err = 0; struct mana_ib_qp *qp = container_of(ibqp, struct mana_ib_qp, ibqp); for (; wr; wr = wr->next) { From e939334ea7dd219f100f963dbb1cb43df520c20a Mon Sep 17 00:00:00 2001 From: Chenguang Zhao Date: Thu, 25 Jun 2026 10:01:48 +0800 Subject: [PATCH 0435/1054] RDMA/core: Fix memory leak in __ib_create_cq() on invalid cqe Move the zero CQE validation before rdma_zalloc_drv_obj() to avoid leaking the CQ object when returning -EINVAL. Fixes: a2917582887a ("RDMA/core: Reject zero CQE count") Link: https://patch.msgid.link/r/20260625020148.224537-1-zhaochenguang@kylinos.cn Signed-off-by: Chenguang Zhao Reviewed-by: Kalesh AP Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/verbs.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/infiniband/core/verbs.c b/drivers/infiniband/core/verbs.c index 3b613b57e269..86811d31092c 100644 --- a/drivers/infiniband/core/verbs.c +++ b/drivers/infiniband/core/verbs.c @@ -2196,13 +2196,13 @@ struct ib_cq *__ib_create_cq(struct ib_device *device, struct ib_cq *cq; int ret; + if (WARN_ON_ONCE(!cq_attr->cqe)) + return ERR_PTR(-EINVAL); + cq = rdma_zalloc_drv_obj(device, ib_cq); if (!cq) return ERR_PTR(-ENOMEM); - if (WARN_ON_ONCE(!cq_attr->cqe)) - return ERR_PTR(-EINVAL); - cq->device = device; cq->comp_handler = comp_handler; cq->event_handler = event_handler; From 9f0f2d2121f16d420199a82ac5bbc242269133b3 Mon Sep 17 00:00:00 2001 From: Danila Chernetsov Date: Sat, 27 Jun 2026 09:59:51 +0000 Subject: [PATCH 0436/1054] RDMA/hns: Fix potential integer overflow in mhop hem cleanup In hns_roce_cleanup_mhop_hem_table(), the expression: obj = i * buf_chunk_size / table->obj_size; is evaluated using 32-bit unsigned arithmetic because 'buf_chunk_size' is u32 and the usual arithmetic conversions convert 'i' to unsigned int. The result is assigned to a u64 variable, but the multiplication may overflow before the assignment. For sufficiently large HEM tables, this produces an incorrect object index passed to hns_roce_table_mhop_put(). Cast 'i' to u64 before the multiplication so that the intermediate calculation is performed with 64-bit arithmetic. Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: a25d13cbe816 ("RDMA/hns: Add the interfaces to support multi hop addressing for the contexts in hip08") Link: https://patch.msgid.link/r/20260627095951.51378-1-listdansp@mail.ru Signed-off-by: Danila Chernetsov Reviewed-by: Junxian Huang Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/hns/hns_roce_hem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/hns/hns_roce_hem.c b/drivers/infiniband/hw/hns/hns_roce_hem.c index 7041a8e9134b..92edec4fa61b 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hem.c +++ b/drivers/infiniband/hw/hns/hns_roce_hem.c @@ -836,7 +836,7 @@ static void hns_roce_cleanup_mhop_hem_table(struct hns_roce_dev *hr_dev, mhop.bt_chunk_size; for (i = 0; i < table->num_hem; ++i) { - obj = i * buf_chunk_size / table->obj_size; + obj = (u64)i * buf_chunk_size / table->obj_size; if (table->hem[i]) hns_roce_table_mhop_put(hr_dev, table, obj, 0); } From 63bbf9ac5dde2ba85e7b39d0a0b7d540e6252ba4 Mon Sep 17 00:00:00 2001 From: Wentao Liang Date: Thu, 25 Jun 2026 19:32:39 +0800 Subject: [PATCH 0437/1054] accel/amdxdna: Fix use-after-free in amdxdna_gem_dmabuf_mmap() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When vm_insert_pages() fails, the error path calls vma->vm_ops->close(vma) which internally calls drm_gem_vm_close() → drm_gem_object_put(), releasing the GEM object reference acquired at the start of the function. However, the close_vma label then falls through to put_obj, which calls drm_gem_object_put() a second time on the same object. If the first put releases the last reference, the object is freed and the second put accesses freed memory, causing a use-after-free. Fix by returning directly from close_vma instead of falling through to put_obj, since the close handler already performs all necessary cleanup including the object put. Cc: stable@vger.kernel.org Fixes: e486147c912f ("accel/amdxdna: Add BO import and export") Signed-off-by: Wentao Liang Reviewed-by: Lizhi Hou Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/20260625113239.49764-1-vulab@iscas.ac.cn --- drivers/accel/amdxdna/amdxdna_gem.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/accel/amdxdna/amdxdna_gem.c b/drivers/accel/amdxdna/amdxdna_gem.c index 891112c2cddf..45abd6a804cc 100644 --- a/drivers/accel/amdxdna/amdxdna_gem.c +++ b/drivers/accel/amdxdna/amdxdna_gem.c @@ -527,6 +527,7 @@ static int amdxdna_gem_dmabuf_mmap(struct dma_buf *dma_buf, struct vm_area_struc close_vma: vma->vm_ops->close(vma); + return ret; put_obj: drm_gem_object_put(gobj); return ret; From bb27fcc67c429d97f785c92c35a6c5adebb05d7f Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Tue, 30 Jun 2026 14:00:40 +0800 Subject: [PATCH 0438/1054] RDMA/siw: publish QP after initialization siw_create_qp() currently calls siw_qp_add() before the queues, CQ pointers, state, completion, and device list entry are ready. A QPN lookup can therefore reach a QP that is still being constructed. Move siw_qp_add() to the end of siw_create_qp(), after QP initialization and before adding the QP to the siw device list. Fixes: f29dd55b0236 ("rdma/siw: queue pair methods") Link: https://patch.msgid.link/r/20260630060040.966461-1-ruoyuw560@gmail.com Suggested-by: Bernard Metzler Signed-off-by: Ruoyu Wang Acked-by: Bernard Metzler Signed-off-by: Jason Gunthorpe --- drivers/infiniband/sw/siw/siw_verbs.c | 44 +++++++++++++++------------ 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/drivers/infiniband/sw/siw/siw_verbs.c b/drivers/infiniband/sw/siw/siw_verbs.c index b74ac85c1b8b..2349c08c32ed 100644 --- a/drivers/infiniband/sw/siw/siw_verbs.c +++ b/drivers/infiniband/sw/siw/siw_verbs.c @@ -318,6 +318,7 @@ int siw_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *attrs, struct siw_ucontext *uctx = rdma_udata_to_drv_context(udata, struct siw_ucontext, base_ucontext); + struct siw_uresp_create_qp uresp = {}; unsigned long flags; int num_sqe, num_rqe, rv = 0; size_t length; @@ -371,11 +372,6 @@ int siw_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *attrs, spin_lock_init(&qp->rq_lock); spin_lock_init(&qp->orq_lock); - rv = siw_qp_add(sdev, qp); - if (rv) - goto err_atomic; - - /* All queue indices are derived from modulo operations * on a free running 'get' (consumer) and 'put' (producer) * unsigned counter. Having queue sizes at power of two @@ -393,14 +389,14 @@ int siw_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *attrs, if (qp->sendq == NULL) { rv = -ENOMEM; - goto err_out_xa; + goto err_out; } if (attrs->sq_sig_type != IB_SIGNAL_REQ_WR) { if (attrs->sq_sig_type == IB_SIGNAL_ALL_WR) qp->attrs.flags |= SIW_SIGNAL_ALL_WR; else { rv = -EINVAL; - goto err_out_xa; + goto err_out; } } qp->pd = pd; @@ -426,7 +422,7 @@ int siw_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *attrs, if (qp->recvq == NULL) { rv = -ENOMEM; - goto err_out_xa; + goto err_out; } qp->attrs.rq_size = num_rqe; } @@ -441,11 +437,8 @@ int siw_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *attrs, qp->attrs.state = SIW_QP_STATE_IDLE; if (udata) { - struct siw_uresp_create_qp uresp = {}; - uresp.num_sqe = num_sqe; uresp.num_rqe = num_rqe; - uresp.qp_id = qp_id(qp); if (qp->sendq) { length = num_sqe * sizeof(struct siw_sqe); @@ -454,7 +447,7 @@ int siw_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *attrs, length, &uresp.sq_key); if (!qp->sq_entry) { rv = -ENOMEM; - goto err_out_xa; + goto err_out; } } @@ -466,9 +459,23 @@ int siw_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *attrs, if (!qp->rq_entry) { uresp.sq_key = SIW_INVAL_UOBJ_KEY; rv = -ENOMEM; - goto err_out_xa; + goto err_out; } } + } + qp->tx_cpu = siw_get_tx_cpu(sdev); + if (qp->tx_cpu < 0) { + rv = -EINVAL; + goto err_out; + } + init_completion(&qp->qp_free); + + rv = siw_qp_add(sdev, qp); + if (rv) + goto err_out_tx; + + if (udata) { + uresp.qp_id = qp_id(qp); if (udata->outlen < sizeof(uresp)) { rv = -EINVAL; @@ -478,22 +485,19 @@ int siw_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *attrs, if (rv) goto err_out_xa; } - qp->tx_cpu = siw_get_tx_cpu(sdev); - if (qp->tx_cpu < 0) { - rv = -EINVAL; - goto err_out_xa; - } + INIT_LIST_HEAD(&qp->devq); spin_lock_irqsave(&sdev->lock, flags); list_add_tail(&qp->devq, &sdev->qp_list); spin_unlock_irqrestore(&sdev->lock, flags); - init_completion(&qp->qp_free); - return 0; err_out_xa: xa_erase(&sdev->qp_xa, qp_id(qp)); +err_out_tx: + siw_put_tx_cpu(qp->tx_cpu); +err_out: if (uctx) { rdma_user_mmap_entry_remove(qp->sq_entry); rdma_user_mmap_entry_remove(qp->rq_entry); From 83245e7a436c04e511378af14dd81fd188b41541 Mon Sep 17 00:00:00 2001 From: John Madieu Date: Tue, 30 Jun 2026 17:53:29 +0000 Subject: [PATCH 0439/1054] ASoC: rsnd: src: Add missing scu_supply clock to suspend/resume scu_supply is enabled alongside scu and scu_x2 during normal SRC operation, but rsnd_src_suspend() and rsnd_src_resume() only disable and re-enable scu and scu_x2. The supply clock is left enabled across a system suspend and its prepare/enable refcount becomes unbalanced after a suspend/resume cycle. Disable scu_supply in rsnd_src_suspend() and re-enable it in rsnd_src_resume() so the SRC clocks are managed consistently across system PM transitions. Fixes: ef19ecf042b4 ("ASoC: rsnd: Add system suspend/resume support") Signed-off-by: John Madieu Acked-by: Kuninori Morimoto Link: https://patch.msgid.link/20260630175329.4145703-1-john.madieu.xa@bp.renesas.com Signed-off-by: Mark Brown --- sound/soc/renesas/rcar/src.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/renesas/rcar/src.c b/sound/soc/renesas/rcar/src.c index ac806bdc96d9..2cdb39e898af 100644 --- a/sound/soc/renesas/rcar/src.c +++ b/sound/soc/renesas/rcar/src.c @@ -850,6 +850,7 @@ void rsnd_src_suspend(struct rsnd_priv *priv) clk_disable_unprepare(src_ctrl->scu_x2); clk_disable_unprepare(src_ctrl->scu); + clk_disable_unprepare(src_ctrl->scu_supply); } void rsnd_src_resume(struct rsnd_priv *priv) @@ -861,6 +862,7 @@ void rsnd_src_resume(struct rsnd_priv *priv) if (!src_ctrl) return; + clk_prepare_enable(src_ctrl->scu_supply); clk_prepare_enable(src_ctrl->scu); clk_prepare_enable(src_ctrl->scu_x2); From fdb3be00ba4dafa313e699d6b5b90d13f22f3f25 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Thu, 2 Jul 2026 20:45:24 +0200 Subject: [PATCH 0440/1054] batman-adv: fix VLAN priority offset The batadv_skb_set_priority() receives an SKB with the inner ethernet header at position "offset". When it tries to extract the IPv4 and IPv6 header, it needs to skip the ethernet header to get access to the IP header. But for VLAN header, it performs the access with the struct vlan_ethhdr. This struct contains both both the ethernet header and the VLAN header. It is therefore incorrect to skip over the whole vlan_ethhdr size to get access to the vlan_ethhdr. Cc: stable@vger.kernel.org Fixes: c54f38c9aa22 ("batman-adv: set skb priority according to content") Signed-off-by: Sven Eckelmann --- net/batman-adv/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c index 4d3807a645b7..8844e40e6a80 100644 --- a/net/batman-adv/main.c +++ b/net/batman-adv/main.c @@ -368,7 +368,7 @@ void batadv_skb_set_priority(struct sk_buff *skb, int offset) switch (ethhdr->h_proto) { case htons(ETH_P_8021Q): - vhdr = skb_header_pointer(skb, offset + sizeof(*vhdr), + vhdr = skb_header_pointer(skb, offset, sizeof(*vhdr), &vhdr_tmp); if (!vhdr) return; From 39def6d250d370298f86c116f4ac60093cefadaa Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 1 Jul 2026 15:11:50 +0200 Subject: [PATCH 0441/1054] futex/requeue: Revert "Prevent NULL pointer dereference in remove_waiter() on self-deadlock"" The commit cited below should not have been merged. It attemted to fix an existing problem ansd thereby introduced new problems by keeping the pi_state in state Q_REQUEUE_PI_IN_PROGRESS and leaking it. Based on the commit description the intention was to handle the case when task_blocks_on_rt_mutex() returns -EDEADLK and the following remove_waiter() dereferences the NULL pointer in waiter->task. That is already handled by Davidlohr in commit 40a25d59e85b3 ("locking/rtmutex: Skip remove_waiter() when waiter is not enqueued") and requires no further acting. Revert the commit breaking the "waiter == owner" case again. Fixes: 74e144274af39 ("futex/requeue: Prevent NULL pointer dereference in remove_waiter() on self-deadlock") Reported-by: Michael Bommarito Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Thomas Gleixner Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260701131150.0Ijhq4Dw@linutronix.de Closes: https://lore.kernel.org/all/20260629020049.2082397-1-michael.bommarito@gmail.com --- kernel/futex/requeue.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/kernel/futex/requeue.c b/kernel/futex/requeue.c index 7384672916fb..79823ad13683 100644 --- a/kernel/futex/requeue.c +++ b/kernel/futex/requeue.c @@ -645,12 +645,6 @@ 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 58481c749c976e81bef9a540e2225ddb021daaa6 Mon Sep 17 00:00:00 2001 From: Yang Xiuwei Date: Thu, 2 Jul 2026 16:29:37 +0800 Subject: [PATCH 0442/1054] io_uring/uring_cmd: fix uring_cmd.c comments Fix "concelable" -> "cancelable" in the comment above io_uring_cmd_mark_cancelable(), and fix the memory ordering comment in __io_uring_cmd_done() to reference io_do_iopoll() and ->iopoll_completed. Signed-off-by: Yang Xiuwei Link: https://patch.msgid.link/20260702082937.3707134-3-yangxiuwei@kylinos.cn Signed-off-by: Jens Axboe --- io_uring/uring_cmd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/io_uring/uring_cmd.c b/io_uring/uring_cmd.c index 7b25dcd9d05f..c14c22cff49e 100644 --- a/io_uring/uring_cmd.c +++ b/io_uring/uring_cmd.c @@ -90,7 +90,7 @@ static void io_uring_cmd_del_cancelable(struct io_uring_cmd *cmd, } /* - * Mark this command as concelable, then io_uring_try_cancel_uring_cmd() + * Mark this command as cancelable, then io_uring_try_cancel_uring_cmd() * will try to cancel this issued command by sending ->uring_cmd() with * issue_flags of IO_URING_F_CANCEL. * @@ -168,7 +168,7 @@ void __io_uring_cmd_done(struct io_uring_cmd *ioucmd, s32 ret, u64 res2, } io_req_uring_cleanup(req, issue_flags); if (req->flags & REQ_F_IOPOLL) { - /* order with io_iopoll_req_issued() checking ->iopoll_complete */ + /* order with io_do_iopoll() checking ->iopoll_completed */ smp_store_release(&req->iopoll_completed, 1); } else if (issue_flags & IO_URING_F_COMPLETE_DEFER) { if (WARN_ON_ONCE(issue_flags & IO_URING_F_UNLOCKED)) From 738f24bbbc95dd50cb4229d1ed62a05f29db2bda Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 29 Jun 2026 22:47:34 -0700 Subject: [PATCH 0443/1054] Input: maplemouse - set driver data before registering input device Set maple driver data before calling input_register_device() to ensure that it is available if the device is opened immediately and the callback is triggered. Cc: stable@vger.kernel.org Assisted-by: Antigravity:gemini-3.5-flash Tested-by: Florian Fuchs Link: https://patch.msgid.link/akNXw45L_8bxD6QV@google.com Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/maplemouse.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/input/mouse/maplemouse.c b/drivers/input/mouse/maplemouse.c index c41182766538..0c8f7d1b02aa 100644 --- a/drivers/input/mouse/maplemouse.c +++ b/drivers/input/mouse/maplemouse.c @@ -88,6 +88,8 @@ static int probe_maple_mouse(struct device *dev) mse->dev = input_dev; mse->mdev = mdev; + maple_set_drvdata(mdev, mse); + input_set_drvdata(input_dev, mse); input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) | @@ -103,12 +105,12 @@ static int probe_maple_mouse(struct device *dev) goto fail_register; mdev->driver = mdrv; - maple_set_drvdata(mdev, mse); return error; fail_register: input_free_device(input_dev); + maple_set_drvdata(mdev, NULL); fail_nomem: kfree(mse); fail: From fe938ee497d58c644f6910cfe6ae155f6fb3e523 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 29 Jun 2026 22:49:15 -0700 Subject: [PATCH 0444/1054] Input: maplecontrol - set driver data before registering input device Set maple driver data before calling input_register_device() to ensure that it is available if the device is opened immediately and the callback is triggered. Cc: stable@vger.kernel.org Assisted-by: Antigravity:gemini-3.5-flash Tested-by: Florian Fuchs Link: https://patch.msgid.link/akNYib9hQFNN1fA9@google.com Signed-off-by: Dmitry Torokhov --- drivers/input/joystick/maplecontrol.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/input/joystick/maplecontrol.c b/drivers/input/joystick/maplecontrol.c index 7f36f73844a9..6293b6e8148b 100644 --- a/drivers/input/joystick/maplecontrol.c +++ b/drivers/input/joystick/maplecontrol.c @@ -112,6 +112,8 @@ static int probe_maple_controller(struct device *dev) pad->dev = idev; pad->mdev = mdev; + maple_set_drvdata(mdev, pad); + idev->open = dc_pad_open; idev->close = dc_pad_close; @@ -146,7 +148,6 @@ static int probe_maple_controller(struct device *dev) goto fail; mdev->driver = mdrv; - maple_set_drvdata(mdev, pad); return 0; From 536394ec81419b67d9f4f0028812c4372397be1b Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 29 Jun 2026 18:44:41 -0700 Subject: [PATCH 0445/1054] Input: maple_keyb - set driver data before registering input device Set maple driver data before calling input_register_device() to ensure that it is available if the device is opened immediately and the callback is triggered. Cc: stable@vger.kernel.org Assisted-by: Antigravity:gemini-3.5-flash Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/maple_keyb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/input/keyboard/maple_keyb.c b/drivers/input/keyboard/maple_keyb.c index 80a5181313e1..3d5538dd4f23 100644 --- a/drivers/input/keyboard/maple_keyb.c +++ b/drivers/input/keyboard/maple_keyb.c @@ -166,6 +166,8 @@ static int probe_maple_kbd(struct device *dev) kbd->dev = idev; memcpy(kbd->keycode, dc_kbd_keycode, sizeof(kbd->keycode)); + maple_set_drvdata(mdev, kbd); + idev->name = mdev->product_name; idev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP); idev->keycode = kbd->keycode; @@ -190,8 +192,6 @@ static int probe_maple_kbd(struct device *dev) mdev->driver = mdrv; - maple_set_drvdata(mdev, kbd); - return error; fail_register: From 03f384bc0cb8d4a1301d4f5b0baef2d980258383 Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Mon, 29 Jun 2026 21:51:21 -0700 Subject: [PATCH 0446/1054] net: usb: net1080: validate packet_len before pad-byte access in rx_fixup For an even packet_len, net1080_rx_fixup() reads the pad byte at skb->data[packet_len] before the skb->len != packet_len check further down, and packet_len is only bounded against NC_MAX_PACKET. A malicious NetChip 1080 device can send a short frame advertising a large even packet_len (e.g. 0x4000), so the pad-byte read lands past the end of the skb: BUG: KASAN: slab-out-of-bounds in net1080_rx_fixup Read of size 1 at addr ffff8880106c83c6 by task ksoftirqd/0/14 ... net1080_rx_fixup (drivers/net/usb/net1080.c:384) usbnet_bh (drivers/net/usb/usbnet.c:1589) process_one_work (kernel/workqueue.c:3322) bh_worker (kernel/workqueue.c:3708) tasklet_action (kernel/softirq.c:965) handle_softirqs (kernel/softirq.c:622) ... Reject the frame when packet_len >= skb->len before reading. Fixes: 904813cd8a0b ("[PATCH] USB: usbnet (4/9) module for net1080 cables") Reported-by: Weiming Shi Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Xiang Mei Link: https://patch.msgid.link/20260630045121.1565324-1-xmei5@asu.edu Signed-off-by: Paolo Abeni --- drivers/net/usb/net1080.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/usb/net1080.c b/drivers/net/usb/net1080.c index 5d4a1fd2b524..19f6e1222d93 100644 --- a/drivers/net/usb/net1080.c +++ b/drivers/net/usb/net1080.c @@ -381,7 +381,7 @@ static int net1080_rx_fixup(struct usbnet *dev, struct sk_buff *skb) skb_trim(skb, skb->len - sizeof *trailer); if ((packet_len & 0x01) == 0) { - if (skb->data [packet_len] != PAD_BYTE) { + if (packet_len >= skb->len || skb->data[packet_len] != PAD_BYTE) { dev->net->stats.rx_frame_errors++; netdev_dbg(dev->net, "bad pad\n"); return 0; From 62e7df6d042aeebd5efb581074e28865c04477be Mon Sep 17 00:00:00 2001 From: Dawei Feng Date: Tue, 30 Jun 2026 15:16:25 +0800 Subject: [PATCH 0447/1054] octeontx2-pf: fix SQB pointer leak on init failure otx2_init_hw_resources() initializes SQ aura and pool resources before several later setup steps. On failure, err_free_sq_ptrs only frees SQB pages, leaving the per-SQ sqb_ptrs arrays behind. Use otx2_free_sq_res() for the SQ unwind path and let it free sqb_ptrs even when sq->sqe has not been allocated yet. 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.1. An x86_64 allyesconfig build showed no new warnings. As we do not have an OcteonTX2 PF device and the corresponding AF mailbox setup to test with, no runtime testing was able to be performed. Fixes: caa2da34fd25 ("octeontx2-pf: Initialize and config queues") Cc: stable@vger.kernel.org Reviewed-by: Ratheesh Kannoth Signed-off-by: Dawei Feng Link: https://patch.msgid.link/20260630071625.349996-1-dawei.feng@seu.edu.cn Signed-off-by: Paolo Abeni --- .../ethernet/marvell/octeontx2/nic/otx2_pf.c | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c index b63df5737ff2..88ac85354445 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c @@ -1568,15 +1568,15 @@ static void otx2_free_sq_res(struct otx2_nic *pf) otx2_sq_free_sqbs(pf); for (qidx = 0; qidx < otx2_get_total_tx_queues(pf); qidx++) { sq = &qset->sq[qidx]; - /* Skip freeing Qos queues if they are not initialized */ - if (!sq->sqe) - continue; - qmem_free(pf->dev, sq->sqe); - qmem_free(pf->dev, sq->sqe_ring); - qmem_free(pf->dev, sq->cpt_resp); - qmem_free(pf->dev, sq->tso_hdrs); - qmem_free(pf->dev, sq->timestamps); - kfree(sq->sg); + /* sq->sqe is not initialized for unused QoS queues */ + if (sq->sqe) { + qmem_free(pf->dev, sq->sqe); + qmem_free(pf->dev, sq->sqe_ring); + qmem_free(pf->dev, sq->cpt_resp); + qmem_free(pf->dev, sq->tso_hdrs); + qmem_free(pf->dev, sq->timestamps); + kfree(sq->sg); + } kfree(sq->sqb_ptrs); } } @@ -1711,13 +1711,12 @@ int otx2_init_hw_resources(struct otx2_nic *pf) return err; err_free_nix_queues: - otx2_free_sq_res(pf); otx2_free_cq_res(pf); otx2_ctx_disable(mbox, NIX_AQ_CTYPE_RQ, false); err_free_txsch: otx2_txschq_stop(pf); err_free_sq_ptrs: - otx2_sq_free_sqbs(pf); + otx2_free_sq_res(pf); err_free_rq_ptrs: otx2_free_aura_ptr(pf, AURA_NIX_RQ); otx2_ctx_disable(mbox, NPA_AQ_CTYPE_POOL, true); From d335dcc6f521571d57117b8deeebc940836e5450 Mon Sep 17 00:00:00 2001 From: Qihang Date: Wed, 1 Jul 2026 10:26:17 +0800 Subject: [PATCH 0448/1054] gue: validate REMCSUM private option length GUE private flags can indicate that remote checksum offload metadata is present. The private flags field itself is accounted for by guehdr_flags_len(), but guehdr_priv_flags_len() currently returns 0 even when GUE_PFLAG_REMCSUM is set. This lets a packet with only the private flags field pass validate_gue_flags(), after which gue_remcsum() and gue_gro_remcsum() read the missing REMCSUM start/offset fields from the following bytes. Account for GUE_PLEN_REMCSUM when GUE_PFLAG_REMCSUM is present so that malformed packets are rejected during option validation. Fixes: c1aa8347e73e ("gue: Protocol constants for remote checksum offload") Signed-off-by: Qihang Reviewed-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/gue.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/net/gue.h b/include/net/gue.h index dfca298bec9c..caefd6da8693 100644 --- a/include/net/gue.h +++ b/include/net/gue.h @@ -80,7 +80,7 @@ static inline size_t guehdr_flags_len(__be16 flags) static inline size_t guehdr_priv_flags_len(__be32 flags) { - return 0; + return (flags & GUE_PFLAG_REMCSUM) ? GUE_PLEN_REMCSUM : 0; } /* Validate standard and private flags. Returns non-zero (meaning invalid) From 5c6ce05e406520290c1d89da97fb3cd70c09137d Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 2 Jul 2026 09:23:02 +0100 Subject: [PATCH 0449/1054] netfs: Fix barriering when walking subrequest list Fix the barriering used when walking the subrequest list in retry as there's a possibility of seeing a subreq that's just been added by the application thread. Fixes: ee4cdf7ba857 ("netfs: Speed up buffered reading") Fixes: 288ace2f57c9 ("netfs: New writeback implementation") Link: https://sashiko.dev/#/patchset/20260608145432.681865-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/138807.1782980582@warthog.procyon.org.uk Reviewed-by: Paulo Alcantara (Red Hat) cc: Paulo Alcantara cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner (Amutable) --- fs/netfs/read_retry.c | 7 ++++++- fs/netfs/write_retry.c | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/fs/netfs/read_retry.c b/fs/netfs/read_retry.c index f59a70f3a086..2b42758e01ec 100644 --- a/fs/netfs/read_retry.c +++ b/fs/netfs/read_retry.c @@ -98,7 +98,12 @@ static void netfs_retry_read_subrequests(struct netfs_io_request *rreq) goto abandon; } - list_for_each_continue(next, &stream->subrequests) { + for (;;) { + /* Read pointer to subreq before reading subreq state. */ + next = smp_load_acquire(&next->next); + if (next == &stream->subrequests) + break; + subreq = list_entry(next, struct netfs_io_subrequest, rreq_link); if (subreq->start + subreq->transferred != start + len || test_bit(NETFS_SREQ_BOUNDARY, &subreq->flags) || diff --git a/fs/netfs/write_retry.c b/fs/netfs/write_retry.c index 32735abfa03f..058bc7a166a5 100644 --- a/fs/netfs/write_retry.c +++ b/fs/netfs/write_retry.c @@ -72,7 +72,12 @@ static void netfs_retry_write_stream(struct netfs_io_request *wreq, !test_bit(NETFS_SREQ_NEED_RETRY, &from->flags)) return; - list_for_each_continue(next, &stream->subrequests) { + for (;;) { + /* Read pointer to subreq before reading subreq state. */ + next = smp_load_acquire(&next->next); + if (next == &stream->subrequests) + break; + subreq = list_entry(next, struct netfs_io_subrequest, rreq_link); if (subreq->start + subreq->transferred != start + len || test_bit(NETFS_SREQ_BOUNDARY, &subreq->flags) || From 77bfebf110773f5a0d6b5ff8110896adb2c9c335 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Fri, 5 Jun 2026 11:13:50 +0000 Subject: [PATCH 0450/1054] rust_binder: fix BINDER_GET_EXTENDED_ERROR This code currently copies the ExtendedError struct to the stack, modifies the copy, and then doesn't modify the original. Thus, fix it. Furthermore, errors when replying must be delivered directly to the remote thread, so update deliver_reply() to take an extended error argument. Cc: stable Fixes: eafedbc7c050 ("rust_binder: add Rust Binder driver") Signed-off-by: Alice Ryhl Acked-by: Carlos Llamas Link: https://patch.msgid.link/20260605-set-extended-error-v3-1-d60b69a75f97@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/error.rs | 13 +++--- drivers/android/binder/thread.rs | 65 +++++++++++++++++++-------- drivers/android/binder/transaction.rs | 15 +++---- 3 files changed, 58 insertions(+), 35 deletions(-) diff --git a/drivers/android/binder/error.rs b/drivers/android/binder/error.rs index 45d85d4c2815..1296072c35d9 100644 --- a/drivers/android/binder/error.rs +++ b/drivers/android/binder/error.rs @@ -73,20 +73,17 @@ impl fmt::Debug for BinderError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.reply { BR_FAILED_REPLY => match self.source.as_ref() { - Some(source) => f - .debug_struct("BR_FAILED_REPLY") - .field("source", source) - .finish(), + Some(source) => source.fmt(f), None => f.pad("BR_FAILED_REPLY"), }, BR_DEAD_REPLY => f.pad("BR_DEAD_REPLY"), BR_FROZEN_REPLY => f.pad("BR_FROZEN_REPLY"), BR_TRANSACTION_PENDING_FROZEN => f.pad("BR_TRANSACTION_PENDING_FROZEN"), BR_TRANSACTION_COMPLETE => f.pad("BR_TRANSACTION_COMPLETE"), - _ => f - .debug_struct("BinderError") - .field("reply", &self.reply) - .finish(), + _ => match self.source.as_ref() { + Some(source) => source.fmt(f), + None => self.reply.fmt(f), + }, } } } diff --git a/drivers/android/binder/thread.rs b/drivers/android/binder/thread.rs index 97d5f31e8fe3..3b8520813941 100644 --- a/drivers/android/binder/thread.rs +++ b/drivers/android/binder/thread.rs @@ -495,9 +495,16 @@ pub(crate) fn debug_print(self: &Arc, m: &SeqFile, print_all: bool) -> Res Ok(()) } + pub(crate) fn clear_extended_error(&self, debug_id: usize) { + self.inner.lock().extended_error = ExtendedError::new(debug_id as u32, BR_OK, 0); + } + pub(crate) fn get_extended_error(&self, data: UserSlice) -> Result { let mut writer = data.writer(); - let ee = self.inner.lock().extended_error; + let mut inner = self.inner.lock(); + let ee = inner.extended_error; + inner.extended_error = ExtendedError::new(0, BR_OK, 0); + drop(inner); writer.write(&ee)?; Ok(()) } @@ -1109,7 +1116,10 @@ fn unwind_transaction_stack(self: &Arc) { inner.pop_transaction_to_reply(thread.as_ref()) } { let reply = Err(BR_DEAD_REPLY); - if !transaction.from.deliver_single_reply(reply, &transaction) { + if !transaction + .from + .deliver_single_reply(reply, &transaction, None) + { break; } @@ -1121,8 +1131,9 @@ pub(crate) fn deliver_reply( &self, reply: Result, u32>, transaction: &DArc, + extended_error: Option, ) { - if self.deliver_single_reply(reply, transaction) { + if self.deliver_single_reply(reply, transaction, extended_error) { transaction.from.unwind_transaction_stack(); } } @@ -1136,6 +1147,7 @@ fn deliver_single_reply( &self, reply: Result, u32>, transaction: &DArc, + extended_error: Option, ) -> bool { if let Ok(transaction) = &reply { crate::trace::trace_transaction(true, transaction, Some(&self.task)); @@ -1152,6 +1164,12 @@ fn deliver_single_reply( return true; } + if let Some(ee) = extended_error { + if inner.extended_error.command == BR_OK { + inner.extended_error = ee; + } + } + match reply { Ok(work) => { inner.push_work(work); @@ -1222,6 +1240,9 @@ fn read_transaction_info( info.buffers_size = td.buffers_size as usize; // SAFETY: Above `read` call initializes all bytes, so this union read is ok. info.target_handle = unsafe { td.transaction_data.target.handle }; + + info.debug_id = super::next_debug_id(); + Ok(()) } @@ -1230,6 +1251,8 @@ fn transaction(self: &Arc, cmd: u32, reader: &mut UserSliceReader) -> Resu let mut info = TransactionInfo::zeroed(); self.read_transaction_info(cmd, reader, &mut info)?; + self.clear_extended_error(info.debug_id); + let ret = if info.is_reply { self.reply_inner(&mut info) } else if info.is_oneway() { @@ -1239,23 +1262,21 @@ fn transaction(self: &Arc, cmd: u32, reader: &mut UserSliceReader) -> Resu }; if let Err(err) = ret { + self.push_return_work(err.reply); if err.reply != BR_TRANSACTION_COMPLETE { info.reply = err.reply; - } + if let Some(source) = &err.source { + info.errno = source.to_errno(); - self.push_return_work(err.reply); - if let Some(source) = &err.source { - info.errno = source.to_errno(); - info.reply = err.reply; - - { - let mut ee = self.inner.lock().extended_error; - ee.command = err.reply; - ee.param = source.to_errno(); + { + let mut inner = self.inner.lock(); + inner.extended_error = + ExtendedError::new(info.debug_id as u32, err.reply, source.to_errno()); + } } pr_warn!( - "{}:{} transaction to {} failed: {source:?}", + "{}:{} transaction to {} failed: {err:?}", info.from_pid, info.from_tid, info.to_pid @@ -1320,18 +1341,24 @@ fn reply_inner(self: &Arc, info: &mut TransactionInfo) -> BinderResult { let allow_fds = orig.flags & TF_ACCEPT_FDS != 0; let reply = Transaction::new_reply(self, process, info, allow_fds)?; self.inner.lock().push_work(completion); - orig.from.deliver_reply(Ok(reply), &orig); + orig.from.deliver_reply(Ok(reply), &orig, None); Ok(()) })() .map_err(|mut err| { // At this point we only return `BR_TRANSACTION_COMPLETE` to the caller, and we must let // the sender know that the transaction has completed (with an error in this case). + pr_warn!( - "Failure {:?} during reply - delivering BR_FAILED_REPLY to sender.", - err + "{}:{} reply to {} failed: {err:?}", + info.from_pid, + info.from_tid, + info.to_pid ); - let reply = Err(BR_FAILED_REPLY); - orig.from.deliver_reply(reply, &orig); + + let param = err.source.as_ref().map_or(0, |e| e.to_errno()); + let ee = ExtendedError::new(info.debug_id as u32, err.reply, param); + orig.from + .deliver_reply(Err(BR_FAILED_REPLY), &orig, Some(ee)); err.reply = BR_TRANSACTION_COMPLETE; err }); diff --git a/drivers/android/binder/transaction.rs b/drivers/android/binder/transaction.rs index 1d9b66920a21..0e5d07b7e6f0 100644 --- a/drivers/android/binder/transaction.rs +++ b/drivers/android/binder/transaction.rs @@ -42,6 +42,7 @@ pub(crate) struct TransactionInfo { pub(crate) reply: u32, pub(crate) oneway_spam_suspect: bool, pub(crate) is_reply: bool, + pub(crate) debug_id: usize, } impl TransactionInfo { @@ -93,7 +94,6 @@ pub(crate) fn new( from: &Arc, info: &mut TransactionInfo, ) -> BinderResult> { - let debug_id = super::next_debug_id(); let allow_fds = node_ref.node.flags & FLAT_BINDER_FLAG_ACCEPTS_FDS != 0; let txn_security_ctx = node_ref.node.flags & FLAT_BINDER_FLAG_TXN_SECURITY_CTX != 0; let mut txn_security_ctx_off = if txn_security_ctx { Some(0) } else { None }; @@ -101,7 +101,7 @@ pub(crate) fn new( let mut alloc = match from.copy_transaction_data( to.clone(), info, - debug_id, + info.debug_id, allow_fds, txn_security_ctx_off.as_mut(), ) { @@ -128,7 +128,7 @@ pub(crate) fn new( let data_address = alloc.ptr; Ok(DTRWrap::arc_pin_init(pin_init!(Transaction { - debug_id, + debug_id: info.debug_id, target_node: Some(target_node), from_parent, sender_euid: Kuid::current_euid(), @@ -152,9 +152,8 @@ pub(crate) fn new_reply( info: &mut TransactionInfo, allow_fds: bool, ) -> BinderResult> { - let debug_id = super::next_debug_id(); let mut alloc = - match from.copy_transaction_data(to.clone(), info, debug_id, allow_fds, None) { + match from.copy_transaction_data(to.clone(), info, info.debug_id, allow_fds, None) { Ok(alloc) => alloc, Err(err) => { pr_warn!("Failure in copy_transaction_data: {:?}", err); @@ -165,7 +164,7 @@ pub(crate) fn new_reply( alloc.set_info_clear_on_drop(); } Ok(DTRWrap::arc_pin_init(pin_init!(Transaction { - debug_id, + debug_id: info.debug_id, target_node: None, from_parent: None, sender_euid: Kuid::current_euid(), @@ -394,7 +393,7 @@ fn do_work( let send_failed_reply = ScopeGuard::new(|| { if self.target_node.is_some() && self.flags & TF_ONE_WAY == 0 { let reply = Err(BR_FAILED_REPLY); - self.from.deliver_reply(reply, &self); + self.from.deliver_reply(reply, &self, None); } self.drop_outstanding_txn(); }); @@ -478,7 +477,7 @@ fn cancel(self: DArc) { // If this is not a reply or oneway transaction, then send a dead reply. if self.target_node.is_some() && self.flags & TF_ONE_WAY == 0 { let reply = Err(BR_DEAD_REPLY); - self.from.deliver_reply(reply, &self); + self.from.deliver_reply(reply, &self, None); } self.drop_outstanding_txn(); From b34826e55aad3520ec813f1f367c11b24b29dc9f Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Wed, 3 Jun 2026 10:44:54 -0700 Subject: [PATCH 0451/1054] binder: cache secctx size before release zeroes it binder_transaction() bounds the scatter-gather buffer area with sg_buf_end_offset and subtracts the aligned LSM context size because the secctx is written at the tail of that area. The subtraction reads lsmctx.len, but that field has already been cleared by the time the line runs: security_secid_to_secctx(secid, &lsmctx) /* lsmctx.len set */ lsmctx_aligned_size = ALIGN(lsmctx.len, sizeof(u64)) extra_buffers_size += lsmctx_aligned_size ... security_release_secctx(&lsmctx) /* memset zeroes len */ ... sg_buf_end_offset = sg_buf_offset + extra_buffers_size - ALIGN(lsmctx.len, sizeof(u64)) /* ALIGN(0,8) */ security_release_secctx() does memset(cp, 0, sizeof(*cp)), so lsmctx.len reads back as 0 and the subtraction contributes nothing, leaving sg_buf_end_offset too large by the aligned secctx size on every transaction to a txn_security_ctx node. Each BINDER_TYPE_PTR object then derives buf_left = sg_buf_end_offset - sg_buf_offset as the sole upper bound on its copy, so the inflated end offset lets the copy run into the bytes that already hold the secctx. The aligned size must therefore be cached before release rather than re-read from the now-cleared field. Fix by caching it in lsmctx_aligned_size at function scope when it is first computed and subtracting lsmctx_aligned_size instead of re-reading lsmctx.len after release. Reuse the same value for the earlier buf_offset computation. Fixes: 6fba89813ccf ("lsm: ensure the correct LSM context releaser") Cc: stable Assisted-by: kres:claude-opus-4-8 Signed-off-by: Chris Mason Reviewed-by: Alice Ryhl Acked-by: Carlos Llamas Link: https://patch.msgid.link/20260603174506.1957278-1-clm@meta.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index ec0ab4f28530..c48c22264266 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -3080,6 +3080,7 @@ static void binder_transaction(struct binder_proc *proc, int t_debug_id = atomic_inc_return(&binder_last_id); ktime_t t_start_time = ktime_get(); struct lsm_context lsmctx = { }; + size_t lsmctx_aligned_size = 0; LIST_HEAD(sgc_head); LIST_HEAD(pf_head); const void __user *user_buffer = (const void __user *) @@ -3346,7 +3347,6 @@ static void binder_transaction(struct binder_proc *proc, if (target_node && target_node->txn_security_ctx) { u32 secid; - size_t added_size; security_cred_getsecid(proc->cred, &secid); ret = security_secid_to_secctx(secid, &lsmctx); @@ -3358,9 +3358,9 @@ static void binder_transaction(struct binder_proc *proc, return_error_line = __LINE__; goto err_get_secctx_failed; } - added_size = ALIGN(lsmctx.len, sizeof(u64)); - extra_buffers_size += added_size; - if (extra_buffers_size < added_size) { + lsmctx_aligned_size = ALIGN(lsmctx.len, sizeof(u64)); + extra_buffers_size += lsmctx_aligned_size; + if (extra_buffers_size < lsmctx_aligned_size) { binder_txn_error("%d:%d integer overflow of extra_buffers_size\n", thread->pid, proc->pid); return_error = BR_FAILED_REPLY; @@ -3397,7 +3397,7 @@ static void binder_transaction(struct binder_proc *proc, size_t buf_offset = ALIGN(tr->data_size, sizeof(void *)) + ALIGN(tr->offsets_size, sizeof(void *)) + ALIGN(extra_buffers_size, sizeof(void *)) - - ALIGN(lsmctx.len, sizeof(u64)); + lsmctx_aligned_size; t->security_ctx = t->buffer->user_data + buf_offset; err = binder_alloc_copy_to_buffer(&target_proc->alloc, @@ -3452,7 +3452,7 @@ static void binder_transaction(struct binder_proc *proc, off_end_offset = off_start_offset + tr->offsets_size; sg_buf_offset = ALIGN(off_end_offset, sizeof(void *)); sg_buf_end_offset = sg_buf_offset + extra_buffers_size - - ALIGN(lsmctx.len, sizeof(u64)); + lsmctx_aligned_size; off_min = 0; for (buffer_offset = off_start_offset; buffer_offset < off_end_offset; buffer_offset += sizeof(binder_size_t)) { From eb1645bf10190e71f6f0316e37ff70755d719b53 Mon Sep 17 00:00:00 2001 From: Keshav Verma Date: Tue, 16 Jun 2026 02:47:43 +0530 Subject: [PATCH 0452/1054] rust_binder: synchronize Rust Binder stats with freeze commands Rust Binder stats use BC_COUNT and BR_COUNT to size the command and return counters, and use event string tables when printing debug statistics. The Binder protocol includes freeze-related commands and return codes, but the Rust Binder statistics code was not updated to cover them. As a result, those commands and return codes are not accounted for or printed by the stats debug output. Update the counts and event string tables so these commands and return codes are included in the debug statistics output. Fixes: eafedbc7c050 ("rust_binder: add Rust Binder driver") Cc: stable Acked-by: Carlos Llamas Reviewed-by: Alice Ryhl Signed-off-by: Keshav Verma Link: https://patch.msgid.link/20260615211743.734-1-iganschel@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/rust_binder_events.c | 7 ++++++- drivers/android/binder/stats.rs | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/android/binder/rust_binder_events.c b/drivers/android/binder/rust_binder_events.c index 488b1470060c..5792aa59cc82 100644 --- a/drivers/android/binder/rust_binder_events.c +++ b/drivers/android/binder/rust_binder_events.c @@ -28,6 +28,9 @@ const char * const binder_command_strings[] = { "BC_DEAD_BINDER_DONE", "BC_TRANSACTION_SG", "BC_REPLY_SG", + "BC_REQUEST_FREEZE_NOTIFICATION", + "BC_CLEAR_FREEZE_NOTIFICATION", + "BC_FREEZE_NOTIFICATION_DONE", }; const char * const binder_return_strings[] = { @@ -51,7 +54,9 @@ const char * const binder_return_strings[] = { "BR_FAILED_REPLY", "BR_FROZEN_REPLY", "BR_ONEWAY_SPAM_SUSPECT", - "BR_TRANSACTION_PENDING_FROZEN" + "BR_TRANSACTION_PENDING_FROZEN", + "BR_FROZEN_BINDER", + "BR_CLEAR_FREEZE_NOTIFICATION_DONE", }; #define CREATE_TRACE_POINTS diff --git a/drivers/android/binder/stats.rs b/drivers/android/binder/stats.rs index ab75e9561cbf..ec81dc7747db 100644 --- a/drivers/android/binder/stats.rs +++ b/drivers/android/binder/stats.rs @@ -8,8 +8,8 @@ use kernel::sync::atomic::{ordering::Relaxed, Atomic}; use kernel::{ioctl::_IOC_NR, seq_file::SeqFile, seq_print}; -const BC_COUNT: usize = _IOC_NR(BC_REPLY_SG) as usize + 1; -const BR_COUNT: usize = _IOC_NR(BR_TRANSACTION_PENDING_FROZEN) as usize + 1; +const BC_COUNT: usize = _IOC_NR(BC_FREEZE_NOTIFICATION_DONE) as usize + 1; +const BR_COUNT: usize = _IOC_NR(BR_CLEAR_FREEZE_NOTIFICATION_DONE) as usize + 1; pub(crate) static GLOBAL_STATS: BinderStats = BinderStats::new(); From 114a116aaa5f0295376cdf12da743c5bce3b20ce Mon Sep 17 00:00:00 2001 From: Carlos Llamas Date: Fri, 19 Jun 2026 18:52:30 +0000 Subject: [PATCH 0453/1054] binder: fix UAF in binder_thread_release() When a thread exits, binder_thread_release() walks its transaction stack to clear the t->from and t->to_proc that correspond with the exiting thread. However, a process dying in parallel might attempt to kfree some of these transactions. And if one of them has no associated t->to_proc, the t->to_proc->inner_lock will not be acquired. This means that transaction accesses in binder_thread_release() after t->to_proc has been cleared might race with binder_free_transaction() and cause a use-after-free error as reported by KASAN: ================================================================== BUG: KASAN: slab-use-after-free in binder_thread_release+0x5d0/0x798 Write of size 8 at addr ffff000016627500 by task X/715 CPU: 17 UID: 0 PID: 715 Comm: X Not tainted 7.1.0-rc5-00149-g8fde5d1d47f6 #30 PREEMPT Hardware name: linux,dummy-virt (DT) Call trace: binder_thread_release+0x5d0/0x798 binder_ioctl+0x12c0/0x299c [...] Allocated by task 717 on cpu 18 at 67.267803s: __kasan_kmalloc+0xa0/0xbc __kmalloc_cache_noprof+0x174/0x444 binder_transaction+0x554/0x8150 binder_thread_write+0xa30/0x4354 binder_ioctl+0x20f0/0x299c [...] Freed by task 202 on cpu 18 at 90.416221s: __kasan_slab_free+0x58/0x80 kfree+0x1a0/0x4a4 binder_free_transaction+0x150/0x294 binder_send_failed_reply+0x398/0x6d8 binder_release_work+0x3e4/0x4ec binder_deferred_func+0xbd8/0x104c [...] ================================================================== In order to avoid this, make sure that binder_free_transaction() reads the t->to_proc under the transaction lock. This will serialize the transaction release with the accesses in binder_thread_release(). Plus, it matches the documented locking rules for @to_proc. Cc: stable Fixes: 7a4408c6bd3e ("binder: make sure accesses to proc/thread are safe") Reviewed-by: Alice Ryhl Signed-off-by: Carlos Llamas Link: https://patch.msgid.link/20260619185233.2194678-1-cmllamas@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index c48c22264266..013e2bfab070 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -1658,7 +1658,11 @@ static void binder_txn_latency_free(struct binder_transaction *t) static void binder_free_transaction(struct binder_transaction *t) { - struct binder_proc *target_proc = t->to_proc; + struct binder_proc *target_proc; + + spin_lock(&t->lock); + target_proc = t->to_proc; + spin_unlock(&t->lock); if (target_proc) { binder_inner_proc_lock(target_proc); From f223d27a546c1e1f48d38fd67760e78f068fe8c4 Mon Sep 17 00:00:00 2001 From: Carlos Llamas Date: Fri, 19 Jun 2026 18:52:31 +0000 Subject: [PATCH 0454/1054] binder: fix UAF in binder_free_transaction() In binder_free_transaction(), the t->to_proc is read under the t->lock. However, once the t->lock is dropped, the to_proc can die in parallel. This leads to a use-after-free error when we attempt to acquire its inner lock right afterwards: ================================================================== BUG: KASAN: slab-use-after-free in _raw_spin_lock+0xe4/0x1a0 Write of size 4 at addr ffff00001125da70 by task B/672 CPU: 20 UID: 0 PID: 672 Comm: B Not tainted 7.1.0-rc6-00284-g8e65320d91cd #4 PREEMPT Hardware name: linux,dummy-virt (DT) Call trace: _raw_spin_lock+0xe4/0x1a0 binder_free_transaction+0x8c/0x320 binder_send_failed_reply+0x21c/0x2f8 binder_thread_release+0x488/0x7e0 binder_ioctl+0x12c0/0x29a0 [...] Allocated by task 675: __kmalloc_cache_noprof+0x174/0x444 binder_open+0x118/0xb70 do_dentry_open+0x374/0x1040 vfs_open+0x58/0x3bc [...] Freed by task 212: __kasan_slab_free+0x58/0x80 kfree+0x1a0/0x4a4 binder_proc_dec_tmpref+0x32c/0x5e0 binder_deferred_func+0xc48/0x104c process_one_work+0x53c/0xbc0 [...] ================================================================== To prevent this, pin the target thread (t->to_thread) to guarantee the target process remains alive. Undelivered transactions without a target thread are already safe, as the target process can only be the current context in those paths. Cc: stable Reported-by: Alice Ryhl Closes: https://lore.kernel.org/all/aikJKVuny_eOivwN@google.com/ Fixes: a370003cc301 ("binder: fix possible UAF when freeing buffer") Signed-off-by: Carlos Llamas Reviewed-by: Alice Ryhl Link: https://patch.msgid.link/20260619185233.2194678-2-cmllamas@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 013e2bfab070..8f2ef1bd539f 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -1658,10 +1658,19 @@ static void binder_txn_latency_free(struct binder_transaction *t) static void binder_free_transaction(struct binder_transaction *t) { + struct binder_thread *target_thread; struct binder_proc *target_proc; spin_lock(&t->lock); target_proc = t->to_proc; + target_thread = t->to_thread; + /* + * Pin target_thread to keep target_proc alive. Undelivered + * transactions with !target_thread are safe, as target_proc + * can only be the current context there. + */ + if (target_thread) + atomic_inc(&target_thread->tmp_ref); spin_unlock(&t->lock); if (target_proc) { @@ -1676,6 +1685,10 @@ static void binder_free_transaction(struct binder_transaction *t) t->buffer->transaction = NULL; binder_inner_proc_unlock(target_proc); } + + if (target_thread) + binder_thread_dec_tmpref(target_thread); + if (trace_binder_txn_latency_free_enabled()) binder_txn_latency_free(t); /* From 803c8a9502e9b97cd6ae937618ef4a8fd6274343 Mon Sep 17 00:00:00 2001 From: Hyunwoo Kim Date: Sun, 31 May 2026 22:29:24 +0900 Subject: [PATCH 0455/1054] rust_binder: use a u64 stride when cleaning up the offsets array Allocation's Drop walks the offsets array (binder_size_t = u64 entries), cleaning up the objects, but it used usize instead of u64 for both the stride and the per-entry read. On 64-bit kernels (usize == u64) this is harmless, but on 32-bit kernels it walks the 8-byte entries in 4-byte steps, iterating an N-entry array 2N times, and reads the always-zero high word as offset 0, cleaning up the object at offset 0 N extra times. As a result the referenced node or handle ends up with a lower reference count than it actually has (a refcount over-decrement), and binder's reference accounting is corrupted; for example, the owner can be notified of a strong reference release (BR_RELEASE) even though references still remain. Change the stride to u64, and read each entry as a u64, narrowing it to usize with try_into(). On 32-bit ARM, when this over-decrement would drive a count below zero, the driver's existing refcount guard refuses it and fires: rust_binder: Failure: refcount underflow! Cc: stable Fixes: eafedbc7c050 ("rust_binder: add Rust Binder driver") Signed-off-by: Hyunwoo Kim Acked-by: Carlos Llamas Reviewed-by: Alice Ryhl Link: https://patch.msgid.link/ahw3tFhLz9bMMJAO@v4bel Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/allocation.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/android/binder/allocation.rs b/drivers/android/binder/allocation.rs index b7b05e72970a..ea5846e4da16 100644 --- a/drivers/android/binder/allocation.rs +++ b/drivers/android/binder/allocation.rs @@ -259,7 +259,7 @@ fn drop(&mut self) { if let Some(offsets) = info.offsets.clone() { let view = AllocationView::new(self, offsets.start); - for i in offsets.step_by(size_of::()) { + for i in offsets.step_by(size_of::()) { if view.cleanup_object(i).is_err() { pr_warn!("Error cleaning up object at offset {}\n", i) } @@ -420,7 +420,8 @@ pub(crate) fn transfer_binder_object( } fn cleanup_object(&self, index_offset: usize) -> Result { - let offset = self.alloc.read(index_offset)?; + let offset = self.alloc.read::(index_offset)?; + let offset: usize = offset.try_into().map_err(|_| EINVAL)?; let header = self.read::(offset)?; match header.type_ { BINDER_TYPE_WEAK_BINDER | BINDER_TYPE_BINDER => { From 6849cabfd30fb5727cfd31e8241e15801e17ebf9 Mon Sep 17 00:00:00 2001 From: Keshav Verma Date: Thu, 25 Jun 2026 16:09:57 +0530 Subject: [PATCH 0456/1054] rust_binder: reject context manager self-transaction Rust binder resolved handle 0 to the context manager node, but it does not reject the case where the caller owns the same node. The C binder driver rejects transactions from the context-manager process to handle 0 after resolving the target node. Match that behavior in Rust Binder by rejecting handle 0 transactions when the resolved context-manager node is owned by the calling process. This applies to both synchronous and oneway transactions because both paths resolve the target through Process::get_transaction_node(). Cc: stable Fixes: eafedbc7c050 ("rust_binder: add Rust Binder driver") Signed-off-by: Keshav Verma Reviewed-by: Alice Ryhl Link: https://patch.msgid.link/20260625103957.730-1-iganschel@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/process.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/android/binder/process.rs b/drivers/android/binder/process.rs index 96b8440ceac6..ca664fda8e81 100644 --- a/drivers/android/binder/process.rs +++ b/drivers/android/binder/process.rs @@ -900,7 +900,11 @@ pub(crate) fn insert_or_update_handle( pub(crate) fn get_transaction_node(&self, handle: u32) -> BinderResult { // When handle is zero, try to get the context manager. if handle == 0 { - Ok(self.ctx.get_manager_node(true)?) + let node_ref = self.ctx.get_manager_node(true)?; + if core::ptr::eq(self, &*node_ref.node.owner) { + return Err(EINVAL.into()); + } + Ok(node_ref) } else { Ok(self.get_node_from_handle(handle, true)?) } From b28d3d0841a6083f82dfa9fe940afcd748f2a1e2 Mon Sep 17 00:00:00 2001 From: Federico Lombardo Date: Thu, 2 Jul 2026 22:43:47 +0200 Subject: [PATCH 0457/1054] ALSA: hda/realtek: Enable mute LED quirk for HP Laptop 15-dw0xxx HP Laptop 15-dw0xxx with subsystem ID 103c:85f0 needs the ALC236_FIXUP_HP_MUTE_LED_COEFBIT2 quirk to correctly disable and enable the Audio Mute LED. Add the missing quirk entry. Signed-off-by: Federico Lombardo Link: https://patch.msgid.link/20260702204347.187524-1-lombardo.federico.lf@gmail.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index f7700713dc62..c54f55730fdc 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -6992,6 +6992,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x856a, "HP Pavilion 15-cs1xxx", ALC295_FIXUP_HP_PAVILION_MUTE_LED_1B), SND_PCI_QUIRK(0x103c, 0x85c6, "HP Pavilion x360 Convertible 14-dy1xxx", ALC295_FIXUP_HP_MUTE_LED_COEFBIT11), SND_PCI_QUIRK(0x103c, 0x85de, "HP Envy x360 13-ar0xxx", ALC285_FIXUP_HP_ENVY_X360), + SND_PCI_QUIRK(0x103c, 0x85f0, "HP Laptop 15-dw0xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), SND_PCI_QUIRK(0x103c, 0x8603, "HP Omen 17-cb0xxx", ALC285_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x860c, "HP ZBook 17 G6", ALC285_FIXUP_HP_GPIO_AMP_INIT), SND_PCI_QUIRK(0x103c, 0x860f, "HP ZBook 15 G6", ALC285_FIXUP_HP_GPIO_AMP_INIT), From bc4a9828897871ff3e5a1f8a1d346decbf4ee95e Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Fri, 3 Jul 2026 11:25:12 +0000 Subject: [PATCH 0458/1054] rust_binder: clear freeze listener on node removal Generally userspace is supposed to explicitly clear freeze listeners before they drop the refcount on the node ref to zero, but there's nothing forcing that. Currently, in this scenario the freeze listener remains in the freeze_listeners rbtree and in the remote node's freeze listener list, even though the ref for which the listener is registered is gone. This could potentially lead to a memory leak due to a refcount cycle. Thus, remove the freeze listener in this scenario. Cc: stable Fixes: eafedbc7c050 ("rust_binder: add Rust Binder driver") Signed-off-by: Alice Ryhl Link: https://patch.msgid.link/20260703-remove-freeze-on-remove-node-v3-1-6e0c4547af46@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/freeze.rs | 11 +++++++++-- drivers/android/binder/node.rs | 10 ++++++---- drivers/android/binder/process.rs | 12 +++++++++++- 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/drivers/android/binder/freeze.rs b/drivers/android/binder/freeze.rs index 53b60035639a..f4df14568b25 100644 --- a/drivers/android/binder/freeze.rs +++ b/drivers/android/binder/freeze.rs @@ -154,10 +154,17 @@ fn debug_print(&self, m: &SeqFile, prefix: &str, _tprefix: &str) -> Result<()> { } impl FreezeListener { - pub(crate) fn on_process_exit(&self, proc: &Arc) { + /// Called when this freeze listener is cleared abnormally. + /// + /// This occurs either because the process exited or because the process dropped its last + /// refcount on the node ref without explicitly removing the freeze listener first. + /// + /// The returned `KVVec` is just a value that should be dropped outside of the lock. + pub(crate) fn on_process_cleanup(&self, proc: &Process) -> KVVec> { if !self.is_clearing { - self.node.remove_freeze_listener(proc); + return self.node.remove_freeze_listener(proc); } + KVVec::new() } } diff --git a/drivers/android/binder/node.rs b/drivers/android/binder/node.rs index 69f757ff7461..c10148e9069f 100644 --- a/drivers/android/binder/node.rs +++ b/drivers/android/binder/node.rs @@ -682,12 +682,13 @@ pub(crate) fn add_freeze_listener( } } - pub(crate) fn remove_freeze_listener(&self, p: &Arc) { - let _unused_capacity; + pub(crate) fn remove_freeze_listener(&self, p: &Process) -> KVVec> { let mut guard = self.owner.inner.lock(); let inner = self.inner.access_mut(&mut guard); let len = inner.freeze_list.len(); - inner.freeze_list.retain(|proc| !Arc::ptr_eq(proc, p)); + inner + .freeze_list + .retain(|proc| !core::ptr::eq::(&**proc, p)); if len == inner.freeze_list.len() { pr_warn!( "Could not remove freeze listener for {}\n", @@ -695,8 +696,9 @@ pub(crate) fn remove_freeze_listener(&self, p: &Arc) { ); } if inner.freeze_list.is_empty() { - _unused_capacity = mem::take(&mut inner.freeze_list); + return mem::take(&mut inner.freeze_list); } + KVVec::new() } pub(crate) fn freeze_list<'a>(&'a self, guard: &'a ProcessInner) -> &'a [Arc] { diff --git a/drivers/android/binder/process.rs b/drivers/android/binder/process.rs index ca664fda8e81..cdd1a9079726 100644 --- a/drivers/android/binder/process.rs +++ b/drivers/android/binder/process.rs @@ -946,6 +946,8 @@ pub(crate) fn update_ref( // To preserve original binder behaviour, we only fail requests where the manager tries to // increment references on itself. + let _to_free_freeze_listener; + let _to_free_freeze_listener_cleanup; let mut refs = self.node_refs.lock(); if let Some(info) = refs.by_handle.get_mut(&handle) { if info.node_ref().update(inc, strong) { @@ -961,6 +963,14 @@ pub(crate) fn update_ref( unsafe { info.node_ref2().node.remove_node_info(info) }; let id = info.node_ref().node.global_id(); + + if let Some(freeze) = *info.freeze() { + if let Some(fl) = refs.freeze_listeners.remove(&freeze) { + _to_free_freeze_listener_cleanup = fl.on_process_cleanup(&self); + _to_free_freeze_listener = fl; + } + } + refs.by_handle.remove(&handle); refs.by_node.remove(&id); refs.handle_is_present.release_id(handle as usize); @@ -1384,7 +1394,7 @@ fn deferred_release(self: Arc) { // Clean up freeze listeners. let freeze_listeners = take(&mut self.node_refs.lock().freeze_listeners); for listener in freeze_listeners.values() { - listener.on_process_exit(&self); + listener.on_process_cleanup(&self); } drop(freeze_listeners); From 77e43bcb7ec177e293a5c3f1b91a2c5aebfb6c68 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 1 Jul 2026 07:44:17 +0200 Subject: [PATCH 0459/1054] netfilter: nf_nat_sip: reload possible stale data pointer quoting sashiko: ------------------------------------------------------------------------ [..] noticed a potential memory bug and header corruption involving the SIP NAT helper. In net/netfilter/nf_nat_sip.c:nf_nat_sip(): if (skb_ensure_writable(skb, skb->len)) { nf_ct_helper_log(skb, ct, "cannot mangle packet"); return NF_DROP; } uh = (void *)skb->data + protoff; uh->dest = ct_sip_info->forced_dport; if (!nf_nat_mangle_udp_packet(skb, ct, ctinfo, protoff, 0, 0, NULL, 0)) { If a cloned or fragmented SKB is reallocated by skb_ensure_writable(), the old data buffer is freed. However, nf_nat_sip() fails to update *dptr to point to the new buffer. It also appears to use nf_nat_mangle_udp_packet() on what could be a TCP packet, which would overwrite the sequence number with a checksum update. ------------------------------------------------------------------------ nf_conntrack_sip linerizes skbs, hence no fragmented skb can be seen. But clones are possible, so rebuild dptr. Disable nf_nat_mangle_udp_packet() branch for TCP streams. It doesn't look like this can ever happen, else we should have received bug reports about this, so just check the conntrack is UDP and drop otherwise. The calling conntrack_sip set ->forced_dport for SIP_HDR_VIA_UDP messages, so I don't think this is ever expected to be true for a TCP stream. Fixes: 7266507d8999 ("netfilter: nf_ct_sip: support Cisco 7941/7945 IP phones") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-sonnet-4-6 Signed-off-by: Florian Westphal --- net/netfilter/nf_nat_sip.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/net/netfilter/nf_nat_sip.c b/net/netfilter/nf_nat_sip.c index 67c04d8143ab..aea02f6aff09 100644 --- a/net/netfilter/nf_nat_sip.c +++ b/net/netfilter/nf_nat_sip.c @@ -289,13 +289,24 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff, /* Mangle destination port for Cisco phones, then fix up checksums */ if (dir == IP_CT_DIR_REPLY && ct_sip_info->forced_dport) { + int doff = *dptr - (const char *)skb->data; struct udphdr *uh; + if (doff <= 0) { + DEBUG_NET_WARN_ON_ONCE(1); + return NF_DROP; + } + + /* ct_sip_info->forced_dport only expected with UDP */ + if (nf_ct_protonum(ct) != IPPROTO_UDP) + return NF_DROP; + if (skb_ensure_writable(skb, skb->len)) { nf_ct_helper_log(skb, ct, "cannot mangle packet"); return NF_DROP; } + *dptr = skb->data + doff; uh = (void *)skb->data + protoff; uh->dest = ct_sip_info->forced_dport; From 64cdf7d30ac18e43df6c48004435febb965809a8 Mon Sep 17 00:00:00 2001 From: Wyatt Feng Date: Sun, 28 Jun 2026 16:05:54 +0800 Subject: [PATCH 0460/1054] netfilter: xt_u32: reject invalid shift counts u32_match_it() executes rule-supplied shift operands on a 32-bit value. A malformed u32 rule can provide a shift count of 32 or more, triggering an undefined shift out-of-bounds during packet evaluation. Validate XT_U32_LEFTSH and XT_U32_RIGHTSH operands in u32_mt_checkentry() and reject malformed rules before they reach the packet path. Fixes: 1b50b8a371e9 ("[NETFILTER]: Add u32 match") 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: Florian Westphal --- net/netfilter/xt_u32.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/net/netfilter/xt_u32.c b/net/netfilter/xt_u32.c index 117d4615d668..ec1a21e3b6e2 100644 --- a/net/netfilter/xt_u32.c +++ b/net/netfilter/xt_u32.c @@ -100,7 +100,7 @@ static int u32_mt_checkentry(const struct xt_mtchk_param *par) { const struct xt_u32 *data = par->matchinfo; const struct xt_u32_test *ct; - unsigned int i; + unsigned int i, j; if (data->ntests > ARRAY_SIZE(data->tests)) return -EINVAL; @@ -111,6 +111,16 @@ static int u32_mt_checkentry(const struct xt_mtchk_param *par) if (ct->nnums > ARRAY_SIZE(ct->location) || ct->nvalues > ARRAY_SIZE(ct->value)) return -EINVAL; + + for (j = 1; j < ct->nnums; ++j) { + switch (ct->location[j].nextop) { + case XT_U32_LEFTSH: + case XT_U32_RIGHTSH: + if (ct->location[j].number >= 32) + return -EINVAL; + break; + } + } } return 0; From 444853cd438201007da5359821adcc2995655ab1 Mon Sep 17 00:00:00 2001 From: Feng Wu Date: Thu, 25 Jun 2026 08:44:25 +0000 Subject: [PATCH 0461/1054] netfilter: xt_rateest: fix u64 truncation in xt_rateest_mt() On links faster than ~34 Gbps, where byte rate may exceed 2^32-1 (~ 4.3 GBps), the comparison result becomes incorrect because the truncated value no longer reflects the actual estimator rate. Fix by changing the local variables to u64. Fixes: 1c0d32fde5bd ("net_sched: gen_estimator: complete rewrite of rate estimators") Signed-off-by: Feng Wu Signed-off-by: Florian Westphal --- net/netfilter/xt_rateest.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/xt_rateest.c b/net/netfilter/xt_rateest.c index b1d736c15fcb..7c05b6342578 100644 --- a/net/netfilter/xt_rateest.c +++ b/net/netfilter/xt_rateest.c @@ -16,7 +16,7 @@ xt_rateest_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct xt_rateest_match_info *info = par->matchinfo; struct gnet_stats_rate_est64 sample = {0}; - u_int32_t bps1, bps2, pps1, pps2; + u64 bps1, bps2, pps1, pps2; bool ret = true; gen_estimator_read(&info->est1->rate_est, &sample); From 278296b69fae5dd951599692cd481bae4995215c Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Wed, 1 Jul 2026 12:46:57 +0200 Subject: [PATCH 0462/1054] netfilter: nfnetlink_cthelper: cap to maximum number of expectation per master on updates Really cap it to NF_CT_EXPECT_MAX_CNT (255) on updates. The commit ("netfilter: nfnetlink_cthelper: cap to maximum number of expectation per master") only covers creation of helpers, not updates. Fixes: 397c8300972f ("netfilter: nf_conntrack_helper: cap maximum number of expectation at helper registration") Signed-off-by: Pablo Neira Ayuso Signed-off-by: Florian Westphal --- net/netfilter/nfnetlink_cthelper.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/netfilter/nfnetlink_cthelper.c b/net/netfilter/nfnetlink_cthelper.c index 2cbcca9110db..f062ac210343 100644 --- a/net/netfilter/nfnetlink_cthelper.c +++ b/net/netfilter/nfnetlink_cthelper.c @@ -316,6 +316,8 @@ nfnl_cthelper_update_policy_one(const struct nf_conntrack_expect_policy *policy, new_policy->max_expected = ntohl(nla_get_be32(tb[NFCTH_POLICY_EXPECT_MAX])); + if (!new_policy->max_expected) + new_policy->max_expected = NF_CT_EXPECT_MAX_CNT; if (new_policy->max_expected > NF_CT_EXPECT_MAX_CNT) return -EINVAL; From 43ccc20b5a733226417832cf16ef45322e594990 Mon Sep 17 00:00:00 2001 From: Zhixing Chen Date: Wed, 1 Jul 2026 18:09:30 +0800 Subject: [PATCH 0463/1054] netfilter: ip6tables: mark malformed IPv6 extension headers for hotdrop The ah, hbh and rt matches check that the fixed extension header is present, then use the header length field to derive the advertised extension header length for matching. For the ah match, add the missing advertised-length check. For hbh and rt, update the existing advertised-length checks. In all three cases, set hotdrop to true before returning false when the advertised extension header length exceeds the available skb data. Returning false treats the packet as a rule mismatch. Set hotdrop to true and drop malformed packets so they cannot bypass rules intended to drop packets with these IPv6 extension headers. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Zhixing Chen Signed-off-by: Florian Westphal --- net/ipv6/netfilter/ip6t_ah.c | 5 +++++ net/ipv6/netfilter/ip6t_hbh.c | 1 + net/ipv6/netfilter/ip6t_rt.c | 3 ++- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/net/ipv6/netfilter/ip6t_ah.c b/net/ipv6/netfilter/ip6t_ah.c index 70da2f2ce064..1258783ed876 100644 --- a/net/ipv6/netfilter/ip6t_ah.c +++ b/net/ipv6/netfilter/ip6t_ah.c @@ -56,6 +56,11 @@ static bool ah_mt6(const struct sk_buff *skb, struct xt_action_param *par) } hdrlen = ipv6_authlen(ah); + if (skb->len - ptr < hdrlen) { + /* Packet smaller than its length field */ + par->hotdrop = true; + return false; + } pr_debug("IPv6 AH LEN %u %u ", hdrlen, ah->hdrlen); pr_debug("RES %04X ", ah->reserved); diff --git a/net/ipv6/netfilter/ip6t_hbh.c b/net/ipv6/netfilter/ip6t_hbh.c index 450dd53846a2..6d1a5d2026a6 100644 --- a/net/ipv6/netfilter/ip6t_hbh.c +++ b/net/ipv6/netfilter/ip6t_hbh.c @@ -75,6 +75,7 @@ hbh_mt6(const struct sk_buff *skb, struct xt_action_param *par) hdrlen = ipv6_optlen(oh); if (skb->len - ptr < hdrlen) { /* Packet smaller than it's length field */ + par->hotdrop = true; return false; } diff --git a/net/ipv6/netfilter/ip6t_rt.c b/net/ipv6/netfilter/ip6t_rt.c index 5561bd9cea81..278b52752f36 100644 --- a/net/ipv6/netfilter/ip6t_rt.c +++ b/net/ipv6/netfilter/ip6t_rt.c @@ -56,7 +56,8 @@ static bool rt_mt6(const struct sk_buff *skb, struct xt_action_param *par) hdrlen = ipv6_optlen(rh); if (skb->len - ptr < hdrlen) { - /* Pcket smaller than its length field */ + /* Packet smaller than its length field */ + par->hotdrop = true; return false; } From d63611cbe8af99dd61b118ee6e5b5e3e518250b2 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 2 Jul 2026 14:33:09 +0200 Subject: [PATCH 0464/1054] netfilter: nft_set_rbtree: get command skips end element with open interval The get command on intervals provide partial matches such as subranges for usability reasons. However, an open interval has no closing end element. If the closing element matches within the range of the open internal, ie. its closest match is the start element of the open range, then, return 0 but offer no matching element to userspace through netlink as a special case. Userspace provides at least a matching start element in this case and the closing end element matching the open interal is ignored. Another possibility is to report the matching start element of the open interval for this end interval. However, this results in duplicated matching being listed in userspace because userspace does not expect a start element as response to a end element. Fixes: 2aa34191f06f ("netfilter: nft_set_rbtree: use binary search array in get command") Reported-by: Melbin K Mathew Signed-off-by: Pablo Neira Ayuso Signed-off-by: Florian Westphal --- net/netfilter/nf_tables_api.c | 3 +++ net/netfilter/nft_set_rbtree.c | 8 ++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 4884f7f7aaee..a9eaf9455c77 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -6563,6 +6563,9 @@ static int nft_get_set_elem(struct nft_ctx *ctx, const struct nft_set *set, if (err < 0) return err; + if (!elem.priv) + return 0; + err = -ENOMEM; skb = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC); if (skb == NULL) diff --git a/net/netfilter/nft_set_rbtree.c b/net/netfilter/nft_set_rbtree.c index 018bbb6df4ce..6222e9bb57bc 100644 --- a/net/netfilter/nft_set_rbtree.c +++ b/net/netfilter/nft_set_rbtree.c @@ -184,10 +184,14 @@ nft_rbtree_get(const struct net *net, const struct nft_set *set, if (!interval || nft_set_elem_expired(interval->from)) return ERR_PTR(-ENOENT); - if (flags & NFT_SET_ELEM_INTERVAL_END) + if (flags & NFT_SET_ELEM_INTERVAL_END) { + if (!interval->to) + return NULL; + rbe = container_of(interval->to, struct nft_rbtree_elem, ext); - else + } else { rbe = container_of(interval->from, struct nft_rbtree_elem, ext); + } return &rbe->priv; } From 6b335af0d0d1ff44ac579d106953bf19299e5233 Mon Sep 17 00:00:00 2001 From: Yizhou Zhao Date: Thu, 2 Jul 2026 15:34:28 +0800 Subject: [PATCH 0465/1054] ipvs: fix PMTU for GUE/GRE tunnel ICMP errors When an ICMP Fragmentation Needed error is received for a tunneled IPVS connection, ip_vs_in_icmp() recomputes the MTU that the original packet can use by subtracting the tunnel overhead from the reported next-hop MTU. The current code always subtracts sizeof(struct iphdr), which is only the IPIP overhead. For GUE and GRE tunnels, ipvs_udp_decap() and ipvs_gre_decap() already compute the additional tunnel header length, but that value is scoped to the decapsulation block and is lost before the ICMP_FRAG_NEEDED handling. As a result, the ICMP error sent back to the client advertises an MTU that is too large, so PMTUD can fail to converge for GUE/GRE-tunneled real servers. With a reported next-hop MTU of 1400, a GUE tunnel currently returns 1380 to the client. The correct value is 1368: 1400 - sizeof(struct iphdr) - sizeof(struct udphdr) - sizeof(struct guehdr) Hoist the tunnel header length into the main ip_vs_in_icmp() scope and subtract sizeof(struct iphdr) + ulen in the Fragmentation Needed path. The IPIP path keeps ulen as 0, so its existing 1400 - 20 = 1380 result is unchanged. Fixes: 508f744c0de3 ("ipvs: strip udp tunnel headers from icmp errors") Cc: stable@vger.kernel.org Reported-by: Yizhou Zhao Reported-by: Yuxiang Yang Reported-by: Ao Wang Reported-by: Xuewei Feng Reported-by: Qi Li Reported-by: Ke Xu Assisted-by: Claude-Code:GLM-5.2 Signed-off-by: Yizhou Zhao Acked-by: Julian Anastasov Signed-off-by: Florian Westphal --- net/netfilter/ipvs/ip_vs_core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index d40b404c1bf6..906f2c361676 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -1767,6 +1767,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, bool tunnel, new_cp = false; union nf_inet_addr *raddr; char *outer_proto = "IPIP"; + int ulen = 0; *related = 1; @@ -1831,7 +1832,6 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, /* Error for our tunnel must arrive at LOCAL_IN */ (skb_rtable(skb)->rt_flags & RTCF_LOCAL)) { __u8 iproto; - int ulen; /* Non-first fragment has no UDP/GRE header */ if (unlikely(cih->frag_off & htons(IP_OFFSET))) @@ -1936,8 +1936,8 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, if (dest_dst) mtu = dst_mtu(dest_dst->dst_cache); } - if (mtu > 68 + sizeof(struct iphdr)) - mtu -= sizeof(struct iphdr); + if (mtu > 68 + sizeof(struct iphdr) + ulen) + mtu -= sizeof(struct iphdr) + ulen; info = htonl(mtu); } /* Strip outer IP, ICMP and IPIP/UDP/GRE, go to IP header of From 2975324d164c552b028632f107b567302863b7f6 Mon Sep 17 00:00:00 2001 From: Yizhou Zhao Date: Thu, 2 Jul 2026 19:28:36 +0800 Subject: [PATCH 0466/1054] ipvs: reset full ip_vs_seq structs in ip_vs_conn_new Commit 9a05475cebdd ("ipvs: avoid kmem_cache_zalloc in ip_vs_conn_new") changed ip_vs_conn_new() to allocate an ip_vs_conn object with kmem_cache_alloc(). The function then initializes many fields explicitly, but only resets in_seq.delta and out_seq.delta in the two struct ip_vs_seq members. That leaves init_seq and previous_delta uninitialized. This is normally harmless while the corresponding IP_VS_CONN_F_IN_SEQ or IP_VS_CONN_F_OUT_SEQ flag is clear. For connections learned from a sync message, however, ip_vs_proc_conn() preserves those flags from IP_VS_CONN_F_BACKUP_MASK and passes opt=NULL when the message omits IPVS_OPT_SEQ_DATA. In that case the new connection can be hashed with SEQ flags set but with the rest of in_seq/out_seq still containing stale slab data. When a packet for such a connection is later handled by an IPVS application helper, vs_fix_seq() and vs_fix_ack_seq() use previous_delta and init_seq to rewrite TCP sequence numbers. A malformed sync message can therefore make forwarded packets carry stale slab bytes in their TCP seq/ack numbers, and can also corrupt the forwarded TCP flow. Reset both struct ip_vs_seq members completely before publishing the connection. This matches the existing "reset struct ip_vs_seq" comment and keeps the sequence-adjustment gates inactive unless valid sequence data is installed later. Fixes: 9a05475cebdd ("ipvs: avoid kmem_cache_zalloc in ip_vs_conn_new") Cc: stable@vger.kernel.org Reported-by: Yizhou Zhao Reported-by: Yuxiang Yang Reported-by: Ao Wang Reported-by: Xuewei Feng Reported-by: Qi Li Reported-by: Ke Xu Assisted-by: Claude-Code:GLM-5.2 Signed-off-by: Yizhou Zhao Signed-off-by: Florian Westphal --- net/netfilter/ipvs/ip_vs_conn.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c index cb36641f8d1c..6ed2622363f0 100644 --- a/net/netfilter/ipvs/ip_vs_conn.c +++ b/net/netfilter/ipvs/ip_vs_conn.c @@ -1420,8 +1420,8 @@ ip_vs_conn_new(const struct ip_vs_conn_param *p, int dest_af, cp->app = NULL; cp->app_data = NULL; /* reset struct ip_vs_seq */ - cp->in_seq.delta = 0; - cp->out_seq.delta = 0; + memset(&cp->in_seq, 0, sizeof(cp->in_seq)); + memset(&cp->out_seq, 0, sizeof(cp->out_seq)); if (unlikely(flags & IP_VS_CONN_F_NO_CPORT)) { int af_id = ip_vs_af_index(cp->af); From 1b47026fb4b35bac850ad6e8a4ad7fc018e09ebc Mon Sep 17 00:00:00 2001 From: Wyatt Feng Date: Fri, 3 Jul 2026 13:04:46 +0800 Subject: [PATCH 0467/1054] netfilter: xt_connmark: reject invalid shift parameters Revision 2 of the CONNMARK target accepts user-controlled shift parameters and applies them to 32-bit mark values in connmark_tg_shift(). A shift_bits value of 32 or more triggers an undefined-shift bug when the rule is evaluated. Invalid shift_dir values are also accepted and silently fall back to the left-shift path. Reject invalid revision-2 shift parameters in connmark_tg_check() so malformed rules fail at installation time, before they can reach the packet path. Fixes: 472a73e00757 ("netfilter: xt_conntrack: Support bit-shifting for CONNMARK & MARK targets.") Reported-by: Yuan Tan Reported-by: Zhengchuan Liang Reported-by: Xin Liu Assisted-by: Codex:GPT-5.4 Signed-off-by: Wyatt Feng Reviewed-by: Ren Wei Reviewed-by: Phil Sutter Signed-off-by: Florian Westphal --- net/netfilter/xt_connmark.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/net/netfilter/xt_connmark.c b/net/netfilter/xt_connmark.c index 4277084de2e7..2cf27f7d59b9 100644 --- a/net/netfilter/xt_connmark.c +++ b/net/netfilter/xt_connmark.c @@ -112,6 +112,16 @@ static int connmark_tg_check(const struct xt_tgchk_param *par) return ret; } +static int connmark_tg_check_v2(const struct xt_tgchk_param *par) +{ + const struct xt_connmark_tginfo2 *info = par->targinfo; + + if (info->shift_dir > D_SHIFT_RIGHT || info->shift_bits >= 32) + return -EINVAL; + + return connmark_tg_check(par); +} + static void connmark_tg_destroy(const struct xt_tgdtor_param *par) { nf_ct_netns_put(par->net, par->family); @@ -162,7 +172,7 @@ static struct xt_target connmark_tg_reg[] __read_mostly = { .name = "CONNMARK", .revision = 2, .family = NFPROTO_IPV4, - .checkentry = connmark_tg_check, + .checkentry = connmark_tg_check_v2, .target = connmark_tg_v2, .targetsize = sizeof(struct xt_connmark_tginfo2), .destroy = connmark_tg_destroy, @@ -183,7 +193,7 @@ static struct xt_target connmark_tg_reg[] __read_mostly = { .name = "CONNMARK", .revision = 2, .family = NFPROTO_IPV6, - .checkentry = connmark_tg_check, + .checkentry = connmark_tg_check_v2, .target = connmark_tg_v2, .targetsize = sizeof(struct xt_connmark_tginfo2), .destroy = connmark_tg_destroy, From a0738abd042f7406edd2175a819cf2e66388ed97 Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Sat, 27 Jun 2026 00:02:10 +0200 Subject: [PATCH 0468/1054] platform/x86/amd/pmc: Avoid logging "(null)" for DMI values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dmi_get_system_info(...) can return NULL. Using that as %s arguments of dev_info() would log "(null)" (as part of a message like '... System Vendor: "(null)", Product Name: "(null)" ...'), which may be confusing for users. Use Elvis operator to print "(Unknown)" instead. Fixes: 428b9fd2dce5 ("platform/x86/amd/pmc: Add delay_suspend module parameter") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202606251540.Nr2BtaNu-lkp@intel.com/ Suggested-by: Ilpo Järvinen Signed-off-by: Daniel Gibson Link: https://patch.msgid.link/20260626220210.1761783-2-daniel@gibson.sh Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/amd/pmc/pmc.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/platform/x86/amd/pmc/pmc.c b/drivers/platform/x86/amd/pmc/pmc.c index d50ea62fa2f3..347c3f6c5ae7 100644 --- a/drivers/platform/x86/amd/pmc/pmc.c +++ b/drivers/platform/x86/amd/pmc/pmc.c @@ -735,11 +735,11 @@ static bool amd_pmc_want_suspend_delay(struct amd_pmc_dev *pdev) } else if (delay_suspend == 1) { if (!intermediate_wakeup) dev_info(pdev->dev, "Delaying suspend by 2.5s because delay_suspend=1. If this solves problems on your machine, please report this whole line to: platform-driver-x86@vger.kernel.org so it can be automatically detected as affected in the future. System Vendor: \"%s\" Product Name: \"%s\" Product Family: \"%s\" Board Vendor: \"%s\" Board Name: \"%s\"\n", - dmi_get_system_info(DMI_SYS_VENDOR), - dmi_get_system_info(DMI_PRODUCT_NAME), - dmi_get_system_info(DMI_PRODUCT_FAMILY), - dmi_get_system_info(DMI_BOARD_VENDOR), - dmi_get_system_info(DMI_BOARD_NAME)); + dmi_get_system_info(DMI_SYS_VENDOR) ?: "(Unknown)", + dmi_get_system_info(DMI_PRODUCT_NAME) ?: "(Unknown)", + dmi_get_system_info(DMI_PRODUCT_FAMILY) ?: "(Unknown)", + dmi_get_system_info(DMI_BOARD_VENDOR) ?: "(Unknown)", + dmi_get_system_info(DMI_BOARD_NAME) ?: "(Unknown)"); return true; } return false; From d3666875c75eb1bc8090343fa0d6fc8fb7924356 Mon Sep 17 00:00:00 2001 From: Mingyou Chen Date: Wed, 1 Jul 2026 20:01:39 +0800 Subject: [PATCH 0469/1054] platform/x86: bitland-mifs-wmi: Fix NULL pointer dereference during suspend/resume MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver registers two distinct WMI devices: a control device (BITLAND_WMI_CONTROL) and an event device (BITLAND_WMI_EVENT). During the probe phase, the event device handling path returns early before initializing the platform profile device (data->pp_dev), leaving it NULL. However, the PM sleep operations are registered globally for the WMI driver and are triggered for both devices. When entering suspend, the event device invokes bitland_mifs_wmi_suspend(), which passes the uninitialized data->pp_dev (NULL) into laptop_profile_get(). This leads to a NULL pointer dereference inside dev_get_drvdata(), causing a kernel Oops and halting the suspend sequence. Fix this by adding a validity check for data->pp_dev in both the suspend and resume callbacks, safely skipping profile operations for the event device. Fixes: dc1ec4fa86b2 ("platform/x86: bitland-mifs-wmi: Add new Bitland MIFS WMI driver") Reviewed-by: Armin Wolf Signed-off-by: Mingyou Chen Reviewed-by: Armin Wolf Link: https://patch.msgid.link/20260701120140.430659-1-qby140326@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/bitland-mifs-wmi.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/platform/x86/bitland-mifs-wmi.c b/drivers/platform/x86/bitland-mifs-wmi.c index b0d06a80e89e..3a373184519d 100644 --- a/drivers/platform/x86/bitland-mifs-wmi.c +++ b/drivers/platform/x86/bitland-mifs-wmi.c @@ -300,6 +300,10 @@ static int bitland_mifs_wmi_suspend(struct device *dev) enum platform_profile_option profile; int ret; + /* Skip event device */ + if (!data->pp_dev) + return 0; + ret = laptop_profile_get(data->pp_dev, &profile); if (ret == 0) data->saved_profile = profile; @@ -311,6 +315,10 @@ static int bitland_mifs_wmi_resume(struct device *dev) { struct bitland_mifs_wmi_data *data = dev_get_drvdata(dev); + /* Skip event device */ + if (!data->pp_dev) + return 0; + dev_dbg(dev, "Resuming, restoring profile %d\n", data->saved_profile); return laptop_profile_set(dev, data->saved_profile); } From caa0ecbeff4f7fbf70f22bd8ca598918bffb1b78 Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Fri, 26 Jun 2026 10:13:38 +0800 Subject: [PATCH 0470/1054] mtd: virt-concat: free duplicate generated name Every MTD registration runs mtd_virt_concat_create_join(). Once a virtual concat has already been registered, the function builds the same name again and takes the equal-name branch. That branch skips to the next item without freeing the newly allocated string. Free the temporary name before continuing. Fixes: 43db6366fc2d ("mtd: Add driver for concatenating devices") Cc: stable@vger.kernel.org Signed-off-by: Xu Rao Signed-off-by: Miquel Raynal --- drivers/mtd/mtd_virt_concat.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/mtd_virt_concat.c b/drivers/mtd/mtd_virt_concat.c index c872a8827718..da4277ced4d6 100644 --- a/drivers/mtd/mtd_virt_concat.c +++ b/drivers/mtd/mtd_virt_concat.c @@ -321,8 +321,10 @@ int mtd_virt_concat_create_join(void) if (concat->mtd.name) { ret = memcmp(concat->mtd.name, name, name_sz); - if (ret == 0) + if (ret == 0) { + kfree(name); continue; + } } mtd = mtd_concat_create(concat->subdev, concat->num_subdev, name); if (!mtd) { From 9d4af746af8ce27eefc2338b2feaa1e01f28b6c3 Mon Sep 17 00:00:00 2001 From: Xue Lei Date: Wed, 1 Jul 2026 20:10:42 +0800 Subject: [PATCH 0471/1054] mtd: fix double free and WARN_ON in add_mtd_device() error paths When device_register() or mtd_nvmem_add() fails inside add_mtd_device() for a partition, the error handling triggers mtd_release() via put_device() or device_unregister(). mtd_release() calls release_mtd_partition() which frees the mtd_info structure. However, callers such as mtd_add_partition() and add_mtd_partitions() also call free_partition() in their error paths, resulting in a double free. Additionally, release_mtd_partition() hits WARN_ON(!list_empty( &mtd->part.node)) because the partition node is still linked in the parent's partitions list when the release callback fires from the add_mtd_device() error path. Fix this by overriding dev->type and dev->release before put_device() in the error paths, so that device_release() invokes a no-op function instead of mtd_release(). For the mtd_nvmem_add() failure case, device_unregister() is replaced with device_del() to separate the device removal from the final kobject reference drop, allowing the override to take effect before put_device() is called. The callers' error paths (list_del + free_partition) remain the sole owners of mtd_info lifetime on add_mtd_device() failure, which is the expected contract. The normal partition teardown path is not affected: del_mtd_device() goes through kref_put() -> mtd_device_release() -> device_unregister() with dev->type still set to &mtd_devtype, so mtd_release() -> release_mtd_partition() continues to work correctly for the regular removal case. Reported-by: syzbot+e9c76b56dc05023b8117@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=e9c76b56dc05023b8117 Fixes: 19bfa9ebebb5 ("mtd: use refcount to prevent corruption") Signed-off-by: Xue Lei Signed-off-by: Miquel Raynal --- drivers/mtd/mtdcore.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 576537774628..16629382a787 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -105,6 +105,15 @@ static void mtd_release(struct device *dev) device_destroy(&mtd_class, index + 1); } +/* + * No-op device release used in add_mtd_device() error paths. + * Prevents mtd_release() from being called via device_release(), + * which would free the mtd_info that the caller still manages. + */ +static void mtd_dev_release_nop(struct device *dev) +{ +} + static void mtd_device_release(struct kref *kref) { struct mtd_info *mtd = container_of(kref, struct mtd_info, refcnt); @@ -799,10 +808,8 @@ int add_mtd_device(struct mtd_info *mtd) mtd_check_of_node(mtd); of_node_get(mtd_get_of_node(mtd)); error = device_register(&mtd->dev); - if (error) { - put_device(&mtd->dev); + if (error) goto fail_added; - } /* Add the nvmem provider */ error = mtd_nvmem_add(mtd); @@ -840,8 +847,16 @@ int add_mtd_device(struct mtd_info *mtd) return 0; fail_nvmem_add: - device_unregister(&mtd->dev); + device_del(&mtd->dev); fail_added: + /* + * Clear type and set nop release to prevent mtd_release() -> + * release_mtd_partition() -> free_partition() from freeing mtd. + * The caller handles cleanup on failure. + */ + mtd->dev.type = NULL; + mtd->dev.release = mtd_dev_release_nop; + put_device(&mtd->dev); of_node_put(mtd_get_of_node(mtd)); fail_devname: idr_remove(&mtd_idr, i); From dbf590b662695b16fbf5917ef129697be4410ea9 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Fri, 3 Jul 2026 15:37:59 +0800 Subject: [PATCH 0472/1054] mtd: rawnand: lpc32xx_mlc: fail DMA transfers on timeout lpc32xx_xmit_dma() starts a DMA transfer and waits up to one second for its completion, but it ignores the wait result and returns success after unmapping the buffer. A timed out read can therefore return success with incomplete data, and a timed out write can continue the NAND operation without proof that the DMA payload reached the controller. Terminate the DMA channel on timeout, unmap the scatterlist through the existing cleanup path, and return -ETIMEDOUT to the NAND read/write callers. Initialize the shared cleanup-path result before using it for dmaengine_prep_slave_sg() failures. Fixes: 70f7cb78ec53 ("mtd: add LPC32xx MLC NAND driver") Cc: stable@vger.kernel.org Reviewed-by: Vladimir Zapolskiy Signed-off-by: Pengpeng Hou Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/lpc32xx_mlc.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/raw/lpc32xx_mlc.c b/drivers/mtd/nand/raw/lpc32xx_mlc.c index 19b13ae536d4..8f6a89d9ba83 100644 --- a/drivers/mtd/nand/raw/lpc32xx_mlc.c +++ b/drivers/mtd/nand/raw/lpc32xx_mlc.c @@ -396,6 +396,7 @@ static int lpc32xx_xmit_dma(struct mtd_info *mtd, void *mem, int len, struct lpc32xx_nand_host *host = nand_get_controller_data(chip); struct dma_async_tx_descriptor *desc; int flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT; + unsigned long time_left; int res; sg_init_one(&host->sgl, mem, len); @@ -410,6 +411,7 @@ static int lpc32xx_xmit_dma(struct mtd_info *mtd, void *mem, int len, flags); if (!desc) { dev_err(mtd->dev.parent, "Failed to prepare slave sg\n"); + res = -ENXIO; goto out1; } @@ -420,7 +422,13 @@ static int lpc32xx_xmit_dma(struct mtd_info *mtd, void *mem, int len, dmaengine_submit(desc); dma_async_issue_pending(host->dma_chan); - wait_for_completion_timeout(&host->comp_dma, msecs_to_jiffies(1000)); + time_left = wait_for_completion_timeout(&host->comp_dma, + msecs_to_jiffies(1000)); + if (!time_left) { + dmaengine_terminate_sync(host->dma_chan); + res = -ETIMEDOUT; + goto out1; + } dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1, DMA_BIDIRECTIONAL); @@ -428,7 +436,7 @@ static int lpc32xx_xmit_dma(struct mtd_info *mtd, void *mem, int len, out1: dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1, DMA_BIDIRECTIONAL); - return -ENXIO; + return res; } static int lpc32xx_read_page(struct nand_chip *chip, uint8_t *buf, From 17a8ce84964f243c8f89dc7353ac7e8d3137bc74 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Fri, 3 Jul 2026 15:39:43 +0800 Subject: [PATCH 0473/1054] mtd: rawnand: lpc32xx_slc: fail DMA transfer on completion timeout lpc32xx_xmit_dma() waits for the DMA completion callback but ignores wait_for_completion_timeout(). A timed out DMA transfer is therefore unmapped and reported as successful to the NAND read/write path. Return -ETIMEDOUT when the completion wait expires. Terminate the DMA channel before unmapping the scatterlist so the timed out transfer cannot continue to access the buffer after the error is returned. Fixes: 2944a44da09e ("mtd: add LPC32xx SLC NAND driver") Cc: stable@vger.kernel.org Reviewed-by: Vladimir Zapolskiy Signed-off-by: Pengpeng Hou Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/lpc32xx_slc.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/raw/lpc32xx_slc.c b/drivers/mtd/nand/raw/lpc32xx_slc.c index 3ca30e7dce33..10c8080207f4 100644 --- a/drivers/mtd/nand/raw/lpc32xx_slc.c +++ b/drivers/mtd/nand/raw/lpc32xx_slc.c @@ -430,6 +430,7 @@ static int lpc32xx_xmit_dma(struct mtd_info *mtd, dma_addr_t dma, struct dma_async_tx_descriptor *desc; int flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT; int res; + unsigned long time_left; host->dma_slave_config.direction = dir; host->dma_slave_config.src_addr = dma; @@ -467,12 +468,19 @@ static int lpc32xx_xmit_dma(struct mtd_info *mtd, dma_addr_t dma, dmaengine_submit(desc); dma_async_issue_pending(host->dma_chan); - wait_for_completion_timeout(&host->comp, msecs_to_jiffies(1000)); + time_left = wait_for_completion_timeout(&host->comp, + msecs_to_jiffies(1000)); + if (!time_left) { + dmaengine_terminate_sync(host->dma_chan); + res = -ETIMEDOUT; + } else { + res = 0; + } dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1, DMA_BIDIRECTIONAL); - return 0; + return res; out1: dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1, DMA_BIDIRECTIONAL); From d322e40f4edf92bf0ca329e5aa4ae1c0316feb38 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Fri, 3 Jul 2026 15:40:52 +0800 Subject: [PATCH 0474/1054] mtd: mchp23k256: use SPI match data for chip caps The driver stores chip capacity information in both the OF match table and the SPI id table. Probe currently uses of_device_get_match_data(), so a non-OF SPI modalias match falls back to mchp23k256_caps even when the SPI id table selected a different part. Use spi_get_device_match_data() so SPI id-table driver_data is consumed when OF match data is absent. This keeps the existing default fallback while avoiding the wrong MTD geometry for id-table-only matches. Fixes: 4379075a870b ("mtd: mchp23k256: Add support for mchp23lcv1024") Cc: stable@vger.kernel.org Signed-off-by: Pengpeng Hou Signed-off-by: Miquel Raynal --- drivers/mtd/devices/mchp23k256.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/devices/mchp23k256.c b/drivers/mtd/devices/mchp23k256.c index cef5f9677d39..66e058df4c32 100644 --- a/drivers/mtd/devices/mchp23k256.c +++ b/drivers/mtd/devices/mchp23k256.c @@ -188,7 +188,7 @@ static int mchp23k256_probe(struct spi_device *spi) data = dev_get_platdata(&spi->dev); - flash->caps = of_device_get_match_data(&spi->dev); + flash->caps = spi_get_device_match_data(spi); if (!flash->caps) flash->caps = &mchp23k256_caps; From f9a13e05a327080c3a1c8165adf9e678fb68fef2 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Fri, 3 Jul 2026 15:42:33 +0800 Subject: [PATCH 0475/1054] mtd: rawnand: fsl_ifc: return errors for failed page reads fsl_ifc_run_command() logs controller timeout and other non-OPC completion states in ctrl->nand_stat. fsl_ifc_read_page() then only increments the ECC failure counter for non-OPC status and still returns max_bitflips, which can be zero. Return -ETIMEDOUT when the command did not complete at all and -EIO for other non-OPC read completions so the NAND core does not treat a failed page read as a clean page. Fixes: 82771882d960 ("NAND Machine support for Integrated Flash Controller") Cc: stable@vger.kernel.org Signed-off-by: Pengpeng Hou Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/fsl_ifc_nand.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/nand/raw/fsl_ifc_nand.c b/drivers/mtd/nand/raw/fsl_ifc_nand.c index fad0334f759d..a88ac2cfaccd 100644 --- a/drivers/mtd/nand/raw/fsl_ifc_nand.c +++ b/drivers/mtd/nand/raw/fsl_ifc_nand.c @@ -684,8 +684,15 @@ static int fsl_ifc_read_page(struct nand_chip *chip, uint8_t *buf, return check_erased_page(chip, buf); } - if (ctrl->nand_stat != IFC_NAND_EVTER_STAT_OPC) + if (!ctrl->nand_stat) { mtd->ecc_stats.failed++; + return -ETIMEDOUT; + } + + if (ctrl->nand_stat != IFC_NAND_EVTER_STAT_OPC) { + mtd->ecc_stats.failed++; + return -EIO; + } return nctrl->max_bitflips; } From d03a19bd6c7f86b99ca8fb61a6ec2345cee1d9d6 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Fri, 3 Jul 2026 15:43:50 +0800 Subject: [PATCH 0476/1054] mtd: onenand: samsung: report DMA completion timeouts The S5PC110 OneNAND DMA helpers have bounded waits for transfer completion. The polling helper falls out of its timeout loop and returns success, and the IRQ helper ignores wait_for_completion_timeout(). Return -ETIMEDOUT when the DMA transfer-done bit or completion does not arrive before the timeout so callers can treat the buffer transfer as failed. Fixes: e23abf4b7743 ("mtd: OneNAND: S5PC110: Implement DMA interrupt method") Cc: stable@vger.kernel.org Signed-off-by: Pengpeng Hou Signed-off-by: Miquel Raynal --- drivers/mtd/nand/onenand/onenand_samsung.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/nand/onenand/onenand_samsung.c b/drivers/mtd/nand/onenand/onenand_samsung.c index 6d6aa709a21f..b7b7758ce4d8 100644 --- a/drivers/mtd/nand/onenand/onenand_samsung.c +++ b/drivers/mtd/nand/onenand/onenand_samsung.c @@ -554,6 +554,9 @@ static int s5pc110_dma_poll(dma_addr_t dst, dma_addr_t src, size_t count, int di } while (!(status & S5PC110_DMA_TRANS_STATUS_TD) && time_before(jiffies, timeout)); + if (!(status & S5PC110_DMA_TRANS_STATUS_TD)) + return -ETIMEDOUT; + writel(S5PC110_DMA_TRANS_CMD_TDC, base + S5PC110_DMA_TRANS_CMD); return 0; @@ -608,7 +611,9 @@ static int s5pc110_dma_irq(dma_addr_t dst, dma_addr_t src, size_t count, int dir writel(S5PC110_DMA_TRANS_CMD_TR, base + S5PC110_DMA_TRANS_CMD); - wait_for_completion_timeout(&onenand->complete, msecs_to_jiffies(20)); + if (!wait_for_completion_timeout(&onenand->complete, + msecs_to_jiffies(20))) + return -ETIMEDOUT; return 0; } From 2b533e775aec580cf60074417f4ca00ac9cf3580 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 3 Jul 2026 13:35:47 +0200 Subject: [PATCH 0477/1054] Revert "mtd: maps: remove uclinux map driver" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The uclinux MTD map driver is still actively used on ColdFire (m68k) platforms. The ColdFire startup code in arch/m68k/coldfire/head.S copies an attached filesystem blob to _bss_stop, and the uclinux driver picks it up from there. Most ColdFire defconfigs enable CONFIG_MTD_UCLINUX, as the driver seems to serve as a replacement for CONFIG_BLK_DEV_INITRD on these non-MMU targets. The driver was removed as part of a series cleaning up AMD Élan specific drivers, but unlike the other three drivers in that series, this driver seemed to not be as Élan specific (nor unused) as initially thought. This reverts commit c584b8a7ad01a334a89732f3c5791ba14e58642b. Reported-by: Greg Ungerer Closes: https://lore.kernel.org/all/b55ac37f-4523-46d0-8e77-3a17d7235ffe@westnet.com.au/ Acked-by: Arnd Bergmann Signed-off-by: Miquel Raynal --- drivers/mtd/maps/Kconfig | 6 ++ drivers/mtd/maps/Makefile | 1 + drivers/mtd/maps/uclinux.c | 118 +++++++++++++++++++++++++++++++++++++ 3 files changed, 125 insertions(+) create mode 100644 drivers/mtd/maps/uclinux.c diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index dce5e67ce3c2..f447902d707e 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -277,6 +277,12 @@ config MTD_PCMCIA_ANONYMOUS If unsure, say N. +config MTD_UCLINUX + bool "Generic uClinux RAM/ROM filesystem support" + depends on (MTD_RAM=y || MTD_ROM=y) && (!MMU || COLDFIRE) + help + Map driver to support image based filesystems for uClinux. + config MTD_PLATRAM tristate "Map driver for platform device RAM (mtd-ram)" select MTD_RAM diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index fbed278157f6..01745eca1f73 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -30,6 +30,7 @@ obj-$(CONFIG_MTD_SUN_UFLASH) += sun_uflash.o obj-$(CONFIG_MTD_SCx200_DOCFLASH)+= scx200_docflash.o obj-$(CONFIG_MTD_SOLUTIONENGINE)+= solutionengine.o obj-$(CONFIG_MTD_PCI) += pci.o +obj-$(CONFIG_MTD_UCLINUX) += uclinux.o obj-$(CONFIG_MTD_SCB2_FLASH) += scb2_flash.o obj-$(CONFIG_MTD_PLATRAM) += plat-ram.o obj-$(CONFIG_MTD_VMU) += vmu-flash.o diff --git a/drivers/mtd/maps/uclinux.c b/drivers/mtd/maps/uclinux.c new file mode 100644 index 000000000000..de4c46318abb --- /dev/null +++ b/drivers/mtd/maps/uclinux.c @@ -0,0 +1,118 @@ +/****************************************************************************/ + +/* + * uclinux.c -- generic memory mapped MTD driver for uclinux + * + * (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com) + * + * License: GPL + */ + +/****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/****************************************************************************/ + +#ifdef CONFIG_MTD_ROM +#define MAP_NAME "rom" +#else +#define MAP_NAME "ram" +#endif + +static struct map_info uclinux_ram_map = { + .name = MAP_NAME, + .size = 0, +}; + +static unsigned long physaddr = -1; +module_param(physaddr, ulong, S_IRUGO); + +static struct mtd_info *uclinux_ram_mtdinfo; + +/****************************************************************************/ + +static const struct mtd_partition uclinux_romfs[] = { + { .name = "ROMfs" } +}; + +#define NUM_PARTITIONS ARRAY_SIZE(uclinux_romfs) + +/****************************************************************************/ + +static int uclinux_point(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, void **virt, resource_size_t *phys) +{ + struct map_info *map = mtd->priv; + *virt = map->virt + from; + if (phys) + *phys = map->phys + from; + *retlen = len; + return(0); +} + +/****************************************************************************/ + +static int __init uclinux_mtd_init(void) +{ + struct mtd_info *mtd; + struct map_info *mapp; + + mapp = &uclinux_ram_map; + + if (physaddr == -1) + mapp->phys = (resource_size_t)__bss_stop; + else + mapp->phys = physaddr; + + if (!mapp->size) + mapp->size = PAGE_ALIGN(ntohl(*((unsigned long *)(mapp->phys + 8)))); + mapp->bankwidth = 4; + + printk("uclinux[mtd]: probe address=0x%x size=0x%x\n", + (int) mapp->phys, (int) mapp->size); + + /* + * The filesystem is guaranteed to be in direct mapped memory. It is + * directly following the kernels own bss region. Following the same + * mechanism used by architectures setting up traditional initrds we + * use phys_to_virt to get the virtual address of its start. + */ + mapp->virt = phys_to_virt(mapp->phys); + + if (mapp->virt == 0) { + printk("uclinux[mtd]: no virtual mapping?\n"); + return(-EIO); + } + + simple_map_init(mapp); + + mtd = do_map_probe("map_" MAP_NAME, mapp); + if (!mtd) { + printk("uclinux[mtd]: failed to find a mapping?\n"); + return(-ENXIO); + } + + mtd->owner = THIS_MODULE; + mtd->_point = uclinux_point; + mtd->priv = mapp; + + uclinux_ram_mtdinfo = mtd; + mtd_device_register(mtd, uclinux_romfs, NUM_PARTITIONS); + + return(0); +} +device_initcall(uclinux_mtd_init); + +/****************************************************************************/ From 6301f6a34ed86fe6f3b7b3211ea069f3677fc559 Mon Sep 17 00:00:00 2001 From: Jamal Hadi Salim Date: Tue, 30 Jun 2026 11:09:22 -0400 Subject: [PATCH 0478/1054] net/sched: sch_teql: move rcu_read_lock()/spin_lock() from _bh variants This is a followup based on sashiko comments [1] on commit e5b811fe7931 ("net/sched: sch_teql: Introduce slaves_lock to avoid race condition and UAF") Use plain rcu_read_lock()/spin_lock() in teql_master_xmit() instead of the _bh variants, since ndo_start_xmit is already invoked with BH disabled by the core stack and the _bh primitives can warn in_hardirq() when xmit is reached through netpoll or a softirq xmit path with hard IRQs disabled. Moves rcu_read_lock() after restart: label + adds rcu_read_unlock() before goto restart (fixes the unbounded RCU hold across retries) [1] https://sashiko.dev/#/patchset/20260628111229.669751-1-jhs%40mojatatu.com Signed-off-by: Jamal Hadi Salim Fixes: e5b811fe7931 ("net/sched: sch_teql: Introduce slaves_lock to avoid race condition and UAF") Link: https://patch.msgid.link/20260630150922.238714-1-jhs@mojatatu.com Signed-off-by: Paolo Abeni --- net/sched/sch_teql.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c index 24ba31f8c828..5c42a29a981c 100644 --- a/net/sched/sch_teql.c +++ b/net/sched/sch_teql.c @@ -311,14 +311,14 @@ static netdev_tx_t teql_master_xmit(struct sk_buff *skb, struct net_device *dev) int subq = skb_get_queue_mapping(skb); struct sk_buff *skb_res = NULL; - rcu_read_lock_bh(); - - start = rcu_dereference_bh(master->slaves); - restart: nores = 0; busy = 0; + rcu_read_lock(); + + start = rcu_dereference(master->slaves); + q = start; if (!q) goto drop; @@ -345,17 +345,17 @@ static netdev_tx_t teql_master_xmit(struct sk_buff *skb, struct net_device *dev) netdev_start_xmit(skb, slave, slave_txq, false) == NETDEV_TX_OK) { __netif_tx_unlock(slave_txq); - spin_lock_bh(&master->slaves_lock); + spin_lock(&master->slaves_lock); if (rcu_dereference_protected(master->slaves, lockdep_is_held(&master->slaves_lock)) == q) rcu_assign_pointer(master->slaves, rcu_dereference_protected(NEXT_SLAVE(q), lockdep_is_held(&master->slaves_lock))); - spin_unlock_bh(&master->slaves_lock); + spin_unlock(&master->slaves_lock); netif_wake_queue(dev); master->tx_packets++; master->tx_bytes += length; - rcu_read_unlock_bh(); + rcu_read_unlock(); return NETDEV_TX_OK; } __netif_tx_unlock(slave_txq); @@ -364,37 +364,38 @@ static netdev_tx_t teql_master_xmit(struct sk_buff *skb, struct net_device *dev) busy = 1; break; case 1: - spin_lock_bh(&master->slaves_lock); + spin_lock(&master->slaves_lock); if (rcu_dereference_protected(master->slaves, lockdep_is_held(&master->slaves_lock)) == q) rcu_assign_pointer(master->slaves, rcu_dereference_protected(NEXT_SLAVE(q), lockdep_is_held(&master->slaves_lock))); - spin_unlock_bh(&master->slaves_lock); - rcu_read_unlock_bh(); + spin_unlock(&master->slaves_lock); + rcu_read_unlock(); return NETDEV_TX_OK; default: nores = 1; break; } __skb_pull(skb, skb_network_offset(skb)); - } while ((q = rcu_dereference_bh(NEXT_SLAVE(q))) != start); + } while ((q = rcu_dereference(NEXT_SLAVE(q))) != start); if (nores && skb_res == NULL) { skb_res = skb; + rcu_read_unlock(); goto restart; } if (busy) { netif_stop_queue(dev); - rcu_read_unlock_bh(); + rcu_read_unlock(); return NETDEV_TX_BUSY; } master->tx_errors++; drop: master->tx_dropped++; - rcu_read_unlock_bh(); + rcu_read_unlock(); dev_kfree_skb(skb); return NETDEV_TX_OK; } From 539dfcf69105d8d3d4d677b71de6e5ede2e6dfa0 Mon Sep 17 00:00:00 2001 From: Yousef Alhouseen Date: Wed, 1 Jul 2026 18:42:22 +0200 Subject: [PATCH 0479/1054] mac802154: remove interfaces with RCU list deletion Queue wake, stop, and disable paths walk local->interfaces under RCU. The bulk hardware teardown path removes entries with list_del(), so an asynchronous transmit completion can follow a poisoned list node in ieee802154_wake_queue(). Use list_del_rcu() as in the single-interface removal path. The following unregister_netdevice() waits for in-flight RCU readers before freeing the netdevice, so no separate grace-period wait is needed. Fixes: 592dfbfc72f5 ("mac820154: move interface unregistration into iface") Reported-by: syzbot+36256deb69a588e9290e@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=36256deb69a588e9290e Cc: stable@vger.kernel.org Signed-off-by: Yousef Alhouseen Reviewed-by: Kuniyuki Iwashima Reviewed-by: Miquel Raynal Link: https://patch.msgid.link/20260701164222.9094-1-alhouseenyousef@gmail.com Signed-off-by: Paolo Abeni --- net/mac802154/iface.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/mac802154/iface.c b/net/mac802154/iface.c index 000be60d9580..b823720630e7 100644 --- a/net/mac802154/iface.c +++ b/net/mac802154/iface.c @@ -703,7 +703,7 @@ void ieee802154_remove_interfaces(struct ieee802154_local *local) mutex_lock(&local->iflist_mtx); list_for_each_entry_safe(sdata, tmp, &local->interfaces, list) { - list_del(&sdata->list); + list_del_rcu(&sdata->list); unregister_netdevice(sdata->dev); } From 0f0e4ae6975c773f7854fc48932a267f6c79088f Mon Sep 17 00:00:00 2001 From: Shay Drory Date: Tue, 30 Jun 2026 14:29:15 +0300 Subject: [PATCH 0480/1054] net/mlx5: LAG, Fix off-by-one in single-FDB error rollback On failure at index i, the reverse cleanup loop in mlx5_lag_create_single_fdb() starts from i, so the failed index itself is rolled back. That can operate on uninitialized state or double-tear-down a rule the add_one path already self-rolled-back. Start the rollback from i - 1 so only successfully-installed entries are undone. Fixes: ddbb5ddc43ad ("net/mlx5: LAG, Refactor lag logic") Signed-off-by: Shay Drory Reviewed-by: Mark Bloch Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260630112917.698313-2-tariqt@nvidia.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/mellanox/mlx5/core/lag/shared_fdb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/shared_fdb.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/shared_fdb.c index 113866494d16..6b4ad3c53f2f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lag/shared_fdb.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/shared_fdb.c @@ -78,7 +78,7 @@ static int mlx5_lag_create_single_fdb_filter(struct mlx5_lag *ldev, u32 filter) } return 0; err: - mlx5_lag_for_each_reverse(j, i, 0, ldev, filter) { + mlx5_lag_for_each_reverse(j, i - 1, 0, ldev, filter) { struct mlx5_eswitch *slave_esw; if (j == master_idx) From d4b85f9a668b9c44216bb78daf4ec1a915cc92d1 Mon Sep 17 00:00:00 2001 From: Shay Drory Date: Tue, 30 Jun 2026 14:29:16 +0300 Subject: [PATCH 0481/1054] net/mlx5: LAG, MPESW, Fix missing complete() on devcom error mlx5_mpesw_work() returned without calling complete() when mlx5_lag_get_devcom_comp() returned NULL. A caller that queued the work and waited on mpesww->comp would block indefinitely. Funnel the early-return path through a new "complete" label so the waiter is always woken. Fixes: b430c1b4f63b ("net/mlx5: Replace global mlx5_intf_lock with HCA devcom component lock") Signed-off-by: Shay Drory Reviewed-by: Mark Bloch Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260630112917.698313-3-tariqt@nvidia.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.c index 50bfb450c71e..abf72026c751 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.c @@ -194,8 +194,10 @@ static void mlx5_mpesw_work(struct work_struct *work) struct mlx5_lag *ldev = mpesww->lag; devcom = mlx5_lag_get_devcom_comp(ldev); - if (!devcom) - return; + if (!devcom) { + mpesww->result = -ENODEV; + goto complete; + } mlx5_devcom_comp_lock(devcom); mlx5_mpesw_sd_devcoms_lock(ldev); @@ -213,6 +215,7 @@ static void mlx5_mpesw_work(struct work_struct *work) mutex_unlock(&ldev->lock); mlx5_mpesw_sd_devcoms_unlock(ldev); mlx5_devcom_comp_unlock(devcom); +complete: complete(&mpesww->comp); } From 7bed4af0ced82948d660205efecd551ef8bc3912 Mon Sep 17 00:00:00 2001 From: Shay Drory Date: Tue, 30 Jun 2026 14:29:17 +0300 Subject: [PATCH 0482/1054] net/mlx5e: TC, skip peer flow cleanup when LAG seq is unavailable mlx5_lag_get_dev_seq() will return error when the peer isn't in the LAG or when no device is marked as master. Result bad memory access and kernel crash[1]. Hence, skip the peer when lookup fails. Note: In case there are peer flows, they are cleaned before LAG cleared the master mark. [1] RIP: 0010:mlx5e_tc_del_fdb_peers_flow+0x3d/0x350 [mlx5_core] Call Trace: mlx5e_tc_clean_fdb_peer_flows+0xc1/0x130 [mlx5_core] mlx5_esw_offloads_unpair+0x3a/0x400 [mlx5_core] mlx5_esw_offloads_devcom_event+0xee/0x360 [mlx5_core] mlx5_devcom_send_event+0x7a/0x140 [mlx5_core] mlx5_esw_offloads_devcom_cleanup+0x2f/0x90 [mlx5_core] mlx5e_tc_esw_cleanup+0x28/0xf0 [mlx5_core] mlx5e_rep_tc_cleanup+0x19/0x30 [mlx5_core] mlx5e_cleanup_uplink_rep_tx+0x36/0x40 [mlx5_core] mlx5e_cleanup_rep_tx+0x55/0x60 [mlx5_core] mlx5e_detach_netdev+0x96/0xf0 [mlx5_core] mlx5e_netdev_change_profile+0x5b/0x120 [mlx5_core] mlx5e_netdev_attach_nic_profile+0x1b/0x30 [mlx5_core] mlx5e_vport_rep_unload+0xdd/0x110 [mlx5_core] __esw_offloads_unload_rep+0x81/0xb0 [mlx5_core] mlx5_eswitch_unregister_vport_reps+0x1d7/0x220 [mlx5_core] mlx5e_rep_remove+0x22/0x30 [mlx5_core] device_release_driver_internal+0x194/0x1f0 bus_remove_device+0xe8/0x1b0 device_del+0x159/0x3c0 mlx5_rescan_drivers_locked+0xbc/0x2d0 [mlx5_core] mlx5_unregister_device+0x54/0x80 [mlx5_core] mlx5_uninit_one+0x73/0x130 [mlx5_core] remove_one+0x78/0xe0 [mlx5_core] pci_device_remove+0x39/0xa0 Fixes: 971b28accc09 ("net/mlx5: LAG, replace mlx5_get_dev_index with LAG sequence number") Signed-off-by: Shay Drory Reviewed-by: Mark Bloch Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260630112917.698313-4-tariqt@nvidia.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/mellanox/mlx5/core/en_tc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index 910492eb51f2..1bc7b9019124 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -5547,6 +5547,9 @@ void mlx5e_tc_clean_fdb_peer_flows(struct mlx5_eswitch *esw) mlx5_devcom_for_each_peer_entry(devcom, peer_esw, pos) { i = mlx5_lag_get_dev_seq(peer_esw->dev); + if (i < 0) + continue; + list_for_each_entry_safe(flow, tmp, &esw->offloads.peer_flows[i], peer[i]) mlx5e_tc_del_fdb_peers_flow(flow); } From 6bb8898f702385d363dc2c513a1efa62807f8068 Mon Sep 17 00:00:00 2001 From: Damon Ding Date: Tue, 23 Jun 2026 10:35:06 +0800 Subject: [PATCH 0483/1054] drm/bridge: analogix_dp: Fix PE/VS value shift mismatch during link training VS/PE values returned by drm_dp_get_adjust_request_voltage() and drm_dp_get_adjust_request_pre_emphasis() are already encoded to their native DPCD register bit positions. However, DPCD_VOLTAGE_SWING_SET / DPCD_PRE_EMPHASIS_SET macros perform an extra internal shift. Feeding the raw offset-bearing values directly leads to overlapping bitfields and invalid lane training configuration, causing link training failures and black screen. Add right shift using DP_TRAIN_*_SHIFT constants to strip the DPCD bit offsets before passing values to the SET macros and subsequent checks. Apply this fix for both clock recovery and adjust training code paths. Reported-by: Vicente Bergas Closes: https://lore.kernel.org/all/CAAMcf8D-d+5n=H44KeKBSqWY42m+o32W+mO-r15VqWNyYhJL7Q@mail.gmail.com/ Fixes: d84b087c7662 ("drm/bridge: analogix_dp: Apply DP helper APIs to get adjusted voltages and pre-emphasises") Signed-off-by: Damon Ding Link: https://lore.kernel.org/all/CAAMcf8D-d+5n=H44KeKBSqWY42m+o32W+mO-r15VqWNyYhJL7Q@mail.gmail.com/ Signed-off-by: Heiko Stuebner Link: https://patch.msgid.link/20260623023506.309858-1-damon.ding@rock-chips.com --- drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c index 8cf6b73bceac..5006ac181b2d 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c @@ -309,7 +309,9 @@ static void analogix_dp_get_adjust_training_lane(struct analogix_dp_device *dp, lane_count = dp->link_train.lane_count; for (lane = 0; lane < lane_count; lane++) { voltage_swing = drm_dp_get_adjust_request_voltage(link_status, lane); + voltage_swing >>= DP_TRAIN_VOLTAGE_SWING_SHIFT; pre_emphasis = drm_dp_get_adjust_request_pre_emphasis(link_status, lane); + pre_emphasis >>= DP_TRAIN_PRE_EMPHASIS_SHIFT; training_lane = DPCD_VOLTAGE_SWING_SET(voltage_swing) | DPCD_PRE_EMPHASIS_SET(pre_emphasis); @@ -355,7 +357,9 @@ static int analogix_dp_process_clock_recovery(struct analogix_dp_device *dp) for (lane = 0; lane < lane_count; lane++) { training_lane = analogix_dp_get_lane_link_training(dp, lane); voltage_swing = drm_dp_get_adjust_request_voltage(link_status, lane); + voltage_swing >>= DP_TRAIN_VOLTAGE_SWING_SHIFT; pre_emphasis = drm_dp_get_adjust_request_pre_emphasis(link_status, lane); + pre_emphasis >>= DP_TRAIN_PRE_EMPHASIS_SHIFT; if (DPCD_VOLTAGE_SWING_GET(training_lane) == voltage_swing && DPCD_PRE_EMPHASIS_GET(training_lane) == pre_emphasis) From 25f6b929c7e379cbea7cb8caa67b49b2d1efae17 Mon Sep 17 00:00:00 2001 From: Feng Liu Date: Tue, 30 Jun 2026 14:51:49 +0300 Subject: [PATCH 0484/1054] net/mlx5e: Fix HV VHCA stats zero-sized buffer allocation mlx5e_hv_vhca_stats_create() is called from mlx5e_nic_enable(), before mlx5e_open(). At that point priv->stats_nch is still zero, because it is only ever incremented in mlx5e_channel_stats_alloc(), which is reached only from mlx5e_open_channel(). mlx5e_hv_vhca_stats_buf_size() therefore returns 0, and kvzalloc(0, GFP_KERNEL) returns ZERO_SIZE_PTR ((void *)16) rather than NULL. The "if (!buf)" guard does not catch this, and mlx5e_hv_vhca_stats_create() completes "successfully" with priv->stats_agent.buf set to ZERO_SIZE_PTR. Once channels are opened (priv->stats_nch > 0) and the hypervisor enables stats reporting, mlx5e_hv_vhca_stats_work() recomputes buf_len using the new non-zero stats_nch and calls memset(buf, 0, buf_len) on ZERO_SIZE_PTR, faulting at address 0x10. Allocate the buffer based on priv->max_nch, which is set in mlx5e_priv_init() and is the upper bound on stats_nch: - Add a separate helper mlx5e_hv_vhca_stats_buf_max_size() that returns sizeof(per_ring_stats) * max(max_nch, stats_nch), and use it for the kvzalloc() in mlx5e_hv_vhca_stats_create(). - Keep mlx5e_hv_vhca_stats_buf_size() (which returns based on stats_nch) for the worker's active payload size, so the wire format (block->rings = stats_nch) and the amount of data filled by mlx5e_hv_vhca_fill_stats() are unchanged. The max(max_nch, stats_nch) guard handles the rare case where mlx5e_attach_netdev() recomputes max_nch downward across a detach/resume cycle while priv->stats_nch persists (mlx5e_detach_netdev does not call mlx5e_priv_cleanup, so stats_nch is only reset when the netdev is destroyed). Without the guard, the worker could compute buf_len from stats_nch and overrun the smaller buffer allocated based on the reduced max_nch. Allocating a non-zero buffer also makes the kvzalloc() failure path in mlx5e_hv_vhca_stats_create() reachable for the first time: it returns early without (re)creating the agent. Clear priv->stats_agent.{agent,buf} in mlx5e_hv_vhca_stats_destroy() after freeing them, so that if a later create() bails out on this path, a subsequent teardown does not double-free the stale agent/buffer left from a previous enable/disable cycle. This mirrors the existing mlx5e pattern of preallocating arrays of size max_nch (e.g. priv->channel_stats) and lazily populating entries up to stats_nch on demand. Fixes: fa691d0c9c08 ("net/mlx5e: Allocate per-channel stats dynamically at first usage") Signed-off-by: Feng Liu Reviewed-by: Eran Ben Elisha Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260630115151.729219-2-tariqt@nvidia.com Signed-off-by: Paolo Abeni --- .../net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c b/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c index 195863b2c013..72f3ca4dd076 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c @@ -54,6 +54,12 @@ static int mlx5e_hv_vhca_stats_buf_size(struct mlx5e_priv *priv) priv->stats_nch); } +static int mlx5e_hv_vhca_stats_buf_max_size(struct mlx5e_priv *priv) +{ + return (sizeof(struct mlx5e_hv_vhca_per_ring_stats) * + max(priv->max_nch, priv->stats_nch)); +} + static void mlx5e_hv_vhca_stats_work(struct work_struct *work) { struct mlx5e_hv_vhca_stats_agent *sagent; @@ -122,7 +128,7 @@ static void mlx5e_hv_vhca_stats_cleanup(struct mlx5_hv_vhca_agent *agent) void mlx5e_hv_vhca_stats_create(struct mlx5e_priv *priv) { - int buf_len = mlx5e_hv_vhca_stats_buf_size(priv); + int buf_len = mlx5e_hv_vhca_stats_buf_max_size(priv); struct mlx5_hv_vhca_agent *agent; priv->stats_agent.buf = kvzalloc(buf_len, GFP_KERNEL); @@ -155,5 +161,7 @@ void mlx5e_hv_vhca_stats_destroy(struct mlx5e_priv *priv) return; mlx5_hv_vhca_agent_destroy(priv->stats_agent.agent); + priv->stats_agent.agent = NULL; kvfree(priv->stats_agent.buf); + priv->stats_agent.buf = NULL; } From 89b25b5f46f488ea3b29b3444864c76944c9075b Mon Sep 17 00:00:00 2001 From: Feng Liu Date: Tue, 30 Jun 2026 14:51:50 +0300 Subject: [PATCH 0485/1054] net/mlx5e: Fix HV VHCA stats agent registration race mlx5e_hv_vhca_stats_create() registers the stats agent through mlx5_hv_vhca_agent_create(). The helper publishes the agent in hv_vhca->agents[type] under agents_lock and immediately schedules an asynchronous control invalidation on the HV VHCA workqueue before returning to mlx5e. The asynchronous invalidation invokes the control agent's invalidate callback, which reads the hypervisor control block and forwards the command to mlx5e_hv_vhca_stats_control(). That callback may either: - call cancel_delayed_work_sync(&priv->stats_agent.work), or - call queue_delayed_work(priv->wq, &sagent->work, sagent->delay). However, the delayed_work and priv->stats_agent.agent are only initialized after mlx5_hv_vhca_agent_create() returns to mlx5e: agent = mlx5_hv_vhca_agent_create(...); /* publish + invalidate */ ... priv->stats_agent.agent = agent; /* too late */ INIT_DELAYED_WORK(&priv->stats_agent.work, ...); /* too late */ If the asynchronous control path runs before the two assignments above, it can: - Operate on an uninitialized delayed_work whose timer.function is NULL. queue_delayed_work() calls add_timer() unconditionally, so when the timer expires the timer softirq invokes a NULL function pointer. - Re-initialize the timer later through INIT_DELAYED_WORK() while the timer is already enqueued in the timer wheel, corrupting the hlist (entry.pprev cleared while the previous bucket node still points at this entry). - When the worker eventually runs, mlx5e_hv_vhca_stats_work() reads sagent->agent (NULL) and dereferences it inside mlx5_hv_vhca_agent_write(). Fix this by: - Initializing priv->stats_agent.work before invoking mlx5_hv_vhca_agent_create(), so the work is always in a valid state when the control callback observes it. - Adding a struct mlx5_hv_vhca_agent **ctx_update out-parameter to mlx5_hv_vhca_agent_create(). The helper writes the agent pointer to *ctx_update before publishing into hv_vhca->agents[] and triggering the agents_update flow, so any callback subsequently invoked from that flow already sees a valid priv->stats_agent.agent. This avoids having the control callback participate in agent initialization. While at it, access priv->stats_agent.agent with READ_ONCE()/WRITE_ONCE() for the cross-CPU access with the worker, and clear priv->stats_agent.buf on the agent_create() failure path. Fixes: cef35af34d6d ("net/mlx5e: Add mlx5e HV VHCA stats agent") Signed-off-by: Feng Liu Reviewed-by: Eran Ben Elisha Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260630115151.729219-3-tariqt@nvidia.com Signed-off-by: Paolo Abeni --- .../mellanox/mlx5/core/en/hv_vhca_stats.c | 21 +++++++++++-------- .../ethernet/mellanox/mlx5/core/lib/hv_vhca.c | 8 +++++-- .../ethernet/mellanox/mlx5/core/lib/hv_vhca.h | 6 ++++-- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c b/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c index 72f3ca4dd076..cdaf77650164 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c @@ -73,7 +73,7 @@ static void mlx5e_hv_vhca_stats_work(struct work_struct *work) sagent = container_of(dwork, struct mlx5e_hv_vhca_stats_agent, work); priv = container_of(sagent, struct mlx5e_priv, stats_agent); buf_len = mlx5e_hv_vhca_stats_buf_size(priv); - agent = sagent->agent; + agent = READ_ONCE(sagent->agent); buf = sagent->buf; memset(buf, 0, buf_len); @@ -135,11 +135,14 @@ void mlx5e_hv_vhca_stats_create(struct mlx5e_priv *priv) if (!priv->stats_agent.buf) return; + INIT_DELAYED_WORK(&priv->stats_agent.work, mlx5e_hv_vhca_stats_work); + agent = mlx5_hv_vhca_agent_create(priv->mdev->hv_vhca, MLX5_HV_VHCA_AGENT_STATS, mlx5e_hv_vhca_stats_control, NULL, mlx5e_hv_vhca_stats_cleanup, - priv); + priv, + &priv->stats_agent.agent); if (IS_ERR_OR_NULL(agent)) { if (IS_ERR(agent)) @@ -148,20 +151,20 @@ void mlx5e_hv_vhca_stats_create(struct mlx5e_priv *priv) agent); kvfree(priv->stats_agent.buf); - return; + priv->stats_agent.buf = NULL; } - - priv->stats_agent.agent = agent; - INIT_DELAYED_WORK(&priv->stats_agent.work, mlx5e_hv_vhca_stats_work); } void mlx5e_hv_vhca_stats_destroy(struct mlx5e_priv *priv) { - if (IS_ERR_OR_NULL(priv->stats_agent.agent)) + struct mlx5_hv_vhca_agent *agent; + + agent = READ_ONCE(priv->stats_agent.agent); + if (IS_ERR_OR_NULL(agent)) return; - mlx5_hv_vhca_agent_destroy(priv->stats_agent.agent); - priv->stats_agent.agent = NULL; + mlx5_hv_vhca_agent_destroy(agent); + WRITE_ONCE(priv->stats_agent.agent, NULL); kvfree(priv->stats_agent.buf); priv->stats_agent.buf = NULL; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.c index d6dc7bce855e..305752dab7bd 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.c @@ -190,7 +190,7 @@ mlx5_hv_vhca_control_agent_create(struct mlx5_hv_vhca *hv_vhca) return mlx5_hv_vhca_agent_create(hv_vhca, MLX5_HV_VHCA_AGENT_CONTROL, NULL, mlx5_hv_vhca_control_agent_invalidate, - NULL, NULL); + NULL, NULL, NULL); } static void mlx5_hv_vhca_control_agent_destroy(struct mlx5_hv_vhca_agent *agent) @@ -256,7 +256,8 @@ mlx5_hv_vhca_agent_create(struct mlx5_hv_vhca *hv_vhca, void (*invalidate)(struct mlx5_hv_vhca_agent*, u64 block_mask), void (*cleaup)(struct mlx5_hv_vhca_agent *agent), - void *priv) + void *priv, + struct mlx5_hv_vhca_agent **ctx_update) { struct mlx5_hv_vhca_agent *agent; @@ -284,6 +285,9 @@ mlx5_hv_vhca_agent_create(struct mlx5_hv_vhca *hv_vhca, agent->invalidate = invalidate; agent->cleanup = cleaup; + if (ctx_update) + WRITE_ONCE(*ctx_update, agent); + mutex_lock(&hv_vhca->agents_lock); hv_vhca->agents[type] = agent; mutex_unlock(&hv_vhca->agents_lock); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.h b/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.h index f240ffe5116c..8b3974cf0ee4 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.h @@ -43,7 +43,8 @@ mlx5_hv_vhca_agent_create(struct mlx5_hv_vhca *hv_vhca, void (*invalidate)(struct mlx5_hv_vhca_agent*, u64 block_mask), void (*cleanup)(struct mlx5_hv_vhca_agent *agent), - void *context); + void *context, + struct mlx5_hv_vhca_agent **ctx_update); void mlx5_hv_vhca_agent_destroy(struct mlx5_hv_vhca_agent *agent); int mlx5_hv_vhca_agent_write(struct mlx5_hv_vhca_agent *agent, @@ -84,7 +85,8 @@ mlx5_hv_vhca_agent_create(struct mlx5_hv_vhca *hv_vhca, void (*invalidate)(struct mlx5_hv_vhca_agent*, u64 block_mask), void (*cleanup)(struct mlx5_hv_vhca_agent *agent), - void *context) + void *context, + struct mlx5_hv_vhca_agent **ctx_update) { return NULL; } From 5a799714e8ca0bce9ea40694f49914cf1adbbaa9 Mon Sep 17 00:00:00 2001 From: Feng Liu Date: Tue, 30 Jun 2026 14:51:51 +0300 Subject: [PATCH 0486/1054] net/mlx5e: Fix publication race for priv->channel_stats[] mlx5e_channel_stats_alloc() publishes a new entry to priv->channel_stats[] and then increments priv->stats_nch as a publication token, but neither store carries any memory barrier: priv->channel_stats[ix] = kvzalloc_node(...); if (!priv->channel_stats[ix]) return -ENOMEM; priv->stats_nch++; Concurrent readers compute the loop bound from priv->stats_nch and then dereference priv->channel_stats[i] using plain accesses, e.g. for (i = 0; i < priv->stats_nch; i++) { struct mlx5e_channel_stats *cs = priv->channel_stats[i]; ... cs->rq.packets ... } On weakly-ordered architectures (ARM, PowerPC, RISC-V) the writes to channel_stats[ix] and stats_nch may become visible to other CPUs out of program order. A reader can observe stats_nch == N while still seeing channel_stats[N-1] == NULL, leading to a NULL pointer dereference in the channel_stats loop. This has been observed in production on BlueField-3 DPUs (arm64), where ovs-vswitchd queries netdev statistics over netlink during NIC bringup, racing mlx5e_open_channel() -> mlx5e_channel_stats_alloc() on another CPU: Unable to handle kernel NULL pointer dereference at virtual address 0x840 Hardware name: BlueField-3 DPU pc : mlx5e_fold_sw_stats64+0x30/0x180 [mlx5_core] Call trace: mlx5e_fold_sw_stats64+0x30/0x180 [mlx5_core] dev_get_stats+0x50/0xc0 ovs_vport_get_stats+0x38/0xac [openvswitch] ovs_vport_cmd_fill_info+0x194/0x290 [openvswitch] ovs_vport_cmd_get+0xbc/0x10c [openvswitch] genl_family_rcv_msg_doit+0xd0/0x160 genl_rcv_msg+0xec/0x1f0 netlink_rcv_skb+0x64/0x130 genl_rcv+0x40/0x60 netlink_unicast+0x2fc/0x370 netlink_sendmsg+0x1dc/0x454 ... __arm64_sys_sendmsg+0x2c/0x40 Add mlx5e_stats_nch_write() and mlx5e_stats_nch_read() helpers in en.h that wrap the smp_store_release()/smp_load_acquire() pair on stats_nch. The release/acquire pair establishes the contract: stats_nch == N => channel_stats[0..N-1] are visible and non-NULL. Publish the stats_nch increment via mlx5e_stats_nch_write() in the writer (mlx5e_channel_stats_alloc()), and read stats_nch via mlx5e_stats_nch_read() in all readers: mlx5e RX/TX queue stats, mlx5e_get_base_stats(), ethtool channels stats, IPoIB stats, the sw_stats fold and the HV VHCA stats agent. Fixes: fa691d0c9c08 ("net/mlx5e: Allocate per-channel stats dynamically at first usage") Signed-off-by: Feng Liu Reviewed-by: Eran Ben Elisha Reviewed-by: Cosmin Ratiu Reviewed-by: Nimrod Oren Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260630115151.729219-4-tariqt@nvidia.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 12 ++++++++++++ .../ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c | 10 ++++++---- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 14 ++++++++------ drivers/net/ethernet/mellanox/mlx5/core/en_stats.c | 9 +++++---- .../net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c | 3 ++- 5 files changed, 33 insertions(+), 15 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 2270e2e550dd..d507289096c2 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -987,6 +987,18 @@ struct mlx5e_priv { struct ethtool_fec_hist_range *fec_ranges; }; +static inline u16 mlx5e_stats_nch_read(const struct mlx5e_priv *priv) +{ + /* Pairs with smp_store_release in mlx5e_stats_nch_write(). */ + return smp_load_acquire(&priv->stats_nch); +} + +static inline void mlx5e_stats_nch_write(struct mlx5e_priv *priv, u16 n) +{ + /* Pairs with smp_load_acquire in mlx5e_stats_nch_read(). */ + smp_store_release(&priv->stats_nch, n); +} + struct mlx5e_dev { struct net_device *netdev; struct devlink_port dl_port; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c b/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c index cdaf77650164..631f802105d5 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c @@ -33,9 +33,10 @@ mlx5e_hv_vhca_fill_ring_stats(struct mlx5e_priv *priv, int ch, static void mlx5e_hv_vhca_fill_stats(struct mlx5e_priv *priv, void *data, int buf_len) { + u16 nch = mlx5e_stats_nch_read(priv); int ch, i = 0; - for (ch = 0; ch < priv->stats_nch; ch++) { + for (ch = 0; ch < nch; ch++) { void *buf = data + i; if (WARN_ON_ONCE(buf + @@ -50,8 +51,9 @@ static void mlx5e_hv_vhca_fill_stats(struct mlx5e_priv *priv, void *data, static int mlx5e_hv_vhca_stats_buf_size(struct mlx5e_priv *priv) { - return (sizeof(struct mlx5e_hv_vhca_per_ring_stats) * - priv->stats_nch); + u16 nch = mlx5e_stats_nch_read(priv); + + return sizeof(struct mlx5e_hv_vhca_per_ring_stats) * nch; } static int mlx5e_hv_vhca_stats_buf_max_size(struct mlx5e_priv *priv) @@ -106,7 +108,7 @@ static void mlx5e_hv_vhca_stats_control(struct mlx5_hv_vhca_agent *agent, sagent = &priv->stats_agent; block->version = MLX5_HV_VHCA_STATS_VERSION; - block->rings = priv->stats_nch; + block->rings = mlx5e_stats_nch_read(priv); if (!block->command) { cancel_delayed_work_sync(&priv->stats_agent.work); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 775f0c6e55c9..aa8610cedaa8 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -2773,7 +2773,7 @@ static int mlx5e_channel_stats_alloc(struct mlx5e_priv *priv, int ix, int cpu) GFP_KERNEL, cpu_to_node(cpu)); if (!priv->channel_stats[ix]) return -ENOMEM; - priv->stats_nch++; + mlx5e_stats_nch_write(priv, priv->stats_nch + 1); return 0; } @@ -4040,9 +4040,10 @@ static int mlx5e_setup_tc(struct net_device *dev, enum tc_setup_type type, void mlx5e_fold_sw_stats64(struct mlx5e_priv *priv, struct rtnl_link_stats64 *s) { + u16 nch = mlx5e_stats_nch_read(priv); int i; - for (i = 0; i < priv->stats_nch; i++) { + for (i = 0; i < nch; i++) { struct mlx5e_channel_stats *channel_stats = priv->channel_stats[i]; struct mlx5e_rq_stats *xskrq_stats = &channel_stats->xskrq; struct mlx5e_rq_stats *rq_stats = &channel_stats->rq; @@ -5488,7 +5489,7 @@ static void mlx5e_get_queue_stats_rx(struct net_device *dev, int i, struct mlx5e_rq_stats *xskrq_stats; struct mlx5e_rq_stats *rq_stats; - if (mlx5e_is_uplink_rep(priv) || !priv->stats_nch) + if (mlx5e_is_uplink_rep(priv) || !mlx5e_stats_nch_read(priv)) return; channel_stats = priv->channel_stats[i]; @@ -5512,7 +5513,7 @@ static void mlx5e_get_queue_stats_tx(struct net_device *dev, int i, struct mlx5e_priv *priv = netdev_priv(dev); struct mlx5e_sq_stats *sq_stats; - if (!priv->stats_nch) + if (!mlx5e_stats_nch_read(priv)) return; /* no special case needed for ptp htb etc since txq2sq_stats is kept up @@ -5538,6 +5539,7 @@ static void mlx5e_get_base_stats(struct net_device *dev, struct netdev_queue_stats_tx *tx) { struct mlx5e_priv *priv = netdev_priv(dev); + u16 nch = mlx5e_stats_nch_read(priv); struct mlx5e_ptp *ptp_channel; int i, tc; @@ -5549,7 +5551,7 @@ static void mlx5e_get_base_stats(struct net_device *dev, rx->hw_gro_wire_packets = 0; rx->hw_gro_wire_bytes = 0; - for (i = priv->channels.params.num_channels; i < priv->stats_nch; i++) { + for (i = priv->channels.params.num_channels; i < nch; i++) { struct netdev_queue_stats_rx rx_i = {0}; mlx5e_get_queue_stats_rx(dev, i, &rx_i); @@ -5585,7 +5587,7 @@ static void mlx5e_get_base_stats(struct net_device *dev, tx->stop = 0; tx->wake = 0; - for (i = 0; i < priv->stats_nch; i++) { + for (i = 0; i < nch; i++) { struct mlx5e_channel_stats *channel_stats = priv->channel_stats[i]; /* handle two cases: diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c index 7f33261ba655..de38b60806c2 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c @@ -515,6 +515,7 @@ static void mlx5e_stats_update_stats_rq_page_pool(struct mlx5e_channel *c) static MLX5E_DECLARE_STATS_GRP_OP_UPDATE_STATS(sw) { struct mlx5e_sw_stats *s = &priv->stats.sw; + u16 nch = mlx5e_stats_nch_read(priv); int i; memset(s, 0, sizeof(*s)); @@ -522,7 +523,7 @@ static MLX5E_DECLARE_STATS_GRP_OP_UPDATE_STATS(sw) for (i = 0; i < priv->channels.num; i++) /* for active channels only */ mlx5e_stats_update_stats_rq_page_pool(priv->channels.c[i]); - for (i = 0; i < priv->stats_nch; i++) { + for (i = 0; i < nch; i++) { struct mlx5e_channel_stats *channel_stats = priv->channel_stats[i]; @@ -2614,7 +2615,7 @@ static MLX5E_DECLARE_STATS_GRP_OP_UPDATE_STATS(ptp) { return; } static MLX5E_DECLARE_STATS_GRP_OP_NUM_STATS(channels) { - int max_nch = priv->stats_nch; + int max_nch = mlx5e_stats_nch_read(priv); return (NUM_RQ_STATS * max_nch) + (NUM_CH_STATS * max_nch) + @@ -2627,8 +2628,8 @@ static MLX5E_DECLARE_STATS_GRP_OP_NUM_STATS(channels) static MLX5E_DECLARE_STATS_GRP_OP_FILL_STRS(channels) { + int max_nch = mlx5e_stats_nch_read(priv); bool is_xsk = priv->xsk.ever_used; - int max_nch = priv->stats_nch; int i, j, tc; for (i = 0; i < max_nch; i++) @@ -2660,8 +2661,8 @@ static MLX5E_DECLARE_STATS_GRP_OP_FILL_STRS(channels) static MLX5E_DECLARE_STATS_GRP_OP_FILL_STATS(channels) { + int max_nch = mlx5e_stats_nch_read(priv); bool is_xsk = priv->xsk.ever_used; - int max_nch = priv->stats_nch; int i, j, tc; for (i = 0; i < max_nch; i++) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c index 0a6003fe60e9..674bed721e63 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c @@ -135,10 +135,11 @@ void mlx5i_cleanup(struct mlx5e_priv *priv) static void mlx5i_grp_sw_update_stats(struct mlx5e_priv *priv) { + u16 nch = mlx5e_stats_nch_read(priv); struct rtnl_link_stats64 s = {}; int i, j; - for (i = 0; i < priv->stats_nch; i++) { + for (i = 0; i < nch; i++) { struct mlx5e_channel_stats *channel_stats; struct mlx5e_rq_stats *rq_stats; From aede83625ff5d9539508582036df30c809d51058 Mon Sep 17 00:00:00 2001 From: Andreas Kempe Date: Thu, 2 Jul 2026 10:41:23 +0000 Subject: [PATCH 0487/1054] iio: imu: st_lsm6dsx: deselect shub page before reading whoami As part of driver initialization, e.g. st_lsm6dsx_init_shub() selects the shub register page using st_lsm6dsx_set_page(). Selecting the shub register page shadows the regular register space so whoami, among other registers, is no longer accessible. In applications where the IMU is permanently powered separately from the processor, there is a window where a reset of the CPU leaves the IMU in the shub register page. Once this occurs, any subsequent probe attempt fails because of the register shadowing. Using the ism330dlc, the error typically looks like st_lsm6dsx_i2c 3-006a: unsupported whoami [10] with the unknown whoami read from a reserved register in the shub page. The reset register is also shadowed by the page select, preventing a reset from recovering the chip. Unconditionally clear the shub page before the whoami readout to ensure normal register access and allow the initialization to proceed. Place the fix in st_lsm6dsx_check_whoami() before the whoami check because hw->settings, which st_lsm6dsx_set_page() relies on, is first assigned in that function. Placing the fix in a more logical place than the whoami check would require a bigger restructuring of the code. Fixes: c91c1c844ebd ("iio: imu: st_lsm6dsx: add i2c embedded controller support") Signed-off-by: Andreas Kempe Acked-by: Lorenzo Bianconi Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c | 21 +++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c index 630e2cae6f19..f4edcb73ec8c 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c @@ -1712,6 +1712,26 @@ static int st_lsm6dsx_check_whoami(struct st_lsm6dsx_hw *hw, int id, return -ENODEV; } + hw->settings = &st_lsm6dsx_sensor_settings[i]; + + if (hw->settings->shub_settings.page_mux.addr) { + /* + * If the IMU has the shub page selected on init, for example + * after a CPU watchdog reset while the page is selected, the + * regular register space is shadowed. While the regular + * register space is shadowed, the registers needed for + * initializing the IMU are not available. + * + * Unconditionally clear the shub page selection to ensure + * normal register access. + */ + err = st_lsm6dsx_set_page(hw, false); + if (err < 0) { + dev_err(hw->dev, "failed to clear shub page\n"); + return err; + } + } + err = regmap_read(hw->regmap, ST_LSM6DSX_REG_WHOAMI_ADDR, &data); if (err < 0) { dev_err(hw->dev, "failed to read whoami register\n"); @@ -1724,7 +1744,6 @@ static int st_lsm6dsx_check_whoami(struct st_lsm6dsx_hw *hw, int id, } *name = st_lsm6dsx_sensor_settings[i].id[j].name; - hw->settings = &st_lsm6dsx_sensor_settings[i]; return 0; } From affe3f077d7a4eeb25937f5323ff059a54b4712c Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Maneyrol Date: Mon, 29 Jun 2026 21:51:55 +0200 Subject: [PATCH 0488/1054] iio: imu: inv_icm42600: fix timestamping by limiting FIFO reading Timestamps are made by measuring the chip clock using the watermark interrupts. If we read more than watermark samples as done today, we are reducing the period between interrupts and distort the time measurement. Fix that by reading only watermark samples in the interrupt case. Fixes: 7f85e42a6c54 ("iio: imu: inv_icm42600: add buffer support in iio devices") Cc: stable@vger.kernel.org Signed-off-by: Jean-Baptiste Maneyrol Signed-off-by: Jonathan Cameron --- drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c | 9 +++++---- drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h | 1 + 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c index 68a395758031..5c3840acf085 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c @@ -248,6 +248,7 @@ int inv_icm42600_buffer_update_watermark(struct inv_icm42600_state *st) /* compute watermark value in bytes */ wm_size = watermark * packet_size; + st->fifo.watermark.value = watermark; /* changing FIFO watermark requires to turn off watermark interrupt */ ret = regmap_update_bits_check(st->map, INV_ICM42600_REG_INT_SOURCE0, @@ -454,11 +455,10 @@ int inv_icm42600_buffer_fifo_read(struct inv_icm42600_state *st, st->fifo.nb.accel = 0; st->fifo.nb.total = 0; - /* compute maximum FIFO read size */ + /* compute maximum FIFO read size (watermark for max = 0 interrupt case) */ if (max == 0) - max_count = sizeof(st->fifo.data); - else - max_count = max * inv_icm42600_get_packet_size(st->fifo.en); + max = st->fifo.watermark.value; + max_count = max * inv_icm42600_get_packet_size(st->fifo.en); /* read FIFO count value */ raw_fifo_count = (__be16 *)st->buffer; @@ -574,6 +574,7 @@ int inv_icm42600_buffer_init(struct inv_icm42600_state *st) st->fifo.watermark.eff_gyro = 1; st->fifo.watermark.eff_accel = 1; + st->fifo.watermark.value = 1; /* * Default FIFO configuration (bits 7 to 5) diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h index ffca4da1e249..88b8b9f780af 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h @@ -34,6 +34,7 @@ struct inv_icm42600_fifo { unsigned int accel; unsigned int eff_gyro; unsigned int eff_accel; + unsigned int value; } watermark; size_t count; struct { From a00ffd15674bfaf8b906503c1600e3d8709af56c Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Mon, 18 May 2026 14:43:11 +0500 Subject: [PATCH 0489/1054] iio: light: tsl2591: return actual error from probe IRQ failure When devm_request_threaded_irq() fails, probe logs the error and then returns -EINVAL, dropping the real error code and breaking the deferred-probe flow for -EPROBE_DEFER. Return ret directly; the IRQ subsystem already prints on failure. Fixes: 2335f0d7c790 ("iio: light: Added AMS tsl2591 driver implementation") Cc: stable@vger.kernel.org Signed-off-by: Stepan Ionichev Signed-off-by: Jonathan Cameron --- drivers/iio/light/tsl2591.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/light/tsl2591.c b/drivers/iio/light/tsl2591.c index f3ffa9721ad5..ef3ed9635a1e 100644 --- a/drivers/iio/light/tsl2591.c +++ b/drivers/iio/light/tsl2591.c @@ -1070,10 +1070,8 @@ static int tsl2591_probe(struct i2c_client *client) NULL, tsl2591_event_handler, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "tsl2591_irq", indio_dev); - if (ret) { - dev_err_probe(&client->dev, ret, "IRQ request error\n"); - return -EINVAL; - } + if (ret) + return ret; indio_dev->info = &tsl2591_info; } else { indio_dev->info = &tsl2591_info_no_irq; From a9f41809bf1bd8e5c1bc4b6a1052adac58eb7ab6 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 19 May 2026 23:56:06 +0200 Subject: [PATCH 0490/1054] iio: adc: nxp-sar-adc: Fix the delay calculation in nxp_sar_adc_wait_for() The original code was using ndelay() twice. In one case the delay is calculated as 1/3 of ADC clock and in the other as 80 ADC clocks. But according to the comments in all cases it should be a multiplier of the ADC clock, and not a fraction of it. Inadvertently nxp_sar_adc_wait_for() takes the wrong case and spread it over the code make it wrong in all places. Fix this by modifying a helper to correctly use the multiplier. Fixes: 7e5c0f97c66a ("iio: adc: nxp-sar-adc: Avoid division by zero") Fixes: 4434072a893e ("iio: adc: Add the NXP SAR ADC support for the s32g2/3 platforms") Reported-by: Sashiko Closes: https://sashiko.dev/#/patchset/20260416090122.758990-1-andriy.shevchenko%40linux.intel.com Signed-off-by: Andy Shevchenko Reviewed-by: Stepan Ionichev Acked-by: Daniel Lezcano Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/nxp-sar-adc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/adc/nxp-sar-adc.c b/drivers/iio/adc/nxp-sar-adc.c index 15c7432808f4..6bf896915788 100644 --- a/drivers/iio/adc/nxp-sar-adc.c +++ b/drivers/iio/adc/nxp-sar-adc.c @@ -198,13 +198,13 @@ static void nxp_sar_adc_irq_cfg(struct nxp_sar_adc *info, bool enable) writel(0, NXP_SAR_ADC_IMR(info->regs)); } -static void nxp_sar_adc_wait_for(struct nxp_sar_adc *info, unsigned int cycles) +static void nxp_sar_adc_wait_for(struct nxp_sar_adc *info, u64 cycles) { u64 rate; rate = clk_get_rate(info->clk); if (rate) - ndelay(div64_u64(NSEC_PER_SEC, rate * cycles)); + ndelay(div64_u64(NSEC_PER_SEC * cycles, rate)); } static bool nxp_sar_adc_set_enabled(struct nxp_sar_adc *info, bool enable) From aa411adc6ce40ad1a55ebc965f255a4cfc0005f8 Mon Sep 17 00:00:00 2001 From: Vidhu Sarwal Date: Sat, 4 Jul 2026 17:22:45 +0530 Subject: [PATCH 0491/1054] iio: light: al3010: fix incorrect scale for the highest gain range al3010_scales[] encodes the highest gain range as {0, 1187200}. For IIO_VAL_INT_PLUS_MICRO, the fractional part must be less than 1000000, so the scale 1.1872 should instead be represented as { 1, 187200 }. Since write_raw() compares the value from userspace against this table, writing the advertised 1.1872 scale never matches the malformed entry and returns -EINVAL. As a result, the highest gain range cannot be selected. Reading the scale in that state also reports the malformed value. Fixes: c36b5195ab70 ("iio: light: add Dyna-Image AL3010 driver") Signed-off-by: Vidhu Sarwal Reviewed-by: Andy Shevchenko Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/light/al3010.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/light/al3010.c b/drivers/iio/light/al3010.c index d603b4a6b8e8..ca1d7fd6defb 100644 --- a/drivers/iio/light/al3010.c +++ b/drivers/iio/light/al3010.c @@ -42,7 +42,7 @@ enum al3xxxx_range { }; static const int al3010_scales[][2] = { - {0, 1187200}, {0, 296800}, {0, 74200}, {0, 18600} + { 1, 187200 }, { 0, 296800 }, { 0, 74200 }, { 0, 18600 }, }; static const struct regmap_config al3010_regmap_config = { From 660667cd406648bbaffbd5c0d897c2263a852f11 Mon Sep 17 00:00:00 2001 From: Shuangpeng Bai Date: Tue, 30 Jun 2026 15:48:56 -0400 Subject: [PATCH 0492/1054] llc: fix SAP refcount leak in llc_ui_autobind() llc_ui_autobind() opens a SAP after choosing a dynamic LSAP. llc_sap_open() returns a reference owned by the caller, and llc_sap_add_socket() takes a second reference for the socket's membership in the SAP hash tables. llc_ui_bind() drops the caller's reference after adding the socket, but llc_ui_autobind() keeps it. When the socket is closed, llc_sap_remove_socket() releases only the socket reference, leaving the SAP on llc_sap_list with sk_count == 0. This is user-visible because repeated autobind and close cycles can consume all dynamic SAP values and make later autobinds fail with -EUSERS. Drop the caller's reference after a successful autobind, matching llc_ui_bind()'s ownership model. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Shuangpeng Bai Link: https://patch.msgid.link/20260630194856.1036497-1-shuangpeng.kernel@gmail.com Signed-off-by: Paolo Abeni --- net/llc/af_llc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/llc/af_llc.c b/net/llc/af_llc.c index 8ed1be1ecccc..b0447c33dbf0 100644 --- a/net/llc/af_llc.c +++ b/net/llc/af_llc.c @@ -312,6 +312,7 @@ static int llc_ui_autobind(struct socket *sock, struct sockaddr_llc *addr) /* assign new connection to its SAP */ llc_sap_add_socket(sap, sk); sock_reset_flag(sk, SOCK_ZAPPED); + llc_sap_put(sap); rc = 0; out: dev_put(dev); From d7a8d500d7e42837bd8dce40cb52c97c6e8706a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Emil=20Schulz=20=C3=98stergaard?= Date: Tue, 30 Jun 2026 14:20:13 +0200 Subject: [PATCH 0493/1054] net: microchip: vcap: fix races on the shared Super VCAP block MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The VCAP instances on a chip are not independent, yet they are locked independently. On sparx5 and lan969x the IS0 and IS2 instances are backed by the same Super VCAP hardware block and share its cache and command registers: every access drives the shared VCAP_SUPER_CTRL register and moves data through the shared cache registers. Accessing one instance therefore races with accessing another. The per-instance admin->lock cannot prevent this, as each instance takes a different lock. The locking issue is mostly disguised by the fact that the core usage of the vcap api runs under rtnl. However, the full rule dump in debugfs decodes rules straight from hardware (a READ command followed by a cache read) and runs outside rtnl, so it races a concurrent tc-flower rule write to another Super VCAP instance. Besides corrupting the dump, the read repopulates the shared cache between the writers cache fill and its write command, so the writer commits the wrong data and corrupts the hardware entry. Introduce vcap_lock() and vcap_unlock() helpers and route every rule lock site in the VCAP API and its debugfs code through them. Replace the per-instance admin->lock with a single mutex in struct vcap_control that serializes access to all instances. The helpers reach it through a new admin->vctrl back-pointer, and the clients initialise and destroy the control lock instead of a per-instance one. No path holds more than one instance lock, so collapsing them onto a single mutex cannot self-deadlock. Fixes: 71c9de995260 ("net: microchip: sparx5: Add VCAP locking to protect rules") Signed-off-by: Jens Emil Schulz Østergaard Link: https://patch.msgid.link/20260630-microchip_fix_vcap_locking-v1-1-f60a4596734d@microchip.com Signed-off-by: Paolo Abeni --- .../microchip/lan966x/lan966x_vcap_impl.c | 5 +- .../microchip/sparx5/sparx5_vcap_impl.c | 5 +- .../net/ethernet/microchip/vcap/vcap_api.c | 72 +++++++++++-------- .../net/ethernet/microchip/vcap/vcap_api.h | 3 +- .../microchip/vcap/vcap_api_debugfs.c | 8 +-- .../microchip/vcap/vcap_api_debugfs_kunit.c | 3 +- .../ethernet/microchip/vcap/vcap_api_kunit.c | 3 +- .../microchip/vcap/vcap_api_private.h | 3 + 8 files changed, 60 insertions(+), 42 deletions(-) diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c b/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c index 72e3b189bac5..eb28df80b281 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c @@ -601,7 +601,6 @@ static void lan966x_vcap_admin_free(struct vcap_admin *admin) kfree(admin->cache.keystream); kfree(admin->cache.maskstream); kfree(admin->cache.actionstream); - mutex_destroy(&admin->lock); kfree(admin); } @@ -615,7 +614,7 @@ lan966x_vcap_admin_alloc(struct lan966x *lan966x, struct vcap_control *ctrl, if (!admin) return ERR_PTR(-ENOMEM); - mutex_init(&admin->lock); + admin->vctrl = ctrl; INIT_LIST_HEAD(&admin->list); INIT_LIST_HEAD(&admin->rules); INIT_LIST_HEAD(&admin->enabled); @@ -721,6 +720,7 @@ int lan966x_vcap_init(struct lan966x *lan966x) ctrl->ops = &lan966x_vcap_ops; INIT_LIST_HEAD(&ctrl->list); + mutex_init(&ctrl->lock); for (int i = 0; i < ARRAY_SIZE(lan966x_vcap_inst_cfg); ++i) { cfg = &lan966x_vcap_inst_cfg[i]; @@ -780,5 +780,6 @@ void lan966x_vcap_deinit(struct lan966x *lan966x) lan966x_vcap_admin_free(admin); } + mutex_destroy(&ctrl->lock); kfree(ctrl); } diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c index 95b93e46a41d..cf332de6bf73 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c @@ -1930,7 +1930,6 @@ static void sparx5_vcap_admin_free(struct vcap_admin *admin) { if (!admin) return; - mutex_destroy(&admin->lock); kfree(admin->cache.keystream); kfree(admin->cache.maskstream); kfree(admin->cache.actionstream); @@ -1950,7 +1949,7 @@ sparx5_vcap_admin_alloc(struct sparx5 *sparx5, struct vcap_control *ctrl, INIT_LIST_HEAD(&admin->list); INIT_LIST_HEAD(&admin->rules); INIT_LIST_HEAD(&admin->enabled); - mutex_init(&admin->lock); + admin->vctrl = ctrl; admin->vtype = cfg->vtype; admin->vinst = cfg->vinst; admin->ingress = cfg->ingress; @@ -2059,6 +2058,7 @@ int sparx5_vcap_init(struct sparx5 *sparx5) ctrl->ops = &sparx5_vcap_ops; INIT_LIST_HEAD(&ctrl->list); + mutex_init(&ctrl->lock); for (idx = 0; idx < ARRAY_SIZE(sparx5_vcap_inst_cfg); ++idx) { cfg = &consts->vcaps_cfg[idx]; admin = sparx5_vcap_admin_alloc(sparx5, ctrl, cfg); @@ -2097,5 +2097,6 @@ void sparx5_vcap_deinit(struct sparx5 *sparx5) list_del(&admin->list); sparx5_vcap_admin_free(admin); } + mutex_destroy(&ctrl->lock); kfree(ctrl); } diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.c b/drivers/net/ethernet/microchip/vcap/vcap_api.c index 0fdb5e363bad..ff86cde11a32 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api.c @@ -934,6 +934,16 @@ static bool vcap_rule_exists(struct vcap_control *vctrl, u32 id) return false; } +void vcap_lock(struct vcap_admin *admin) +{ + mutex_lock(&admin->vctrl->lock); +} + +void vcap_unlock(struct vcap_admin *admin) +{ + mutex_unlock(&admin->vctrl->lock); +} + /* Find a rule with a provided rule id return a locked vcap */ static struct vcap_rule_internal * vcap_get_locked_rule(struct vcap_control *vctrl, u32 id) @@ -943,11 +953,11 @@ vcap_get_locked_rule(struct vcap_control *vctrl, u32 id) /* Look for the rule id in all vcaps */ list_for_each_entry(admin, &vctrl->list, list) { - mutex_lock(&admin->lock); + vcap_lock(admin); list_for_each_entry(ri, &admin->rules, list) if (ri->data.id == id) return ri; - mutex_unlock(&admin->lock); + vcap_unlock(admin); } return NULL; } @@ -961,14 +971,14 @@ int vcap_lookup_rule_by_cookie(struct vcap_control *vctrl, u64 cookie) /* Look for the rule id in all vcaps */ list_for_each_entry(admin, &vctrl->list, list) { - mutex_lock(&admin->lock); + vcap_lock(admin); list_for_each_entry(ri, &admin->rules, list) { if (ri->data.cookie == cookie) { id = ri->data.id; break; } } - mutex_unlock(&admin->lock); + vcap_unlock(admin); if (id) return id; } @@ -985,11 +995,11 @@ int vcap_admin_rule_count(struct vcap_admin *admin, int cid) int count = 0; list_for_each_entry(elem, &admin->rules, list) { - mutex_lock(&admin->lock); + vcap_lock(admin); if (elem->data.vcap_chain_id >= min_cid && elem->data.vcap_chain_id < max_cid) ++count; - mutex_unlock(&admin->lock); + vcap_unlock(admin); } return count; } @@ -2266,7 +2276,7 @@ int vcap_add_rule(struct vcap_rule *rule) if (ret) return ret; /* Insert the new rule in the list of vcap rules */ - mutex_lock(&ri->admin->lock); + vcap_lock(ri->admin); vcap_rule_set_state(ri); ret = vcap_insert_rule(ri, &move); @@ -2302,7 +2312,7 @@ int vcap_add_rule(struct vcap_rule *rule) goto out; } out: - mutex_unlock(&ri->admin->lock); + vcap_unlock(ri->admin); return ret; } EXPORT_SYMBOL_GPL(vcap_add_rule); @@ -2330,7 +2340,7 @@ struct vcap_rule *vcap_alloc_rule(struct vcap_control *vctrl, if (vctrl->vcaps[admin->vtype].rows == 0) return ERR_PTR(-EINVAL); - mutex_lock(&admin->lock); + vcap_lock(admin); /* Check if a rule with this id already exists */ if (vcap_rule_exists(vctrl, id)) { err = -EINVAL; @@ -2369,13 +2379,13 @@ struct vcap_rule *vcap_alloc_rule(struct vcap_control *vctrl, goto out_free; } - mutex_unlock(&admin->lock); + vcap_unlock(admin); return (struct vcap_rule *)ri; out_free: kfree(ri); out_unlock: - mutex_unlock(&admin->lock); + vcap_unlock(admin); return ERR_PTR(err); } @@ -2446,7 +2456,7 @@ struct vcap_rule *vcap_get_rule(struct vcap_control *vctrl, u32 id) return ERR_PTR(-ENOENT); rule = vcap_decode_rule(elem); - mutex_unlock(&elem->admin->lock); + vcap_unlock(elem->admin); return rule; } EXPORT_SYMBOL_GPL(vcap_get_rule); @@ -2483,7 +2493,7 @@ int vcap_mod_rule(struct vcap_rule *rule) err = vcap_write_counter(ri, &ctr); out: - mutex_unlock(&ri->admin->lock); + vcap_unlock(ri->admin); return err; } EXPORT_SYMBOL_GPL(vcap_mod_rule); @@ -2570,7 +2580,7 @@ int vcap_del_rule(struct vcap_control *vctrl, struct net_device *ndev, u32 id) admin->last_used_addr = elem->addr; } - mutex_unlock(&admin->lock); + vcap_unlock(admin); return err; } EXPORT_SYMBOL_GPL(vcap_del_rule); @@ -2585,7 +2595,7 @@ int vcap_del_rules(struct vcap_control *vctrl, struct vcap_admin *admin) if (ret) return ret; - mutex_lock(&admin->lock); + vcap_lock(admin); list_for_each_entry_safe(ri, next_ri, &admin->rules, list) { vctrl->ops->init(ri->ndev, admin, ri->addr, ri->size); list_del(&ri->list); @@ -2598,7 +2608,7 @@ int vcap_del_rules(struct vcap_control *vctrl, struct vcap_admin *admin) list_del(&eport->list); kfree(eport); } - mutex_unlock(&admin->lock); + vcap_unlock(admin); return 0; } @@ -3016,7 +3026,7 @@ static int vcap_enable_rules(struct vcap_control *vctrl, continue; /* Found the admin, now find the offloadable rules */ - mutex_lock(&admin->lock); + vcap_lock(admin); list_for_each_entry(ri, &admin->rules, list) { /* Is the rule in the lookup defined by the chain */ if (!(ri->data.vcap_chain_id >= chain && @@ -3034,7 +3044,7 @@ static int vcap_enable_rules(struct vcap_control *vctrl, if (err) break; } - mutex_unlock(&admin->lock); + vcap_unlock(admin); if (err) break; } @@ -3074,7 +3084,7 @@ static int vcap_disable_rules(struct vcap_control *vctrl, continue; /* Found the admin, now find the rules on the chain */ - mutex_lock(&admin->lock); + vcap_lock(admin); list_for_each_entry(ri, &admin->rules, list) { if (ri->data.vcap_chain_id != chain) continue; @@ -3089,7 +3099,7 @@ static int vcap_disable_rules(struct vcap_control *vctrl, if (err) break; } - mutex_unlock(&admin->lock); + vcap_unlock(admin); if (err) break; } @@ -3133,9 +3143,9 @@ static int vcap_enable(struct vcap_control *vctrl, struct net_device *ndev, eport->cookie = cookie; eport->src_cid = src_cid; eport->dst_cid = dst_cid; - mutex_lock(&admin->lock); + vcap_lock(admin); list_add_tail(&eport->list, &admin->enabled); - mutex_unlock(&admin->lock); + vcap_unlock(admin); if (vcap_path_exist(vctrl, ndev, src_cid)) { /* Enable chained lookups */ @@ -3185,9 +3195,9 @@ static int vcap_disable(struct vcap_control *vctrl, struct net_device *ndev, dst_cid = vcap_get_next_chain(vctrl, ndev, dst_cid); } - mutex_lock(&found->lock); + vcap_lock(found); list_del(&eport->list); - mutex_unlock(&found->lock); + vcap_unlock(found); kfree(eport); return 0; } @@ -3270,9 +3280,9 @@ int vcap_rule_set_counter(struct vcap_rule *rule, struct vcap_counter *ctr) return -EINVAL; } - mutex_lock(&ri->admin->lock); + vcap_lock(ri->admin); err = vcap_write_counter(ri, ctr); - mutex_unlock(&ri->admin->lock); + vcap_unlock(ri->admin); return err; } @@ -3291,9 +3301,9 @@ int vcap_rule_get_counter(struct vcap_rule *rule, struct vcap_counter *ctr) return -EINVAL; } - mutex_lock(&ri->admin->lock); + vcap_lock(ri->admin); err = vcap_read_counter(ri, ctr); - mutex_unlock(&ri->admin->lock); + vcap_unlock(ri->admin); return err; } @@ -3395,7 +3405,7 @@ int vcap_get_rule_count_by_cookie(struct vcap_control *vctrl, /* Iterate all rules in each VCAP instance */ list_for_each_entry(admin, &vctrl->list, list) { - mutex_lock(&admin->lock); + vcap_lock(admin); list_for_each_entry(ri, &admin->rules, list) { if (ri->data.cookie != cookie) continue; @@ -3412,12 +3422,12 @@ int vcap_get_rule_count_by_cookie(struct vcap_control *vctrl, if (err) goto unlock; } - mutex_unlock(&admin->lock); + vcap_unlock(admin); } return err; unlock: - mutex_unlock(&admin->lock); + vcap_unlock(admin); return err; } EXPORT_SYMBOL_GPL(vcap_get_rule_count_by_cookie); diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.h b/drivers/net/ethernet/microchip/vcap/vcap_api.h index 6069ad95c27e..05b4b02e59ef 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api.h +++ b/drivers/net/ethernet/microchip/vcap/vcap_api.h @@ -164,7 +164,7 @@ struct vcap_admin { struct list_head list; /* for insertion in vcap_control */ struct list_head rules; /* list of rules */ struct list_head enabled; /* list of enabled ports */ - struct mutex lock; /* control access to rules */ + struct vcap_control *vctrl; /* the control instance owning this vcap */ enum vcap_type vtype; /* type of vcap */ int vinst; /* instance number within the same type */ int first_cid; /* first chain id in this vcap */ @@ -275,6 +275,7 @@ struct vcap_control { const struct vcap_info *vcaps; /* client supplied vcap models */ const struct vcap_statistics *stats; /* client supplied vcap stats */ struct list_head list; /* list of vcap instances */ + struct mutex lock; /* serialize access to all vcap instances */ }; #endif /* __VCAP_API__ */ diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c b/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c index 59bfbda29bb3..e0c65c7ab23e 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c @@ -410,9 +410,9 @@ static int vcap_debugfs_show(struct seq_file *m, void *unused) }; int ret; - mutex_lock(&info->admin->lock); + vcap_lock(info->admin); ret = vcap_show_admin(info->vctrl, info->admin, &out); - mutex_unlock(&info->admin->lock); + vcap_unlock(info->admin); return ret; } DEFINE_SHOW_ATTRIBUTE(vcap_debugfs); @@ -427,9 +427,9 @@ static int vcap_raw_debugfs_show(struct seq_file *m, void *unused) }; int ret; - mutex_lock(&info->admin->lock); + vcap_lock(info->admin); ret = vcap_show_admin_raw(info->vctrl, info->admin, &out); - mutex_unlock(&info->admin->lock); + vcap_unlock(info->admin); return ret; } DEFINE_SHOW_ATTRIBUTE(vcap_raw_debugfs); diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs_kunit.c b/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs_kunit.c index 9c9d38042125..ac2a3b8c4f32 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs_kunit.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs_kunit.c @@ -243,10 +243,11 @@ static void vcap_test_api_init(struct vcap_admin *admin) { /* Initialize the shared objects */ INIT_LIST_HEAD(&test_vctrl.list); + mutex_init(&test_vctrl.lock); INIT_LIST_HEAD(&admin->list); INIT_LIST_HEAD(&admin->rules); INIT_LIST_HEAD(&admin->enabled); - mutex_init(&admin->lock); + admin->vctrl = &test_vctrl; list_add_tail(&admin->list, &test_vctrl.list); memset(test_updateaddr, 0, sizeof(test_updateaddr)); test_updateaddridx = 0; diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c b/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c index ce26ccbdccdf..83de384d3e3b 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c @@ -233,10 +233,11 @@ static void vcap_test_api_init(struct vcap_admin *admin) { /* Initialize the shared objects */ INIT_LIST_HEAD(&test_vctrl.list); + mutex_init(&test_vctrl.lock); INIT_LIST_HEAD(&admin->list); INIT_LIST_HEAD(&admin->rules); INIT_LIST_HEAD(&admin->enabled); - mutex_init(&admin->lock); + admin->vctrl = &test_vctrl; list_add_tail(&admin->list, &test_vctrl.list); memset(test_updateaddr, 0, sizeof(test_updateaddr)); test_updateaddridx = 0; diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_private.h b/drivers/net/ethernet/microchip/vcap/vcap_api_private.h index 844bdf6b5f45..b4057fbe3d18 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api_private.h +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_private.h @@ -50,6 +50,9 @@ struct vcap_stream_iter { /* Check that the control has a valid set of callbacks */ int vcap_api_check(struct vcap_control *ctrl); +/* Serialize access to the vcap instances of a control */ +void vcap_lock(struct vcap_admin *admin); +void vcap_unlock(struct vcap_admin *admin); /* Erase the VCAP cache area used or encoding and decoding */ void vcap_erase_cache(struct vcap_rule_internal *ri); From cb89f0c1aed02eb233c4271f76f830b37e222ff6 Mon Sep 17 00:00:00 2001 From: Malaya Kumar Rout Date: Sat, 4 Jul 2026 16:27:36 +0530 Subject: [PATCH 0494/1054] selftests/alsa: Fix memory leak in find_controls error path In find_controls(), card_data is allocated with malloc() but when snd_ctl_open_lconf() fails, the code jumps to next_card without freeing the allocated memory. This results in a memory leak for each card where snd_ctl_open_lconf() fails. Add free(card_data) before goto next_card to ensure proper cleanup of the allocated memory in the error path. Fixes: 5aaf9efffc57 ("kselftest: alsa: Add simplistic test for ALSA mixer controls kselftest") Signed-off-by: Malaya Kumar Rout Link: https://patch.msgid.link/20260704105736.94874-1-malayarout91@gmail.com Signed-off-by: Takashi Iwai --- tools/testing/selftests/alsa/mixer-test.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/testing/selftests/alsa/mixer-test.c b/tools/testing/selftests/alsa/mixer-test.c index d4f845c32804..c5ae2a026ce9 100644 --- a/tools/testing/selftests/alsa/mixer-test.c +++ b/tools/testing/selftests/alsa/mixer-test.c @@ -84,6 +84,7 @@ static void find_controls(void) if (err < 0) { ksft_print_msg("Failed to get hctl for card %d: %s\n", card, snd_strerror(err)); + free(card_data); goto next_card; } From 920f893f735e92ba3a1cd9256899a186b161928d Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 3 Jul 2026 12:02:38 +0200 Subject: [PATCH 0495/1054] posix-cpu-timers: Prevent UAF caused by non-leader exec() race Wongi and Jungwoo decoded and reported a non-leader exec() related race which can result in an UAF: sys_timer_delete() exec() posix_cpu_timer_del() // Observes old leader p = pid_task(pid, pid_type); de_thread() switch_leader(); release_task(old_leader) __exit_signal(old_leader) sighand = lock(old_leader, sighand); posix_cpu_timers*_exit(); sighand = lock_task_sighand(p) unhash_task(old_leader); sh = lock(p, sighand) old_leader->sighand = NULL; unlock(sighand); (p->sighand == NULL) unlock(sh) return NULL; // Returns without action if(!sighand) return 0; free_posix_timer(); This is "harmless" unless the deleted timer was armed and enqueued in p->signal because on exec() a TGID targeted timer is inherited. As sys_timer_delete() freed the underlying posix timer object run_posix_cpu_timers() or any timerqueue related add/delete operations on other timers will access the freed object's timerqueue node, which results in an UAF. There is a similar problem vs. posix_cpu_timer_set(). For regular posix timers it just transiently returns -ESRCH to user space, but for the use case in do_cpu_nanosleep() it's the same UAF just that the k_itimer is allocated on the stack. Also posix_cpu_timer_rearm() fails to rearm the timer, which means it stops to expire. While debating solutions Frederic pointed out another problem: posix_cpu_timer_del(tmr) __exit_signal(p) posix_cpu_timers*_exit(p); unhash_task(p); p->sighand = NULL; sh = lock_task_sighand(p) sighand = p->sighand; if (!sighand) return NULL; lock(sighand); if (!sh) WARN_ON_ONCE(timer_queued(tmr)); On weakly ordered architectures it is not guaranteed that posix_cpu_timer_del() will observe the stores in posix_cpu_timers*_exit() when p->sighand is observed as NULL, which means the WARN() can be a false positive. Solve these issues by: 1) Changing the store in __exit_signal() to smp_store_release(). 2) Adding a smp_acquire__after_ctrl_dep() into the !sighand path of lock_task_sighand(). 3) Creating a helper function for looking up the task and locking sighand which does not return when sighand == NULL. Instead it retries the task lookup and only if that fails it gives up. 4) Using that helper in the three affected functions. #1/#2 ensures that the reader side which observes sighand == NULL also observes all preceeding stores, i.e. the stores in posix_cpu_timers*_exit() and the ones in unhash_task(). #3 ensures that the above described non-leader exec() situation is handled gracefully. When the task lookup returns the old leader, but sighand == NULL then it retries. In the non-leader exec() case the subsequent task lookup will observe the new leader due to #1/#2. In normal exit() scenarios the subsequent lookup fails. When the task lookup fails, the function also checks whether the timer is still enqueued and issues a warning if that's the case. Unfortunately there is nothing which can be done about it, but as the task is already not longer visible the timer should not be accessed anymore. This check also requires memory ordering, which is not provided when the first lookup fails. To achieve that the check is preceeded by a smp_rmb() which pairs with the smp_wmb() in write_seqlock() in __exit_signal(). That ensures that the stores in posix_cpu_timers*_exit() are visible. The history of the non-leader exec() issue goes back to the early days of posix CPU timers, which stored a pointer to the group leader task in the timer. That obviously fails when a non-leader exec() switches the leader. commit e0a70217107e ("posix-cpu-timers: workaround to suppress the problems with mt exec") added a temporary workaround for that in 2010 which survived about 10 years. The fix for the workaround changed the task pointer to a pid pointer, but failed to see the subtle race described above. So the Fixes tag picks that commit, which seems to be halfways accurate. Thanks to Frederic Weissbecker, Oleg Nesterov and Peter Zijlstra for review, feedback and suggestions and to Wongi and Jungwoo for the excellent bug report and analysis! Fixes: 55e8c8eb2c7b ("posix-cpu-timers: Store a reference to a pid not a task") Reported-by: Wongi Lee Reported-by: Jungwoo Lee Signed-off-by: Thomas Gleixner Reviewed-by: Oleg Nesterov Cc: stable@vger.kernel.org --- kernel/exit.c | 7 +- kernel/signal.c | 10 +- kernel/time/posix-cpu-timers.c | 173 ++++++++++++++++++++++----------- 3 files changed, 130 insertions(+), 60 deletions(-) diff --git a/kernel/exit.c b/kernel/exit.c index 1056422bc101..2c0b1c02920f 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -212,7 +212,12 @@ static void __exit_signal(struct release_task_post *post, struct task_struct *ts __unhash_process(post, tsk, group_dead); write_sequnlock(&sig->stats_lock); - tsk->sighand = NULL; + /* + * Ensure that all preceeding state is visible. Pairs with + * the smp_acquire__after_ctrl_dep() in the sighand == NULL + * path of lock_task_sighand(). + */ + smp_store_release(&tsk->sighand, NULL); spin_unlock(&sighand->siglock); __cleanup_sighand(sighand); diff --git a/kernel/signal.c b/kernel/signal.c index 9c2b32c4d755..bbc0fd4cc4d7 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -1362,8 +1362,16 @@ struct sighand_struct *lock_task_sighand(struct task_struct *tsk, rcu_read_lock(); for (;;) { sighand = rcu_dereference(tsk->sighand); - if (unlikely(sighand == NULL)) + if (unlikely(sighand == NULL)) { + /* + * Pairs with the smp_store_release() in + * __exit_signal(). It ensures that all state + * modifications to the task preceeding the store are + * visible to the callers of lock_task_sighand(). + */ + smp_acquire__after_ctrl_dep(); break; + } /* * This sighand can be already freed and even reused, but diff --git a/kernel/time/posix-cpu-timers.c b/kernel/time/posix-cpu-timers.c index 5e633d8750d1..a7d3e8229c4b 100644 --- a/kernel/time/posix-cpu-timers.c +++ b/kernel/time/posix-cpu-timers.c @@ -461,6 +461,109 @@ static void disarm_timer(struct k_itimer *timer, struct task_struct *p) trigger_base_recalc_expires(timer, p); } +/* + * Lookup the task via timer->it.cpu.pid and attempt to lock the task's sighand. + * + * This can race with the reaping of the task: + * + * CPU0 CPU1 + * + * // Finds task + * p = pid_task(pid, pid_type); __exit_signal(p) + * lock(p, sighand); + * posix_cpu_timers*_exit(); + * sighand = lock_task_sighand(p); unhash_task(p); + * p->sighand = NULL; + * unlock(sighand); + * + * In this case sighand is NULL, which means the task and the associated timer + * queue cannot be longer accessed safely. + * + * __exit_signal() invokes posix_cpu_timers_exit() and if the thread group is + * dead it also invokes posix_cpu_timers_group_exit(). These functions delete + * all pending timers from the related timer queues. The POSIX timers (k_itimer) + * themself are still accessible, but not longer connected to the task. + * + * exec() works slightly differently. The task which exec()'s terminates all + * other threads in the thread group and runs __exit_signal() on them. As the + * thread group is not dead they only clean up the per task timers via + * posix_cpu_timers_exit(). + * + * As the TGID on exec() stays the same per process timers stay queued, if they + * are armed. This works without a problem when exec() is done by the thread + * group leader. If a non-leader thread exec()'s this can end up in the + * following scenario: + * + * CPU0 CPU1 + * // Returns old leader + * p = pid_task(pid, pid_type); de_thread() + * switch_leader() + * release_task(old leader) + * __exit_signal() + * old_leader->sighand = NULL; + * // Returns NULL + * sighand = lock_task_sighand(p) + * + * That's problematic for several functions: + * + * - posix_cpu_timer_del(): If the timer is still enqueued on the task the + * underlying k_itimer will be freed which results in a UAF in + * run_posix_cpu_timers() or on timerqueue related add/delete operations. + * If the timer is not enqueued, the failure is harmless + * + * - posix_cpu_timer_set(): Independent of the enqueued state that results in a + * transient failure which is user space visible (-ESRCH) for regular posix + * timers. But for the use case in do_cpu_nanosleep() it's the same UAF + * problem just that the timer is allocated on the stack. + * + * - posix_cpu_timer_rearm(): Timer is not enqueued at that point, but this + * silently ignores the rearm request, which is a functional problem as the + * timer wont expire anymore. + */ +static struct task_struct *timer_lock_sighand(struct k_itimer *timer, unsigned long *flags) +{ + enum pid_type type = clock_pid_type(timer->it_clock); + struct cpu_timer *ctmr = &timer->it.cpu; + + guard(rcu)(); + + for (;;) { + struct task_struct *t = pid_task(timer->it.cpu.pid, type); + + /* Fail if the task cannot be found. */ + if (!t) + break; + + /* Try to lock the task's sighand */ + if (lock_task_sighand(t, flags)) + return t; + + /* + * The next PID lookup might either fail or return the new + * leader. This is correct for both exit() and exec(). + */ + } + + /* + * If the timer is still enqueued, warn. There is nothing safe to do + * here as there might be two timers in there which are removed in + * parallel and that will cause more damage than good. This should never + * happen! + * + * Ensure that the stores to the timer and timerqueue are visible: + * + * __exit_signal() + * posix_cpu_timers*_exit() + * write_seqlock(seqlock) + * smp_wmb(); <------- + * __unhash_process() | !pid_task() + * ----> smp_rmb(); + * WARN_ON_ONCE(...) + */ + smp_rmb(); + WARN_ON_ONCE(ctmr->head || timerqueue_node_queued(&ctmr->node)); + return NULL; +} /* * Clean up a CPU-clock timer that is about to be destroyed. @@ -470,29 +573,13 @@ static void disarm_timer(struct k_itimer *timer, struct task_struct *p) */ static int posix_cpu_timer_del(struct k_itimer *timer) { - struct cpu_timer *ctmr = &timer->it.cpu; - struct sighand_struct *sighand; struct task_struct *p; unsigned long flags; int ret = 0; - rcu_read_lock(); - p = cpu_timer_task_rcu(timer); - if (!p) - goto out; + p = timer_lock_sighand(timer, &flags); - /* - * Protect against sighand release/switch in exit/exec and process/ - * thread timer list entry concurrent read/writes. - */ - sighand = lock_task_sighand(p, &flags); - if (unlikely(sighand == NULL)) { - /* - * This raced with the reaping of the task. The exit cleanup - * should have removed this timer from the timer queue. - */ - WARN_ON_ONCE(ctmr->head || timerqueue_node_queued(&ctmr->node)); - } else { + if (likely(p)) { if (timer->it.cpu.firing) { /* * Prevent signal delivery. The timer cannot be dequeued @@ -508,11 +595,8 @@ static int posix_cpu_timer_del(struct k_itimer *timer) unlock_task_sighand(p, &flags); } -out: - rcu_read_unlock(); - if (!ret) { - put_pid(ctmr->pid); + put_pid(timer->it.cpu.pid); timer->it_status = POSIX_TIMER_DISARMED; } return ret; @@ -626,21 +710,17 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags, clockid_t clkid = CPUCLOCK_WHICH(timer->it_clock); struct cpu_timer *ctmr = &timer->it.cpu; u64 old_expires, new_expires, now; - struct sighand_struct *sighand; struct task_struct *p; unsigned long flags; int ret = 0; - rcu_read_lock(); - p = cpu_timer_task_rcu(timer); - if (!p) { - /* - * If p has just been reaped, we can no - * longer get any information about it at all. - */ - rcu_read_unlock(); + p = timer_lock_sighand(timer, &flags); + /* + * If p has just been reaped, we can no longer get any information about + * it at all. + */ + if (!p) return -ESRCH; - } /* * Use the to_ktime conversion because that clamps the maximum @@ -648,20 +728,6 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags, */ new_expires = ktime_to_ns(timespec64_to_ktime(new->it_value)); - /* - * Protect against sighand release/switch in exit/exec and p->cpu_timers - * and p->signal->cpu_timers read/write in arm_timer() - */ - sighand = lock_task_sighand(p, &flags); - /* - * If p has just been reaped, we can no - * longer get any information about it at all. - */ - if (unlikely(sighand == NULL)) { - rcu_read_unlock(); - return -ESRCH; - } - /* Retrieve the current expiry time before disarming the timer */ old_expires = cpu_timer_getexpires(ctmr); @@ -698,7 +764,7 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags, /* Retry if the timer expiry is running concurrently */ if (unlikely(ret)) { unlock_task_sighand(p, &flags); - goto out; + return ret; } /* Convert relative expiry time to absolute */ @@ -733,8 +799,6 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags, */ if (!sigev_none && new_expires && now >= new_expires) cpu_timer_fire(timer); -out: - rcu_read_unlock(); return ret; } @@ -1018,19 +1082,12 @@ static void check_process_timers(struct task_struct *tsk, static bool posix_cpu_timer_rearm(struct k_itimer *timer) { clockid_t clkid = CPUCLOCK_WHICH(timer->it_clock); - struct sighand_struct *sighand; struct task_struct *p; unsigned long flags; u64 now; - guard(rcu)(); - p = cpu_timer_task_rcu(timer); - if (!p) - return true; - - /* Protect timer list r/w in arm_timer() */ - sighand = lock_task_sighand(p, &flags); - if (unlikely(sighand == NULL)) + p = timer_lock_sighand(timer, &flags); + if (unlikely(!p)) return true; /* From 742a87fa54ad7123bff41bd1aa149fef6929a7af Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Sun, 5 Jul 2026 16:07:13 +0800 Subject: [PATCH 0496/1054] ALSA: hda/realtek: add quirk for Framework F111:0010 Similar to commit 67c738152207 ("ALSA: hda/realtek: fix mic boost on Framework PTL") and previous quirks for Framework systems with Realtek codecs. 0010 is another platform with an ALC285 which needs the same quirk. Signed-off-by: Daniel Schaefer Link: https://patch.msgid.link/20260705-framework-audio-ssid-0x0010-v1-1-fffc8996ac18@frame.work Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index c54f55730fdc..b6b502450a95 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -7923,6 +7923,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0xf111, 0x000c, "Framework Laptop", ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE), SND_PCI_QUIRK(0xf111, 0x000f, "Framework Laptop 13 Pro PTL", ALC295_FIXUP_FRAMEWORK_LAPTOP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0xf111, 0x010f, "Framework Laptop 13 PTL", ALC295_FIXUP_FRAMEWORK_LAPTOP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0xf111, 0x0010, "Framework Laptop", ALC295_FIXUP_FRAMEWORK_LAPTOP_LIMIT_INT_MIC_BOOST), #if 0 /* Below is a quirk table taken from the old code. From aba30af07d4fe499b50209801eba9da8a815522f Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sun, 5 Jul 2026 16:46:01 +0800 Subject: [PATCH 0497/1054] ALSA: usb-audio: caiaq: validate EP1 reply lengths usb_ep1_command_reply_dispatch() uses buf[0] as a command byte and then reads command-specific fixed items from the same URB buffer. Several paths use buf + 1, buf[1], buf[2], or buf + 3 without first proving that urb->actual_length contains those bytes. Add per-command length checks, use a payload length derived from the bytes after the command byte for the control-state copy, and reject short analog input payloads before the input helper reads fixed offsets from the EP1 reply. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260705084601.56400-1-pengpeng@iscas.ac.cn Signed-off-by: Takashi Iwai --- sound/usb/caiaq/device.c | 17 ++++++++++++++--- sound/usb/caiaq/input.c | 6 ++++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/sound/usb/caiaq/device.c b/sound/usb/caiaq/device.c index b20aae0caf60..a16e59248480 100644 --- a/sound/usb/caiaq/device.c +++ b/sound/usb/caiaq/device.c @@ -134,14 +134,22 @@ static void usb_ep1_command_reply_dispatch (struct urb* urb) struct device *dev = &urb->dev->dev; struct snd_usb_caiaqdev *cdev = urb->context; unsigned char *buf = urb->transfer_buffer; + unsigned int payload_len; + unsigned int copy_len; if (urb->status || !cdev) { dev_warn(dev, "received EP1 urb->status = %i\n", urb->status); return; } + if (urb->actual_length < 1) + return; + + payload_len = urb->actual_length - 1; switch(buf[0]) { case EP1_CMD_GET_DEVICE_INFO: + if (payload_len < sizeof(struct caiaq_device_spec)) + break; memcpy(&cdev->spec, buf+1, sizeof(struct caiaq_device_spec)); cdev->spec.fw_version = le16_to_cpu(cdev->spec.fw_version); dev_dbg(dev, "device spec (firmware %d): audio: %d in, %d out, " @@ -157,18 +165,21 @@ static void usb_ep1_command_reply_dispatch (struct urb* urb) wake_up(&cdev->ep1_wait_queue); break; case EP1_CMD_AUDIO_PARAMS: + if (payload_len < 1) + break; cdev->audio_parm_answer = buf[1]; wake_up(&cdev->ep1_wait_queue); break; case EP1_CMD_MIDI_READ: + if (urb->actual_length < 3 || urb->actual_length - 3 < buf[2]) + break; snd_usb_caiaq_midi_handle_input(cdev, buf[1], buf + 3, buf[2]); break; case EP1_CMD_READ_IO: if (cdev->chip.usb_id == USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO8DJ)) { - if (urb->actual_length > sizeof(cdev->control_state)) - urb->actual_length = sizeof(cdev->control_state); - memcpy(cdev->control_state, buf + 1, urb->actual_length); + copy_len = min_t(unsigned int, payload_len, sizeof(cdev->control_state)); + memcpy(cdev->control_state, buf + 1, copy_len); wake_up(&cdev->ep1_wait_queue); break; } diff --git a/sound/usb/caiaq/input.c b/sound/usb/caiaq/input.c index eabbf41fdfb2..8d924330c54c 100644 --- a/sound/usb/caiaq/input.c +++ b/sound/usb/caiaq/input.c @@ -203,6 +203,8 @@ static void snd_caiaq_input_read_analog(struct snd_usb_caiaqdev *cdev, switch (cdev->chip.usb_id) { case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL2): + if (len < 6) + return; snd_caiaq_input_report_abs(cdev, ABS_X, buf, 2); snd_caiaq_input_report_abs(cdev, ABS_Y, buf, 0); snd_caiaq_input_report_abs(cdev, ABS_Z, buf, 1); @@ -210,11 +212,15 @@ static void snd_caiaq_input_read_analog(struct snd_usb_caiaqdev *cdev, case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL3): case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER): case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER2): + if (len < 6) + return; snd_caiaq_input_report_abs(cdev, ABS_X, buf, 0); snd_caiaq_input_report_abs(cdev, ABS_Y, buf, 1); snd_caiaq_input_report_abs(cdev, ABS_Z, buf, 2); break; case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1): + if (len < 16) + return; snd_caiaq_input_report_abs(cdev, ABS_HAT0X, buf, 4); snd_caiaq_input_report_abs(cdev, ABS_HAT0Y, buf, 2); snd_caiaq_input_report_abs(cdev, ABS_HAT1X, buf, 6); From 3cda0dfe8c651dcbb9e38977905d3d3b1750c4ab Mon Sep 17 00:00:00 2001 From: Aleksandrova Alyona Date: Wed, 24 Jun 2026 17:48:46 +0300 Subject: [PATCH 0498/1054] RDMA/irdma: Prevent overflows in memory contiguity checks irdma_check_mem_contiguous() and irdma_check_mr_contiguous() verify that PBL entries describe physically contiguous memory ranges. Both functions calculate byte offsets using 32-bit operands. For example, with 4 KiB pages, pg_size * pg_idx overflows 32-bit arithmetic when pg_idx reaches 1048576. In the level-2 check, PBLE_PER_PAGE is 512, so i * pg_size * PBLE_PER_PAGE overflows when i reaches 2048. These values are reachable in the driver. For MRs, palloc->total_cnt comes from iwmr->page_cnt, which is calculated by ib_umem_num_dma_blocks(). The MR size is limited by IRDMA_MAX_MR_SIZE, so a 4 GiB MR with 4 KiB pages can reach page_cnt of 1048576. PBLE resources do not exclude this value either: for gen3, the limit is based on avail_sds * MAX_PBLE_PER_SD, and MAX_PBLE_PER_SD is 0x40000, so 4 SDs are enough for 1048576 PBLEs. Cast one operand to u64 before the multiplications so that the offset calculations are performed in 64-bit arithmetic. Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: b48c24c2d710 ("RDMA/irdma: Implement device supported verb APIs") Signed-off-by: Aleksandrova Alyona Link: https://patch.msgid.link/20260624144846.61242-1-aga@itb.spb.ru Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/irdma/verbs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/hw/irdma/verbs.c b/drivers/infiniband/hw/irdma/verbs.c index be8c5cf12f7f..b7388b41ed95 100644 --- a/drivers/infiniband/hw/irdma/verbs.c +++ b/drivers/infiniband/hw/irdma/verbs.c @@ -2801,7 +2801,7 @@ static bool irdma_check_mem_contiguous(u64 *arr, u32 npages, u32 pg_size) u32 pg_idx; for (pg_idx = 0; pg_idx < npages; pg_idx++) { - if ((*arr + (pg_size * pg_idx)) != arr[pg_idx]) + if ((*arr + ((u64)pg_size * pg_idx)) != arr[pg_idx]) return false; } @@ -2834,7 +2834,7 @@ static bool irdma_check_mr_contiguous(struct irdma_pble_alloc *palloc, for (i = 0; i < lvl2->leaf_cnt; i++, leaf++) { arr = leaf->addr; - if ((*start_addr + (i * pg_size * PBLE_PER_PAGE)) != *arr) + if ((*start_addr + ((u64)i * pg_size * PBLE_PER_PAGE)) != *arr) return false; ret = irdma_check_mem_contiguous(arr, leaf->cnt, pg_size); if (!ret) From 1002500f54b5f60faa6a03636c7865fd4db53ad6 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sun, 28 Jun 2026 20:25:52 -0700 Subject: [PATCH 0499/1054] lib/crypto: md5: Remove support for md5_mod_init_arch() No definitions of md5_mod_init_arch() remain, so remove the code that handles it. Link: https://patch.msgid.link/20260629032552.26100-1-ebiggers@kernel.org Signed-off-by: Eric Biggers --- lib/crypto/md5.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/lib/crypto/md5.c b/lib/crypto/md5.c index 6bf130cfbbf9..3d2b017a0525 100644 --- a/lib/crypto/md5.c +++ b/lib/crypto/md5.c @@ -298,19 +298,5 @@ void hmac_md5_usingrawkey(const u8 *raw_key, size_t raw_key_len, } EXPORT_SYMBOL_GPL(hmac_md5_usingrawkey); -#ifdef md5_mod_init_arch -static int __init md5_mod_init(void) -{ - md5_mod_init_arch(); - return 0; -} -subsys_initcall(md5_mod_init); - -static void __exit md5_mod_exit(void) -{ -} -module_exit(md5_mod_exit); -#endif - MODULE_DESCRIPTION("MD5 and HMAC-MD5 library functions"); MODULE_LICENSE("GPL"); From 8cdeaa50eae8dad34885515f62559ee83e7e8dda Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 5 Jul 2026 14:44:06 -1000 Subject: [PATCH 0500/1054] Linux 7.2-rc2 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index b9c5792c79e0..b4035d3cef26 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ VERSION = 7 PATCHLEVEL = 2 SUBLEVEL = 0 -EXTRAVERSION = -rc1 +EXTRAVERSION = -rc2 NAME = Baby Opossum Posse # *DOCUMENTATION* From 5b4d8e11ba68cd9697de49861a818c75f21e463d Mon Sep 17 00:00:00 2001 From: Mehdi Hassan Date: Sat, 4 Jul 2026 02:35:06 +0000 Subject: [PATCH 0501/1054] smb: client: refactor cifs_revalidate_mapping() to use clear_and_wake_up_bit() In the `skip_invalidate:` path under `cifs_revalidate_mapping()`, the sequence of calls: clear_bit_unlock(); smp_mb__after_atomic(); wake_up_bit(); can be replaced exactly by `clear_and_wake_up_bit()`. The `clear_and_wake_up_bit()` helper function was introduced in 'commit 8236b0ae31c83 ("bdi: wake up concurrent wb_shutdown() callers.")' to replace equivalent instances of this sequence of operations. This substitution has been applied in multiple subsystems. Compile-tested with CONFIG_CIFS=y on x86_64, no new warnings present. Suggested-by: Agatha Isabelle Moreira Link: https://kernelnewbies.org/Beginner%20Cleanup%20and%20Refactor%20Tasks%20by%20Agatha%20Isabelle%20Moreira#task_010 Cc: Agatha Isabelle Moreira Signed-off-by: Mehdi Hassan Signed-off-by: Steve French --- fs/smb/client/inode.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c index 1dbcfd163ff0..2ed1c79c1132 100644 --- a/fs/smb/client/inode.c +++ b/fs/smb/client/inode.c @@ -2812,9 +2812,7 @@ cifs_revalidate_mapping(struct inode *inode) } skip_invalidate: - clear_bit_unlock(CIFS_INO_LOCK, flags); - smp_mb__after_atomic(); - wake_up_bit(flags, CIFS_INO_LOCK); + clear_and_wake_up_bit(CIFS_INO_LOCK, flags); return rc; } From 92ea163c773cb4d0d5eaf103ed80c49d6758b96f Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Sat, 13 Jun 2026 18:16:33 -0400 Subject: [PATCH 0502/1054] NFSD: Prevent post-shutdown use-after-free in NFSD_CMD_UNLOCK_FILESYSTEM The NFSD_CMD_UNLOCK_FILESYSTEM netlink command runs nfsd4_cancel_copy_by_sb() before nfsd_mutex is held and before nn->nfsd_serv is confirmed set, the same pre-mutex ordering the procfs unlock_filesystem path carried. Once nfsd has shut down, nfs4_state_destroy_net() has freed nn->conf_id_hashtbl but left the pointer intact, so the cancel helper iterates freed slab memory as an array of struct list_head and then dereferences a bogus nfs4_client when it takes clp->async_lock. A local administrator holding CAP_SYS_ADMIN can reach this use-after-free by stopping the server and then issuing the command. Move the async COPY cancel into the nfsd_mutex section, after nn->nfsd_serv is confirmed, so every NFSv4 state-table walker on this path observes a running server. Async copies exist only while the server runs, so gating the cancel on nn->nfsd_serv loses nothing. Fixes: 327c5168eff2 ("NFSD: Add NFSD_CMD_UNLOCK_FILESYSTEM netlink command") Cc: stable@vger.kernel.org Reviewed-by: Jeff Layton Link: https://patch.msgid.link/20260613-unlock-filesystem-uaf-v1-2-462b9bec8c84@kernel.org Signed-off-by: Chuck Lever --- fs/nfsd/nfsctl.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index d0486f4a47ba..fa92e31d19d6 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -2340,14 +2340,15 @@ int nfsd_nl_unlock_filesystem_doit(struct sk_buff *skb, if (error) return error; - nfsd4_cancel_copy_by_sb(net, path.dentry->d_sb); error = nlmsvc_unlock_all_by_sb(path.dentry->d_sb); mutex_lock(&nfsd_mutex); - if (nn->nfsd_serv) + if (nn->nfsd_serv) { + nfsd4_cancel_copy_by_sb(net, path.dentry->d_sb); nfsd4_revoke_states(nn, path.dentry->d_sb); - else + } else { error = -EINVAL; + } mutex_unlock(&nfsd_mutex); path_put(&path); From 8669a550c752d86baebc5fdc83b8ff35c4372c0e Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sat, 4 Jul 2026 09:46:09 +0200 Subject: [PATCH 0503/1054] batman-adv: clean untagged VLAN on netdev registration failure When an mesh interface is registered, it creates an untagged struct batadv_meshif_vlan on top of it via the NETDEV_REGISTER notifier. But in this process, another receiver of this notification can veto the registration. The netdev registration will be aborted because of this veto. The register_netdevice() call will try to clean up the net_device using unregister_netdevice_queue() - which only uses the .priv_destructor to free private resources. In this situation, .dellink will not be called. The cleanup of the untagged batadv_meshif_vlan must thefore be done in the destructor to avoid a leak of this object. Cc: stable@vger.kernel.org Fixes: 5d2c05b21337 ("batman-adv: add per VLAN interface attribute framework") Signed-off-by: Sven Eckelmann --- net/batman-adv/main.c | 8 ++++++++ net/batman-adv/mesh-interface.c | 13 ++----------- net/batman-adv/mesh-interface.h | 2 ++ 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c index 8844e40e6a80..67bed3ee77e7 100644 --- a/net/batman-adv/main.c +++ b/net/batman-adv/main.c @@ -259,6 +259,7 @@ int batadv_mesh_init(struct net_device *mesh_iface) void batadv_mesh_free(struct net_device *mesh_iface) { struct batadv_priv *bat_priv = netdev_priv(mesh_iface); + struct batadv_meshif_vlan *vlan; WRITE_ONCE(bat_priv->mesh_state, BATADV_MESH_DEACTIVATING); @@ -273,6 +274,13 @@ void batadv_mesh_free(struct net_device *mesh_iface) batadv_mcast_free(bat_priv); + /* destroy the "untagged" VLAN */ + vlan = batadv_meshif_vlan_get(bat_priv, BATADV_NO_FLAGS); + if (vlan) { + batadv_meshif_destroy_vlan(bat_priv, vlan); + batadv_meshif_vlan_put(vlan); + } + /* Free the TT and the originator tables only after having terminated * all the other depending components which may use these structures for * their purposes. diff --git a/net/batman-adv/mesh-interface.c b/net/batman-adv/mesh-interface.c index 0b75234521b6..fbfd99268de4 100644 --- a/net/batman-adv/mesh-interface.c +++ b/net/batman-adv/mesh-interface.c @@ -595,8 +595,8 @@ int batadv_meshif_create_vlan(struct batadv_priv *bat_priv, unsigned short vid) * @bat_priv: the bat priv with all the mesh interface information * @vlan: the object to remove */ -static void batadv_meshif_destroy_vlan(struct batadv_priv *bat_priv, - struct batadv_meshif_vlan *vlan) +void batadv_meshif_destroy_vlan(struct batadv_priv *bat_priv, + struct batadv_meshif_vlan *vlan) { /* explicitly remove the associated TT local entry because it is marked * with the NOPURGE flag @@ -1091,22 +1091,13 @@ static int batadv_meshif_newlink(struct net_device *dev, static void batadv_meshif_destroy_netlink(struct net_device *mesh_iface, struct list_head *head) { - struct batadv_priv *bat_priv = netdev_priv(mesh_iface); struct batadv_hard_iface *hard_iface; - struct batadv_meshif_vlan *vlan; while (!list_empty(&mesh_iface->adj_list.lower)) { hard_iface = netdev_adjacent_get_private(mesh_iface->adj_list.lower.next); batadv_hardif_disable_interface(hard_iface); } - /* destroy the "untagged" VLAN */ - vlan = batadv_meshif_vlan_get(bat_priv, BATADV_NO_FLAGS); - if (vlan) { - batadv_meshif_destroy_vlan(bat_priv, vlan); - batadv_meshif_vlan_put(vlan); - } - unregister_netdevice_queue(mesh_iface, head); } diff --git a/net/batman-adv/mesh-interface.h b/net/batman-adv/mesh-interface.h index 53756c5a45e0..5e1e83e04ffb 100644 --- a/net/batman-adv/mesh-interface.h +++ b/net/batman-adv/mesh-interface.h @@ -21,6 +21,8 @@ void batadv_interface_rx(struct net_device *mesh_iface, bool batadv_meshif_is_valid(const struct net_device *net_dev); extern struct rtnl_link_ops batadv_link_ops; int batadv_meshif_create_vlan(struct batadv_priv *bat_priv, unsigned short vid); +void batadv_meshif_destroy_vlan(struct batadv_priv *bat_priv, + struct batadv_meshif_vlan *vlan); void batadv_meshif_vlan_release(struct kref *ref); struct batadv_meshif_vlan *batadv_meshif_vlan_get(struct batadv_priv *bat_priv, unsigned short vid); From 27c7d40008231ae4140d35501b60087a9de2d2c3 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Thu, 2 Jul 2026 21:06:23 +0200 Subject: [PATCH 0504/1054] batman-adv: tt: avoid request storms during pending request batadv_send_tt_request() allocates a tt_req_node when none exists for the destination originator node. This should prevent that a multiple TT requests are send at the same time to an originator. But if allocation of the send buffer failed, this request must be cleaned up again. But indicator for such a failure is "ret == false". But the actual implementation is checking for "ret == true". The check must be inverted to not loose the information about the TT request directly after it was attempted to be sent out. This should avoid potential request storms. Cc: stable@vger.kernel.org Fixes: 335fbe0f5d25 ("batman-adv: tvlv - convert tt query packet to use tvlv unicast packets") Signed-off-by: Sven Eckelmann --- net/batman-adv/translation-table.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index 4bfad36a4b70..aae72015645a 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -2971,7 +2971,7 @@ static bool batadv_send_tt_request(struct batadv_priv *bat_priv, out: batadv_hardif_put(primary_if); - if (ret && tt_req_node) { + if (!ret && tt_req_node) { spin_lock_bh(&bat_priv->tt.req_list_lock); if (!hlist_unhashed(&tt_req_node->list)) { hlist_del_init(&tt_req_node->list); From 7a581d9aaba8c82bd6177fa36b2588eea77f6e2b Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 3 Jul 2026 22:27:13 +0200 Subject: [PATCH 0505/1054] batman-adv: tt: prevent TVLV OOB check overflow A TT unicast TVLV contains the number of VLANs stored in it. This number is an u16 and gets multiplied by the size of the struct batadv_tvlv_tt_vlan_data (8 bytes). The size can therefore overflow the u16 used to store the tt_vlan_len. All additional safety checks to prevent out-of-bounds access of the TVLV buffer are invalid due to this overflow. Using size_t prevents this overflow and ensures that the safety checks compare against the actual buffer requirements. Cc: stable@vger.kernel.org Fixes: 7ea7b4a14275 ("batman-adv: make the TT CRC logic VLAN specific") Signed-off-by: Sven Eckelmann --- net/batman-adv/translation-table.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index aae72015645a..dae5e1d8c038 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -4033,7 +4033,8 @@ static int batadv_tt_tvlv_unicast_handler_v1(struct batadv_priv *bat_priv, u16 tvlv_value_len) { struct batadv_tvlv_tt_data *tt_data; - u16 tt_vlan_len, tt_num_entries; + u16 tt_num_entries; + size_t tt_vlan_len; char tt_flag; bool ret; From 6b628425aed49a1c7a4ffc997583840fc582d32b Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 3 Jul 2026 20:28:31 +0200 Subject: [PATCH 0506/1054] batman-adv: frag: free unfragmentable packet The caller of batadv_frag_send_packet() assume that the skb provided to the function are always consumed. But the pre-check for an empty payload or the zero fragment size returned an error without any further actions. A failed pre-check must use the same error handling code as the rest of the function. Cc: stable@vger.kernel.org Fixes: ee75ed88879a ("batman-adv: Fragment and send skbs larger than mtu") Signed-off-by: Sven Eckelmann --- net/batman-adv/fragmentation.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c index 8a006a0473a8..13d4689d332d 100644 --- a/net/batman-adv/fragmentation.c +++ b/net/batman-adv/fragmentation.c @@ -518,8 +518,10 @@ int batadv_frag_send_packet(struct sk_buff *skb, mtu = min_t(unsigned int, mtu, BATADV_FRAG_MAX_FRAG_SIZE); max_fragment_size = mtu - header_size; - if (skb->len == 0 || max_fragment_size == 0) - return -EINVAL; + if (skb->len == 0 || max_fragment_size == 0) { + ret = -EINVAL; + goto free_skb; + } num_fragments = (skb->len - 1) / max_fragment_size + 1; max_fragment_size = (skb->len - 1) / num_fragments + 1; From 353d2c1d5492e53ae34f490a84494124dc3d3531 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 3 Jul 2026 21:04:03 +0200 Subject: [PATCH 0507/1054] batman-adv: frag: fix primary_if leak on failed linearization If the skb has a frag_list, it must be linearized before it can be split using skb_split(). But when this step failed, it must not only free the skb but also take care of the reference to the already found primary_if. Cc: stable@vger.kernel.org Reported-by: Sashiko Fixes: a063f2fba3fa ("batman-adv: Don't skb_split skbuffs with frag_list") Signed-off-by: Sven Eckelmann --- net/batman-adv/fragmentation.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c index 13d4689d332d..2e20a2cb64cb 100644 --- a/net/batman-adv/fragmentation.c +++ b/net/batman-adv/fragmentation.c @@ -547,7 +547,7 @@ int batadv_frag_send_packet(struct sk_buff *skb, */ if (skb_has_frag_list(skb) && __skb_linearize(skb)) { ret = -ENOMEM; - goto free_skb; + goto put_primary_if; } /* Create one header to be copied to all fragments */ From 38eaed28e250895d56f4b7989bd65479a511c5c3 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 3 Jul 2026 20:47:45 +0200 Subject: [PATCH 0508/1054] batman-adv: mcast: avoid OOB read of num_dests header Before the access to struct batadv_tvlv_mcast_tracker's num_dests, it is attempted to check whether enough space is actually in the network header. But instead of using offsetofend() to check for the whole size (2) which must be accessible, offsetof() of is called. The latter is always returning 0. The comparison with the network header length will always return that enough data is available - even when only 1 or 0 bytes are accessible. Instead of using offsetofend(), use the more common check for the whole header. Cc: stable@vger.kernel.org Fixes: 07afe1ba288c ("batman-adv: mcast: implement multicast packet reception and forwarding") Signed-off-by: Sven Eckelmann --- net/batman-adv/multicast_forw.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/net/batman-adv/multicast_forw.c b/net/batman-adv/multicast_forw.c index b8668a80b94a..1404a3b7adfb 100644 --- a/net/batman-adv/multicast_forw.c +++ b/net/batman-adv/multicast_forw.c @@ -927,11 +927,11 @@ static int batadv_mcast_forw_packet(struct batadv_priv *bat_priv, { struct batadv_tvlv_mcast_tracker *mcast_tracker; struct batadv_neigh_node *neigh_node; - unsigned long offset, num_dests_off; struct sk_buff *nexthop_skb; unsigned char *skb_net_hdr; bool local_recv = false; unsigned int tvlv_len; + unsigned long offset; bool xmitted = false; u8 *dest, *next_dest; u16 num_dests; @@ -940,9 +940,8 @@ static int batadv_mcast_forw_packet(struct batadv_priv *bat_priv, /* (at least) TVLV part needs to be linearized */ SKB_LINEAR_ASSERT(skb); - /* check if num_dests is within skb length */ - num_dests_off = offsetof(struct batadv_tvlv_mcast_tracker, num_dests); - if (num_dests_off > skb_network_header_len(skb)) + /* check if batadv_tvlv_mcast_tracker header is within skb length */ + if (sizeof(*mcast_tracker) > skb_network_header_len(skb)) return -EINVAL; skb_net_hdr = skb_network_header(skb); From 98052bdaf6ac1639a63ffc10244eeeab1f62ed2b Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Thu, 2 Jul 2026 19:32:40 +0200 Subject: [PATCH 0509/1054] batman-adv: dat: fix tie-break for candidate selection The original version of the candidate selection for DAT attempted to compare both candidate and max_orig_node to identify which has the smaller MAC address. This comparison is required as tie-break when a hash collision happened. But the used function returned 0 when the function was not equal and a non-zero value when it was equal. As result, the actually selected node was dependent on the order of entries in the orig_hash and not actually on the mac addresses. The last originator in the hash collision would always win. To have a proper ordering, it must diff the actual MAC address bytes and reject the candidate when the diff is not smaller than 0. Cc: stable@vger.kernel.org Fixes: 785ea1144182 ("batman-adv: Distributed ARP Table - create DHT helper functions") Signed-off-by: Sven Eckelmann --- net/batman-adv/distributed-arp-table.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c index c40c9e02391b..a6fe4820f65b 100644 --- a/net/batman-adv/distributed-arp-table.c +++ b/net/batman-adv/distributed-arp-table.c @@ -546,7 +546,7 @@ static bool batadv_is_orig_node_eligible(struct batadv_dat_candidate *res, * the one with the lowest address */ if (tmp_max == max && max_orig_node && - batadv_compare_eth(candidate->orig, max_orig_node->orig)) + memcmp(candidate->orig, max_orig_node->orig, ETH_ALEN) >= 0) goto out; ret = true; From 2538bd3cd1ff5af655908469544ac7b7ae259386 Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Sat, 27 Jun 2026 11:01:17 +0800 Subject: [PATCH 0510/1054] xfrm: clear mode callbacks after failed mode setup xfrm_state_gc_task can run long after a failed IPTFS state setup. In the reproduced case, __xfrm_init_state() cached x->mode_cbs, IPTFS setup returned -ENOMEM before publishing mode_data, and the temporary module reference from xfrm_get_mode_cbs() was dropped immediately. The dead state then kept x->mode_cbs until deferred GC ran after xfrm_iptfs had been unloaded. Clear x->mode_cbs when mode init or clone fails before publishing mode_data. Those states never installed mode-specific state or the long-term IPTFS module pin, so deferred GC has nothing mode-specific to destroy and must not retain a callback table pointer past the temporary lookup reference. The buggy scenario involves two paths, with each column showing the order within that path: failed setup path: 1. cache x->mode_cbs 2. mode setup fails before mode_data 3. drop the temporary module ref 4. dead state keeps x->mode_cbs cached GC/unload path: 1. xfrm_state_put() queues GC work 2. xfrm_iptfs unloads later 3. xfrm_state_gc_task runs 4. GC dereferences stale x->mode_cbs This also covers the failed clone path where clone_state() returns before publishing mode_data. Validation reproduced this kernel report: Kernel panic - not syncing: Fatal exception CONFIG_FAULT_INJECTION_STACKTRACE_FILTER=y failslab_stacktrace_filter matched xfrm_iptfs frames ack_error=-12 FAULT_INJECTION: forcing a failure BUG: unable to handle page fault Workqueue: events xfrm_state_gc_task RIP: xfrm_state_gc_task+0x142/0x650 Modules linked in: esp4_offload xfrm_user [last unloaded: xfrm_iptfs] Kernel panic - not syncing: Fatal exception Fixes: 4b3faf610cc6 ("xfrm: iptfs: add new iptfs xfrm mode impl") Assisted-by: Codex:gpt-5.5 Signed-off-by: Cen Zhang Signed-off-by: Steffen Klassert --- net/xfrm/xfrm_state.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 10e5a1a95fe3..36a4f6793ede 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -2072,8 +2072,11 @@ static struct xfrm_state *xfrm_state_clone_and_setup(struct xfrm_state *orig, x->mode_cbs = orig->mode_cbs; if (x->mode_cbs && x->mode_cbs->clone_state) { - if (x->mode_cbs->clone_state(x, orig)) + if (x->mode_cbs->clone_state(x, orig)) { + if (!x->mode_data) + x->mode_cbs = NULL; goto error; + } } x->props.reqid = m->new_reqid; @@ -3292,6 +3295,8 @@ int __xfrm_init_state(struct xfrm_state *x, struct netlink_ext_ack *extack) if (x->mode_cbs->init_state) err = x->mode_cbs->init_state(x); module_put(x->mode_cbs->owner); + if (err && !x->mode_data) + x->mode_cbs = NULL; } error: return err; From 430ea57d6daf765e88f90046afbfd1e071cb7200 Mon Sep 17 00:00:00 2001 From: Chen YanJun Date: Wed, 1 Jul 2026 11:31:52 +0800 Subject: [PATCH 0511/1054] xfrm: iptfs: propagate SKBFL_SHARED_FRAG in iptfs_skb_add_frags() When iptfs_skb_add_frags() copies frag references from the source frag walk into a new SKB, it increments the page reference count via __skb_frag_ref() but does not propagate SKBFL_SHARED_FRAG to the destination SKB's skb_shinfo->flags. If the source SKB carries shared frags (e.g. from a page-pool backed receive path), the new inner SKB will appear to ESP as having privately owned frags. A subsequent esp_input() call for a nested transport-mode SA then takes the no-COW fast path and decrypts in place, writing over pages that are still referenced by the outer IPTFS SKB. This causes kernel-visible memory corruption and can trigger a panic. All other frag-transfer helpers in the kernel (skb_try_coalesce, skb_gro_receive, __pskb_copy_fclone, skb_shift, skb_segment) correctly propagate SKBFL_SHARED_FRAG; align iptfs_skb_add_frags() with this convention by setting the flag inside the loop immediately after __skb_frag_ref() and nr_frags++, so every exit path that attaches a frag unconditionally propagates SKBFL_SHARED_FRAG. Fixes: 5f2b6a909574 ("xfrm: iptfs: add skb-fragment sharing code") Signed-off-by: Chen YanJun Signed-off-by: Steffen Klassert --- net/xfrm/xfrm_iptfs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/xfrm/xfrm_iptfs.c b/net/xfrm/xfrm_iptfs.c index ad810d1f97c0..597aedeac26e 100644 --- a/net/xfrm/xfrm_iptfs.c +++ b/net/xfrm/xfrm_iptfs.c @@ -480,6 +480,7 @@ static int iptfs_skb_add_frags(struct sk_buff *skb, } __skb_frag_ref(tofrag); shinfo->nr_frags++; + shinfo->flags |= SKBFL_SHARED_FRAG; /* see if we are done */ fraglen = tofrag->len; From 136992de9bb91871084ae52d172610541c76e4d2 Mon Sep 17 00:00:00 2001 From: "Xiang Mei (Microsoft)" Date: Thu, 2 Jul 2026 01:05:16 +0000 Subject: [PATCH 0512/1054] xfrm6: clear dst.dev on error to avoid double netdev_put in xfrm6_fill_dst() On the error path where in6_dev_get(dev) returns NULL, xfrm6_fill_dst() releases the device reference with netdev_put() but leaves xdst->u.dst.dev set. dst_destroy() later calls netdev_put(dst->dev) again, so the same net_device reference is released twice, underflowing its refcount (ref_tracker WARNING + "unregister_netdevice: waiting for to become free"). Clear xdst->u.dst.dev after the netdev_put(), the same way the XFRM device-offload paths xfrm_dev_state_add() and xfrm_dev_policy_add() in net/xfrm/xfrm_device.c NULL ->dev when releasing the reference on error. ref_tracker: reference already released. ref_tracker: allocated in: xfrm6_fill_dst (net/ipv6/xfrm6_policy.c:86) ... udpv6_sendmsg (net/ipv6/udp.c:1696) ... ref_tracker: freed in: xfrm6_fill_dst (net/ipv6/xfrm6_policy.c:90) ... WARNING: lib/ref_tracker.c:322 at ref_tracker_free+0x58b/0x780 dst_destroy (net/core/dst.c:115) rcu_core handle_softirqs ... Fixes: 84c4a9dfbf43 ("xfrm6: release dev before returning error") Reported-by: AutonomousCodeSecurity@microsoft.com Signed-off-by: Xiang Mei (Microsoft) Signed-off-by: Steffen Klassert --- net/ipv6/xfrm6_policy.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c index 125ea9a5b8a0..3b749475f6ed 100644 --- a/net/ipv6/xfrm6_policy.c +++ b/net/ipv6/xfrm6_policy.c @@ -88,6 +88,7 @@ static int xfrm6_fill_dst(struct xfrm_dst *xdst, struct net_device *dev, xdst->u.rt6.rt6i_idev = in6_dev_get(dev); if (!xdst->u.rt6.rt6i_idev) { netdev_put(dev, &xdst->u.dst.dev_tracker); + xdst->u.dst.dev = NULL; return -ENODEV; } From f38f8cce2f7e79775b3db7e8a5eacda04ac908e4 Mon Sep 17 00:00:00 2001 From: "Xiang Mei (Microsoft)" Date: Fri, 3 Jul 2026 05:19:32 +0000 Subject: [PATCH 0513/1054] xfrm: policy: preallocate inexact bins before xfrm_hash_rebuild reinsert xfrm_hash_rebuild()'s first loop preallocates the bins/chains the reinsert loop needs, so the reinsert (after hlist_del_rcu()) cannot allocate or fail. But its guard is inverted: it skips policies with prefixlen < threshold and preallocates for the rest. prefixlen < threshold is exactly when policy_hash_bysel() returns NULL and the reinsert takes the allocating xfrm_policy_inexact_insert() path. So the loop preallocates for the exact policies (which never allocate) and skips the inexact ones, whose bin/node is then allocated GFP_ATOMIC during reinsert. On failure the error path only WARN_ONCE()s and continues, leaving a poisoned bydst node; the next rebuild's hlist_del_rcu() dereferences LIST_POISON2 and takes a GPF. Reachable under memory pressure, deterministic via failslab. Invert the guard so preallocation covers exactly the reinserted policies; the reinsert then allocates nothing and cannot fail. Crash: Oops: general protection fault, probably for non-canonical address 0xfbd59c0000000024: 0000 [#1] SMP KASAN NOPTI KASAN: maybe wild-memory-access in range [0xdead...] ... Workqueue: events xfrm_hash_rebuild RIP: 0010:xfrm_hash_rebuild+0x5b3/0x1190 RAX: dead000000000122 (LIST_POISON2 + offset) ... Call Trace: hlist_del_rcu (include/linux/rculist.h:599) xfrm_hash_rebuild (net/xfrm/xfrm_policy.c:1365) process_one_work (kernel/workqueue.c:3322) worker_thread (kernel/workqueue.c:3486) kthread (kernel/kthread.c:436) ret_from_fork (arch/x86/kernel/process.c:158) ret_from_fork_asm (arch/x86/entry/entry_64.S:245) ... Kernel panic - not syncing: Fatal exception in interrupt Fixes: 24969facd704 ("xfrm: policy: store inexact policies in an rhashtable") Reported-by: AutonomousCodeSecurity@microsoft.com Signed-off-by: Xiang Mei (Microsoft) Reviewed-by: Florian Westphal Signed-off-by: Steffen Klassert --- net/xfrm/xfrm_policy.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 7ef861a0e823..932a313b9460 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -1329,8 +1329,8 @@ static void xfrm_hash_rebuild(struct work_struct *work) } } - if (policy->selector.prefixlen_d < dbits || - policy->selector.prefixlen_s < sbits) + if (policy->selector.prefixlen_d >= dbits && + policy->selector.prefixlen_s >= sbits) continue; bin = xfrm_policy_inexact_alloc_bin(policy, dir); From a707e4127c0f893c7a7703500ab56297a5bd2d51 Mon Sep 17 00:00:00 2001 From: Rafael Beims Date: Wed, 10 Jun 2026 12:00:18 -0300 Subject: [PATCH 0514/1054] wifi: mwifiex: fix roaming to different channel in host_mlme mode When host MLME is enabled, mwifiex_cfg80211_authenticate() transmits the authentication frame on a remain-on-channel (ROC) reservation so that the frame is sent on the target BSS's channel. The ROC is only configured when priv->auth_flag is zero. priv->auth_flag is set to HOST_MLME_AUTH_PENDING when the auth frame is queued and advances to HOST_MLME_AUTH_DONE once authentication completes. It is only cleared back to zero on a disconnect, deauth or timeout path; nothing clears it when an association succeeds. It therefore stays at HOST_MLME_AUTH_DONE for the whole connected session. When the station later roams to a BSS on a different channel, the next authentication finds auth_flag != 0, skips the ROC setup, and the auth frame is transmitted on the currently-associated channel instead of the target's channel. Authentication times out on the new AP and the device stays connected to the original AP. Gate the ROC setup on HOST_MLME_AUTH_PENDING instead of on auth_flag being completely clear. This re-arms the remain-on-channel for every new authentication attempt, while still suppressing a redundant ROC during the multi-frame SAE exchange, where auth_flag stays PENDING between the commit and confirm frames. This change was tested in 3 different devices: Verdin AM62 (IW412 SD-UART) - (16.92.21.p142) Verdin iMX8MM (W8997 SD-SD) - (16.68.1.p197) Verdin iMX8MP (W8997 SD-UART) - (16.92.21.p137) There following loop tests were performed: 1) force roaming between two AP's, one 5GHz and one 2.4GHz, same SSID. Use wpa_cli to trigger the roaming behavior, sleep 2s between iterations. 2) force a disconnection to AP 1 and a connection to AP 2, test scan. Use wpa_cli to trigger the connection changes, sleep 2s between iterations. Each test ran in each device for at least 3 hours. Fixes: 36995892c271 ("wifi: mwifiex: add host mlme for client mode") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Rafael Beims Reviewed-by: Francesco Dolcini Link: https://patch.msgid.link/20260610150021.1018611-1-rafael@beims.me Signed-off-by: Johannes Berg --- drivers/net/wireless/marvell/mwifiex/cfg80211.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c index c9daf893472f..abc703441c5d 100644 --- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c +++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c @@ -4334,7 +4334,7 @@ mwifiex_cfg80211_authenticate(struct wiphy *wiphy, return -EOPNOTSUPP; } - if (!priv->auth_flag) { + if (!(priv->auth_flag & HOST_MLME_AUTH_PENDING)) { ret = mwifiex_remain_on_chan_cfg(priv, HostCmd_ACT_GEN_SET, req->bss->channel, AUTH_TX_DEFAULT_WAIT_TIME); From 44494b0d1d16e76ae805817579eacc801b10ed37 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 11 Jun 2026 15:00:54 +0200 Subject: [PATCH 0515/1054] wifi: mac80211: allocate backup ieee80211_nan_sched_cfg off stack The ieee80211_nan_sched_cfg structure is too large to keep on the per thread stack: net/mac80211/nan.c:251:5: error: stack frame size (1560) exceeds limit (1536) in 'ieee80211_nan_set_local_sched' [-Werror,-Wframe-larger-than] 251 | int ieee80211_nan_set_local_sched(struct ieee80211_sub_if_data *sdata, Allocate this dynamically using kmalloc_obj() to reduce the stack usage of this function to a manageable 344 bytes for the same configuration. Fixes: 589c06e8fdee ("wifi: mac80211: add NAN local schedule support") Signed-off-by: Arnd Bergmann Link: https://patch.msgid.link/20260611130100.3387714-1-arnd@kernel.org Signed-off-by: Johannes Berg --- net/mac80211/nan.c | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/net/mac80211/nan.c b/net/mac80211/nan.c index 1800bb96dd29..19e08661be43 100644 --- a/net/mac80211/nan.c +++ b/net/mac80211/nan.c @@ -253,9 +253,12 @@ int ieee80211_nan_set_local_sched(struct ieee80211_sub_if_data *sdata, { struct ieee80211_nan_channel *sched_idx_to_chan[IEEE80211_NAN_MAX_CHANNELS] = {}; struct ieee80211_nan_sched_cfg *sched_cfg = &sdata->vif.cfg.nan_sched; - struct ieee80211_nan_sched_cfg backup_sched; + struct ieee80211_nan_sched_cfg *backup_sched __free(kfree) = kmalloc_obj(*backup_sched); int ret; + if (!backup_sched) + return -ENOMEM; + if (sched->n_channels > IEEE80211_NAN_MAX_CHANNELS) return -EOPNOTSUPP; @@ -275,13 +278,13 @@ int ieee80211_nan_set_local_sched(struct ieee80211_sub_if_data *sdata, bitmap_zero(sdata->u.nan.removed_channels, IEEE80211_NAN_MAX_CHANNELS); - memcpy(backup_sched.schedule, sched_cfg->schedule, - sizeof(backup_sched.schedule)); - memcpy(backup_sched.channels, sched_cfg->channels, - sizeof(backup_sched.channels)); - memcpy(backup_sched.avail_blob, sched_cfg->avail_blob, - sizeof(backup_sched.avail_blob)); - backup_sched.avail_blob_len = sched_cfg->avail_blob_len; + memcpy(backup_sched->schedule, sched_cfg->schedule, + sizeof(backup_sched->schedule)); + memcpy(backup_sched->channels, sched_cfg->channels, + sizeof(backup_sched->channels)); + memcpy(backup_sched->avail_blob, sched_cfg->avail_blob, + sizeof(backup_sched->avail_blob)); + backup_sched->avail_blob_len = sched_cfg->avail_blob_len; memcpy(sched_cfg->avail_blob, sched->nan_avail_blob, sched->nan_avail_blob_len); @@ -380,17 +383,17 @@ int ieee80211_nan_set_local_sched(struct ieee80211_sub_if_data *sdata, if (!chan_def->chan) continue; - if (!cfg80211_chandef_identical(&backup_sched.channels[i].chanreq.oper, + if (!cfg80211_chandef_identical(&backup_sched->channels[i].chanreq.oper, chan_def)) ieee80211_nan_remove_channel(sdata, &sched_cfg->channels[i]); } /* Re-add all backed up channels */ - for (int i = 0; i < ARRAY_SIZE(backup_sched.channels); i++) { + for (int i = 0; i < ARRAY_SIZE(backup_sched->channels); i++) { struct ieee80211_nan_channel *chan = &sched_cfg->channels[i]; - *chan = backup_sched.channels[i]; + *chan = backup_sched->channels[i]; /* * For deferred update, no channels were removed and the channel @@ -421,11 +424,11 @@ int ieee80211_nan_set_local_sched(struct ieee80211_sub_if_data *sdata, } } - memcpy(sched_cfg->schedule, backup_sched.schedule, - sizeof(backup_sched.schedule)); - memcpy(sched_cfg->avail_blob, backup_sched.avail_blob, - sizeof(backup_sched.avail_blob)); - sched_cfg->avail_blob_len = backup_sched.avail_blob_len; + memcpy(sched_cfg->schedule, backup_sched->schedule, + sizeof(backup_sched->schedule)); + memcpy(sched_cfg->avail_blob, backup_sched->avail_blob, + sizeof(backup_sched->avail_blob)); + sched_cfg->avail_blob_len = backup_sched->avail_blob_len; sched_cfg->deferred = false; bitmap_zero(sdata->u.nan.removed_channels, IEEE80211_NAN_MAX_CHANNELS); From d78a407bad6f500884a8606aea1a5a9207be4030 Mon Sep 17 00:00:00 2001 From: Rafael Beims Date: Fri, 12 Jun 2026 09:25:46 -0300 Subject: [PATCH 0516/1054] wifi: mwifiex: fix permanently busy scans after multiple roam iterations In order for the firmware to sleep, the driver has to confirm a previously received sleep request. The normal sequence of evets goes like this: EVENT_SLEEP -> adapter->ps_state = PS_STATE_PRE_SLEEP -> sleep-confirm -> SLEEP -> EVENT_AWAKE -> AWAKE. Before sending the sleep-confirm command, the driver must make sure there are no commands either running or waiting to be completed. mwifiex_ret_802_11_associate() unconditionally sets ps_state = PS_STATE_AWAKE when it processes the association command response, outside of the normal powersave management flow. If EVENT_SLEEP arrives while the association command is in flight, ps_state is PRE_SLEEP when the association command response is parsed, and the forced AWAKE overwrites it. The deferred sleep-confirm is never sent. A subsequent scan_start command is correctly acknowledged, but the firmware doesn't generate scan_result events. The scan request never finishes, and additional requests from userspace fail with -EBUSY. After testing on both IW412 and W8997, I could only trigger the bug on the IW412 and observed the firmwares behave differently. On the IW412 the firmware still sends EVENT_SLEEP while the authentication / association process is ongoing. A W8997 under the same conditions seems to suppress power-save for the duration of the association, so PRE_SLEEP never coincided with the association response even after extended periods of testing using the loops described below (>12hours). On the IW412, the delay between commands that triggers an EVENT_SLEEP was empirically determined to be ~20ms. This delay can naturally occur when the driver is outputting debugging information (debug_mask = 0x00000037), in which situation the busy scans issue is repeatable while running "test 1)" as described below. If the delay between commands is less than ~20ms, the firmware stays awake and the issue was not reproducible running the same test. The host_mlme=false path also behaves differently. In this case, the entire authentication / association transaction is executed by one command (HostCmd_CMD_802_11_ASSOCIATE), and the firmware doesn't emit EVENT_SLEEP while the command is running. Remove the assignment so the ps_state is only manipulated in the paths that are related to powersave event handling and on the main workqueue for correct sleep confirmation. The following loop tests were performed (with debugging output enabled): 1) force roaming between two AP's, one 5GHz and one 2.4GHz, same SSID. Use wpa_cli to trigger the roaming behavior, sleep 2s between iterations. 2) force a disconnection to AP 1 and a connection to AP 2, test scan. Use wpa_cli to trigger the connection changes, sleep 2s between iterations. Each test ran in each device for at least 3 hours. Fixes: 5e6e3a92b9a4 ("wireless: mwifiex: initial commit for Marvell mwifiex driver") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Rafael Beims Reviewed-by: Jeff Chen Link: https://patch.msgid.link/20260612122547.1586872-2-rafael@beims.me Signed-off-by: Johannes Berg --- drivers/net/wireless/marvell/mwifiex/join.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/wireless/marvell/mwifiex/join.c b/drivers/net/wireless/marvell/mwifiex/join.c index 5a1a0287c1d5..b48f7febaf03 100644 --- a/drivers/net/wireless/marvell/mwifiex/join.c +++ b/drivers/net/wireless/marvell/mwifiex/join.c @@ -736,7 +736,6 @@ int mwifiex_ret_802_11_associate(struct mwifiex_private *priv, /* Send a Media Connected event, according to the Spec */ priv->media_connected = true; - priv->adapter->ps_state = PS_STATE_AWAKE; priv->adapter->pps_uapsd_mode = false; priv->adapter->tx_lock_flag = false; From 536fb3d739d75a03cb318c0c6fe799425cfea501 Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Fri, 19 Jun 2026 15:31:04 +0800 Subject: [PATCH 0517/1054] wifi: rt2x00: avoid full teardown before work setup in probe rt2x00lib_probe_dev() uses the full rt2x00lib_remove_dev() teardown for all probe failures. However, drv_data allocation and workqueue allocation can fail before intf_work, autowakeup_work and sleep_work have been initialized. Do not enter the full remove path until the probe has reached the point where those work items are set up. Return directly for drv_data allocation failure, and use a small early cleanup path for workqueue allocation failure. This issue was found by our static analysis tool and then confirmed by manual review of rt2x00lib_probe_dev() and rt2x00lib_remove_dev(). The early probe exits should not call a common teardown path that assumes the later work setup has already completed. A QEMU PoC forced alloc_ordered_workqueue() to fail before the work initializers are reached. The resulting fail path entered rt2x00lib_remove_dev(), and DEBUG_OBJECTS reported invalid work drains with rt2x00lib_probe_dev() and rt2x00lib_remove_dev() in the stack. Fixes: 1ebbc48520a0 ("rt2x00: Introduce concept of driver data in struct rt2x00_dev.") Fixes: 0439f5367c8d ("rt2x00: Move TX/RX work into dedicated workqueue") Cc: stable@vger.kernel.org Signed-off-by: Runyu Xiao Link: https://patch.msgid.link/20260619073104.1809161-1-runyu.xiao@seu.edu.cn Signed-off-by: Johannes Berg --- drivers/net/wireless/ralink/rt2x00/rt2x00dev.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c index 82fb230a73bb..4d94b7062f44 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c @@ -1388,7 +1388,7 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev) GFP_KERNEL); if (!rt2x00dev->drv_data) { retval = -ENOMEM; - goto exit; + return retval; } } @@ -1422,7 +1422,7 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev) alloc_ordered_workqueue("%s", 0, wiphy_name(rt2x00dev->hw->wiphy)); if (!rt2x00dev->workqueue) { retval = -ENOMEM; - goto exit; + goto exit_free_drv_data; } INIT_WORK(&rt2x00dev->intf_work, rt2x00lib_intf_scheduled); @@ -1494,6 +1494,14 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev) exit: rt2x00lib_remove_dev(rt2x00dev); + return retval; + +exit_free_drv_data: + clear_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags); + + kfree(rt2x00dev->drv_data); + rt2x00dev->drv_data = NULL; + return retval; } EXPORT_SYMBOL_GPL(rt2x00lib_probe_dev); From ea588e4c7484ea883d3dab308f1f2c2f0f51f7d6 Mon Sep 17 00:00:00 2001 From: Rong Zhang Date: Mon, 6 Jul 2026 02:19:31 +0800 Subject: [PATCH 0518/1054] ALSA: usb-audio: Fix imbalance per-channel volume of sticky mixers I accidentally made an off-by-a-line mistake when mimicking other code paths that set all channels. The mistake breaks sticky mixers with multiple channels. I didn't realize this mistake at that time, as my device's mixer is single-channel. Fix it, so that per-channel volume of sticky mixers is balanced. Fixes: aa2f4addab44 ("ALSA: usb-audio: Set the value of potential sticky mixers to maximum") Signed-off-by: Rong Zhang Link: https://patch.msgid.link/20260706-uac-sticky-channels-fix-v1-1-92741c538283@rong.moe Signed-off-by: Takashi Iwai --- sound/usb/mixer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index b4c855c25eef..703c118f9d4e 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -1473,8 +1473,8 @@ static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval, if (!cval->cmask) { snd_usb_set_cur_mix_value(cval, 0, 0, cval->max); } else { + idx = 0; 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++; From c845febafd92b2056abc0af541c1ad85785f1353 Mon Sep 17 00:00:00 2001 From: Malaya Kumar Rout Date: Sun, 5 Jul 2026 22:40:38 +0530 Subject: [PATCH 0519/1054] selftests/alsa: Fix format specifier and function mismatch in mixer-test In the ctl_value_index_valid() function, when validating 64-bit integer controls (SND_CTL_ELEM_TYPE_INTEGER64), the error message for maximum value validation has two bugs: 1. Uses snd_ctl_elem_info_get_max() instead of the 64-bit variant snd_ctl_elem_info_get_max64() 2. Uses %ld format specifier instead of %lld for long long type This is inconsistent with the minimum value validation (line 335-339) which correctly uses get_min64() and %lld. The mismatch can cause: - Incorrect maximum values being reported on some architectures - Undefined behavior due to format specifier mismatch - Potential crashes when printing the error message Fix by using snd_ctl_elem_info_get_max64() and %lld format specifier to match the data type and be consistent with the minimum value check. Signed-off-by: Malaya Kumar Rout Link: https://patch.msgid.link/20260705171038.171966-1-malayarout91@gmail.com Signed-off-by: Takashi Iwai --- tools/testing/selftests/alsa/mixer-test.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/alsa/mixer-test.c b/tools/testing/selftests/alsa/mixer-test.c index c5ae2a026ce9..a329f901c5ed 100644 --- a/tools/testing/selftests/alsa/mixer-test.c +++ b/tools/testing/selftests/alsa/mixer-test.c @@ -340,9 +340,9 @@ static bool ctl_value_index_valid(struct ctl_data *ctl, } if (int64_val > snd_ctl_elem_info_get_max64(ctl->info)) { - ksft_print_msg("%s.%d value %lld more than maximum %ld\n", + ksft_print_msg("%s.%d value %lld more than maximum %lld\n", ctl->name, index, int64_val, - snd_ctl_elem_info_get_max(ctl->info)); + snd_ctl_elem_info_get_max64(ctl->info)); return false; } From db4a79713ed8e252d5e4edf6eaaa80948b6855a2 Mon Sep 17 00:00:00 2001 From: Andreas Kemnade Date: Sat, 4 Jul 2026 10:40:54 +0200 Subject: [PATCH 0520/1054] gpios: palmas: add .get_direction() op Accessing debug/gpio is quite noisy without a get_direction() implementation. To calm that down add an implementation. Fixes: 3d50a2785271 ("gpio: palmas: Add support for Palmas GPIO") Cc: stable@vger.kernel.org Reviewed-by: Linus Walleij Signed-off-by: Andreas Kemnade Link: https://patch.msgid.link/20260704-palmas-getdirection-v2-1-2fd85fee3832@kemnade.info Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-palmas.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/gpio/gpio-palmas.c b/drivers/gpio/gpio-palmas.c index e377f6dd4ccf..e64ee0487718 100644 --- a/drivers/gpio/gpio-palmas.c +++ b/drivers/gpio/gpio-palmas.c @@ -116,6 +116,24 @@ static int palmas_gpio_input(struct gpio_chip *gc, unsigned offset) return ret; } +static int palmas_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) +{ + struct palmas_gpio *pg = gpiochip_get_data(gc); + struct palmas *palmas = pg->palmas; + unsigned int val; + unsigned int reg; + int ret; + int gpio16 = (offset/8); + + offset %= 8; + reg = (gpio16) ? PALMAS_GPIO_DATA_DIR2 : PALMAS_GPIO_DATA_DIR; + ret = palmas_read(palmas, PALMAS_GPIO_BASE, reg, &val); + if (ret) + return ret; + + return (val & BIT(offset)) ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN; +} + static int palmas_gpio_to_irq(struct gpio_chip *gc, unsigned offset) { struct palmas_gpio *pg = gpiochip_get_data(gc); @@ -165,6 +183,7 @@ static int palmas_gpio_probe(struct platform_device *pdev) palmas_gpio->gpio_chip.can_sleep = true; palmas_gpio->gpio_chip.direction_input = palmas_gpio_input; palmas_gpio->gpio_chip.direction_output = palmas_gpio_output; + palmas_gpio->gpio_chip.get_direction = palmas_gpio_get_direction; palmas_gpio->gpio_chip.to_irq = palmas_gpio_to_irq; palmas_gpio->gpio_chip.set = palmas_gpio_set; palmas_gpio->gpio_chip.get = palmas_gpio_get; From a0a558ca7e75b49e71f8c545c30e8c005e6e4e2f Mon Sep 17 00:00:00 2001 From: Shigeru Yoshida Date: Wed, 1 Jul 2026 01:46:20 +0900 Subject: [PATCH 0521/1054] qede: fix off-by-one in BD ring consumption on build_skb failure qede_rx_build_skb() and qede_tpa_rx_build_skb() do not check for a NULL return from qede_build_skb(). When it returns NULL under memory pressure, the functions still consume a BD from the ring before returning NULL. The callers then recycle additional BDs, resulting in one extra BD being consumed (off-by-one). This desynchronizes the BD ring, which can corrupt DMA page reference counts and lead to SLUB freelist corruption. Commit 4e910dbe3650 ("qede: confirm skb is allocated before using") added a NULL check inside qede_build_skb() to prevent a NULL pointer dereference, but did not address the missing NULL checks in the callers, making this off-by-one reachable. Fix this by adding NULL checks for the return value of qede_build_skb() in both qede_rx_build_skb() and qede_tpa_rx_build_skb(), returning NULL immediately before any BD ring manipulation. Fixes: 8a8633978b84 ("qede: Add build_skb() support.") Signed-off-by: Shigeru Yoshida Reviewed-by: Jamie Bainbridge Link: https://patch.msgid.link/20260630164623.3152625-1-syoshida@redhat.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/qlogic/qede/qede_fp.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/ethernet/qlogic/qede/qede_fp.c b/drivers/net/ethernet/qlogic/qede/qede_fp.c index 33e18bb69774..c11e0d8f98aa 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_fp.c +++ b/drivers/net/ethernet/qlogic/qede/qede_fp.c @@ -765,6 +765,9 @@ qede_tpa_rx_build_skb(struct qede_dev *edev, struct sk_buff *skb; skb = qede_build_skb(rxq, bd, len, pad); + if (unlikely(!skb)) + return NULL; + bd->page_offset += rxq->rx_buf_seg_size; if (bd->page_offset == PAGE_SIZE) { @@ -812,6 +815,8 @@ qede_rx_build_skb(struct qede_dev *edev, } skb = qede_build_skb(rxq, bd, len, pad); + if (unlikely(!skb)) + return NULL; if (unlikely(qede_realloc_rx_buffer(rxq, bd))) { /* Incr page ref count to reuse on allocation failure so From dd6a23bac306b7aa322e0aaccb60c6e32a198fb3 Mon Sep 17 00:00:00 2001 From: Nirmoy Das Date: Tue, 30 Jun 2026 09:51:57 -0700 Subject: [PATCH 0522/1054] selftests: net: make busywait timeout clock portable loopy_wait() expects millisecond timestamps. However, Ubuntu Resolute can use uutils date, where `date -u +%s%3N` returns seconds plus full nanoseconds instead of a 3-digit millisecond field. This makes busywait expire too early and can make vlan_bridge_binding.sh read a stale operstate. Fixes: 25ae948b4478 ("selftests/net: add lib.sh") Cc: stable@vger.kernel.org # 6.8+ Link: https://github.com/uutils/coreutils/issues/11658 Signed-off-by: Nirmoy Das Link: https://patch.msgid.link/20260630165157.3814871-1-nirmoyd@nvidia.com Signed-off-by: Paolo Abeni --- tools/testing/selftests/net/lib.sh | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/net/lib.sh b/tools/testing/selftests/net/lib.sh index b3827b43782b..d46d2cec89e4 100644 --- a/tools/testing/selftests/net/lib.sh +++ b/tools/testing/selftests/net/lib.sh @@ -70,12 +70,33 @@ ksft_exit_status_merge() $ksft_xfail $ksft_pass $ksft_skip $ksft_fail } +timestamp_ms() +{ + local now + local seconds + local nanoseconds + + now=$(date -u +%s:%N) || return + seconds=${now%:*} + nanoseconds=${now#*:} + + if [[ $nanoseconds =~ ^[0-9]+$ ]]; then + nanoseconds=${nanoseconds:0:9} + else + nanoseconds=0 + fi + + echo $((seconds * 1000 + 10#$nanoseconds / 1000000)) +} + loopy_wait() { local sleep_cmd=$1; shift local timeout_ms=$1; shift + local start_time + local current_time - local start_time="$(date -u +%s%3N)" + start_time=$(timestamp_ms) || return while true do local out @@ -84,7 +105,7 @@ loopy_wait() return 0 fi - local current_time="$(date -u +%s%3N)" + current_time=$(timestamp_ms) || return if ((current_time - start_time > timeout_ms)); then echo -n "$out" return 1 From f0f1887a9e30712a1df03e152dce6fb91344b1f3 Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Tue, 30 Jun 2026 10:41:09 -0700 Subject: [PATCH 0523/1054] net: qualcomm: rmnet: validate MAP frame length before ingress parsing When ingress deaggregation is disabled, rmnet_map_ingress_handler() passes the skb straight to __rmnet_map_ingress_handler(), skipping the length validation that rmnet_map_deaggregate() performs on the aggregated path. The parser then dereferences the MAP header and csum header/trailer based on the on-wire pkt_len without checking skb->len, so a short frame is read out of bounds: BUG: KASAN: slab-out-of-bounds in rmnet_map_checksum_downlink_packet Read of size 1 at addr ffff88801118ed00 by task exploit/147 Call Trace: ... rmnet_map_checksum_downlink_packet (drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c:413) __rmnet_map_ingress_handler (drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c:96) rmnet_rx_handler (drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c:129) __netif_receive_skb_core.constprop.0 (net/core/dev.c:6089) netif_receive_skb (net/core/dev.c:6460) tun_get_user (drivers/net/tun.c:1955) tun_chr_write_iter (drivers/net/tun.c:2001) vfs_write (fs/read_write.c:688) ksys_write (fs/read_write.c:740) do_syscall_64 (arch/x86/entry/syscall_64.c:94) ... Factor that validation out of rmnet_map_deaggregate() into rmnet_map_validate_packet_len() and run it on the no-aggregation path too. The MAP header is bounds-checked first, since this path can receive a frame shorter than the header. Fixes: ceed73a2cf4a ("drivers: net: ethernet: qualcomm: rmnet: Initial implementation") Reported-by: Weiming Shi Suggested-by: Subash Abhinov Kasiviswanathan Signed-off-by: Xiang Mei Reviewed-by: Subash Abhinov Kasiviswanathan Link: https://patch.msgid.link/20260630174110.2003121-1-xmei5@asu.edu Signed-off-by: Paolo Abeni --- .../ethernet/qualcomm/rmnet/rmnet_handlers.c | 5 +- .../net/ethernet/qualcomm/rmnet/rmnet_map.h | 1 + .../ethernet/qualcomm/rmnet/rmnet_map_data.c | 78 ++++++++++--------- 3 files changed, 48 insertions(+), 36 deletions(-) diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c index 9f3479500f85..d055a2628d8c 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c @@ -126,7 +126,10 @@ rmnet_map_ingress_handler(struct sk_buff *skb, consume_skb(skb); } else { - __rmnet_map_ingress_handler(skb, port); + if (rmnet_map_validate_packet_len(skb, port)) + __rmnet_map_ingress_handler(skb, port); + else + kfree_skb(skb); } } diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h index b70284095568..60ca8b780c88 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h @@ -59,5 +59,6 @@ void rmnet_map_tx_aggregate_init(struct rmnet_port *port); void rmnet_map_tx_aggregate_exit(struct rmnet_port *port); void rmnet_map_update_ul_agg_config(struct rmnet_port *port, u32 size, u32 count, u32 time); +u32 rmnet_map_validate_packet_len(struct sk_buff *skb, struct rmnet_port *port); #endif /* _RMNET_MAP_H_ */ diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c index 8b4640c5d61e..305ae15ae8f3 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c @@ -333,6 +333,47 @@ struct rmnet_map_header *rmnet_map_add_map_header(struct sk_buff *skb, return map_header; } +u32 rmnet_map_validate_packet_len(struct sk_buff *skb, struct rmnet_port *port) +{ + struct rmnet_map_v5_csum_header *next_hdr = NULL; + struct rmnet_map_header *maph; + void *data = skb->data; + u32 packet_len; + + if (skb->len < sizeof(*maph)) + return 0; + + maph = (struct rmnet_map_header *)skb->data; + + /* Some hardware can send us empty frames. Catch them */ + if (!maph->pkt_len) + return 0; + + packet_len = ntohs(maph->pkt_len) + sizeof(*maph); + + if (port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV4) { + packet_len += sizeof(struct rmnet_map_dl_csum_trailer); + } else if ((port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV5) && + !(maph->flags & MAP_CMD_FLAG)) { + /* Mapv5 data pkt without csum hdr is invalid */ + if (!(maph->flags & MAP_NEXT_HEADER_FLAG)) + return 0; + + packet_len += sizeof(*next_hdr); + next_hdr = data + sizeof(*maph); + } + + if (skb->len < packet_len) + return 0; + + if (next_hdr && + u8_get_bits(next_hdr->header_info, MAPV5_HDRINFO_HDR_TYPE_FMASK) != + RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD) + return 0; + + return packet_len; +} + /* Deaggregates a single packet * A whole new buffer is allocated for each portion of an aggregated frame. * Caller should keep calling deaggregate() on the source skb until 0 is @@ -342,46 +383,13 @@ struct rmnet_map_header *rmnet_map_add_map_header(struct sk_buff *skb, struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb, struct rmnet_port *port) { - struct rmnet_map_v5_csum_header *next_hdr = NULL; - struct rmnet_map_header *maph; - void *data = skb->data; struct sk_buff *skbn; - u8 nexthdr_type; u32 packet_len; - if (skb->len == 0) + packet_len = rmnet_map_validate_packet_len(skb, port); + if (!packet_len) return NULL; - maph = (struct rmnet_map_header *)skb->data; - packet_len = ntohs(maph->pkt_len) + sizeof(*maph); - - if (port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV4) { - packet_len += sizeof(struct rmnet_map_dl_csum_trailer); - } else if (port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV5) { - if (!(maph->flags & MAP_CMD_FLAG)) { - packet_len += sizeof(*next_hdr); - if (maph->flags & MAP_NEXT_HEADER_FLAG) - next_hdr = data + sizeof(*maph); - else - /* Mapv5 data pkt without csum hdr is invalid */ - return NULL; - } - } - - if (((int)skb->len - (int)packet_len) < 0) - return NULL; - - /* Some hardware can send us empty frames. Catch them */ - if (!maph->pkt_len) - return NULL; - - if (next_hdr) { - nexthdr_type = u8_get_bits(next_hdr->header_info, - MAPV5_HDRINFO_HDR_TYPE_FMASK); - if (nexthdr_type != RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD) - return NULL; - } - skbn = alloc_skb(packet_len + RMNET_MAP_DEAGGR_SPACING, GFP_ATOMIC); if (!skbn) return NULL; From 8b519cbcabe836a441369fbec1a8a6518a709251 Mon Sep 17 00:00:00 2001 From: Jamal Hadi Salim Date: Wed, 1 Jul 2026 12:19:12 -0400 Subject: [PATCH 0524/1054] net/sched: act_pedit: fix TOCTOU heap OOB write in tc offload There is a TOCTOU race condition in flower lockless approach between sizing a flow_rule buffer and filling it. zdi-disclosures@trendmicro.com reports: The cls_flower classifier operates with TCF_PROTO_OPS_DOIT_UNLOCKED (fl_change runs without RTNL), while RTM_NEWACTION holds RTNL, so the independent locking domains make the race reachable in practice. KASAN confirms: BUG: KASAN: slab-out-of-bounds in tcf_pedit_offload_act_setup+0x81b/0x930 Write of size 4 at addr ffff888001f27520 by task poc-toctou/312 The buggy address is located 0 bytes to the right of allocated 288-byte region [ffff888001f27400, ffff888001f27520) (cache kmalloc-512) Note: The result is a heap OOB write attacker-controlled content into the adjacent slab object (requires CAP_NET_ADMIN). The fix introduces reading tcfp_nkeys under act->tcfa_lock in all places using a new tcf_pedit_nkeys_locked() which replaces the old tcf_pedit_nkeys(). Additionally we close the remaining TOCTOU window between the sizing read and the fill reads by more careful accounting. Rather than silently truncating the key count, which leads to incorrect action semantics offloaded to hardware and secondary OOB writes if the remaining capacity is zero or consumed by prior actions, we enforce remaining capacity checks and return -ENOSPC if the required space exceeds the remaining capacity. Fixes: 71d0ed7079df ("net/act_pedit: Support using offset relative to the conventional network headers") Reported-by: zdi-disclosures@trendmicro.com Tested-by: Victor Nogueira Signed-off-by: Jamal Hadi Salim Link: https://patch.msgid.link/20260701161912.125355-1-jhs@mojatatu.com Signed-off-by: Paolo Abeni --- include/net/tc_act/tc_pedit.h | 18 ++++++++---------- net/sched/act_api.c | 13 +++++++++---- net/sched/act_pedit.c | 13 +++++++++++-- net/sched/cls_api.c | 22 +++++++++++++++++----- 4 files changed, 45 insertions(+), 21 deletions(-) diff --git a/include/net/tc_act/tc_pedit.h b/include/net/tc_act/tc_pedit.h index cb7b82f2cbc7..97754ea0a827 100644 --- a/include/net/tc_act/tc_pedit.h +++ b/include/net/tc_act/tc_pedit.h @@ -37,17 +37,15 @@ static inline bool is_tcf_pedit(const struct tc_action *a) return false; } -static inline int tcf_pedit_nkeys(const struct tc_action *a) +/* Must be called with act->tcfa_lock held to ensure consistency of parallel + * reads of the same action's pedit keys (e.g. flow_offload count vs fill). + * Note, this is only used for pedit offload. + */ +static inline int tcf_pedit_nkeys_locked(const struct tc_action *a) { - struct tcf_pedit_parms *parms; - int nkeys; - - rcu_read_lock(); - parms = to_pedit_parms(a); - nkeys = parms->tcfp_nkeys; - rcu_read_unlock(); - - return nkeys; + lockdep_assert_held(&a->tcfa_lock); + return rcu_dereference_protected(to_pedit(a)->parms, + lockdep_is_held(&a->tcfa_lock))->tcfp_nkeys; } static inline u32 tcf_pedit_htype(const struct tc_action *a, int index) diff --git a/net/sched/act_api.c b/net/sched/act_api.c index b68be143a067..f141634df214 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -148,10 +148,15 @@ static void offload_action_hw_count_dec(struct tc_action *act, static unsigned int tcf_offload_act_num_actions_single(struct tc_action *act) { - if (is_tcf_pedit(act)) - return tcf_pedit_nkeys(act); - else - return 1; + unsigned int count; + + if (is_tcf_pedit(act)) { + spin_lock_bh(&act->tcfa_lock); + count = tcf_pedit_nkeys_locked(act); + spin_unlock_bh(&act->tcfa_lock); + return count; + } + return 1; } static bool tc_act_skip_hw(u32 flags) diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c index 0d652dea4a69..d4d47a9921f4 100644 --- a/net/sched/act_pedit.c +++ b/net/sched/act_pedit.c @@ -567,9 +567,18 @@ static int tcf_pedit_offload_act_setup(struct tc_action *act, void *entry_data, { if (bind) { struct flow_action_entry *entry = entry_data; + int nkeys = tcf_pedit_nkeys_locked(act); int k; - for (k = 0; k < tcf_pedit_nkeys(act); k++) { + /* If the required keys exceed the remaining capacity return + * -ENOSPC to abort the offload and fallback to software. + */ + if (nkeys > *index_inc) { + NL_SET_ERR_MSG_MOD(extack, "Not enough space to offload all pedit keys"); + return -ENOSPC; + } + + for (k = 0; k < nkeys; k++) { switch (tcf_pedit_cmd(act, k)) { case TCA_PEDIT_KEY_EX_CMD_SET: entry->id = FLOW_ACTION_MANGLE; @@ -606,7 +615,7 @@ static int tcf_pedit_offload_act_setup(struct tc_action *act, void *entry_data, return -EOPNOTSUPP; } - for (k = 1; k < tcf_pedit_nkeys(act); k++) { + for (k = 1; k < tcf_pedit_nkeys_locked(act); k++) { if (cmd != tcf_pedit_cmd(act, k)) { NL_SET_ERR_MSG_MOD(extack, "Unsupported pedit command offload"); return -EOPNOTSUPP; diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index 3e67600a4a1a..ffeea6db8337 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -3886,12 +3886,21 @@ int tc_setup_action(struct flow_action *flow_action, entry = &flow_action->entries[j]; spin_lock_bh(&act->tcfa_lock); + + /* Abort the offload if we have exhausted the allocated capacity */ + if (j >= flow_action->num_entries) { + NL_SET_ERR_MSG_MOD(extack, "Flow action buffer overflow"); + err = -ENOSPC; + goto err_out_locked; + } + err = tcf_act_get_user_cookie(entry, act); if (err) goto err_out_locked; - index = 0; - err = tc_setup_offload_act(act, entry, &index, extack); + index = flow_action->num_entries - j; + err = tc_setup_offload_act(act, entry, &index, + extack); if (err) goto err_out_locked; @@ -3945,10 +3954,13 @@ unsigned int tcf_exts_num_actions(struct tcf_exts *exts) int i; tcf_exts_for_each_action(i, act, exts) { - if (is_tcf_pedit(act)) - num_acts += tcf_pedit_nkeys(act); - else + if (is_tcf_pedit(act)) { + spin_lock_bh(&act->tcfa_lock); + num_acts += tcf_pedit_nkeys_locked(act); + spin_unlock_bh(&act->tcfa_lock); + } else { num_acts++; + } } return num_acts; } From 9d160b35cc34a2ba8229d07651468a7848325135 Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Tue, 30 Jun 2026 11:32:27 -0700 Subject: [PATCH 0525/1054] net/smc: fix UAF in smc_cdc_rx_handler() by pinning the socket smc_cdc_rx_handler() looks up the connection by token under the link group's conns_lock, drops the lock, and then dereferences conn and the smc_sock derived from it, ending in sock_hold(&smc->sk) inside smc_cdc_msg_recv(). No reference is held across the lock release. The only reference pinning the socket while the connection is discoverable in the link group is taken in smc_lgr_register_conn() (sock_hold) and dropped in __smc_lgr_unregister_conn() (sock_put), both under conns_lock. Once the handler drops conns_lock, a concurrent close() -> smc_release() -> smc_conn_free() -> smc_lgr_unregister_conn() can drop that reference and free the smc_sock, so the handler's later sock_hold() runs on freed memory: WARNING: lib/refcount.c:25 at refcount_warn_saturate Workqueue: rxe_wq do_work refcount_warn_saturate (lib/refcount.c:25) smc_cdc_msg_recv (net/smc/smc_cdc.c:430) smc_cdc_rx_handler (net/smc/smc_cdc.c:502) smc_wr_rx_tasklet_fn (net/smc/smc_wr.c:445) tasklet_action_common (kernel/softirq.c:938) handle_softirqs (kernel/softirq.c:622) Kernel panic - not syncing: panic_on_warn set Only SMC-R is affected. The SMC-D receive tasklet is stopped by tasklet_kill(&conn->rx_tsklet) in smc_conn_free() before the connection is unregistered, so it cannot run concurrently with the free. Take the socket reference while still holding conns_lock, so the registration reference can no longer be the last one, and drop it once the handler is done. Fixes: d7b0e37c1ac1 ("net/smc: restructure CDC message reception") Reported-by: Weiming Shi Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Xiang Mei Link: https://patch.msgid.link/20260630183227.2044998-1-xmei5@asu.edu Signed-off-by: Paolo Abeni --- net/smc/smc_cdc.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/net/smc/smc_cdc.c b/net/smc/smc_cdc.c index 619b3bab3824..32d6d03df321 100644 --- a/net/smc/smc_cdc.c +++ b/net/smc/smc_cdc.c @@ -470,9 +470,9 @@ static void smc_cdc_rx_handler(struct ib_wc *wc, void *buf) { struct smc_link *link = (struct smc_link *)wc->qp->qp_context; struct smc_cdc_msg *cdc = buf; + struct smc_sock *smc = NULL; struct smc_connection *conn; struct smc_link_group *lgr; - struct smc_sock *smc; if (wc->byte_len < offsetof(struct smc_cdc_msg, reserved)) return; /* short message */ @@ -483,21 +483,26 @@ static void smc_cdc_rx_handler(struct ib_wc *wc, void *buf) lgr = smc_get_lgr(link); read_lock_bh(&lgr->conns_lock); conn = smc_lgr_find_conn(ntohl(cdc->token), lgr); - read_unlock_bh(&lgr->conns_lock); - if (!conn || conn->out_of_sync) + if (!conn || conn->out_of_sync) { + read_unlock_bh(&lgr->conns_lock); return; + } smc = container_of(conn, struct smc_sock, conn); + sock_hold(&smc->sk); + read_unlock_bh(&lgr->conns_lock); if (cdc->prod_flags.failover_validation) { smc_cdc_msg_validate(smc, cdc, link); - return; + goto out; } if (smc_cdc_before(ntohs(cdc->seqno), conn->local_rx_ctrl.seqno)) /* received seqno is old */ - return; + goto out; smc_cdc_msg_recv(smc, cdc); +out: + sock_put(&smc->sk); } static struct smc_wr_rx_handler smc_cdc_rx_handlers[] = { From 9e05e91a9a847ed57926414bd7c2c5e54d6c56c6 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 1 Jul 2026 12:23:29 +0000 Subject: [PATCH 0526/1054] amt: fix size calculation in amt_get_size() amt_get_size() incorrectly used sizeof(struct iphdr) for the sizes of IFLA_AMT_DISCOVERY_IP, IFLA_AMT_REMOTE_IP, and IFLA_AMT_LOCAL_IP. These attributes contain IPv4 addresses (__be32), not full IP headers. Replace sizeof(struct iphdr) with sizeof(__be32) to avoid over-allocating netlink message space. Fixes: b9022b53adad ("amt: add control plane of amt interface") Signed-off-by: Eric Dumazet Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260701122329.3562825-1-edumazet@google.com Signed-off-by: Paolo Abeni --- drivers/net/amt.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/amt.c b/drivers/net/amt.c index 724a8163a514..951dd10e192b 100644 --- a/drivers/net/amt.c +++ b/drivers/net/amt.c @@ -3301,9 +3301,9 @@ static size_t amt_get_size(const struct net_device *dev) nla_total_size(sizeof(__u16)) + /* IFLA_AMT_GATEWAY_PORT */ nla_total_size(sizeof(__u32)) + /* IFLA_AMT_LINK */ nla_total_size(sizeof(__u32)) + /* IFLA_MAX_TUNNELS */ - nla_total_size(sizeof(struct iphdr)) + /* IFLA_AMT_DISCOVERY_IP */ - nla_total_size(sizeof(struct iphdr)) + /* IFLA_AMT_REMOTE_IP */ - nla_total_size(sizeof(struct iphdr)); /* IFLA_AMT_LOCAL_IP */ + nla_total_size(sizeof(__be32)) + /* IFLA_AMT_DISCOVERY_IP */ + nla_total_size(sizeof(__be32)) + /* IFLA_AMT_REMOTE_IP */ + nla_total_size(sizeof(__be32)); /* IFLA_AMT_LOCAL_IP */ } static int amt_fill_info(struct sk_buff *skb, const struct net_device *dev) From f72df3a4c33b64de3418ec74d1ad4f028e09d161 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 2 Jul 2026 20:36:59 +0900 Subject: [PATCH 0527/1054] ntfs: make system files immutable to prevent corruption When a system file such as $Bitmap is exposed via show_sys_files and written from userspace, the volume is corrupted and, because the cluster allocator scans $Bitmap through the same inode's page cache, a write to $Bitmap also deadlocks writeback against the folio it already holds locked. These files are maintained by the driver itself and have no valid reason to be written through the file interface. Mark base metadata files (mft_no < FILE_first_user) as immutable during inode read so the VFS rejects write, mmap, truncate and unlink with -EPERM. Directories are skipped so the root and $Extend remain usable. Internal metadata updates do not go through the VFS write path and are unaffected. Fixes: af0db57d4293 ("ntfs: update inode operations") Cc: stable@vger.kernel.org Signed-off-by: Namjae Jeon --- fs/ntfs/inode.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c index c2715521e562..7381a18cfadd 100644 --- a/fs/ntfs/inode.c +++ b/fs/ntfs/inode.c @@ -1191,6 +1191,15 @@ static int ntfs_read_locked_inode(struct inode *vi) !S_ISFIFO(vi->i_mode) && !S_ISSOCK(vi->i_mode) && !S_ISLNK(vi->i_mode)) vi->i_flags |= S_IMMUTABLE; + /* + * System files such as $Bitmap and $MFT are maintained by the driver + * itself, and writing them from userspace corrupts the volume. + * Always make them immutable regardless of the sys_immutable option. + * Directories are skipped so the root and $Extend stay usable. + */ + if (ni->mft_no < FILE_first_user && S_ISREG(vi->i_mode)) + vi->i_flags |= S_IMMUTABLE; + /* * The number of 512-byte blocks used on disk (for stat). This is in so * far inaccurate as it doesn't account for any named streams or other From 77dc384207d5fa63ba97c3bf3285fe1215a1cbf6 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Thu, 2 Jul 2026 14:28:16 +0900 Subject: [PATCH 0528/1054] ntfs: avoid self-deadlock during inode eviction An attribute-list update performed while allocating clusters can drop the last reference to the temporary attribute inode. Evicting that inode drops its reference to the base inode and can invoke ntfs_drop_big_inode() for the base inode from within the base inode's own writeback path. If the base inode is unlinked, ntfs_drop_big_inode() calls truncate_setsize(), which waits for the inode's folio writeback to complete. The same writeback worker is responsible for completing that writeback, so it waits for itself indefinitely. Prevent this self-deadlock by grabbing a reference to the base inode at the beginning of ntfs_writepages() and releasing it at the end of the function. This defers eviction until all bios have been submitted, allowing the wait for folio writeback to complete safely. Fixes: b041ca562526 ("ntfs: update iomap and address space operations") Cc: stable@vger.kernel.org Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon --- fs/ntfs/aops.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/fs/ntfs/aops.c b/fs/ntfs/aops.c index 1fbf832ad165..88e5b2def66c 100644 --- a/fs/ntfs/aops.c +++ b/fs/ntfs/aops.c @@ -251,6 +251,8 @@ static int ntfs_writepages(struct address_space *mapping, .wbc = wbc, .ops = &ntfs_writeback_ops, }; + bool need_iput = false; + int ret; if (NVolShutdown(ni->vol)) return -EIO; @@ -267,7 +269,20 @@ static int ntfs_writepages(struct address_space *mapping, return -EOPNOTSUPP; } - return iomap_writepages(&wpc); + /* + * Prevent eviction in writeback to avoid deadlock in + * ntfs_drop_big_inode(). + */ + if ((ni->type == AT_DATA || ni->type == AT_INDEX_ALLOCATION) && + igrab(inode)) + need_iput = true; + + ret = iomap_writepages(&wpc); + + if (need_iput) + iput(inode); + + return ret; } static int ntfs_swap_activate(struct swap_info_struct *sis, From b8d6c528e9d57d263fee1a648409f84a68b2561d Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 2 Jul 2026 10:31:44 +0900 Subject: [PATCH 0529/1054] ntfs: fix WARN_ON for resident attribute in ntfs_map_runlist_nolock() When ntfs_map_runlist_nolock() needs to look up the attribute extent containing a target VCN (ctx_needs_reset == true), it calls ntfs_attr_lookup() and then expects the result to be a non-resident attribute, since only non-resident attributes have a mapping pairs array to decompress. A crafted NTFS image can place a resident attribute where a non-resident one is expected, causing ntfs_attr_lookup() to succeed but return a resident attribute record. Previously this was caught only by a WARN_ON(), which does not stop execution. The code then falls through to read a->data.non_resident.highest_vcn from what is actually a resident attribute, accessing the wrong union member and corrupting the VCN range check. The caller path triggering this warning during mount is: ntfs_map_runlist_nolock ntfs_empty_logfile load_system_files ntfs_fill_super In this path ctx is NULL, so ntfs_map_runlist_nolock() allocates a temporary search context internally and sets ctx_needs_reset = true. The existing resident-attribute guard in the ctx != NULL branch already returns -EIO silently for the same condition; make the ctx_needs_reset path consistent by replacing the WARN_ON() with the same -EIO error return. This causes the crafted image to be rejected with a mount error instead of triggering a kernel warning. Fixes: 495e90fa3348 ("ntfs: update attrib operations") Cc: stable@vger.kernel.org Reported-by: Sangho Lee Signed-off-by: Namjae Jeon --- fs/ntfs/attrib.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c index a99b84751eb1..e5e3bc03ad49 100644 --- a/fs/ntfs/attrib.c +++ b/fs/ntfs/attrib.c @@ -175,7 +175,10 @@ int ntfs_map_runlist_nolock(struct ntfs_inode *ni, s64 vcn, struct ntfs_attr_sea err = -EIO; goto err_out; } - WARN_ON(!ctx->attr->non_resident); + if (unlikely(!ctx->attr->non_resident)) { + err = -EIO; + goto err_out; + } } a = ctx->attr; /* From 5b6eedd7cc2936f9238e852b553a1b326105bde8 Mon Sep 17 00:00:00 2001 From: Valeriy Yashnikov Date: Sat, 4 Jul 2026 19:38:57 +1000 Subject: [PATCH 0530/1054] ntfs: avoid calling post_write_mst_fixup() for invalid index_block ntfs_icx_ib_sync_write() calls post_write_mst_fixup() when ntfs_ib_write() returns an error, intending to restore the buffer after a failed write. However, ntfs_ib_write() returns an error immediately if pre_write_mst_fixup() validation fails. The caller, ntfs_icx_ib_sync_write(), interprets any error as a write failure requiring rollback. It does not differentiate between I/O errors and validation failures, and calls post_write_mst_fixup() anyway. Since post_write_mst_fixup() assumes that the index_block contents is correct, it doesn't perform the boundary checks, which results in out-of-bounds memory access. An attacker can craft a malicious NTFS image with: - large index_block.usa_ofs offset, pointing outside the ntfs_record - index_block.usa_count = 0, causing integer underflow - or index_block.usa_count larger than actual number of sectors in the ntfs_record, causing out-of-bounds access KASAN reports describing the memory corruption: ================================================================== BUG: KASAN: slab-out-of-bounds in post_write_mst_fixup+0x19c/0x1d0 Read of size 2 at addr ffff8881586c9018 by task p/9428 Call Trace: dump_stack_lvl+0x100/0x190 print_report+0x139/0x4ad ? post_write_mst_fixup+0x19c/0x1d0 ? __virt_addr_valid+0x262/0x500 ? post_write_mst_fixup+0x19c/0x1d0 kasan_report+0xe4/0x1d0 ? post_write_mst_fixup+0x19c/0x1d0 post_write_mst_fixup+0x19c/0x1d0 ntfs_icx_ib_sync_write+0x179/0x220 ntfs_inode_sync_filename+0x83d/0x1080 __ntfs_write_inode+0x1049/0x1480 ntfs_file_fsync+0x131/0x9b0 ================================================================== BUG: KASAN: slab-out-of-bounds in post_write_mst_fixup+0x1aa/0x1d0 Write of size 2 at addr ffff8881586c91fe by task p/9428 Call Trace: dump_stack_lvl+0x100/0x190 print_report+0x139/0x4ad ? post_write_mst_fixup+0x1aa/0x1d0 ? __virt_addr_valid+0x262/0x500 ? post_write_mst_fixup+0x1aa/0x1d0 kasan_report+0xe4/0x1d0 ? post_write_mst_fixup+0x1aa/0x1d0 post_write_mst_fixup+0x1aa/0x1d0 ntfs_icx_ib_sync_write+0x179/0x220 ntfs_inode_sync_filename+0x83d/0x1080 __ntfs_write_inode+0x1049/0x1480 ntfs_file_fsync+0x131/0x9b0 ================================================================== Let's move the post_write_mst_fixup() call to ntfs_ib_write(). The ntfs_ib_write() function calls pre_write_mst_fixup() at the beginning. If the index_block contents is invalid, pre_write_mst_fixup() fails and ntfs_ib_write() returns early without calling post_write_mst_fixup() on bad index_block. Fixes: 0a8ac0c1fa0b ("ntfs: update directory operations") Cc: stable@vger.kernel.org Signed-off-by: Valeriy Yashnikov Reviewed-by: Hyunchul Lee Signed-off-by: Namjae Jeon --- fs/ntfs/index.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fs/ntfs/index.c b/fs/ntfs/index.c index c5f2cf75b750..faa7ee920a3a 100644 --- a/fs/ntfs/index.c +++ b/fs/ntfs/index.c @@ -110,6 +110,10 @@ static int ntfs_ib_write(struct ntfs_index_context *icx, struct index_block *ib) ret = ntfs_inode_attr_pwrite(VFS_I(icx->ia_ni), ntfs_ib_vcn_to_pos(icx, vcn), icx->block_size, (u8 *)ib, icx->sync_write); + + /* Perform data restoration before returning */ + post_write_mst_fixup((struct ntfs_record *)ib); + if (ret != icx->block_size) { ntfs_debug("Failed to write index block %lld, inode %llu", vcn, (unsigned long long)icx->idx_ni->mft_no); @@ -147,7 +151,6 @@ int ntfs_icx_ib_sync_write(struct ntfs_index_context *icx) icx->ib = NULL; icx->ib_dirty = false; } else { - post_write_mst_fixup((struct ntfs_record *)icx->ib); icx->sync_write = false; } From 06769b8f23b4b645b270c438649fff79768fb6fe Mon Sep 17 00:00:00 2001 From: Peiyang He Date: Sun, 5 Jul 2026 19:14:09 +0800 Subject: [PATCH 0531/1054] ntfs: fix hole runlist memory leak in insert range error path ntfs_non_resident_attr_insert_range() allocates hole_rl before mapping the whole runlist. If ntfs_attr_map_whole_runlist() fails, the error path drops ni->runlist.lock and returns without freeing hole_rl. This leaks memory of sizeof(*hole_rl) * 2 bytes. Fix this memory leak by freeing hole_rl before returning from that error path, matching the later error paths in the same function. Fixes: 495e90fa3348 ("ntfs: update attrib operations") Cc: stable@vger.kernel.org Signed-off-by: Peiyang He Reviewed-by: Hyunchul Lee Signed-off-by: Namjae Jeon --- fs/ntfs/attrib.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c index e5e3bc03ad49..239b7bcbaedf 100644 --- a/fs/ntfs/attrib.c +++ b/fs/ntfs/attrib.c @@ -5328,6 +5328,7 @@ int ntfs_non_resident_attr_insert_range(struct ntfs_inode *ni, s64 start_vcn, s6 ret = ntfs_attr_map_whole_runlist(ni); if (ret) { up_write(&ni->runlist.lock); + kfree(hole_rl); return ret; } From d97a36bae86a9a4021562ded2987f904e6bcb1d7 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Mon, 6 Jul 2026 12:00:00 +0900 Subject: [PATCH 0532/1054] ntfs: sanitize MFT references returned from ntfs_lookup_inode_by_name() ntfs_lookup_inode_by_name() returns MFT references read from directory index entries on disk. These values are untrusted, but the function can currently return an error-marked MFT reference to its callers without validating it. Callers later decode lookup failures with MREF_ERR(). A crafted NTFS image can set the MREF error bit while leaving the low bits as an arbitrary value, causing callers to consume a bogus pseudo-errno instead of treating the lookup result as corrupted on-disk metadata. Fix this at the source by normalizing every error-marked MFT reference returned from ntfs_lookup_inode_by_name() to ERR_MREF(-EIO). Apply this to all four directory lookup return paths so every caller gets a validated result without needing additional checks or an API change. This keeps the sanitization in the common lookup helper, which is cleaner than duplicating validation in each caller. Fixes: 1e9ea7e04472 ("Revert "fs: Remove NTFS classic"") Cc: stable@vger.kernel.org Reported-by: Hongling Zeng Signed-off-by: Namjae Jeon --- fs/ntfs/dir.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/fs/ntfs/dir.c b/fs/ntfs/dir.c index 4b6bd5f30c65..6fa9ae3377cb 100644 --- a/fs/ntfs/dir.c +++ b/fs/ntfs/dir.c @@ -23,6 +23,13 @@ __le16 I30[5] = { cpu_to_le16('$'), cpu_to_le16('I'), cpu_to_le16('3'), cpu_to_le16('0'), 0 }; +static inline u64 ntfs_check_mref(u64 mref) +{ + if (IS_ERR_MREF(mref)) + return ERR_MREF(-EIO); + return mref; +} + /* * ntfs_lookup_inode_by_name - find an inode in a directory given its name * @dir_ni: ntfs inode of the directory in which to search for the name @@ -178,7 +185,7 @@ u64 ntfs_lookup_inode_by_name(struct ntfs_inode *dir_ni, const __le16 *uname, mref = le64_to_cpu(ie->data.dir.indexed_file); ntfs_attr_put_search_ctx(ctx); unmap_mft_record(dir_ni); - return mref; + return ntfs_check_mref(mref); } /* * For a case insensitive mount, we also perform a case @@ -273,7 +280,7 @@ u64 ntfs_lookup_inode_by_name(struct ntfs_inode *dir_ni, const __le16 *uname, if (name) { ntfs_attr_put_search_ctx(ctx); unmap_mft_record(dir_ni); - return name->mref; + return ntfs_check_mref(name->mref); } ntfs_debug("Entry not found."); err = -ENOENT; @@ -413,7 +420,7 @@ u64 ntfs_lookup_inode_by_name(struct ntfs_inode *dir_ni, const __le16 *uname, mref = le64_to_cpu(ie->data.dir.indexed_file); kfree(kaddr); iput(ia_vi); - return mref; + return ntfs_check_mref(mref); } /* * For a case insensitive mount, we also perform a case @@ -538,7 +545,7 @@ u64 ntfs_lookup_inode_by_name(struct ntfs_inode *dir_ni, const __le16 *uname, if (name) { kfree(kaddr); iput(ia_vi); - return name->mref; + return ntfs_check_mref(name->mref); } ntfs_debug("Entry not found."); err = -ENOENT; From 0ebe8f625ab0520217a425d7cd366e4670484941 Mon Sep 17 00:00:00 2001 From: Peiyang He Date: Mon, 6 Jul 2026 12:00:15 +0800 Subject: [PATCH 0533/1054] ntfs: fail attrlist updates when the superblock is inactive generic_shutdown_super() clears SB_ACTIVE before evicting cached inodes. If eviction selects the fake inode for a base inode's unnamed $ATTRIBUTE_LIST attribute, ntfs_evict_big_inode() drops the fake inode's reference on the base inode while the fake inode is still hashed and marked I_FREEING. That iput can synchronously write back the base inode. The writeback path may update mapping pairs and call ntfs_attrlist_update(), which unconditionally calls ntfs_attr_iget() for the same $ATTRIBUTE_LIST fake inode. VFS then finds the I_FREEING inode and waits for eviction to finish, but the current task is still inside that eviction path, causing a self-deadlock in find_inode(). Fix this by mirroring the teardown guard used by __ntfs_write_inode(): once SB_ACTIVE has been cleared, do not try to iget the attribute-list fake inode. Return -EIO so teardown aborts the update instead of waiting on the inode it is evicting. Reported-by: Peiyang He Closes: https://lore.kernel.org/all/AB8D5E603E6EA856+ae5f622a-dd3a-4e38-bdd2-42276ae0e1a8@smail.nju.edu.cn/ Fixes: 495e90fa3348 ("ntfs: update attrib operations") Cc: stable@vger.kernel.org Signed-off-by: Peiyang He Assisted-by: Codex:gpt-5.5 Reviewed-by: Hyunchul Lee Signed-off-by: Namjae Jeon --- fs/ntfs/attrlist.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/fs/ntfs/attrlist.c b/fs/ntfs/attrlist.c index afb13038ba42..be3086d34338 100644 --- a/fs/ntfs/attrlist.c +++ b/fs/ntfs/attrlist.c @@ -57,6 +57,15 @@ int ntfs_attrlist_update(struct ntfs_inode *base_ni) struct ntfs_inode *attr_ni; int err; + /* + * generic_shutdown_super() clears SB_ACTIVE before evicting cached + * inodes. Do not look up the attribute-list inode after SB_ACTIVE has + * been cleared; it may already be I_FREEING, and waiting on it can + * self-deadlock. + */ + if (!(VFS_I(base_ni)->i_sb->s_flags & SB_ACTIVE)) + return -EIO; + attr_vi = ntfs_attr_iget(VFS_I(base_ni), AT_ATTRIBUTE_LIST, AT_UNNAMED, 0); if (IS_ERR(attr_vi)) { err = PTR_ERR(attr_vi); From edf0730be33696a1bd142792830d392129e495cc Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Sat, 20 Jun 2026 00:25:42 +0800 Subject: [PATCH 0534/1054] wifi: cfg80211: cancel sched scan results work on unregister cfg80211_sched_scan_results() can queue rdev->sched_scan_res_wk from a driver result notification while a scheduled scan request is present. The work callback recovers the containing cfg80211_registered_device and then locks the wiphy and walks the scheduled-scan request list. wiphy_unregister() already makes the wiphy unreachable and drains rdev work items before cfg80211_dev_free() can release the object, but it does not drain sched_scan_res_wk. A queued or running result work item can therefore cross the unregister/free boundary and access freed rdev state. The buggy scenario involves two paths, with each column showing the order within that path: scheduled-scan result path: unregister/free path: 1. cfg80211_sched_scan_results() 1. interface teardown stops and queues rdev->sched_scan_res_wk. removes the scheduled scan request. 2. cfg80211_wq starts the work 2. wiphy_unregister() drains other item and recovers rdev. rdev work items. 3. The worker locks rdev->wiphy 3. cfg80211_dev_free() destroys and and walks rdev state. frees rdev. Cancel sched_scan_res_wk in wiphy_unregister() alongside the other rdev work items. cancel_work_sync() removes a pending result notification and waits for an already running callback, so cfg80211_dev_free() cannot free rdev while this work item is still active. Validation reproduced this kernel report: BUG: KASAN: use-after-free in cfg80211_sched_scan_results_wk+0x4a6/0x530 Workqueue: cfg80211 cfg80211_sched_scan_results_wk [cfg80211] Read of size 8 Call trace: dump_stack_lvl+0x66/0xa0 print_report+0xce/0x630 cfg80211_sched_scan_results_wk+0x4a6/0x530 srso_alias_return_thunk+0x5/0xfbef5 __virt_addr_valid+0x224/0x430 kasan_report+0xac/0xe0 lockdep_hardirqs_on_prepare+0xea/0x1a0 process_one_work+0x8d0/0x18f0 (kernel/workqueue.c:3212) lock_is_held_type+0x8f/0x100 worker_thread+0x5ad/0xfd0 __kthread_parkme+0xc6/0x200 kthread+0x31e/0x410 trace_hardirqs_on+0x1a/0x170 ret_from_fork+0x576/0x810 __switch_to+0x57e/0xe20 __switch_to_asm+0x33/0x70 ret_from_fork_asm+0x1a/0x30 Fixes: 807f8a8c3004 ("cfg80211/nl80211: add support for scheduled scans") Assisted-by: Codex:gpt-5.5 Signed-off-by: Cen Zhang Link: https://patch.msgid.link/20260619162542.3878296-1-zzzccc427@gmail.com Signed-off-by: Johannes Berg --- net/wireless/core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/wireless/core.c b/net/wireless/core.c index 3dcf63b04c41..2c729a7aca12 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -1335,6 +1335,7 @@ void wiphy_unregister(struct wiphy *wiphy) /* this has nothing to do now but make sure it's gone */ cancel_work_sync(&rdev->wiphy_work); + cancel_work_sync(&rdev->sched_scan_res_wk); cancel_work_sync(&rdev->rfkill_block); cancel_work_sync(&rdev->conn_work); flush_work(&rdev->event_work); From 0d388f62031dbabcba0f44bb91b59f10e88cac17 Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Sat, 20 Jun 2026 12:22:39 +0530 Subject: [PATCH 0535/1054] wifi: ipw2100: fix potential memory leak in ipw2100_pci_init_one() The memory allocated in the ipw2100_alloc_device() function is not freed in some of the error paths in ipw2100_pci_init_one(). Fix that by converting the direct return into a goto to the error path return. The error path when pci_enable_device() fails cannot jump to fail, since at this point priv is not set, so perform error handling inline. Fixes: 2c86c275015c ("Add ipw2100 wireless driver.") Signed-off-by: Abdun Nihaal Link: https://patch.msgid.link/20260620065242.93798-1-nihaal@cse.iitm.ac.in Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/ipw2x00/ipw2100.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/intel/ipw2x00/ipw2100.c b/drivers/net/wireless/intel/ipw2x00/ipw2100.c index c11428485dcc..2b8a23865bfb 100644 --- a/drivers/net/wireless/intel/ipw2x00/ipw2100.c +++ b/drivers/net/wireless/intel/ipw2x00/ipw2100.c @@ -6157,6 +6157,8 @@ static int ipw2100_pci_init_one(struct pci_dev *pci_dev, if (err) { printk(KERN_WARNING DRV_NAME "Error calling pci_enable_device.\n"); + free_libipw(dev, 0); + pci_iounmap(pci_dev, ioaddr); return err; } @@ -6169,16 +6171,14 @@ static int ipw2100_pci_init_one(struct pci_dev *pci_dev, if (err) { printk(KERN_WARNING DRV_NAME "Error calling pci_set_dma_mask.\n"); - pci_disable_device(pci_dev); - return err; + goto fail; } err = pci_request_regions(pci_dev, DRV_NAME); if (err) { printk(KERN_WARNING DRV_NAME "Error calling pci_request_regions.\n"); - pci_disable_device(pci_dev); - return err; + goto fail; } /* We disable the RETRY_TIMEOUT register (0x41) to keep From c6659f66d4ee4841aafae5659d2ef5e4c5c63cb6 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 20 Jun 2026 21:48:56 +0200 Subject: [PATCH 0536/1054] wifi: cfg80211: Fix an error handling path in cfg80211_wext_siwscan() If the test against IEEE80211_MAX_SSID_LEN fails, then 'creq' leaks. Use the existing error handling path to fix it. Fixes: 2a5193119269 ("cfg80211/nl80211: scanning (and mac80211 update to use it)") Signed-off-by: Christophe JAILLET Link: https://patch.msgid.link/a1be7eea4da0da18f90589af252bb76a18a61978.1781984889.git.christophe.jaillet@wanadoo.fr Signed-off-by: Johannes Berg --- net/wireless/scan.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 05b7dc6b766c..38001684014d 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -3612,8 +3612,10 @@ int cfg80211_wext_siwscan(struct net_device *dev, /* translate "Scan for SSID" request */ if (wreq) { if (wrqu->data.flags & IW_SCAN_THIS_ESSID) { - if (wreq->essid_len > IEEE80211_MAX_SSID_LEN) - return -EINVAL; + if (wreq->essid_len > IEEE80211_MAX_SSID_LEN) { + err = -EINVAL; + goto out; + } memcpy(creq->req.ssids[0].ssid, wreq->essid, wreq->essid_len); creq->req.ssids[0].ssid_len = wreq->essid_len; From 10a2b430f8f06ae14b9590b6f6faa6b588ef0654 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Sat, 20 Jun 2026 21:45:18 -0500 Subject: [PATCH 0537/1054] wifi: mac80211_hwsim: clamp virtio RX length before skb_put hwsim_virtio_rx_work() passes the virtqueue used-ring length reported by the device straight to skb_put() on a fixed-size receive skb. A backend reporting a length larger than the skb tailroom drives skb_put() past the buffer end and hits skb_over_panic() -- a host-triggerable guest panic (denial of service). Clamp the length to the skb's available room before skb_put(). A conforming device never reports more than the posted buffer size, so valid frames are unaffected; a truncated over-report then fails the length/header checks in hwsim_virtio_handle_cmd() and is dropped, so truncating rather than dropping here cannot be turned into a parsing problem. Fixes: 5d44fe7c9808 ("mac80211_hwsim: add frame transmission support over virtio") Signed-off-by: Bryam Vargas Link: https://patch.msgid.link/20260620-b4-disp-474bee37-v1-1-1a4d37f3e2d4@proton.me Signed-off-by: Johannes Berg --- drivers/net/wireless/virtual/mac80211_hwsim_main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_main.c b/drivers/net/wireless/virtual/mac80211_hwsim_main.c index 0dd8a6c85953..5c1718277599 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim_main.c +++ b/drivers/net/wireless/virtual/mac80211_hwsim_main.c @@ -7289,6 +7289,7 @@ static void hwsim_virtio_rx_work(struct work_struct *work) skb->data = skb->head; skb_reset_tail_pointer(skb); + len = min(len, skb_end_offset(skb)); skb_put(skb, len); hwsim_virtio_handle_cmd(skb); From 1d067abcd37062426c59ec73dbc4e87a63f33fea Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Sun, 21 Jun 2026 02:35:31 -0700 Subject: [PATCH 0538/1054] wifi: mac80211: fix unsol_bcast_probe_resp double free on alloc failure ieee80211_set_unsol_bcast_probe_resp() calls kfree_rcu() on the old template before allocating the replacement. If the kzalloc() then fails, it returns -ENOMEM while link->u.ap.unsol_bcast_probe_resp still points at the object already queued for freeing. A later update or AP teardown re-queues that same rcu_head; the second free is caught by KASAN when the RCU sheaf is processed in softirq: BUG: KASAN: double-free in rcu_free_sheaf (mm/slub.c:5850) Free of addr ffff88800d06f300 by task exploit/145 ... __rcu_free_sheaf_prepare (mm/slub.c:2634 mm/slub.c:2940) rcu_free_sheaf (mm/slub.c:5850) rcu_core (kernel/rcu/tree.c:2617 kernel/rcu/tree.c:2869) handle_softirqs (kernel/softirq.c:622) The buggy address belongs to the cache kmalloc-128 of size 128 Queue the old object for kfree_rcu() only after the new one is published, matching ieee80211_set_probe_resp() and ieee80211_set_s1g_short_beacon(). Fixes: 3b1c256eb4ae ("wifi: mac80211: fixes in FILS discovery updates") Reported-by: Weiming Shi Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Xiang Mei Link: https://patch.msgid.link/20260621093532.884188-1-xmei5@asu.edu Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 3b58af59f7e4..932cf20785bc 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1179,8 +1179,6 @@ ieee80211_set_unsol_bcast_probe_resp(struct ieee80211_sub_if_data *sdata, link_conf->unsol_bcast_probe_resp_interval = params->interval; old = sdata_dereference(link->u.ap.unsol_bcast_probe_resp, sdata); - if (old) - kfree_rcu(old, rcu_head); if (params->tmpl && params->tmpl_len) { new = kzalloc(sizeof(*new) + params->tmpl_len, GFP_KERNEL); @@ -1193,6 +1191,9 @@ ieee80211_set_unsol_bcast_probe_resp(struct ieee80211_sub_if_data *sdata, RCU_INIT_POINTER(link->u.ap.unsol_bcast_probe_resp, NULL); } + if (old) + kfree_rcu(old, rcu_head); + *changed |= BSS_CHANGED_UNSOL_BCAST_PROBE_RESP; return 0; } From 286e52a799fa158bdbd77da1426c4d93f9a6e7ad Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Sun, 21 Jun 2026 02:35:32 -0700 Subject: [PATCH 0539/1054] wifi: mac80211: fix fils_discovery double free on alloc failure ieee80211_set_fils_discovery() calls kfree_rcu() on the old template before allocating the replacement. If the kzalloc() then fails, it returns -ENOMEM while link->u.ap.fils_discovery still points at the object already queued for freeing. A later update or AP teardown (ieee80211_stop_ap()) re-queues that same rcu_head; the second free is caught by KASAN when the RCU sheaf is processed in softirq: BUG: KASAN: double-free in rcu_free_sheaf (mm/slub.c:5850) Free of addr ffff88800c065280 by task swapper/0/0 ... __rcu_free_sheaf_prepare (mm/slub.c:2634 mm/slub.c:2940) rcu_free_sheaf (mm/slub.c:5850) rcu_core (kernel/rcu/tree.c:2617 kernel/rcu/tree.c:2869) handle_softirqs (kernel/softirq.c:622) The buggy address belongs to the cache kmalloc-96 of size 96 Queue the old object for kfree_rcu() only after the new one is published, matching ieee80211_set_probe_resp() and ieee80211_set_s1g_short_beacon(). Fixes: 3b1c256eb4ae ("wifi: mac80211: fixes in FILS discovery updates") Reported-by: Weiming Shi Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Xiang Mei Link: https://patch.msgid.link/20260621093532.884188-2-xmei5@asu.edu Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 932cf20785bc..b00191e02a63 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1146,9 +1146,6 @@ static int ieee80211_set_fils_discovery(struct ieee80211_sub_if_data *sdata, fd->max_interval = params->max_interval; old = sdata_dereference(link->u.ap.fils_discovery, sdata); - if (old) - kfree_rcu(old, rcu_head); - if (params->tmpl && params->tmpl_len) { new = kzalloc(sizeof(*new) + params->tmpl_len, GFP_KERNEL); if (!new) @@ -1160,6 +1157,9 @@ static int ieee80211_set_fils_discovery(struct ieee80211_sub_if_data *sdata, RCU_INIT_POINTER(link->u.ap.fils_discovery, NULL); } + if (old) + kfree_rcu(old, rcu_head); + *changed |= BSS_CHANGED_FILS_DISCOVERY; return 0; } From aa6dcd5c8dd9ba1d7d0f60093bcda41c0d6d438d Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Mon, 22 Jun 2026 15:53:38 +0800 Subject: [PATCH 0540/1054] wifi: libertas_tf: fix use-after-free in lbtf_free_adapter() lbtf_free_adapter() calls timer_delete(&priv->command_timer), which does not wait for a running command_timer_fn() callback. lbtf_free_adapter() runs on the teardown path right before ieee80211_free_hw() frees priv, both in lbtf_remove_card() and in the probe error path. command_timer is armed by mod_timer() in lbtf_cmd() whenever a firmware command is sent. command_timer_fn() dereferences priv. If a command times out as the device is removed, command_timer_fn() runs concurrently with teardown and dereferences priv after it has been freed. This is the same use-after-free that commit 03cc8f90d053 ("wifi: libertas: fix use-after-free in lbs_free_adapter()") fixed in the sibling libertas driver. The libertas_tf variant has the identical pattern and was left unchanged. Use timer_delete_sync() so any in-flight callback completes before priv is freed. Fixes: 06b16ae53192 ("libertas_tf: main.c, data paths and mac80211 handlers") Cc: stable@vger.kernel.org Signed-off-by: Maoyi Xie Link: https://patch.msgid.link/178211481807.2212567.8773346114561900100@maoyixie.com Signed-off-by: Johannes Berg --- drivers/net/wireless/marvell/libertas_tf/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/marvell/libertas_tf/main.c b/drivers/net/wireless/marvell/libertas_tf/main.c index fb20fe31cd36..42be6fa22f9c 100644 --- a/drivers/net/wireless/marvell/libertas_tf/main.c +++ b/drivers/net/wireless/marvell/libertas_tf/main.c @@ -174,7 +174,7 @@ static void lbtf_free_adapter(struct lbtf_private *priv) { lbtf_deb_enter(LBTF_DEB_MAIN); lbtf_free_cmd_buffer(priv); - timer_delete(&priv->command_timer); + timer_delete_sync(&priv->command_timer); lbtf_deb_leave(LBTF_DEB_MAIN); } From 63c2391deefb31e1b801b7f32bd502ca4808639b Mon Sep 17 00:00:00 2001 From: Dawei Feng Date: Wed, 24 Jun 2026 16:53:43 +0800 Subject: [PATCH 0541/1054] wifi: libertas: fix memory leak in helper_firmware_cb() helper_firmware_cb() neglects to free the single-stage firmware image after a successful async load, leading to a memory leak in the USB firmware-download path. Fix this memory leak by calling release_firmware() immediately after lbs_fw_loaded() returns. 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 the current wireless tree. An x86_64 allyesconfig build showed no new warnings. As we do not have compatible Libertas USB hardware for exercising this firmware-download path, no runtime testing was able to be performed. Fixes: 1dfba3060fe7 ("libertas: move firmware lifetime handling to firmware.c") Signed-off-by: Dawei Feng Link: https://patch.msgid.link/20260624085343.575508-1-dawei.feng@seu.edu.cn Signed-off-by: Johannes Berg --- drivers/net/wireless/marvell/libertas/firmware.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/marvell/libertas/firmware.c b/drivers/net/wireless/marvell/libertas/firmware.c index f124110944b7..9bf7d4c207b9 100644 --- a/drivers/net/wireless/marvell/libertas/firmware.c +++ b/drivers/net/wireless/marvell/libertas/firmware.c @@ -78,6 +78,7 @@ static void helper_firmware_cb(const struct firmware *firmware, void *context) } else { /* No main firmware needed for this helper --> success! */ lbs_fw_loaded(priv, 0, firmware, NULL); + release_firmware(firmware); } } From 23b493d9dc5f00bba59347bd8ec7b044e26392a0 Mon Sep 17 00:00:00 2001 From: Yousef Alhouseen Date: Sun, 28 Jun 2026 02:25:37 +0200 Subject: [PATCH 0542/1054] wifi: mac80211_hwsim: avoid treating MCS as legacy rate index Injected HT and VHT rates store an MCS value in rates[0].idx rather than an index into the legacy bitrate table. hwsim nevertheless passes these rates to ieee80211_get_tx_rate() while generating monitor frames and timestamps. A crafted injected frame can therefore read beyond the bitrate table. If the resulting bitrate is zero, mac80211_hwsim_write_tsf() also divides by zero, as observed by syzbot. Use ieee80211_get_tx_rate() only for legacy rates. The existing fallback continues to supply a conservative bitrate where hwsim does not yet calculate MCS rates. Reported-by: syzbot+21629c14aa749636db9d@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=21629c14aa749636db9d Signed-off-by: Yousef Alhouseen Link: https://patch.msgid.link/20260628002537.23550-1-alhouseenyousef@gmail.com [drop wrong Fixes tag] Signed-off-by: Johannes Berg --- .../net/wireless/virtual/mac80211_hwsim_main.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_main.c b/drivers/net/wireless/virtual/mac80211_hwsim_main.c index 5c1718277599..956ff9b94526 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim_main.c +++ b/drivers/net/wireless/virtual/mac80211_hwsim_main.c @@ -1324,6 +1324,17 @@ static void mac80211_hwsim_set_tsf(struct ieee80211_hw *hw, } } +static struct ieee80211_rate * +mac80211_hwsim_get_tx_rate(struct ieee80211_hw *hw, + struct ieee80211_tx_info *info) +{ + if (info->control.rates[0].flags & + (IEEE80211_TX_RC_MCS | IEEE80211_TX_RC_VHT_MCS)) + return NULL; + + return ieee80211_get_tx_rate(hw, info); +} + static void mac80211_hwsim_monitor_rx(struct ieee80211_hw *hw, struct sk_buff *tx_skb, struct ieee80211_channel *chan) @@ -1333,7 +1344,7 @@ static void mac80211_hwsim_monitor_rx(struct ieee80211_hw *hw, struct hwsim_radiotap_hdr *hdr; u16 flags, bitrate; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx_skb); - struct ieee80211_rate *txrate = ieee80211_get_tx_rate(hw, info); + struct ieee80211_rate *txrate = mac80211_hwsim_get_tx_rate(hw, info); if (!txrate) bitrate = 0; @@ -1603,7 +1614,7 @@ static void mac80211_hwsim_write_tsf(struct mac80211_hwsim_data *data, spin_lock_bh(&data->tsf_offset_lock); - txrate = ieee80211_get_tx_rate(data->hw, info); + txrate = mac80211_hwsim_get_tx_rate(data->hw, info); if (txrate) bitrate = txrate->bitrate; From 2c51457d930f723e5f2903af90f5847f7df53f42 Mon Sep 17 00:00:00 2001 From: Zhiling Zou Date: Sat, 27 Jun 2026 00:58:30 +0800 Subject: [PATCH 0543/1054] wifi: mac80211: free ack status frame on TX header build failure ieee80211_build_hdr() stores an ACK status frame before it has finished all validation and header construction. If a later error path is taken, the transmit skb is freed but the stored ACK status frame remains in local->ack_status_frames. This can happen for control port frames when the requested MLO link ID does not match the link selected for a non-MLO station. Repeated failures can fill the ACK status IDR and leave pending ACK frames until hardware teardown. Remove any stored ACK status frame before returning an error after it has been inserted into the IDR. Fixes: a729cff8ad51 ("mac80211: implement wifi TX status") Cc: stable@vger.kernel.org Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Xin Liu Assisted-by: Codex:gpt-5.4 Signed-off-by: Zhiling Zou Signed-off-by: Ren Wei Link: https://patch.msgid.link/9de0423da840e92084915b8f92e66a421245c4b8.1782462409.git.roxy520tt@gmail.com Signed-off-by: Johannes Berg --- net/mac80211/tx.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index c13b209fad47..91b14112e24f 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -2607,6 +2607,18 @@ static u16 ieee80211_store_ack_skb(struct ieee80211_local *local, return info_id; } +static void ieee80211_remove_ack_skb(struct ieee80211_local *local, u16 info_id) +{ + struct sk_buff *ack_skb; + unsigned long flags; + + spin_lock_irqsave(&local->ack_status_lock, flags); + ack_skb = idr_remove(&local->ack_status_frames, info_id); + spin_unlock_irqrestore(&local->ack_status_lock, flags); + + kfree_skb(ack_skb); +} + /** * ieee80211_build_hdr - build 802.11 header in the given frame * @sdata: virtual interface to build the header for @@ -2982,7 +2994,8 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata, if (ieee80211_skb_resize(sdata, skb, head_need, ENCRYPT_DATA)) { ieee80211_free_txskb(&local->hw, skb); skb = NULL; - return ERR_PTR(-ENOMEM); + ret = -ENOMEM; + goto free; } } @@ -3050,6 +3063,8 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata, return skb; free: + if (info_id) + ieee80211_remove_ack_skb(local, info_id); kfree_skb(skb); return ERR_PTR(ret); } From aa2eb62525188269cdd402a583b9a8ed94657ff0 Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Sat, 27 Jun 2026 16:30:28 +0800 Subject: [PATCH 0544/1054] wifi: mac80211: defer link RX stats percpu free to RCU sta_remove_link() frees a removed MLO link's RX stats percpu buffer right away, but defers only the link container to RCU: sta_info_free_link(&alloc->info); kfree_rcu(alloc, rcu_head); The RX fast path reads link_sta under rcu_read_lock and writes the percpu stats. A reader that resolved link_sta before the removal keeps the pointer. The container stays alive from the kfree_rcu, so the read still works. But the percpu block it points to is already freed. This needs uses_rss. That is when pcpu_rx_stats exists. The full STA teardown frees the deflink stats only after synchronize_net(). The link removal path had no such barrier. The race is hard to win in practice, but the free should still wait for RCU. Free the link together with its data from a single RCU callback, so the percpu block is reclaimed only after readers drain. Fixes: c71420db653a ("wifi: mac80211: RCU-ify link STA pointers") Link: https://lore.kernel.org/r/20260626080158.3589711-1-maoyixie.tju@gmail.com Suggested-by: Johannes Berg Co-developed-by: Kaixuan Li Signed-off-by: Kaixuan Li Signed-off-by: Maoyi Xie Link: https://patch.msgid.link/20260627083028.3826810-1-maoyixie.tju@gmail.com Signed-off-by: Johannes Berg --- net/mac80211/sta_info.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 02b587ff8504..22eba0e6e54c 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -355,6 +355,15 @@ static void sta_info_free_link(struct link_sta_info *link_sta) free_percpu(link_sta->pcpu_rx_stats); } +static void sta_link_free_rcu(struct rcu_head *head) +{ + struct sta_link_alloc *alloc = + container_of(head, struct sta_link_alloc, rcu_head); + + sta_info_free_link(&alloc->info); + kfree(alloc); +} + static void sta_accumulate_removed_link_stats(struct sta_info *sta, int link_id) { struct link_sta_info *link_sta = wiphy_dereference(sta->local->hw.wiphy, @@ -439,10 +448,8 @@ static void sta_remove_link(struct sta_info *sta, unsigned int link_id, RCU_INIT_POINTER(sta->link[link_id], NULL); RCU_INIT_POINTER(sta->sta.link[link_id], NULL); - if (alloc) { - sta_info_free_link(&alloc->info); - kfree_rcu(alloc, rcu_head); - } + if (alloc) + call_rcu(&alloc->rcu_head, sta_link_free_rcu); ieee80211_sta_recalc_aggregates(&sta->sta); } From ebd6d37fa94bee929e0b4c9ca19fdf9b1dcf6cea Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Sat, 27 Jun 2026 17:05:10 -0700 Subject: [PATCH 0545/1054] wifi: p54: validate RX frame length in p54_rx_eeprom_readback() p54_rx_eeprom_readback() copies the requested EEPROM slice out of a device-supplied readback frame without checking that the skb actually holds that many bytes. Commit da1b9a55ff11 ("wifi: p54: prevent buffer-overflow in p54_rx_eeprom_readback()") closed the destination overflow by copying a fixed priv->eeprom_slice_size (and rejecting a mismatched advertised len), but the source side is still unbounded: nothing verifies the frame is long enough to supply that many bytes. A malicious USB device can send a short frame whose advertised len matches priv->eeprom_slice_size while the payload is truncated. The equality check passes and memcpy() reads past the end of the skb, leaking adjacent heap: BUG: KASAN: slab-out-of-bounds in p54_rx (drivers/net/wireless/intersil/p54/txrx.c:507) Read of size 1016 at addr ffff88800f077114 by task swapper/0/0 Call Trace: ... __asan_memcpy (mm/kasan/shadow.c:105) p54_rx (drivers/net/wireless/intersil/p54/txrx.c:507) p54u_rx_cb (drivers/net/wireless/intersil/p54/p54usb.c:163) __usb_hcd_giveback_urb (drivers/usb/core/hcd.c:1657) dummy_timer (drivers/usb/gadget/udc/dummy_hcd.c:2005) ... The buggy address belongs to the object at ffff88800f0770c0 which belongs to the cache skbuff_small_head of size 704 The buggy address is located 84 bytes inside of allocated 704-byte region [ffff88800f0770c0, ffff88800f077380) Check that the slice fits in the skb before copying. Fixes: 7cb770729ba8 ("p54: move eeprom code into common library") Reported-by: Weiming Shi Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Xiang Mei Acked-by: Christian Lamparter Link: https://patch.msgid.link/20260628000510.4152481-1-xmei5@asu.edu Signed-off-by: Johannes Berg --- drivers/net/wireless/intersil/p54/txrx.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/net/wireless/intersil/p54/txrx.c b/drivers/net/wireless/intersil/p54/txrx.c index 1294a1d6528e..9f491334c8d0 100644 --- a/drivers/net/wireless/intersil/p54/txrx.c +++ b/drivers/net/wireless/intersil/p54/txrx.c @@ -499,11 +499,19 @@ static void p54_rx_eeprom_readback(struct p54_common *priv, if (le16_to_cpu(eeprom->v2.len) != priv->eeprom_slice_size) return; + if (eeprom->v2.data + priv->eeprom_slice_size > + skb_tail_pointer(skb)) + return; + memcpy(priv->eeprom, eeprom->v2.data, priv->eeprom_slice_size); } else { if (le16_to_cpu(eeprom->v1.len) != priv->eeprom_slice_size) return; + if (eeprom->v1.data + priv->eeprom_slice_size > + skb_tail_pointer(skb)) + return; + memcpy(priv->eeprom, eeprom->v1.data, priv->eeprom_slice_size); } From 843fe9bc583b7686ca68312ac9319c9240a73c03 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Wed, 1 Jul 2026 13:34:14 +0800 Subject: [PATCH 0546/1054] wifi: rsi: avoid reading TKIP MIC keys for non-TKIP ciphers rsi_hal_load_key() copies tx_mic_key and rx_mic_key from data[16] and data[24] whenever key data is present. Those offsets are only part of the 32-byte TKIP key layout. Shorter keys used by other ciphers, such as CCMP, do not provide those bytes, so the unconditional copies can read past the supplied key buffer. Only copy the MIC keys for TKIP, and reject malformed TKIP keys that are shorter than the expected 32-byte layout. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260701053414.34015-1-pengpeng@iscas.ac.cn [drop useless length check] Signed-off-by: Johannes Berg --- drivers/net/wireless/rsi/rsi_91x_mgmt.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c index 7f2c1608f2ce..2ddf4d158bfe 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mgmt.c +++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c @@ -848,8 +848,10 @@ int rsi_hal_load_key(struct rsi_common *common, } else { memcpy(&set_key->key[0][0], data, key_len); } - memcpy(set_key->tx_mic_key, &data[16], 8); - memcpy(set_key->rx_mic_key, &data[24], 8); + if (cipher == WLAN_CIPHER_SUITE_TKIP) { + memcpy(set_key->tx_mic_key, &data[16], 8); + memcpy(set_key->rx_mic_key, &data[24], 8); + } } else { memset(&set_key[FRAME_DESC_SZ], 0, frame_len - FRAME_DESC_SZ); } From 74e27cd1d98b546fdb276008a83708d062339661 Mon Sep 17 00:00:00 2001 From: Haofeng Li Date: Wed, 1 Jul 2026 17:33:27 +0800 Subject: [PATCH 0547/1054] wifi: cfg80211: validate EHT MLE before MLD ID read cfg80211_gen_new_ie() copies ML probe response elements from the parent frame when the parent EHT multi-link element has an MLD ID matching the nontransmitted BSSID index. The code only checked that the extension element had more than one byte before calling ieee80211_mle_get_mld_id(). That helper assumes a BASIC MLE with enough common info and documents that callers must first use ieee80211_mle_type_ok(). Attack chain: malicious AP sends a short EHT MLE in an MBSSID beacon. cfg80211_inform_bss_frame_data() stores the copied IE buffer. cfg80211_parse_mbssid_data() builds the nontransmitted BSS IE. cfg80211_gen_new_ie() sees the EHT MLE in the parent frame. ieee80211_mle_get_mld_id() then reads past the IE boundary. Validate the MLE type and size before reading the MLD ID. This matches the contract required by the MLE helper and rejects the short element before any internal MLE fields are accessed. Cc: stable@vger.kernel.org Fixes: 61dcfa8c2a8f ("wifi: cfg80211: copy multi-link element from the multi-link probe request's frame body to the generated elements") Signed-off-by: Haofeng Li Link: https://patch.msgid.link/20260701093327.2680709-1-lihaofeng@kylinos.cn Signed-off-by: Johannes Berg --- net/wireless/scan.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 38001684014d..e1c09040a5c8 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -326,8 +326,11 @@ cfg80211_gen_new_ie(const u8 *ie, size_t ielen, /* For ML probe response, match the MLE in the frame body with * MLD id being 'bssid_index' */ - if (parent->id == WLAN_EID_EXTENSION && parent->datalen > 1 && + if (parent->id == WLAN_EID_EXTENSION && parent->data[0] == WLAN_EID_EXT_EHT_MULTI_LINK && + ieee80211_mle_type_ok(parent->data + 1, + IEEE80211_ML_CONTROL_TYPE_BASIC, + parent->datalen - 1) && bssid_index == ieee80211_mle_get_mld_id(parent->data + 1)) { if (!cfg80211_copy_elem_with_frags(parent, ie, ielen, From 2b0eab425e1f658d8fe1df7590e3b9af5959505e Mon Sep 17 00:00:00 2001 From: Peddolla Harshavardhan Reddy Date: Fri, 3 Jul 2026 13:55:23 +0530 Subject: [PATCH 0548/1054] wifi: cfg80211: convert pmsr_free_wk to wiphy_work to fix deadlock When a netlink socket that owns a PMSR session is closed, cfg80211_release_pmsr() clears the request's nl_portid and queues pmsr_free_wk to call cfg80211_pmsr_process_abort() asynchronously. If the interface tears down concurrently, cfg80211_pmsr_wdev_down() is called under wiphy_lock and calls cancel_work_sync(&pmsr_free_wk) to wait for any running work. The work function acquires wiphy_lock via guard(wiphy) before calling process_abort. This is a deadlock: wdev_down holds wiphy_lock and blocks inside cancel_work_sync(); pmsr_free_wk blocks trying to acquire that same wiphy_lock. Neither thread can proceed. The same deadlock is reachable from cfg80211_leave_locked(), which calls cfg80211_pmsr_wdev_down() for all interface types under wiphy_lock. Fix this by converting pmsr_free_wk from a plain work_struct to a wiphy_work. The wiphy_work dispatcher holds wiphy_lock when running work items, so the explicit guard(wiphy) in the work function is no longer needed. wiphy_work_cancel() can be called safely while holding wiphy_lock - since wiphy_lock prevents the work from running concurrently, wiphy_work_cancel() never blocks, eliminating the deadlock. Remove the cancel_work_sync() for pmsr_free_wk from the NETDEV_GOING_DOWN handler. cfg80211_leave(), called unconditionally just before it, already cancels any pending work under wiphy_lock via wiphy_work_cancel() inside cfg80211_pmsr_wdev_down(). Fixes: 6dccbc9f3e1d ("wifi: cfg80211: cancel pmsr_free_wk in cfg80211_pmsr_wdev_down") Signed-off-by: Peddolla Harshavardhan Reddy Link: https://patch.msgid.link/20260703082523.2629324-1-peddolla.reddy@oss.qualcomm.com Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 2 +- net/wireless/core.c | 3 +-- net/wireless/core.h | 2 +- net/wireless/pmsr.c | 8 +++----- 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 8188ad200de5..3751a1d74765 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -7265,7 +7265,7 @@ struct wireless_dev { struct list_head pmsr_list; spinlock_t pmsr_lock; - struct work_struct pmsr_free_wk; + struct wiphy_work pmsr_free_wk; unsigned long unprot_beacon_reported; diff --git a/net/wireless/core.c b/net/wireless/core.c index 2c729a7aca12..082f0ee12f1b 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -1614,7 +1614,7 @@ void cfg80211_init_wdev(struct wireless_dev *wdev) INIT_LIST_HEAD(&wdev->mgmt_registrations); INIT_LIST_HEAD(&wdev->pmsr_list); spin_lock_init(&wdev->pmsr_lock); - INIT_WORK(&wdev->pmsr_free_wk, cfg80211_pmsr_free_wk); + wiphy_work_init(&wdev->pmsr_free_wk, cfg80211_pmsr_free_wk); #ifdef CONFIG_CFG80211_WEXT wdev->wext.default_key = -1; @@ -1748,7 +1748,6 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, cfg80211_remove_links(wdev); /* since we just did cfg80211_leave() nothing to do there */ cancel_work_sync(&wdev->disconnect_wk); - cancel_work_sync(&wdev->pmsr_free_wk); break; case NETDEV_DOWN: wiphy_lock(&rdev->wiphy); diff --git a/net/wireless/core.h b/net/wireless/core.h index df47ed6208a5..f60c66b88677 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -586,7 +586,7 @@ cfg80211_get_6ghz_power_type(const u8 *elems, size_t elems_len, void cfg80211_release_pmsr(struct wireless_dev *wdev, u32 portid); void cfg80211_pmsr_wdev_down(struct wireless_dev *wdev); -void cfg80211_pmsr_free_wk(struct work_struct *work); +void cfg80211_pmsr_free_wk(struct wiphy *wiphy, struct wiphy_work *work); void cfg80211_remove_link(struct wireless_dev *wdev, unsigned int link_id); void cfg80211_remove_links(struct wireless_dev *wdev); diff --git a/net/wireless/pmsr.c b/net/wireless/pmsr.c index c8447448f3a5..2c8db33d9c30 100644 --- a/net/wireless/pmsr.c +++ b/net/wireless/pmsr.c @@ -807,13 +807,11 @@ static void cfg80211_pmsr_process_abort(struct wireless_dev *wdev) } } -void cfg80211_pmsr_free_wk(struct work_struct *work) +void cfg80211_pmsr_free_wk(struct wiphy *wiphy, struct wiphy_work *work) { struct wireless_dev *wdev = container_of(work, struct wireless_dev, pmsr_free_wk); - guard(wiphy)(wdev->wiphy); - cfg80211_pmsr_process_abort(wdev); } @@ -829,7 +827,7 @@ void cfg80211_pmsr_wdev_down(struct wireless_dev *wdev) } spin_unlock_bh(&wdev->pmsr_lock); - cancel_work_sync(&wdev->pmsr_free_wk); + wiphy_work_cancel(wdev->wiphy, &wdev->pmsr_free_wk); if (found) cfg80211_pmsr_process_abort(wdev); @@ -844,7 +842,7 @@ void cfg80211_release_pmsr(struct wireless_dev *wdev, u32 portid) list_for_each_entry(req, &wdev->pmsr_list, list) { if (req->nl_portid == portid) { req->nl_portid = 0; - schedule_work(&wdev->pmsr_free_wk); + wiphy_work_queue(wdev->wiphy, &wdev->pmsr_free_wk); } } spin_unlock_bh(&wdev->pmsr_lock); From 0a2581cbae9e442835f68d22044157db61cdf54d Mon Sep 17 00:00:00 2001 From: Corentin Labbe Date: Fri, 3 Jul 2026 13:49:32 +0000 Subject: [PATCH 0549/1054] wifi: ralink: RT2X00: init EEPROM properly I have an hostapd setup with a 01:00.0 Network controller: Ralink corp. RT2790 Wireless 802.11n 1T/2R PCIe The setup work fine on 6.18.26-gentoo It breaks on 6.18.33-gentoo (and still broken on 6.18.37) I found an hint in dmesg: On 6.18.26-gentoo I see: May 31 15:48:45 trash01 kernel: ieee80211 phy0: rt2x00_set_rf: Info - RF chipset 0003 detected On 6.18.33-gentoo I see: May 31 15:22:57 trash01 kernel: ieee80211 phy0: rt2x00_set_rf: Info - RF chipset 0006 detected The RF chipset seems badly detected. The problem was the EEPROM which was badly initialized. Probably the origin was in some PCI change but unfortunately I couldn't play to bisect/reboot often the board with this card to do it. Signed-off-by: Corentin Labbe Acked-by: Stanislaw Gruszka Link: https://patch.msgid.link/20260703134932.3786771-1-clabbe@baylibre.com Signed-off-by: Johannes Berg --- drivers/net/wireless/ralink/rt2x00/rt2400pci.c | 2 +- drivers/net/wireless/ralink/rt2x00/rt2500pci.c | 2 +- drivers/net/wireless/ralink/rt2x00/rt2800pci.c | 2 +- drivers/net/wireless/ralink/rt2x00/rt61pci.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ralink/rt2x00/rt2400pci.c b/drivers/net/wireless/ralink/rt2x00/rt2400pci.c index cac191304bf5..b846fe589c2b 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2400pci.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2400pci.c @@ -1429,7 +1429,7 @@ static irqreturn_t rt2400pci_interrupt(int irq, void *dev_instance) */ static int rt2400pci_validate_eeprom(struct rt2x00_dev *rt2x00dev) { - struct eeprom_93cx6 eeprom; + struct eeprom_93cx6 eeprom = {}; u32 reg; u16 word; u8 *mac; diff --git a/drivers/net/wireless/ralink/rt2x00/rt2500pci.c b/drivers/net/wireless/ralink/rt2x00/rt2500pci.c index fc35b60e422c..be9df35acc33 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2500pci.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2500pci.c @@ -1555,7 +1555,7 @@ static irqreturn_t rt2500pci_interrupt(int irq, void *dev_instance) */ static int rt2500pci_validate_eeprom(struct rt2x00_dev *rt2x00dev) { - struct eeprom_93cx6 eeprom; + struct eeprom_93cx6 eeprom = {}; u32 reg; u16 word; u8 *mac; diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800pci.c b/drivers/net/wireless/ralink/rt2x00/rt2800pci.c index 4fa14bb573ad..2596b9fcc7dd 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800pci.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800pci.c @@ -108,7 +108,7 @@ static void rt2800pci_eepromregister_write(struct eeprom_93cx6 *eeprom) static int rt2800pci_read_eeprom_pci(struct rt2x00_dev *rt2x00dev) { - struct eeprom_93cx6 eeprom; + struct eeprom_93cx6 eeprom = {}; u32 reg; reg = rt2x00mmio_register_read(rt2x00dev, E2PROM_CSR); diff --git a/drivers/net/wireless/ralink/rt2x00/rt61pci.c b/drivers/net/wireless/ralink/rt2x00/rt61pci.c index 79e1fd0a1fbd..d4783658b2c5 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt61pci.c +++ b/drivers/net/wireless/ralink/rt2x00/rt61pci.c @@ -2298,7 +2298,7 @@ static irqreturn_t rt61pci_interrupt(int irq, void *dev_instance) */ static int rt61pci_validate_eeprom(struct rt2x00_dev *rt2x00dev) { - struct eeprom_93cx6 eeprom; + struct eeprom_93cx6 eeprom = {}; u32 reg; u16 word; u8 *mac; From 13ff543e0b2c713aedeaadadde686686e949dc78 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 09:11:40 +0800 Subject: [PATCH 0550/1054] wifi: libertas: reject short monitor TX frames In monitor mode, lbs_hard_start_xmit() casts skb->data to a radiotap TX header, skips that header, and then copies the 802.11 destination address from offset 4 in the remaining frame. The generic length check only rejects zero-length and oversized skbs, so a short monitor frame can be read past the end of the skb data. Require enough bytes for the radiotap TX header and the destination address field before using the monitor-mode header layout. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260704011140.37639-1-pengpeng@iscas.ac.cn Signed-off-by: Johannes Berg --- drivers/net/wireless/marvell/libertas/tx.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/wireless/marvell/libertas/tx.c b/drivers/net/wireless/marvell/libertas/tx.c index 27304a98787d..13d08022e414 100644 --- a/drivers/net/wireless/marvell/libertas/tx.c +++ b/drivers/net/wireless/marvell/libertas/tx.c @@ -117,6 +117,13 @@ netdev_tx_t lbs_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) { struct tx_radiotap_hdr *rtap_hdr = (void *)skb->data; + if (skb->len < sizeof(*rtap_hdr) + 4 + ETH_ALEN) { + lbs_deb_tx("tx err: short monitor frame %u\n", skb->len); + dev->stats.tx_dropped++; + dev->stats.tx_errors++; + goto free; + } + /* set txpd fields from the radiotap header */ txpd->tx_control = cpu_to_le32(convert_radiotap_rate_to_mv(rtap_hdr->rate)); From d06a3e60c8fead962f08cf951eb1de7bd22dab76 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 09:12:30 +0800 Subject: [PATCH 0551/1054] wifi: rsi: bound background scan probe request copy rsi_send_bgscan_probe_req() allocates room for struct rsi_bgscan_probe plus MAX_BGSCAN_PROBE_REQ_LEN bytes, but copies the entire mac80211-generated probe request skb after the fixed header. The probe request length depends on scan IEs and is not checked against the fixed firmware buffer. Reject generated probe requests that do not fit the firmware command buffer before copying them into the skb. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260704011231.45593-1-pengpeng@iscas.ac.cn Signed-off-by: Johannes Berg --- drivers/net/wireless/rsi/rsi_91x_mgmt.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c index 2ddf4d158bfe..bb167f03367b 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mgmt.c +++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c @@ -1913,6 +1913,12 @@ int rsi_send_bgscan_probe_req(struct rsi_common *common, return -ENOMEM; } + if (probereq_skb->len > MAX_BGSCAN_PROBE_REQ_LEN) { + dev_kfree_skb(probereq_skb); + dev_kfree_skb(skb); + return -EINVAL; + } + memcpy(&skb->data[frame_len], probereq_skb->data, probereq_skb->len); bgscan->probe_req_length = cpu_to_le16(probereq_skb->len); From 74ed3669f26803b1761c1f55403062bea44c3466 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sun, 5 Jul 2026 16:35:19 +0800 Subject: [PATCH 0552/1054] wifi: libipw: fix key index receive bound checks libipw_rx() reads skb->data[hdrlen + 3] to extract the WEP key index in both the software-decrypt key selection path and the hardware-decrypted IV/ICV strip path. In both places the existing guard only checks skb->len >= hdrlen + 3, which proves bytes up to hdrlen + 2 but not the byte at hdrlen + 3. Require hdrlen + 4 bytes before reading that item in both paths. This is a local source-boundary check only; it does not change the key index semantics. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260705083519.23567-1-pengpeng@iscas.ac.cn Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/ipw2x00/libipw_rx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/ipw2x00/libipw_rx.c b/drivers/net/wireless/intel/ipw2x00/libipw_rx.c index b7bc94f7abd8..c8841f9b9ad9 100644 --- a/drivers/net/wireless/intel/ipw2x00/libipw_rx.c +++ b/drivers/net/wireless/intel/ipw2x00/libipw_rx.c @@ -414,7 +414,7 @@ int libipw_rx(struct libipw_device *ieee, struct sk_buff *skb, ieee->host_mc_decrypt : ieee->host_decrypt; if (can_be_decrypted) { - if (skb->len >= hdrlen + 3) { + if (skb->len >= hdrlen + 4) { /* Top two-bits of byte 3 are the key index */ keyidx = skb->data[hdrlen + 3] >> 6; } @@ -660,7 +660,7 @@ int libipw_rx(struct libipw_device *ieee, struct sk_buff *skb, int trimlen = 0; /* Top two-bits of byte 3 are the key index */ - if (skb->len >= hdrlen + 3) + if (skb->len >= hdrlen + 4) keyidx = skb->data[hdrlen + 3] >> 6; /* To strip off any security data which appears before the From 8ecdeb8b8a33b22c597299043c0dcfce50beb9ea Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sun, 5 Jul 2026 16:48:24 +0800 Subject: [PATCH 0553/1054] wifi: rsi: validate beacon length before fixed buffer copy rsi_prepare_beacon() copies the mac80211 beacon frame after FRAME_DESC_SZ into a management skb whose usable tailroom may be smaller than MAX_MGMT_PKT_SIZE after alignment. Validate the beacon length against the actual tailroom before the copy and skb_put(). Leave ownership of the management skb with the caller on error, matching the existing rsi_send_beacon() cleanup path. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260705084824.68105-1-pengpeng@iscas.ac.cn Signed-off-by: Johannes Berg --- drivers/net/wireless/rsi/rsi_91x_hal.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/net/wireless/rsi/rsi_91x_hal.c b/drivers/net/wireless/rsi/rsi_91x_hal.c index a0c36144eb0b..501071104a9f 100644 --- a/drivers/net/wireless/rsi/rsi_91x_hal.c +++ b/drivers/net/wireless/rsi/rsi_91x_hal.c @@ -431,6 +431,7 @@ int rsi_prepare_beacon(struct rsi_common *common, struct sk_buff *skb) struct ieee80211_vif *vif; struct sk_buff *mac_bcn; u8 vap_id = 0, i; + unsigned int tailroom; u16 tim_offset = 0; for (i = 0; i < RSI_MAX_VIFS; i++) { @@ -480,6 +481,13 @@ int rsi_prepare_beacon(struct rsi_common *common, struct sk_buff *skb) if (mac_bcn->data[tim_offset + 2] == 0) bcn_frm->frame_info |= cpu_to_le16(RSI_DATA_DESC_DTIM_BEACON); + tailroom = skb_tailroom(skb); + if (tailroom < FRAME_DESC_SZ || + mac_bcn->len > tailroom - FRAME_DESC_SZ) { + dev_kfree_skb(mac_bcn); + return -EMSGSIZE; + } + memcpy(&skb->data[FRAME_DESC_SZ], mac_bcn->data, mac_bcn->len); skb_put(skb, mac_bcn->len + FRAME_DESC_SZ); From 07a95ec2b54774201fdf4ef7ffb0ca2ab19ed29c Mon Sep 17 00:00:00 2001 From: Zhao Li Date: Wed, 10 Jun 2026 19:22:09 +0800 Subject: [PATCH 0554/1054] wifi: nl80211: free RNR data on MBSSID mismatch nl80211_parse_beacon() rejects EMA RNR data when there are fewer RNR entries than MBSSID entries. The rejected RNR allocation has not been attached to the beacon data yet, so free it before returning the error. Fixes: dbbb27e183b1 ("cfg80211: support RNR for EMA AP") Signed-off-by: Zhao Li Link: https://patch.msgid.link/20260610112208.1308-2-enderaoelyther@gmail.com Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 53b4b3f76697..056388c04599 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -6803,8 +6803,10 @@ static int nl80211_parse_beacon(struct cfg80211_registered_device *rdev, if (IS_ERR(rnr)) return PTR_ERR(rnr); - if (rnr && rnr->cnt < bcn->mbssid_ies->cnt) + if (rnr && rnr->cnt < bcn->mbssid_ies->cnt) { + kfree(rnr); return -EINVAL; + } bcn->rnr_ies = rnr; } From 57d503ce32eccfa7650065ca4c560f7e29a2e676 Mon Sep 17 00:00:00 2001 From: Zhao Li Date: Fri, 12 Jun 2026 00:19:45 +0800 Subject: [PATCH 0555/1054] wifi: mac80211: validate extension-frame layout before RX Extension frames only have the extension header at the regular 802.11 header offset. The generic RX path can still reach helpers and interface dispatch code that read regular header address fields before unsupported extension subtypes are dropped. mac80211 currently only handles S1G beacon extension frames. Drop other extension subtypes before they can reach regular-header RX processing. For S1G beacons, linearize the SKB with the management-frame path and require the fixed S1G beacon header, including optional fixed fields indicated by frame control, before generic RX dispatch. Route S1G beacons through the station/default-link RX path without regular-header station lookup. Avoid regular-header address reads in the mac80211 RX paths that process S1G extension beacons, including accept-frame, duplicate-detection, address-copy, and MLO address-translation paths. Also make ieee80211_get_bssid() length-safe before returning the S1G source-address pointer. Fixes: 09a740ce352e ("mac80211: receive and process S1G beacons") Cc: stable@vger.kernel.org Signed-off-by: Zhao Li Link: https://patch.msgid.link/20260611161943.91069-5-enderaoelyther@gmail.com Signed-off-by: Johannes Berg --- net/mac80211/rx.c | 34 ++++++++++++++++++++++++++++++++-- net/mac80211/util.c | 3 +++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index fb9a3574afe9..d9ea19be075d 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1526,6 +1526,9 @@ ieee80211_rx_h_check_dup(struct ieee80211_rx_data *rx) if (status->flag & RX_FLAG_DUP_VALIDATED) return RX_CONTINUE; + if (ieee80211_is_ext(hdr->frame_control)) + return RX_CONTINUE; + /* * Drop duplicate 802.11 retransmissions * (IEEE 802.11-2012: 9.3.2.10 "Duplicate detection and recovery") @@ -4510,12 +4513,16 @@ static bool ieee80211_accept_frame(struct ieee80211_rx_data *rx) struct ieee80211_hdr *hdr = (void *)skb->data; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); u8 *bssid = ieee80211_get_bssid(hdr, skb->len, sdata->vif.type); - bool multicast = is_multicast_ether_addr(hdr->addr1) || - ieee80211_is_s1g_beacon(hdr->frame_control); + bool multicast; static const u8 nan_network_id[ETH_ALEN] __aligned(2) = { 0x51, 0x6F, 0x9A, 0x01, 0x00, 0x00 }; + if (ieee80211_is_s1g_beacon(hdr->frame_control)) + return sdata->vif.type == NL80211_IFTYPE_STATION && bssid; + + multicast = is_multicast_ether_addr(hdr->addr1); + switch (sdata->vif.type) { case NL80211_IFTYPE_STATION: if (!bssid && !sdata->u.mgd.use_4addr) @@ -5212,6 +5219,11 @@ static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx, hdr = (struct ieee80211_hdr *)rx->skb->data; } + if (ieee80211_is_s1g_beacon(hdr->frame_control)) { + ieee80211_invoke_rx_handlers(rx); + return true; + } + /* Store a copy of the pre-translated link addresses for SW crypto */ if (unlikely(is_unicast_ether_addr(hdr->addr1) && !ieee80211_is_data(hdr->frame_control))) @@ -5301,6 +5313,13 @@ static bool ieee80211_rx_for_interface(struct ieee80211_rx_data *rx, struct sta_info *sta; int link_id = -1; + if (ieee80211_is_s1g_beacon(hdr->frame_control)) { + if (!ieee80211_rx_data_set_sta(rx, NULL, -1)) + return false; + + return ieee80211_prepare_and_rx_handle(rx, skb, consume); + } + /* * Look up link station first, in case there's a * chance that they might have a link address that @@ -5376,6 +5395,17 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw, err = -ENOBUFS; else err = skb_linearize(skb); + } else if (ieee80211_is_s1g_beacon(fc)) { + size_t s1g_hdr_len = offsetof(struct ieee80211_ext, + u.s1g_beacon.variable) + + ieee80211_s1g_optional_len(fc); + + if (skb->len < s1g_hdr_len) + err = -ENOBUFS; + else + err = skb_linearize(skb); + } else if (ieee80211_is_ext(fc)) { + err = -EINVAL; } else { err = !pskb_may_pull(skb, ieee80211_hdrlen(fc)); } diff --git a/net/mac80211/util.c b/net/mac80211/util.c index f6d4ae4127c8..59f73dabe6e0 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -73,6 +73,9 @@ u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len, if (ieee80211_is_s1g_beacon(fc)) { struct ieee80211_ext *ext = (void *) hdr; + if (len < offsetofend(struct ieee80211_ext, u.s1g_beacon.sa)) + return NULL; + return ext->u.s1g_beacon.sa; } From 4e5a4641e7b4763656336b7891d01359aaf363cd Mon Sep 17 00:00:00 2001 From: Zhao Li Date: Fri, 12 Jun 2026 00:19:46 +0800 Subject: [PATCH 0556/1054] wifi: cfg80211: derive S1G beacon TSF from S1G fields cfg80211_inform_bss_frame_data() parses S1G beacons with the extension frame layout, but still reads the TSF from the regular probe response layout after the S1G branch. For S1G beacons that reads bytes at the regular management-frame timestamp offset instead of the S1G timestamp. Use the 32-bit S1G beacon timestamp and the S1G Beacon Compatibility element's TSF completion field when informing an S1G BSS. Keep the regular management-frame timestamp read in the non-S1G branch. Fixes: 9eaffe5078ca ("cfg80211: convert S1G beacon to scan results") Signed-off-by: Zhao Li Tested-by: Lachlan Hodges Reviewed-by: Lachlan Hodges Link: https://patch.msgid.link/20260611161943.91069-6-enderaoelyther@gmail.com Signed-off-by: Johannes Berg --- net/wireless/scan.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/net/wireless/scan.c b/net/wireless/scan.c index e1c09040a5c8..5c97b5bd5d69 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -3314,14 +3314,15 @@ cfg80211_inform_bss_frame_data(struct wiphy *wiphy, bssid = ext->u.s1g_beacon.sa; capability = le16_to_cpu(compat->compat_info); beacon_interval = le16_to_cpu(compat->beacon_int); + tsf = le32_to_cpu(ext->u.s1g_beacon.timestamp); + tsf |= (u64)le32_to_cpu(compat->tsf_completion) << 32; } else { bssid = mgmt->bssid; beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int); capability = le16_to_cpu(mgmt->u.probe_resp.capab_info); + tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp); } - tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp); - if (ieee80211_is_probe_resp(mgmt->frame_control)) ftype = CFG80211_BSS_FTYPE_PRESP; else if (ext) From 293baeae9b2434a3e432629d7720b5603db2d77e Mon Sep 17 00:00:00 2001 From: Zhao Li Date: Fri, 12 Jun 2026 01:35:07 +0800 Subject: [PATCH 0557/1054] wifi: ieee80211: validate MLE common info length ieee80211_mle_common_size() uses the first common-info octet as the common information length for all known MLE types. However, ieee80211_mle_size_ok() only validates that octet for Basic, Probe Request, and TDLS MLEs. Reconfiguration MLEs also skipped the length octet when calculating the minimum common size, and Priority Access MLEs skipped validation of the advertised common information length. Account for the Reconfiguration common-info length octet and validate the advertised common information length for all known MLE types. Keep unknown-type handling unchanged. Fixes: 0f48b8b88aa9 ("wifi: ieee80211: add definitions for multi-link element") Cc: stable@vger.kernel.org Signed-off-by: Zhao Li Link: https://patch.msgid.link/20260611173506.36838-2-enderaoelyther@gmail.com [remove now misleading comment] Signed-off-by: Johannes Berg --- include/linux/ieee80211-eht.h | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/include/linux/ieee80211-eht.h b/include/linux/ieee80211-eht.h index 18f9c662cf4c..c109722b1969 100644 --- a/include/linux/ieee80211-eht.h +++ b/include/linux/ieee80211-eht.h @@ -857,7 +857,7 @@ static inline bool ieee80211_mle_size_ok(const u8 *data, size_t len) const struct ieee80211_multi_link_elem *mle = (const void *)data; u8 fixed = sizeof(*mle); u8 common = 0; - bool check_common_len = false; + u8 common_len; u16 control; if (!data || len < fixed) @@ -868,7 +868,6 @@ static inline bool ieee80211_mle_size_ok(const u8 *data, size_t len) switch (u16_get_bits(control, IEEE80211_ML_CONTROL_TYPE)) { case IEEE80211_ML_CONTROL_TYPE_BASIC: common += sizeof(struct ieee80211_mle_basic_common_info); - check_common_len = true; if (control & IEEE80211_MLC_BASIC_PRES_LINK_ID) common += 1; if (control & IEEE80211_MLC_BASIC_PRES_BSS_PARAM_CH_CNT) @@ -888,9 +887,9 @@ static inline bool ieee80211_mle_size_ok(const u8 *data, size_t len) common += sizeof(struct ieee80211_mle_preq_common_info); if (control & IEEE80211_MLC_PREQ_PRES_MLD_ID) common += 1; - check_common_len = true; break; case IEEE80211_ML_CONTROL_TYPE_RECONF: + common += 1; if (control & IEEE80211_MLC_RECONF_PRES_MLD_MAC_ADDR) common += ETH_ALEN; if (control & IEEE80211_MLC_RECONF_PRES_EML_CAPA) @@ -902,7 +901,6 @@ static inline bool ieee80211_mle_size_ok(const u8 *data, size_t len) break; case IEEE80211_ML_CONTROL_TYPE_TDLS: common += sizeof(struct ieee80211_mle_tdls_common_info); - check_common_len = true; break; case IEEE80211_ML_CONTROL_TYPE_PRIO_ACCESS: common = ETH_ALEN + 1; @@ -915,11 +913,9 @@ static inline bool ieee80211_mle_size_ok(const u8 *data, size_t len) if (len < fixed + common) return false; - if (!check_common_len) - return true; + common_len = mle->variable[0]; - /* if present, common length is the first octet there */ - return mle->variable[0] >= common; + return common_len >= common && common_len <= len - fixed; } /** From 7f4b01812323443b55e4c65381c9dc851ff009e3 Mon Sep 17 00:00:00 2001 From: Zhao Li Date: Fri, 12 Jun 2026 21:18:55 +0800 Subject: [PATCH 0558/1054] wifi: nl80211: validate nested MBSSID IE blobs Validate each nested NL80211_ATTR_MBSSID_ELEMS entry as a well-formed information-element stream before storing it for beacon construction. RNR parsing already validates each nested blob with validate_ie_attr() before storing it. Apply the same syntactic IE validation to MBSSID entries before counting and copying their data and length pointers. Fixes: dc1e3cb8da8b ("nl80211: MBSSID and EMA support in AP mode") Assisted-by: Codex:gpt-5.5 Assisted-by: Claude:claude-opus-4.8 Signed-off-by: Zhao Li Link: https://patch.msgid.link/20260612131854.43575-3-enderaoelyther@gmail.com Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 056388c04599..26b781770d4f 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -6510,7 +6510,8 @@ static int nl80211_parse_mbssid_config(struct wiphy *wiphy, } static struct cfg80211_mbssid_elems * -nl80211_parse_mbssid_elems(struct wiphy *wiphy, struct nlattr *attrs) +nl80211_parse_mbssid_elems(struct wiphy *wiphy, struct nlattr *attrs, + struct netlink_ext_ack *extack) { struct nlattr *nl_elems; struct cfg80211_mbssid_elems *elems; @@ -6521,6 +6522,12 @@ nl80211_parse_mbssid_elems(struct wiphy *wiphy, struct nlattr *attrs) return ERR_PTR(-EINVAL); nla_for_each_nested(nl_elems, attrs, rem_elems) { + int ret; + + ret = validate_ie_attr(nl_elems, extack); + if (ret) + return ERR_PTR(ret); + if (num_elems >= 255) return ERR_PTR(-EINVAL); num_elems++; @@ -6787,7 +6794,8 @@ static int nl80211_parse_beacon(struct cfg80211_registered_device *rdev, if (attrs[NL80211_ATTR_MBSSID_ELEMS]) { struct cfg80211_mbssid_elems *mbssid = nl80211_parse_mbssid_elems(&rdev->wiphy, - attrs[NL80211_ATTR_MBSSID_ELEMS]); + attrs[NL80211_ATTR_MBSSID_ELEMS], + extack); if (IS_ERR(mbssid)) return PTR_ERR(mbssid); From 172f06023669f0a96d32511669ff45c600731380 Mon Sep 17 00:00:00 2001 From: Zhao Li Date: Fri, 12 Jun 2026 21:18:56 +0800 Subject: [PATCH 0559/1054] wifi: nl80211: constrain MBSSID TX link ID range MBSSID transmitted-profile link IDs are valid only in the range 0..IEEE80211_MLD_MAX_NUM_LINKS - 1. Constrain the nl80211 policy to reject out-of-range values during attribute validation. Fixes: 37523c3c47b3 ("wifi: nl80211: add link id of transmitted profile for MLO MBSSID") Assisted-by: Codex:gpt-5.5 Assisted-by: Claude:claude-opus-4.8 Signed-off-by: Zhao Li Link: https://patch.msgid.link/20260612131854.43575-4-enderaoelyther@gmail.com Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 26b781770d4f..3e05db942a18 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -630,7 +630,7 @@ nl80211_mbssid_config_policy[NL80211_MBSSID_CONFIG_ATTR_MAX + 1] = { [NL80211_MBSSID_CONFIG_ATTR_TX_IFINDEX] = { .type = NLA_U32 }, [NL80211_MBSSID_CONFIG_ATTR_EMA] = { .type = NLA_FLAG }, [NL80211_MBSSID_CONFIG_ATTR_TX_LINK_ID] = - NLA_POLICY_MAX(NLA_U8, IEEE80211_MLD_MAX_NUM_LINKS), + NLA_POLICY_RANGE(NLA_U8, 0, IEEE80211_MLD_MAX_NUM_LINKS - 1), }; static const struct nla_policy From 41aa973eb05922848dded26875c55ef982ac1c49 Mon Sep 17 00:00:00 2001 From: Zhao Li Date: Fri, 12 Jun 2026 21:36:57 +0800 Subject: [PATCH 0560/1054] wifi: cfg80211: validate PMSR measurement type data PMSR request parsing accepts missing or duplicated measurement type entries in NL80211_PMSR_REQ_ATTR_DATA. Track whether one measurement type was already provided, reject a second one immediately, and return an error if the request data block contains no measurement type at all. Fixes: 9bb7e0f24e7e7 ("cfg80211: add peer measurement with FTM initiator API") Assisted-by: Codex:gpt-5.5 Assisted-by: Claude:claude-opus-4.8 Signed-off-by: Zhao Li Link: https://patch.msgid.link/20260612133656.92900-2-enderaoelyther@gmail.com Signed-off-by: Johannes Berg --- net/wireless/pmsr.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/net/wireless/pmsr.c b/net/wireless/pmsr.c index 2c8db33d9c30..4962456fda30 100644 --- a/net/wireless/pmsr.c +++ b/net/wireless/pmsr.c @@ -310,6 +310,7 @@ static int pmsr_parse_peer(struct cfg80211_registered_device *rdev, { struct nlattr *tb[NL80211_PMSR_PEER_ATTR_MAX + 1]; struct nlattr *req[NL80211_PMSR_REQ_ATTR_MAX + 1]; + bool have_measurement_type = false; struct nlattr *treq; int err, rem; @@ -376,6 +377,14 @@ static int pmsr_parse_peer(struct cfg80211_registered_device *rdev, } nla_for_each_nested(treq, req[NL80211_PMSR_REQ_ATTR_DATA], rem) { + if (have_measurement_type) { + NL_SET_ERR_MSG_ATTR(info->extack, treq, + "multiple measurement types in request data"); + return -EINVAL; + } + + have_measurement_type = true; + switch (nla_type(treq)) { case NL80211_PMSR_TYPE_FTM: err = pmsr_parse_ftm(rdev, treq, out, info); @@ -385,10 +394,16 @@ static int pmsr_parse_peer(struct cfg80211_registered_device *rdev, "unsupported measurement type"); err = -EINVAL; } + if (err) + return err; } - if (err) - return err; + if (!have_measurement_type) { + NL_SET_ERR_MSG_ATTR(info->extack, + req[NL80211_PMSR_REQ_ATTR_DATA], + "missing measurement type in request data"); + return -EINVAL; + } return 0; } From 36230936468f0ba4930e94aef496fc229d4bb951 Mon Sep 17 00:00:00 2001 From: Zhao Li Date: Fri, 12 Jun 2026 21:37:04 +0800 Subject: [PATCH 0561/1054] wifi: cfg80211: validate PMSR FTM preamble range PMSR FTM request parsing accepts preamble values outside the enumerated nl80211 preamble range. Reject out-of-range values before using them in the parser capability bit test using the policy. Fixes: 9bb7e0f24e7e7 ("cfg80211: add peer measurement with FTM initiator API") Assisted-by: Codex:gpt-5.5 Assisted-by: Claude:claude-opus-4.8 Signed-off-by: Zhao Li Link: https://patch.msgid.link/20260612133703.93274-2-enderaoelyther@gmail.com [drop unnecessary check] Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 3e05db942a18..625c99cf70a3 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -461,7 +461,9 @@ nl80211_ftm_responder_policy[NL80211_FTM_RESP_ATTR_MAX + 1] = { static const struct nla_policy nl80211_pmsr_ftm_req_attr_policy[NL80211_PMSR_FTM_REQ_ATTR_MAX + 1] = { [NL80211_PMSR_FTM_REQ_ATTR_ASAP] = { .type = NLA_FLAG }, - [NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE] = { .type = NLA_U32 }, + [NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE] = + NLA_POLICY_RANGE(NLA_U32, NL80211_PREAMBLE_LEGACY, + NL80211_PREAMBLE_HE), [NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP] = NLA_POLICY_MAX(NLA_U8, 15), [NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD] = { .type = NLA_U16 }, From 69ef6a7ec277f16d216be8da2b3cbe872786c999 Mon Sep 17 00:00:00 2001 From: Zhao Li Date: Fri, 12 Jun 2026 21:37:11 +0800 Subject: [PATCH 0562/1054] wifi: cfg80211: reject unsupported PMSR FTM location requests PMSR FTM location request flags are syntactically valid, but they must be rejected when the device capability does not advertise support for them. Return an error immediately after rejecting unsupported LCI or civic location request bits so the request cannot reach the driver. Fixes: 9bb7e0f24e7e7 ("cfg80211: add peer measurement with FTM initiator API") Assisted-by: Codex:gpt-5.5 Assisted-by: Claude:claude-opus-4.8 Signed-off-by: Zhao Li Link: https://patch.msgid.link/20260612133710.93544-2-enderaoelyther@gmail.com Signed-off-by: Johannes Berg --- net/wireless/pmsr.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/wireless/pmsr.c b/net/wireless/pmsr.c index 4962456fda30..34ecbc6644a3 100644 --- a/net/wireless/pmsr.c +++ b/net/wireless/pmsr.c @@ -125,6 +125,7 @@ static int pmsr_parse_ftm(struct cfg80211_registered_device *rdev, NL_SET_ERR_MSG_ATTR(info->extack, tb[NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI], "FTM: LCI request not supported"); + return -EOPNOTSUPP; } out->ftm.request_civicloc = @@ -133,6 +134,7 @@ static int pmsr_parse_ftm(struct cfg80211_registered_device *rdev, NL_SET_ERR_MSG_ATTR(info->extack, tb[NL80211_PMSR_FTM_REQ_ATTR_REQUEST_CIVICLOC], "FTM: civic location request not supported"); + return -EOPNOTSUPP; } out->ftm.trigger_based = From 57c05ce14fea03df01288fe1250f49197e161710 Mon Sep 17 00:00:00 2001 From: Zhao Li Date: Fri, 12 Jun 2026 21:37:18 +0800 Subject: [PATCH 0563/1054] wifi: cfg80211: reject empty PMSR peer lists A PMSR request with an empty peers array is not a useful request and weakens the cfg80211-to-driver contract by allowing start_pmsr() with no target peer. Reject empty peer lists before allocating the request object or calling into the driver. Fixes: 9bb7e0f24e7e7 ("cfg80211: add peer measurement with FTM initiator API") Assisted-by: Codex:gpt-5.5 Assisted-by: Claude:claude-opus-4.8 Signed-off-by: Zhao Li Link: https://patch.msgid.link/20260612133717.93783-2-enderaoelyther@gmail.com Signed-off-by: Johannes Berg --- net/wireless/pmsr.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net/wireless/pmsr.c b/net/wireless/pmsr.c index 34ecbc6644a3..34c3625f7fd5 100644 --- a/net/wireless/pmsr.c +++ b/net/wireless/pmsr.c @@ -444,6 +444,11 @@ int nl80211_pmsr_start(struct sk_buff *skb, struct genl_info *info) } } + if (!count) { + NL_SET_ERR_MSG_ATTR(info->extack, peers, "No peers specified"); + return -EINVAL; + } + req = kzalloc_flex(*req, peers, count); if (!req) return -ENOMEM; From 035ed430ce6a2c35b01e211844a9f0a7643e57a4 Mon Sep 17 00:00:00 2001 From: Zhao Li Date: Fri, 12 Jun 2026 23:24:41 +0800 Subject: [PATCH 0564/1054] wifi: mac80211: avoid non-S1G AID fallback for S1G assoc When assoc_data->s1g is set and no AID Response element is present, falling back to mgmt->u.assoc_resp.aid reads the non-S1G association-response layout. Keep the fallback for non-S1G only. If a successful S1G association response omits the AID Response element, abandon the association instead of proceeding with AID 0. Initialize aid to 0 for other S1G responses so the later mask and logging flow keeps a defined value without reading the non-S1G layout. Fixes: 2a8a6b7c4cb0 ("wifi: mac80211: handle station association response with S1G") Assisted-by: Codex:gpt-5.5 Assisted-by: Claude:claude-opus-4.8 Signed-off-by: Zhao Li Link: https://patch.msgid.link/20260612152440.25955-2-enderaoelyther@gmail.com Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 9e92337bb6f9..86c90b504cfd 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -7138,7 +7138,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_mgd_assoc_data *assoc_data = ifmgd->assoc_data; - u16 capab_info, status_code, aid; + u16 capab_info, status_code, aid = 0; struct ieee80211_elems_parse_params parse_params = { .bss = NULL, .link_id = -1, @@ -7217,8 +7217,10 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, if (elems->aid_resp) aid = le16_to_cpu(elems->aid_resp->aid); - else + else if (!assoc_data->s1g) aid = le16_to_cpu(mgmt->u.assoc_resp.aid); + else if (status_code == WLAN_STATUS_SUCCESS) + goto abandon_assoc; /* * The 5 MSB of the AID field are reserved for a non-S1G STA. For From 4a360c6e18dfa9d70006c7247a6a8cc8dfe0d60f Mon Sep 17 00:00:00 2001 From: Zhao Li Date: Sat, 13 Jun 2026 02:50:45 +0800 Subject: [PATCH 0565/1054] wifi: mac80211: validate deauth frame length before reason access ieee80211_rx_mgmt_deauth() reads the deauth reason code before checking that the fixed field is actually present in the received frame. Validate the deauth frame length first and only then read the reason code. Assisted-by: Codex:gpt-5.5 Assisted-by: Claude:claude-opus-4.8 Signed-off-by: Zhao Li Link: https://patch.msgid.link/20260612185042.66260-6-enderaoelyther@gmail.com Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 86c90b504cfd..fa773f3b0541 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -5641,13 +5641,15 @@ static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, size_t len) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; - u16 reason_code = le16_to_cpu(mgmt->u.deauth.reason_code); + u16 reason_code; lockdep_assert_wiphy(sdata->local->hw.wiphy); - if (len < 24 + 2) + if (len < offsetofend(struct ieee80211_mgmt, u.deauth.reason_code)) return; + reason_code = le16_to_cpu(mgmt->u.deauth.reason_code); + if (!ether_addr_equal(mgmt->bssid, mgmt->sa)) { ieee80211_tdls_handle_disconnect(sdata, mgmt->sa, reason_code); return; From fbe0bb2b75eb3c61e8464486506253d1b471240b Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 1 Jul 2026 22:58:35 +0900 Subject: [PATCH 0566/1054] ksmbd: validate SID namespace before mapping IDs sid_to_id() currently treats the last subauthority of any owner or group SID as a Unix uid or gid. For example, this maps Everyone (S-1-1-0) to uid 0 and BUILTIN\Users (S-1-5-32-545) to gid 545. When an SMB2 CREATE security descriptor contains those SIDs, ksmbd attempts to change the newly created file to the bogus Unix ownership. notify_change() then returns -EPERM, which makes smb2.create.aclfile fail with NT_STATUS_SHARING_VIOLATION. Validate the SID prefix before extracting its RID. Only server-domain owner SIDs and S-1-22-2 Unix group SIDs have local ID representations. Treat other valid Windows SIDs as unmapped so their original values can still be preserved in the NT ACL xattr. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/smbacl.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/fs/smb/server/smbacl.c b/fs/smb/server/smbacl.c index 9c59c8f73b66..67b39b4d218c 100644 --- a/fs/smb/server/smbacl.c +++ b/fs/smb/server/smbacl.c @@ -258,6 +258,7 @@ static int sid_to_id(struct mnt_idmap *idmap, struct smb_sid *psid, uint sidtype, struct smb_fattr *fattr) { + const struct smb_sid *sid_prefix; int rc = -EINVAL; /* @@ -279,6 +280,12 @@ static int sid_to_id(struct mnt_idmap *idmap, kuid_t uid; uid_t id; + /* Only the server domain RID has a local uid representation. */ + sid_prefix = &server_conf.domain_sid; + if (psid->num_subauth != sid_prefix->num_subauth + 1 || + compare_sids(psid, sid_prefix)) + return -EINVAL; + id = le32_to_cpu(psid->sub_auth[psid->num_subauth - 1]); uid = KUIDT_INIT(id); uid = from_vfsuid(idmap, &init_user_ns, VFSUIDT_INIT(uid)); @@ -290,6 +297,12 @@ static int sid_to_id(struct mnt_idmap *idmap, kgid_t gid; gid_t id; + /* Local gids are represented by S-1-22-2-. */ + sid_prefix = &sid_unix_groups; + if (psid->num_subauth != sid_prefix->num_subauth + 1 || + compare_sids(psid, sid_prefix)) + return -EINVAL; + id = le32_to_cpu(psid->sub_auth[psid->num_subauth - 1]); gid = KGIDT_INIT(id); gid = from_vfsgid(idmap, &init_user_ns, VFSGIDT_INIT(gid)); @@ -900,9 +913,9 @@ int parse_sec_desc(struct mnt_idmap *idmap, struct smb_ntsd *pntsd, rc = sid_to_id(idmap, owner_sid_ptr, SIDOWNER, fattr); if (rc) { - pr_err("%s: Error %d mapping Owner SID to uid\n", - __func__, rc); + ksmbd_debug(SMB, "Owner SID has no Unix uid mapping\n"); owner_sid_ptr = NULL; + rc = 0; } } @@ -918,9 +931,9 @@ int parse_sec_desc(struct mnt_idmap *idmap, struct smb_ntsd *pntsd, } rc = sid_to_id(idmap, group_sid_ptr, SIDUNIX_GROUP, fattr); if (rc) { - pr_err("%s: Error %d mapping Group SID to gid\n", - __func__, rc); + ksmbd_debug(SMB, "Group SID has no Unix gid mapping\n"); group_sid_ptr = NULL; + rc = 0; } } From 4b706360ffb7e459cb3d3edae30b06a584f6eddd Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 2 Jul 2026 15:49:53 +0900 Subject: [PATCH 0567/1054] ksmbd: fix multichannel binding and enforce channel limit A signed multichannel SESSION_SETUP binding request can require multiple authentication rounds. ksmbd excludes SESSION_SETUP from the signed request check and tries to sign every binding response with the channel signing key. The channel does not exist for STATUS_MORE_PROCESSING_REQUIRED, so that response is sent unsigned. Clients reject it with STATUS_ACCESS_DENIED. The final channel signing key also needs the key exported by the binding authentication context. Keep that key in the channel instead of overwriting the established session key, and use the session signing key for intermediate and failed binding responses. Retain the binding session reference until an error response has been signed and sent. Limit a session to 32 channels while holding the channel lock. Return STATUS_INSUFFICIENT_RESOURCES for an additional binding, matching the server limit expected by clients. This fixes smb2.multichannel.generic.num_channels, which previously failed the first binding with STATUS_ACCESS_DENIED and returned the same status instead of STATUS_INSUFFICIENT_RESOURCES for channel 33. Fixes: f5a544e3bab7 ("ksmbd: add support for SMB3 multichannel") Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/auth.c | 47 +++++----- fs/smb/server/auth.h | 7 +- fs/smb/server/mgmt/user_session.c | 4 +- fs/smb/server/mgmt/user_session.h | 1 + fs/smb/server/smb2pdu.c | 142 +++++++++++++++++++----------- 5 files changed, 126 insertions(+), 75 deletions(-) diff --git a/fs/smb/server/auth.c b/fs/smb/server/auth.c index 86f521e849d5..2c7096a782da 100644 --- a/fs/smb/server/auth.c +++ b/fs/smb/server/auth.c @@ -133,16 +133,17 @@ static int calc_ntlmv2_hash(struct ksmbd_conn *conn, struct ksmbd_session *sess, * @blen: NTLMv2 blob length * @domain_name: domain name * @cryptkey: session crypto key + * @sess_key: derived session key output buffer * * Return: 0 on success, error number on error */ int ksmbd_auth_ntlmv2(struct ksmbd_conn *conn, struct ksmbd_session *sess, struct ntlmv2_resp *ntlmv2, int blen, char *domain_name, - char *cryptkey) + char *cryptkey, char *sess_key) { char ntlmv2_hash[CIFS_ENCPWD_SIZE]; char ntlmv2_rsp[CIFS_HMAC_MD5_HASH_SIZE]; - char sess_key[SMB2_NTLMV2_SESSKEY_SIZE]; + char base_key[SMB2_NTLMV2_SESSKEY_SIZE]; struct hmac_md5_ctx ctx; int rc; @@ -165,7 +166,7 @@ int ksmbd_auth_ntlmv2(struct ksmbd_conn *conn, struct ksmbd_session *sess, /* Generate the session key */ hmac_md5_usingrawkey(ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE, ntlmv2_rsp, CIFS_HMAC_MD5_HASH_SIZE, - sess_key); + base_key); if (crypto_memneq(ntlmv2->ntlmv2_hash, ntlmv2_rsp, CIFS_HMAC_MD5_HASH_SIZE)) { @@ -173,12 +174,12 @@ int ksmbd_auth_ntlmv2(struct ksmbd_conn *conn, struct ksmbd_session *sess, goto out; } - memcpy(sess->sess_key, sess_key, sizeof(sess_key)); + memcpy(sess_key, base_key, sizeof(base_key)); rc = 0; out: memzero_explicit(ntlmv2_hash, sizeof(ntlmv2_hash)); memzero_explicit(ntlmv2_rsp, sizeof(ntlmv2_rsp)); - memzero_explicit(sess_key, sizeof(sess_key)); + memzero_explicit(base_key, sizeof(base_key)); return rc; } @@ -189,12 +190,13 @@ int ksmbd_auth_ntlmv2(struct ksmbd_conn *conn, struct ksmbd_session *sess, * @blob_len: length of the @authblob message * @conn: connection * @sess: session of connection + * @sess_key: derived session key output buffer * * Return: 0 on success, error number on error */ int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob, int blob_len, struct ksmbd_conn *conn, - struct ksmbd_session *sess) + struct ksmbd_session *sess, char *sess_key) { char *domain_name; unsigned int nt_off, dn_off; @@ -234,7 +236,7 @@ int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob, ret = ksmbd_auth_ntlmv2(conn, sess, (struct ntlmv2_resp *)((char *)authblob + nt_off), nt_len - CIFS_ENCPWD_SIZE, - domain_name, conn->ntlmssp.cryptkey); + domain_name, conn->ntlmssp.cryptkey, sess_key); kfree(domain_name); if (ret) return ret; @@ -257,8 +259,8 @@ int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob, if (!ctx_arc4) return -ENOMEM; - arc4_setkey(ctx_arc4, sess->sess_key, SMB2_NTLMV2_SESSKEY_SIZE); - arc4_crypt(ctx_arc4, sess->sess_key, + arc4_setkey(ctx_arc4, sess_key, SMB2_NTLMV2_SESSKEY_SIZE); + arc4_crypt(ctx_arc4, sess_key, (char *)authblob + sess_key_off, sess_key_len); kfree_sensitive(ctx_arc4); } @@ -400,7 +402,8 @@ ksmbd_build_ntlmssp_challenge_blob(struct challenge_message *chgblob, #ifdef CONFIG_SMB_SERVER_KERBEROS5 int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob, - int in_len, char *out_blob, int *out_len) + int in_len, char *out_blob, int *out_len, + char *sess_key) { struct ksmbd_spnego_authen_response *resp; struct ksmbd_login_response_ext *resp_ext = NULL; @@ -455,7 +458,7 @@ int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob, ksmbd_free_user(user); } - memcpy(sess->sess_key, resp->payload, resp->session_key_len); + memcpy(sess_key, resp->payload, resp->session_key_len); memcpy(out_blob, resp->payload + resp->session_key_len, resp->spnego_blob_len); *out_len = resp->spnego_blob_len; @@ -466,7 +469,8 @@ int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob, } #else int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob, - int in_len, char *out_blob, int *out_len) + int in_len, char *out_blob, int *out_len, + char *sess_key) { return -EOPNOTSUPP; } @@ -525,7 +529,7 @@ struct derivation { bool binding; }; -static void generate_key(struct ksmbd_conn *conn, struct ksmbd_session *sess, +static void generate_key(struct ksmbd_conn *conn, const char *sess_key, struct kvec label, struct kvec context, __u8 *key, unsigned int key_size) { @@ -536,7 +540,7 @@ static void generate_key(struct ksmbd_conn *conn, struct ksmbd_session *sess, unsigned char prfhash[SMB2_HMACSHA256_SIZE]; struct hmac_sha256_ctx ctx; - hmac_sha256_init_usingrawkey(&ctx, sess->sess_key, + hmac_sha256_init_usingrawkey(&ctx, sess_key, SMB2_NTLMV2_SESSKEY_SIZE); hmac_sha256_update(&ctx, i, 4); hmac_sha256_update(&ctx, label.iov_base, label.iov_len); @@ -559,18 +563,21 @@ static int generate_smb3signingkey(struct ksmbd_session *sess, const struct derivation *signing) { struct channel *chann; - char *key; + char *key, *sess_key; chann = lookup_chann_list(sess, conn); if (!chann) return 0; - if (conn->dialect >= SMB30_PROT_ID && signing->binding) + if (conn->dialect >= SMB30_PROT_ID && signing->binding) { key = chann->smb3signingkey; - else + sess_key = chann->sess_key; + } else { key = sess->smb3signingkey; + sess_key = sess->sess_key; + } - generate_key(conn, sess, signing->label, signing->context, key, + generate_key(conn, sess_key, signing->label, signing->context, key, SMB3_SIGN_KEY_SIZE); if (!(conn->dialect >= SMB30_PROT_ID && signing->binding)) @@ -627,11 +634,11 @@ static void generate_smb3encryptionkey(struct ksmbd_conn *conn, struct ksmbd_session *sess, const struct derivation_twin *ptwin) { - generate_key(conn, sess, ptwin->encryption.label, + generate_key(conn, sess->sess_key, ptwin->encryption.label, ptwin->encryption.context, sess->smb3encryptionkey, SMB3_ENC_DEC_KEY_SIZE); - generate_key(conn, sess, ptwin->decryption.label, + generate_key(conn, sess->sess_key, ptwin->decryption.label, ptwin->decryption.context, sess->smb3decryptionkey, SMB3_ENC_DEC_KEY_SIZE); diff --git a/fs/smb/server/auth.h b/fs/smb/server/auth.h index 5767aabc63c9..f14b7c033264 100644 --- a/fs/smb/server/auth.h +++ b/fs/smb/server/auth.h @@ -41,17 +41,18 @@ int ksmbd_crypt_message(struct ksmbd_work *work, struct kvec *iov, void ksmbd_copy_gss_neg_header(void *buf); int ksmbd_auth_ntlmv2(struct ksmbd_conn *conn, struct ksmbd_session *sess, struct ntlmv2_resp *ntlmv2, int blen, char *domain_name, - char *cryptkey); + char *cryptkey, char *sess_key); int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob, int blob_len, struct ksmbd_conn *conn, - struct ksmbd_session *sess); + struct ksmbd_session *sess, char *sess_key); int ksmbd_decode_ntlmssp_neg_blob(struct negotiate_message *negblob, int blob_len, struct ksmbd_conn *conn); unsigned int ksmbd_build_ntlmssp_challenge_blob(struct challenge_message *chgblob, struct ksmbd_conn *conn); int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob, - int in_len, char *out_blob, int *out_len); + int in_len, char *out_blob, int *out_len, + char *sess_key); void ksmbd_sign_smb2_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov, int n_vec, char *sig); void ksmbd_sign_smb3_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov, diff --git a/fs/smb/server/mgmt/user_session.c b/fs/smb/server/mgmt/user_session.c index de58aed76cb4..d6331184ebfc 100644 --- a/fs/smb/server/mgmt/user_session.c +++ b/fs/smb/server/mgmt/user_session.c @@ -255,7 +255,7 @@ static void free_channel_list(struct ksmbd_session *sess) down_write(&sess->chann_lock); xa_for_each(&sess->ksmbd_chann_list, index, chann) { xa_erase(&sess->ksmbd_chann_list, index); - kfree(chann); + kfree_sensitive(chann); } xa_destroy(&sess->ksmbd_chann_list); @@ -449,7 +449,7 @@ static int ksmbd_chann_del(struct ksmbd_conn *conn, struct ksmbd_session *sess) if (!chann) return -ENOENT; - kfree(chann); + kfree_sensitive(chann); return 0; } diff --git a/fs/smb/server/mgmt/user_session.h b/fs/smb/server/mgmt/user_session.h index 6aebd385be84..8893a9aaede7 100644 --- a/fs/smb/server/mgmt/user_session.h +++ b/fs/smb/server/mgmt/user_session.h @@ -19,6 +19,7 @@ struct ksmbd_file_table; struct channel { + char sess_key[SMB2_NTLMV2_SESSKEY_SIZE]; __u8 smb3signingkey[SMB3_SIGN_KEY_SIZE]; struct ksmbd_conn *conn; }; diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 097f51fc7ed6..9c00f944fa3b 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -95,6 +95,47 @@ struct channel *lookup_chann_list(struct ksmbd_session *sess, struct ksmbd_conn return chann; } +#define KSMBD_MAX_CHANNELS 32 + +static int register_session_channel(struct ksmbd_session *sess, + struct ksmbd_conn *conn, + const char *sess_key) +{ + struct channel *chann, *old; + unsigned long index; + unsigned int count = 0; + int rc = 0; + + down_write(&sess->chann_lock); + if (xa_load(&sess->ksmbd_chann_list, (long)conn)) + goto out; + + xa_for_each(&sess->ksmbd_chann_list, index, chann) + count++; + if (count >= KSMBD_MAX_CHANNELS) { + rc = -ENOSPC; + goto out; + } + + chann = kmalloc_obj(struct channel, KSMBD_DEFAULT_GFP); + if (!chann) { + rc = -ENOMEM; + goto out; + } + + chann->conn = conn; + memcpy(chann->sess_key, sess_key, sizeof(chann->sess_key)); + old = xa_store(&sess->ksmbd_chann_list, (long)conn, chann, + KSMBD_DEFAULT_GFP); + if (xa_is_err(old)) { + kfree_sensitive(chann); + rc = xa_err(old); + } +out: + up_write(&sess->chann_lock); + return rc; +} + /** * smb2_get_ksmbd_tcon() - get tree connection information using a tree id. * @work: smb work @@ -1644,9 +1685,11 @@ static int ntlm_authenticate(struct ksmbd_work *work, { struct ksmbd_conn *conn = work->conn; struct ksmbd_session *sess = work->sess; - struct channel *chann = NULL, *old; struct ksmbd_user *user; + char channel_key[SMB2_NTLMV2_SESSKEY_SIZE] = {}; + char *auth_key = conn->binding ? channel_key : sess->sess_key; u64 prev_id; + bool binding = conn->binding; int sz, rc; ksmbd_debug(SMB, "authenticate phase\n"); @@ -1705,11 +1748,13 @@ static int ntlm_authenticate(struct ksmbd_work *work, sz = conn->mechTokenLen; else sz = le16_to_cpu(req->SecurityBufferLength); - rc = ksmbd_decode_ntlmssp_auth_blob(authblob, sz, conn, sess); + rc = ksmbd_decode_ntlmssp_auth_blob(authblob, sz, conn, sess, + auth_key); if (rc) { set_user_flag(sess->user, KSMBD_USER_FLAG_BAD_PASSWORD); ksmbd_debug(SMB, "authentication failed\n"); - return -EPERM; + rc = -EPERM; + goto out; } } @@ -1744,37 +1789,30 @@ static int ntlm_authenticate(struct ksmbd_work *work, binding_session: if (conn->dialect >= SMB30_PROT_ID) { - chann = lookup_chann_list(sess, conn); - if (!chann) { - chann = kmalloc_obj(struct channel, KSMBD_DEFAULT_GFP); - if (!chann) - return -ENOMEM; - - chann->conn = conn; - down_write(&sess->chann_lock); - old = xa_store(&sess->ksmbd_chann_list, (long)conn, chann, - KSMBD_DEFAULT_GFP); - up_write(&sess->chann_lock); - if (xa_is_err(old)) { - kfree(chann); - return xa_err(old); - } - } + rc = register_session_channel(sess, conn, auth_key); + if (rc) + goto out; } if (conn->ops->generate_signingkey) { rc = conn->ops->generate_signingkey(sess, conn); if (rc) { ksmbd_debug(SMB, "SMB3 signing key generation failed\n"); - return -EINVAL; + rc = -EINVAL; + goto out; } } if (!ksmbd_conn_lookup_dialect(conn)) { pr_err("fail to verify the dialect\n"); - return -ENOENT; + rc = -ENOENT; + goto out; } - return 0; + rc = 0; +out: + if (binding) + memzero_explicit(channel_key, sizeof(channel_key)); + return rc; } #ifdef CONFIG_SMB_SERVER_KERBEROS5 @@ -1785,8 +1823,10 @@ static int krb5_authenticate(struct ksmbd_work *work, struct ksmbd_conn *conn = work->conn; struct ksmbd_session *sess = work->sess; char *in_blob, *out_blob; - struct channel *chann = NULL, *old; + char channel_key[SMB2_NTLMV2_SESSKEY_SIZE] = {}; + char *auth_key = conn->binding ? channel_key : sess->sess_key; u64 prev_sess_id; + bool binding = conn->binding; int in_len, out_len; int retval; @@ -1799,10 +1839,11 @@ static int krb5_authenticate(struct ksmbd_work *work, (le16_to_cpu(rsp->SecurityBufferOffset) + 4); retval = ksmbd_krb5_authenticate(sess, in_blob, in_len, - out_blob, &out_len); + out_blob, &out_len, auth_key); if (retval) { ksmbd_debug(SMB, "krb5 authentication failed\n"); - return -EINVAL; + retval = -EINVAL; + goto out; } /* Check previous session */ @@ -1839,37 +1880,30 @@ static int krb5_authenticate(struct ksmbd_work *work, binding_session: if (conn->dialect >= SMB30_PROT_ID) { - chann = lookup_chann_list(sess, conn); - if (!chann) { - chann = kmalloc_obj(struct channel, KSMBD_DEFAULT_GFP); - if (!chann) - return -ENOMEM; - - chann->conn = conn; - down_write(&sess->chann_lock); - old = xa_store(&sess->ksmbd_chann_list, (long)conn, - chann, KSMBD_DEFAULT_GFP); - up_write(&sess->chann_lock); - if (xa_is_err(old)) { - kfree(chann); - return xa_err(old); - } - } + retval = register_session_channel(sess, conn, auth_key); + if (retval) + goto out; } if (conn->ops->generate_signingkey) { retval = conn->ops->generate_signingkey(sess, conn); if (retval) { ksmbd_debug(SMB, "SMB3 signing key generation failed\n"); - return -EINVAL; + retval = -EINVAL; + goto out; } } if (!ksmbd_conn_lookup_dialect(conn)) { pr_err("fail to verify the dialect\n"); - return -ENOENT; + retval = -ENOENT; + goto out; } - return 0; + retval = 0; +out: + if (binding) + memzero_explicit(channel_key, sizeof(channel_key)); + return retval; } #else static int krb5_authenticate(struct ksmbd_work *work, @@ -2091,7 +2125,7 @@ int smb2_sess_setup(struct ksmbd_work *work) rsp->hdr.Status = STATUS_REQUEST_NOT_ACCEPTED; else if (rc == -EFAULT) rsp->hdr.Status = STATUS_NETWORK_SESSION_EXPIRED; - else if (rc == -ENOMEM) + else if (rc == -ENOMEM || rc == -ENOSPC) rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; else if (rc == -EOPNOTSUPP) rsp->hdr.Status = STATUS_NOT_SUPPORTED; @@ -2130,8 +2164,16 @@ int smb2_sess_setup(struct ksmbd_work *work) sess->last_active = jiffies; sess->state = SMB2_SESSION_EXPIRED; } - ksmbd_user_session_put(sess); - work->sess = NULL; + /* + * Keep the binding session reference until the response is + * signed and sent. Error responses for a signed binding + * request are signed with the existing session signing key. + */ + if (!(req->Flags & SMB2_SESSION_REQ_FLAG_BINDING) || + work->sess != sess) { + ksmbd_user_session_put(sess); + work->sess = NULL; + } if (try_delay) { ksmbd_conn_set_need_reconnect(conn); ssleep(5); @@ -9483,7 +9525,6 @@ bool smb2_is_sign_req(struct ksmbd_work *work, unsigned int command) if ((rcv_hdr2->Flags & SMB2_FLAGS_SIGNED) && command != SMB2_NEGOTIATE_HE && - command != SMB2_SESSION_SETUP_HE && command != SMB2_OPLOCK_BREAK_HE) return true; @@ -9632,13 +9673,14 @@ void smb3_set_sign_rsp(struct ksmbd_work *work) struct channel *chann; char signature[SMB2_CMACAES_SIZE]; struct kvec *iov; + u16 command = conn->ops->get_cmd_val(work); int n_vec = 1; char *signing_key; hdr = ksmbd_resp_buf_curr(work); - if (conn->binding == false && - le16_to_cpu(hdr->Command) == SMB2_SESSION_SETUP_HE) { + if (command == SMB2_SESSION_SETUP_HE && + (!conn->binding || hdr->Status != STATUS_SUCCESS)) { signing_key = work->sess->smb3signingkey; } else { chann = lookup_chann_list(work->sess, work->conn); From a0b765030f6ba37aaac59618d4f39dac18d42757 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 1 Jul 2026 23:59:29 +0900 Subject: [PATCH 0568/1054] ksmbd: coalesce sub-15ms write time updates on close Windows reports automatic write-time updates with a resolution of roughly 15 milliseconds. If a file is written and closed within that interval, a close response requesting full information can report the write time from the open rather than the filesystem's finer-grained mtime update. ksmbd currently converts the filesystem mtime directly in SMB2 CLOSE, so even a sub-millisecond write is visible to the client. This makes smb2.timestamp_resolution.resolution1 fail because the immediate write changes LastWriteTime. Save the write time returned by SMB2 CREATE in the file handle. When CLOSE requests post-query attributes, coalesce a positive mtime change smaller than 15 milliseconds to that saved value. Larger changes remain visible, including the test's write after a 20 millisecond delay. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/smb2pdu.c | 7 +++++++ fs/smb/server/vfs_cache.h | 1 + 2 files changed, 8 insertions(+) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 9c00f944fa3b..b1204d1d3cdd 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -61,6 +61,9 @@ static void __wbuf(struct ksmbd_work *work, void **req, void **rsp) (FILE_ATTRIBUTE_MASK & ~(FILE_ATTRIBUTE_INTEGRITY_STREAM | \ FILE_ATTRIBUTE_NO_SCRUB_DATA)) +/* Windows reports automatic write-time updates at roughly 15 ms resolution. */ +#define KSMBD_WRITE_TIME_RESOLUTION (15ULL * 10000) + /** * check_session_id() - check for valid session id in smb header * @conn: connection instance @@ -3985,6 +3988,7 @@ int smb2_open(struct ksmbd_work *work) time = ksmbd_UnixTimeToNT(stat.atime); rsp->LastAccessTime = cpu_to_le64(time); time = ksmbd_UnixTimeToNT(stat.mtime); + fp->open_mtime = time; rsp->LastWriteTime = cpu_to_le64(time); rsp->ChangeTime = cpu_to_le64(fp->change_time); /* @@ -6451,6 +6455,9 @@ int smb2_close(struct ksmbd_work *work) time = ksmbd_UnixTimeToNT(stat.atime); rsp->LastAccessTime = cpu_to_le64(time); time = ksmbd_UnixTimeToNT(stat.mtime); + if (time > fp->open_mtime && + time - fp->open_mtime < KSMBD_WRITE_TIME_RESOLUTION) + time = fp->open_mtime; rsp->LastWriteTime = cpu_to_le64(time); rsp->ChangeTime = cpu_to_le64(fp->change_time); ksmbd_fd_put(work, fp); diff --git a/fs/smb/server/vfs_cache.h b/fs/smb/server/vfs_cache.h index 287f3e675cd3..b9e27307a26c 100644 --- a/fs/smb/server/vfs_cache.h +++ b/fs/smb/server/vfs_cache.h @@ -105,6 +105,7 @@ struct ksmbd_file { __u64 change_time; __u64 allocation_size; __u64 itime; + __u64 open_mtime; bool is_nt_open; bool attrib_only; From 216c5aba4ebde1a6e85d7831c0cf39a9a2ad7a38 Mon Sep 17 00:00:00 2001 From: Huiwen He Date: Wed, 1 Jul 2026 23:21:57 +0800 Subject: [PATCH 0569/1054] smb/server: map SET_INFO ENOSPC to disk full FILE_ALLOCATION_INFORMATION can call vfs_fallocate(). If the allocation cannot be satisfied, vfs_fallocate() returns -ENOSPC. smb2_set_info() did not map -ENOSPC, so ksmbd returned a generic SMB error and the client reported EIO instead of ENOSPC. This makes the ENOSPC step in xfstests generic/213 fail. Map -ENOSPC and -EFBIG to STATUS_DISK_FULL in the SET_INFO error path. Tested with xfstests generic/213 on ksmbd. Signed-off-by: Huiwen He Reviewed-by: ChenXiaoSong Acked-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/smb2pdu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index b1204d1d3cdd..af4c3ad2673c 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -7129,6 +7129,8 @@ int smb2_set_info(struct ksmbd_work *work) rsp->hdr.Status = STATUS_INVALID_PARAMETER; else if (rc == -EMSGSIZE) rsp->hdr.Status = STATUS_INFO_LENGTH_MISMATCH; + else if (rc == -ENOSPC || rc == -EFBIG) + rsp->hdr.Status = STATUS_DISK_FULL; else if (rc == -ESHARE) rsp->hdr.Status = STATUS_SHARING_VIOLATION; else if (rc == -ENOENT) From 9e8ad620ddfde5a5f4ef58372e3805e9388cb0f4 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 2 Jul 2026 19:37:53 +0900 Subject: [PATCH 0570/1054] ksmbd: mark invalid session responses as signed When a signed request uses a session that is not registered on the connection, ksmbd returns STATUS_USER_SESSION_DELETED before reaching the normal response signing path. The response therefore lacks SMB2_FLAGS_SIGNED. Clients that require signing check this flag before handling STATUS_USER_SESSION_DELETED and replace the server status with STATUS_ACCESS_DENIED when it is absent. The protocol permits this error response to skip signature verification because the connection has no matching session key. Preserve SMB2_FLAGS_SIGNED on the early error response when the request was signed. This lets the client propagate STATUS_USER_SESSION_DELETED. It fixes smb2.session.bind2. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/server.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fs/smb/server/server.c b/fs/smb/server/server.c index 36a5ea4828ad..bc861ca4f0cc 100644 --- a/fs/smb/server/server.c +++ b/fs/smb/server/server.c @@ -199,6 +199,12 @@ static void __handle_ksmbd_work(struct ksmbd_work *work, else conn->ops->set_rsp_status(work, STATUS_USER_SESSION_DELETED); + if (conn->ops->is_sign_req(work, conn->ops->get_cmd_val(work))) { + struct smb2_hdr *rsp_hdr; + + rsp_hdr = ksmbd_resp_buf_curr(work); + rsp_hdr->Flags |= SMB2_FLAGS_SIGNED; + } goto send; } else if (rc > 0) { rc = conn->ops->get_ksmbd_tcon(work); From faf8578c77f3d846aca9cd882c293e03eafcc6df Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 2 Jul 2026 19:50:26 +0900 Subject: [PATCH 0571/1054] ksmbd: find bound sessions during reauthentication A session bound to an additional connection is stored in the session channel list, but it is not added to that connection's local session table. After the binding exchange completes, conn->binding is cleared. A later SESSION_SETUP reauthentication on the bound channel only searches the local session table. It fails to find the session and returns STATUS_USER_SESSION_DELETED instead of processing authentication and returning STATUS_LOGON_FAILURE for invalid credentials. If the local lookup fails, look up the session globally and accept it only when the current connection is registered in its channel list. This keeps unbound connections from using the session while allowing reauthentication on an established channel. This fixes smb2.session.bind_invalid_auth. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/smb2pdu.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index af4c3ad2673c..a8665054151e 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -2018,6 +2018,13 @@ int smb2_sess_setup(struct ksmbd_work *work) } else { sess = ksmbd_session_lookup(conn, le64_to_cpu(req->hdr.SessionId)); + if (!sess) { + sess = ksmbd_session_lookup_slowpath(le64_to_cpu(req->hdr.SessionId)); + if (sess && !lookup_chann_list(sess, conn)) { + ksmbd_user_session_put(sess); + sess = NULL; + } + } if (!sess) { rc = -ENOENT; goto out_err; From 1c5daa2ea924be89d78b5525510bcf3a3eb9735c Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 2 Jul 2026 20:07:25 +0900 Subject: [PATCH 0572/1054] ksmbd: handle channel binding with a different user When an authenticated user tries to bind a channel to a session owned by a different user, ksmbd returns STATUS_LOGON_FAILURE. Windows instead rejects this attempt with STATUS_ACCESS_DENIED. The supplied credentials are valid but cannot be used with the existing session. Use a distinct internal error for a user mismatch in both NTLM and Kerberos authentication and map it to STATUS_ACCESS_DENIED during SESSION_SETUP. Keep ordinary authentication failures mapped to STATUS_LOGON_FAILURE. A failed SMB 3.1.1 binding also leaves its preauthentication context on the connection. A subsequent binding attempt for the same session reuses the stale hash and derives an incorrect channel signing key. Remove the binding preauthentication context on failure so a valid retry starts with a fresh hash. This fixes smb2.session.bind_different_user. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/auth.c | 2 +- fs/smb/server/smb2pdu.c | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/fs/smb/server/auth.c b/fs/smb/server/auth.c index 2c7096a782da..4e7b6f0e6b8c 100644 --- a/fs/smb/server/auth.c +++ b/fs/smb/server/auth.c @@ -451,7 +451,7 @@ int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob, } else { if (!ksmbd_compare_user(sess->user, user)) { ksmbd_debug(AUTH, "different user tried to reuse session\n"); - retval = -EPERM; + retval = -EKEYREJECTED; ksmbd_free_user(user); goto out; } diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index a8665054151e..1f51b7d68de1 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -1734,7 +1734,7 @@ static int ntlm_authenticate(struct ksmbd_work *work, if (!ksmbd_compare_user(sess->user, user)) { ksmbd_free_user(user); - return -EPERM; + return -EKEYREJECTED; } ksmbd_free_user(user); } else { @@ -1845,7 +1845,8 @@ static int krb5_authenticate(struct ksmbd_work *work, out_blob, &out_len, auth_key); if (retval) { ksmbd_debug(SMB, "krb5 authentication failed\n"); - retval = -EINVAL; + if (retval != -EKEYREJECTED) + retval = -EINVAL; goto out; } @@ -2139,6 +2140,8 @@ int smb2_sess_setup(struct ksmbd_work *work) rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; else if (rc == -EOPNOTSUPP) rsp->hdr.Status = STATUS_NOT_SUPPORTED; + else if (rc == -EKEYREJECTED) + rsp->hdr.Status = STATUS_ACCESS_DENIED; else if (rc) rsp->hdr.Status = STATUS_LOGON_FAILURE; @@ -2148,6 +2151,16 @@ int smb2_sess_setup(struct ksmbd_work *work) } if (rc < 0) { + if (sess && (req->Flags & SMB2_SESSION_REQ_FLAG_BINDING)) { + struct preauth_session *preauth_sess; + + preauth_sess = ksmbd_preauth_session_lookup(conn, sess->id); + if (preauth_sess) { + list_del(&preauth_sess->preauth_entry); + kfree(preauth_sess); + } + } + /* * SecurityBufferOffset should be set to zero * in session setup error response. From 1f12738b0ed7b89252271d71159c67e6349ef532 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 2 Jul 2026 20:27:43 +0900 Subject: [PATCH 0573/1054] ksmbd: sign rejected SMB2.1 session binding responses SMB2_SESSION_REQ_FLAG_BINDING is not supported before SMB 3.0. ksmbd maps such a request to STATUS_REQUEST_NOT_ACCEPTED, but it rejects the request without looking up the referenced session. The response is then sent unsigned. A client requiring signing reports STATUS_ACCESS_DENIED instead of the server status. Look up the referenced session and verify the binding request with its signing key. Keep the session reference only after successful verification so the rejected response can be signed without providing a signing oracle. A signed SESSION_SETUP without the binding flag can reference a session that does not belong to the connection. Preserve SMB2_FLAGS_SIGNED on the STATUS_USER_SESSION_DELETED response. Clients skip signature verification for this status but still require the signed flag before propagating it. Also restrict failed binding preauthentication cleanup to SMB 3.1.1, the only dialect that initializes and uses that context. This fixes smb2.session.bind_negative_smb210s. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/smb2pdu.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 1f51b7d68de1..cd2dab1907cf 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -2013,7 +2013,17 @@ int smb2_sess_setup(struct ksmbd_work *work) } else if ((conn->dialect < SMB30_PROT_ID || server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) && (req->Flags & SMB2_SESSION_REQ_FLAG_BINDING)) { - sess = NULL; + sess = ksmbd_session_lookup_slowpath(le64_to_cpu(req->hdr.SessionId)); + if (sess) { + work->sess = sess; + if (sess->state != SMB2_SESSION_VALID || + !(req->hdr.Flags & SMB2_FLAGS_SIGNED) || + !conn->ops->check_sign_req(work)) { + ksmbd_user_session_put(sess); + work->sess = NULL; + sess = NULL; + } + } rc = -EACCES; goto out_err; } else { @@ -2144,6 +2154,9 @@ int smb2_sess_setup(struct ksmbd_work *work) rsp->hdr.Status = STATUS_ACCESS_DENIED; else if (rc) rsp->hdr.Status = STATUS_LOGON_FAILURE; + if (rsp->hdr.Status == STATUS_USER_SESSION_DELETED && + (req->hdr.Flags & SMB2_FLAGS_SIGNED)) + rsp->hdr.Flags |= SMB2_FLAGS_SIGNED; if (conn->mechToken) { kfree(conn->mechToken); @@ -2151,7 +2164,8 @@ int smb2_sess_setup(struct ksmbd_work *work) } if (rc < 0) { - if (sess && (req->Flags & SMB2_SESSION_REQ_FLAG_BINDING)) { + if (sess && conn->dialect == SMB311_PROT_ID && + (req->Flags & SMB2_SESSION_REQ_FLAG_BINDING)) { struct preauth_session *preauth_sess; preauth_sess = ksmbd_preauth_session_lookup(conn, sess->id); From 3e67423336f08a28e9efaac66e842210f9421484 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 2 Jul 2026 20:40:41 +0900 Subject: [PATCH 0574/1054] ksmbd: mark rejected cross-dialect bindings as signed Binding an SMB 2.1 session to an SMB 3.x connection is invalid because the dialects do not match. ksmbd returns STATUS_INVALID_PARAMETER. The check fails before attaching the referenced session to the request, so the error response lacks SMB2_FLAGS_SIGNED. A client requiring signing checks this flag before handling the status and reports STATUS_ACCESS_DENIED instead of STATUS_INVALID_PARAMETER. Preserve the signed flag for a signed binding request rejected with STATUS_INVALID_PARAMETER. The client can then apply the special error path without attempting to validate a response using incompatible signing algorithms. This fixes smb2.session.bind_negative_smb2to3s. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/smb2pdu.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index cd2dab1907cf..123ba63b2410 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -2154,7 +2154,9 @@ int smb2_sess_setup(struct ksmbd_work *work) rsp->hdr.Status = STATUS_ACCESS_DENIED; else if (rc) rsp->hdr.Status = STATUS_LOGON_FAILURE; - if (rsp->hdr.Status == STATUS_USER_SESSION_DELETED && + if ((rsp->hdr.Status == STATUS_USER_SESSION_DELETED || + (rsp->hdr.Status == STATUS_INVALID_PARAMETER && + (req->Flags & SMB2_SESSION_REQ_FLAG_BINDING))) && (req->hdr.Flags & SMB2_FLAGS_SIGNED)) rsp->hdr.Flags |= SMB2_FLAGS_SIGNED; From f49bca41c12f9f79d39dbf0779d0c672d74b09fe Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 2 Jul 2026 21:16:50 +0900 Subject: [PATCH 0575/1054] ksmbd: use the session dialect for rejected binding signatures When an SMB3 session is referenced by a binding request on an SMB2.1 connection, the request is signed with the existing session's SMB3 signing algorithm. ksmbd instead verifies it with the new connection's SMB2.1 HMAC algorithm, so verification fails and the client receives STATUS_ACCESS_DENIED instead of STATUS_REQUEST_NOT_ACCEPTED. Select the signing verifier from the referenced session dialect. Permit a signed SESSION_SETUP without an established channel to use the SMB3 session signing key for verification. This is limited to SESSION_SETUP so other unbound requests remain rejected. The rejected response must use the same existing session algorithm. When an SMB3 session is referenced on an SMB2.1 connection, sign the SESSION_SETUP response with the SMB3 signing path rather than the connection's SMB2.1 path. This fixes smb2.session.bind_negative_smb3to2s. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/server.c | 10 ++++++++-- fs/smb/server/smb2pdu.c | 16 +++++++++++++--- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/fs/smb/server/server.c b/fs/smb/server/server.c index bc861ca4f0cc..f5baba934840 100644 --- a/fs/smb/server/server.c +++ b/fs/smb/server/server.c @@ -243,8 +243,14 @@ static void __handle_ksmbd_work(struct ksmbd_work *work, if (work->sess && (work->sess->sign || smb3_11_final_sess_setup_resp(work) || - conn->ops->is_sign_req(work, command))) - conn->ops->set_sign_rsp(work); + conn->ops->is_sign_req(work, command))) { + if (command == SMB2_SESSION_SETUP_HE && + work->sess->dialect >= SMB30_PROT_ID && + conn->dialect < SMB30_PROT_ID) + smb3_set_sign_rsp(work); + else + conn->ops->set_sign_rsp(work); + } } while (is_chained == true); send: diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 123ba63b2410..b73167785e87 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -2015,10 +2015,16 @@ int smb2_sess_setup(struct ksmbd_work *work) (req->Flags & SMB2_SESSION_REQ_FLAG_BINDING)) { sess = ksmbd_session_lookup_slowpath(le64_to_cpu(req->hdr.SessionId)); if (sess) { + int sign_ret; + work->sess = sess; + if (sess->dialect >= SMB30_PROT_ID) + sign_ret = smb3_check_sign_req(work); + else + sign_ret = smb2_check_sign_req(work); if (sess->state != SMB2_SESSION_VALID || !(req->hdr.Flags & SMB2_FLAGS_SIGNED) || - !conn->ops->check_sign_req(work)) { + !sign_ret) { ksmbd_user_session_put(sess); work->sess = NULL; sess = NULL; @@ -9681,9 +9687,13 @@ int smb3_check_sign_req(struct ksmbd_work *work) } else { chann = lookup_chann_list(work->sess, conn); if (!chann) { - return 0; + if (le16_to_cpu(hdr->Command) != SMB2_SESSION_SETUP_HE || + !(hdr->Flags & SMB2_FLAGS_SIGNED)) + return 0; + signing_key = work->sess->smb3signingkey; + } else { + signing_key = chann->smb3signingkey; } - signing_key = chann->smb3signingkey; } if (!signing_key) { From dce002f685e04e0c94e088cb66cb4021311cc6b5 Mon Sep 17 00:00:00 2001 From: Steve French Date: Sun, 5 Jul 2026 16:04:09 -0500 Subject: [PATCH 0576/1054] smb: client: preserve leading slash for POSIX absolute symlink targets When creating a native SMB symbolic link (CIFS_SYMLINK_TYPE_NATIVE) whose target is an absolute path on a mount that uses POSIX paths, the leading path separator was silently dropped from the stored symlink target. create_native_symlink() converted the target to UTF-16 with cifs_convert_path_to_utf16(). That helper was intended for share-relative SMB paths and therefore unconditionally strips a leading path separator. For an absolute POSIX symlink target the leading '/' is significant, so a target of "/foo/bar" was stored and read back as "foo/bar", even though the reparse point was still flagged as absolute (SYMLINK_FLAG_RELATIVE cleared). On a POSIX paths mount the symlink target is stored verbatim, so convert it directly with cifs_strndup_to_utf16() instead. This preserves the leading separator, avoids the leading-backslash stripping that cifs_convert_path_to_utf16() also performs (a backslash is a valid POSIX filename character), and uses NO_MAP_UNI_RSVD to match the readback path in smb2_parse_native_symlink(), which always converts the target with cifs_strndup_from_utf16() / NO_MAP_UNI_RSVD. This mirrors how the NFS and WSL reparse symlink creators convert their targets. The NT-style absolute symlink handling, which needs the "\??\" prefix and drive-letter colon preserved, continues to use cifs_convert_path_to_utf16() together with the existing masking of those bytes. Fixes: 12b466eb52d9 ("cifs: Fix creating and resolving absolute NT-style symlinks") Reviewed-by: Paulo Alcantara (Red Hat) Acked-by: Ralph Boehme Signed-off-by: Steve French --- fs/smb/client/reparse.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/fs/smb/client/reparse.c b/fs/smb/client/reparse.c index cd1e1eaee67a..5cc5b0410d48 100644 --- a/fs/smb/client/reparse.c +++ b/fs/smb/client/reparse.c @@ -67,6 +67,7 @@ static int create_native_symlink(const unsigned int xid, struct inode *inode, char *sym = NULL; struct kvec iov; bool directory; + int path_len; int rc = 0; if (strlen(symname) > REPARSE_SYM_PATH_MAX) @@ -168,7 +169,21 @@ static int create_native_symlink(const unsigned int xid, struct inode *inode, if (!(sbflags & CIFS_MOUNT_POSIX_PATHS) && symname[0] == '/') sym[0] = sym[1] = sym[2] = sym[5] = '_'; - path = cifs_convert_path_to_utf16(sym, cifs_sb); + /* + * On a POSIX paths mount the symlink target is stored verbatim, so + * convert it with cifs_strndup_to_utf16(). cifs_convert_path_to_utf16() + * must not be used here: it strips a leading path separator (it is + * meant for share-relative SMB paths), which would corrupt an absolute + * POSIX symlink target such as "/foo/bar". Using NO_MAP_UNI_RSVD also + * matches the readback path in smb2_parse_native_symlink(). + */ + if (sbflags & CIFS_MOUNT_POSIX_PATHS) + path = cifs_strndup_to_utf16(sym, strlen(sym), &path_len, + cifs_sb->local_nls, + NO_MAP_UNI_RSVD); + else + path = cifs_convert_path_to_utf16(sym, cifs_sb); + if (!path) { rc = -ENOMEM; goto out; From 521f39ca93cc43ce1b3eae8d44201f8f55dd9151 Mon Sep 17 00:00:00 2001 From: Sergey Shtylyov Date: Mon, 1 Jun 2026 17:49:01 +0300 Subject: [PATCH 0577/1054] mmc: sdhci-of-dwcmshc: check bus clock enable result in the probe() method In the driver's probe() method, clk_disable_unprepare() for the bus clock is called on the error path even if the prior clk_prepare_enable() call has failed (and the same thing happens in the remove() method as well) -- that would cause the prepare/enable counter imbalance. Also, the same problem can happen in the driver's suspend() method; note that the resume() method does check the clk_prepare_enable()'s result -- let's be consistent and do that in probe() method as well. BTW, I don't know for sure what does the bus clock control -- if it affects the register accesses, the driver will likely cause (e.g. on ARM) a kernel oops if it fails to prepare/enable the bus clock in the probe() method... Found by Linux Verification Center (linuxtesting.org) with the Svace static analysis tool. Fixes: e438cf49b305 ("mmc: sdhci-of-dwcmshc: add SDHCI OF Synopsys DWC MSHC driver") Fixes: bccce2ec7790 ("mmc: sdhci-of-dwcmshc: add suspend/resume support") Signed-off-by: Sergey Shtylyov Acked-by: Adrian Hunter Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-dwcmshc.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c index eef53455b8ee..c688f3eaf468 100644 --- a/drivers/mmc/host/sdhci-of-dwcmshc.c +++ b/drivers/mmc/host/sdhci-of-dwcmshc.c @@ -2433,13 +2433,16 @@ static int dwcmshc_probe(struct platform_device *pdev) return err; priv->bus_clk = devm_clk_get(dev, "bus"); - if (!IS_ERR(priv->bus_clk)) - clk_prepare_enable(priv->bus_clk); + if (!IS_ERR(priv->bus_clk)) { + err = clk_prepare_enable(priv->bus_clk); + if (err) + goto err_clk; + } } err = mmc_of_parse(host->mmc); if (err) - goto err_clk; + goto err_bus_clk; sdhci_get_of_property(pdev); @@ -2453,7 +2456,7 @@ static int dwcmshc_probe(struct platform_device *pdev) if (pltfm_data->init) { err = pltfm_data->init(&pdev->dev, host, priv); if (err) - goto err_clk; + goto err_bus_clk; } #ifdef CONFIG_ACPI @@ -2499,9 +2502,10 @@ static int dwcmshc_probe(struct platform_device *pdev) err_rpm: pm_runtime_disable(dev); pm_runtime_put_noidle(dev); +err_bus_clk: + clk_disable_unprepare(priv->bus_clk); err_clk: clk_disable_unprepare(pltfm_host->clk); - clk_disable_unprepare(priv->bus_clk); clk_bulk_disable_unprepare(priv->num_other_clks, priv->other_clks); return err; } From 04f15d8f393037ed72876399882265604a370b2b Mon Sep 17 00:00:00 2001 From: Maciej Strozek Date: Fri, 3 Jul 2026 14:02:10 +0100 Subject: [PATCH 0578/1054] ASoC: sof_sdw: Add a quirk for 0x17aa383c laptop using sidecar amps with cs42l43 Add a quirk for a Lenovo laptop (SSID: 0x17aa383c) to allow using sidecar CS35L56 amps with CS42L43 codec. Signed-off-by: Maciej Strozek Link: https://patch.msgid.link/20260703130210.21991-1-mstrozek@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_sdw.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index d43daf9b025d..59eb0328bbb5 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -855,6 +855,7 @@ static const struct snd_pci_quirk sof_sdw_ssid_quirk_table[] = { SND_PCI_QUIRK(0x17aa, 0x2348, "Lenovo P16", SOC_SDW_CODEC_MIC), SND_PCI_QUIRK(0x17aa, 0x2349, "Lenovo P1", SOC_SDW_CODEC_MIC), SND_PCI_QUIRK(0x17aa, 0x3821, "Lenovo 0x3821", SOC_SDW_SIDECAR_AMPS), + SND_PCI_QUIRK(0x17aa, 0x383c, "Lenovo 0x383c", SOC_SDW_SIDECAR_AMPS), {} }; From b30973e8c3920ddfa9255a959b19057515b4e5e8 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 30 Jun 2026 16:28:16 +0200 Subject: [PATCH 0579/1054] gpio: shared: make the voting mechanism adaptable The current voting mechanism in GPIO shared proxy assumes that "low" is always the default value and users can only vote for driving the GPIO "high" in which case it will remain high as long as there's at least one user voting. This makes it impossible to use the automatic sharing management for certain use-cases such as the write-protect GPIOs of EEPROMs which are requested "high" and driven "low" to enable writing. In this case, if the WP GPIO is shared by multiple EEPROMs, and at least one of them wants to enable writing, the pin must be set to "low". Modify the voting heuristic to assume the value set by the first user on request to be the "default" and subseqent calls to gpiod_set_value() will constitute votes for a change of the value to the opposite. In the wp-gpios case it will mean that the nvmem core requests the GPIO as "out-high" for all EEPROMs sharing the pin, and when one of them wants to write, the pin will be driven low, enabling it. Fixes: e992d54c6f97 ("gpio: shared-proxy: implement the shared GPIO proxy driver") Reported-by: Marek Vasut Closes: https://lore.kernel.org/all/20260511163518.51104-1-marex@nabladev.com/ Reviewed-by: Linus Walleij Link: https://patch.msgid.link/20260630-gpio-shared-dynamic-voting-v3-1-8ecf0542953b@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-shared-proxy.c | 66 ++++++++++++++++---------------- drivers/gpio/gpiolib-shared.h | 3 +- 2 files changed, 34 insertions(+), 35 deletions(-) diff --git a/drivers/gpio/gpio-shared-proxy.c b/drivers/gpio/gpio-shared-proxy.c index 0f39d23ea9cb..bc69b8729d19 100644 --- a/drivers/gpio/gpio-shared-proxy.c +++ b/drivers/gpio/gpio-shared-proxy.c @@ -21,7 +21,7 @@ struct gpio_shared_proxy_data { struct gpio_chip gc; struct gpio_shared_desc *shared_desc; struct device *dev; - bool voted_high; + bool voted_change; }; static int @@ -33,52 +33,54 @@ gpio_shared_proxy_set_unlocked(struct gpio_shared_proxy_data *proxy, int value) lockdep_assert_held(&shared_desc->mutex); - if (value) { - /* User wants to set value to high. */ - if (proxy->voted_high) - /* Already voted for high, nothing to do. */ + if (value != shared_desc->def_val) { + /* User wants to vote for a value change. */ + if (proxy->voted_change) + /* Already voted for a change, nothing to do. */ goto out; - /* Haven't voted for high yet. */ - if (!shared_desc->highcnt) { + /* Haven't voted for a value change yet. */ + if (!shared_desc->votecnt) { /* - * Current value is low, need to actually set value - * to high. + * Current value is default, need to actually set value + * to the opposite. */ - ret = gpiod_set_value_cansleep(desc, 1); + ret = gpiod_set_value_cansleep(desc, value); if (ret) goto out; } - shared_desc->highcnt++; - proxy->voted_high = true; + shared_desc->votecnt++; + proxy->voted_change = true; goto out; } - /* Desired value is low. */ - if (!proxy->voted_high) - /* We didn't vote for high, nothing to do. */ + /* Desired value is the default. */ + if (!proxy->voted_change) + /* We didn't vote for change previously, nothing to do. */ goto out; - /* We previously voted for high. */ - if (shared_desc->highcnt == 1) { - /* This is the last remaining vote for high, set value to low. */ - ret = gpiod_set_value_cansleep(desc, 0); + /* We previously voted for change. */ + if (shared_desc->votecnt == 1) { + /* This is the last remaining vote for change, set value to default. */ + ret = gpiod_set_value_cansleep(desc, shared_desc->def_val); if (ret) goto out; } - shared_desc->highcnt--; - proxy->voted_high = false; + shared_desc->votecnt--; + proxy->voted_change = false; out: - if (shared_desc->highcnt) + if (shared_desc->votecnt) dev_dbg(proxy->dev, - "Voted for value '%s', effective value is 'high', number of votes for 'high': %u\n", - str_high_low(value), shared_desc->highcnt); + "Voted for value '%s', effective value is '%s', number of votes: %u\n", + str_high_low(value), str_high_low(!shared_desc->def_val), + shared_desc->votecnt); else - dev_dbg(proxy->dev, "Voted for value 'low', effective value is 'low'\n"); + dev_dbg(proxy->dev, "Voted for value '%s', effective value is '%s'\n", + str_high_low(value), str_high_low(shared_desc->def_val)); return ret; } @@ -106,8 +108,8 @@ static void gpio_shared_proxy_free(struct gpio_chip *gc, unsigned int offset) guard(mutex)(&shared_desc->mutex); - if (proxy->voted_high) { - ret = gpio_shared_proxy_set_unlocked(proxy, 0); + if (proxy->voted_change) { + ret = gpio_shared_proxy_set_unlocked(proxy, shared_desc->def_val); if (ret) dev_err(proxy->dev, "Failed to unset the shared GPIO value on release: %d\n", ret); @@ -196,13 +198,9 @@ static int gpio_shared_proxy_direction_output(struct gpio_chip *gc, if (ret) return ret; - if (value) { - proxy->voted_high = true; - shared_desc->highcnt = 1; - } else { - proxy->voted_high = false; - shared_desc->highcnt = 0; - } + shared_desc->def_val = value; + shared_desc->votecnt = 0; + proxy->voted_change = false; return 0; } diff --git a/drivers/gpio/gpiolib-shared.h b/drivers/gpio/gpiolib-shared.h index bbdc0ab7b647..618756f6c6aa 100644 --- a/drivers/gpio/gpiolib-shared.h +++ b/drivers/gpio/gpiolib-shared.h @@ -41,7 +41,8 @@ struct gpio_shared_desc { struct gpio_desc *desc; unsigned long cfg; unsigned int usecnt; - unsigned int highcnt; + unsigned int votecnt; + int def_val; struct mutex mutex; /* serializes all proxy operations on this descriptor */ }; From 4c9854ad3cdc7ce408453207f153bb910cc6f3c1 Mon Sep 17 00:00:00 2001 From: Tianze Shao Date: Wed, 1 Jul 2026 16:56:06 +0200 Subject: [PATCH 0580/1054] ASoC: rt712-sdca: reset codec at io_init to fix silent headphone On ThinkPad X1 Carbon Gen 13 (Lunar Lake, RT712-SDCA version VA) the 3.5mm headphone jack is silent after rebooting from Windows. rt712_sdca_va_io_init() gates rt712_sdca_calibration() on the persisted vendor SW_CONFIG1 flag, and io_init writes SW_CONFIG1=1 at the end regardless of whether the calibration succeeded. Across a warm reboot the codec keeps power, so SW_CONFIG1 stays unchanged, the calibration may be skipped, and the retained state can be invalid, leaving the headphone amp disabled. This patch mimics the reset sequence in rt711-sdca.c, it adds an rt712_sdca_reset() helper, and calls it from io_init so the codec is reset before initialization. RT712_PARA_VERB_CTL, RT712_HIDDEN_REG_SW_RESET and RT712_HDA_LEGACY_RESET_CTL are already defined but were unused. The reset clears SW_CONFIG1 and the analog state so rt712_sdca_calibration() runs from a clean state and completes. Problem reproducible: boot Windows (headphone is good) -> reboot to Linux (silent). The reproducibility may depend on Windows' behaviour. Signed-off-by: Tianze Shao Link: https://patch.msgid.link/TYCPR01MB872223CB999D2099A5E0F58DC4F62@TYCPR01MB8722.jpnprd01.prod.outlook.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt712-sdca.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/sound/soc/codecs/rt712-sdca.c b/sound/soc/codecs/rt712-sdca.c index 4796fce084ff..d6353af07380 100644 --- a/sound/soc/codecs/rt712-sdca.c +++ b/sound/soc/codecs/rt712-sdca.c @@ -1843,6 +1843,15 @@ static void rt712_sdca_vb_io_init(struct rt712_sdca_priv *rt712) } } +static void rt712_sdca_reset(struct rt712_sdca_priv *rt712) +{ + rt712_sdca_index_update_bits(rt712, RT712_VENDOR_REG, + RT712_PARA_VERB_CTL, RT712_HIDDEN_REG_SW_RESET, + RT712_HIDDEN_REG_SW_RESET); + rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL, + RT712_HDA_LEGACY_RESET_CTL, 0x1, 0x1); +} + int rt712_sdca_io_init(struct device *dev, struct sdw_slave *slave) { struct rt712_sdca_priv *rt712 = dev_get_drvdata(dev); @@ -1870,6 +1879,8 @@ int rt712_sdca_io_init(struct device *dev, struct sdw_slave *slave) pm_runtime_get_noresume(&slave->dev); + rt712_sdca_reset(rt712); + rt712_sdca_index_read(rt712, RT712_VENDOR_REG, RT712_JD_PRODUCT_NUM, &val); rt712->hw_id = (val & 0xf000) >> 12; rt712->version_id = (val & 0x0f00) >> 8; From dbbb5bc5176e36b13aa22e2174ab4779c5ae1dca Mon Sep 17 00:00:00 2001 From: Syed Saba Kareem Date: Fri, 3 Jul 2026 18:02:47 +0530 Subject: [PATCH 0581/1054] ASoC: amd: acp: Fix linker error with SDCA quirks Fix undefined reference to `snd_soc_acpi_amd_sdca_is_device_rt712_vb` linker error when CONFIG_SND_SOC_ACPI_AMD_MATCH=y and CONFIG_SND_SOC_ACPI_AMD_SDCA_QUIRKS=m, which causes built-in code to reference a symbol only available in a module. Fix this by changing SND_SOC_ACPI_AMD_SDCA_QUIRKS from tristate to bool and compiling the quirks code directly into snd-soc-acpi-amd-match rather than as a separate module. This ensures the quirks symbols are always available at link time when the match tables reference them. Fixes: 10d366a846be ("ASoC: amd: acp: Fix Kconfig dependencies for SND_SOC_ACPI_AMD_SDCA_QUIRKS") Reported-by: Arnd Bergmann Tested-by: Arnd Bergmann Signed-off-by: Syed Saba Kareem Reviewed-by: Vijendar Mukunda Link: https://patch.msgid.link/20260703123314.147977-1-syed.sabakareem@amd.com Signed-off-by: Mark Brown --- sound/soc/amd/acp/Kconfig | 10 +++++++--- sound/soc/amd/acp/Makefile | 3 +-- sound/soc/amd/acp/amd-acp70-acpi-match.c | 2 +- sound/soc/amd/acp/soc-acpi-amd-sdca-quirks.c | 4 ---- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/sound/soc/amd/acp/Kconfig b/sound/soc/amd/acp/Kconfig index 977e4f2a7a70..a04c301df4d6 100644 --- a/sound/soc/amd/acp/Kconfig +++ b/sound/soc/amd/acp/Kconfig @@ -15,13 +15,17 @@ config SND_SOC_AMD_ACP_COMMON config SND_SOC_ACPI_AMD_MATCH tristate - select SND_SOC_ACPI_AMD_SDCA_QUIRKS if SND_SOC_SDCA select SND_SOC_ACPI if ACPI config SND_SOC_ACPI_AMD_SDCA_QUIRKS - tristate - depends on ACPI + bool "AMD ACPI SDCA quirks" + depends on SND_SOC_ACPI_AMD_MATCH depends on SND_SOC_SDCA + depends on SND_SOC_ACPI_AMD_MATCH = m || SND_SOC_SDCA = y + default y + help + Enable SDCA quirk support for AMD ACPI match tables. + This is compiled into the snd-soc-acpi-amd-match module. if SND_SOC_AMD_ACP_COMMON diff --git a/sound/soc/amd/acp/Makefile b/sound/soc/amd/acp/Makefile index 81d23aded348..ab5f9dc871a6 100644 --- a/sound/soc/amd/acp/Makefile +++ b/sound/soc/amd/acp/Makefile @@ -24,10 +24,10 @@ snd-acp-mach-y := acp-mach-common.o snd-acp-legacy-mach-y := acp-legacy-mach.o acp3x-es83xx/acp3x-es83xx.o snd-acp-sof-mach-y := acp-sof-mach.o snd-soc-acpi-amd-match-y := amd-acp63-acpi-match.o amd-acp70-acpi-match.o +snd-soc-acpi-amd-match-$(CONFIG_SND_SOC_ACPI_AMD_SDCA_QUIRKS) += soc-acpi-amd-sdca-quirks.o snd-acp-sdw-mach-y := acp-sdw-mach-common.o snd-acp-sdw-sof-mach-y += acp-sdw-sof-mach.o snd-acp-sdw-legacy-mach-y += acp-sdw-legacy-mach.o -snd-soc-acpi-amd-sdca-quirks-y += soc-acpi-amd-sdca-quirks.o obj-$(CONFIG_SND_SOC_AMD_ACP_PCM) += snd-acp-pcm.o obj-$(CONFIG_SND_SOC_AMD_ACP_I2S) += snd-acp-i2s.o @@ -41,7 +41,6 @@ obj-$(CONFIG_SND_AMD_ASOC_REMBRANDT) += snd-acp-rembrandt.o obj-$(CONFIG_SND_AMD_ASOC_ACP63) += snd-acp63.o obj-$(CONFIG_SND_AMD_ASOC_ACP70) += snd-acp70.o -obj-$(CONFIG_SND_SOC_ACPI_AMD_SDCA_QUIRKS) += snd-soc-acpi-amd-sdca-quirks.o obj-$(CONFIG_SND_AMD_SOUNDWIRE_ACPI) += snd-amd-sdw-acpi.o obj-$(CONFIG_SND_SOC_AMD_MACH_COMMON) += snd-acp-mach.o obj-$(CONFIG_SND_SOC_AMD_LEGACY_MACH) += snd-acp-legacy-mach.o diff --git a/sound/soc/amd/acp/amd-acp70-acpi-match.c b/sound/soc/amd/acp/amd-acp70-acpi-match.c index 18f2918d4ada..9e97a805d0df 100644 --- a/sound/soc/amd/acp/amd-acp70-acpi-match.c +++ b/sound/soc/amd/acp/amd-acp70-acpi-match.c @@ -775,5 +775,5 @@ MODULE_DESCRIPTION("AMD ACP7.0 & ACP7.1 tables and support for ACPI enumeration" MODULE_LICENSE("GPL"); MODULE_AUTHOR("Vijendar.Mukunda@amd.com"); #if IS_ENABLED(CONFIG_SND_SOC_ACPI_AMD_SDCA_QUIRKS) -MODULE_IMPORT_NS("SND_SOC_ACPI_AMD_SDCA_QUIRKS"); +MODULE_IMPORT_NS("SND_SOC_SDCA"); #endif diff --git a/sound/soc/amd/acp/soc-acpi-amd-sdca-quirks.c b/sound/soc/amd/acp/soc-acpi-amd-sdca-quirks.c index 63bf9e3c0ae1..9248b6d09e76 100644 --- a/sound/soc/amd/acp/soc-acpi-amd-sdca-quirks.c +++ b/sound/soc/amd/acp/soc-acpi-amd-sdca-quirks.c @@ -35,8 +35,4 @@ bool snd_soc_acpi_amd_sdca_is_device_rt712_vb(void *arg) return false; } -EXPORT_SYMBOL_NS(snd_soc_acpi_amd_sdca_is_device_rt712_vb, "SND_SOC_ACPI_AMD_SDCA_QUIRKS"); -MODULE_DESCRIPTION("ASoC ACPI AMD SDCA quirks"); -MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS("SND_SOC_SDCA"); From c35fac6481a1dbb2d114dd337b54a40799437546 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Tue, 2 Jun 2026 21:13:44 +0100 Subject: [PATCH 0582/1054] mmc: mmc_test: Fix __counted_by handling after kzalloc_flex() conversion Fix logic issues introduced by the kzalloc_flex() conversion in mmc_test_alloc_mem() due to interaction with the __counted_by annotation on the flexible array. Bounds-checking sanitizers rely on the counter field reflecting the allocated array size before any array access occurs. However, use mem->cnt both as the allocation size and as the runtime insertion index, causing incorrect indexing and potentially invalid bounds tracking. Initialize mem->cnt to the maximum allocated number of segments immediately after kzalloc_flex(), then use a separate local index variable to track successfully allocated entries. Update mem->cnt to the actual number of initialized elements before returning or entering the cleanup path. Also rewrite mmc_test_free_mem() to use a forward for-loop, improving readability and ensuring only initialized entries are freed. Fixes: c3126dccfd7b ("mmc: mmc_test: use kzalloc_flex") Signed-off-by: Lad Prabhakar Reviewed-by: Geert Uytterhoeven Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson --- drivers/mmc/core/mmc_test.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/mmc/core/mmc_test.c b/drivers/mmc/core/mmc_test.c index ab38e4c45a8d..4dc16649e61d 100644 --- a/drivers/mmc/core/mmc_test.c +++ b/drivers/mmc/core/mmc_test.c @@ -318,9 +318,9 @@ static void mmc_test_free_mem(struct mmc_test_mem *mem) { if (!mem) return; - while (mem->cnt--) - __free_pages(mem->arr[mem->cnt].page, - mem->arr[mem->cnt].order); + for (unsigned int i = 0; i < mem->cnt; i++) + __free_pages(mem->arr[i].page, + mem->arr[i].order); kfree(mem); } @@ -341,6 +341,7 @@ static struct mmc_test_mem *mmc_test_alloc_mem(unsigned long min_sz, unsigned long page_cnt = 0; unsigned long limit = nr_free_buffer_pages() >> 4; struct mmc_test_mem *mem; + unsigned int idx = 0; if (max_page_cnt > limit) max_page_cnt = limit; @@ -375,23 +376,26 @@ static struct mmc_test_mem *mmc_test_alloc_mem(unsigned long min_sz, goto out_free; break; } - mem->arr[mem->cnt].page = page; - mem->arr[mem->cnt].order = order; - mem->cnt += 1; + mem->arr[idx].page = page; + mem->arr[idx].order = order; + idx += 1; if (max_page_cnt <= (1UL << order)) break; max_page_cnt -= 1UL << order; page_cnt += 1UL << order; - if (mem->cnt >= max_segs) { + if (idx >= mem->cnt) { if (page_cnt < min_page_cnt) goto out_free; break; } } + mem->cnt = idx; + return mem; out_free: + mem->cnt = idx; mmc_test_free_mem(mem); return NULL; } From 1b0d946d6f08bd39211385bc703a440911b41e46 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sat, 13 Jun 2026 21:43:37 +0300 Subject: [PATCH 0583/1054] Bluetooth: hci_uart: clear HCI_UART_SENDING when write_work is canceled HCI_UART_SENDING bit in tx_state means write_work is pending and blocks queueing it again. Currently this bit is not cleared when canceling the work in hci_uart_close(), which blocks future writes when device is reopened later if write_work was pending. Fix by clearing HCI_UART_SENDING when canceling the work. Also make clearing of tx_skb safe by using disable_work_sync + enable_work instead of just cancel_work_sync. hci_uart_flush() purges the proto tx queue so we can cancel the pending write_work there, instead of doing it just in hci_uart_close(). Re-enable and possibly requeue the work after queue flush. Fixes: c1bb9336ae6b ("Bluetooth: hci_uart: fix UAFs and race conditions in close and init paths") Link: https://lore.kernel.org/linux-bluetooth/07e0a28650773abec711ee492fdb1bf5d21a6c98.camel@iki.fi/ Cc: stable@vger.kernel.org Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/hci_ldisc.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 47f4902b40b4..2ad42c3bbaac 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -239,6 +239,8 @@ static int hci_uart_flush(struct hci_dev *hdev) BT_DBG("hdev %p tty %p", hdev, tty); + disable_work_sync(&hu->write_work); + if (hu->tx_skb) { kfree_skb(hu->tx_skb); hu->tx_skb = NULL; } @@ -254,6 +256,14 @@ static int hci_uart_flush(struct hci_dev *hdev) percpu_up_read(&hu->proto_lock); + /* Resume TX. Also reschedule in case work was queued concurrently; + * this may schedule write_work although there's nothing to do. + */ + enable_work(&hu->write_work); + clear_bit(HCI_UART_SENDING, &hu->tx_state); + if (test_bit(HCI_UART_TX_WAKEUP, &hu->tx_state)) + hci_uart_tx_wakeup(hu); + return 0; } @@ -271,12 +281,8 @@ static int hci_uart_open(struct hci_dev *hdev) /* Close device */ static int hci_uart_close(struct hci_dev *hdev) { - struct hci_uart *hu = hci_get_drvdata(hdev); - BT_DBG("hdev %p", hdev); - cancel_work_sync(&hu->write_work); - hci_uart_flush(hdev); hdev->flush = NULL; return 0; From a3b5f242997a3be7404112fd48784881560aea57 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Fri, 12 Jun 2026 11:27:56 +0800 Subject: [PATCH 0584/1054] mmc: vub300: fix use-after-free on probe failure The vub300 driver lifetime-manages its controller state using vub300->kref, with vub300_delete() freeing the mmc host when the last reference is dropped. The probe error path after the inactivity timer has been armed still bypasses that lifetime rule, however, and falls through to mmc_free_host() directly if mmc_add_host() fails. The race window is between arming the inactivity timer and reaching the probe error unwind after mmc_add_host() fails: probe thread timer/workqueue ------------ --------------- kref_init(&vub300->kref) ref = 1 kref_get(&vub300->kref) ref = 2, timer ref add_timer(inactivity_timer) fires after one second | | race window |<----------------------------------------------------> | mmc_add_host(mmc) inactivity timer fires vub300_queue_dead_work() kref_get() ref = 3 queue_work(deadwork) mmc_add_host() fails timer_delete_sync() mmc_free_host(mmc) frees vub300 deadwork runs use-after-free The inactivity timeout is one second, so this would require mmc_add_host() to both fail and take more than one second to do so. This is unlikely to happen in practice, but the error path is still wrong. timer_delete_sync() only waits for the timer callback itself. It does not flush deadwork that the callback may already have queued. As a result, queued deadwork can still hold a kref while the probe error path directly frees the backing mmc host, including the vub300 storage. Fix this by using the same lifetime mechanism as disconnect. Clear vub300->interface so that the timer callback and any queued deadwork return early and drop their references, then drop the initial probe reference and return without falling through to err_free_host. Fixes: 0613ad2401f8 ("mmc: vub300: fix return value check of mmc_add_host()") Signed-off-by: Guangshuo Li Reviewed-by: Johan Hovold Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/vub300.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/vub300.c b/drivers/mmc/host/vub300.c index 6c3cb2f1c9d3..c1c21e95f5bf 100644 --- a/drivers/mmc/host/vub300.c +++ b/drivers/mmc/host/vub300.c @@ -2336,12 +2336,16 @@ static int vub300_probe(struct usb_interface *interface, interface_to_InterfaceNumber(interface)); retval = mmc_add_host(mmc); if (retval) - goto err_delete_timer; + goto err_stop_io; return 0; -err_delete_timer: - timer_delete_sync(&vub300->inactivity_timer); +err_stop_io: + vub300->interface = NULL; + kref_put(&vub300->kref, vub300_delete); + + return retval; + err_free_host: mmc_free_host(mmc); /* From ee5fb641c4ccac8406c668d3e947eb20ce44f233 Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Wed, 17 Jun 2026 23:23:19 +0800 Subject: [PATCH 0585/1054] mmc: vub300: defer reset until cmd_mutex is unlocked vub300_cmndwork_thread() holds cmd_mutex while it sends a command and waits for the command response. If the response wait times out, __vub300_command_response() kills the command URBs and then synchronously resets the USB device through usb_reset_device(). That reset path re-enters the driver through vub300_pre_reset(), which also takes cmd_mutex. The worker therefore tries to acquire the same mutex recursively while it is still holding it from the command path. This issue was found by our static analysis tool and then manually reviewed against the current tree. The grounded PoC kept the real worker and timeout/reset carrier: vub300_cmndwork_thread() __vub300_command_response() usb_lock_device_for_reset() usb_reset_device() vub300_pre_reset() Lockdep reported the same-task recursive acquisition on cmd_mutex: WARNING: possible recursive locking detected ... (&test_vub300.cmd_mutex) ... at: usb_reset_device... [vuln_msv] ... (&test_vub300.cmd_mutex) ... at: vub300_cmndwork_thread+0x12/0x20 [vuln_msv] Workqueue: vub300_cmd_wq vub300_cmndwork_thread [vuln_msv] *** DEADLOCK *** Return a flag from __vub300_command_response() when the timeout path needs a device reset, then perform the reset after vub300_cmndwork_thread() has cleared the in-flight command state and dropped cmd_mutex. The reset is still attempted before mmc_request_done(), preserving the existing request completion ordering while avoiding the recursive lock. Fixes: 88095e7b473a ("mmc: Add new VUB300 USB-to-SD/SDIO/MMC driver") Cc: stable@vger.kernel.org Signed-off-by: Runyu Xiao Signed-off-by: Ulf Hansson --- drivers/mmc/host/vub300.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/drivers/mmc/host/vub300.c b/drivers/mmc/host/vub300.c index c1c21e95f5bf..2dae474dcd06 100644 --- a/drivers/mmc/host/vub300.c +++ b/drivers/mmc/host/vub300.c @@ -1586,7 +1586,7 @@ static int __command_write_data(struct vub300_mmc_host *vub300, return linear_length; } -static void __vub300_command_response(struct vub300_mmc_host *vub300, +static bool __vub300_command_response(struct vub300_mmc_host *vub300, struct mmc_command *cmd, struct mmc_data *data, int data_length) { @@ -1598,17 +1598,11 @@ static void __vub300_command_response(struct vub300_mmc_host *vub300, msecs_to_jiffies(msec_timeout)); if (respretval == 0) { /* TIMED OUT */ /* we don't know which of "out" and "res" if any failed */ - int result; vub300->usb_timed_out = 1; usb_kill_urb(vub300->command_out_urb); usb_kill_urb(vub300->command_res_urb); cmd->error = -ETIMEDOUT; - result = usb_lock_device_for_reset(vub300->udev, - vub300->interface); - if (result == 0) { - result = usb_reset_device(vub300->udev); - usb_unlock_device(vub300->udev); - } + return true; } else if (respretval < 0) { /* we don't know which of "out" and "res" if any failed */ usb_kill_urb(vub300->command_out_urb); @@ -1704,6 +1698,8 @@ static void __vub300_command_response(struct vub300_mmc_host *vub300, } else { cmd->error = -EINVAL; } + + return false; } static void construct_request_response(struct vub300_mmc_host *vub300, @@ -1749,6 +1745,7 @@ static void vub300_cmndwork_thread(struct work_struct *work) struct mmc_request *req = vub300->req; struct mmc_command *cmd = vub300->cmd; struct mmc_data *data = vub300->data; + bool reset_device; int data_length; mutex_lock(&vub300->cmd_mutex); init_completion(&vub300->command_complete); @@ -1771,7 +1768,8 @@ static void vub300_cmndwork_thread(struct work_struct *work) data_length = __command_read_data(vub300, cmd, data); else data_length = __command_write_data(vub300, cmd, data); - __vub300_command_response(vub300, cmd, data, data_length); + reset_device = __vub300_command_response(vub300, cmd, + data, data_length); vub300->req = NULL; vub300->cmd = NULL; vub300->data = NULL; @@ -1779,6 +1777,16 @@ static void vub300_cmndwork_thread(struct work_struct *work) if (cmd->error == -ENOMEDIUM) check_vub300_port_status(vub300); mutex_unlock(&vub300->cmd_mutex); + if (reset_device) { + int result; + + result = usb_lock_device_for_reset(vub300->udev, + vub300->interface); + if (result == 0) { + result = usb_reset_device(vub300->udev); + usb_unlock_device(vub300->udev); + } + } mmc_request_done(vub300->mmc, req); kref_put(&vub300->kref, vub300_delete); return; From e91da9053006da622b865e672a4a37439a3bb88b Mon Sep 17 00:00:00 2001 From: Suraj Kandpal Date: Wed, 1 Jul 2026 14:45:03 +0530 Subject: [PATCH 0586/1054] drm/i915/ltphy: Fix SSC Enablement bit in PORT_CLOCK_CTL According to Bspec we only need to write SSC Enable PLL A bit and leave PLL B bit alone in PORT_CLOCK_CTL register. Bspec: 74667, 74492 Fixes: 3383ba2479f7 ("drm/i915/ltphy: Enable SSC during port clock programming") Signed-off-by: Suraj Kandpal Reviewed-by: Ankit Nautiyal Link: https://patch.msgid.link/20260701091503.1302226-3-suraj.kandpal@intel.com (cherry picked from commit 8e27f752037e72ccee9c4a7c4a6202ecf3daf603) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/i915/display/intel_lt_phy.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_lt_phy.c b/drivers/gpu/drm/i915/display/intel_lt_phy.c index 615ee980470e..34dbe450cc5b 100644 --- a/drivers/gpu/drm/i915/display/intel_lt_phy.c +++ b/drivers/gpu/drm/i915/display/intel_lt_phy.c @@ -1223,11 +1223,7 @@ intel_lt_phy_program_port_clock_ctl(struct intel_encoder *encoder, else val |= XELPDP_DDI_CLOCK_SELECT_PREP(display, XELPDP_DDI_CLOCK_SELECT_MAXPCLK); - /* DP2.0 10G and 20G rates enable MPLLA*/ - if (port_clock == 1000000 || port_clock == 2000000) - val |= XELPDP_SSC_ENABLE_PLLA; - else - val |= ltpll->ssc_enabled ? XELPDP_SSC_ENABLE_PLLB : 0; + val |= ltpll->ssc_enabled ? XELPDP_SSC_ENABLE_PLLA : 0; intel_de_rmw(display, XELPDP_PORT_CLOCK_CTL(display, encoder->port), XELPDP_LANE1_PHY_CLOCK_SELECT | XELPDP_FORWARD_CLOCK_UNGATE | From 2b56757a9a7456825eb668fde92299e01c5e2721 Mon Sep 17 00:00:00 2001 From: Joonas Lahtinen Date: Wed, 1 Jul 2026 10:55:55 +0300 Subject: [PATCH 0587/1054] drm/i915/gem: Fix NULL deref in I915_CONTEXT_PARAM_SSEU Setting context engine slot N into I915_ENGINE_CLASS_INVALID / I915_ENGINE_CLASS_INVALID_NONE and attempting to apply I915_CONTEXT_PARAM_SSEU to the same slot N will deref NULL. Fix that. Discovered using AI-assisted static analysis confirmed by Intel Product Security. Reported-by: Martin Hodo Fixes: d4433c7600f7 ("drm/i915/gem: Use the proto-context to handle create parameters (v5)") Cc: Faith Ekstrand Cc: Simona Vetter Cc: Tvrtko Ursulin Cc: Maarten Lankhorst Cc: # v5.15+ Signed-off-by: Joonas Lahtinen Reviewed-by: Maarten Lankhorst Reviewed-by: Andi Shyti Link: https://patch.msgid.link/20260701075555.52142-1-joonas.lahtinen@linux.intel.com (cherry picked from commit 36eda5b5c2d40da41cc0a5403c26986237cf9e87) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/i915/gem/i915_gem_context.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/gem/i915_gem_context.c b/drivers/gpu/drm/i915/gem/i915_gem_context.c index aeafe1742d30..347d1f2c05f5 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_context.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_context.c @@ -850,7 +850,7 @@ static int set_proto_ctx_sseu(struct drm_i915_file_private *fpriv, pe = &pc->user_engines[idx]; /* Only render engine supports RPCS configuration. */ - if (pe->engine->class != RENDER_CLASS) + if (!pe->engine || pe->engine->class != RENDER_CLASS) return -EINVAL; sseu = &pe->sseu; From 005771c18c5b2c98cb4e7517661aea460990fd3f Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Thu, 25 Jun 2026 17:22:04 +0300 Subject: [PATCH 0588/1054] drm/i915/mst: limit DP MST ESI service loop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The loop in intel_dp_check_mst_status() keeps servicing interrupts originating from the sink without bound. Add an upper bound to the new interrupts occurring during interrupt processing to not get stuck on potentially stuck sink devices. Use arbitrary 32 tries to clear incoming interrupts in one go. Discovered using AI-assisted static analysis confirmed by Intel Product Security. Note: The condition likely pre-dates the commit in the Fixes: tag, but this is about as far back as a backport has any chance of succeeding. Before that, the retry had a goto. Reported-by: Martin Hodo Fixes: 3c0ec2c2d594 ("drm/i915: Flatten intel_dp_check_mst_status() a bit") Cc: stable@vger.kernel.org # v5.8+ Cc: Ville Syrjälä Cc: Imre Deak Reviewed-by: Imre Deak Link: https://patch.msgid.link/20260625142204.1078287-1-jani.nikula@intel.com Signed-off-by: Jani Nikula (cherry picked from commit b4ea5272133059acb493cc36599071a9e852ec2e) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/i915/display/intel_dp.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 85d3aa3b9894..7ff5712f8b19 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -5737,8 +5737,9 @@ intel_dp_check_mst_status(struct intel_dp *intel_dp) struct intel_display *display = to_intel_display(intel_dp); bool force_retrain = intel_dp->link.force_retrain; bool reprobe_needed = false; + int tries = 33; - for (;;) { + while (--tries) { u8 esi[4] = {}; u8 ack[4] = {}; bool new_irqs; @@ -5781,6 +5782,11 @@ intel_dp_check_mst_status(struct intel_dp *intel_dp) break; } + if (!tries) { + drm_dbg_kms(display->drm, "DPRX ESI not clearing, device may be stuck\n"); + reprobe_needed = true; + } + return !reprobe_needed; } From 82ec992c404c3dc774c5e9f3d4aa858e97187675 Mon Sep 17 00:00:00 2001 From: Joonas Lahtinen Date: Wed, 1 Jul 2026 14:45:13 +0300 Subject: [PATCH 0589/1054] drm/i915/gt: Fix NULL deref on sched_engine alloc failure Avoid using intel_context_put() before intel_context_init() in execlists_create_virtual() as the kref_put() inside would lead to NULL deref on the IOCTL path when sched_engine allocation fails. Discovered using AI-assisted static analysis confirmed by Intel Product Security. Reported-by: Martin Hodo Fixes: 3e28d37146db ("drm/i915: Move priolist to new i915_sched_engine object") Cc: Matthew Brost Cc: Daniele Ceraolo Spurio Cc: Tvrtko Ursulin Cc: # v5.15+ Signed-off-by: Joonas Lahtinen Reviewed-by: Andi Shyti Signed-off-by: Tvrtko Ursulin Link: https://lore.kernel.org/r/20260701114513.221254-1-joonas.lahtinen@linux.intel.com (cherry picked from commit 4f2a12f2d50e9f48227656e4dcbd6423506be31d) Signed-off-by: Rodrigo Vivi --- .../drm/i915/gt/intel_execlists_submission.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/i915/gt/intel_execlists_submission.c b/drivers/gpu/drm/i915/gt/intel_execlists_submission.c index 1359fc9cb88e..e693b0c9d2a3 100644 --- a/drivers/gpu/drm/i915/gt/intel_execlists_submission.c +++ b/drivers/gpu/drm/i915/gt/intel_execlists_submission.c @@ -3932,11 +3932,11 @@ execlists_create_virtual(struct intel_engine_cs **siblings, unsigned int count, struct drm_i915_private *i915 = siblings[0]->i915; struct virtual_engine *ve; unsigned int n; - int err; + int err = -ENOMEM; ve = kzalloc_flex(*ve, siblings, count); if (!ve) - return ERR_PTR(-ENOMEM); + goto err; ve->base.i915 = i915; ve->base.gt = siblings[0]->gt; @@ -3968,10 +3968,8 @@ execlists_create_virtual(struct intel_engine_cs **siblings, unsigned int count, intel_engine_init_execlists(&ve->base); ve->base.sched_engine = i915_sched_engine_create(ENGINE_VIRTUAL); - if (!ve->base.sched_engine) { - err = -ENOMEM; - goto err_put; - } + if (!ve->base.sched_engine) + goto err_noput; ve->base.sched_engine->private_data = &ve->base; ve->base.cops = &virtual_context_ops; @@ -3987,10 +3985,8 @@ execlists_create_virtual(struct intel_engine_cs **siblings, unsigned int count, intel_context_init(&ve->context, &ve->base); ve->base.breadcrumbs = intel_breadcrumbs_create(NULL); - if (!ve->base.breadcrumbs) { - err = -ENOMEM; + if (!ve->base.breadcrumbs) goto err_put; - } for (n = 0; n < count; n++) { struct intel_engine_cs *sibling = siblings[n]; @@ -4065,8 +4061,13 @@ execlists_create_virtual(struct intel_engine_cs **siblings, unsigned int count, virtual_engine_initial_hint(ve); return &ve->context; +err_noput: + kfree(ve); + goto err; + err_put: intel_context_put(&ve->context); +err: return ERR_PTR(err); } From eed3de2acf6aa5154d49098b026710b646db67ee Mon Sep 17 00:00:00 2001 From: Joonas Lahtinen Date: Wed, 1 Jul 2026 10:30:30 +0300 Subject: [PATCH 0590/1054] drm/i915/gem: Do not leak siblings[] on proto context error After a successful BALANCE/PARALLEL_SUBMIT extension on context creation, error during processing of next user extension leaks the siblings[] array. Fix that. Discovered using AI-assisted static analysis confirmed by Intel Product Security. Reported-by: Martin Hodo Fixes: d4433c7600f7 ("drm/i915/gem: Use the proto-context to handle create parameters (v5)") Cc: Faith Ekstrand Cc: Simona Vetter Cc: Tvrtko Ursulin Cc: Maarten Lankhorst Cc: # v5.15+ Signed-off-by: Joonas Lahtinen Reviewed-by: Maarten Lankhorst Signed-off-by: Tvrtko Ursulin Link: https://lore.kernel.org/r/20260701073030.44850-1-joonas.lahtinen@linux.intel.com (cherry picked from commit aa65e0a4b51b3b54b53e4142aaa2d997aa1061ff) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/i915/gem/i915_gem_context.c | 22 +++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/gem/i915_gem_context.c b/drivers/gpu/drm/i915/gem/i915_gem_context.c index 347d1f2c05f5..c58ffa5a8fa6 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_context.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_context.c @@ -769,8 +769,8 @@ static int set_proto_ctx_engines(struct drm_i915_file_private *fpriv, struct intel_engine_cs *engine; if (copy_from_user(&ci, &user->engines[n], sizeof(ci))) { - kfree(set.engines); - return -EFAULT; + err = -EFAULT; + goto err; } memset(&set.engines[n], 0, sizeof(set.engines[n])); @@ -786,8 +786,8 @@ static int set_proto_ctx_engines(struct drm_i915_file_private *fpriv, drm_dbg(&i915->drm, "Invalid engine[%d]: { class:%d, instance:%d }\n", n, ci.engine_class, ci.engine_instance); - kfree(set.engines); - return -ENOENT; + err = -ENOENT; + goto err; } set.engines[n].type = I915_GEM_ENGINE_TYPE_PHYSICAL; @@ -800,15 +800,21 @@ static int set_proto_ctx_engines(struct drm_i915_file_private *fpriv, set_proto_ctx_engines_extensions, ARRAY_SIZE(set_proto_ctx_engines_extensions), &set); - if (err) { - kfree(set.engines); - return err; - } + if (err) + goto err_extensions; pc->num_user_engines = set.num_engines; pc->user_engines = set.engines; return 0; + +err_extensions: + for (n = 0; n < set.num_engines; n++) + kfree(set.engines[n].siblings); +err: + kfree(set.engines); + + return err; } static int set_proto_ctx_sseu(struct drm_i915_file_private *fpriv, From d38eaf611839b85ade3dd3db309dbc8aaaaf0095 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Fri, 12 Jun 2026 10:21:09 -0400 Subject: [PATCH 0591/1054] Bluetooth: 6lowpan: Fix using chan->conn as indication to no remote netdev b66774b48dd9 ("Bluetooth: L2CAP: Fix UAF in channel timeout by holding conn ref") don't reset the chan->conn to NULL anymore making the bt# netdev not be remove once the last l2cap_chan_del is removed. Instead of restoring the original behavior this remove the logic of keeping the interface after the last channel is removed because it never worked as intended and the l2cap_chan_del always detach its l2cap_conn which results in always removing the channel anyway. Fixes: b66774b48dd9 ("Bluetooth: L2CAP: Fix UAF in channel timeout by holding conn ref") Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/6lowpan.c | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index cb1e329d66fd..962e0e885105 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -797,20 +797,10 @@ static void chan_close_cb(struct l2cap_chan *chan) struct lowpan_btle_dev *dev = NULL; struct lowpan_peer *peer; int err = -ENOENT; - bool last = false, remove = true; + bool last = false; BT_DBG("chan %p conn %p", chan, chan->conn); - if (chan->conn && chan->conn->hcon) { - if (!is_bt_6lowpan(chan->conn->hcon)) - return; - - /* If conn is set, then the netdev is also there and we should - * not remove it. - */ - remove = false; - } - spin_lock(&devices_lock); list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) { @@ -837,10 +827,8 @@ static void chan_close_cb(struct l2cap_chan *chan) ifdown(dev->netdev); - if (remove) { - INIT_WORK(&entry->delete_netdev, delete_netdev); - schedule_work(&entry->delete_netdev); - } + INIT_WORK(&entry->delete_netdev, delete_netdev); + schedule_work(&entry->delete_netdev); } else { spin_unlock(&devices_lock); } From fa85d985f614bc3feb343000f14a1072e99b0df1 Mon Sep 17 00:00:00 2001 From: Samuel Page Date: Mon, 15 Jun 2026 16:09:22 +0100 Subject: [PATCH 0592/1054] Bluetooth: MGMT: Fix UAF of hci_conn_params in add_device_complete add_device_complete() runs from the hci_cmd_sync_work kworker, which holds only hci_req_sync_lock and *not* hci_dev_lock. It calls hci_conn_params_lookup() and then dereferences the returned object (params->flags) without taking hci_dev_lock: params = hci_conn_params_lookup(hdev, &cp->addr.bdaddr, le_addr_type(cp->addr.type)); ... device_flags_changed(NULL, hdev, &cp->addr.bdaddr, cp->addr.type, hdev->conn_flags, params ? params->flags : 0); hci_conn_params_lookup() walks hdev->le_conn_params and is documented to require hdev->lock. A concurrent MGMT_OP_REMOVE_DEVICE (remove_device()), which does run under hci_dev_lock, can call hci_conn_params_free() to list_del() and kfree() the very object the lookup returned, so the subsequent params->flags read touches freed memory [0]. Hold hci_dev_lock() across the hci_conn_params_lookup() and the read of params->flags (and the matching event emission) so the lookup result cannot be freed by a concurrent remove_device() before it is used, honouring the locking contract of hci_conn_params_lookup(). [0]: (trailing page/memory-state dump trimmed) BUG: KASAN: slab-use-after-free in add_device_complete+0x358/0x3d8 net/bluetooth/mgmt.c:7671 Read of size 1 at addr ffff000017ab26c1 by task kworker/u9:8/388 CPU: 1 UID: 0 PID: 388 Comm: kworker/u9:8 Not tainted 7.0.11 #20 PREEMPT Hardware name: linux,dummy-virt (DT) Workqueue: hci0 hci_cmd_sync_work Call trace: show_stack+0x2c/0x3c arch/arm64/kernel/stacktrace.c:499 (C) __dump_stack lib/dump_stack.c:94 [inline] dump_stack_lvl+0xb4/0xd4 lib/dump_stack.c:120 print_address_description mm/kasan/report.c:378 [inline] print_report+0x118/0x5d8 mm/kasan/report.c:482 kasan_report+0xb0/0xf4 mm/kasan/report.c:595 __asan_report_load1_noabort+0x20/0x2c mm/kasan/report_generic.c:378 add_device_complete+0x358/0x3d8 net/bluetooth/mgmt.c:7671 hci_cmd_sync_work+0x14c/0x240 net/bluetooth/hci_sync.c:334 process_one_work+0x628/0xd38 kernel/workqueue.c:3289 process_scheduled_works kernel/workqueue.c:3372 [inline] worker_thread+0x7a8/0xac0 kernel/workqueue.c:3453 kthread+0x39c/0x444 kernel/kthread.c:436 ret_from_fork+0x10/0x20 arch/arm64/kernel/entry.S:860 Allocated by task 3401: kasan_save_stack+0x3c/0x64 mm/kasan/common.c:57 kasan_save_track+0x20/0x3c mm/kasan/common.c:78 kasan_save_alloc_info+0x40/0x54 mm/kasan/generic.c:570 poison_kmalloc_redzone mm/kasan/common.c:398 [inline] __kasan_kmalloc+0xd4/0xd8 mm/kasan/common.c:415 kasan_kmalloc include/linux/kasan.h:263 [inline] __kmalloc_cache_noprof+0x1b0/0x458 mm/slub.c:5385 kmalloc_noprof include/linux/slab.h:950 [inline] kzalloc_noprof include/linux/slab.h:1188 [inline] hci_conn_params_add+0x10c/0x4b0 net/bluetooth/hci_core.c:2279 hci_conn_params_set net/bluetooth/mgmt.c:5162 [inline] add_device+0x5b4/0xa54 net/bluetooth/mgmt.c:7755 hci_mgmt_cmd net/bluetooth/hci_sock.c:1721 [inline] hci_sock_sendmsg+0x10b4/0x1dd0 net/bluetooth/hci_sock.c:1841 sock_sendmsg_nosec net/socket.c:727 [inline] __sock_sendmsg+0xe0/0x128 net/socket.c:742 sock_write_iter+0x250/0x390 net/socket.c:1195 new_sync_write fs/read_write.c:595 [inline] vfs_write+0x66c/0xab0 fs/read_write.c:688 ksys_write+0x1fc/0x24c fs/read_write.c:740 __do_sys_write fs/read_write.c:751 [inline] __se_sys_write fs/read_write.c:748 [inline] __arm64_sys_write+0x70/0xa4 fs/read_write.c:748 __invoke_syscall arch/arm64/kernel/syscall.c:35 [inline] invoke_syscall+0x84/0x2a8 arch/arm64/kernel/syscall.c:49 el0_svc_common.constprop.0+0xe4/0x294 arch/arm64/kernel/syscall.c:132 do_el0_svc+0x44/0x5c arch/arm64/kernel/syscall.c:151 el0_svc+0x38/0xac arch/arm64/kernel/entry-common.c:724 el0t_64_sync_handler+0xa0/0xe4 arch/arm64/kernel/entry-common.c:743 el0t_64_sync+0x198/0x19c arch/arm64/kernel/entry.S:596 Freed by task 3740: kasan_save_stack+0x3c/0x64 mm/kasan/common.c:57 kasan_save_track+0x20/0x3c mm/kasan/common.c:78 kasan_save_free_info+0x4c/0x74 mm/kasan/generic.c:584 poison_slab_object mm/kasan/common.c:253 [inline] __kasan_slab_free+0x88/0xb8 mm/kasan/common.c:285 kasan_slab_free include/linux/kasan.h:235 [inline] slab_free_hook mm/slub.c:2685 [inline] slab_free mm/slub.c:6170 [inline] kfree+0x14c/0x458 mm/slub.c:6488 hci_conn_params_free+0x288/0x484 net/bluetooth/hci_core.c:2312 remove_device+0x4b0/0x968 net/bluetooth/mgmt.c:7919 hci_mgmt_cmd net/bluetooth/hci_sock.c:1721 [inline] hci_sock_sendmsg+0x10b4/0x1dd0 net/bluetooth/hci_sock.c:1841 sock_sendmsg_nosec net/socket.c:727 [inline] __sock_sendmsg+0xe0/0x128 net/socket.c:742 sock_write_iter+0x250/0x390 net/socket.c:1195 new_sync_write fs/read_write.c:595 [inline] vfs_write+0x66c/0xab0 fs/read_write.c:688 ksys_write+0x1fc/0x24c fs/read_write.c:740 __do_sys_write fs/read_write.c:751 [inline] __se_sys_write fs/read_write.c:748 [inline] __arm64_sys_write+0x70/0xa4 fs/read_write.c:748 __invoke_syscall arch/arm64/kernel/syscall.c:35 [inline] invoke_syscall+0x84/0x2a8 arch/arm64/kernel/syscall.c:49 el0_svc_common.constprop.0+0xe4/0x294 arch/arm64/kernel/syscall.c:132 do_el0_svc+0x44/0x5c arch/arm64/kernel/syscall.c:151 el0_svc+0x38/0xac arch/arm64/kernel/entry-common.c:724 el0t_64_sync_handler+0xa0/0xe4 arch/arm64/kernel/entry-common.c:743 el0t_64_sync+0x198/0x19c arch/arm64/kernel/entry.S:596 Fixes: 1e2e3044c1bc ("Bluetooth: MGMT: Fix MGMT_OP_ADD_DEVICE invalid device flags") Cc: stable@vger.kernel.org Assisted-by: Bynario AI Signed-off-by: Samuel Page Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/mgmt.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index d23ca1dd0893..dc55763f9e58 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -7658,6 +7658,8 @@ static void add_device_complete(struct hci_dev *hdev, void *data, int err) if (!err) { struct hci_conn_params *params; + hci_dev_lock(hdev); + params = hci_conn_params_lookup(hdev, &cp->addr.bdaddr, le_addr_type(cp->addr.type)); @@ -7666,6 +7668,7 @@ static void add_device_complete(struct hci_dev *hdev, void *data, int err) device_flags_changed(NULL, hdev, &cp->addr.bdaddr, cp->addr.type, hdev->conn_flags, params ? params->flags : 0); + hci_dev_unlock(hdev); } mgmt_cmd_complete(cmd->sk, hdev->id, MGMT_OP_ADD_DEVICE, From d5541eb148da72d5e0a1bca8ecd171f9fc8b366f Mon Sep 17 00:00:00 2001 From: Muhammad Bilal Date: Sun, 21 Jun 2026 21:23:05 +0500 Subject: [PATCH 0593/1054] Bluetooth: ISO: avoid NULL deref of conn in iso_conn_big_sync() iso_conn_big_sync() drops the socket lock to call hci_get_route() and then re-acquires it, but dereferences iso_pi(sk)->conn->hcon afterwards without re-checking that conn is still valid. While the lock is dropped, the connection can be torn down under the same socket lock: iso_disconn_cfm() -> iso_conn_del() -> iso_chan_del() sets iso_pi(sk)->conn to NULL (and the broadcast teardown path can also clear conn->hcon on its own). When iso_conn_big_sync() re-acquires the lock and reads conn->hcon, conn may be NULL, causing a NULL pointer dereference (hcon is the first member of struct iso_conn). This path is reached from iso_sock_recvmsg() for a PA-sync broadcast sink socket (BT_SK_DEFER_SETUP | BT_SK_PA_SYNC), so the dropped-lock window can race with connection teardown driven by controller events. Re-validate iso_pi(sk)->conn and its hcon after re-acquiring the socket lock and bail out if the connection went away, as already done in the sibling iso_sock_rebind_bc(). Fixes: 7a17308c17880d ("Bluetooth: iso: Fix circular lock in iso_conn_big_sync") Cc: stable@vger.kernel.org Signed-off-by: Muhammad Bilal Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/iso.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index 793a481d7066..cd7c7c9ea4fc 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -1590,6 +1590,7 @@ static void iso_conn_big_sync(struct sock *sk) { int err; struct hci_dev *hdev; + struct iso_conn *conn; bdaddr_t src, dst; u8 src_type; @@ -1612,8 +1613,17 @@ static void iso_conn_big_sync(struct sock *sk) hci_dev_lock(hdev); lock_sock(sk); + /* The socket lock was dropped for hci_get_route(), so the connection + * may have been torn down meanwhile: iso_chan_del() clears conn and + * the broadcast teardown path can clear conn->hcon on its own. Check + * both before dereferencing conn->hcon. + */ + conn = iso_pi(sk)->conn; + if (!conn || !conn->hcon) + goto unlock; + if (!test_and_set_bit(BT_SK_BIG_SYNC, &iso_pi(sk)->flags)) { - err = hci_conn_big_create_sync(hdev, iso_pi(sk)->conn->hcon, + err = hci_conn_big_create_sync(hdev, conn->hcon, &iso_pi(sk)->qos, iso_pi(sk)->sync_handle, iso_pi(sk)->bc_num_bis, @@ -1622,6 +1632,7 @@ static void iso_conn_big_sync(struct sock *sk) bt_dev_err(hdev, "hci_big_create_sync: %d", err); } +unlock: release_sock(sk); hci_dev_unlock(hdev); hci_dev_put(hdev); From 2641a9e0a1dd4af2e21995470a21d55dd35e5203 Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Wed, 17 Jun 2026 23:36:13 +0800 Subject: [PATCH 0594/1054] Bluetooth: L2CAP: cancel pending_rx_work before taking conn->lock l2cap_conn_del() takes conn->lock and then calls cancel_work_sync() for pending_rx_work. process_pending_rx() takes the same mutex, so teardown can deadlock against the worker it is flushing. This issue was found by our static analysis tool and then manually reviewed against the current tree. The grounded PoC kept the l2cap_conn_ready() -> queue_work(..., &conn->pending_rx_work) submit path, the l2cap_conn_del() -> cancel_work_sync(&conn->pending_rx_work) teardown path, and the process_pending_rx() -> mutex_lock(&conn->lock) worker edge. Lockdep reported: WARNING: possible circular locking dependency detected process_pending_rx+0x21/0x2a [vuln_msv] l2cap_conn_del.constprop.0+0x3f/0x4e [vuln_msv] *** DEADLOCK *** Cancel pending_rx_work before taking conn->lock, matching the existing lock-before-drain ordering used for the two delayed works in the same teardown path. The pending_rx queue is still purged after the work has been cancelled and conn->lock has been acquired. Fixes: 7ab56c3a6ecc ("Bluetooth: Fix deadlock in l2cap_conn_del()") Cc: stable@vger.kernel.org Signed-off-by: Runyu Xiao Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/l2cap_core.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 62133eef9d2f..036d887dec34 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1775,19 +1775,13 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err) disable_delayed_work_sync(&conn->info_timer); disable_delayed_work_sync(&conn->id_addr_timer); + cancel_work_sync(&conn->pending_rx_work); + mutex_lock(&conn->lock); kfree_skb(conn->rx_skb); skb_queue_purge(&conn->pending_rx); - - /* We can not call flush_work(&conn->pending_rx_work) here since we - * might block if we are running on a worker from the same workqueue - * pending_rx_work is waiting on. - */ - if (work_pending(&conn->pending_rx_work)) - cancel_work_sync(&conn->pending_rx_work); - ida_destroy(&conn->tx_ida); l2cap_unregister_all_users(conn); From 687617555cedfb74c9e3cb85d759b908dcb17856 Mon Sep 17 00:00:00 2001 From: Muhammad Bilal Date: Sun, 21 Jun 2026 00:56:35 +0500 Subject: [PATCH 0595/1054] Bluetooth: L2CAP: validate option length before reading conf opt value l2cap_get_conf_opt() derives the option length from the attacker-controlled opt->len field and immediately dereferences opt->val (as u8, get_unaligned_le16() or get_unaligned_le32(), or a raw pointer for the default case) before any caller has confirmed that opt->len bytes are present in the buffer. The callers (l2cap_parse_conf_req(), l2cap_parse_conf_rsp() and l2cap_conf_rfc_get()) only detect a malformed option afterwards, once the running length has gone negative, by which point the out-of-bounds read has already executed. An existing post-hoc length check keeps the garbage value from being consumed, so this is not a data leak in the current control flow. It is still a validate-after-use ordering bug: up to 4 bytes are read past the end of the buffer before it is known to contain them, and it is fragile to future changes in the callers. Fix it at the source. Pass the end of the buffer into l2cap_get_conf_opt() and refuse to touch opt->val unless the full option (header + value) fits. Each caller computes an end pointer once before the loop and checks the return value directly instead of inferring the error from a negative length. Fixes: 7c9cbd0b5e38 ("Bluetooth: Verify that l2cap_get_conf_opt provides large enough buffer") Cc: stable@vger.kernel.org Signed-off-by: Muhammad Bilal Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/l2cap_core.c | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 036d887dec34..a1d249f42be7 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -3045,13 +3045,24 @@ static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn, u8 code, return NULL; } -static inline int l2cap_get_conf_opt(void **ptr, int *type, int *olen, - unsigned long *val) +static inline int l2cap_get_conf_opt(void **ptr, void *end, int *type, + int *olen, unsigned long *val) { struct l2cap_conf_opt *opt = *ptr; int len; + /* opt->len is attacker-controlled. Validate that the full option + * (header + value) actually fits in the buffer before touching + * opt->val, otherwise the switch below reads past the end of the + * caller's buffer. + */ + if (end - *ptr < L2CAP_CONF_OPT_SIZE) + return -EINVAL; + len = L2CAP_CONF_OPT_SIZE + opt->len; + if (end - *ptr < len) + return -EINVAL; + *ptr += len; *type = opt->type; @@ -3423,6 +3434,7 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data, size_t data void *ptr = rsp->data; void *endptr = data + data_size; void *req = chan->conf_req; + void *req_end = req + chan->conf_len; int len = chan->conf_len; int type, hint, olen; unsigned long val; @@ -3436,9 +3448,11 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data, size_t data BT_DBG("chan %p", chan); while (len >= L2CAP_CONF_OPT_SIZE) { - len -= l2cap_get_conf_opt(&req, &type, &olen, &val); - if (len < 0) + int ret = l2cap_get_conf_opt(&req, req_end, &type, &olen, &val); + + if (ret < 0) break; + len -= ret; hint = type & L2CAP_CONF_HINT; type &= L2CAP_CONF_MASK; @@ -3666,6 +3680,7 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, struct l2cap_conf_req *req = data; void *ptr = req->data; void *endptr = data + size; + void *rsp_end = rsp + len; int type, olen; unsigned long val; struct l2cap_conf_rfc rfc = { .mode = L2CAP_MODE_BASIC }; @@ -3674,9 +3689,11 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, BT_DBG("chan %p, rsp %p, len %d, req %p", chan, rsp, len, data); while (len >= L2CAP_CONF_OPT_SIZE) { - len -= l2cap_get_conf_opt(&rsp, &type, &olen, &val); - if (len < 0) + int ret = l2cap_get_conf_opt(&rsp, rsp_end, &type, &olen, &val); + + if (ret < 0) break; + len -= ret; switch (type) { case L2CAP_CONF_MTU: @@ -3927,6 +3944,7 @@ static void l2cap_conf_rfc_get(struct l2cap_chan *chan, void *rsp, int len) { int type, olen; unsigned long val; + void *rsp_end = rsp + len; /* Use sane default values in case a misbehaving remote device * did not send an RFC or extended window size option. */ @@ -3945,9 +3963,11 @@ static void l2cap_conf_rfc_get(struct l2cap_chan *chan, void *rsp, int len) return; while (len >= L2CAP_CONF_OPT_SIZE) { - len -= l2cap_get_conf_opt(&rsp, &type, &olen, &val); - if (len < 0) + int ret = l2cap_get_conf_opt(&rsp, rsp_end, &type, &olen, &val); + + if (ret < 0) break; + len -= ret; switch (type) { case L2CAP_CONF_RFC: From badff6c3bed8923a1257a853f137d447976eec30 Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Wed, 17 Jun 2026 16:36:52 +0800 Subject: [PATCH 0596/1054] Bluetooth: btnxpuart: Fix out-of-bounds firmware read in nxp_recv_fw_req_v3() During the v3 firmware download the controller sends a v3_data_req with a 32 bit offset and a 16 bit len. nxp_recv_fw_req_v3() checks only the lower bound of the offset and then sends firmware from that offset. nxpdev->fw_dnld_v3_offset = offset - nxpdev->fw_v3_offset_correction; serdev_device_write_buf(nxpdev->serdev, nxpdev->fw->data + nxpdev->fw_dnld_v3_offset, len); Nothing checks that fw_dnld_v3_offset + len stays within nxpdev->fw->size, so a controller that asks for an offset or length past the firmware image makes the driver read past the end of nxpdev->fw->data and send that memory back over UART. nxp_recv_fw_req_v1() already bounds the same write. Add the equivalent check to the v3 path, reject the request when it falls outside the firmware image, and zero len on the error path so the fw_v3_prev_sent bookkeeping at free_skb stays consistent. Fixes: 689ca16e5232 ("Bluetooth: NXP: Add protocol support for NXP Bluetooth chipsets") Suggested-by: Neeraj Sanjay Kale Reviewed-by: Neeraj Sanjay Kale Cc: stable@vger.kernel.org Signed-off-by: Maoyi Xie Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btnxpuart.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/bluetooth/btnxpuart.c b/drivers/bluetooth/btnxpuart.c index e7036a48ce48..6a1cffe08d5f 100644 --- a/drivers/bluetooth/btnxpuart.c +++ b/drivers/bluetooth/btnxpuart.c @@ -1267,6 +1267,12 @@ static int nxp_recv_fw_req_v3(struct hci_dev *hdev, struct sk_buff *skb) } nxpdev->fw_dnld_v3_offset = offset - nxpdev->fw_v3_offset_correction; + if (nxpdev->fw_dnld_v3_offset >= nxpdev->fw->size || + len > nxpdev->fw->size - nxpdev->fw_dnld_v3_offset) { + bt_dev_err(hdev, "FW download out of bounds, ignoring request"); + len = 0; + goto free_skb; + } serdev_device_write_buf(nxpdev->serdev, nxpdev->fw->data + nxpdev->fw_dnld_v3_offset, len); From 12917f591cea1af36087dba5b9ec888652f0b42a Mon Sep 17 00:00:00 2001 From: Siwei Zhang Date: Mon, 15 Jun 2026 11:33:05 -0400 Subject: [PATCH 0597/1054] Bluetooth: hci_conn: Fix null ptr deref in hci_abort_conn() hci_abort_conn() read hci_skb_event(hdev->sent_cmd) when a connection was pending, but hdev->sent_cmd can be NULL while req_status is still HCI_REQ_PEND, leading to a NULL pointer dereference and a general protection fault from the hci_rx_work() receive path. Instead of inspecting hdev->sent_cmd, track the in-flight create connection command with a new per-connection HCI_CONN_CREATE flag and route all cancellation through hci_cancel_connect_sync(), which dispatches to a dedicated per-type cancel function. The create command is in exactly one of two states: still queued, or in flight. The cancel function holds cmd_sync_work_lock across the whole decision: the worker takes this lock to dequeue every entry, so while it is held a queued command cannot start running and an in-flight command cannot complete and let the next command become pending. This keeps the flag test and hci_cmd_sync_cancel() atomic with respect to the worker, so a queued command is simply dequeued, and an in-flight command owned by this connection is cancelled without the risk of cancelling an unrelated command that became pending in the meantime. CIS uses the same flag mechanism via HCI_CONN_CREATE_CIS but cannot be dequeued per-connection. hci_acl_create_conn_sync() and hci_le_create_conn_sync() clear HCI_CONN_CREATE after the create command completes, but the command status handler can free conn via hci_conn_del() (for example when the controller rejects the connection) while the worker is still blocked on the connection complete event. Hold a reference on conn across the create command so the flag can be cleared without a use-after-free. Fixes: a13f316e90fd ("Bluetooth: hci_conn: Consolidate code for aborting connections") Cc: stable@vger.kernel.org Suggested-by: XIAO WU Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Siwei Zhang Signed-off-by: Luiz Augusto von Dentz --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_conn.c | 21 +---- net/bluetooth/hci_sync.c | 137 +++++++++++++++++++++++++++---- 3 files changed, 125 insertions(+), 34 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 7e15da47fe3a..4ca09298e11a 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -985,6 +985,7 @@ enum { HCI_CONN_AUTH_FAILURE, HCI_CONN_PER_ADV, HCI_CONN_BIG_CREATED, + HCI_CONN_CREATE, HCI_CONN_CREATE_CIS, HCI_CONN_CREATE_BIG_SYNC, HCI_CONN_BIG_SYNC, diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index c335372e4062..1966cd153d97 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -3178,26 +3178,11 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason) conn->abort_reason = reason; - /* If the connection is pending check the command opcode since that - * might be blocking on hci_cmd_sync_work while waiting its respective - * event so we need to hci_cmd_sync_cancel to cancel it. - * - * hci_connect_le serializes the connection attempts so only one - * connection can be in BT_CONNECT at time. + /* Cancel the connect attempt. A return of 0 means the create command + * was still queued and got dequeued, so there is nothing to disconnect. */ - if (conn->state == BT_CONNECT && READ_ONCE(hdev->req_status) == HCI_REQ_PEND) { - switch (hci_skb_event(hdev->sent_cmd)) { - case HCI_EV_CONN_COMPLETE: - case HCI_EV_LE_CONN_COMPLETE: - case HCI_EV_LE_ENHANCED_CONN_COMPLETE: - case HCI_EVT_LE_CIS_ESTABLISHED: - hci_cmd_sync_cancel(hdev, ECANCELED); - break; - } - /* Cancel connect attempt if still queued/pending */ - } else if (!hci_cancel_connect_sync(hdev, conn)) { + if (!hci_cancel_connect_sync(hdev, conn)) return 0; - } /* Run immediately if on cmd_sync_work since this may be called * as a result to MGMT_OP_DISCONNECT/MGMT_OP_UNPAIR which does diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index 3be8c3581c6c..c896d4edd013 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -6633,6 +6633,11 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data) bt_dev_dbg(hdev, "conn %p", conn); + /* Hold a reference so conn stays valid for the HCI_CONN_CREATE + * clear_bit() at done. + */ + hci_conn_get(conn); + clear_bit(HCI_CONN_SCANNING, &conn->flags); conn->state = BT_CONNECT; @@ -6645,6 +6650,7 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data) hdev->le_scan_type == LE_SCAN_ACTIVE && !hci_dev_test_flag(hdev, HCI_LE_SIMULTANEOUS_ROLES)) { hci_conn_del(conn); + hci_conn_put(conn); return -EBUSY; } @@ -6690,6 +6696,12 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data) &own_addr_type); if (err) goto done; + + /* Mark create connection in flight so hci_cancel_connect_sync() can + * cancel it while blocking on the connection complete event. + */ + set_bit(HCI_CONN_CREATE, &conn->flags); + /* Send command LE Extended Create Connection if supported */ if (use_ext_conn(hdev)) { err = hci_le_ext_create_conn_sync(hdev, conn, own_addr_type); @@ -6725,11 +6737,14 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data) conn->conn_timeout, NULL); done: + clear_bit(HCI_CONN_CREATE, &conn->flags); + if (err == -ETIMEDOUT) hci_le_connect_cancel_sync(hdev, conn, 0x00); /* Re-enable advertising after the connection attempt is finished. */ hci_resume_advertising_sync(hdev); + hci_conn_put(conn); return err; } @@ -7004,10 +7019,25 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data) else cp.role_switch = 0x00; - return __hci_cmd_sync_status_sk(hdev, HCI_OP_CREATE_CONN, - sizeof(cp), &cp, - HCI_EV_CONN_COMPLETE, - conn->conn_timeout, NULL); + /* Hold a reference so conn stays valid for the HCI_CONN_CREATE + * clear_bit() below. + */ + hci_conn_get(conn); + + /* Mark create connection in flight so hci_cancel_connect_sync() can + * cancel it while blocking on the connection complete event. + */ + set_bit(HCI_CONN_CREATE, &conn->flags); + + err = __hci_cmd_sync_status_sk(hdev, HCI_OP_CREATE_CONN, + sizeof(cp), &cp, + HCI_EV_CONN_COMPLETE, + conn->conn_timeout, NULL); + + clear_bit(HCI_CONN_CREATE, &conn->flags); + hci_conn_put(conn); + + return err; } int hci_connect_acl_sync(struct hci_dev *hdev, struct hci_conn *conn) @@ -7059,22 +7089,97 @@ int hci_connect_le_sync(struct hci_dev *hdev, struct hci_conn *conn) return (err == -EEXIST) ? 0 : err; } -int hci_cancel_connect_sync(struct hci_dev *hdev, struct hci_conn *conn) +static int hci_acl_cancel_create_conn_sync(struct hci_dev *hdev, + struct hci_conn *conn) { - if (conn->state != BT_OPEN) - return -EINVAL; + struct hci_cmd_sync_work_entry *entry; + int err = -EBUSY; - switch (conn->type) { - case ACL_LINK: - return !hci_cmd_sync_dequeue_once(hdev, - hci_acl_create_conn_sync, - conn, NULL); - case LE_LINK: - return !hci_cmd_sync_dequeue_once(hdev, hci_le_create_conn_sync, - conn, create_le_conn_complete); + /* cmd_sync_work_lock makes the HCI_CONN_CREATE test and the cancel + * atomic against the worker, which takes this lock to dequeue every + * entry: while it is held no other command can become pending, so + * hci_cmd_sync_cancel() cannot cancel an unrelated command. + */ + mutex_lock(&hdev->cmd_sync_work_lock); + + /* In flight: this connection owns the pending request, cancel it. */ + if (test_bit(HCI_CONN_CREATE, &conn->flags)) { + hci_cmd_sync_cancel(hdev, ECANCELED); + goto unlock; } - return -ENOENT; + /* Still queued: a successful dequeue means it never started, so there + * is nothing to disconnect. + */ + entry = _hci_cmd_sync_lookup_entry(hdev, hci_acl_create_conn_sync, conn, + NULL); + if (entry) { + _hci_cmd_sync_cancel_entry(hdev, entry, -ECANCELED); + err = 0; + } + +unlock: + mutex_unlock(&hdev->cmd_sync_work_lock); + return err; +} + +static int hci_le_cancel_create_conn_sync(struct hci_dev *hdev, + struct hci_conn *conn) +{ + struct hci_cmd_sync_work_entry *entry; + int err = -EBUSY; + + /* cmd_sync_work_lock keeps the HCI_CONN_CREATE test and the cancel + * atomic against the cmd_sync worker. + */ + mutex_lock(&hdev->cmd_sync_work_lock); + + if (test_bit(HCI_CONN_CREATE, &conn->flags)) { + hci_cmd_sync_cancel(hdev, ECANCELED); + goto unlock; + } + + entry = _hci_cmd_sync_lookup_entry(hdev, hci_le_create_conn_sync, conn, + create_le_conn_complete); + if (entry) { + _hci_cmd_sync_cancel_entry(hdev, entry, -ECANCELED); + err = 0; + } + +unlock: + mutex_unlock(&hdev->cmd_sync_work_lock); + return err; +} + +static int hci_cis_cancel_create_conn_sync(struct hci_dev *hdev, + struct hci_conn *conn) +{ + /* LE Create CIS is shared by the whole CIG and cannot be dequeued + * per-connection, so only an in-flight command can be cancelled. + * cmd_sync_work_lock keeps the test and the cancel atomic against the + * cmd_sync worker. + */ + mutex_lock(&hdev->cmd_sync_work_lock); + + if (test_bit(HCI_CONN_CREATE_CIS, &conn->flags)) + hci_cmd_sync_cancel(hdev, ECANCELED); + + mutex_unlock(&hdev->cmd_sync_work_lock); + return -EBUSY; +} + +int hci_cancel_connect_sync(struct hci_dev *hdev, struct hci_conn *conn) +{ + switch (conn->type) { + case ACL_LINK: + return hci_acl_cancel_create_conn_sync(hdev, conn); + case LE_LINK: + return hci_le_cancel_create_conn_sync(hdev, conn); + case CIS_LINK: + return hci_cis_cancel_create_conn_sync(hdev, conn); + default: + return -ENOENT; + } } int hci_le_conn_update_sync(struct hci_dev *hdev, struct hci_conn *conn, From 352a59dc1f4a41314b6f827c17e16af7ca88271a Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Wed, 24 Jun 2026 00:12:29 +0800 Subject: [PATCH 0598/1054] Bluetooth: 6lowpan: avoid untracked enable work lowpan_enable_set() allocates a temporary work item and schedules do_enable_set() on system_wq, then returns to debugfs. The debugfs active operation has ended at that point, but the worker still executes module text and manipulates enable_6lowpan and listen_chan. bt_6lowpan_exit() removes the debugfs files and immediately closes and puts listen_chan. It has no pointer to the queued work item, so it cannot cancel or flush it before tearing down the state that the worker uses. The buggy scenario involves two paths, with each column showing the order within that path: debugfs enable write module exit 1. lowpan_enable_set() allocates 1. bt_6lowpan_exit() removes set_enable work the debugfs file 2. schedule_work() queues 2. bt_6lowpan_exit() closes do_enable_set() and puts listen_chan 3. the write operation returns 3. module teardown can continue 4. do_enable_set() later runs against stale state Run the enable state transition synchronously in lowpan_enable_set() instead. The simple debugfs setter can sleep, and this file already handles the 6LoWPAN control write synchronously under the same set_lock. Once the setter returns, debugfs removal covers the whole operation and exit can no longer race with an untracked work item. Validation reproduced this kernel report: BUG: KASAN: slab-use-after-free in do_enable_set+0x113/0x2e0 Workqueue: events do_enable_set [bluetooth_6lowpan] The buggy address belongs to the object at ffff888109cb8000 Fixes: 90305829635d ("Bluetooth: 6lowpan: Converting rwlocks to use RCU") Assisted-by: Codex:gpt-5.5 Signed-off-by: Cen Zhang Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/6lowpan.c | 27 ++++----------------------- 1 file changed, 4 insertions(+), 23 deletions(-) diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index 962e0e885105..c4b0a4048be2 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -1081,23 +1081,15 @@ static void disconnect_all_peers(void) } while (nchans); } -struct set_enable { - struct work_struct work; - bool flag; -}; - -static void do_enable_set(struct work_struct *work) +static void do_enable_set(bool flag) { - struct set_enable *set_enable = container_of(work, - struct set_enable, work); - - if (!set_enable->flag || enable_6lowpan != set_enable->flag) + if (!flag || enable_6lowpan != flag) /* Disconnect existing connections if 6lowpan is * disabled */ disconnect_all_peers(); - enable_6lowpan = set_enable->flag; + enable_6lowpan = flag; mutex_lock(&set_lock); if (listen_chan) { @@ -1109,22 +1101,11 @@ static void do_enable_set(struct work_struct *work) listen_chan = bt_6lowpan_listen(); mutex_unlock(&set_lock); - - kfree(set_enable); } static int lowpan_enable_set(void *data, u64 val) { - struct set_enable *set_enable; - - set_enable = kzalloc_obj(*set_enable); - if (!set_enable) - return -ENOMEM; - - set_enable->flag = !!val; - INIT_WORK(&set_enable->work, do_enable_set); - - schedule_work(&set_enable->work); + do_enable_set(!!val); return 0; } From 518aa9505fa10ea5662349e5d2efd8c9e32a820b Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Wed, 24 Jun 2026 00:12:59 +0800 Subject: [PATCH 0599/1054] Bluetooth: 6lowpan: hold L2CAP conn across debugfs control get_l2cap_conn() looks up an LE hci_conn under hdev protection, but then drops that protection before reading hcon->l2cap_data and before lowpan_control_write() later dereferences conn->hcon. A disconnect or device close can tear down the same L2CAP connection in that window. The buggy scenario involves two paths, with each column showing the order within that path: 6LoWPAN control write: HCI disconnect/device close: 1. get_l2cap_conn() finds hcon 1. hci_disconn_cfm() dispatches and hcon->l2cap_data. the L2CAP disconnect callback. 2. get_l2cap_conn() drops hdev 2. l2cap_conn_del() clears protection and returns conn. hcon->l2cap_data and drops the L2CAP connection reference. 3. lowpan_control_write() reads 3. hci_conn_del() removes and drops conn->hcon. the HCI connection. Take a reference to the L2CAP connection with l2cap_conn_hold_unless_zero() while hdev is still locked, and drop that reference after the debugfs command's last use of conn. This mirrors the existing L2CAP ACL receive-side handoff and keeps the connection dereferenceable after leaving hdev protection. Export the existing helper so the bluetooth_6lowpan module can use the same lifetime primitive. Validation reproduced this kernel report: BUG: KASAN: slab-use-after-free in lowpan_control_write+0x374/0x520 The buggy address belongs to the object at ffff888111b9d000 which belongs to the cache kmalloc-1k of size 1024 The buggy address is located 0 bytes inside of freed 1024-byte region [ffff888111b9d000, ffff888111b9d400) Read of size 8 Call trace: dump_stack_lvl+0x66/0xa0 print_report+0xce/0x5f0 lowpan_control_write+0x374/0x520 (net/bluetooth/6lowpan.c:1131) srso_alias_return_thunk+0x5/0xfbef5 __virt_addr_valid+0x19f/0x330 kasan_report+0xe0/0x110 __debugfs_file_get+0xf7/0x400 full_proxy_write+0x9e/0xd0 vfs_write+0x1b0/0x810 ksys_write+0xd2/0x170 dnotify_flush+0x32/0x220 do_syscall_64+0x115/0x6a0 (arch/x86/entry/syscall_64.c:87) entry_SYSCALL_64_after_hwframe+0x77/0x7f Allocated by task stack: kasan_save_stack+0x33/0x60 kasan_save_track+0x17/0x60 __kasan_kmalloc+0xaa/0xb0 l2cap_conn_add+0x45/0x520 l2cap_chan_connect+0xac6/0xd90 l2cap_sock_connect+0x216/0x350 __sys_connect+0x101/0x130 __x64_sys_connect+0x40/0x50 do_syscall_64+0x115/0x6a0 (arch/x86/entry/syscall_64.c:87) entry_SYSCALL_64_after_hwframe+0x77/0x7f Freed by task stack: kasan_save_stack+0x33/0x60 kasan_save_track+0x17/0x60 kasan_save_free_info+0x3b/0x60 __kasan_slab_free+0x5f/0x80 kfree+0x313/0x590 hci_conn_hash_flush+0xc0/0x140 hci_dev_close_sync+0x41a/0xb00 hci_dev_close+0x12f/0x160 hci_sock_ioctl+0x157/0x570 sock_do_ioctl+0xf7/0x210 sock_ioctl+0x32f/0x490 __x64_sys_ioctl+0xc7/0x110 do_syscall_64+0x115/0x6a0 (arch/x86/entry/syscall_64.c:87) entry_SYSCALL_64_after_hwframe+0x77/0x7f kasan_record_aux_stack+0xa7/0xc0 insert_work+0x32/0x100 __queue_work+0x262/0xa60 queue_work_on+0xad/0xb0 l2cap_connect_cfm+0x4ef/0x670 hci_le_remote_feat_complete_evt+0x247/0x430 hci_event_packet+0x360/0x6f0 hci_rx_work+0x2ae/0x7a0 process_one_work+0x4fd/0xbc0 worker_thread+0x2d8/0x570 kthread+0x1ad/0x1f0 ret_from_fork+0x3c9/0x540 ret_from_fork_asm+0x1a/0x30 Fixes: 6b8d4a6a0314 ("Bluetooth: 6LoWPAN: Use connected oriented channel instead of fixed one") Assisted-by: Codex:gpt-5.5 Signed-off-by: Cen Zhang Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/6lowpan.c | 21 +++++++++++++++------ net/bluetooth/l2cap_core.c | 1 + 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index c4b0a4048be2..e7be18a3af33 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -1017,16 +1017,19 @@ static int get_l2cap_conn(char *buf, bdaddr_t *addr, u8 *addr_type, hci_dev_lock(hdev); hcon = hci_conn_hash_lookup_le(hdev, addr, le_addr_type); - hci_dev_unlock(hdev); - hci_dev_put(hdev); - - if (!hcon) + if (!hcon) { + hci_dev_unlock(hdev); + hci_dev_put(hdev); return -ENOENT; + } - *conn = (struct l2cap_conn *)hcon->l2cap_data; + *conn = l2cap_conn_hold_unless_zero(hcon->l2cap_data); BT_DBG("conn %p dst %pMR type %u", *conn, &hcon->dst, hcon->dst_type); + hci_dev_unlock(hdev); + hci_dev_put(hdev); + return 0; } @@ -1154,18 +1157,22 @@ static ssize_t lowpan_control_write(struct file *fp, if (conn) { struct lowpan_peer *peer; - if (!is_bt_6lowpan(conn->hcon)) + if (!is_bt_6lowpan(conn->hcon)) { + l2cap_conn_put(conn); return -EINVAL; + } peer = lookup_peer(conn); if (peer) { BT_DBG("6LoWPAN connection already exists"); + l2cap_conn_put(conn); return -EALREADY; } BT_DBG("conn %p dst %pMR type %d user %u", conn, &conn->hcon->dst, conn->hcon->dst_type, addr_type); + l2cap_conn_put(conn); } ret = bt_6lowpan_connect(&addr, addr_type); @@ -1181,6 +1188,8 @@ static ssize_t lowpan_control_write(struct file *fp, return ret; ret = bt_6lowpan_disconnect(conn, addr_type); + if (conn) + l2cap_conn_put(conn); if (ret < 0) return ret; diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index a1d249f42be7..4ee3b9e30c65 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -7716,6 +7716,7 @@ struct l2cap_conn *l2cap_conn_hold_unless_zero(struct l2cap_conn *c) return c; } +EXPORT_SYMBOL(l2cap_conn_hold_unless_zero); int l2cap_recv_acldata(struct hci_dev *hdev, u16 handle, struct sk_buff *skb, u16 flags) From 384a4b2fef9ffe5e270ee5558975c0504881c5fb Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Wed, 24 Jun 2026 00:13:28 +0800 Subject: [PATCH 0600/1054] Bluetooth: MGMT: Fix adv monitor add failure cleanup hci_add_adv_monitor() publishes a new adv_monitor in hdev->adv_monitors_idr before the powered MSFT setup step. The MSFT offload add path can then fail either locally before the controller add command completes, or in the MSFT add callback. In the current queued management add flow, hci_cmd_sync_work() still invokes mgmt_add_adv_patterns_monitor_complete() with the original pending command after msft_add_monitor_pattern() returns. The buggy scenario involves two paths, with each column showing the order within that path: MSFT add handling MGMT completion 1. insert monitor and handle 1. receive sync error 2. send MSFT add command 2. call add-monitor completion 3. callback sees bad response 3. load cmd->user_data 4. callback frees monitor 4. read monitor->handle Local MSFT setup failures have the other half of the same ownership bug: they return an error after the IDR insertion, but no later code removes the failed monitor from the IDR. Keep ownership with the pending management command until its completion. For normal management adds, the MSFT add callback now records successful controller state and returns errors to its caller. The management completion frees the monitor on non-success after copying the response handle, while resume/reregister callback-error cleanup remains in the MSFT callback. The success path keeps the existing bookkeeping. Validation reproduced this kernel report: BUG: KASAN: slab-use-after-free in mgmt_add_adv_patterns_monitor_complete+0xfb/0x260 [bluetooth] Call Trace: dump_stack_lvl+0x66/0xa0 print_report+0xce/0x5f0 ? mgmt_add_adv_patterns_monitor_complete+0xfb/0x260 [bluetooth] ? srso_alias_return_thunk+0x5/0xfbef5 ? __virt_addr_valid+0x19f/0x330 ? mgmt_add_adv_patterns_monitor_complete+0xfb/0x260 [bluetooth] kasan_report+0xe0/0x110 ? mgmt_add_adv_patterns_monitor_complete+0xfb/0x260 [bluetooth] mgmt_add_adv_patterns_monitor_complete+0xfb/0x260 [bluetooth] ? srso_alias_return_thunk+0x5/0xfbef5 ? 0xffffffffc00d00da ? __pfx_mgmt_add_adv_patterns_monitor_complete+0x10/0x10 [bluetooth] ? __pfx_mgmt_add_adv_patterns_monitor_complete+0x10/0x10 [bluetooth] ? hci_cmd_sync_work+0x1ab/0x210 [bluetooth] hci_cmd_sync_work+0x1c0/0x210 [bluetooth] ? __pfx_mgmt_add_adv_patterns_monitor_complete+0x10/0x10 [bluetooth] process_one_work+0x4fd/0xbc0 ? __pfx_process_one_work+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? srso_alias_return_thunk+0x5/0xfbef5 ? __list_add_valid_or_report+0x37/0xf0 ? __pfx_hci_cmd_sync_work+0x10/0x10 [bluetooth] ? srso_alias_return_thunk+0x5/0xfbef5 worker_thread+0x2d8/0x570 ? __pfx_worker_thread+0x10/0x10 kthread+0x1ad/0x1f0 ? __pfx_kthread+0x10/0x10 ret_from_fork+0x3c9/0x540 ? __pfx_ret_from_fork+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? __switch_to+0x2e9/0x730 ? __pfx_kthread+0x10/0x10 ret_from_fork_asm+0x1a/0x30 Allocated by task 471 on cpu 3 at 285.205389s: kasan_save_stack+0x33/0x60 kasan_save_track+0x17/0x60 __kasan_kmalloc+0xaa/0xb0 add_adv_patterns_monitor_rssi+0xd5/0x230 [bluetooth] hci_sock_sendmsg+0x96b/0xf80 [bluetooth] __sys_sendto+0x2bc/0x2d0 __x64_sys_sendto+0x76/0x90 do_syscall_64+0x115/0x6a0 entry_SYSCALL_64_after_hwframe+0x77/0x7f Freed by task 454 on cpu 2 at 285.217112s: kasan_save_stack+0x33/0x60 kasan_save_track+0x17/0x60 kasan_save_free_info+0x3b/0x60 __kasan_slab_free+0x5f/0x80 kfree+0x313/0x590 msft_add_monitor_sync+0x54a/0x570 [bluetooth] hci_add_adv_monitor+0x133/0x180 [bluetooth] hci_cmd_sync_work+0x187/0x210 [bluetooth] process_one_work+0x4fd/0xbc0 worker_thread+0x2d8/0x570 kthread+0x1ad/0x1f0 ret_from_fork+0x3c9/0x540 ret_from_fork_asm+0x1a/0x30 Fixes: a2a4dedf88ab ("Bluetooth: advmon offload MSFT add monitor") Assisted-by: Codex:gpt-5.5 Signed-off-by: Cen Zhang Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/mgmt.c | 2 ++ net/bluetooth/msft.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index dc55763f9e58..733a4b70e10c 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -5375,6 +5375,8 @@ static void mgmt_add_adv_patterns_monitor_complete(struct hci_dev *hdev, if (monitor->state == ADV_MONITOR_STATE_NOT_REGISTERED) monitor->state = ADV_MONITOR_STATE_REGISTERED; hci_update_passive_scan(hdev); + } else { + hci_free_adv_monitor(hdev, monitor); } mgmt_cmd_complete(cmd->sk, cmd->hdev->id, cmd->opcode, diff --git a/net/bluetooth/msft.c b/net/bluetooth/msft.c index 2f008167cbaa..d7badce8746c 100644 --- a/net/bluetooth/msft.c +++ b/net/bluetooth/msft.c @@ -291,7 +291,7 @@ static int msft_le_monitor_advertisement_cb(struct hci_dev *hdev, u16 opcode, monitor->state = ADV_MONITOR_STATE_OFFLOADED; unlock: - if (status) + if (status && msft->resuming) hci_free_adv_monitor(hdev, monitor); hci_dev_unlock(hdev); From 0f8a5dcc66648b6e1458a9f3ba4c5a0463a228fc Mon Sep 17 00:00:00 2001 From: Sungwoo Kim Date: Wed, 24 Jun 2026 17:33:04 -0400 Subject: [PATCH 0601/1054] Bluetooth: sco: Fix a race condition in sco_sock_timeout() sco_sock_timeout() runs asynchronously and lock_sock(sk). If the socket is closing while the timer is running, it holds the same lock (lock_sock(sk)) twice, leading to a deadlock. CPU 0 CPU 1 ==================== ====================== sco_sock_close() sco_sock_timeout() lock_sock(sk) // <-- LOCK __sco_sock_close() sco_chan_del() sco_conn_put() sco_conn_free() disable_delayed_work_sync() lock(sk) // <-- SAME LOCK Fix this by moving disable_delayed_work_sync() outside of lock_sock(sk), ensuring that no lock_sock(sk) is held before sco_sock_timeout(). Lockdep splat: WARNING: possible circular locking dependency detected 6.13.0-rc4 #7 Not tainted syz-executor292/9514 is trying to acquire lock: ffff8881115d5070 ((work_completion)(&(&conn->timeout_work)->work)){+.+.}-{0:0}, at: rcu_lock_acquire sect/v6.13-rc4/./include/linux/rcupdate.h:337 [inline] ffff8881115d5070 ((work_completion)(&(&conn->timeout_work)->work)){+.+.}-{0:0}, at: rcu_read_lock sect/v6.13-rc4/./include/linux/rcupdate.h:849 [inline] ffff8881115d5070 ((work_completion)(&(&conn->timeout_work)->work)){+.+.}-{0:0}, at: start_flush_work sect/v6.13-rc4/kernel/workqueue.c:4137 [inline] ffff8881115d5070 ((work_completion)(&(&conn->timeout_work)->work)){+.+.}-{0:0}, at: __flush_work+0xd1/0xc40 sect/v6.13-rc4/kernel/workqueue.c:4195 but task is already holding lock: ffff88807db3a258 (sk_lock-AF_BLUETOOTH-BTPROTO_SCO){+.+.}-{0:0}, at: lock_sock sect/v6.13-rc4/./include/net/sock.h:1623 [inline] ffff88807db3a258 (sk_lock-AF_BLUETOOTH-BTPROTO_SCO){+.+.}-{0:0}, at: sco_sock_close+0x25/0x100 sect/v6.13-rc4/net/bluetooth/sco.c:524 which lock already depends on the new lock. the existing dependency chain (in reverse order) is: -> #1 (sk_lock-AF_BLUETOOTH-BTPROTO_SCO){+.+.}-{0:0}: lock_acquire+0x1c4/0x520 sect/v6.13-rc4/kernel/locking/lockdep.c:5849 lock_sock_nested+0x48/0x130 sect/v6.13-rc4/net/core/sock.c:3622 lock_sock sect/v6.13-rc4/./include/net/sock.h:1623 [inline] sco_sock_timeout+0xbe/0x270 sect/v6.13-rc4/net/bluetooth/sco.c:158 process_one_work sect/v6.13-rc4/kernel/workqueue.c:3229 [inline] process_scheduled_works+0xa99/0x18f0 sect/v6.13-rc4/kernel/workqueue.c:3310 worker_thread+0x8a9/0xd80 sect/v6.13-rc4/kernel/workqueue.c:3391 kthread+0x2c6/0x360 sect/v6.13-rc4/kernel/kthread.c:389 ret_from_fork+0x4e/0x80 sect/v6.13-rc4/arch/x86/kernel/process.c:147 ret_from_fork_asm+0x1a/0x30 sect/v6.13-rc4/arch/x86/entry/entry_64.S:244 -> #0 ((work_completion)(&(&conn->timeout_work)->work)){+.+.}-{0:0}: check_prev_add sect/v6.13-rc4/kernel/locking/lockdep.c:3161 [inline] check_prevs_add sect/v6.13-rc4/kernel/locking/lockdep.c:3280 [inline] validate_chain+0x1888/0x5760 sect/v6.13-rc4/kernel/locking/lockdep.c:3904 __lock_acquire+0x13b4/0x2120 sect/v6.13-rc4/kernel/locking/lockdep.c:5226 lock_acquire+0x1c4/0x520 sect/v6.13-rc4/kernel/locking/lockdep.c:5849 touch_work_lockdep_map sect/v6.13-rc4/kernel/workqueue.c:3909 [inline] start_flush_work sect/v6.13-rc4/kernel/workqueue.c:4163 [inline] __flush_work+0x70f/0xc40 sect/v6.13-rc4/kernel/workqueue.c:4195 __cancel_work_sync sect/v6.13-rc4/kernel/workqueue.c:4351 [inline] disable_delayed_work_sync+0xbb/0xf0 sect/v6.13-rc4/kernel/workqueue.c:4514 sco_conn_free sect/v6.13-rc4/net/bluetooth/sco.c:95 [inline] kref_put sect/v6.13-rc4/./include/linux/kref.h:65 [inline] sco_conn_put+0x18f/0x270 sect/v6.13-rc4/net/bluetooth/sco.c:107 sco_chan_del+0xe2/0x210 sect/v6.13-rc4/net/bluetooth/sco.c:236 sco_sock_close+0x8f/0x100 sect/v6.13-rc4/net/bluetooth/sco.c:526 sco_sock_release+0x62/0x2d0 sect/v6.13-rc4/net/bluetooth/sco.c:1300 __sock_release+0xe1/0x2d0 sect/v6.13-rc4/net/socket.c:640 sock_close+0x1c/0x30 sect/v6.13-rc4/net/socket.c:1408 __fput+0x2bd/0xa80 sect/v6.13-rc4/fs/file_table.c:450 __fput_sync+0x15e/0x1c0 sect/v6.13-rc4/fs/file_table.c:535 __do_sys_close sect/v6.13-rc4/fs/open.c:1554 [inline] __se_sys_close sect/v6.13-rc4/fs/open.c:1539 [inline] __x64_sys_close+0x93/0x120 sect/v6.13-rc4/fs/open.c:1539 do_syscall_x64 sect/v6.13-rc4/arch/x86/entry/common.c:52 [inline] do_syscall_64+0xee/0x210 sect/v6.13-rc4/arch/x86/entry/common.c:83 entry_SYSCALL_64_after_hwframe+0x77/0x7f Fixes: e6720779ae61 ("Bluetooth: SCO: Use kref to track lifetime of sco_conn") Acked-by: Dave Tian Signed-off-by: Sungwoo Kim Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/sco.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index fcc597be5bbd..c05f79b7aa31 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -570,10 +570,23 @@ static void __sco_sock_close(struct sock *sk) /* Must be called on unlocked socket. */ static void sco_sock_close(struct sock *sk) { + struct sco_conn *conn; + + lock_sock(sk); + conn = sco_pi(sk)->conn; + if (conn) + sco_conn_hold(conn); + release_sock(sk); + + if (conn) + disable_delayed_work_sync(&conn->timeout_work); + lock_sock(sk); - sco_sock_clear_timer(sk); __sco_sock_close(sk); release_sock(sk); + + if (conn) + sco_conn_put(conn); } static void sco_sock_init(struct sock *sk, struct sock *parent) From bb067a99a0356196c0b89a95721985485ebce5a5 Mon Sep 17 00:00:00 2001 From: Yousef Alhouseen Date: Sun, 28 Jun 2026 02:50:58 +0200 Subject: [PATCH 0602/1054] Bluetooth: bnep: pin L2CAP connection during netdev registration bnep_add_connection() reads the L2CAP connection without holding the channel lock, then passes its HCI device to register_netdev(). Controller teardown can clear and release that connection concurrently, leaving the network device registration path to dereference a freed parent device. Take a reference to the L2CAP connection while holding the channel lock. Retain it until register_netdev() has taken the parent device reference. Fixes: 65f53e9802db ("Bluetooth: Access BNEP session addresses through L2CAP channel") Reported-by: syzbot+fed5dce4553262f3b35c@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=fed5dce4553262f3b35c Cc: stable@vger.kernel.org Signed-off-by: Yousef Alhouseen Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/bnep/core.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c index add9a8f7535d..f7d88c33e23e 100644 --- a/net/bluetooth/bnep/core.c +++ b/net/bluetooth/bnep/core.c @@ -559,14 +559,18 @@ static int bnep_session(void *arg) return 0; } -static struct device *bnep_get_device(struct bnep_session *session) +static struct l2cap_conn *bnep_get_conn(struct bnep_session *session) { - struct l2cap_conn *conn = l2cap_pi(session->sock->sk)->chan->conn; + struct l2cap_chan *chan = l2cap_pi(session->sock->sk)->chan; + struct l2cap_conn *conn; - if (!conn || !conn->hcon) - return NULL; + l2cap_chan_lock(chan); + conn = chan->conn; + if (conn) + l2cap_conn_get(conn); + l2cap_chan_unlock(chan); - return &conn->hcon->dev; + return conn; } static const struct device_type bnep_type = { @@ -578,6 +582,7 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock) u32 valid_flags = BIT(BNEP_SETUP_RESPONSE); struct net_device *dev; struct bnep_session *s, *ss; + struct l2cap_conn *conn = NULL; u8 dst[ETH_ALEN], src[ETH_ALEN]; int err; @@ -637,10 +642,18 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock) bnep_set_default_proto_filter(s); #endif - SET_NETDEV_DEV(dev, bnep_get_device(s)); + conn = bnep_get_conn(s); + if (!conn) { + err = -ENOTCONN; + goto failed; + } + + SET_NETDEV_DEV(dev, &conn->hcon->dev); SET_NETDEV_DEVTYPE(dev, &bnep_type); err = register_netdev(dev); + l2cap_conn_put(conn); + conn = NULL; if (err) goto failed; @@ -662,6 +675,8 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock) return 0; failed: + if (conn) + l2cap_conn_put(conn); up_write(&bnep_session_sem); free_netdev(dev); return err; From 4bd0b274054f2679f28b70222b607bb0afc3ab9a Mon Sep 17 00:00:00 2001 From: Yousef Alhouseen Date: Sun, 28 Jun 2026 02:23:05 +0200 Subject: [PATCH 0603/1054] Bluetooth: fix UAF in bt_accept_dequeue() bt_accept_get() takes a temporary reference before dropping the accept queue lock. bt_accept_dequeue() currently drops that reference before bt_accept_unlink(), leaving only the queue reference. bt_accept_unlink() drops the queue reference. The subsequent sock_hold() therefore accesses freed memory if it was the final reference, as observed by KASAN during listening L2CAP socket cleanup. Retain the temporary queue-walk reference through unlink and hand it to the caller on success. Drop it explicitly on the closed and not-yet-connected paths. Fixes: ab1513597c6c ("Bluetooth: fix UAF in l2cap_sock_cleanup_listen() vs l2cap_conn_del()") Reported-by: syzbot+674ff7e4d7fdfd572afc@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=674ff7e4d7fdfd572afc Cc: stable@vger.kernel.org Signed-off-by: Yousef Alhouseen Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/af_bluetooth.c | 17 +++-------------- net/bluetooth/l2cap_sock.c | 4 ++-- 2 files changed, 5 insertions(+), 16 deletions(-) diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c index bcbc11c9cb15..a2290ffdc2c1 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -305,7 +305,7 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock) restart: for (sk = bt_accept_get(parent, NULL); sk; sk = next) { - /* Prevent early freeing of sk due to unlink and sock_kill */ + /* The reference from bt_accept_get() keeps sk alive. */ lock_sock(sk); /* Check sk has not already been unlinked via @@ -321,13 +321,11 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock) next = bt_accept_get(parent, sk); - /* sk is safely in the parent list so reduce reference count */ - sock_put(sk); - /* FIXME: Is this check still needed */ if (sk->sk_state == BT_CLOSED) { bt_accept_unlink(sk); release_sock(sk); + sock_put(sk); continue; } @@ -337,16 +335,6 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock) if (newsock) sock_graft(sk, newsock); - /* Hand the caller a reference taken while sk is - * still locked. bt_accept_unlink() just dropped - * the accept-queue reference; without this hold a - * concurrent teardown (e.g. l2cap_conn_del() -> - * l2cap_sock_kill()) could free sk between - * release_sock() and the caller using it. Every - * caller drops this with sock_put() when done. - */ - sock_hold(sk); - release_sock(sk); if (next) sock_put(next); @@ -354,6 +342,7 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock) } release_sock(sk); + sock_put(sk); } return NULL; diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 4853f1b33449..de56ca691afa 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -1492,8 +1492,8 @@ static void l2cap_sock_cleanup_listen(struct sock *parent) /* Close not yet accepted channels. * - * bt_accept_dequeue() now returns sk with an extra reference held - * (taken while sk was still locked) so a concurrent l2cap_conn_del() + * bt_accept_dequeue() returns sk with its temporary queue-walk + * reference held, so a concurrent l2cap_conn_del() * -> l2cap_sock_kill() cannot free sk under us. * * cleanup_listen() runs under the parent sk lock, so unlike From 6fef032af0092ed5ccb767239a9ac1bc38c08a40 Mon Sep 17 00:00:00 2001 From: Siwei Zhang Date: Mon, 29 Jun 2026 09:49:58 -0400 Subject: [PATCH 0604/1054] Bluetooth: L2CAP: Fix use-after-free in l2cap_sock_new_connection_cb() l2cap_sock_new_connection_cb() returned l2cap_pi(sk)->chan after release_sock(parent). Once the parent lock is dropped the newly enqueued child socket sk is reachable via the accept queue, so another task can accept and free it before the callback dereferences sk, resulting in a use-after-free. Rework the ->new_connection() op so the core, rather than the callback, owns the child channel's lifetime. The op now receives a pre-allocated new_chan and returns an errno instead of allocating and returning a channel. l2cap_new_connection() allocates the child channel and links it into the conn list via __l2cap_chan_add() before invoking the callback, so the conn-list reference keeps the channel alive once release_sock(parent) exposes the socket to other tasks. Channel configuration that was duplicated in l2cap_sock_init() and the various new_connection callbacks is consolidated into l2cap_chan_set_defaults(), which now inherits from the parent channel when one is supplied. Fixes: 8ffb929098a5 ("Bluetooth: Remove parent socket usage from l2cap_core.c") Cc: stable@kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Siwei Zhang Signed-off-by: Luiz Augusto von Dentz --- include/net/bluetooth/l2cap.h | 10 ++-- net/bluetooth/6lowpan.c | 18 +----- net/bluetooth/l2cap_core.c | 78 ++++++++++++++++++++----- net/bluetooth/l2cap_sock.c | 103 ++++++++++++++++------------------ net/bluetooth/smp.c | 27 ++------- 5 files changed, 127 insertions(+), 109 deletions(-) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 1640cc9bf83a..ef6ce1c20a4f 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -617,7 +617,8 @@ struct l2cap_chan { struct l2cap_ops { char *name; - struct l2cap_chan *(*new_connection) (struct l2cap_chan *chan); + int (*new_connection)(struct l2cap_chan *chan, + struct l2cap_chan *new_chan); int (*recv) (struct l2cap_chan * chan, struct sk_buff *skb); void (*teardown) (struct l2cap_chan *chan, int err); @@ -882,9 +883,10 @@ static inline __u16 __next_seq(struct l2cap_chan *chan, __u16 seq) return (seq + 1) % (chan->tx_win_max + 1); } -static inline struct l2cap_chan *l2cap_chan_no_new_connection(struct l2cap_chan *chan) +static inline int l2cap_chan_no_new_connection(struct l2cap_chan *chan, + struct l2cap_chan *new_chan) { - return NULL; + return -EOPNOTSUPP; } static inline int l2cap_chan_no_recv(struct l2cap_chan *chan, struct sk_buff *skb) @@ -961,7 +963,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len, void l2cap_chan_busy(struct l2cap_chan *chan, int busy); void l2cap_chan_rx_avail(struct l2cap_chan *chan, ssize_t rx_avail); int l2cap_chan_check_security(struct l2cap_chan *chan, bool initiator); -void l2cap_chan_set_defaults(struct l2cap_chan *chan); +void l2cap_chan_set_defaults(struct l2cap_chan *chan, struct l2cap_chan *pchan); int l2cap_ertm_init(struct l2cap_chan *chan); void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan); void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan); diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index e7be18a3af33..d504a363a30f 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -632,7 +632,7 @@ static struct l2cap_chan *chan_create(void) if (!chan) return NULL; - l2cap_chan_set_defaults(chan); + l2cap_chan_set_defaults(chan, NULL); chan->chan_type = L2CAP_CHAN_CONN_ORIENTED; chan->mode = L2CAP_MODE_LE_FLOWCTL; @@ -745,21 +745,6 @@ static inline void chan_ready_cb(struct l2cap_chan *chan) ifup(dev->netdev); } -static inline struct l2cap_chan *chan_new_conn_cb(struct l2cap_chan *pchan) -{ - struct l2cap_chan *chan; - - chan = chan_create(); - if (!chan) - return NULL; - - chan->ops = pchan->ops; - - BT_DBG("chan %p pchan %p", chan, pchan); - - return chan; -} - static void unregister_dev(struct lowpan_btle_dev *dev) { struct hci_dev *hdev = READ_ONCE(dev->hdev); @@ -889,7 +874,6 @@ static long chan_get_sndtimeo_cb(struct l2cap_chan *chan) static const struct l2cap_ops bt_6lowpan_chan_ops = { .name = "L2CAP 6LoWPAN channel", - .new_connection = chan_new_conn_cb, .recv = chan_recv_cb, .close = chan_close_cb, .state_change = chan_state_change_cb, diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 4ee3b9e30c65..519cd9552d86 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -522,7 +522,10 @@ void l2cap_chan_put(struct l2cap_chan *c) } EXPORT_SYMBOL_GPL(l2cap_chan_put); -void l2cap_chan_set_defaults(struct l2cap_chan *chan) +/* Initialise @chan with default values, inheriting from the parent channel + * @pchan when it is given. + */ +void l2cap_chan_set_defaults(struct l2cap_chan *chan, struct l2cap_chan *pchan) { chan->fcs = L2CAP_FCS_CRC16; chan->max_tx = L2CAP_DEFAULT_MAX_TX; @@ -536,6 +539,31 @@ void l2cap_chan_set_defaults(struct l2cap_chan *chan) chan->retrans_timeout = L2CAP_DEFAULT_RETRANS_TO; chan->monitor_timeout = L2CAP_DEFAULT_MONITOR_TO; + if (pchan) { + BT_DBG("chan %p pchan %p", chan, pchan); + + chan->chan_type = pchan->chan_type; + chan->imtu = pchan->imtu; + chan->omtu = pchan->omtu; + chan->mode = pchan->mode; + chan->fcs = pchan->fcs; + chan->max_tx = pchan->max_tx; + chan->tx_win = pchan->tx_win; + chan->tx_win_max = pchan->tx_win_max; + chan->sec_level = pchan->sec_level; + chan->conf_state = pchan->conf_state; + chan->flags = pchan->flags; + chan->tx_credits = pchan->tx_credits; + chan->rx_credits = pchan->rx_credits; + + if (chan->chan_type == L2CAP_CHAN_FIXED) { + chan->scid = pchan->scid; + chan->dcid = pchan->scid; + } + + return; + } + chan->conf_state = 0; set_bit(CONF_NOT_COMPLETE, &chan->conf_state); @@ -4024,6 +4052,38 @@ static inline int l2cap_command_rej(struct l2cap_conn *conn, return 0; } +/* Allocate and initialise a channel for an incoming connection. + * + * The channel inherits its configuration from @pchan and is linked into @conn + * before ->new_connection() runs, so the conn list reference keeps it alive if + * the callback exposes it (e.g. via the socket accept queue) before this + * returns. The l2cap_chan_create() reference is taken over by the subsystem on + * success and dropped here on failure. + */ +static struct l2cap_chan *l2cap_new_connection(struct l2cap_conn *conn, + struct l2cap_chan *pchan) +{ + struct l2cap_chan *chan; + + chan = l2cap_chan_create(); + if (!chan) + return NULL; + + l2cap_chan_set_defaults(chan, pchan); + chan->ops = pchan->ops; + + __l2cap_chan_add(conn, chan); + + if (pchan->ops->new_connection && + pchan->ops->new_connection(pchan, chan) < 0) { + l2cap_chan_del(chan, 0); + l2cap_chan_put(chan); + return NULL; + } + + return chan; +} + static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data, u8 rsp_code) { @@ -4070,7 +4130,7 @@ static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, goto response; } - chan = pchan->ops->new_connection(pchan); + chan = l2cap_new_connection(conn, pchan); if (!chan) goto response; @@ -4088,8 +4148,6 @@ static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, chan->psm = psm; chan->dcid = scid; - __l2cap_chan_add(conn, chan); - dcid = chan->scid; __set_chan_timer(chan, chan->ops->get_sndtimeo(chan)); @@ -4972,7 +5030,7 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn, goto response_unlock; } - chan = pchan->ops->new_connection(pchan); + chan = l2cap_new_connection(conn, pchan); if (!chan) { result = L2CAP_CR_LE_NO_MEM; goto response_unlock; @@ -4987,8 +5045,6 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn, chan->omtu = mtu; chan->remote_mps = mps; - __l2cap_chan_add(conn, chan); - l2cap_le_flowctl_init(chan, __le16_to_cpu(req->credits)); dcid = chan->scid; @@ -5196,7 +5252,7 @@ static inline int l2cap_ecred_conn_req(struct l2cap_conn *conn, continue; } - chan = pchan->ops->new_connection(pchan); + chan = l2cap_new_connection(conn, pchan); if (!chan) { result = L2CAP_CR_LE_NO_MEM; continue; @@ -5211,8 +5267,6 @@ static inline int l2cap_ecred_conn_req(struct l2cap_conn *conn, chan->omtu = mtu; chan->remote_mps = mps; - __l2cap_chan_add(conn, chan); - l2cap_ecred_init(chan, __le16_to_cpu(req->credits)); /* Init response */ @@ -7492,14 +7546,12 @@ static void l2cap_connect_cfm(struct hci_conn *hcon, u8 status) goto next; l2cap_chan_lock(pchan); - chan = pchan->ops->new_connection(pchan); + chan = l2cap_new_connection(conn, pchan); if (chan) { bacpy(&chan->src, &hcon->src); bacpy(&chan->dst, &hcon->dst); chan->src_type = bdaddr_src_type(hcon); chan->dst_type = dst_type; - - __l2cap_chan_add(conn, chan); } l2cap_chan_unlock(pchan); diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index de56ca691afa..4058ff50cc27 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -43,7 +43,8 @@ static struct bt_sock_list l2cap_sk_list = { static const struct proto_ops l2cap_sock_ops; static void l2cap_sock_init(struct sock *sk, struct sock *parent); static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, - int proto, gfp_t prio, int kern); + int proto, gfp_t prio, int kern, + struct l2cap_chan *chan); static void l2cap_sock_cleanup_listen(struct sock *parent); bool l2cap_is_socket(struct socket *sock) @@ -1284,6 +1285,23 @@ static int l2cap_sock_recvmsg(struct socket *sock, struct msghdr *msg, return err; } +/* Release the sock's ref on chan and clear the pointer so that the ref is + * dropped exactly once even if both l2cap_sock_kill() and + * l2cap_sock_destruct() run. Setting chan->data to NULL first stops any other + * task from dereferencing the now-dead sock pointer. + */ +static void l2cap_sock_put_chan(struct sock *sk) +{ + struct l2cap_chan *chan = l2cap_pi(sk)->chan; + + if (!chan) + return; + + chan->data = NULL; + l2cap_pi(sk)->chan = NULL; + l2cap_chan_put(chan); +} + /* Kill socket (only if zapped and orphan) * Must be called on unlocked socket, with l2cap channel lock. */ @@ -1294,13 +1312,9 @@ static void l2cap_sock_kill(struct sock *sk) BT_DBG("sk %p state %s", sk, state_to_string(sk->sk_state)); - /* Sock is dead, so set chan data to NULL, avoid other task use invalid - * sock pointer. - */ - l2cap_pi(sk)->chan->data = NULL; - /* Kill poor orphan */ + l2cap_sock_put_chan(sk); - l2cap_chan_put(l2cap_pi(sk)->chan); + /* Kill poor orphan */ sock_set_flag(sk, SOCK_DEAD); sock_put(sk); } @@ -1543,12 +1557,13 @@ static void l2cap_sock_cleanup_listen(struct sock *parent) } } -static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan) +static int l2cap_sock_new_connection_cb(struct l2cap_chan *chan, + struct l2cap_chan *new_chan) { struct sock *sk, *parent = chan->data; if (!parent) - return NULL; + return -EINVAL; lock_sock(parent); @@ -1556,25 +1571,28 @@ static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan) if (sk_acceptq_is_full(parent)) { BT_DBG("backlog full %d", parent->sk_ack_backlog); release_sock(parent); - return NULL; + return -ENOBUFS; } sk = l2cap_sock_alloc(sock_net(parent), NULL, BTPROTO_L2CAP, - GFP_ATOMIC, 0); + GFP_ATOMIC, 0, new_chan); if (!sk) { release_sock(parent); - return NULL; - } + return -ENOMEM; + } bt_sock_reclassify_lock(sk, BTPROTO_L2CAP); l2cap_sock_init(sk, parent); + /* The conn list reference taken by l2cap_new_connection() keeps new_chan + * alive once release_sock() lets another task free this socket. + */ bt_accept_enqueue(parent, sk, false); release_sock(parent); - return l2cap_pi(sk)->chan; + return 0; } static int l2cap_sock_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb) @@ -1871,10 +1889,7 @@ static void l2cap_sock_destruct(struct sock *sk) BT_DBG("sk %p", sk); - if (l2cap_pi(sk)->chan) { - l2cap_pi(sk)->chan->data = NULL; - l2cap_chan_put(l2cap_pi(sk)->chan); - } + l2cap_sock_put_chan(sk); list_for_each_entry_safe(rx_busy, next, &l2cap_pi(sk)->rx_busy, list) { kfree_skb(rx_busy->skb); @@ -1907,30 +1922,12 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent) BT_DBG("sk %p", sk); if (parent) { - struct l2cap_chan *pchan = l2cap_pi(parent)->chan; - sk->sk_type = parent->sk_type; bt_sk(sk)->flags = bt_sk(parent)->flags; - chan->chan_type = pchan->chan_type; - chan->imtu = pchan->imtu; - chan->omtu = pchan->omtu; - chan->conf_state = pchan->conf_state; - chan->mode = pchan->mode; - chan->fcs = pchan->fcs; - chan->max_tx = pchan->max_tx; - chan->tx_win = pchan->tx_win; - chan->tx_win_max = pchan->tx_win_max; - chan->sec_level = pchan->sec_level; - chan->flags = pchan->flags; - chan->tx_credits = pchan->tx_credits; - chan->rx_credits = pchan->rx_credits; - - if (chan->chan_type == L2CAP_CHAN_FIXED) { - chan->scid = pchan->scid; - chan->dcid = pchan->scid; - } - + /* Channel configuration is inherited from the parent by + * l2cap_new_connection(). + */ security_sk_clone(parent, sk); } else { switch (sk->sk_type) { @@ -1956,7 +1953,7 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent) chan->mode = L2CAP_MODE_BASIC; } - l2cap_chan_set_defaults(chan); + l2cap_chan_set_defaults(chan, NULL); } /* Default config options */ @@ -1975,10 +1972,10 @@ static struct proto l2cap_proto = { }; static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, - int proto, gfp_t prio, int kern) + int proto, gfp_t prio, int kern, + struct l2cap_chan *chan) { struct sock *sk; - struct l2cap_chan *chan; sk = bt_sock_alloc(net, sock, &l2cap_proto, proto, prio, kern); if (!sk) @@ -1989,16 +1986,7 @@ static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, INIT_LIST_HEAD(&l2cap_pi(sk)->rx_busy); - chan = l2cap_chan_create(); - if (!chan) { - sk_free(sk); - if (sock) - sock->sk = NULL; - return NULL; - } - - l2cap_chan_hold(chan); - + /* The sock takes ownership of the caller's reference on chan. */ l2cap_pi(sk)->chan = chan; return sk; @@ -2008,6 +1996,7 @@ static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol, int kern) { struct sock *sk; + struct l2cap_chan *chan; BT_DBG("sock %p", sock); @@ -2022,10 +2011,16 @@ static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol, sock->ops = &l2cap_sock_ops; - sk = l2cap_sock_alloc(net, sock, protocol, GFP_ATOMIC, kern); - if (!sk) + chan = l2cap_chan_create(); + if (!chan) return -ENOMEM; + sk = l2cap_sock_alloc(net, sock, protocol, GFP_ATOMIC, kern, chan); + if (!sk) { + l2cap_chan_put(chan); + return -ENOMEM; + } + l2cap_sock_init(sk, NULL); bt_sock_link(&l2cap_sk_list, sk); return 0; diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 031d3022cb1e..c4470958b0d5 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -3201,34 +3201,19 @@ static const struct l2cap_ops smp_chan_ops = { .get_sndtimeo = l2cap_chan_no_get_sndtimeo, }; -static inline struct l2cap_chan *smp_new_conn_cb(struct l2cap_chan *pchan) +static inline int smp_new_conn_cb(struct l2cap_chan *chan, + struct l2cap_chan *new_chan) { - struct l2cap_chan *chan; - - BT_DBG("pchan %p", pchan); - - chan = l2cap_chan_create(); - if (!chan) - return NULL; - - chan->chan_type = pchan->chan_type; - chan->ops = &smp_chan_ops; - chan->scid = pchan->scid; - chan->dcid = chan->scid; - chan->imtu = pchan->imtu; - chan->omtu = pchan->omtu; - chan->mode = pchan->mode; + new_chan->ops = &smp_chan_ops; /* Other L2CAP channels may request SMP routines in order to * change the security level. This means that the SMP channel * lock must be considered in its own category to avoid lockdep * warnings. */ - atomic_set(&chan->nesting, L2CAP_NESTING_SMP); + atomic_set(&new_chan->nesting, L2CAP_NESTING_SMP); - BT_DBG("created chan %p", chan); - - return chan; + return 0; } static const struct l2cap_ops smp_root_chan_ops = { @@ -3288,7 +3273,7 @@ static struct l2cap_chan *smp_add_cid(struct hci_dev *hdev, u16 cid) l2cap_add_scid(chan, cid); - l2cap_chan_set_defaults(chan); + l2cap_chan_set_defaults(chan, NULL); if (cid == L2CAP_CID_SMP) { u8 bdaddr_type; From 9c36951474d8e1127f4946f39cb874a200f34e9f Mon Sep 17 00:00:00 2001 From: Kiran K Date: Tue, 30 Jun 2026 22:29:19 +0530 Subject: [PATCH 0605/1054] Bluetooth: btintel_pcie: Refactor FLR to use device_reprobe() The FLR branch in btintel_pcie_reset_work() open-coded the entire re-init sequence: btintel_pcie_release_hdev() (hci_unregister_dev + hci_free_dev), pci_try_reset_function(), enable_interrupts / config_msix / enable_bt / reset_ia / start_rx, then btintel_pcie_setup_hdev() (hci_alloc_dev_priv + hci_register_dev). Every probe() init step had to be kept in sync with this second copy in the reset path, and any failure mid-sequence left state to unwind by hand. The PLDR path already delegates teardown and re-init to the PCI core via device_reprobe(): .remove() destroys data through devres and unregisters hdev, then .probe() rebuilds everything from scratch. Apply the same model to FLR. Introduce btintel_pcie_perform_flr() mirroring perform_pldr(). It runs pci_try_reset_function() (required to avoid the device_lock ABBA against btintel_pcie_remove(), which calls disable_work_sync(&reset_work) while holding device_lock) followed by device_reprobe(). On success, data is destroyed and a fresh probe re-INIT_WORKs coredump_work with disable count 0, so enable_work() must not be called; on failure, data is still alive and the caller balances the earlier disable_work_sync(). The contract is documented on the helper and reiterated at the reset_work() call site. reset_work() shrinks to interrupt/worker drain, dispatch on reset_type, and the single asymmetry between the two paths. The out_enable label, the manual unregister/register pair, and the forward declaration of btintel_pcie_setup_hdev() are dropped. No intended functional change; FLR and PLDR now share one teardown contract. Fixes: 256ab9520d15 ("Bluetooth: btintel_pcie: Support Function level reset") Assisted-by: GitHub-Copilot:claude-4.7-opus Signed-off-by: Kiran K Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btintel_pcie.c | 102 ++++++++++++++++--------------- 1 file changed, 52 insertions(+), 50 deletions(-) diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c index 9e39327dc1fe..2b7231be5973 100644 --- a/drivers/bluetooth/btintel_pcie.c +++ b/drivers/bluetooth/btintel_pcie.c @@ -2127,6 +2127,9 @@ static int btintel_pcie_send_frame(struct hci_dev *hdev, if (test_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags)) return -ENODEV; + if (test_bit(BTINTEL_PCIE_RECOVERY_IN_PROGRESS, &data->flags)) + return -ENODEV; + /* Due to the fw limitation, the type header of the packet should be * 4 bytes unlike 1 byte for UART. In UART, the firmware can read * the first byte to get the packet type and redirect the rest of data @@ -2485,7 +2488,6 @@ static void btintel_pcie_inc_recovery_count(struct pci_dev *pdev, } } -static int btintel_pcie_setup_hdev(struct btintel_pcie_data *data); static void btintel_pcie_reset(struct hci_dev *hdev); static int btintel_pcie_acpi_reset_method(struct btintel_pcie_data *data) @@ -2596,12 +2598,45 @@ static void btintel_pcie_perform_pldr(struct btintel_pcie_data *data) } } +/* + * Issue a Function Level Reset and hand teardown/re-init off to the PCI + * core via device_reprobe(), mirroring the PLDR path's contract. + * + * Caller must hold pci_lock_rescan_remove() and must have already + * disabled interrupts and drained both rx_work and coredump_work. + */ +static int btintel_pcie_perform_flr(struct btintel_pcie_data *data) +{ + struct pci_dev *pdev = data->pdev; + int err; + + /* pci_try_reset_function() avoids the device_lock ABBA against + * btintel_pcie_remove(): .remove() runs with device_lock held and + * then waits for this work via disable_work_sync(); the blocking + * pci_reset_function() would deadlock by trying to re-acquire + * device_lock here. + */ + err = pci_try_reset_function(pdev); + if (err) { + BT_ERR("Failed resetting the pcie device (%d)", err); + return err; + } + + /* device_reprobe() always detaches the driver first (running + * .remove(), which frees 'data'); any re-probe failure leaves the + * device unbound but 'data' is already gone, so just log it. + */ + if (device_reprobe(&pdev->dev)) + BT_ERR("BT reprobe failed for BDF:%s", pci_name(pdev)); + + return 0; +} + static void btintel_pcie_reset_work(struct work_struct *wk) { struct btintel_pcie_data *data = container_of(wk, struct btintel_pcie_data, reset_work); struct pci_dev *pdev = data->pdev; - int err; pci_lock_rescan_remove(); @@ -2621,60 +2656,27 @@ static void btintel_pcie_reset_work(struct work_struct *wk) disable_work_sync(&data->coredump_work); bt_dev_dbg(data->hdev, "Release bluetooth interface"); + + /* Both reset paths follow the same contract: on success they + * destroy 'data' via device_reprobe() (a fresh probe re-INIT_WORKs + * the coredump_work with disable count 0), so enable_work() must + * NOT be called on the success path. Only the FLR path can fail + * with 'data' still alive, in which case we balance the + * disable_work_sync() above so a later successful reset is not + * permanently blocked. + * + * pci_lock_rescan_remove() (held above) serializes against PCI + * device addition/removal (hotplug), so no device can be added to + * or removed from the bus list while this code runs. + */ if (data->reset_type == BTINTEL_PCIE_IOSF_PRR_PLDR) { - /* This function holds pci_lock_rescan_remove(), which acquires - * pci_rescan_remove_lock. This mutex serializes against PCI device - * addition/removal (hotplug), so no device can be added to or - * removed from the bus list while this code runs. - * - * device_reprobe() inside btintel_pcie_perform_pldr() destroys - * 'data' via .remove(); a fresh probe re-INIT_WORKs the - * coredump_work with disable count 0, so we must not call - * enable_work() on this path. - */ btintel_pcie_perform_pldr(data); goto out; } - btintel_pcie_release_hdev(data); - /* Use pci_try_reset_function() rather than pci_reset_function() to - * avoid an ABBA deadlock against btintel_pcie_remove(): the PCI core - * calls .remove() with device_lock held, and remove() then waits for - * this work via cancel_work_sync(); pci_reset_function() would in - * turn try to acquire the same device_lock, deadlocking both paths. - */ - err = pci_try_reset_function(pdev); - if (err) { - BT_ERR("Failed resetting the pcie device (%d)", err); - goto out_enable; - } + if (btintel_pcie_perform_flr(data)) + enable_work(&data->coredump_work); - btintel_pcie_enable_interrupts(data); - btintel_pcie_config_msix(data); - - err = btintel_pcie_enable_bt(data); - if (err) { - BT_ERR("Failed to enable bluetooth hardware after reset (%d)", - err); - goto out_enable; - } - - btintel_pcie_reset_ia(data); - btintel_pcie_start_rx(data); - data->flags = 0; - - err = btintel_pcie_setup_hdev(data); - if (err) { - BT_ERR("Failed registering hdev (%d)", err); - goto out_enable; - } - -out_enable: - /* Balance disable_work_sync() above on every exit. Leaving the - * counter incremented on a failed reset would permanently disable - * coredump_work even after a later successful reset. - */ - enable_work(&data->coredump_work); out: pci_dev_put(pdev); pci_unlock_rescan_remove(); From e054c1a6ae7310d2815778fddb87da616e11c255 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Wed, 1 Jul 2026 18:46:38 +0300 Subject: [PATCH 0606/1054] Bluetooth: ISO: fix malformed ISO_END/CONT handling Core specification (Part C vol 4 sec 5.4.5) does not exclude empty ISO_CONT, ISO_END packets. We currently reject them if they are last. If controller sends malformed sequence ISO_START -> rx_len = 4, ISO_CONT skb->len 4, ISO_START that ends payload in ISO_CONT, we leak conn->rx_skb. If controller sends too long ISO_END, we panic on skb_put. If controller sends too short ISO_END we accept it. Fix by marking unfinished ISO_START via conn->rx_skb != NULL. Check skb->len properly before skb_put. Combine the ISO_CONT/END code paths as they require the same initial checks. Reject too short ISO_END packets. Fixes: 84c24fb151fc ("Bluetooth: ISO: drop ISO_END frames received without prior ISO_START") Fixes: ccf74f2390d6 ("Bluetooth: Add BTPROTO_ISO socket type") Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/iso.c | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index cd7c7c9ea4fc..2e95a153912c 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -2540,7 +2540,7 @@ int iso_recv(struct hci_dev *hdev, u16 handle, struct sk_buff *skb, u16 flags) switch (pb) { case ISO_START: case ISO_SINGLE: - if (conn->rx_len) { + if (conn->rx_skb || conn->rx_len) { BT_ERR("Unexpected start frame (len %d)", skb->len); kfree_skb(conn->rx_skb); conn->rx_skb = NULL; @@ -2621,12 +2621,14 @@ int iso_recv(struct hci_dev *hdev, u16 handle, struct sk_buff *skb, u16 flags) break; case ISO_CONT: - BT_DBG("Cont: frag len %d (expecting %d)", skb->len, + case ISO_END: + BT_DBG("%s: frag len %d (expecting %d)", + (pb == ISO_END) ? "End" : "Cont", skb->len, conn->rx_len); - if (!conn->rx_len) { - BT_ERR("Unexpected continuation frame (len %d)", - skb->len); + if (!conn->rx_skb) { + BT_ERR("Unexpected ISO %s frame (len %d)", + (pb == ISO_END) ? "End" : "Cont", skb->len); goto drop; } @@ -2642,17 +2644,9 @@ int iso_recv(struct hci_dev *hdev, u16 handle, struct sk_buff *skb, u16 flags) skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len), skb->len); conn->rx_len -= skb->len; - break; - case ISO_END: - if (!conn->rx_len) { - BT_ERR("Unexpected end frame (len %d)", skb->len); - goto drop; - } - - skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len), - skb->len); - conn->rx_len -= skb->len; + if (pb == ISO_CONT) + break; if (!conn->rx_len) { struct sk_buff *rx_skb = conn->rx_skb; @@ -2663,6 +2657,13 @@ int iso_recv(struct hci_dev *hdev, u16 handle, struct sk_buff *skb, u16 flags) */ conn->rx_skb = NULL; iso_recv_frame(conn, rx_skb); + } else { + BT_ERR("ISO fragment incomplete (len %d, expected %d)", + skb->len, conn->rx_len); + kfree_skb(conn->rx_skb); + conn->rx_skb = NULL; + conn->rx_len = 0; + goto drop; } break; } From fd076d8deeab6f9f18ef13400f89e1f550df665b Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Wed, 1 Jul 2026 18:46:39 +0300 Subject: [PATCH 0607/1054] Bluetooth: ISO: exclude RFU bits from ISO_SDU_Length slen contains ISO_SDU_Length (12 bits), RFU (2 bits), Packet_Status_Flags (2 bits). Exclude the RFU bits from hci_iso_data_len. Also add masks to the pack macro. Fixes: 4de0fc599eb9 ("Bluetooth: Add definitions for CIS connections") Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz --- include/net/bluetooth/hci.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 38186a245f14..50f0eef71fb1 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -3413,8 +3413,9 @@ static inline struct hci_iso_hdr *hci_iso_hdr(const struct sk_buff *skb) #define hci_iso_flags_pack(pb, ts) ((pb & 0x03) | ((ts & 0x01) << 2)) /* ISO data length and flags pack/unpack */ -#define hci_iso_data_len_pack(h, f) ((__u16) ((h) | ((f) << 14))) -#define hci_iso_data_len(h) ((h) & 0x3fff) +#define hci_iso_data_len_pack(h, f) ((__u16) (((h) & 0x0fff) | \ + (((f) & 0x3) << 14))) +#define hci_iso_data_len(h) ((h) & 0x0fff) #define hci_iso_data_flags(h) ((h) >> 14) /* codec transport types */ From dd068ef044128db655f48323a4acfd5907e04903 Mon Sep 17 00:00:00 2001 From: Weiming Shi Date: Wed, 1 Jul 2026 09:06:14 -0700 Subject: [PATCH 0608/1054] Bluetooth: bpa10x: avoid OOB read of revision string in bpa10x_setup() bpa10x_setup() sends the vendor command 0xfc0e and passes the response to bt_dev_info() and hci_set_fw_info() as a "%s" string starting at skb->data + 1, without checking the length: bt_dev_info(hdev, "%s", (char *)(skb->data + 1)); hci_set_fw_info(hdev, "%s", skb->data + 1); A device that returns a one-byte response (status only) leaves skb->data + 1 past the end of the data, and the %s walk reads adjacent slab memory until it meets a NUL. The same happens when the payload is not NUL-terminated within skb->len. The out-of-bounds bytes end up in the kernel log and the firmware-info debugfs file. Print the revision string with a bounded "%.*s" limited to skb->len - 1 instead. This keeps the string readable for well-behaved devices while never reading past the received data, and does not fail setup, so a device returning a short or unterminated response keeps working. Fixes: ddd68ec8f484 ("Bluetooth: bpa10x: Read revision information in setup stage") Reported-by: Xiang Mei Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Weiming Shi Reported-by: Xiang Mei Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/bpa10x.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/bluetooth/bpa10x.c b/drivers/bluetooth/bpa10x.c index 2ae38a321c4b..e63d1af250ec 100644 --- a/drivers/bluetooth/bpa10x.c +++ b/drivers/bluetooth/bpa10x.c @@ -255,9 +255,13 @@ static int bpa10x_setup(struct hci_dev *hdev) if (IS_ERR(skb)) return PTR_ERR(skb); - bt_dev_info(hdev, "%s", (char *)(skb->data + 1)); + /* Bounded print: the device controls skb->len. */ + if (skb->len > 1) { + int len = skb->len - 1; - hci_set_fw_info(hdev, "%s", skb->data + 1); + bt_dev_info(hdev, "%.*s", len, (char *)(skb->data + 1)); + hci_set_fw_info(hdev, "%.*s", len, skb->data + 1); + } kfree_skb(skb); return 0; From 6e1930ece855a4c256f1c7e6632d634cfb9888b5 Mon Sep 17 00:00:00 2001 From: Stig Hornang Date: Fri, 12 Jun 2026 16:38:18 +0200 Subject: [PATCH 0609/1054] Bluetooth: L2CAP: fix tx ident leak for commands without a response Commit 6c3ea155e5ee ("Bluetooth: L2CAP: Fix not tracking outstanding TX ident") changed ident allocation to use an IDA, releasing idents in l2cap_put_ident() when the matching response command is received. But identifiers allocated for commands that have no response defined are never released. In particular L2CAP_LE_CREDITS is sent repeatedly for the lifetime of an LE CoC channel, so a peer streaming data to the host exhausts the 1-255 ident range after 254 credit packets. From then on l2cap_get_ident() fails: kernel: Bluetooth: Unable to allocate ident: -28 and every subsequent L2CAP_LE_CREDITS packet is sent with ident 0, which is invalid (Core Spec, Vol 3, Part A, Section 4: "Signaling identifier 0x00 is an invalid identifier and shall never be used in any command"). Remote stacks that validate the ident drop these commands, never receive new credits, and the channel stalls permanently. With default socket buffers this happens after roughly 0.5 MB of received data (the exact amount depends on the socket receive buffer): < ACL Data TX: Handle 2048 flags 0x00 dlen 12 LE L2CAP: LE Flow Control Credit (0x16) ident 0 len 4 Source CID: 64 Credits: 1 Release the ident immediately after sending L2CAP_LE_CREDITS since no response will ever release it. Use a local variable instead of chan->ident so that an ident that an EXT_FLOWCTL channel may be waiting on (e.g. a pending reconfigure) is not overwritten by a credit packet. Also add the missing L2CAP_LE_CONN_RSP case to l2cap_put_ident() so idents allocated for outgoing L2CAP_LE_CONN_REQ commands are released when the response arrives. Fixes: 6c3ea155e5ee ("Bluetooth: L2CAP: Fix not tracking outstanding TX ident") Link: https://bugzilla.kernel.org/show_bug.cgi?id=221629 Assisted-by: Claude:claude-opus-4.8 Assisted-by: Fable:5 Signed-off-by: Stig Hornang Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/l2cap_core.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 519cd9552d86..538ae9aa3479 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -4879,6 +4879,7 @@ static void l2cap_put_ident(struct l2cap_conn *conn, u8 code, u8 id) case L2CAP_ECHO_RSP: case L2CAP_INFO_RSP: case L2CAP_CONN_PARAM_UPDATE_RSP: + case L2CAP_LE_CONN_RSP: case L2CAP_ECRED_CONN_RSP: case L2CAP_ECRED_RECONF_RSP: /* First do a lookup since the remote may send bogus ids that @@ -6772,6 +6773,7 @@ static void l2cap_chan_le_send_credits(struct l2cap_chan *chan) struct l2cap_conn *conn = chan->conn; struct l2cap_le_credits pkt; u16 return_credits = l2cap_le_rx_credits(chan); + int ident; if (chan->mode != L2CAP_MODE_LE_FLOWCTL && chan->mode != L2CAP_MODE_EXT_FLOWCTL) @@ -6789,9 +6791,18 @@ static void l2cap_chan_le_send_credits(struct l2cap_chan *chan) pkt.cid = cpu_to_le16(chan->scid); pkt.credits = cpu_to_le16(return_credits); - chan->ident = l2cap_get_ident(conn); + ident = l2cap_get_ident(conn); - l2cap_send_cmd(conn, chan->ident, L2CAP_LE_CREDITS, sizeof(pkt), &pkt); + l2cap_send_cmd(conn, ident, L2CAP_LE_CREDITS, sizeof(pkt), &pkt); + + /* L2CAP_LE_CREDITS has no response so the ident is never released by + * l2cap_put_ident() - release it right away, otherwise the tx_ida + * range is exhausted after 254 packets and from then on credits are + * sent with the invalid ident 0, which some remote stacks ignore, + * stalling the channel. + */ + if (ident > 0) + ida_free(&conn->tx_ida, ident); } void l2cap_chan_rx_avail(struct l2cap_chan *chan, ssize_t rx_avail) From 3d04d9f390eeaab4d9e1ed4e9737e3d83581e18b Mon Sep 17 00:00:00 2001 From: Ankit Nautiyal Date: Mon, 22 Jun 2026 15:47:36 +0530 Subject: [PATCH 0610/1054] Revert "drm/i915/psr: Allow SCL=0 on platforms with always-on VRR TG" This reverts commit 4f1cab2e4863d96ce13b8d94151f4848e38c3d5b. Allowing SCL=0 on platforms with always-on VRR timing generator is causing underruns and other issues on PTL in some cases. SCL still needs to be non-zero in certain scenarios. Revert for now until this is better understood. Fixes: 4f1cab2e4863 ("drm/i915/psr: Allow SCL=0 on platforms with always-on VRR TG") Signed-off-by: Ankit Nautiyal Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260622101736.2389991-1-ankit.k.nautiyal@intel.com (cherry picked from commit 4dfcc789a144a21aa9be94f19f928aaa9fdc834d) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/i915/display/intel_psr.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c index e138982dc91f..beaa1d62613d 100644 --- a/drivers/gpu/drm/i915/display/intel_psr.c +++ b/drivers/gpu/drm/i915/display/intel_psr.c @@ -1522,9 +1522,6 @@ int _intel_psr_min_set_context_latency(const struct intel_crtc_state *crtc_state needs_panel_replay) return 0; - if (intel_vrr_always_use_vrr_tg(display)) - return 0; - return 1; } From c505d54c1a9e99d36d322ced6c9c71a1157424b0 Mon Sep 17 00:00:00 2001 From: Louis-Alexis Eyraud Date: Wed, 1 Jul 2026 16:44:26 +0200 Subject: [PATCH 0611/1054] dt-bindings: mmc: mtk-sd: Document extra clocks for MT8189 MT8189 SoC MMC Controller IP has 4 additional clocks. Describe them in the dt-bindings for this SoC. Fixes: 7514f64780a4 ("dt-bindings: mmc: mtk-sd: Add support for MT8189 SoC") Signed-off-by: Louis-Alexis Eyraud Acked-by: Conor Dooley Signed-off-by: Ulf Hansson --- .../devicetree/bindings/mmc/mtk-sd.yaml | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/mmc/mtk-sd.yaml b/Documentation/devicetree/bindings/mmc/mtk-sd.yaml index eb3755bdfdf7..a4d032224dce 100644 --- a/Documentation/devicetree/bindings/mmc/mtk-sd.yaml +++ b/Documentation/devicetree/bindings/mmc/mtk-sd.yaml @@ -193,7 +193,6 @@ allOf: - mediatek,mt8183-mmc - mediatek,mt8186-mmc - mediatek,mt8188-mmc - - mediatek,mt8189-mmc - mediatek,mt8195-mmc - mediatek,mt8196-mmc - mediatek,mt8516-mmc @@ -348,6 +347,34 @@ allOf: - const: axi_cg - const: ahb_cg + - if: + properties: + compatible: + contains: + const: mediatek,mt8189-mmc + then: + properties: + clocks: + minItems: 6 + items: + - description: source clock + - description: HCLK which used for host + - description: independent source clock gate + - description: bus clock used for internal register access + - description: peripheral bus clock gate + - description: AXI bus clock gate + - description: crypto clock used for data encrypt/decrypt (optional) + clock-names: + minItems: 6 + items: + - const: source + - const: hclk + - const: source_cg + - const: bus_clk + - const: pclk_cg + - const: axi_cg + - const: crypto + unevaluatedProperties: false examples: From 718178f524b98bc920d74bc771aed823c8b81425 Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Thu, 2 Jul 2026 16:27:45 +0800 Subject: [PATCH 0612/1054] memstick: ms_block: reject a card that reports too many blocks msb_ftl_initialize() computes the zone count from the card block count with no bound: msb->zone_count = msb->block_count / MS_BLOCKS_IN_ZONE; ... for (i = 0; i < msb->zone_count; i++) msb->free_block_count[i] = MS_BLOCKS_IN_ZONE; msb->block_count is a card value. msb_read_boot_blocks() reads number_of_blocks from the card boot page and byte swaps it. free_block_count is a fixed int[MS_MAX_ZONES]. MS_MAX_ZONES is 16, so the valid indices are 0 to 15. The init loop above indexes it by zone_count. msb_mark_block_used() and msb_mark_block_unused() index it by pba / MS_BLOCKS_IN_ZONE, for pba up to block_count - 1. A card may report up to 65535 blocks. A block_count above 8192 (MS_MAX_ZONES * MS_BLOCKS_IN_ZONE) lets the pba index reach 16. That writes past free_block_count[] and corrupts struct msb_data. A larger count runs the init loop past the end too. A real Memory Stick has at most 16 zones. So it has at most 8192 blocks. msb_ftl_initialize() now rejects a card that reports more than MS_MAX_ZONES * MS_BLOCKS_IN_ZONE blocks. Fixes: 0ab30494bc4f ("memstick: add support for legacy memorysticks") Cc: stable@vger.kernel.org Signed-off-by: Maoyi Xie Signed-off-by: Ulf Hansson --- drivers/memstick/core/ms_block.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/memstick/core/ms_block.c b/drivers/memstick/core/ms_block.c index a01fe313558e..ce33907bfc24 100644 --- a/drivers/memstick/core/ms_block.c +++ b/drivers/memstick/core/ms_block.c @@ -1338,6 +1338,10 @@ static int msb_ftl_initialize(struct msb_data *msb) return 0; msb->zone_count = msb->block_count / MS_BLOCKS_IN_ZONE; + if (msb->block_count > MS_MAX_ZONES * MS_BLOCKS_IN_ZONE) { + pr_err("Too many blocks: %d\n", msb->block_count); + return -EINVAL; + } msb->logical_block_count = msb->zone_count * 496 - 2; msb->used_blocks_bitmap = bitmap_zalloc(msb->block_count, GFP_KERNEL); From 86152fef52cac15cd662ed3bfc7604fbfef378f0 Mon Sep 17 00:00:00 2001 From: Ao Sun Date: Mon, 6 Jul 2026 11:43:00 +0000 Subject: [PATCH 0613/1054] mmc: block: fix RPMB device unregister ordering Since commit 7852028a35f0 ("mmc: block: register RPMB partition with the RPMB subsystem"), each mmc RPMB partition is represented by two device objects: - the mmc-owned device (`rpmb->dev`, backing the legacy /dev/mmcblkXrpmb char device) and - the rpmb-core device (`rdev`, backing /dev/rpmbN). The child RPMB device holds a reference to its parent, so the parent's release callback cannot be invoked if the child device is still registered. Remove rpmb_dev_unregister() from the parent release handler and unregister the child RPMB device in the remove path before tearing down the parent device. Also delete the extra blank line between mmc_blk_remove_rpmb_part() and {. Fixes: 7852028a35f0 ("mmc: block: register RPMB partition with the RPMB subsystem") Cc: stable@vger.kernel.org Signed-off-by: Jiazi Li Signed-off-by: Ao Sun Reviewed-by: Avri Altman Signed-off-by: Ulf Hansson --- drivers/mmc/core/block.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index 0274e8d07660..54a923ba4f1e 100644 --- a/drivers/mmc/core/block.c +++ b/drivers/mmc/core/block.c @@ -2715,7 +2715,6 @@ static void mmc_blk_rpmb_device_release(struct device *dev) { struct mmc_rpmb_data *rpmb = dev_get_drvdata(dev); - rpmb_dev_unregister(rpmb->rdev); mmc_blk_put(rpmb->md); ida_free(&mmc_rpmb_ida, rpmb->id); kfree(rpmb); @@ -2930,8 +2929,8 @@ static int mmc_blk_alloc_rpmb_part(struct mmc_card *card, } static void mmc_blk_remove_rpmb_part(struct mmc_rpmb_data *rpmb) - { + rpmb_dev_unregister(rpmb->rdev); cdev_device_del(&rpmb->chrdev, &rpmb->dev); put_device(&rpmb->dev); } From 8b0b864c11a2e2ada470f9d5010e1c2bf1eceef2 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Maneyrol Date: Tue, 23 Jun 2026 16:22:15 +0200 Subject: [PATCH 0614/1054] iio: imu: inv_icm42600: fix timestamp clock period by using lower value Clock period value is used for computing periods of sampling. There is no need for it to be higher than the maximum odr, otherwise we are losing precision in the computation for nothing. Switch clock period value to maximum odr period (8kHz). Fixes: 0ecc363ccea7 ("iio: make invensense timestamp module generic") Cc: stable@vger.kernel.org Signed-off-by: Jean-Baptiste Maneyrol Signed-off-by: Jonathan Cameron --- drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c | 4 ++-- drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c index 532d5fdffaf8..7df920ef3cf0 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c @@ -1170,10 +1170,10 @@ struct iio_dev *inv_icm42600_accel_init(struct inv_icm42600_state *st) accel_st->filter = INV_ICM42600_FILTER_AVG_16X; /* - * clock period is 32kHz (31250ns) + * clock period is 8kHz (125000ns) * jitter is +/- 2% (20 per mille) */ - ts_chip.clock_period = 31250; + ts_chip.clock_period = 125000; ts_chip.jitter = 20; ts_chip.init_period = inv_icm42600_odr_to_period(st->conf.accel.odr); inv_sensors_timestamp_init(&accel_st->ts, &ts_chip); diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c index 11339ddf1da3..a18dcac93929 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c @@ -755,10 +755,10 @@ struct iio_dev *inv_icm42600_gyro_init(struct inv_icm42600_state *st) } /* - * clock period is 32kHz (31250ns) + * clock period is 8kHz (125000ns) * jitter is +/- 2% (20 per mille) */ - ts_chip.clock_period = 31250; + ts_chip.clock_period = 125000; ts_chip.jitter = 20; ts_chip.init_period = inv_icm42600_odr_to_period(st->conf.accel.odr); inv_sensors_timestamp_init(&gyro_st->ts, &ts_chip); From 3a07249981629ace483ebbef81ef6b34c2d2afec Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Sat, 27 Jun 2026 11:51:05 +0100 Subject: [PATCH 0615/1054] KVM: Move kvm_io_bus_get_dev() locking responsibilities to callers kvm_io_bus_get_dev() returns a device that is only matched by the address, and nothing else. This can cause a lifetime issue if the matched device is not the expected type, as by the time the caller can introspect the object, it might be gone (the srcu lock having been dropped). Given that there is only a single user of this helper, the simplest option is to move the locking responsibility to the caller, which can keep the srcu lock held for as long as it wants. Note that this aligns with other kvm_io_bus*() helpers, which already require the srcu lock to be held by the callers. Reported-by: Will Deacon Fixes: 8a39d00670f07 ("KVM: kvm_io_bus: Add kvm_io_bus_get_dev() call") Link: https://lore.kernel.org/all/20260626111344.802555-1-maz@kernel.org Cc: stable@vger.kernel.org Reviewed-by: Oliver Upton Link: https://patch.msgid.link/20260627105105.1005990-1-maz@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kvm/vgic/vgic-its.c | 2 ++ virt/kvm/kvm_main.c | 16 +++++----------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/arch/arm64/kvm/vgic/vgic-its.c b/arch/arm64/kvm/vgic/vgic-its.c index 67d107e9a77d..c90abde39fb8 100644 --- a/arch/arm64/kvm/vgic/vgic-its.c +++ b/arch/arm64/kvm/vgic/vgic-its.c @@ -508,6 +508,8 @@ static struct vgic_its *__vgic_doorbell_to_its(struct kvm *kvm, gpa_t db) struct kvm_io_device *kvm_io_dev; struct vgic_io_device *iodev; + guard(srcu)(&kvm->srcu); + kvm_io_dev = kvm_io_bus_get_dev(kvm, KVM_MMIO_BUS, db); if (!kvm_io_dev) return ERR_PTR(-EINVAL); diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 89489996fbc1..5788eac0ab81 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -6068,25 +6068,19 @@ struct kvm_io_device *kvm_io_bus_get_dev(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr) { struct kvm_io_bus *bus; - int dev_idx, srcu_idx; - struct kvm_io_device *iodev = NULL; + int dev_idx; - srcu_idx = srcu_read_lock(&kvm->srcu); + lockdep_assert_held(&kvm->srcu); bus = kvm_get_bus_srcu(kvm, bus_idx); if (!bus) - goto out_unlock; + return NULL; dev_idx = kvm_io_bus_get_first_dev(bus, addr, 1); if (dev_idx < 0) - goto out_unlock; + return NULL; - iodev = bus->range[dev_idx].dev; - -out_unlock: - srcu_read_unlock(&kvm->srcu, srcu_idx); - - return iodev; + return bus->range[dev_idx].dev; } EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_io_bus_get_dev); From 100baf0184896f859290a684f864b8200d8ac872 Mon Sep 17 00:00:00 2001 From: Oliver Upton Date: Wed, 1 Jul 2026 16:16:19 -0700 Subject: [PATCH 0616/1054] KVM: arm64: Ensure level is always initialized when relaxing perms stage2_update_leaf_attrs() returns early before writing to @level if the table walker returned an error. At the same time, kvm_pgtable_stage2_relax_perms() uses the level as a TLBI TTL hint when the error was EAGAIN, indicating the vCPU raced with a table update and the TLB entry it hit is now stale. Fall back to an unknown TTL if none was provided by the walk. Cc: stable@vger.kernel.org Fixes: be097997a273 ("KVM: arm64: Always invalidate TLB for stage-2 permission faults") Signed-off-by: Oliver Upton Reviewed-by: Wei-Lin Chang Link: https://patch.msgid.link/20260701231620.3300204-2-oupton@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kvm/hyp/pgtable.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/kvm/hyp/pgtable.c b/arch/arm64/kvm/hyp/pgtable.c index 0c1defa5fb0f..386a7468136a 100644 --- a/arch/arm64/kvm/hyp/pgtable.c +++ b/arch/arm64/kvm/hyp/pgtable.c @@ -1356,7 +1356,7 @@ int kvm_pgtable_stage2_relax_perms(struct kvm_pgtable *pgt, u64 addr, enum kvm_pgtable_prot prot, enum kvm_pgtable_walk_flags flags) { kvm_pte_t xn = 0, set = 0, clr = 0; - s8 level; + s8 level = TLBI_TTL_UNKNOWN; int ret; if (prot & KVM_PTE_LEAF_ATTR_HI_SW) From f35c08c092505f3a83ce097d94fe51eb8bc9c1b5 Mon Sep 17 00:00:00 2001 From: Oliver Upton Date: Wed, 1 Jul 2026 16:16:20 -0700 Subject: [PATCH 0617/1054] KVM: arm64: Only update XN attr when requested during S2 relaxation On systems without DIC, KVM lazily grants execute permission to stage-2 translations after taking an instruction abort due to a permission fault, allowing it to defer I-cache invalidations to the point they're absolutely required. If a data abort happens later down the line to such a translation, KVM will not request execute permissions as part of the S2 relaxation on the assumption that kvm_pgtable_stage2_relax_perms() does exactly what the name implies and adds the requested permissions to the pre-existing ones. Avoid taking unintended execute permission faults by only preparing the XN attribute if KVM_PGTABLE_PROT_X is set. Fixes: 2608563b466b ("KVM: arm64: Add support for FEAT_XNX stage-2 permissions") Signed-off-by: Oliver Upton Reviewed-by: Wei-Lin Chang Link: https://patch.msgid.link/20260701231620.3300204-3-oupton@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kvm/hyp/pgtable.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/arch/arm64/kvm/hyp/pgtable.c b/arch/arm64/kvm/hyp/pgtable.c index 386a7468136a..8754c99c22f2 100644 --- a/arch/arm64/kvm/hyp/pgtable.c +++ b/arch/arm64/kvm/hyp/pgtable.c @@ -1368,12 +1368,14 @@ int kvm_pgtable_stage2_relax_perms(struct kvm_pgtable *pgt, u64 addr, if (prot & KVM_PGTABLE_PROT_W) set |= KVM_PTE_LEAF_ATTR_LO_S2_S2AP_W; - ret = stage2_set_xn_attr(prot, &xn); - if (ret) - return ret; + if (prot & KVM_PGTABLE_PROT_X) { + ret = stage2_set_xn_attr(prot, &xn); + if (ret) + return ret; - set |= xn & KVM_PTE_LEAF_ATTR_HI_S2_XN; - clr |= ~xn & KVM_PTE_LEAF_ATTR_HI_S2_XN; + set |= xn & KVM_PTE_LEAF_ATTR_HI_S2_XN; + clr |= ~xn & KVM_PTE_LEAF_ATTR_HI_S2_XN; + } ret = stage2_update_leaf_attrs(pgt, addr, 1, set, clr, NULL, &level, flags); if (!ret || ret == -EAGAIN) From 85f56708a443ec02a290878f00d79a1ff5110e41 Mon Sep 17 00:00:00 2001 From: Fuad Tabba Date: Mon, 6 Jul 2026 12:55:21 +0100 Subject: [PATCH 0618/1054] KVM: arm64: Fix sign-extension of MMIO loads A sign-extending load (LDRSB, LDRSH, LDRSW) from MMIO returns a zero-extended value to the guest. The architecture performs such a load as a memory read of the access size, then a sign-extension to the register width. For LDRSH (DDI 0487 M.b C6.2.225, with the Mem accessor at J1.2.3.111): data = Mem{16}(address, accdesc); X{regsize}(t) = SignExtend{regsize}(data); The byte order is handled inside the Mem accessor, keyed on the access size; the register width is separate, applied afterwards by SignExtend(). kvm_handle_mmio_return() runs these in the wrong order: it sign-extends the access-width data, then calls vcpu_data_host_to_guest(), which masks the value back to the access width (the size-keyed byte-order step). The mask drops the sign bits that sign-extension produced. Reorder so vcpu_data_host_to_guest() runs first, with the sign-extension to register width after it. trace_kvm_mmio() moves with it and now logs the access-width data before sign-extension. Fixes: b30070862edbd ("ARM64: KVM: MMIO support BE host running LE code") Reviewed-by: Oliver Upton Signed-off-by: Fuad Tabba Link: https://patch.msgid.link/20260706115522.954913-2-fuad.tabba@linux.dev Signed-off-by: Marc Zyngier --- arch/arm64/kvm/mmio.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/arch/arm64/kvm/mmio.c b/arch/arm64/kvm/mmio.c index e2285ed8c91d..d1c3a352d5a2 100644 --- a/arch/arm64/kvm/mmio.c +++ b/arch/arm64/kvm/mmio.c @@ -126,6 +126,10 @@ int kvm_handle_mmio_return(struct kvm_vcpu *vcpu) len = kvm_vcpu_dabt_get_as(vcpu); data = kvm_mmio_read_buf(run->mmio.data, len); + trace_kvm_mmio(KVM_TRACE_MMIO_READ, len, run->mmio.phys_addr, + &data); + data = vcpu_data_host_to_guest(vcpu, data, len); + if (kvm_vcpu_dabt_issext(vcpu) && len < sizeof(unsigned long)) { mask = 1U << ((len * 8) - 1); @@ -135,9 +139,6 @@ int kvm_handle_mmio_return(struct kvm_vcpu *vcpu) if (!kvm_vcpu_dabt_issf(vcpu)) data = data & 0xffffffff; - trace_kvm_mmio(KVM_TRACE_MMIO_READ, len, run->mmio.phys_addr, - &data); - data = vcpu_data_host_to_guest(vcpu, data, len); vcpu_set_reg(vcpu, kvm_vcpu_dabt_get_rd(vcpu), data); } From 65a38ddeaeed1a962bc1b4ca03c98201c877fea5 Mon Sep 17 00:00:00 2001 From: Fuad Tabba Date: Mon, 6 Jul 2026 12:55:22 +0100 Subject: [PATCH 0619/1054] KVM: arm64: selftests: Add MMIO sign-extending load test Add a test for sign-extending MMIO loads (LDRSB, LDRSH, LDRSW) into Xt and Wt destinations, with and without the sign bit set. The host supplies the MMIO data and checks the guest register holds the sign-extended value. Repeat the loads big-endian on a mixed-endian implementation. Issue those at EL0: SCTLR_EL1.EE would make an EL1 load big-endian but also walk the little-endian page tables big-endian, whereas SCTLR_EL1.E0E selects only EL0 data endianness and leaves the walk little-endian. Signed-off-by: Fuad Tabba Link: https://patch.msgid.link/20260706115522.954913-3-fuad.tabba@linux.dev Signed-off-by: Marc Zyngier --- tools/testing/selftests/kvm/Makefile.kvm | 1 + .../selftests/kvm/arm64/mmio_sign_ext.c | 255 ++++++++++++++++++ 2 files changed, 256 insertions(+) create mode 100644 tools/testing/selftests/kvm/arm64/mmio_sign_ext.c diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm index 9118a5a51b89..0f5803a1092e 100644 --- a/tools/testing/selftests/kvm/Makefile.kvm +++ b/tools/testing/selftests/kvm/Makefile.kvm @@ -171,6 +171,7 @@ TEST_GEN_PROGS_arm64 += arm64/hello_el2 TEST_GEN_PROGS_arm64 += arm64/host_sve TEST_GEN_PROGS_arm64 += arm64/hypercalls TEST_GEN_PROGS_arm64 += arm64/external_aborts +TEST_GEN_PROGS_arm64 += arm64/mmio_sign_ext TEST_GEN_PROGS_arm64 += arm64/page_fault_test TEST_GEN_PROGS_arm64 += arm64/psci_test TEST_GEN_PROGS_arm64 += arm64/sea_to_user diff --git a/tools/testing/selftests/kvm/arm64/mmio_sign_ext.c b/tools/testing/selftests/kvm/arm64/mmio_sign_ext.c new file mode 100644 index 000000000000..25196f1e6322 --- /dev/null +++ b/tools/testing/selftests/kvm/arm64/mmio_sign_ext.c @@ -0,0 +1,255 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * mmio_sign_ext - Test sign-extending MMIO load emulation (LDRSB/LDRSH/LDRSW) + * + * Copyright (c) 2026 Google LLC + * Author: Fuad Tabba + */ + +#include + +#include "processor.h" +#include "test_util.h" + +#define MMIO_ADDR 0x8000000ULL + +/* AP[1]: allow unprivileged (EL0) access to a mapping. */ +#define PTE_USER BIT(6) + +/* SPSR for ERET to EL0t with DAIF masked. */ +#define SPSR_EL0 (PSR_MODE_EL0t | PSR_D_BIT | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT) + +struct mmio_test { + const char *name; + uint64_t data; /* access-width value, host byte order */ + uint8_t len; + uint64_t expected; /* sign-extended result; same for LE and BE */ +}; + +/* Paired 1:1, in order, with the loads in guest_loads_le() and el0_be_loads. */ +static const struct mmio_test tests[] = { + /* LDRSB Xt: byte sign-extended to 64 bits */ + { "LDRSB Xt 0xFF", 0xFF, 1, 0xFFFFFFFFFFFFFFFFULL }, + { "LDRSB Xt 0x7F", 0x7F, 1, 0x7FULL }, + + /* LDRSB Wt: byte sign-extended to 32 bits, upper 32 bits zeroed */ + { "LDRSB Wt 0xFF", 0xFF, 1, 0xFFFFFFFFULL }, + { "LDRSB Wt 0x7F", 0x7F, 1, 0x7FULL }, + + /* LDRSH Xt: halfword sign-extended to 64 bits */ + { "LDRSH Xt 0x8001", 0x8001, 2, 0xFFFFFFFFFFFF8001ULL }, + { "LDRSH Xt 0x7FFF", 0x7FFF, 2, 0x7FFFULL }, + + /* LDRSH Wt: halfword sign-extended to 32 bits, upper 32 bits zeroed */ + { "LDRSH Wt 0x8001", 0x8001, 2, 0xFFFF8001ULL }, + { "LDRSH Wt 0x7FFF", 0x7FFF, 2, 0x7FFFULL }, + + /* LDRSW Xt: word sign-extended to 64 bits (no Wt form) */ + { "LDRSW Xt 0x80000001", 0x80000001, 4, 0xFFFFFFFF80000001ULL }, + { "LDRSW Xt 0x7FFFFFFF", 0x7FFFFFFF, 4, 0x7FFFFFFFULL }, +}; + +/* Issue one sign-extending load from MMIO and report the result. */ +#define GUEST_LDRS(load) do { \ + uint64_t val; \ + \ + asm volatile(load : "=r"(val) : "r"(MMIO_ADDR) : "memory"); \ + GUEST_SYNC(val); \ +} while (0) + +/* Little-endian pass: loads issued at EL1. */ +static void guest_loads_le(void) +{ + GUEST_LDRS("ldrsb %0, [%1]"); + GUEST_LDRS("ldrsb %0, [%1]"); + GUEST_LDRS("ldrsb %w0, [%1]"); + GUEST_LDRS("ldrsb %w0, [%1]"); + GUEST_LDRS("ldrsh %0, [%1]"); + GUEST_LDRS("ldrsh %0, [%1]"); + GUEST_LDRS("ldrsh %w0, [%1]"); + GUEST_LDRS("ldrsh %w0, [%1]"); + GUEST_LDRS("ldrsw %0, [%1]"); + GUEST_LDRS("ldrsw %0, [%1]"); +} + +/* + * Run the big-endian loads at EL0, where SCTLR_EL1.E0E flips only the data + * endianness; at EL1, SCTLR_EL1.EE would also flip the page-table walk and + * fault on the little-endian tables. x0 holds MMIO_ADDR; results return in + * x19..x28 (tests[] order) via a single SVC. + */ +extern char el0_be_loads[]; +asm( +" .pushsection .text, \"ax\"\n" +" .global el0_be_loads\n" +"el0_be_loads:\n" +" ldrsb x19, [x0]\n" +" ldrsb x20, [x0]\n" +" ldrsb w21, [x0]\n" +" ldrsb w22, [x0]\n" +" ldrsh x23, [x0]\n" +" ldrsh x24, [x0]\n" +" ldrsh w25, [x0]\n" +" ldrsh w26, [x0]\n" +" ldrsw x27, [x0]\n" +" ldrsw x28, [x0]\n" +" svc #0\n" +" .popsection\n" +); + +/* EL1 handler for the EL0 SVC: report the results, then finish. */ +static void el0_svc_handler(struct ex_regs *regs) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(tests); i++) + GUEST_SYNC(regs->regs[19 + i]); + + GUEST_DONE(); +} + +static bool guest_mixed_endian_el0(void) +{ + uint64_t mmfr0 = read_sysreg(id_aa64mmfr0_el1); + + return SYS_FIELD_GET(ID_AA64MMFR0_EL1, BIGEND, mmfr0) || + SYS_FIELD_GET(ID_AA64MMFR0_EL1, BIGENDEL0, mmfr0); +} + +static void guest_code(void) +{ + guest_loads_le(); + + if (guest_mixed_endian_el0()) { + write_sysreg(read_sysreg(sctlr_el1) | SCTLR_EL1_E0E, sctlr_el1); + isb(); + + asm volatile( + " msr elr_el1, %[pc]\n" + " msr spsr_el1, %[spsr]\n" + " mov x0, %[mmio]\n" + " isb\n" + " eret\n" + : + : [pc] "r"(el0_be_loads), + [spsr] "r"((uint64_t)SPSR_EL0), + [mmio] "r"(MMIO_ADDR) + : "x0", "memory"); + __builtin_unreachable(); /* el0_svc_handler ends the test */ + } + + GUEST_DONE(); +} + +static void handle_mmio(struct kvm_run *run, const struct mmio_test *t, bool be) +{ + int i; + + TEST_ASSERT_EQ(run->mmio.phys_addr, MMIO_ADDR); + TEST_ASSERT(!run->mmio.is_write, "Expected MMIO read for %s", t->name); + TEST_ASSERT_EQ(run->mmio.len, t->len); + + memset(run->mmio.data, 0, sizeof(run->mmio.data)); + if (be) { + /* The guest reads the device bytes most-significant first. */ + for (i = 0; i < t->len; i++) + run->mmio.data[i] = t->data >> (8 * (t->len - 1 - i)); + } else { + /* Works because arm64 KVM hosts are always little-endian. */ + memcpy(run->mmio.data, &t->data, t->len); + } +} + +static void expect_sync(struct kvm_vcpu *vcpu, struct ucall *uc, + const struct mmio_test *t) +{ + switch (get_ucall(vcpu, uc)) { + case UCALL_SYNC: + TEST_ASSERT(uc->args[1] == t->expected, + "%s: got %#lx, want %#lx", t->name, + (unsigned long)uc->args[1], (unsigned long)t->expected); + break; + case UCALL_ABORT: + REPORT_GUEST_ASSERT(*uc); + break; + default: + TEST_FAIL("Unexpected ucall for %s", t->name); + } +} + +/* OR PTE_USER into the leaf descriptors covering [gva, gva + len). */ +static void make_el0_accessible(struct kvm_vm *vm, uint64_t gva, uint64_t len) +{ + uint64_t addr; + + for (addr = gva & ~((uint64_t)vm->page_size - 1); addr < gva + len; + addr += vm->page_size) + *virt_get_pte_hva(vm, addr) |= PTE_USER; +} + +static bool vcpu_mixed_endian_el0(struct kvm_vcpu *vcpu) +{ + uint64_t mmfr0 = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_ID_AA64MMFR0_EL1)); + + return SYS_FIELD_GET(ID_AA64MMFR0_EL1, BIGEND, mmfr0) || + SYS_FIELD_GET(ID_AA64MMFR0_EL1, BIGENDEL0, mmfr0); +} + +int main(void) +{ + struct kvm_vcpu *vcpu; + struct kvm_vm *vm; + struct ucall uc; + unsigned int i; + bool be; + + vm = vm_create_with_one_vcpu(&vcpu, guest_code); + virt_map(vm, MMIO_ADDR, MMIO_ADDR, 1); + + vm_init_descriptor_tables(vm); + vcpu_init_descriptor_tables(vcpu); + vm_install_sync_handler(vm, VECTOR_SYNC_LOWER_64, ESR_ELx_EC_SVC64, + el0_svc_handler); + + be = vcpu_mixed_endian_el0(vcpu); + if (be) + make_el0_accessible(vm, MMIO_ADDR, vm->page_size); + + ksft_print_header(); + ksft_set_plan(ARRAY_SIZE(tests) * (be ? 2 : 1)); + + /* Little-endian pass: one load and one result per iteration. */ + for (i = 0; i < ARRAY_SIZE(tests); i++) { + const struct mmio_test *t = &tests[i]; + + vcpu_run(vcpu); + TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_MMIO); + handle_mmio(vcpu->run, t, false); + + vcpu_run(vcpu); + expect_sync(vcpu, &uc, t); + + ksft_test_result_pass("%s\n", t->name); + } + + if (be) { + /* The EL0 stub issues all the loads, then reports the results. */ + for (i = 0; i < ARRAY_SIZE(tests); i++) { + vcpu_run(vcpu); + TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_MMIO); + handle_mmio(vcpu->run, &tests[i], true); + } + for (i = 0; i < ARRAY_SIZE(tests); i++) { + vcpu_run(vcpu); + expect_sync(vcpu, &uc, &tests[i]); + ksft_test_result_pass("BE %s\n", tests[i].name); + } + } + + vcpu_run(vcpu); + TEST_ASSERT(get_ucall(vcpu, &uc) == UCALL_DONE, "Expected UCALL_DONE"); + + kvm_vm_free(vm); + + ksft_finished(); +} From 15d9968b68e848a1a38ca875ba553b05dadae03d Mon Sep 17 00:00:00 2001 From: Yu Peng Date: Wed, 3 Jun 2026 14:25:32 +0800 Subject: [PATCH 0620/1054] tracing/synthetic: Free pending field on error path Some __create_synth_event() error paths run after parse_synth_field() succeeds but before the field is stored in fields[]. The common cleanup then misses the field. Free it before freeing argv. Link: https://patch.msgid.link/20260603062533.1096320-1-pengyu@kylinos.cn Signed-off-by: Yu Peng Signed-off-by: Steven Rostedt --- kernel/trace/trace_events_synth.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/kernel/trace/trace_events_synth.c b/kernel/trace/trace_events_synth.c index e6871230bde9..cdd5b9332835 100644 --- a/kernel/trace/trace_events_synth.c +++ b/kernel/trace/trace_events_synth.c @@ -1446,13 +1446,13 @@ static int __create_synth_event(const char *name, const char *raw_fields) if (cmd_version > 1 && n_fields_this_loop >= 1) { synth_err(SYNTH_ERR_INVALID_CMD, errpos(field_str)); ret = -EINVAL; - goto err_free_arg; + goto err_free_field; } if (n_fields == SYNTH_FIELDS_MAX) { synth_err(SYNTH_ERR_TOO_MANY_FIELDS, 0); ret = -EINVAL; - goto err_free_arg; + goto err_free_field; } fields[n_fields++] = field; @@ -1491,6 +1491,8 @@ static int __create_synth_event(const char *name, const char *raw_fields) kfree(saved_fields); return ret; + err_free_field: + free_synth_field(field); err_free_arg: argv_free(argv); err: From c37e0a4b79a6bbb96ce5ffe279d7c001e20529e0 Mon Sep 17 00:00:00 2001 From: Hui Wang Date: Sun, 7 Jun 2026 15:24:30 +0800 Subject: [PATCH 0621/1054] ring-buffer: Fix event length with forced 8-byte alignment When RB_FORCE_8BYTE_ALIGNMENT is true, rb_calculate_event_length() reserves the space of event->array[0] for placing the data length and rb_update_event() stores the data length in event->array[0] accordingly. As a result the whole event length will add extra 4 bytes for sizeof(event.array[0]) unconditionally. But ring_buffer_event_length() only subtracts the sizeof(event->array[0]) for events larger than RB_MAX_SMALL_DATA + sizeof(event->array[0]). As a result, small events on architectures with RB_FORCE_8BYTE_ALIGNMENT=true report a data length that is 4 bytes larger than expected. To fix it, add the RB_FORCE_8BYTE_ALIGNMENT as a condition to subtract the size of that length field whenever RB_FORCE_8BYTE_ALIGNMENT is true. This issue is observed in a riscv64 kernel with CONFIG_HAVE_64BIT_ALIGNED_ACCESS set to y, when we run ftrace selftest trace_marker_raw.tc, we get the weird log: for cases where the id is 1..100, the number of data field is 8*N, but once id exceeds 100, the number of data field becomes 8*N+4: # 1 buf: 58 00 00 00 80 5e d1 63 (number of data field is 8*1) ... # a buf: 58 ... (number of data field is 8*2) ... # 64 buf: 58 ... (number of data field is 8*13) # 65 buf: 58 ... (number of data field is 8*13+4) After applying this change, the number of data field keeps being 8*N+4 consistently. Link: https://patch.msgid.link/20260607072431.125633-2-hui.wang@canonical.com Fixes: 2271048d1b3b ("ring-buffer: Do 8 byte alignment for 64 bit that can not handle 4 byte align") Signed-off-by: Hui Wang Reviewed-by: Masami Hiramatsu (Google) Signed-off-by: Steven Rostedt --- kernel/trace/ring_buffer.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index 56a328e94395..d9af2bbaf9c0 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -270,7 +270,8 @@ unsigned ring_buffer_event_length(struct ring_buffer_event *event) if (event->type_len > RINGBUF_TYPE_DATA_TYPE_LEN_MAX) return length; length -= RB_EVNT_HDR_SIZE; - if (length > RB_MAX_SMALL_DATA + sizeof(event->array[0])) + if (length > RB_MAX_SMALL_DATA + sizeof(event->array[0]) || + RB_FORCE_8BYTE_ALIGNMENT) length -= sizeof(event->array[0]); return length; } From fe58f457ad8d0a2bef4e053cfecca4b5cd266b1a Mon Sep 17 00:00:00 2001 From: Crystal Wood Date: Mon, 8 Jun 2026 23:54:30 -0500 Subject: [PATCH 0622/1054] tracing/osnoise: Call synchronize_rcu() when unregistering This ensures that any RCU readers traversing the instance list have finished, before releasing the reference on the tracer that the instance points to. Cc: stable@vger.kernel.org Fixes: a6ed2aee54644 ("tracing: Switch to kvfree_rcu() API") Link: https://patch.msgid.link/20260609045430.1589786-1-crwood@redhat.com Suggested-by: Steven Rostedt Signed-off-by: Crystal Wood Signed-off-by: Steven Rostedt --- kernel/trace/trace_osnoise.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kernel/trace/trace_osnoise.c b/kernel/trace/trace_osnoise.c index 5e83c4f6f2b4..0e1265acd1cc 100644 --- a/kernel/trace/trace_osnoise.c +++ b/kernel/trace/trace_osnoise.c @@ -179,7 +179,9 @@ static void osnoise_unregister_instance(struct trace_array *tr) if (!found) return; - kvfree_rcu_mightsleep(inst); + /* Do a full sync to ensure that tr remains valid, not just inst */ + synchronize_rcu(); + kvfree(inst); } /* From a20b08de862c2983eac183b7560ad678acb58608 Mon Sep 17 00:00:00 2001 From: "Wayen.Yan" Date: Thu, 11 Jun 2026 11:52:57 +0800 Subject: [PATCH 0623/1054] tracing: Remove unused ret assignment in tracing_set_tracer() In tracing_set_tracer(), the assignment 'ret = 0' following the __tracing_resize_ring_buffer() error check is a dead store. After this point, all subsequent code paths either return with a constant value (-EINVAL, 0, -EBUSY) or reassign ret before reading it (tracing_arm_snapshot_locked, tracer_init). Remove the unnecessary assignment. No functional change. Link: https://patch.msgid.link/6a2a37c4.f0a9eb5a.2fc603.7724@mx.google.com Signed-off-by: Wayen.Yan Signed-off-by: Steven Rostedt --- kernel/trace/trace.c | 1 - 1 file changed, 1 deletion(-) diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 1146b83b711a..299f5ab630b9 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -5015,7 +5015,6 @@ int tracing_set_tracer(struct trace_array *tr, const char *buf) RING_BUFFER_ALL_CPUS); if (ret < 0) return ret; - ret = 0; } list_for_each_entry(t, &tr->tracers, list) { From b983c56426383e4a06fa5970c4e33cee879b1482 Mon Sep 17 00:00:00 2001 From: Farhad Alemi Date: Mon, 6 Jul 2026 10:20:23 +0200 Subject: [PATCH 0624/1054] cgroup/cpuset: rebind mm mempolicy to effective_mems, not mems_allowed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Creating a child cpuset where cpuset.mems is never set leads to a div/0 when a VMA mempolicy with MPOL_F_RELATIVE_NODES rebinds in response to a CPU hotplug event. Reproduction steps: 1) Create a cgroup w/ cpuset controls (do not set cpuset.mems) 2) Move the task into the child cpuset 3) Create a VMA mempolicy for that task with MPOL_F_RELATIVE_NODES 4) unplug and hotplug a cpu echo 0 > /sys/devices/system/cpu/cpu1/online echo 1 > /sys/devices/system/cpu/cpu1/online 5) mempolicy rebind does a div/0 in mpol_relative_nodemask on the call to __nodes_fold() The cpuset code passes (cs->mems_allowed) which is not guaranteed to have nodes to the rebind routine. Use cs->effective_mems instead, which is guaranteed to have a non-empty nodemask once we reach that code path. Link: https://lore.kernel.org/all/CA+0ovCiEz6SP_sn3kN4Tb+_oC=eHMXy_Ffj=usV3wREdQrUtww@mail.gmail.com/ Fixes: ae1c802382f7 ("cpuset: apply cs->effective_{cpus,mems}") Closes: https://lore.kernel.org/linux-mm/CA+0ovCgxbZkXa+OU8w3s84R3KNPNxxRfmsNR-udh+afQBbGNmw@mail.gmail.com/ Suggested-by: Gregory Price Suggested-by: Waiman Long Acked-by: Waiman Long Signed-off-by: Farhad Alemi Cc: Andrew Morton Cc: Alistair Popple Cc: Byungchul Park Cc: Gregory Price Cc: "Huang, Ying" Cc: Joshua Hahn Cc: Matthew Brost Cc: Rakie Kim Cc: Rasmus Villemoes Cc: Zi Yan Cc: Tejun Heo Cc: Ridong Chen Cc: Johannes Weiner Cc: "Michal Koutný" Cc: [ david: add a comment, slightly rephrase description ] Signed-off-by: David Hildenbrand (Arm) Signed-off-by: Tejun Heo --- kernel/cgroup/cpuset.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c index 591e3aa487fc..45944b3e31ca 100644 --- a/kernel/cgroup/cpuset.c +++ b/kernel/cgroup/cpuset.c @@ -2653,7 +2653,12 @@ void cpuset_update_tasks_nodemask(struct cpuset *cs) migrate = is_memory_migrate(cs); - mpol_rebind_mm(mm, &cs->mems_allowed); + /* + * For v1 we can have empty effective_mems, but we cannot + * attach any tasks (see cpuset_can_attach_check()). For v2, + * effective_mems is guaranteed to not be empty. + */ + mpol_rebind_mm(mm, &cs->effective_mems); if (migrate) cpuset_migrate_mm(mm, &cs->old_mems_allowed, &newmems); else From cead34ac1ce10046cb745fbc33a4b21cac899753 Mon Sep 17 00:00:00 2001 From: Nick Desaulniers Date: Thu, 28 May 2026 14:38:41 -0700 Subject: [PATCH 0625/1054] MAINTAINERS: update ndesaulniers I'm coming back. I will return. I will possess your body, and I'll make LKML burn. Signed-off-by: Nick Desaulniers Acked-by: Nathan Chancellor Acked-by: Will Deacon Signed-off-by: Linus Torvalds --- .mailmap | 1 - Documentation/process/embargoed-hardware-issues.rst | 2 +- .../translations/sp_SP/process/embargoed-hardware-issues.rst | 2 +- MAINTAINERS | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.mailmap b/.mailmap index e7e639aeb23c..833ccaa583d6 100644 --- a/.mailmap +++ b/.mailmap @@ -642,7 +642,6 @@ Nicholas Piggin Nicholas Piggin Nicholas Piggin Nicholas Piggin -Nick Desaulniers Nicolas Ferre Nicolas Pitre Nicolas Pitre diff --git a/Documentation/process/embargoed-hardware-issues.rst b/Documentation/process/embargoed-hardware-issues.rst index 34e00848e0da..d07f16c3c7b8 100644 --- a/Documentation/process/embargoed-hardware-issues.rst +++ b/Documentation/process/embargoed-hardware-issues.rst @@ -308,7 +308,7 @@ an involved disclosed party. The current ambassadors list: Google Kees Cook - LLVM Nick Desaulniers + LLVM Nick Desaulniers ============= ======================================================== If you want your organization to be added to the ambassadors list, please diff --git a/Documentation/translations/sp_SP/process/embargoed-hardware-issues.rst b/Documentation/translations/sp_SP/process/embargoed-hardware-issues.rst index 9d444b9c46d3..7d4d694967c7 100644 --- a/Documentation/translations/sp_SP/process/embargoed-hardware-issues.rst +++ b/Documentation/translations/sp_SP/process/embargoed-hardware-issues.rst @@ -287,7 +287,7 @@ revelada involucrada. La lista de embajadores actuales: Google Kees Cook - LLVM Nick Desaulniers + LLVM Nick Desaulniers ============= ======================================================== Si quiere que su organización se añada a la lista de embajadores, por diff --git a/MAINTAINERS b/MAINTAINERS index 4a8b0fd665ce..57a7aef5d8cf 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6342,7 +6342,7 @@ F: .clang-format CLANG/LLVM BUILD SUPPORT M: Nathan Chancellor -R: Nick Desaulniers +R: Nick Desaulniers R: Bill Wendling R: Justin Stitt L: llvm@lists.linux.dev From 81bbcff0c053c4f5c711c31a9b72fc492bd96c3f Mon Sep 17 00:00:00 2001 From: Tao Liu Date: Mon, 6 Jul 2026 11:27:07 +1200 Subject: [PATCH 0626/1054] riscv: Prevent NULL pointer dereference in machine_kexec_prepare() A NULL pointer dereference issue is noticed in riscv's machine_kexec_prepare(), where image->segment[i].buf might be NULL and copied unchecked. The NULL buf comes from ima_add_kexec_buffer(), where kbuf is added by kexec_add_buffer(), but kbuf.buffer is NULL, then it is copied without a check in machine_kexec_prepare(): kexec_file_load -> kimage_file_alloc_init() -> kimage_file_prepare_segments() -> ima_add_kexec_buffer() -> kexec_add_buffer() -> machine_kexec_prepare() -> memcpy() Address this by adding a check before the data copy attempt. Fixes: b7fb4d78a6ad ("RISC-V: use memcpy for kexec_file mode") Cc: stable@vger.kernel.org Closes: https://lore.kernel.org/kexec/CAO7dBbVftLUhd2qrh7hmijTB3PEPfZAhykCGqEfrPoOcSrrj-w@mail.gmail.com/ Acked-by: Baoquan He Acked-by: Pratyush Yadav Reviewed-by: Nutty Liu Signed-off-by: Tao Liu Link: https://patch.msgid.link/20260705232706.30265-2-ltao@redhat.com Signed-off-by: Paul Walmsley --- arch/riscv/kernel/machine_kexec.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/riscv/kernel/machine_kexec.c b/arch/riscv/kernel/machine_kexec.c index 2306ce3e5f22..738df176ff6f 100644 --- a/arch/riscv/kernel/machine_kexec.c +++ b/arch/riscv/kernel/machine_kexec.c @@ -41,6 +41,9 @@ machine_kexec_prepare(struct kimage *image) if (image->segment[i].memsz <= sizeof(fdt)) continue; + if (!image->segment[i].buf) + continue; + if (image->file_mode) memcpy(&fdt, image->segment[i].buf, sizeof(fdt)); else if (copy_from_user(&fdt, image->segment[i].buf, sizeof(fdt))) From ca0676ae2e1a23ac3e858224fc630b99f9e216ea Mon Sep 17 00:00:00 2001 From: Tony Luck Date: Mon, 6 Jul 2026 15:46:21 -0700 Subject: [PATCH 0627/1054] fs/resctrl: Free mon_data structures on rdt_get_tree() failure If mkdir_mondata_all() or a subsequent call in rdt_get_tree() fails, the mon_data structures allocated by mon_get_kn_priv() are leaked. Add mon_put_kn_priv() to the out_mongrp error path to free the mon_data structures. Fixes: 2a6566038544 ("x86/resctrl: Expand the width of domid by replacing mon_data_bits") Closes: https://lore.kernel.org/lkml/5d38c1fb-8f91-472b-8897-24b2f50c772b@intel.com/ Reported-by: Reinette Chatre Signed-off-by: Tony Luck Signed-off-by: Reinette Chatre Signed-off-by: Borislav Petkov (AMD) Reviewed-by: Chen Yu Reviewed-by: Ben Horgan Cc: Link: https://patch.msgid.link/433623b7e3316ffd52323255d1aa4f156ad97cb1.1783377598.git.reinette.chatre@intel.com --- fs/resctrl/rdtgroup.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/resctrl/rdtgroup.c b/fs/resctrl/rdtgroup.c index af2cbab14497..1312b52c7f7b 100644 --- a/fs/resctrl/rdtgroup.c +++ b/fs/resctrl/rdtgroup.c @@ -74,6 +74,8 @@ static int rdtgroup_setup_root(struct rdt_fs_context *ctx); static void rdtgroup_destroy_root(void); +static void mon_put_kn_priv(void); + struct dentry *debugfs_resctrl; /* @@ -2893,6 +2895,7 @@ static int rdt_get_tree(struct fs_context *fc) kernfs_remove(kn_mondata); out_mongrp: if (resctrl_arch_mon_capable()) { + mon_put_kn_priv(); rdtgroup_unassign_cntrs(&rdtgroup_default); kernfs_remove(kn_mongrp); } From cd053d788c3f13b3eaf16672d427ee828fda16ed Mon Sep 17 00:00:00 2001 From: Hao-Yu Yang Date: Tue, 7 Jul 2026 02:33:04 +0800 Subject: [PATCH 0628/1054] io_uring: fix dangling iovec after provided-buffer bundle grow failure When growing a provided-buffer bundle, the old cached iovec is freed before the new buffers have all been validated. If validation fails, the request still points at the freed iovec, which can be freed again during completion cleanup. Fix this by deferring the free of the old cached iovec until validation has succeeded. On failure, free the newly allocated iovec and leave the request pointing at the original one. Fixes: 46800585ae04 ("io_uring/kbuf: validate ring provided buffer addresses with access_ok()") Signed-off-by: Hao-Yu Yang Link: https://patch.msgid.link/20260706183304.919275-1-naup96721@gmail.com Suggested-by: Jens Axboe Signed-off-by: Jens Axboe --- io_uring/kbuf.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/io_uring/kbuf.c b/io_uring/kbuf.c index 3cd29477fff2..b6b969b55e12 100644 --- a/io_uring/kbuf.c +++ b/io_uring/kbuf.c @@ -287,8 +287,6 @@ static int io_ring_buffers_peek(struct io_kiocb *req, struct buf_sel_arg *arg, iov = kmalloc_objs(struct iovec, nr_avail); if (unlikely(!iov)) return -ENOMEM; - if (arg->mode & KBUF_MODE_FREE) - kfree(arg->iovs); arg->iovs = iov; nr_iovs = nr_avail; } else if (nr_avail < nr_iovs) { @@ -330,6 +328,9 @@ static int io_ring_buffers_peek(struct io_kiocb *req, struct buf_sel_arg *arg, buf = io_ring_head_to_buf(br, ++head, bl->mask); } while (--nr_iovs); + if (arg->mode & KBUF_MODE_FREE) + kfree(arg->iovs); + if (head == tail) req->flags |= REQ_F_BL_EMPTY; From 52fce648607e0d6a76eeb443d78708c49df1c554 Mon Sep 17 00:00:00 2001 From: Tony Luck Date: Mon, 6 Jul 2026 15:46:22 -0700 Subject: [PATCH 0629/1054] fs/resctrl: Fix use-after-free during unmount During unmount or failure teardown all mon_data structures that contain monitoring event file private data are freed after which kernfs nodes are removed. However, the RDT_DELETED flag is never set for the statically allocated default resource group. A concurrent reader of an event file associated with the default resource group may, after dropping kernfs active protection, block on rdtgroup_mutex while unmount proceeds to free the file private data and destroy the kernfs node without waiting for the reader. When the mutex is released, the reader wakes up, observes that RDT_DELETED is not set for the default group, and dereferences the already-freed file private data. The scenario can be depicted as follows: CPU0 CPU1 /* * Default resource group's * monitoring data accessible via * kernfs file with kernfs_node::priv * pointing to a struct mon_data. * User opens the file for reading. */ rdtgroup_mondata_show() /* arch encounters fatal error */ rdtgroup_kn_lock_live() resctrl_exit() atomic_inc(&rdtgroup_default.waitcount) cpus_read_lock() kernfs_break_active_protection(kn) mutex_lock(&rdtgroup_mutex) cpus_read_lock() resctrl_fs_teardown() mutex_lock(&rdtgroup_mutex) rmdir_all_sub() mon_put_kn_priv() /* Delete all mon_data structures */ rdtgroup_destroy_root() kernfs_destroy_root() rdtgroup_default.kn = NULL mutex_unlock(&rdtgroup_mutex) /* * rdtgroup_default.flags is empty so * rdtgroup_kn_lock_live() returns * &rdtgroup_default */ md = of->kn->priv; /* md points to freed mon_data */ Set RDT_DELETED for the default group unconditionally since the flag does not lead to the freeing of this statically allocated group. Do not allow a new resctrl mount if there are any waiters on default group of previous mount. A new mount will re-initialize the default group that would appear to waiters from previous mount as though the default group is accessible causing them to access the mon_data structures from the previous mount that have been removed. Fixes: 2a6566038544 ("x86/resctrl: Expand the width of domid by replacing mon_data_bits") Closes: https://sashiko.dev/#/patchset/20260508182143.14592-1-tony.luck%40intel.com?part=2 [1] Reported-by: Sashiko Signed-off-by: Tony Luck Signed-off-by: Reinette Chatre Signed-off-by: Borislav Petkov (AMD) Reviewed-by: Chen Yu Cc: Link: https://patch.msgid.link/49a2ca3ca688f27e1a646cf90e1dc69287021127.1783377598.git.reinette.chatre@intel.com --- fs/resctrl/rdtgroup.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/fs/resctrl/rdtgroup.c b/fs/resctrl/rdtgroup.c index 1312b52c7f7b..ce277f16ea12 100644 --- a/fs/resctrl/rdtgroup.c +++ b/fs/resctrl/rdtgroup.c @@ -587,14 +587,20 @@ static ssize_t rdtgroup_cpus_write(struct kernfs_open_file *of, * * On resource group creation via a mkdir, an extra kernfs_node reference is * taken to ensure that the rdtgroup structure remains accessible for the - * rdtgroup_kn_unlock() calls where it is removed. + * rdtgroup_kn_unlock() calls where it is removed. The default group is + * statically allocated: it does not have an extra reference but will have + * RDT_DELETED set on unmount to support safe access to its associated files + * via rdtgroup_kn_lock_live/rdtgroup_kn_unlock(). * - * Drop the extra reference here, then free the rdtgroup structure. + * For all but the default group: drop the extra reference, then free the + * rdtgroup structure. * * Return: void */ static void rdtgroup_remove(struct rdtgroup *rdtgrp) { + if (rdtgrp == &rdtgroup_default) + return; kernfs_put(rdtgrp->kn); kfree(rdtgrp); } @@ -2814,6 +2820,12 @@ static int rdt_get_tree(struct fs_context *fc) goto out; } + /* Avoid races from pending operations from a previous mount */ + if (atomic_read(&rdtgroup_default.waitcount) != 0) { + ret = -EBUSY; + goto out; + } + ret = setup_rmid_lru_list(); if (ret) goto out; @@ -3177,6 +3189,7 @@ static void resctrl_fs_teardown(void) mon_put_kn_priv(); rdt_pseudo_lock_release(); rdtgroup_default.mode = RDT_MODE_SHAREABLE; + rdtgroup_default.flags = RDT_DELETED; closid_exit(); schemata_list_destroy(); rdtgroup_destroy_root(); @@ -4277,6 +4290,7 @@ static int rdtgroup_setup_root(struct rdt_fs_context *ctx) ctx->kfc.root = rdt_root; rdtgroup_default.kn = kernfs_root_to_node(rdt_root); + rdtgroup_default.flags = 0; return 0; } From b9f089723aee892efc77c349ae47a6b452b293c4 Mon Sep 17 00:00:00 2001 From: Reinette Chatre Date: Mon, 6 Jul 2026 15:46:23 -0700 Subject: [PATCH 0630/1054] fs/resctrl: Fix double-add of pseudo-locked region's RMID to free list A pseudo-locked group's RMID is freed when it is created. On unmount rmdir_all_sub() unconditionally frees all RMID of all groups, resulting in a double-free of the pseudo-locked group's RMID. The consequence of this is that the original free results in the pseudo-locked group's RMID being added to the rmid_free_lru linked list and the second free then attempts to add the same RMID entry to the rmid_free_lru again. Do not double-free a pseudo-locked group's RMID. Fixes: e0bdfe8e36f3 ("x86/intel_rdt: Support creation/removal of pseudo-locked region") Signed-off-by: Reinette Chatre Signed-off-by: Borislav Petkov (AMD) Cc: Link: https://patch.msgid.link/551432dd7e624a862b8e58314c38aaba0afff3e9.1783377598.git.reinette.chatre@intel.com --- fs/resctrl/rdtgroup.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/fs/resctrl/rdtgroup.c b/fs/resctrl/rdtgroup.c index ce277f16ea12..cc9966ff6cdf 100644 --- a/fs/resctrl/rdtgroup.c +++ b/fs/resctrl/rdtgroup.c @@ -3084,10 +3084,6 @@ static void rmdir_all_sub(void) if (rdtgrp == &rdtgroup_default) continue; - if (rdtgrp->mode == RDT_MODE_PSEUDO_LOCKSETUP || - rdtgrp->mode == RDT_MODE_PSEUDO_LOCKED) - rdtgroup_pseudo_lock_remove(rdtgrp); - /* * Give any CPUs back to the default group. We cannot copy * cpu_online_mask because a CPU might have executed the @@ -3098,7 +3094,13 @@ static void rmdir_all_sub(void) rdtgroup_unassign_cntrs(rdtgrp); - free_rmid(rdtgrp->closid, rdtgrp->mon.rmid); + if (rdtgrp->mode == RDT_MODE_PSEUDO_LOCKSETUP || + rdtgrp->mode == RDT_MODE_PSEUDO_LOCKED) { + rdtgroup_pseudo_lock_remove(rdtgrp); + } else { + /* Pseudo-locked group's RMID is freed during setup. */ + free_rmid(rdtgrp->closid, rdtgrp->mon.rmid); + } kernfs_remove(rdtgrp->kn); list_del(&rdtgrp->rdtgroup_list); From e35c9cf5512814fb04f369f2eada64f0a7164609 Mon Sep 17 00:00:00 2001 From: Lizhi Hou Date: Tue, 16 Jun 2026 14:24:29 -0700 Subject: [PATCH 0631/1054] accel/amdxdna: Prevent PM resume deadlock in hwctx_sync_debug_bo() amdxdna_hwctx_sync_debug_bo() invokes the hardware hwctx_sync_debug_bo() callback while holding xdna->dev_lock. The callback may call amdxdna_cmd_submit(), which in turn calls amdxdna_pm_resume_get(). If the device is suspended, amdxdna_pm_resume_get() may synchronously execute amdxdna_pm_resume(), which also acquires xdna->dev_lock, resulting in a deadlock. Avoid the deadlock by calling amdxdna_pm_resume_get() before holding xdna->dev_lock in both amdxdna_hwctx_sync_debug_bo() and amdxdna_drm_config_hwctx_ioctl() Fixes: 7ea046838021 ("accel/amdxdna: Support firmware debug buffer") Reviewed-by: Max Zhen Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/20260616212429.3620645-1-lizhi.hou@amd.com --- drivers/accel/amdxdna/aie2_ctx.c | 2 +- drivers/accel/amdxdna/amdxdna_ctx.c | 26 ++++++++++++++++++++++---- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/drivers/accel/amdxdna/aie2_ctx.c b/drivers/accel/amdxdna/aie2_ctx.c index 54486960cbf5..4fa9abd90cd7 100644 --- a/drivers/accel/amdxdna/aie2_ctx.c +++ b/drivers/accel/amdxdna/aie2_ctx.c @@ -875,7 +875,7 @@ static int aie2_hwctx_cu_config(struct amdxdna_hwctx *hwctx, void *buf, u32 size if (!hwctx->cus) return -ENOMEM; - ret = amdxdna_pm_resume_get_locked(xdna); + ret = amdxdna_pm_resume_get(xdna); if (ret) goto free_cus; diff --git a/drivers/accel/amdxdna/amdxdna_ctx.c b/drivers/accel/amdxdna/amdxdna_ctx.c index 855da8c79a1c..67a2abcf173e 100644 --- a/drivers/accel/amdxdna/amdxdna_ctx.c +++ b/drivers/accel/amdxdna/amdxdna_ctx.c @@ -382,16 +382,25 @@ int amdxdna_drm_config_hwctx_ioctl(struct drm_device *dev, void *data, struct dr return -EINVAL; } - guard(mutex)(&xdna->dev_lock); + ret = amdxdna_pm_resume_get(xdna); + if (ret) { + XDNA_ERR(xdna, "Resume failed, ret %d", ret); + goto free_buf; + } + + mutex_lock(&xdna->dev_lock); hwctx = xa_load(&client->hwctx_xa, args->handle); if (!hwctx) { XDNA_DBG(xdna, "PID %d failed to get hwctx %d", client->pid, args->handle); ret = -EINVAL; - goto free_buf; + goto unlock; } ret = xdna->dev_info->ops->hwctx_config(hwctx, args->param_type, val, buf, buf_size); +unlock: + mutex_unlock(&xdna->dev_lock); + amdxdna_pm_suspend_put(xdna); free_buf: kfree(buf); return ret; @@ -412,16 +421,25 @@ int amdxdna_hwctx_sync_debug_bo(struct amdxdna_client *client, u32 debug_bo_hdl) if (!gobj) return -EINVAL; + ret = amdxdna_pm_resume_get(xdna); + if (ret) { + XDNA_ERR(xdna, "Resume failed, ret %d", ret); + goto put_obj; + } + abo = to_xdna_obj(gobj); - guard(mutex)(&xdna->dev_lock); + mutex_lock(&xdna->dev_lock); hwctx = xa_load(&client->hwctx_xa, abo->assigned_hwctx); if (!hwctx) { ret = -EINVAL; - goto put_obj; + goto unlock; } ret = xdna->dev_info->ops->hwctx_sync_debug_bo(hwctx, debug_bo_hdl); +unlock: + mutex_unlock(&xdna->dev_lock); + amdxdna_pm_suspend_put(xdna); put_obj: drm_gem_object_put(gobj); return ret; From 18aaebdf43366954345a6721ed2bff3ac3c9fa61 Mon Sep 17 00:00:00 2001 From: Lizhi Hou Date: Mon, 15 Jun 2026 23:15:32 -0700 Subject: [PATCH 0632/1054] accel/amdxdna: Use unsigned long for nr_pages in amdxdna_hmm_register() nr_pages is declared as u32 in amdxdna_hmm_register(), which may not be large enough to represent the number of pages for large mappings. Use unsigned long for nr_pages to avoid potential overflow. Fixes: ac49797c1815 ("accel/amdxdna: Add GEM buffer object management") Reviewed-by: Max Zhen Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/20260616061532.3533469-1-lizhi.hou@amd.com --- drivers/accel/amdxdna/amdxdna_gem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/accel/amdxdna/amdxdna_gem.c b/drivers/accel/amdxdna/amdxdna_gem.c index 45abd6a804cc..3afa5ffff93f 100644 --- a/drivers/accel/amdxdna/amdxdna_gem.c +++ b/drivers/accel/amdxdna/amdxdna_gem.c @@ -346,7 +346,7 @@ static int amdxdna_hmm_register(struct amdxdna_gem_obj *abo, unsigned long len = vma->vm_end - vma->vm_start; unsigned long addr = vma->vm_start; struct amdxdna_umap *mapp; - u32 nr_pages; + unsigned long nr_pages; int ret; if (!amdxdna_pasid_on(abo->client)) { From 1ba02717e821cf14ece642273958647e79698d3d Mon Sep 17 00:00:00 2001 From: Lizhi Hou Date: Mon, 8 Jun 2026 18:12:42 -0700 Subject: [PATCH 0633/1054] accel/amdxdna: Fix VMA access race aie2_populate_range() and amdxdna_umap_release() access a saved VMA pointer that may have already been freed, leading to a potential use-after-free. Remove the VMA accesses from these functions to avoid the race. Fixes: e486147c912f ("accel/amdxdna: Add BO import and export") Reviewed-by: Max Zhen Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/20260609011242.2833740-1-lizhi.hou@amd.com --- drivers/accel/amdxdna/aie2_ctx.c | 2 -- drivers/accel/amdxdna/amdxdna_gem.c | 31 +++++++++++++++++++---------- drivers/accel/amdxdna/amdxdna_gem.h | 1 - 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/drivers/accel/amdxdna/aie2_ctx.c b/drivers/accel/amdxdna/aie2_ctx.c index 4fa9abd90cd7..408ff7e2a272 100644 --- a/drivers/accel/amdxdna/aie2_ctx.c +++ b/drivers/accel/amdxdna/aie2_ctx.c @@ -1053,8 +1053,6 @@ static int aie2_populate_range(struct amdxdna_gem_obj *abo) kref_get(&mapp->refcnt); up_write(&xdna->notifier_lock); - XDNA_DBG(xdna, "populate memory range %lx %lx", - mapp->vma->vm_start, mapp->vma->vm_end); mm = mapp->notifier.mm; if (!mmget_not_zero(mm)) { amdxdna_umap_put(mapp); diff --git a/drivers/accel/amdxdna/amdxdna_gem.c b/drivers/accel/amdxdna/amdxdna_gem.c index 3afa5ffff93f..0c10ec0cc5e4 100644 --- a/drivers/accel/amdxdna/amdxdna_gem.c +++ b/drivers/accel/amdxdna/amdxdna_gem.c @@ -254,7 +254,7 @@ static bool amdxdna_hmm_invalidate(struct mmu_interval_notifier *mni, xdna = to_xdna_dev(to_gobj(abo)->dev); XDNA_DBG(xdna, "Invalidating range 0x%lx, 0x%lx, type %d", - mapp->vma->vm_start, mapp->vma->vm_end, abo->type); + mapp->range.start, mapp->range.end, abo->type); if (!mmu_notifier_range_blockable(range)) return false; @@ -284,15 +284,23 @@ static const struct mmu_interval_notifier_ops amdxdna_hmm_ops = { .invalidate = amdxdna_hmm_invalidate, }; +static inline bool compare_range(struct amdxdna_umap *mapp, + struct mm_struct *mm, + unsigned long start, unsigned long end) +{ + return (!mapp->unmapped && mapp->notifier.mm == mm && + mapp->range.start == start && mapp->range.end == end); +} + static void amdxdna_hmm_unregister(struct amdxdna_gem_obj *abo, struct vm_area_struct *vma) { struct amdxdna_dev *xdna = to_xdna_dev(to_gobj(abo)->dev); struct amdxdna_umap *mapp; - down_read(&xdna->notifier_lock); + down_write(&xdna->notifier_lock); list_for_each_entry(mapp, &abo->mem.umap_list, node) { - if (!vma || mapp->vma == vma) { + if (!vma || compare_range(mapp, vma->vm_mm, vma->vm_start, vma->vm_end)) { if (!mapp->unmapped) { queue_work(xdna->notifier_wq, &mapp->hmm_unreg_work); mapp->unmapped = true; @@ -301,19 +309,16 @@ static void amdxdna_hmm_unregister(struct amdxdna_gem_obj *abo, break; } } - up_read(&xdna->notifier_lock); + up_write(&xdna->notifier_lock); } static void amdxdna_umap_release(struct kref *ref) { struct amdxdna_umap *mapp = container_of(ref, struct amdxdna_umap, refcnt); struct amdxdna_gem_obj *abo = mapp->abo; - struct vm_area_struct *vma = mapp->vma; struct amdxdna_dev *xdna; mmu_interval_notifier_remove(&mapp->notifier); - if (is_import_bo(abo) && vma->vm_file && vma->vm_file->f_mapping) - mapping_clear_unevictable(vma->vm_file->f_mapping); xdna = to_xdna_dev(to_gobj(mapp->abo)->dev); down_write(&xdna->notifier_lock); @@ -355,6 +360,15 @@ static int amdxdna_hmm_register(struct amdxdna_gem_obj *abo, return 0; } + down_read(&xdna->notifier_lock); + list_for_each_entry(mapp, &abo->mem.umap_list, node) { + if (compare_range(mapp, current->mm, addr, addr + len)) { + up_read(&xdna->notifier_lock); + return 0; + } + } + up_read(&xdna->notifier_lock); + mapp = kzalloc_obj(*mapp); if (!mapp) return -ENOMEM; @@ -380,13 +394,10 @@ static int amdxdna_hmm_register(struct amdxdna_gem_obj *abo, mapp->range.start = vma->vm_start; mapp->range.end = vma->vm_end; mapp->range.default_flags = HMM_PFN_REQ_FAULT; - mapp->vma = vma; mapp->abo = abo; kref_init(&mapp->refcnt); INIT_WORK(&mapp->hmm_unreg_work, amdxdna_hmm_unreg_work); - if (is_import_bo(abo) && vma->vm_file && vma->vm_file->f_mapping) - mapping_set_unevictable(vma->vm_file->f_mapping); down_write(&xdna->notifier_lock); if (list_empty(&abo->mem.umap_list)) diff --git a/drivers/accel/amdxdna/amdxdna_gem.h b/drivers/accel/amdxdna/amdxdna_gem.h index a3e44c7a2395..a35d2f15d32c 100644 --- a/drivers/accel/amdxdna/amdxdna_gem.h +++ b/drivers/accel/amdxdna/amdxdna_gem.h @@ -12,7 +12,6 @@ #include "amdxdna_pci_drv.h" struct amdxdna_umap { - struct vm_area_struct *vma; struct mmu_interval_notifier notifier; struct hmm_range range; struct work_struct hmm_unreg_work; From f3858d5b1432098c1936e03d6e03dd0e33facf60 Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Mon, 6 Jul 2026 22:08:41 +0800 Subject: [PATCH 0634/1054] wifi: mac80211: free AP_VLAN bc_buf SKBs outside IRQ lock ieee80211_do_stop() removes AP_VLAN packets from the parent AP ps->bc_buf while holding ps->bc_buf.lock with IRQs disabled. It then calls ieee80211_free_txskb() before dropping the lock. ieee80211_free_txskb() is not just a passive SKB release. For SKBs with TX status state it can report a dropped frame through cfg80211/nl80211, and that path can reach netlink tap transmit. This is the same reason the pending queue cleanup in ieee80211_do_stop() already unlinks SKBs under the queue lock and frees them after IRQ state is restored. The buggy scenario involves two paths, with each column showing the order within that path: AP_VLAN management TX: AP_VLAN stop: 1. attach ACK-status state 1. clear the running state 2. queue a multicast SKB on 2. take ps->bc_buf.lock with IRQs parent ps->bc_buf disabled 3. unlink the AP_VLAN SKB 4. call ieee80211_free_txskb() Unlink matching AP_VLAN SKBs from ps->bc_buf under the existing lock, but move them to a local free queue. Drop the lock and restore IRQ state before calling ieee80211_free_txskb(). WARNING: kernel/softirq.c:430 at __local_bh_enable_ip Fixes: 397a7a24ef8c ("mac80211: free ps->bc_buf skbs on vlan device stop") Assisted-by: Codex:gpt-5.5 Signed-off-by: Cen Zhang Link: https://patch.msgid.link/20260706140841.581566-1-zzzccc427@gmail.com Signed-off-by: Johannes Berg --- net/mac80211/iface.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 086272c3ec08..43460a705a6b 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -588,6 +588,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do WARN_ON(!list_empty(&sdata->u.ap.vlans)); } else if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { /* remove all packets in parent bc_buf pointing to this dev */ + __skb_queue_head_init(&freeq); ps = &sdata->bss->ps; spin_lock_irqsave(&ps->bc_buf.lock, flags); @@ -595,10 +596,15 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do if (skb->dev == sdata->dev) { __skb_unlink(skb, &ps->bc_buf); local->total_ps_buffered--; - ieee80211_free_txskb(&local->hw, skb); + __skb_queue_tail(&freeq, skb); } } spin_unlock_irqrestore(&ps->bc_buf.lock, flags); + + skb_queue_walk_safe(&freeq, skb, tmp) { + __skb_unlink(skb, &freeq); + ieee80211_free_txskb(&local->hw, skb); + } } if (going_down) From 95fc02722edde02946d0d475221f2b2054d3d8ba Mon Sep 17 00:00:00 2001 From: Dawei Feng Date: Mon, 6 Jul 2026 22:35:07 +0800 Subject: [PATCH 0635/1054] wifi: mac80211: fix memory leak in ieee80211_register_hw() If kmemdup() fails while copying supported band structures, the error path jumps to fail_rate. This skips rate_control_deinitialize() and leaks the initialized local->rate_ctrl. Fix this by adding a fail_band label that shares the rate-control cleanup path before falling through to the remaining teardown. 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-rc7. An x86_64 allyesconfig build showed no new warnings. As we do not have a suitable mac80211 device/driver combination to test with, no runtime testing was able to be performed. Fixes: 09b4a4faf9d0 ("mac80211: introduce capability flags for VHT EXT NSS support") Cc: stable@vger.kernel.org Reviewed-by: Zilin Guan Signed-off-by: Dawei Feng Link: https://patch.msgid.link/20260706143507.146131-1-dawei.feng@seu.edu.cn Signed-off-by: Johannes Berg --- net/mac80211/main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 90d295cc364f..eb1eaaf34612 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -1602,7 +1602,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) sband = kmemdup(sband, sizeof(*sband), GFP_KERNEL); if (!sband) { result = -ENOMEM; - goto fail_rate; + goto fail_band; } wiphy_dbg(hw->wiphy, "copying sband (band %d) due to VHT EXT NSS BW flag\n", @@ -1678,6 +1678,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) #endif wiphy_unregister(local->hw.wiphy); fail_wiphy_register: + fail_band: rtnl_lock(); rate_control_deinitialize(local); ieee80211_remove_interfaces(local); From 0c2ed186bbe14304415476d6707b747dddcd8583 Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Mon, 6 Jul 2026 23:24:18 +0800 Subject: [PATCH 0636/1054] wifi: cfg80211: use wiphy work for socket owner autodisconnect nl80211_netlink_notify() walks the cfg80211 wireless device list when a NETLINK_GENERIC socket is released. If the socket owns a connection, the notifier queues the embedded wdev->disconnect_wk work item. That work is a plain work_struct today. NETDEV_GOING_DOWN cancels it, but a NETLINK_URELEASE notifier that already observed conn_owner_nlportid can queue it after that cancel returns. _cfg80211_unregister_wdev() then removes the wdev from the list and waits for RCU readers, but synchronize_net() does not drain work queued by such a reader. Make the autodisconnect work a wiphy_work instead. The callback already needs the wiphy mutex, and wiphy_work runs under that mutex. This lets teardown cancel pending autodisconnect work while holding the mutex, without a cancel_work_sync() vs. worker locking concern. Also cancel the wiphy work after list_del_rcu() and synchronize_net(). Any NETLINK_URELEASE notifier that had already reached the wdev list has then either queued the work and it is removed, or can no longer find the wdev. Fixes: bd2522b16884 ("cfg80211: NL80211_ATTR_SOCKET_OWNER support for CMD_CONNECT") Suggested-by: Johannes Berg Assisted-by: Codex:gpt-5.5 Signed-off-by: Cen Zhang Link: https://patch.msgid.link/20260706152418.779226-1-zzzccc427@gmail.com Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 2 +- net/wireless/core.c | 10 ++++++---- net/wireless/core.h | 2 +- net/wireless/nl80211.c | 3 ++- net/wireless/sme.c | 6 ++---- 5 files changed, 12 insertions(+), 11 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 3751a1d74765..f5abf1db7558 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -7228,7 +7228,7 @@ struct wireless_dev { enum ieee80211_bss_type conn_bss_type; u32 conn_owner_nlportid; - struct work_struct disconnect_wk; + struct wiphy_work disconnect_wk; u8 disconnect_bssid[ETH_ALEN]; struct list_head event_list; diff --git a/net/wireless/core.c b/net/wireless/core.c index 082f0ee12f1b..610238d723ff 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -1425,6 +1425,7 @@ static void _cfg80211_unregister_wdev(struct wireless_dev *wdev, list_del_rcu(&wdev->list); synchronize_net(); rdev->devlist_generation++; + wiphy_work_cancel(wdev->wiphy, &wdev->disconnect_wk); cfg80211_mlme_purge_registrations(wdev); @@ -1638,7 +1639,7 @@ void cfg80211_init_wdev(struct wireless_dev *wdev) wdev->iftype == NL80211_IFTYPE_ADHOC) && !wdev->use_4addr) wdev->netdev->priv_flags |= IFF_DONT_BRIDGE; - INIT_WORK(&wdev->disconnect_wk, cfg80211_autodisconnect_wk); + wiphy_work_init(&wdev->disconnect_wk, cfg80211_autodisconnect_wk); } void cfg80211_register_wdev(struct cfg80211_registered_device *rdev, @@ -1744,10 +1745,11 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, break; case NETDEV_GOING_DOWN: cfg80211_leave(rdev, wdev, -1); - scoped_guard(wiphy, &rdev->wiphy) + scoped_guard(wiphy, &rdev->wiphy) { cfg80211_remove_links(wdev); - /* since we just did cfg80211_leave() nothing to do there */ - cancel_work_sync(&wdev->disconnect_wk); + /* since we just did cfg80211_leave() nothing to do there */ + wiphy_work_cancel(wdev->wiphy, &wdev->disconnect_wk); + } break; case NETDEV_DOWN: wiphy_lock(&rdev->wiphy); diff --git a/net/wireless/core.h b/net/wireless/core.h index f60c66b88677..ac6ce9f967ec 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -428,7 +428,7 @@ void __cfg80211_port_authorized(struct wireless_dev *wdev, const u8 *peer_addr, const u8 *td_bitmap, u8 td_bitmap_len); int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev); -void cfg80211_autodisconnect_wk(struct work_struct *work); +void cfg80211_autodisconnect_wk(struct wiphy *wiphy, struct wiphy_work *work); /* SME implementation */ void cfg80211_conn_work(struct work_struct *work); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 625c99cf70a3..5adcb6bd0fc5 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -22954,7 +22954,8 @@ static int nl80211_netlink_notify(struct notifier_block * nb, wdev->nl_owner_dead = true; schedule_work(&rdev->destroy_work); } else if (wdev->conn_owner_nlportid == notify->portid) { - schedule_work(&wdev->disconnect_wk); + wiphy_work_queue(wdev->wiphy, + &wdev->disconnect_wk); } cfg80211_release_pmsr(wdev, notify->portid); diff --git a/net/wireless/sme.c b/net/wireless/sme.c index b451df3096dd..2a719b5c487e 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -1578,13 +1578,11 @@ int cfg80211_disconnect(struct cfg80211_registered_device *rdev, * Used to clean up after the connection / connection attempt owner socket * disconnects */ -void cfg80211_autodisconnect_wk(struct work_struct *work) +void cfg80211_autodisconnect_wk(struct wiphy *wiphy, struct wiphy_work *work) { struct wireless_dev *wdev = container_of(work, struct wireless_dev, disconnect_wk); - struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); - - guard(wiphy)(wdev->wiphy); + struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); if (wdev->conn_owner_nlportid) { switch (wdev->iftype) { From 4e72459683b5185568e9ffe2584a7b834f7902b5 Mon Sep 17 00:00:00 2001 From: Shahar Tzarfati Date: Mon, 6 Jul 2026 22:27:52 +0300 Subject: [PATCH 0637/1054] wifi: mac80211: recalculate rx_nss on IBSS peer capability update When IBSS peer capabilities change, rates_updated is set to true in ieee80211_update_sta_info(), but rx_nss is never recalculated. For peers with HT/VHT, this leaves rx_nss at 0 instead of the correct value, causing drivers to use incorrect rate scaling parameters. The root cause is that the commit below moved NSS initialisation out of rate_control_rate_init() into explicit call sites, but missing the rates_updated path in ieee80211_update_sta_info(). Fix this by calling ieee80211_sta_init_nss_bw_capa() before rate_control_rate_init() when peer capabilities are updated, consistent with the other IBSS call sites added by that commit. Fixes: e5ad38a9b261 ("wifi: mac80211: clean up STA NSS handling") Signed-off-by: Shahar Tzarfati Reviewed-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260706222724.422adfd57b71.I5a47f65c5e38a221712f5203e5c8040304b382b5@changeid Signed-off-by: Johannes Berg --- net/mac80211/ibss.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index d0fd6054f182..1e5414ee27c0 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -1029,8 +1029,8 @@ static void ieee80211_update_sta_info(struct ieee80211_sub_if_data *sdata, u32 changed = IEEE80211_RC_SUPP_RATES_CHANGED; u8 rx_nss = sta->sta.deflink.rx_nss; - /* Force rx_nss recalculation */ - sta->sta.deflink.rx_nss = 0; + ieee80211_sta_init_nss_bw_capa(&sta->deflink, + &sdata->deflink.conf->chanreq.oper); rate_control_rate_init(&sta->deflink); if (sta->sta.deflink.rx_nss != rx_nss) changed |= IEEE80211_RC_NSS_CHANGED; From d0e69d9afa59b93c30294eba89b1f15f69e91105 Mon Sep 17 00:00:00 2001 From: Pagadala Yesu Anjaneyulu Date: Mon, 6 Jul 2026 22:37:56 +0300 Subject: [PATCH 0638/1054] wifi: mac80211: ibss: wait for in-flight TX on disconnect While leaving an IBSS in ieee80211_ibss_disconnect() mac80211 flushes stations, turns the carrier off and immediately tells the driver to leave as well. While there may be synchronize_net() in station flush and in this code later, packets can still be transmitted due to cross-CPU race conditions after carrier off is set. Therefore, it's possible for a race to happen where a TX to the driver occurs while or after telling it to leave the IBSS. This can be confusing to drivers, and in the case of iwlwifi leads to an attempt to use invalid queues. Move netif_carrier_off() to occur before sta_info_flush() during IBSS disconnect, and add synchronize_net() if flushing didn't, so that the synchronize_net() always happens between turning the carrier off and telling the driver, avoiding this race. Signed-off-by: Pagadala Yesu Anjaneyulu Reviewed-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260706223751.da1ce439cc93.If5cf482f87ab98ce66dd48724e24c81fed236d3f@changeid Signed-off-by: Johannes Berg --- net/mac80211/ibss.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 1e5414ee27c0..882f91abbb66 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -668,7 +668,9 @@ static void ieee80211_ibss_disconnect(struct ieee80211_sub_if_data *sdata) ifibss->state = IEEE80211_IBSS_MLME_SEARCH; - sta_info_flush(sdata, -1); + netif_carrier_off(sdata->dev); + if (!sta_info_flush(sdata, -1)) + synchronize_net(); spin_lock_bh(&ifibss->incomplete_lock); while (!list_empty(&ifibss->incomplete_stations)) { @@ -682,8 +684,6 @@ static void ieee80211_ibss_disconnect(struct ieee80211_sub_if_data *sdata) } spin_unlock_bh(&ifibss->incomplete_lock); - netif_carrier_off(sdata->dev); - sdata->vif.cfg.ibss_joined = false; sdata->vif.cfg.ibss_creator = false; sdata->vif.bss_conf.enable_beacon = false; @@ -710,7 +710,6 @@ static void ieee80211_csa_connection_drop_work(struct wiphy *wiphy, u.ibss.csa_connection_drop_work); ieee80211_ibss_disconnect(sdata); - synchronize_rcu(); skb_queue_purge(&sdata->skb_queue); /* trigger a scan to find another IBSS network to join */ @@ -1797,8 +1796,6 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata) memset(&ifibss->ht_capa, 0, sizeof(ifibss->ht_capa)); memset(&ifibss->ht_capa_mask, 0, sizeof(ifibss->ht_capa_mask)); - synchronize_rcu(); - skb_queue_purge(&sdata->skb_queue); timer_delete_sync(&sdata->u.ibss.timer); From d5e4586546974179feca305a94e07fac3e9727fe Mon Sep 17 00:00:00 2001 From: Zhao Li Date: Tue, 7 Jul 2026 10:53:34 +0800 Subject: [PATCH 0639/1054] wifi: cfg80211: validate rx/tx MLME callback frame lengths before access cfg80211_rx_mlme_mgmt() and cfg80211_tx_mlme_mgmt() call tracepoints before rejecting frames shorter than the frame-control field. After that, they only require len >= 2 before dispatching into subtype handlers that assume their fixed fields are present. The frames that trip this are not shorter than 2 bytes; they are short relative to their subtype. mwifiex is a concrete in-tree example on the length side: mwifiex_process_mgmt_packet() only requires a 4-address ieee80211_hdr plus the 2-byte firmware length prefix before handing the frame to cfg80211_rx_mlme_mgmt(). After stripping the length prefix and removing addr4, pkt_len can be exactly 24: a bare 3-address management header with no reason-code body. The existing WARN_ON(len < 2) does not fire on such a frame, and cfg80211_process_deauth() then reads u.deauth.reason_code as a two-byte access starting at offset 24, immediately past the 24-byte buffer. Add a frame-control length gate, then validate each subtype's minimum frame size in an if/else-if chain that mirrors the dispatch logic. Trace only after the frame is known to be well-formed. Side effects of this change: - The WARN_ON(len < 2) is dropped. It only guarded the frame_control read, never the subtype fixed fields, and it does not fire on the frames that actually trigger the out-of-bounds read (which are >= 2). The len >= 2 check is kept as the guard before dereferencing frame_control, but without the warning: these are exported callbacks and a malformed frame from a driver should be dropped silently rather than backtraced. - cfg80211_tx_mlme_mgmt() previously routed every non-deauth subtype through disassociation handling; it now silently ignores unrecognised subtypes. Assisted-by: Codex:gpt-5.5 Assisted-by: Claude:claude-opus-4.8 Signed-off-by: Zhao Li Link: https://patch.msgid.link/20260707025336.22557-1-enderaoelyther@gmail.com Signed-off-by: Johannes Berg --- net/wireless/mlme.c | 49 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 39 insertions(+), 10 deletions(-) diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 2a2c173058ba..acc749eee2fe 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -151,19 +151,35 @@ void cfg80211_rx_mlme_mgmt(struct net_device *dev, const u8 *buf, size_t len) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct ieee80211_mgmt *mgmt = (void *)buf; + __le16 fc; lockdep_assert_wiphy(wdev->wiphy); - trace_cfg80211_rx_mlme_mgmt(dev, buf, len); - - if (WARN_ON(len < 2)) + if (len < sizeof(fc)) return; - if (ieee80211_is_auth(mgmt->frame_control)) + fc = mgmt->frame_control; + + if (ieee80211_is_auth(fc)) { + if (len < offsetofend(struct ieee80211_mgmt, u.auth.status_code)) + return; + } else if (ieee80211_is_deauth(fc)) { + if (len < offsetofend(struct ieee80211_mgmt, u.deauth.reason_code)) + return; + } else if (ieee80211_is_disassoc(fc)) { + if (len < offsetofend(struct ieee80211_mgmt, u.disassoc.reason_code)) + return; + } else { + return; + } + + trace_cfg80211_rx_mlme_mgmt(dev, buf, len); + + if (ieee80211_is_auth(fc)) cfg80211_process_auth(wdev, buf, len); - else if (ieee80211_is_deauth(mgmt->frame_control)) + else if (ieee80211_is_deauth(fc)) cfg80211_process_deauth(wdev, buf, len, false); - else if (ieee80211_is_disassoc(mgmt->frame_control)) + else cfg80211_process_disassoc(wdev, buf, len, false); } EXPORT_SYMBOL(cfg80211_rx_mlme_mgmt); @@ -216,15 +232,28 @@ void cfg80211_tx_mlme_mgmt(struct net_device *dev, const u8 *buf, size_t len, { struct wireless_dev *wdev = dev->ieee80211_ptr; struct ieee80211_mgmt *mgmt = (void *)buf; + __le16 fc; lockdep_assert_wiphy(wdev->wiphy); - trace_cfg80211_tx_mlme_mgmt(dev, buf, len, reconnect); - - if (WARN_ON(len < 2)) + if (len < sizeof(fc)) return; - if (ieee80211_is_deauth(mgmt->frame_control)) + fc = mgmt->frame_control; + + if (ieee80211_is_deauth(fc)) { + if (len < offsetofend(struct ieee80211_mgmt, u.deauth.reason_code)) + return; + } else if (ieee80211_is_disassoc(fc)) { + if (len < offsetofend(struct ieee80211_mgmt, u.disassoc.reason_code)) + return; + } else { + return; + } + + trace_cfg80211_tx_mlme_mgmt(dev, buf, len, reconnect); + + if (ieee80211_is_deauth(fc)) cfg80211_process_deauth(wdev, buf, len, reconnect); else cfg80211_process_disassoc(wdev, buf, len, reconnect); From b760113aeca2e9362d56bf9e9263373ffe6c8eb3 Mon Sep 17 00:00:00 2001 From: Zhao Li Date: Tue, 7 Jul 2026 10:53:35 +0800 Subject: [PATCH 0640/1054] wifi: cfg80211: validate assoc response length before status and IE access cfg80211_rx_assoc_resp() initialises the status and response-IE fields of cfg80211_connect_resp_params from the management frame before proving that the frame is long enough for those offsets. S1G and regular association responses also have different IE offsets, but the S1G path only patched resp_ie after the unsafe initialiser had already run. Defer resp_ie, resp_ie_len, and status to after the link-iteration loop. Use a bool to remember whether the frame is S1G, then validate the appropriate minimum length and set all three fields in a single if/else block. Funnel short-frame and SME-reject cleanup through a shared free_bss label for the abandon paths. Assisted-by: Codex:gpt-5.5 Assisted-by: Claude:claude-opus-4.8 Signed-off-by: Zhao Li Link: https://patch.msgid.link/20260707025336.22557-2-enderaoelyther@gmail.com Signed-off-by: Johannes Berg --- net/wireless/mlme.c | 56 ++++++++++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 21 deletions(-) diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index acc749eee2fe..7824b7ac2770 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -32,15 +32,11 @@ void cfg80211_rx_assoc_resp(struct net_device *dev, .timeout_reason = NL80211_TIMEOUT_UNSPECIFIED, .req_ie = data->req_ies, .req_ie_len = data->req_ies_len, - .resp_ie = mgmt->u.assoc_resp.variable, - .resp_ie_len = data->len - - offsetof(struct ieee80211_mgmt, - u.assoc_resp.variable), - .status = le16_to_cpu(mgmt->u.assoc_resp.status_code), .ap_mld_addr = data->ap_mld_addr, .assoc_encrypted = data->assoc_encrypted, }; unsigned int link_id; + bool is_s1g = false; for (link_id = 0; link_id < ARRAY_SIZE(data->links); link_id++) { cr.links[link_id].status = data->links[link_id].status; @@ -61,16 +57,32 @@ void cfg80211_rx_assoc_resp(struct net_device *dev, if (cr.links[link_id].bss->channel->band == NL80211_BAND_S1GHZ) { WARN_ON(link_id); - cr.resp_ie = (u8 *)&mgmt->u.s1g_assoc_resp.variable; - cr.resp_ie_len = data->len - - offsetof(struct ieee80211_mgmt, - u.s1g_assoc_resp.variable); + is_s1g = true; } if (cr.ap_mld_addr) cr.valid_links |= BIT(link_id); } + if (is_s1g) { + if (data->len < offsetof(struct ieee80211_mgmt, + u.s1g_assoc_resp.variable)) + goto free_bss; + cr.resp_ie = (u8 *)&mgmt->u.s1g_assoc_resp.variable; + cr.resp_ie_len = data->len - + offsetof(struct ieee80211_mgmt, + u.s1g_assoc_resp.variable); + } else { + if (data->len < offsetof(struct ieee80211_mgmt, + u.assoc_resp.variable)) + goto free_bss; + cr.resp_ie = mgmt->u.assoc_resp.variable; + cr.resp_ie_len = data->len - + offsetof(struct ieee80211_mgmt, + u.assoc_resp.variable); + } + cr.status = le16_to_cpu(mgmt->u.assoc_resp.status_code); + trace_cfg80211_send_rx_assoc(dev, data); /* @@ -79,22 +91,24 @@ void cfg80211_rx_assoc_resp(struct net_device *dev, * and got a reject -- we only try again with an assoc * frame instead of reassoc. */ - if (cfg80211_sme_rx_assoc_resp(wdev, cr.status)) { - for (link_id = 0; link_id < ARRAY_SIZE(data->links); link_id++) { - struct cfg80211_bss *bss = data->links[link_id].bss; - - if (!bss) - continue; - - cfg80211_unhold_bss(bss_from_pub(bss)); - cfg80211_put_bss(wiphy, bss); - } - return; - } + if (cfg80211_sme_rx_assoc_resp(wdev, cr.status)) + goto free_bss; nl80211_send_rx_assoc(rdev, dev, data); /* update current_bss etc., consumes the bss reference */ __cfg80211_connect_result(dev, &cr, cr.status == WLAN_STATUS_SUCCESS); + return; + +free_bss: + for (link_id = 0; link_id < ARRAY_SIZE(data->links); link_id++) { + struct cfg80211_bss *bss = data->links[link_id].bss; + + if (!bss) + continue; + + cfg80211_unhold_bss(bss_from_pub(bss)); + cfg80211_put_bss(wiphy, bss); + } } EXPORT_SYMBOL(cfg80211_rx_assoc_resp); From 5b4396e12cb10fa89ca0a272fae2b5408cfeaa91 Mon Sep 17 00:00:00 2001 From: Weiming Shi Date: Thu, 2 Jul 2026 09:19:58 -0700 Subject: [PATCH 0641/1054] xfs: drop ASSERT(0) on unrecognized log item type The item type passed to ITEM_TYPE() comes from the on-disk log, so a fuzzed or crafted image can reach the "unrecognized type" path in xlog_recover_reorder_trans() and trip its ASSERT(0) on a CONFIG_XFS_DEBUG kernel. The -EFSCORRUPTED return handles it fine; drop the assert. Reviewed-by: Darrick J. Wong Reviewed-by: Christoph Hellwig Signed-off-by: Weiming Shi Reviewed-by: "Darrick J. Wong" Signed-off-by: Carlos Maiolino --- fs/xfs/xfs_log_recover.c | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c index 5f984bf5698a..a1b373c68f0e 100644 --- a/fs/xfs/xfs_log_recover.c +++ b/fs/xfs/xfs_log_recover.c @@ -1912,7 +1912,6 @@ xlog_recover_reorder_trans( xfs_warn(log->l_mp, "%s: unrecognized type of log operation (%d)", __func__, ITEM_TYPE(item)); - ASSERT(0); /* * return the remaining items back to the transaction * item list so they can be freed in caller. From 34dd2253414d228fc9bf8a70b5f9f83107eda3e1 Mon Sep 17 00:00:00 2001 From: Weiming Shi Date: Thu, 2 Jul 2026 09:19:59 -0700 Subject: [PATCH 0642/1054] xfs: splice unsorted log items back to the transaction after the loop On error, xlog_recover_reorder_trans() splices the leftover sort_list items back to trans->r_itemq inside the loop before breaking out. The loop tail already splices the per-fate lists back, so do sort_list there too, guarded by the assert that used to sit after the loop. No functional change. It drops the duplicated splice so the next patch can add another error case without repeating it. Reviewed-by: Darrick J. Wong Reviewed-by: Christoph Hellwig Signed-off-by: Weiming Shi Signed-off-by: Carlos Maiolino --- fs/xfs/xfs_log_recover.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c index a1b373c68f0e..103b2a79667b 100644 --- a/fs/xfs/xfs_log_recover.c +++ b/fs/xfs/xfs_log_recover.c @@ -1912,12 +1912,6 @@ xlog_recover_reorder_trans( xfs_warn(log->l_mp, "%s: unrecognized type of log operation (%d)", __func__, ITEM_TYPE(item)); - /* - * return the remaining items back to the transaction - * item list so they can be freed in caller. - */ - if (!list_empty(&sort_list)) - list_splice_init(&sort_list, &trans->r_itemq); error = -EFSCORRUPTED; break; } @@ -1945,7 +1939,15 @@ xlog_recover_reorder_trans( } } - ASSERT(list_empty(&sort_list)); + /* + * Return the remaining items back to the transaction item list so they + * can be freed in caller. This should only happen when we encounter + * an error. + */ + if (!list_empty(&sort_list)) { + ASSERT(error); + list_splice_init(&sort_list, &trans->r_itemq); + } if (!list_empty(&buffer_list)) list_splice(&buffer_list, &trans->r_itemq); if (!list_empty(&item_list)) From 2a665946e0407a05a3f81bd56a08553c446498e0 Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Fri, 19 Jun 2026 14:44:01 +0800 Subject: [PATCH 0643/1054] wifi: brcmfmac: initialize SDIO data work before cleanup brcmf_sdio_probe() stores the newly allocated bus in sdiodev->bus before allocating the ordered workqueue. If that allocation fails, the function jumps to fail and calls brcmf_sdio_remove(). brcmf_sdio_remove() unconditionally cancels bus->datawork. Initialize the work item before the first failure path that can reach brcmf_sdio_remove(), so the cleanup path always observes a valid work object. This issue was found by our static analysis tool and then confirmed by manual review of the probe error path and the remove-time work drain. The problem pattern is an early setup failure that reaches a cleanup helper which cancels an embedded work item before its initializer has run. A QEMU PoC forced alloc_ordered_workqueue() to fail at the same point in brcmf_sdio_probe(), before INIT_WORK(&bus->datawork) is reached. The resulting fail path calls brcmf_sdio_remove(), and DEBUG_OBJECTS reports the invalid work drain with brcmf_sdio_probe() and brcmf_sdio_remove() in the stack. Fixes: 9982464379e8 ("brcmfmac: make sdio suspend wait for threads to freeze") Signed-off-by: Runyu Xiao Acked-by: Arend van Spriel Link: https://patch.msgid.link/20260619064401.1048976-1-runyu.xiao@seu.edu.cn Signed-off-by: Johannes Berg --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c index 8fb595733b9c..b725c64e5b5c 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c @@ -4465,6 +4465,7 @@ int brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) bus->sdiodev = sdiodev; sdiodev->bus = bus; skb_queue_head_init(&bus->glom); + INIT_WORK(&bus->datawork, brcmf_sdio_dataworker); bus->txbound = BRCMF_TXBOUND; bus->rxbound = BRCMF_RXBOUND; bus->txminmax = BRCMF_TXMINMAX; @@ -4479,7 +4480,6 @@ int brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) goto fail; } brcmf_sdiod_freezer_count(sdiodev); - INIT_WORK(&bus->datawork, brcmf_sdio_dataworker); bus->brcmf_wq = wq; /* attempt to attach to the dongle */ From 3be28e2c9cd0230cb51fd4967df095273afd3848 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Tue, 30 Jun 2026 15:15:51 -0400 Subject: [PATCH 0644/1054] net/tls: Consume empty data records in tls_sw_read_sock() A peer may send a zero-length TLS application_data record; TLS 1.3 explicitly permits these as a traffic-analysis countermeasure (RFC 8446, Section 5.1). After decryption such a record has full_len == 0. tls_sw_read_sock() hands it to the read_actor, which has no payload to consume and returns zero. The loop treats a zero return as backpressure (used <= 0), requeues the skb at the head of rx_list, and stops. rx_list is serviced head-first on the next call, so the empty record is dequeued, fails the same way, and is requeued again; every later record on the connection is blocked behind it. tls_sw_recvmsg() does not stall on this: a zero-length data record copies nothing and falls through to consume_skb(). Mirror that in the read_sock() path by recognizing an empty data record before the actor runs, consuming it, and continuing. Fixes: 662fbcec32f4 ("net/tls: implement ->read_sock()") Signed-off-by: Chuck Lever Reviewed-by: Sabrina Dubroca Link: https://patch.msgid.link/20260630191551.875664-1-cel@kernel.org Signed-off-by: Paolo Abeni --- net/tls/tls_sw.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c index 9324e4ed20a3..d4afc90fd796 100644 --- a/net/tls/tls_sw.c +++ b/net/tls/tls_sw.c @@ -2115,6 +2115,17 @@ int tls_sw_read_sock(struct sock *sk, read_descriptor_t *desc, goto read_sock_requeue; } + /* An empty data record (legal in TLS 1.3) gives a zero + * read_actor return, indistinguishable from the consumer + * stalling; the used <= 0 path would requeue it at the + * head of rx_list and block all later records. Consume it + * here instead. + */ + if (rxm->full_len == 0) { + consume_skb(skb); + continue; + } + used = read_actor(desc, skb, rxm->offset, rxm->full_len); if (used <= 0) { if (!copied) From 240c8d2c717b3f8153e7e877b22a82518d78dbdc Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Sat, 27 Jun 2026 21:13:13 +0800 Subject: [PATCH 0645/1054] wifi: brcmfmac: cyw: fix heap overflow on a short auth frame brcmf_notify_auth_frame_rx() takes the frame length from the firmware event and copies the frame body with the management header offset subtracted: u32 mgmt_frame_len = e->datalen - sizeof(struct brcmf_rx_mgmt_data); ... memcpy(&mgmt_frame->u, frame, mgmt_frame_len - offsetof(struct ieee80211_mgmt, u)); The only length check is e->datalen >= sizeof(*rxframe), so mgmt_frame_len can be anything from 0 up. offsetof(struct ieee80211_mgmt, u) is 24. When mgmt_frame_len is below that, the subtraction wraps as an unsigned value to a huge length. The memcpy then runs far past the kzalloc'd buffer. A malicious or malfunctioning AP can make the frame short during the external SAE auth exchange, so this is a remotely triggered heap overflow. Reject frames shorter than the management header offset before the copy. Fixes: 66f909308a7c ("wifi: brcmfmac: cyw: support external SAE authentication in station mode") Link: https://lore.kernel.org/r/178214417708.2368577.16740907093694208834@maoyixie.com Cc: stable@vger.kernel.org Co-developed-by: Kaixuan Li Signed-off-by: Kaixuan Li Signed-off-by: Maoyi Xie Acked-by: Arend van Spriel Link: https://patch.msgid.link/20260627131313.3878893-1-maoyixie.tju@gmail.com Signed-off-by: Johannes Berg --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/core.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/core.c index ce09d44fa73c..873754be5174 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/core.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/core.c @@ -293,6 +293,12 @@ brcmf_notify_auth_frame_rx(struct brcmf_if *ifp, return -EINVAL; } + if (mgmt_frame_len < offsetof(struct ieee80211_mgmt, u)) { + bphy_err(drvr, "Event %s (%d) frame too small. Ignore\n", + brcmf_fweh_event_name(e->event_code), e->event_code); + return -EINVAL; + } + wdev = &ifp->vif->wdev; WARN_ON(!wdev); From d9d6d67f4c0877fde783c9d5beee013bcf1b1e85 Mon Sep 17 00:00:00 2001 From: Dong Yibo Date: Wed, 1 Jul 2026 11:22:08 +0800 Subject: [PATCH 0646/1054] net: rnpgbe: fix mailbox endianness and remove pointer casts The rnpgbe mailbox exchanges data through 32-bit MMIO registers in little-endian wire format. The original code had two problems: 1. FW structs (with __le16/__le32 fields) were cast to (u32 *) before reaching the mailbox transport, hiding the endian annotations from sparse. 2. No cpu_to_le32()/le32_to_cpu() conversion was done between CPU-endian MMIO values and the little-endian payload, causing data corruption on big-endian systems. Fix by adding the missing byte-order conversions in the transport layer and introducing union wrappers (mbx_fw_cmd_req_u, mbx_fw_cmd_reply_u) that overlay each FW struct with a __le32 dwords[] array. Callers fill named fields using cpu_to_le16/32(), then pass dwords[] to the transport, which now takes explicit __le32 * instead of u32 *. This eliminates all pointer casts on the mailbox data path and lets sparse verify the conversions. Fixes: 4543534c3ef5 ("net: rnpgbe: Add basic mbx ops support") Signed-off-by: Dong Yibo Reviewed-by: Andrew Lunn Link: https://patch.msgid.link/20260701032208.1843156-2-dong100@mucse.com Signed-off-by: Paolo Abeni --- .../net/ethernet/mucse/rnpgbe/rnpgbe_mbx.c | 26 ++++-- .../net/ethernet/mucse/rnpgbe/rnpgbe_mbx.h | 5 +- .../net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c | 82 ++++++++++--------- .../net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.h | 14 ++++ 4 files changed, 80 insertions(+), 47 deletions(-) diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.c b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.c index de5e29230b3c..c46408698263 100644 --- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.c +++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.c @@ -166,18 +166,23 @@ static void mucse_mbx_inc_pf_ack(struct mucse_hw *hw) * * Return: 0 on success, negative errno on failure **/ -static int mucse_read_mbx_pf(struct mucse_hw *hw, u32 *msg, u16 size) +static int mucse_read_mbx_pf(struct mucse_hw *hw, __le32 *msg, u16 size) { - const int size_in_words = size / sizeof(u32); + const int size_in_words = size / sizeof(__le32); struct mucse_mbx_info *mbx = &hw->mbx; + int off = MUCSE_MBX_FWPF_SHM; int err; err = mucse_obtain_mbx_lock_pf(hw); if (err) return err; + /* memcpy_fromio() is unsuitable: the mailbox uses 32-bit MMIO + * registers, not byte-addressable RAM. readl() guarantees + * the required 32-bit access width. + */ for (int i = 0; i < size_in_words; i++) - msg[i] = mbx_data_rd32(mbx, MUCSE_MBX_FWPF_SHM + 4 * i); + msg[i] = cpu_to_le32(mbx_data_rd32(mbx, off + 4 * i)); /* Hw needs write data_reg at last */ mbx_data_wr32(mbx, MUCSE_MBX_FWPF_SHM, 0); /* flush reqs as we have read this request data */ @@ -236,7 +241,7 @@ static int mucse_poll_for_msg(struct mucse_hw *hw) * Return: 0 if it successfully received a message notification and * copied it into the receive buffer, negative errno on failure **/ -int mucse_poll_and_read_mbx(struct mucse_hw *hw, u32 *msg, u16 size) +int mucse_poll_and_read_mbx(struct mucse_hw *hw, __le32 *msg, u16 size) { int err; @@ -290,9 +295,9 @@ static void mucse_mbx_inc_pf_req(struct mucse_hw *hw) * Return: 0 if it successfully copied message into the buffer, * negative errno on failure **/ -static int mucse_write_mbx_pf(struct mucse_hw *hw, u32 *msg, u16 size) +static int mucse_write_mbx_pf(struct mucse_hw *hw, const __le32 *msg, u16 size) { - const int size_in_words = size / sizeof(u32); + const int size_in_words = size / sizeof(__le32); struct mucse_mbx_info *mbx = &hw->mbx; int err; @@ -300,8 +305,12 @@ static int mucse_write_mbx_pf(struct mucse_hw *hw, u32 *msg, u16 size) if (err) return err; + /* memcpy_toio() would decompose into arbitrary-width accesses; + * the mailbox requires 32-bit MMIO writes via writel(). + */ for (int i = 0; i < size_in_words; i++) - mbx_data_wr32(mbx, MUCSE_MBX_FWPF_SHM + i * 4, msg[i]); + mbx_data_wr32(mbx, MUCSE_MBX_FWPF_SHM + i * 4, + le32_to_cpu(msg[i])); /* flush acks as we are overwriting the message buffer */ hw->mbx.fw_ack = mucse_mbx_get_fwack(mbx); @@ -360,7 +369,8 @@ static int mucse_poll_for_ack(struct mucse_hw *hw) * Return: 0 if it successfully copied message into the buffer and * received an ack to that message within delay * timeout_cnt period **/ -int mucse_write_and_wait_ack_mbx(struct mucse_hw *hw, u32 *msg, u16 size) +int mucse_write_and_wait_ack_mbx(struct mucse_hw *hw, const __le32 *msg, + u16 size) { int err; diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.h b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.h index e6fcc8d1d3ca..75b88b18b04d 100644 --- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.h +++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.h @@ -14,7 +14,8 @@ #define MUCSE_MBX_REQ BIT(0) /* Request a req to mailbox */ #define MUCSE_MBX_PFU BIT(3) /* PF owns the mailbox buffer */ -int mucse_write_and_wait_ack_mbx(struct mucse_hw *hw, u32 *msg, u16 size); +int mucse_write_and_wait_ack_mbx(struct mucse_hw *hw, + const __le32 *msg, u16 size); void mucse_init_mbx_params_pf(struct mucse_hw *hw); -int mucse_poll_and_read_mbx(struct mucse_hw *hw, u32 *msg, u16 size); +int mucse_poll_and_read_mbx(struct mucse_hw *hw, __le32 *msg, u16 size); #endif /* _RNPGBE_MBX_H */ diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c index 8c8bd5e8e1db..5ba74997beac 100644 --- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c +++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c @@ -20,32 +20,32 @@ * Return: 0 on success, negative errno on failure **/ static int mucse_fw_send_cmd_wait_resp(struct mucse_hw *hw, - struct mbx_fw_cmd_req *req, - struct mbx_fw_cmd_reply *reply) + union mbx_fw_cmd_req_u *req, + union mbx_fw_cmd_reply_u *reply) { - int len = le16_to_cpu(req->datalen); + int len = le16_to_cpu(req->r.datalen); int retry_cnt = 3; int err; mutex_lock(&hw->mbx.lock); - err = mucse_write_and_wait_ack_mbx(hw, (u32 *)req, len); + err = mucse_write_and_wait_ack_mbx(hw, req->dwords, len); if (err) goto out; do { - err = mucse_poll_and_read_mbx(hw, (u32 *)reply, - sizeof(*reply)); + err = mucse_poll_and_read_mbx(hw, reply->dwords, + sizeof(reply->r)); if (err) goto out; /* mucse_write_and_wait_ack_mbx return 0 means fw has * received request, wait for the expect opcode * reply with 'retry_cnt' times. */ - } while (--retry_cnt >= 0 && reply->opcode != req->opcode); + } while (--retry_cnt >= 0 && reply->r.opcode != req->r.opcode); out: mutex_unlock(&hw->mbx.lock); if (!err && retry_cnt < 0) return -ETIMEDOUT; - if (!err && reply->error_code) + if (!err && reply->r.error_code) return -EIO; return err; @@ -61,17 +61,19 @@ static int mucse_fw_send_cmd_wait_resp(struct mucse_hw *hw, **/ static int mucse_mbx_get_info(struct mucse_hw *hw) { - struct mbx_fw_cmd_req req = { - .datalen = cpu_to_le16(MUCSE_MBX_REQ_HDR_LEN), - .opcode = cpu_to_le16(GET_HW_INFO), + union mbx_fw_cmd_req_u req = { + .r = { + .datalen = cpu_to_le16(MUCSE_MBX_REQ_HDR_LEN), + .opcode = cpu_to_le16(GET_HW_INFO), + }, }; - struct mbx_fw_cmd_reply reply = {}; + union mbx_fw_cmd_reply_u reply = {}; int err; err = mucse_fw_send_cmd_wait_resp(hw, &req, &reply); if (!err) hw->pfvfnum = FIELD_GET(GENMASK_U16(7, 0), - le16_to_cpu(reply.hw_info.pfnum)); + le16_to_cpu(reply.r.hw_info.pfnum)); return err; } @@ -111,21 +113,23 @@ int mucse_mbx_sync_fw(struct mucse_hw *hw) **/ int mucse_mbx_powerup(struct mucse_hw *hw, bool is_powerup) { - struct mbx_fw_cmd_req req = { - .datalen = cpu_to_le16(sizeof(req.powerup) + - MUCSE_MBX_REQ_HDR_LEN), - .opcode = cpu_to_le16(POWER_UP), - .powerup = { - /* fw needs this to reply correct cmd */ - .version = cpu_to_le32(GENMASK_U32(31, 0)), - .status = cpu_to_le32(is_powerup ? 1 : 0), + union mbx_fw_cmd_req_u req = { + .r = { + .datalen = cpu_to_le16(sizeof(req.r.powerup) + + MUCSE_MBX_REQ_HDR_LEN), + .opcode = cpu_to_le16(POWER_UP), + .powerup = { + /* fw needs this to reply correct cmd */ + .version = cpu_to_le32(GENMASK_U32(31, 0)), + .status = cpu_to_le32(is_powerup ? 1 : 0), + }, }, }; int len, err; - len = le16_to_cpu(req.datalen); + len = le16_to_cpu(req.r.datalen); mutex_lock(&hw->mbx.lock); - err = mucse_write_and_wait_ack_mbx(hw, (u32 *)&req, len); + err = mucse_write_and_wait_ack_mbx(hw, req.dwords, len); mutex_unlock(&hw->mbx.lock); return err; @@ -142,11 +146,13 @@ int mucse_mbx_powerup(struct mucse_hw *hw, bool is_powerup) **/ int mucse_mbx_reset_hw(struct mucse_hw *hw) { - struct mbx_fw_cmd_req req = { - .datalen = cpu_to_le16(MUCSE_MBX_REQ_HDR_LEN), - .opcode = cpu_to_le16(RESET_HW), + union mbx_fw_cmd_req_u req = { + .r = { + .datalen = cpu_to_le16(MUCSE_MBX_REQ_HDR_LEN), + .opcode = cpu_to_le16(RESET_HW), + }, }; - struct mbx_fw_cmd_reply reply = {}; + union mbx_fw_cmd_reply_u reply = {}; return mucse_fw_send_cmd_wait_resp(hw, &req, &reply); } @@ -166,24 +172,26 @@ int mucse_mbx_get_macaddr(struct mucse_hw *hw, int pfvfnum, u8 *mac_addr, int port) { - struct mbx_fw_cmd_req req = { - .datalen = cpu_to_le16(sizeof(req.get_mac_addr) + - MUCSE_MBX_REQ_HDR_LEN), - .opcode = cpu_to_le16(GET_MAC_ADDRESS), - .get_mac_addr = { - .port_mask = cpu_to_le32(BIT(port)), - .pfvf_num = cpu_to_le32(pfvfnum), + union mbx_fw_cmd_req_u req = { + .r = { + .datalen = cpu_to_le16(sizeof(req.r.get_mac_addr) + + MUCSE_MBX_REQ_HDR_LEN), + .opcode = cpu_to_le16(GET_MAC_ADDRESS), + .get_mac_addr = { + .port_mask = cpu_to_le32(BIT(port)), + .pfvf_num = cpu_to_le32(pfvfnum), + }, }, }; - struct mbx_fw_cmd_reply reply = {}; + union mbx_fw_cmd_reply_u reply = {}; int err; err = mucse_fw_send_cmd_wait_resp(hw, &req, &reply); if (err) return err; - if (le32_to_cpu(reply.mac_addr.ports) & BIT(port)) - memcpy(mac_addr, reply.mac_addr.addrs[port].mac, ETH_ALEN); + if (le32_to_cpu(reply.r.mac_addr.ports) & BIT(port)) + memcpy(mac_addr, reply.r.mac_addr.addrs[port].mac, ETH_ALEN); else return -ENODATA; diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.h b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.h index fb24fc12b613..fe996aeffc4d 100644 --- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.h +++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.h @@ -80,6 +80,20 @@ struct mbx_fw_cmd_reply { }; } __packed; +/* Union wrappers to expose struct as __le32 dword array for mailbox + * transport, eliminating the need for pointer casts. The __packed + * structs have no padding, so dwords[] overlays the fields exactly. + */ +union mbx_fw_cmd_req_u { + struct mbx_fw_cmd_req r; + __le32 dwords[sizeof(struct mbx_fw_cmd_req) / sizeof(__le32)]; +}; + +union mbx_fw_cmd_reply_u { + struct mbx_fw_cmd_reply r; + __le32 dwords[sizeof(struct mbx_fw_cmd_reply) / sizeof(__le32)]; +}; + int mucse_mbx_sync_fw(struct mucse_hw *hw); int mucse_mbx_powerup(struct mucse_hw *hw, bool is_powerup); int mucse_mbx_reset_hw(struct mucse_hw *hw); From 5c0e3ba4f500fd4314ceb42f07f16bc445156431 Mon Sep 17 00:00:00 2001 From: Yuho Choi Date: Wed, 1 Jul 2026 00:08:47 -0400 Subject: [PATCH 0647/1054] net/liquidio: drop cached VF pci_dev LUT The PF SR-IOV enable path caches VF pci_dev pointers in dpiring_to_vfpcidev_lut[] by iterating with pci_get_device(). Those entries do not own a reference, because the iterator drops the previous device reference on each step. The cached pointer is then dereferenced later when handling OCTEON_VF_FLR_REQUEST. Replace the cached VF mapping with runtime lookup on the mailbox DPI ring: derive the VF index from q_no, resolve the VF via exported PCI IOV helpers, validate it with the PF pointer and VF ID, then issue pcie_flr() and drop the reference with pci_dev_put(). Remove the unused VF lookup table initialization and cleanup. Fixes: ca6139ffc67ee ("liquidio CN23XX: sysfs VF config support") Fixes: 8c978d059224 ("liquidio CN23XX: Mailbox support") Signed-off-by: Yuho Choi Link: https://patch.msgid.link/20260701040847.1897845-1-dbgh9129@gmail.com Signed-off-by: Paolo Abeni --- .../net/ethernet/cavium/liquidio/lio_main.c | 27 --------------- .../ethernet/cavium/liquidio/octeon_device.h | 3 -- .../ethernet/cavium/liquidio/octeon_mailbox.c | 33 ++++++++++++++++++- 3 files changed, 32 insertions(+), 31 deletions(-) diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index 0db08ac3d098..e303956b4bf1 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -3779,9 +3779,7 @@ static int setup_nic_devices(struct octeon_device *octeon_dev) static int octeon_enable_sriov(struct octeon_device *oct) { unsigned int num_vfs_alloced = oct->sriov_info.num_vfs_alloced; - struct pci_dev *vfdev; int err; - u32 u; if (OCTEON_CN23XX_PF(oct) && num_vfs_alloced) { err = pci_enable_sriov(oct->pci_dev, @@ -3794,23 +3792,6 @@ static int octeon_enable_sriov(struct octeon_device *oct) return err; } oct->sriov_info.sriov_enabled = 1; - - /* init lookup table that maps DPI ring number to VF pci_dev - * struct pointer - */ - u = 0; - vfdev = pci_get_device(PCI_VENDOR_ID_CAVIUM, - OCTEON_CN23XX_VF_VID, NULL); - while (vfdev) { - if (vfdev->is_virtfn && - (vfdev->physfn == oct->pci_dev)) { - oct->sriov_info.dpiring_to_vfpcidev_lut[u] = - vfdev; - u += oct->sriov_info.rings_per_vf; - } - vfdev = pci_get_device(PCI_VENDOR_ID_CAVIUM, - OCTEON_CN23XX_VF_VID, vfdev); - } } return num_vfs_alloced; @@ -3818,8 +3799,6 @@ static int octeon_enable_sriov(struct octeon_device *oct) static int lio_pci_sriov_disable(struct octeon_device *oct) { - int u; - if (pci_vfs_assigned(oct->pci_dev)) { dev_err(&oct->pci_dev->dev, "VFs are still assigned to VMs.\n"); return -EPERM; @@ -3827,12 +3806,6 @@ static int lio_pci_sriov_disable(struct octeon_device *oct) pci_disable_sriov(oct->pci_dev); - u = 0; - while (u < MAX_POSSIBLE_VFS) { - oct->sriov_info.dpiring_to_vfpcidev_lut[u] = NULL; - u += oct->sriov_info.rings_per_vf; - } - oct->sriov_info.num_vfs_alloced = 0; dev_info(&oct->pci_dev->dev, "oct->pf_num:%d disabled VFs\n", oct->pf_num); diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_device.h b/drivers/net/ethernet/cavium/liquidio/octeon_device.h index 19344b21f8fb..858a0fff2cc0 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_device.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_device.h @@ -390,9 +390,6 @@ struct octeon_sriov_info { struct lio_trusted_vf trusted_vf; - /*lookup table that maps DPI ring number to VF pci_dev struct pointer*/ - struct pci_dev *dpiring_to_vfpcidev_lut[MAX_POSSIBLE_VFS]; - u64 vf_macaddr[MAX_POSSIBLE_VFS]; u16 vf_vlantci[MAX_POSSIBLE_VFS]; diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_mailbox.c b/drivers/net/ethernet/cavium/liquidio/octeon_mailbox.c index ad685f5d0a13..697fcdc41e3c 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_mailbox.c +++ b/drivers/net/ethernet/cavium/liquidio/octeon_mailbox.c @@ -26,6 +26,31 @@ #include "octeon_mailbox.h" #include "cn23xx_pf_device.h" +static struct pci_dev *lio_vf_pci_dev_by_qno(struct octeon_device *oct, u32 q_no) +{ + struct pci_dev *vfdev = NULL; + int vfidx; + + if (!oct->sriov_info.rings_per_vf) + return NULL; + + if (q_no % oct->sriov_info.rings_per_vf) + return NULL; + + vfidx = q_no / oct->sriov_info.rings_per_vf; + if (vfidx >= oct->sriov_info.num_vfs_alloced) + return NULL; + + while ((vfdev = pci_get_device(PCI_VENDOR_ID_CAVIUM, + OCTEON_CN23XX_VF_VID, vfdev))) { + if (pci_physfn(vfdev) && pci_physfn(vfdev) == oct->pci_dev && + pci_iov_vf_id(vfdev) == vfidx) + return vfdev; + } + + return NULL; +} + /** * octeon_mbox_read: * @mbox: Pointer mailbox @@ -237,6 +262,7 @@ static int octeon_mbox_process_cmd(struct octeon_mbox *mbox, struct octeon_mbox_cmd *mbox_cmd) { struct octeon_device *oct = mbox->oct_dev; + struct pci_dev *vfdev; switch (mbox_cmd->msg.s.cmd) { case OCTEON_VF_ACTIVE: @@ -260,7 +286,12 @@ static int octeon_mbox_process_cmd(struct octeon_mbox *mbox, dev_info(&oct->pci_dev->dev, "got a request for FLR from VF that owns DPI ring %u\n", mbox->q_no); - pcie_flr(oct->sriov_info.dpiring_to_vfpcidev_lut[mbox->q_no]); + vfdev = lio_vf_pci_dev_by_qno(oct, mbox->q_no); + if (!vfdev) + break; + + pcie_flr(vfdev); + pci_dev_put(vfdev); break; case OCTEON_PF_CHANGED_VF_MACADDR: From 7993211bde166471dffac074dc965489f86531f8 Mon Sep 17 00:00:00 2001 From: Yuyang Huang Date: Thu, 2 Jul 2026 08:50:14 +0900 Subject: [PATCH 0648/1054] ipv4: igmp: remove multicast group from hash table on device destruction When a device is destroyed under RTNL, ip_mc_destroy_dev() iterates through the multicast list and calls ip_ma_put() on each membership, scheduling them for RCU reclamation. However, they are not unlinked from the device's multicast hash table (mc_hash). Since the device remains published in dev->ip_ptr until after ip_mc_destroy_dev() completes, concurrent RCU readers traversing mc_hash can still locate and access the multicast group after its refcount is decremented. If the RCU callback runs and frees the group while a reader is accessing it, a use-after-free occurs. Fix this by unlinking the multicast group from mc_hash using ip_mc_hash_remove() before scheduling it for reclamation. BUG: KASAN: slab-use-after-free in ip_check_mc_rcu+0x149/0x3f0 Read of size 4 at addr ffff888009bf1408 by task mausezahn/2276 Call Trace: dump_stack_lvl+0x67/0x90 print_report+0x175/0x7c0 kasan_report+0x147/0x180 ip_check_mc_rcu+0x149/0x3f0 udp_v4_early_demux+0x36d/0x12d0 ip_rcv_finish_core+0xb8b/0x1390 ip_rcv_finish+0x54/0x120 NF_HOOK+0x213/0x2b0 __netif_receive_skb+0x126/0x340 process_backlog+0x4f2/0xf00 __napi_poll+0x92/0x2c0 net_rx_action+0x583/0xc60 handle_softirqs+0x236/0x7f0 do_softirq+0x57/0x80 Allocated by task 2239: kasan_save_track+0x3e/0x80 __kasan_kmalloc+0x72/0x90 ____ip_mc_inc_group+0x31a/0xa40 __ip_mc_join_group+0x334/0x3f0 do_ip_setsockopt+0x16fa/0x2010 ip_setsockopt+0x3f/0x90 do_sock_setsockopt+0x1ad/0x300 Freed by task 0: kasan_save_track+0x3e/0x80 kasan_save_free_info+0x40/0x50 __kasan_slab_free+0x3a/0x60 __rcu_free_sheaf_prepare+0xd4/0x220 rcu_free_sheaf+0x36/0x190 rcu_core+0x8d9/0x12f0 handle_softirqs+0x236/0x7f0 Fixes: e9897071350b ("igmp: hash a hash table to speedup ip_check_mc_rcu()") Cc: stable@vger.kernel.org Signed-off-by: Yuyang Huang Reviewed-by: Kuniyuki Iwashima Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260701235014.73505-1-yuyanghuang@google.com Signed-off-by: Paolo Abeni --- net/ipv4/igmp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index b6337a47c141..d520ea4f6d14 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -1922,6 +1922,7 @@ void ip_mc_destroy_dev(struct in_device *in_dev) #endif while ((i = rtnl_dereference(in_dev->mc_list)) != NULL) { + ip_mc_hash_remove(in_dev, i); in_dev->mc_list = i->next_rcu; WRITE_ONCE(in_dev->mc_count, in_dev->mc_count - 1); ip_mc_clear_src(i); From 8c6db30d79528279abbeb416e4f533f1f91b8724 Mon Sep 17 00:00:00 2001 From: Mostafa Saleh Date: Thu, 2 Jul 2026 10:38:40 +0000 Subject: [PATCH 0649/1054] KVM: arm64: Fix bounds checking in do_ffa_mem_reclaim() Sashiko (locally) reports out of bound write possiblity if SPMD returns an invalid data. While SPMD is considered trusted, pKVM does some basic checks, for offset to be less than or equal len. However, that is incorrect as even if the offset is smaller than len pKVM can still access out of bound memory in the next ffa_host_unshare_ranges(). Split this check into 2: 1- Check that the fixed portion of the descriptor fits. 2- After getting reg, check the variable array size addr_range_cnt fits. Also, drop the WARN_ONs as that will panic the kernel and in the next checks there are no WARNs, so that makes it consistent. Fixes: 0a9f15fd5674 ("KVM: arm64: pkvm: Add support for fragmented FF-A descriptors") Signed-off-by: Mostafa Saleh Reviewed-by: Vincent Donnefort Signed-off-by: Sebastian Ene Link: https://patch.msgid.link/20260702103848.1647249-4-sebastianene@google.com Signed-off-by: Marc Zyngier --- arch/arm64/kvm/hyp/nvhe/ffa.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/arch/arm64/kvm/hyp/nvhe/ffa.c b/arch/arm64/kvm/hyp/nvhe/ffa.c index 1af722771178..41cc4c1bafeb 100644 --- a/arch/arm64/kvm/hyp/nvhe/ffa.c +++ b/arch/arm64/kvm/hyp/nvhe/ffa.c @@ -607,8 +607,8 @@ static void do_ffa_mem_reclaim(struct arm_smccc_1_2_regs *res, * check that we end up with something that doesn't look _completely_ * bogus. */ - if (WARN_ON(offset > len || - fraglen > KVM_FFA_MBOX_NR_PAGES * PAGE_SIZE)) { + if (offset + CONSTITUENTS_OFFSET(0) > len || + fraglen > KVM_FFA_MBOX_NR_PAGES * PAGE_SIZE) { ret = FFA_RET_ABORTED; ffa_rx_release(res); goto out_unlock; @@ -636,11 +636,16 @@ static void do_ffa_mem_reclaim(struct arm_smccc_1_2_regs *res, ffa_rx_release(res); } + reg = (void *)buf + offset; + if (offset + CONSTITUENTS_OFFSET(reg->addr_range_cnt) > len) { + ret = FFA_RET_ABORTED; + goto out_unlock; + } + ffa_mem_reclaim(res, handle_lo, handle_hi, flags); if (res->a0 != FFA_SUCCESS) goto out_unlock; - reg = (void *)buf + offset; /* If the SPMD was happy, then we should be too. */ WARN_ON(ffa_host_unshare_ranges(reg->constituents, reg->addr_range_cnt)); From a6b49d27c17909608d54523220bb6f3498d4a1df Mon Sep 17 00:00:00 2001 From: Sebastian Ene Date: Thu, 2 Jul 2026 10:38:41 +0000 Subject: [PATCH 0650/1054] KVM: arm64: Validate the offset to the mem access descriptor Prevent the pKVM hypervisor from making assumptions that the endpoint memory access descriptor (EMAD) comes right after the FF-A memory region header. Prior to FF-A version 1.1 the header of the memory region didn't contain an offset to the endpoint memory access descriptor. The layout of a memory transaction looks like this from 1.1 onward: Type | Field name | Offset [ Header | ffa_mem_region | 0 EMAD 1 | ffa_mem_region_attributes) | ffa_mem_region.ep_mem_offset ] Verify that the offset to the first endpoint memory access descriptor is within the mailbox buffer bounds. Also, fix one hardcoded sizeof(struct ffa_mem_region_attributes) that should be replaced ffa_emad_size_get() for compatibility with FFA v1.0. Fixes: 42fb33dde42b ("KVM: arm64: Use FF-A 1.1 with pKVM") Signed-off-by: Mostafa Saleh Signed-off-by: Sebastian Ene Reviewed-by: Vincent Donnefort Link: https://patch.msgid.link/20260702103848.1647249-5-sebastianene@google.com Signed-off-by: Marc Zyngier --- arch/arm64/kvm/hyp/nvhe/ffa.c | 27 +++++++++++++++++++-------- include/linux/arm_ffa.h | 7 +++++++ 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/arch/arm64/kvm/hyp/nvhe/ffa.c b/arch/arm64/kvm/hyp/nvhe/ffa.c index 41cc4c1bafeb..2e7ab7e3319d 100644 --- a/arch/arm64/kvm/hyp/nvhe/ffa.c +++ b/arch/arm64/kvm/hyp/nvhe/ffa.c @@ -476,11 +476,12 @@ static void __do_ffa_mem_xfer(const u64 func_id, DECLARE_REG(u32, fraglen, ctxt, 2); DECLARE_REG(u64, addr_mbz, ctxt, 3); DECLARE_REG(u32, npages_mbz, ctxt, 4); + u32 offset, nr_ranges, checked_offset, em_mem_access_off; struct ffa_mem_region_attributes *ep_mem_access; struct ffa_composite_mem_region *reg; struct ffa_mem_region *buf; - u32 offset, nr_ranges, checked_offset; int ret = 0; + size_t mem_region_len = FFA_MEM_REGION_SZ(hyp_ffa_version); if (addr_mbz || npages_mbz || fraglen > len || fraglen > KVM_FFA_MBOX_NR_PAGES * PAGE_SIZE) { @@ -488,8 +489,7 @@ static void __do_ffa_mem_xfer(const u64 func_id, goto out; } - if (fraglen < sizeof(struct ffa_mem_region) + - sizeof(struct ffa_mem_region_attributes)) { + if (fraglen < mem_region_len + ffa_emad_size_get(hyp_ffa_version)) { ret = FFA_RET_INVALID_PARAMETERS; goto out; } @@ -508,8 +508,13 @@ static void __do_ffa_mem_xfer(const u64 func_id, buf = hyp_buffers.tx; memcpy(buf, host_buffers.tx, fraglen); - ep_mem_access = (void *)buf + - ffa_mem_desc_offset(buf, 0, hyp_ffa_version); + em_mem_access_off = ffa_mem_desc_offset(buf, 0, hyp_ffa_version); + if ((u64)em_mem_access_off + ffa_emad_size_get(hyp_ffa_version) > fraglen) { + ret = FFA_RET_INVALID_PARAMETERS; + goto out_unlock; + } + + ep_mem_access = (void *)buf + em_mem_access_off; offset = ep_mem_access->composite_off; if (!offset || buf->ep_count != 1 || buf->sender_id != HOST_FFA_ID) { ret = FFA_RET_INVALID_PARAMETERS; @@ -574,9 +579,9 @@ static void do_ffa_mem_reclaim(struct arm_smccc_1_2_regs *res, DECLARE_REG(u32, handle_lo, ctxt, 1); DECLARE_REG(u32, handle_hi, ctxt, 2); DECLARE_REG(u32, flags, ctxt, 3); + u32 offset, len, fraglen, fragoff, em_mem_access_off; struct ffa_mem_region_attributes *ep_mem_access; struct ffa_composite_mem_region *reg; - u32 offset, len, fraglen, fragoff; struct ffa_mem_region *buf; int ret = 0; u64 handle; @@ -599,8 +604,14 @@ static void do_ffa_mem_reclaim(struct arm_smccc_1_2_regs *res, len = res->a1; fraglen = res->a2; - ep_mem_access = (void *)buf + - ffa_mem_desc_offset(buf, 0, hyp_ffa_version); + em_mem_access_off = ffa_mem_desc_offset(buf, 0, hyp_ffa_version); + if ((u64)em_mem_access_off + ffa_emad_size_get(hyp_ffa_version) > fraglen) { + ret = FFA_RET_INVALID_PARAMETERS; + ffa_rx_release(res); + goto out_unlock; + } + + ep_mem_access = (void *)buf + em_mem_access_off; offset = ep_mem_access->composite_off; /* * We can trust the SPMD to get this right, but let's at least diff --git a/include/linux/arm_ffa.h b/include/linux/arm_ffa.h index 81e603839c4a..3c91d4c4153c 100644 --- a/include/linux/arm_ffa.h +++ b/include/linux/arm_ffa.h @@ -421,6 +421,13 @@ struct ffa_mem_region { #define FFA_EMAD_HAS_IMPDEF_FIELD(version) ((version) >= FFA_VERSION_1_2) #define FFA_MEM_REGION_HAS_EP_MEM_OFFSET(version) ((version) > FFA_VERSION_1_0) +/* The layout changed from FFA_VERSION_1_0 and the region includes an + * ep_mem_offset. + */ +#define FFA_MEM_REGION_SZ(version) (!FFA_MEM_REGION_HAS_EP_MEM_OFFSET((version)) ?\ + offsetof(struct ffa_mem_region, ep_mem_offset) :\ + sizeof(struct ffa_mem_region)) + static inline u32 ffa_emad_size_get(u32 ffa_version) { u32 sz; From 6a7a181f6921db3d9aed1bba7e15547fefd7eedc Mon Sep 17 00:00:00 2001 From: Mostafa Saleh Date: Thu, 2 Jul 2026 10:38:42 +0000 Subject: [PATCH 0651/1054] KVM: arm64: Ensure FFA ranges are page aligned Harden the check for the constituent memory region page alignment to prevent over-sharing when the negotiated FFA_PAGE_SIZE size is smaller than the system PAGE_SIZE. At the moment we only check that the size of the range is page aligned, and truncate the address to the page boundary which can annotate more memory than needed as being used by the FF-A. Fixes: 436090001776 ("KVM: arm64: Handle FFA_MEM_SHARE calls from the host") Signed-off-by: Mostafa Saleh Reviewed-by: Vincent Donnefort Signed-off-by: Sebastian Ene Link: https://patch.msgid.link/20260702103848.1647249-6-sebastianene@google.com Signed-off-by: Marc Zyngier --- arch/arm64/kvm/hyp/nvhe/ffa.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm64/kvm/hyp/nvhe/ffa.c b/arch/arm64/kvm/hyp/nvhe/ffa.c index 2e7ab7e3319d..9c96e72e522e 100644 --- a/arch/arm64/kvm/hyp/nvhe/ffa.c +++ b/arch/arm64/kvm/hyp/nvhe/ffa.c @@ -352,7 +352,7 @@ static u32 __ffa_host_share_ranges(struct ffa_mem_region_addr_range *ranges, u64 sz = (u64)range->pg_cnt * FFA_PAGE_SIZE; u64 pfn = hyp_phys_to_pfn(range->address); - if (!PAGE_ALIGNED(sz)) + if (!PAGE_ALIGNED(sz | range->address)) break; if (__pkvm_host_share_ffa(pfn, sz / PAGE_SIZE)) @@ -372,7 +372,7 @@ static u32 __ffa_host_unshare_ranges(struct ffa_mem_region_addr_range *ranges, u64 sz = (u64)range->pg_cnt * FFA_PAGE_SIZE; u64 pfn = hyp_phys_to_pfn(range->address); - if (!PAGE_ALIGNED(sz)) + if (!PAGE_ALIGNED(sz | range->address)) break; if (__pkvm_host_unshare_ffa(pfn, sz / PAGE_SIZE)) From 2bd3c6c702f3a9e2bcb3b536b0fbbaa645005d71 Mon Sep 17 00:00:00 2001 From: Sebastian Ene Date: Thu, 2 Jul 2026 10:38:43 +0000 Subject: [PATCH 0652/1054] KVM: arm64: Zero out the stack initialized data in the FFA handler Don't leak hypervisor stack data when using the FFA_VERSION call. When the compiler doesn't support -ftrivial-auto-var-init=zero option we need to zero out the stack initialized variable before returning data to the host caller. Closes: https://lore.kernel.org/all/20260616160016.C62C81F000E9@smtp.kernel.org/ Reported-by: Sashiko AI Fixes: c9c012625e12 ("KVM: arm64: Trap FFA_VERSION host call in pKVM") Reviewed-by: Vincent Donnefort Link: https://lore.kernel.org/all/20260616160016.C62C81F000E9@smtp.kernel.org/ Signed-off-by: Sebastian Ene Link: https://patch.msgid.link/20260702103848.1647249-7-sebastianene@google.com Signed-off-by: Marc Zyngier --- arch/arm64/kvm/hyp/nvhe/ffa.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/kvm/hyp/nvhe/ffa.c b/arch/arm64/kvm/hyp/nvhe/ffa.c index 9c96e72e522e..a327c2bbb6b6 100644 --- a/arch/arm64/kvm/hyp/nvhe/ffa.c +++ b/arch/arm64/kvm/hyp/nvhe/ffa.c @@ -880,7 +880,7 @@ static void do_ffa_part_get(struct arm_smccc_1_2_regs *res, bool kvm_host_ffa_handler(struct kvm_cpu_context *host_ctxt, u32 func_id) { - struct arm_smccc_1_2_regs res; + struct arm_smccc_1_2_regs res = {0}; /* * There's no way we can tell what a non-standard SMC call might From 60444706aa17616efc03190d099ac347e28b3d0a Mon Sep 17 00:00:00 2001 From: Enrico Pozzobon Date: Wed, 1 Jul 2026 16:47:23 +0200 Subject: [PATCH 0653/1054] net: usb: lan78xx: disable VLAN filter in promiscuous mode The hardware VLAN filter (RFE_CTL_VLAN_FILTER_) drops VLAN-tagged frames whose VID has not been registered via lan78xx_vlan_rx_add_vid(). It is left enabled in promiscuous mode, so packet capture (e.g. tcpdump or Wireshark) does not see tagged frames for unregistered VIDs. Clear the filter while the interface is promiscuous and restore it from NETIF_F_HW_VLAN_CTAG_FILTER otherwise. Enforce the same condition in lan78xx_set_features() so netdev_update_features() cannot re-enable the filter while promiscuous. Fixes: 55d7de9de6c3 ("Microchip's LAN7800 family USB 2/3 to 10/100/1000 Ethernet device driver") Signed-off-by: Enrico Pozzobon Reviewed-by: Nicolai Buchwitz Link: https://patch.msgid.link/20260701-lan78xx-vlan-promisc-v3-1-232266d32743@dissecto.com Signed-off-by: Paolo Abeni --- drivers/net/usb/lan78xx.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c index c4cebacabcb5..cb782d81d84f 100644 --- a/drivers/net/usb/lan78xx.c +++ b/drivers/net/usb/lan78xx.c @@ -1499,6 +1499,17 @@ static void lan78xx_deferred_multicast_write(struct work_struct *param) return; } +static void lan78xx_update_vlan_filter(struct lan78xx_priv *pdata, + struct net_device *netdev, + netdev_features_t features) +{ + if ((features & NETIF_F_HW_VLAN_CTAG_FILTER) && + !(netdev->flags & IFF_PROMISC)) + pdata->rfe_ctl |= RFE_CTL_VLAN_FILTER_; + else + pdata->rfe_ctl &= ~RFE_CTL_VLAN_FILTER_; +} + static void lan78xx_set_multicast(struct net_device *netdev) { struct lan78xx_net *dev = netdev_priv(netdev); @@ -1533,6 +1544,8 @@ static void lan78xx_set_multicast(struct net_device *netdev) } } + lan78xx_update_vlan_filter(pdata, dev->net, dev->net->features); + if (netdev_mc_count(dev->net)) { struct netdev_hw_addr *ha; int i; @@ -3074,10 +3087,7 @@ static int lan78xx_set_features(struct net_device *netdev, else pdata->rfe_ctl &= ~RFE_CTL_VLAN_STRIP_; - if (features & NETIF_F_HW_VLAN_CTAG_FILTER) - pdata->rfe_ctl |= RFE_CTL_VLAN_FILTER_; - else - pdata->rfe_ctl &= ~RFE_CTL_VLAN_FILTER_; + lan78xx_update_vlan_filter(pdata, netdev, features); spin_unlock_irqrestore(&pdata->rfe_ctl_lock, flags); From 252f8c681adc8614b70f844ba3de3a138c33a783 Mon Sep 17 00:00:00 2001 From: Christopher Mackle Date: Sat, 20 Jun 2026 01:39:16 +0000 Subject: [PATCH 0654/1054] staging: rtl8723bs: don't drop short TX frames in _rtw_pktfile_read() Commit bc4df274dca6 ("staging: rtl8723bs: update _rtw_pktfile_read() to return error codes") changed _rtw_pktfile_read() to fail when the caller asks for more bytes than remain in the packet: if (rtw_remainder_len(pfile) < rlen) return -EINVAL; That breaks the assumption made by the data TX path. In rtw_xmitframe_coalesce() (core/rtw_xmit.c) the per-fragment copy is issued with the full fragment length, mpdu_len, which is derived from pxmitpriv->frag_len (~2300 bytes), and the code relies on the historical behaviour of copying only what is left and returning the number of bytes actually copied: mem_sz = _rtw_pktfile_read(&pktfile, pframe, mpdu_len); if (mem_sz < 0) return mem_sz; So for every outbound packet smaller than the fragmentation threshold - i.e. essentially all normal traffic, including the EAPOL frames of the WPA 4-way handshake and DHCP - rlen is larger than the bytes remaining, _rtw_pktfile_read() returns -EINVAL, rtw_xmitframe_coalesce() aborts, and the frame is dropped before it is queued to the hardware. The driver floods the log with: rtl8723bs ...: xmit_xmitframes: coalesce failed with error -22 Management frames (authentication/association) use a different path and still go out, so the interface scans and associates, but no data frame is ever transmitted. The 4-way handshake therefore never completes and wpa_supplicant misreports it as: WPA: 4-Way Handshake failed - pre-shared key may be incorrect AP mode is unaffected. The net effect is that the chip is unusable in station mode on any kernel carrying the offending commit. This was confirmed with a wpa_supplicant -dd trace on an RTL8723BS SDIO adapter (Bay Trail): message 1/4 is received and the PTK is derived, but each "Sending EAPOL-Key 2/4" coincides 1:1 with a "coalesce failed with error -22", so message 2/4 never reaches the AP, which keeps retrying message 1/4 until the handshake times out. Restore the original semantics: clamp the requested length to the bytes remaining in the packet and return that length. The skb_copy_bits() error path is kept, so genuine copy failures are still propagated. Fixes: bc4df274dca6 ("staging: rtl8723bs: update _rtw_pktfile_read() to return error codes") Cc: stable Tested-by: Christopher Mackle Signed-off-by: Christopher Mackle Link: https://patch.msgid.link/20260620013916.7148-1-christophermackle01@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/os_dep/xmit_linux.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/staging/rtl8723bs/os_dep/xmit_linux.c b/drivers/staging/rtl8723bs/os_dep/xmit_linux.c index d5bb1c7932fc..4260ed5f4e97 100644 --- a/drivers/staging/rtl8723bs/os_dep/xmit_linux.c +++ b/drivers/staging/rtl8723bs/os_dep/xmit_linux.c @@ -24,9 +24,11 @@ void _rtw_open_pktfile(struct sk_buff *pktptr, struct pkt_file *pfile) int _rtw_pktfile_read(struct pkt_file *pfile, u8 *rmem, unsigned int rlen) { int ret; + unsigned int remain = rtw_remainder_len(pfile); - if (rtw_remainder_len(pfile) < rlen) - return -EINVAL; + /* clamp to bytes remaining; the coalesce loop relies on short reads */ + if (rlen > remain) + rlen = remain; if (rmem) { ret = skb_copy_bits(pfile->pkt, pfile->buf_len - pfile->pkt_len, rmem, rlen); From 9f32f38265014fac7f5dc9490fb01a638ce6e121 Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Thu, 18 Jun 2026 13:47:09 +0200 Subject: [PATCH 0655/1054] staging: vme_user: bound slave read/write to the kern_buf size The SLAVE-path helpers buffer_to_user() and buffer_from_user() copy 'count' bytes into/out of the fixed-size kern_buf (size_buf == PCI_BUF_SIZE == 0x20000, 128 KiB) using *ppos as the offset, without bounding *ppos + count against size_buf. vme_user_write()/vme_user_read() only clamp count to the VME window size (image_size = vme_get_size(resource)), which VME_SET_SLAVE sets from the user-supplied slave.size -- validated against the VME address space (up to VME_A32_MAX = 4 GiB), not against PCI_BUF_SIZE. When the window exceeds 128 KiB, a write()/read() copies past the kern_buf allocation. Clamp count against size_buf in both helpers, with an early return when *ppos is already at/after the buffer end. *ppos is >= 0 here (the caller rejects negative offsets), so size_buf - *ppos cannot wrap. This mirrors the existing clamp in the MASTER-path helpers resource_to_user() / resource_from_user(), and matches the read()/write() convention of a short transfer at end-of-buffer. Found by static analysis (CodeQL taint tracking + CBMC bounded model checking) and confirmed dynamically under KASAN with the vme_fake bridge: BUG: KASAN: slab-out-of-bounds in _copy_from_user+0x2d/0x80 Write of size 262144 at addr ffff888004100000 by task trigger/68 _copy_from_user+0x2d/0x80 vme_user_write+0x13e/0x240 [vme_user] vfs_write+0x1b8/0x7a0 ksys_write+0xb8/0x150 Fixes: f00a86d98a1e ("Staging: vme: add VME userspace driver") Cc: stable Signed-off-by: Michael Tautschnig Link: https://patch.msgid.link/20260618114709.72499-1-tautschn@amazon.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/vme_user/vme_user.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/drivers/staging/vme_user/vme_user.c b/drivers/staging/vme_user/vme_user.c index 11e25c2f6b0a..a472a38ef613 100644 --- a/drivers/staging/vme_user/vme_user.c +++ b/drivers/staging/vme_user/vme_user.c @@ -156,6 +156,17 @@ static ssize_t buffer_to_user(unsigned int minor, char __user *buf, { void *image_ptr; + /* + * The slave window (image_size) can exceed the fixed kern_buf + * (size_buf == PCI_BUF_SIZE), so bound the copy to kern_buf. + * *ppos is >= 0 here (checked by the caller), so the + * subtraction below cannot wrap. + */ + if (*ppos >= image[minor].size_buf) + return 0; + if (count > image[minor].size_buf - *ppos) + count = image[minor].size_buf - *ppos; + image_ptr = image[minor].kern_buf + *ppos; if (copy_to_user(buf, image_ptr, (unsigned long)count)) return -EFAULT; @@ -168,6 +179,17 @@ static ssize_t buffer_from_user(unsigned int minor, const char __user *buf, { void *image_ptr; + /* + * The slave window (image_size) can exceed the fixed kern_buf + * (size_buf == PCI_BUF_SIZE), so bound the copy to kern_buf. + * *ppos is >= 0 here (checked by the caller), so the + * subtraction below cannot wrap. + */ + if (*ppos >= image[minor].size_buf) + return 0; + if (count > image[minor].size_buf - *ppos) + count = image[minor].size_buf - *ppos; + image_ptr = image[minor].kern_buf + *ppos; if (copy_from_user(image_ptr, buf, (unsigned long)count)) return -EFAULT; From e8422d89e8af41d87f0e9db564be8e2634f4c602 Mon Sep 17 00:00:00 2001 From: Hao-Qun Huang Date: Sat, 4 Jul 2026 14:58:15 +0800 Subject: [PATCH 0656/1054] staging: vme_user: fix location monitor leak in fake bridge fake_init() allocates a location monitor resource and links it into fake_bridge->lm_resources. The init error path frees this list, but fake_exit() only frees the slave and master resource lists. Loading and unloading the module therefore triggers a kmemleak warning: unreferenced object 0xffff8b8b82aebe40 (size 64): comm "init", pid 1, jiffies 4294894572 backtrace (crc c1e013ef): kmemleak_alloc+0x4e/0x90 __kmalloc_cache_noprof+0x338/0x430 0xffffffffc0602246 do_one_initcall+0x4f/0x320 do_init_module+0x68/0x270 load_module+0x2a3b/0x2d90 Free the lm_resources list in fake_exit() as well, before fake_bridge is freed. Fixes: 658bcdae9c67 ("vme: Adding Fake VME driver") Cc: stable Cc: Martyn Welch Assisted-by: Claude:claude-fable-5 Signed-off-by: Hao-Qun Huang Link: https://patch.msgid.link/20260704065817.403111-1-alvinhuang0603@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/vme_user/vme_fake.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/staging/vme_user/vme_fake.c b/drivers/staging/vme_user/vme_fake.c index 8abaa3165fbb..434cf760ade6 100644 --- a/drivers/staging/vme_user/vme_fake.c +++ b/drivers/staging/vme_user/vme_fake.c @@ -1239,6 +1239,7 @@ static void __exit fake_exit(void) { struct list_head *pos = NULL; struct list_head *tmplist; + struct vme_lm_resource *lm; struct vme_master_resource *master_image; struct vme_slave_resource *slave_image; int i; @@ -1268,6 +1269,13 @@ static void __exit fake_exit(void) vme_unregister_bridge(fake_bridge); fake_crcsr_exit(fake_bridge); + /* resources are stored in link list */ + list_for_each_safe(pos, tmplist, &fake_bridge->lm_resources) { + lm = list_entry(pos, struct vme_lm_resource, list); + list_del(pos); + kfree(lm); + } + /* resources are stored in link list */ list_for_each_safe(pos, tmplist, &fake_bridge->slave_resources) { slave_image = list_entry(pos, struct vme_slave_resource, list); From 151edde741f8bc7f2931c5f44ab376d32b0c8beb Mon Sep 17 00:00:00 2001 From: Hao-Qun Huang Date: Sat, 4 Jul 2026 14:58:16 +0800 Subject: [PATCH 0657/1054] staging: vme_user: fix location monitor leak in tsi148 bridge tsi148_probe() allocates a location monitor resource and links it into tsi148_bridge->lm_resources. The probe error path frees this list, but tsi148_remove() only frees the dma, slave and master resource lists, so the location monitor resource is leaked on device unbind or module unload. Free the lm_resources list in tsi148_remove() as well, before tsi148_bridge is freed. Fixes: d22b8ed9a3b0 ("Staging: vme: add Tundra TSI148 VME-PCI Bridge driver") Cc: stable Cc: Martyn Welch Assisted-by: Claude:claude-fable-5 Signed-off-by: Hao-Qun Huang Link: https://patch.msgid.link/20260704065817.403111-2-alvinhuang0603@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/vme_user/vme_tsi148.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/staging/vme_user/vme_tsi148.c b/drivers/staging/vme_user/vme_tsi148.c index 4cf3486646ce..c695ad9b4ca2 100644 --- a/drivers/staging/vme_user/vme_tsi148.c +++ b/drivers/staging/vme_user/vme_tsi148.c @@ -2534,6 +2534,7 @@ static void tsi148_remove(struct pci_dev *pdev) { struct list_head *pos = NULL; struct list_head *tmplist; + struct vme_lm_resource *lm; struct vme_master_resource *master_image; struct vme_slave_resource *slave_image; struct vme_dma_resource *dma_ctrlr; @@ -2590,6 +2591,13 @@ static void tsi148_remove(struct pci_dev *pdev) tsi148_crcsr_exit(tsi148_bridge, pdev); + /* resources are stored in link list */ + list_for_each_safe(pos, tmplist, &tsi148_bridge->lm_resources) { + lm = list_entry(pos, struct vme_lm_resource, list); + list_del(pos); + kfree(lm); + } + /* resources are stored in link list */ list_for_each_safe(pos, tmplist, &tsi148_bridge->dma_resources) { dma_ctrlr = list_entry(pos, struct vme_dma_resource, list); From a1fc19d61f661d47204f095b593de507884849f7 Mon Sep 17 00:00:00 2001 From: Alexandru Hossu Date: Fri, 22 May 2026 02:46:05 +0200 Subject: [PATCH 0658/1054] staging: rtl8723bs: fix WEP length underflow and OOB read in OnAuth() OnAuth() has two bugs in the shared-key authentication path. When the Privacy bit is set, rtw_wep_decrypt() is called without verifying that the frame is long enough to contain a valid WEP IV and ICV. Inside rtw_wep_decrypt(), length is computed as: length = len - WLAN_HDR_A3_LEN - iv_len and then passed as (length - 4) to crc32_le(). If len is less than WLAN_HDR_A3_LEN + iv_len + icv_len (32 bytes), length - 4 is negative and, after the implicit cast to size_t, causes crc32_le() to read far beyond the frame buffer. Add a minimum length check before accessing the IV field and calling the decryption path. When processing a seq=3 response, rtw_get_ie() stores the Challenge Text IE length in ie_len, but the subsequent memcmp() always reads 128 bytes regardless of ie_len. IEEE 802.11 mandates a challenge text of exactly 128 bytes; reject any IE whose length field differs, matching the check already applied to OnAuthClient(). Fixes: 554c0a3abf21 ("staging: Add rtl8723bs sdio wifi driver") Cc: stable Signed-off-by: Alexandru Hossu Link: https://patch.msgid.link/20260522004605.1039209-1-hossu.alexandru@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_mlme_ext.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c index a86d6f97cf02..7198be795000 100644 --- a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c +++ b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c @@ -677,6 +677,9 @@ unsigned int OnAuth(struct adapter *padapter, union recv_frame *precv_frame) if ((pmlmeinfo->state&0x03) != WIFI_FW_AP_STATE) return _FAIL; + if (len < WLAN_HDR_A3_LEN) + return _FAIL; + sa = GetAddr2Ptr(pframe); auth_mode = psecuritypriv->dot11AuthAlgrthm; @@ -688,6 +691,9 @@ unsigned int OnAuth(struct adapter *padapter, union recv_frame *precv_frame) prxattrib->hdrlen = WLAN_HDR_A3_LEN; prxattrib->encrypt = _WEP40_; + if (len < WLAN_HDR_A3_LEN + 8) + return _FAIL; + iv = pframe+prxattrib->hdrlen; prxattrib->key_index = ((iv[3]>>6)&0x3); @@ -787,7 +793,7 @@ unsigned int OnAuth(struct adapter *padapter, union recv_frame *precv_frame) p = rtw_get_ie(pframe + WLAN_HDR_A3_LEN + 4 + _AUTH_IE_OFFSET_, WLAN_EID_CHALLENGE, (int *)&ie_len, len - WLAN_HDR_A3_LEN - _AUTH_IE_OFFSET_ - 4); - if (!p || ie_len <= 0) { + if (!p || ie_len != 128) { status = WLAN_STATUS_CHALLENGE_FAIL; goto auth_fail; } From ed51de4a86e173c3b0ef78e039c2e49e08b11f16 Mon Sep 17 00:00:00 2001 From: Alexandru Hossu Date: Fri, 22 May 2026 02:45:25 +0200 Subject: [PATCH 0659/1054] staging: rtl8723bs: fix OOB read in update_beacon_info() IE loop The IE parsing loop in update_beacon_info() advances by (pIE->length + 2) each iteration but only guards on i < len. When a malicious AP sends a Beacon whose last IE has only one byte remaining in the frame (the element_id byte lands at len-1), the loop reads pIE->length from one byte past the allocated receive buffer. Additionally, even when the header bytes are in bounds, pIE->length itself can extend the data window beyond len, passing a truncated IE to the handler functions. Add two guards at the top of the loop body: 1. Break if fewer than sizeof(*pIE) bytes remain (can't read header). 2. Break if the IE's declared data extends past len. Also replace i += (pIE->length + 2) with i += sizeof(*pIE) + pIE->length for consistency with the sizeof(*pIE) guards added above. Fixes: 554c0a3abf21 ("staging: Add rtl8723bs sdio wifi driver") Cc: stable Reviewed-by: Luka Gejak Signed-off-by: Alexandru Hossu Link: https://patch.msgid.link/20260522004531.1038924-2-hossu.alexandru@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_wlan_util.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_wlan_util.c b/drivers/staging/rtl8723bs/core/rtw_wlan_util.c index 1d37c2d5b10d..b355d8a3ceab 100644 --- a/drivers/staging/rtl8723bs/core/rtw_wlan_util.c +++ b/drivers/staging/rtl8723bs/core/rtw_wlan_util.c @@ -1262,7 +1262,11 @@ void update_beacon_info(struct adapter *padapter, u8 *pframe, uint pkt_len, stru len = pkt_len - (_BEACON_IE_OFFSET_ + WLAN_HDR_A3_LEN); for (i = 0; i < len;) { + if (i + sizeof(*pIE) > len) + break; pIE = (struct ndis_80211_var_ie *)(pframe + (_BEACON_IE_OFFSET_ + WLAN_HDR_A3_LEN) + i); + if (i + sizeof(*pIE) + pIE->length > len) + break; switch (pIE->element_id) { case WLAN_EID_VENDOR_SPECIFIC: @@ -1287,7 +1291,7 @@ void update_beacon_info(struct adapter *padapter, u8 *pframe, uint pkt_len, stru break; } - i += (pIE->length + 2); + i += sizeof(*pIE) + pIE->length; } } From ef61d628dfad38fead1fd2e08979ae9126d011d5 Mon Sep 17 00:00:00 2001 From: Alexandru Hossu Date: Fri, 22 May 2026 02:45:26 +0200 Subject: [PATCH 0660/1054] staging: rtl8723bs: fix OOB reads in IE loops in issue_assocreq() and join_cmd_hdl() Two IE parsing loops are missing the header bounds checks before they dereference pIE->length: - issue_assocreq() walks pmlmeinfo->network.ies to build the association request. If the stored IE data ends with only an element_id byte and no length byte, pIE->length is read one byte past the end of the buffer. - join_cmd_hdl() walks pnetwork->ies during station join and has the same problem under the same conditions. Both buffers are filled from AP beacon and probe-response frames, so a malicious AP that sends a truncated final IE can trigger the issue. Apply the two-guard pattern established in update_beacon_info(): 1. Break if fewer than sizeof(*pIE) bytes remain. 2. Break if the IE's declared data extends past the buffer end. Fixes: 554c0a3abf21 ("staging: Add rtl8723bs sdio wifi driver") Cc: stable Reviewed-by: Luka Gejak Signed-off-by: Alexandru Hossu Link: https://patch.msgid.link/20260522004531.1038924-3-hossu.alexandru@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_mlme_ext.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c index 7198be795000..70e864cd530d 100644 --- a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c +++ b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c @@ -2861,7 +2861,11 @@ void issue_assocreq(struct adapter *padapter) /* vendor specific IE, such as WPA, WMM, WPS */ for (i = sizeof(struct ndis_802_11_fix_ie); i < pmlmeinfo->network.ie_length;) { + if (i + sizeof(*pIE) > pmlmeinfo->network.ie_length) + break; pIE = (struct ndis_80211_var_ie *)(pmlmeinfo->network.ies + i); + if (i + sizeof(*pIE) + pIE->length > pmlmeinfo->network.ie_length) + break; switch (pIE->element_id) { case WLAN_EID_VENDOR_SPECIFIC: @@ -5189,7 +5193,11 @@ u8 join_cmd_hdl(struct adapter *padapter, u8 *pbuf) /* sizeof(struct ndis_802_11_fix_ie) */ for (i = _FIXED_IE_LENGTH_; i < pnetwork->ie_length;) { + if (i + sizeof(*pIE) > pnetwork->ie_length) + break; pIE = (struct ndis_80211_var_ie *)(pnetwork->ies + i); + if (i + sizeof(*pIE) + pIE->length > pnetwork->ie_length) + break; switch (pIE->element_id) { case WLAN_EID_VENDOR_SPECIFIC:/* Get WMM IE. */ From 5a752a616e756844388a1a45404db9fc29fec655 Mon Sep 17 00:00:00 2001 From: Alexandru Hossu Date: Fri, 22 May 2026 02:45:27 +0200 Subject: [PATCH 0661/1054] staging: rtl8723bs: fix heap buffer overflow in rtw_cfg80211_set_wpa_ie() supplicant_ie is a 256-byte array in struct security_priv. The WPA and WPA2 IE copy paths use: memcpy(padapter->securitypriv.supplicant_ie, &pwpa[0], wpa_ielen + 2); where wpa_ielen is the raw IE length field (u8, 0-255). When a local user supplies a connect request via nl80211 with a crafted WPA IE of length 255, wpa_ielen + 2 equals 257, overflowing the 256-byte buffer by one byte into the adjacent last_mic_err_time field. rtw_parse_wpa_ie() does not prevent this: its length consistency check compares *(wpa_ie+1) against (u8)(wpa_ie_len-2), which is (u8)(255) == 255 when wpa_ie_len = 257, so the check passes silently. Add explicit bounds checks for both the WPA and WPA2 paths before the memcpy, rejecting any IE whose total size (wpa_ielen + 2) exceeds the supplicant_ie buffer. Fixes: 554c0a3abf21 ("staging: Add rtl8723bs sdio wifi driver") Cc: stable Reviewed-by: Luka Gejak Signed-off-by: Alexandru Hossu Link: https://patch.msgid.link/20260522004531.1038924-4-hossu.alexandru@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c index 1484336d7551..6a97afd89dc7 100644 --- a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c +++ b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c @@ -1446,6 +1446,10 @@ static int rtw_cfg80211_set_wpa_ie(struct adapter *padapter, u8 *pie, size_t iel pwpa = rtw_get_wpa_ie(buf, &wpa_ielen, ielen); if (pwpa && wpa_ielen > 0) { + if (wpa_ielen + 2 > sizeof(padapter->securitypriv.supplicant_ie)) { + ret = -EINVAL; + goto exit; + } if (rtw_parse_wpa_ie(pwpa, wpa_ielen + 2, &group_cipher, &pairwise_cipher, NULL) == _SUCCESS) { padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_8021X; padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeWPAPSK; @@ -1455,6 +1459,10 @@ static int rtw_cfg80211_set_wpa_ie(struct adapter *padapter, u8 *pie, size_t iel pwpa2 = rtw_get_wpa2_ie(buf, &wpa2_ielen, ielen); if (pwpa2 && wpa2_ielen > 0) { + if (wpa2_ielen + 2 > sizeof(padapter->securitypriv.supplicant_ie)) { + ret = -EINVAL; + goto exit; + } if (rtw_parse_wpa2_ie(pwpa2, wpa2_ielen + 2, &group_cipher, &pairwise_cipher, NULL) == _SUCCESS) { padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_8021X; padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeWPA2PSK; From f8001e1a516ba3b495728c65b61f799cbfad6bd0 Mon Sep 17 00:00:00 2001 From: Alexandru Hossu Date: Fri, 22 May 2026 02:45:28 +0200 Subject: [PATCH 0662/1054] staging: rtl8723bs: fix OOB write in HT_caps_handler() HT_caps_handler() iterates pIE->length bytes and writes into HT_caps.u.HT_cap[], which is a fixed 26-byte array (sizeof struct HT_caps_element). Because pIE->length is a raw u8 from an over-the-air 802.11 AssocResponse frame and is never validated, a malicious AP can set it up to 255, causing up to 229 bytes of out-of-bounds writes into adjacent fields of struct mlme_ext_info. Truncate the iteration count to the size of HT_caps.u.HT_cap using umin() so that data from a longer-than-expected IE is silently ignored rather than written out of bounds, preserving interoperability with APs that pad the element. An early return on oversized IEs was considered but rejected: it would bypass the pmlmeinfo->HT_caps_enable = 1 assignment that precedes the loop, silently disabling HT mode for APs that append extra bytes to the HT Capabilities IE. Fixes: 554c0a3abf21 ("staging: Add rtl8723bs sdio wifi driver") Cc: stable Signed-off-by: Alexandru Hossu Reviewed-by: Luka Gejak Link: https://patch.msgid.link/20260522004531.1038924-5-hossu.alexandru@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_wlan_util.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_wlan_util.c b/drivers/staging/rtl8723bs/core/rtw_wlan_util.c index b355d8a3ceab..4c0eb93f412e 100644 --- a/drivers/staging/rtl8723bs/core/rtw_wlan_util.c +++ b/drivers/staging/rtl8723bs/core/rtw_wlan_util.c @@ -909,7 +909,8 @@ void HT_caps_handler(struct adapter *padapter, struct ndis_80211_var_ie *pIE) pmlmeinfo->HT_caps_enable = 1; - for (i = 0; i < (pIE->length); i++) { + for (i = 0; i < umin(pIE->length, + sizeof(pmlmeinfo->HT_caps.u.HT_cap)); i++) { if (i != 2) { /* Commented by Albert 2010/07/12 */ /* Got the endian issue here. */ From f9654207e92283e0acac5d64fe5f8835383b5a23 Mon Sep 17 00:00:00 2001 From: Alexandru Hossu Date: Fri, 22 May 2026 02:45:29 +0200 Subject: [PATCH 0663/1054] staging: rtl8723bs: fix OOB read in OnAssocRsp() IE loop The IE parsing loop in OnAssocRsp() advances by (pIE->length + 2) each iteration but only guards on i < pkt_len. When a malicious AP sends an AssocResponse whose last IE has only one byte remaining in the frame (the element_id byte lands at pkt_len-1), the loop reads pIE->length from pframe[pkt_len], which is one byte past the allocated receive buffer. Additionally, even when the header bytes are in bounds, pIE->length itself can extend the data window beyond pkt_len, silently passing a truncated IE to the handler functions. Add two guards at the top of the loop body: 1. Break if fewer than sizeof(*pIE) bytes remain (can't read header). 2. Break if the IE's declared data extends past pkt_len. Fixes: 554c0a3abf21 ("staging: Add rtl8723bs sdio wifi driver") Cc: stable Signed-off-by: Alexandru Hossu Reviewed-by: Luka Gejak Link: https://patch.msgid.link/20260522004531.1038924-6-hossu.alexandru@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_mlme_ext.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c index 70e864cd530d..a443b3530fb9 100644 --- a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c +++ b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c @@ -1371,7 +1371,11 @@ unsigned int OnAssocRsp(struct adapter *padapter, union recv_frame *precv_frame) /* to handle HT, WMM, rate adaptive, update MAC reg */ /* for not to handle the synchronous IO in the tasklet */ for (i = (6 + WLAN_HDR_A3_LEN); i < pkt_len;) { + if (i + sizeof(*pIE) > pkt_len) + break; pIE = (struct ndis_80211_var_ie *)(pframe + i); + if (i + sizeof(*pIE) + pIE->length > pkt_len) + break; switch (pIE->element_id) { case WLAN_EID_VENDOR_SPECIFIC: From 3bf39f711ff27c64be8680a8938bcc5001982e81 Mon Sep 17 00:00:00 2001 From: Alexandru Hossu Date: Fri, 22 May 2026 02:45:30 +0200 Subject: [PATCH 0664/1054] staging: rtl8723bs: fix OOB reads in is_ap_in_tkip() IE loop The loop in is_ap_in_tkip() iterates over IEs without verifying that enough bytes remain before dereferencing the IE header or its payload: - pIE->element_id and pIE->length are read without checking that i + sizeof(*pIE) <= ie_length, so a truncated IE at the end of the buffer causes an OOB read. - For WLAN_EID_VENDOR_SPECIFIC the code compares pIE->data + 12, which requires pIE->length >= 16. For WLAN_EID_RSN it compares pIE->data + 8, requiring pIE->length >= 12. Neither requirement is checked. Add the missing IE header and payload bounds checks and guard each data access with an explicit pIE->length minimum, matching the pattern established in update_beacon_info(). Fixes: 554c0a3abf21 ("staging: Add rtl8723bs sdio wifi driver") Cc: stable Signed-off-by: Alexandru Hossu Link: https://patch.msgid.link/20260522004531.1038924-7-hossu.alexandru@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_wlan_util.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_wlan_util.c b/drivers/staging/rtl8723bs/core/rtw_wlan_util.c index 4c0eb93f412e..a4de538722b5 100644 --- a/drivers/staging/rtl8723bs/core/rtw_wlan_util.c +++ b/drivers/staging/rtl8723bs/core/rtw_wlan_util.c @@ -1308,15 +1308,23 @@ unsigned int is_ap_in_tkip(struct adapter *padapter) for (i = sizeof(struct ndis_802_11_fix_ie); i < pmlmeinfo->network.ie_length;) { pIE = (struct ndis_80211_var_ie *)(pmlmeinfo->network.ies + i); + if (i + sizeof(*pIE) > pmlmeinfo->network.ie_length) + break; + if (i + sizeof(*pIE) + pIE->length > pmlmeinfo->network.ie_length) + break; + switch (pIE->element_id) { case WLAN_EID_VENDOR_SPECIFIC: - if ((!memcmp(pIE->data, RTW_WPA_OUI, 4)) && (!memcmp((pIE->data + 12), WPA_TKIP_CIPHER, 4))) + if (pIE->length >= 16 && + !memcmp(pIE->data, RTW_WPA_OUI, 4) && + !memcmp((pIE->data + 12), WPA_TKIP_CIPHER, 4)) return true; break; case WLAN_EID_RSN: - if (!memcmp((pIE->data + 8), RSN_TKIP_CIPHER, 4)) + if (pIE->length >= 12 && + !memcmp((pIE->data + 8), RSN_TKIP_CIPHER, 4)) return true; break; @@ -1324,7 +1332,7 @@ unsigned int is_ap_in_tkip(struct adapter *padapter) break; } - i += (pIE->length + 2); + i += sizeof(*pIE) + pIE->length; } return false; From 1463ca3ec6601cbb097d8d87dbf5dcf1cb86a344 Mon Sep 17 00:00:00 2001 From: Alexandru Hossu Date: Fri, 22 May 2026 02:45:31 +0200 Subject: [PATCH 0665/1054] staging: rtl8723bs: fix OOB reads in rtw_get_sec_ie(), rtw_get_wapi_ie(), and rtw_get_wps_attr() Three IE/attribute parsing functions have missing bounds checks. rtw_get_sec_ie() and rtw_get_wapi_ie() iterate over a raw IE buffer without verifying that the header bytes (tag + length) are within the remaining buffer before reading them. Additionally, rtw_get_sec_ie() compares the 4-byte WPA OUI at cnt+2 without checking that at least 6 bytes remain, and rtw_get_wapi_ie() compares a 4-byte WAPI OUI at cnt+6 without checking that at least 10 bytes remain. rtw_get_wps_attr() reads wps_ie[0] and wps_ie+2 unconditionally at entry, before verifying that wps_ielen is large enough to contain the 6-byte WPS IE header (element_id + length + 4-byte OUI). Inside the attribute loop, get_unaligned_be16() is called on attr_ptr and attr_ptr+2 without checking that 4 bytes remain in the buffer. Add a cnt+2 bounds check before each loop body in rtw_get_sec_ie() and rtw_get_wapi_ie(), guard each multi-byte comparison with a minimum IE length requirement, add a wps_ielen < 6 early return in rtw_get_wps_attr(), and add a 4-byte bounds check in its inner loop. Fixes: 554c0a3abf21 ("staging: Add rtl8723bs sdio wifi driver") Cc: stable Signed-off-by: Alexandru Hossu Link: https://patch.msgid.link/20260522004531.1038924-8-hossu.alexandru@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_ieee80211.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/staging/rtl8723bs/core/rtw_ieee80211.c b/drivers/staging/rtl8723bs/core/rtw_ieee80211.c index d0bbe1bb979c..54f805a6b5ce 100644 --- a/drivers/staging/rtl8723bs/core/rtw_ieee80211.c +++ b/drivers/staging/rtl8723bs/core/rtw_ieee80211.c @@ -583,9 +583,14 @@ int rtw_get_wapi_ie(u8 *in_ie, uint in_len, u8 *wapi_ie, u16 *wapi_len) cnt = (_TIMESTAMP_ + _BEACON_ITERVAL_ + _CAPABILITY_); while (cnt < in_len) { + if (cnt + 2 > in_len) + break; + if (cnt + 2 + in_ie[cnt + 1] > in_len) + break; authmode = in_ie[cnt]; if (authmode == WLAN_EID_BSS_AC_ACCESS_DELAY && + in_ie[cnt + 1] >= 8 && (!memcmp(&in_ie[cnt + 6], wapi_oui1, 4) || !memcmp(&in_ie[cnt + 6], wapi_oui2, 4))) { if (wapi_ie) @@ -615,9 +620,14 @@ void rtw_get_sec_ie(u8 *in_ie, uint in_len, u8 *rsn_ie, u16 *rsn_len, u8 *wpa_ie cnt = (_TIMESTAMP_ + _BEACON_ITERVAL_ + _CAPABILITY_); while (cnt < in_len) { + if (cnt + 2 > in_len) + break; + if (cnt + 2 + in_ie[cnt + 1] > in_len) + break; authmode = in_ie[cnt]; if ((authmode == WLAN_EID_VENDOR_SPECIFIC) && + in_ie[cnt + 1] >= 4 && (!memcmp(&in_ie[cnt + 2], &wpa_oui[0], 4))) { if (wpa_ie) memcpy(wpa_ie, &in_ie[cnt], in_ie[cnt + 1] + 2); @@ -698,6 +708,9 @@ u8 *rtw_get_wps_attr(u8 *wps_ie, uint wps_ielen, u16 target_attr_id, u8 *buf_att if (len_attr) *len_attr = 0; + if (wps_ielen < 6) + return attr_ptr; + if ((wps_ie[0] != WLAN_EID_VENDOR_SPECIFIC) || (memcmp(wps_ie + 2, wps_oui, 4))) { return attr_ptr; @@ -708,6 +721,8 @@ u8 *rtw_get_wps_attr(u8 *wps_ie, uint wps_ielen, u16 target_attr_id, u8 *buf_att while (attr_ptr - wps_ie < wps_ielen) { /* 4 = 2(Attribute ID) + 2(Length) */ + if (attr_ptr + 4 > wps_ie + wps_ielen) + break; u16 attr_id = get_unaligned_be16(attr_ptr); u16 attr_data_len = get_unaligned_be16(attr_ptr + 2); u16 attr_len = attr_data_len + 4; From cb8afea4655ff004fa7feee825d5c79783525383 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?HE=20WEI=20=28=E3=82=AE=E3=82=AB=E3=82=AF=29?= Date: Tue, 7 Jul 2026 18:48:28 +0900 Subject: [PATCH 0666/1054] wifi: cfg80211: bound element ID read when checking non-inheritance MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cfg80211_is_element_inherited() reads the first data octet of the candidate element (id = elem->data[0]) to look it up in an extension non-inheritance list. It does so after testing elem->id, but without verifying that the element actually has a data octet. A zero-length extension element (WLAN_EID_EXTENSION with length 0) therefore makes it read one octet past the end of the element. _ieee802_11_parse_elems_full() runs this check for every element of a frame once a non-inheritance context exists -- e.g. while parsing a per-STA profile of a Multi-Link element in a (re)association response, or a non-transmitted BSS profile -- so a crafted frame from an AP can trigger a one-octet slab-out-of-bounds read during element parsing: BUG: KASAN: slab-out-of-bounds in cfg80211_is_element_inherited Read of size 1 ... in net/wireless/scan.c Return early (treat the element as inherited) when an extension element carries no data, mirroring the existing handling of empty ID lists. The bug was found by fuzzing ieee802_11_parse_elems_full() under KASAN. Fixes: f7dacfb11475 ("cfg80211: support non-inheritance element") Signed-off-by: HE WEI (ギカク) Link: https://patch.msgid.link/20260707094828.16465-1-skyexpoc@gmail.com Signed-off-by: Johannes Berg --- net/wireless/scan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 5c97b5bd5d69..071083cc3367 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -205,7 +205,7 @@ bool cfg80211_is_element_inherited(const struct element *elem, return true; if (elem->id == WLAN_EID_EXTENSION) { - if (!ext_id_len) + if (!ext_id_len || !elem->datalen) return true; loop_len = ext_id_len; list = &non_inherit_elem->data[3 + id_len]; From 4e28aa8f7ee26d67a4addee6e3980f1cbf861b49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20K=C3=B6nig?= Date: Sat, 4 Jul 2026 10:41:33 +0200 Subject: [PATCH 0667/1054] drm/drm_exec: avoid indirect goto MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The drm_exec component uses a variable with scope limited to the for() and an indirect goto to allow instantiating multiple macros in the same function. This unfortunately doesn't work well with certain compilers when the indirect goto can't be lowered to a direct jump. Switch the indirect goto to a direct goto, the drawback is that we now can't use the dma_exec_until_all_locked() macro in the same function multiple times. The is currently only one user of this and only as a hacky workaround which is about to be removed. So document that the __label__ statement should be used when the macro is used multiple times and fix the tests and the only use case where that is necessary. Suggested-by: Peter Zijlstra Signed-off-by: Christian König Fixes: 9920249a5288 ("drm/amdgpu: convert amdgpu_vm_lock_by_pasid() to drm_exec") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202606231854.7LeCtlLe-lkp@intel.com/ Closes: https://lore.kernel.org/oe-kbuild-all/202606232356.gwHMAJAW-lkp@intel.com/ Closes: https://lore.kernel.org/oe-kbuild-all/202606240753.kYjobJVl-lkp@intel.com/ Closes: https://lore.kernel.org/oe-kbuild-all/202606241110.iUga5vVw-lkp@intel.com/ Closes: https://lore.kernel.org/oe-kbuild-all/202607031446.1PWG18mN-lkp@intel.com/ Closes: https://lore.kernel.org/oe-kbuild-all/202607031837.HSmBj8pr-lkp@intel.com/ Closes: https://lore.kernel.org/oe-kbuild-all/202607040159.GopyEswS-lkp@intel.com/ Tested-by: Mikhail Gavrilov Reviewed-by: Dave Airlie Link: https://lore.kernel.org/r/20260704084133.122053-1-christian.koenig@amd.com --- drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c | 2 ++ drivers/gpu/drm/tests/drm_exec_test.c | 28 +++++++++++++-------- include/drm/drm_exec.h | 34 ++++++++++++++------------ 3 files changed, 38 insertions(+), 26 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c index fee4c94c2585..fc28d0fdad37 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c @@ -3011,6 +3011,8 @@ bool amdgpu_vm_handle_fault(struct amdgpu_device *adev, u32 pasid, is_compute_context = vm->is_compute_context; if (is_compute_context) { + __label__ drm_exec_retry; + /* Release the root PD lock since svm_range_restore_pages * might try to take it. * TODO: rework svm_range_restore_pages so that this isn't diff --git a/drivers/gpu/drm/tests/drm_exec_test.c b/drivers/gpu/drm/tests/drm_exec_test.c index 2fc47f3b463b..7a374e462348 100644 --- a/drivers/gpu/drm/tests/drm_exec_test.c +++ b/drivers/gpu/drm/tests/drm_exec_test.c @@ -180,19 +180,27 @@ static void test_multiple_loops(struct kunit *test) { struct drm_exec exec; - drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT, 0); - drm_exec_until_all_locked(&exec) { - break; - } - drm_exec_fini(&exec); + __label__ drm_exec_retry; - drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT, 0); - drm_exec_until_all_locked(&exec) - { - break; + drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT, 0); + drm_exec_until_all_locked(&exec) + { + break; + } + drm_exec_fini(&exec); + } + + { + __label__ drm_exec_retry; + + drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT, 0); + drm_exec_until_all_locked(&exec) + { + break; + } + drm_exec_fini(&exec); } - drm_exec_fini(&exec); KUNIT_SUCCEED(test); } diff --git a/include/drm/drm_exec.h b/include/drm/drm_exec.h index 8725ba92ff91..cc2937185a9f 100644 --- a/include/drm/drm_exec.h +++ b/include/drm/drm_exec.h @@ -101,17 +101,6 @@ drm_exec_obj(struct drm_exec *exec, unsigned long index) #define drm_exec_for_each_locked_object_reverse(exec, obj) \ __drm_exec_for_each_locked_object_reverse(exec, obj, __UNIQUE_ID(drm_exec)) -/* - * Helper to drm_exec_until_all_locked(). Don't use directly. - * - * Since labels can't be defined local to the loop's body we use a jump pointer - * to make sure that the retry is only used from within the loop's body. - */ -#define __drm_exec_until_all_locked(exec, _label) \ -_label: \ - for (void *const __maybe_unused __drm_exec_retry_ptr = &&_label; \ - drm_exec_cleanup(exec);) - /** * drm_exec_until_all_locked - loop until all GEM objects are locked * @exec: drm_exec object @@ -119,9 +108,18 @@ _label: \ * Core functionality of the drm_exec object. Loops until all GEM objects are * locked and no more contention exists. At the beginning of the loop it is * guaranteed that no GEM object is locked. + * + * A global label name drm_exec_retry is used, if you need to use more than one + * instance of this macro in the same function the label needs to be made local + * to the block with the __label__ keyword. */ #define drm_exec_until_all_locked(exec) \ - __drm_exec_until_all_locked(exec, __UNIQUE_ID(drm_exec)) + for (bool const __maybe_unused __drm_exec_loop = false; \ + drm_exec_cleanup(exec);) \ + if (false) { \ +drm_exec_retry: __maybe_unused; \ + continue; \ + } else /** * drm_exec_retry_on_contention - restart the loop to grap all locks @@ -129,12 +127,14 @@ _label: \ * * Control flow helper to continue when a contention was detected and we need to * clean up and re-start the loop to prepare all GEM objects. + * The __drm_exec_loop check exists to prevent usage outside of an + * drm_exec_until_all_locked() loop. */ #define drm_exec_retry_on_contention(exec) \ do { \ if (unlikely(drm_exec_is_contended(exec))) \ - goto *__drm_exec_retry_ptr; \ - } while (0) + goto drm_exec_retry; \ + } while (__drm_exec_loop) /** * drm_exec_is_contended - check for contention @@ -154,12 +154,14 @@ static inline bool drm_exec_is_contended(struct drm_exec *exec) * * Unconditionally retry the loop to lock all objects. For consistency, * the exec object needs to be newly initialized. + * The __drm_exec_loop check exists to prevent usage outside of an + * drm_exec_until_all_locked() loop. */ #define drm_exec_retry(_exec) \ do { \ WARN_ON((_exec)->contended != DRM_EXEC_DUMMY); \ - goto *__drm_exec_retry_ptr; \ - } while (0) + goto drm_exec_retry; \ + } while (__drm_exec_loop) /** * drm_exec_ticket - return the ww_acquire_ctx for this exec context From d775b9451eb8f52021dea6483ad758fef81dbf1e Mon Sep 17 00:00:00 2001 From: Jia Wang Date: Thu, 2 Jul 2026 17:22:12 +0800 Subject: [PATCH 0668/1054] gpio: dwapb: Defer clock gating until noirq GPIO consumers such as gpio-keys can enable IRQ wake and adjust the wake trigger type from their suspend callbacks. If the DWAPB controller suspends first, masking interrupts and disabling its clocks in the normal suspend phase prevents that late wake configuration from reliably reaching the hardware. Systems with real DWAPB bus clocks then fail to wake from s2idle through GPIO keys. Save the register context in the normal suspend callback, but defer IRQ masking and clock gating until suspend_noirq. At that point all consumers have finished configuring wake IRQs, so keep the clocks enabled when wake lines are armed and only gate them when no wake source is active. Resume_noirq reenables clocks, if they were gated, before the normal resume path restores registers. Propagate wake requests to the parent irqchip while keeping the local wake mask in sync with failures. Fixes: 6437c7ba69c3 ("gpio: dwapb: Add wakeup source support") Signed-off-by: Jia Wang Link: https://patch.msgid.link/20260702-gpio-dwapb-wakeup-v2-1-203f2f33429f@ultrarisc.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-dwapb.c | 83 ++++++++++++++++++++++++++++++++------- 1 file changed, 69 insertions(+), 14 deletions(-) diff --git a/drivers/gpio/gpio-dwapb.c b/drivers/gpio/gpio-dwapb.c index 7b92b233fafe..21e39fb940fe 100644 --- a/drivers/gpio/gpio-dwapb.c +++ b/drivers/gpio/gpio-dwapb.c @@ -117,6 +117,7 @@ struct dwapb_gpio { unsigned int flags; struct reset_control *rst; struct clk_bulk_data clks[DWAPB_NR_CLOCKS]; + bool clocks_on_for_wake; struct dwapb_gpio_port ports[] __counted_by(nr_ports); }; @@ -364,11 +365,24 @@ static int dwapb_irq_set_wake(struct irq_data *d, unsigned int enable) struct dwapb_gpio *gpio = to_dwapb_gpio(gc); struct dwapb_context *ctx = gpio->ports[0].ctx; irq_hw_number_t bit = irqd_to_hwirq(d); + u32 wake_en = ctx->wake_en; if (enable) - ctx->wake_en |= BIT(bit); + wake_en |= BIT(bit); else - ctx->wake_en &= ~BIT(bit); + wake_en &= ~BIT(bit); + +#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY + if (d->parent_data && !!ctx->wake_en != !!wake_en) { + int err; + + err = irq_chip_set_wake_parent(d, enable); + if (err) + return err; + } +#endif + + ctx->wake_en = wake_en; return 0; } @@ -749,6 +763,8 @@ static int dwapb_gpio_suspend(struct device *dev) int i; scoped_guard(gpio_generic_lock_irqsave, gen_gc) { + gpio->clocks_on_for_wake = false; + for (i = 0; i < gpio->nr_ports; i++) { unsigned int offset; unsigned int idx = gpio->ports[i].idx; @@ -770,30 +786,66 @@ static int dwapb_gpio_suspend(struct device *dev) ctx->int_pol = dwapb_read(gpio, GPIO_INT_POLARITY); ctx->int_type = dwapb_read(gpio, GPIO_INTTYPE_LEVEL); ctx->int_deb = dwapb_read(gpio, GPIO_PORTA_DEBOUNCE); - - /* Mask out interrupts */ - dwapb_write(gpio, GPIO_INTMASK, ~ctx->wake_en); } } } + return 0; +} + +static int dwapb_gpio_suspend_noirq(struct device *dev) +{ + struct dwapb_gpio *gpio = dev_get_drvdata(dev); + struct gpio_generic_chip *gen_gc = &gpio->ports[0].chip; + bool wake_enabled = false; + int i; + + scoped_guard(gpio_generic_lock_irqsave, gen_gc) { + for (i = 0; i < gpio->nr_ports; i++) { + unsigned int idx = gpio->ports[i].idx; + struct dwapb_context *ctx = gpio->ports[i].ctx; + + if (idx == 0) { + wake_enabled = ctx->wake_en; + dwapb_write(gpio, GPIO_INTMASK, ~ctx->wake_en); + break; + } + } + + gpio->clocks_on_for_wake = wake_enabled; + } + + if (wake_enabled) { + device_set_wakeup_path(dev); + return 0; + } + clk_bulk_disable_unprepare(DWAPB_NR_CLOCKS, gpio->clks); return 0; } +static int dwapb_gpio_resume_noirq(struct device *dev) +{ + struct dwapb_gpio *gpio = dev_get_drvdata(dev); + int err; + + if (gpio->clocks_on_for_wake) + return 0; + + err = clk_bulk_prepare_enable(DWAPB_NR_CLOCKS, gpio->clks); + if (err) + dev_err(gpio->dev, "Cannot reenable APB/Debounce clocks\n"); + + return err; +} + static int dwapb_gpio_resume(struct device *dev) { struct dwapb_gpio *gpio = dev_get_drvdata(dev); struct gpio_chip *gc = &gpio->ports[0].chip.gc; struct gpio_generic_chip *gen_gc = to_gpio_generic_chip(gc); - int i, err; - - err = clk_bulk_prepare_enable(DWAPB_NR_CLOCKS, gpio->clks); - if (err) { - dev_err(gpio->dev, "Cannot reenable APB/Debounce clocks\n"); - return err; - } + int i; guard(gpio_generic_lock_irqsave)(gen_gc); @@ -827,8 +879,11 @@ static int dwapb_gpio_resume(struct device *dev) return 0; } -static DEFINE_SIMPLE_DEV_PM_OPS(dwapb_gpio_pm_ops, - dwapb_gpio_suspend, dwapb_gpio_resume); +static const struct dev_pm_ops dwapb_gpio_pm_ops = { + SYSTEM_SLEEP_PM_OPS(dwapb_gpio_suspend, dwapb_gpio_resume) + NOIRQ_SYSTEM_SLEEP_PM_OPS(dwapb_gpio_suspend_noirq, + dwapb_gpio_resume_noirq) +}; static struct platform_driver dwapb_gpio_driver = { .driver = { From aaf7766ba3b99a3834319e7cf939838afc705574 Mon Sep 17 00:00:00 2001 From: Liang Hao Date: Sun, 5 Jul 2026 15:47:59 +0800 Subject: [PATCH 0669/1054] gpio: dwapb: Mask interrupts at hardware initialization GPIO interrupts may retain stale state across warm reboots when peripherals remain powered. If a GPIO line is not explicitly configured for interrupts, this can result in interrupt storms due to missing handlers. Fix this by ensuring all interrupts are masked and disabled at hardware initialization time via the init_hw() callback. Pending interrupts are also cleared to start from a known-safe state. Interrupts will be unmasked only when explicitly configured by userspace or kernel drivers. Signed-off-by: Liang Hao Link: https://patch.msgid.link/20260705074759.47863-1-haohlliang@gmail.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-dwapb.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/gpio/gpio-dwapb.c b/drivers/gpio/gpio-dwapb.c index 21e39fb940fe..aa7c08e60707 100644 --- a/drivers/gpio/gpio-dwapb.c +++ b/drivers/gpio/gpio-dwapb.c @@ -200,6 +200,22 @@ static void dwapb_toggle_trigger(struct dwapb_gpio *gpio, unsigned int offs) dwapb_write(gpio, GPIO_INT_POLARITY, pol); } +static int dwapb_irq_init_hw(struct gpio_chip *gc) +{ + struct dwapb_gpio *gpio = to_dwapb_gpio(gc); + + /* + * GPIO interrupts may retain stale state across warm reboots when + * peripherals stay powered. Force a known-safe state before the GPIO + * irqchip and irq domain are set up. + */ + dwapb_write(gpio, GPIO_INTEN, 0); + dwapb_write(gpio, GPIO_INTMASK, 0xffffffff); + dwapb_write(gpio, GPIO_PORTA_EOI, 0xffffffff); + + return 0; +} + static u32 dwapb_do_irq(struct dwapb_gpio *gpio) { struct gpio_generic_chip *gen_gc = &gpio->ports[0].chip; @@ -471,6 +487,7 @@ static void dwapb_configure_irqs(struct dwapb_gpio *gpio, girq = &gc->irq; girq->handler = handle_bad_irq; girq->default_type = IRQ_TYPE_NONE; + girq->init_hw = dwapb_irq_init_hw; port->pirq = pirq; From ac7c6b4308c5e757eb278d47b9c0761c31ac9d6b Mon Sep 17 00:00:00 2001 From: Robert Mader Date: Fri, 3 Jul 2026 09:32:30 +0200 Subject: [PATCH 0670/1054] drm: Guard DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE The client cap is currently advertised unconditionally, even for drivers that do not support plane color pipelines. If clients supporting the latter, like Wayland compositors or tools like drm_info, enable the client cap on such drivers they will be left without both color pipeline and the legacy properties COLOR_ENCODING and COLOR_RANGE, effectively breaking YUV->RGB conversion support. Prevent that by only marking the cap supported if there are actually planes with color pipelines. Note: while the color pipeline replacement for the legacy properties is still under review (1), we can assume that it will work as a drop-in replacement. That means any plane on any hardware currently supporting the legacy properties will be able to offer a functionally equal color pipeline and there will be no technical reason keep using the legacy properties if both the driver and the client support the new API. [1] https://lore.kernel.org/dri-devel/20260623164812.81110-1-harry.wentland@amd.com/ Signed-off-by: Robert Mader Reviewed-by: Chaitanya Kumar Borah Reviewed-by: Melissa Wen Fixes: 179ab8e7d7b3 ("drm/colorop: Introduce DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE") Link: https://patch.msgid.link/20260703073230.19982-1-robert.mader@collabora.com Suggested-by: Maarten Lankhorst Signed-off-by: Maarten Lankhorst --- drivers/gpu/drm/drm_ioctl.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c index e2df4becce62..9039a39c4324 100644 --- a/drivers/gpu/drm/drm_ioctl.c +++ b/drivers/gpu/drm/drm_ioctl.c @@ -373,13 +373,25 @@ drm_setclientcap(struct drm_device *dev, void *data, struct drm_file *file_priv) return -EINVAL; file_priv->supports_virtualized_cursor_plane = req->value; break; - case DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE: + case DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE: { + struct drm_plane *plane; + bool has_plane_with_color_pipeline = false; + if (!file_priv->atomic) return -EINVAL; if (req->value > 1) return -EINVAL; + drm_for_each_plane(plane, dev) { + if (plane->color_pipeline_property) { + has_plane_with_color_pipeline = true; + break; + } + } + if (!has_plane_with_color_pipeline) + return -EOPNOTSUPP; file_priv->plane_color_pipeline = req->value; break; + } default: return -EINVAL; } From 4d6941c0a8fd44bcc9693a1c8201a1d26dd21bc8 Mon Sep 17 00:00:00 2001 From: Andreas Ziegler Date: Wed, 24 Jun 2026 05:33:41 +0200 Subject: [PATCH 0671/1054] rtla: Fix missing unistd include MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Compiling RTLA 7.1.x with GCC 16 and uClibc as standard library fails with these errors: src/common.c: In function ‘set_signals’: src/common.c:40:17: error: implicit declaration of function ‘alarm’ [-Wimplicit-function-declaration] 40 | alarm(params->duration); | ^~~~~ src/common.c: In function ‘common_apply_config’: src/common.c:187:44: error: implicit declaration of function ‘getpid’; did you mean ‘getpt’? [-Wimplicit-function-declaration] 187 | retval = sched_setaffinity(getpid(), sizeof(params->hk_cpu_set), | ^~~~~~ | getpt In file included from src/common.c:9: src/common.c: In function ‘run_tool’: src/common.c:262:19: error: implicit declaration of function ‘sysconf’; did you mean ‘sscanf’? [-Wimplicit-function-declaration] 262 | nr_cpus = get_nprocs_conf(); | ^~~~~~~~~~~~~~~ src/common.c:262:19: error: ‘_SC_NPROCESSORS_CONF’ undeclared (first use in this function) 262 | nr_cpus = get_nprocs_conf(); | ^~~~~~~~~~~~~~~ src/common.c:262:19: note: each undeclared identifier is reported only once for each function it appears in src/common.c:370:17: error: implicit declaration of function ‘sleep’ [-Wimplicit-function-declaration] 370 | sleep(1); | ^~~~~ Restore the missing unistd.h include. Fixes: 115b06a00875 ("tools/rtla: Consolidate nr_cpus usage across all tools") Signed-off-by: Andreas Ziegler Link: https://lore.kernel.org/r/20260624033342.129970-1-br025@umbiko.net [ capital letter in patch subject, remove EOL after Fixes ] Signed-off-by: Tomas Glozar --- tools/tracing/rtla/src/common.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/tracing/rtla/src/common.c b/tools/tracing/rtla/src/common.c index d0a8a6edbf0c..8c7f5e75b2ec 100644 --- a/tools/tracing/rtla/src/common.c +++ b/tools/tracing/rtla/src/common.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include "common.h" From 3383ffb7ef937317361713ffcc21921a7848511a Mon Sep 17 00:00:00 2001 From: Mostafa Saleh Date: Thu, 2 Jul 2026 10:38:38 +0000 Subject: [PATCH 0672/1054] firmware: arm_ffa: Fix out-of-bound writes in ffa_setup_and_transmit() Sashiko (locally) reports multiple out-of-bound issues in ffa_setup_and_transmit: 1) Writing ep_mem_access->reserved can write out of bounds for FFA versions < 1.2 as ffa_emad_size_get() returns 16 bytes in that case while reserved has an offset of 24. Instead of zeroing fields, memset the struct to zero first based on the FFA version. 2) Make sure there is enough size to write constituents. While at it, convert the only sizeof() in the driver that uses a type instead of variable. Reviewed-by: Sudeep Holla Fixes: 111a833dc5cb ("firmware: arm_ffa: Set reserved/MBZ fields to zero in the memory descriptors") Signed-off-by: Mostafa Saleh Signed-off-by: Sebastian Ene Link: https://patch.msgid.link/20260702103848.1647249-2-sebastianene@google.com Signed-off-by: Marc Zyngier --- drivers/firmware/arm_ffa/driver.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c index eb2782848283..b700b2e93e72 100644 --- a/drivers/firmware/arm_ffa/driver.c +++ b/drivers/firmware/arm_ffa/driver.c @@ -697,11 +697,10 @@ ffa_setup_and_transmit(u32 func_id, void *buffer, u32 max_fragsize, for (idx = 0; idx < args->nattrs; idx++) { ep_mem_access = buffer + ffa_mem_desc_offset(buffer, idx, drv_info->version); + memset(ep_mem_access, 0, ffa_emad_size_get(drv_info->version)); ep_mem_access->receiver = args->attrs[idx].receiver; ep_mem_access->attrs = args->attrs[idx].attrs; ep_mem_access->composite_off = composite_offset; - ep_mem_access->flag = 0; - ep_mem_access->reserved = 0; ffa_emad_impdef_value_init(drv_info->version, ep_mem_access->impdef_val, args->attrs[idx].impdef_val); @@ -741,7 +740,7 @@ ffa_setup_and_transmit(u32 func_id, void *buffer, u32 max_fragsize, constituents = buffer; } - if ((void *)constituents - buffer > max_fragsize) { + if ((void *)constituents + sizeof(*constituents) - buffer > max_fragsize) { pr_err("Memory Region Fragment > Tx Buffer size\n"); return -EFAULT; } @@ -750,7 +749,7 @@ ffa_setup_and_transmit(u32 func_id, void *buffer, u32 max_fragsize, constituents->pg_cnt = args->sg->length / FFA_PAGE_SIZE; constituents->reserved = 0; constituents++; - frag_len += sizeof(struct ffa_mem_region_addr_range); + frag_len += sizeof(*constituents); } while ((args->sg = sg_next(args->sg))); return ffa_transmit_fragment(func_id, addr, buf_sz, frag_len, From b4d961351aa84fdf0148783fb1f3a1391b8a0adb Mon Sep 17 00:00:00 2001 From: Sebastian Ene Date: Thu, 2 Jul 2026 10:38:39 +0000 Subject: [PATCH 0673/1054] firmware: arm_ffa: Fix Endpoint Memory Access Descriptor offset calculation Use the descriptor's `ep_mem_offset` to calculate the start of the endpoint memory access array and to comply with the FF-A spec instead of defaulting to `sizeof(struct ffa_mem_region)`. This requires moving `ffa_mem_region_additional_setup()` earlier in the setup flow. Also, add sanity checks to ensure the calculated descriptor offsets do not exceed `max_fragsize`. Fixes: 113580530ee7 ("firmware: arm_ffa: Update memory descriptor to support v1.1 format") Reviewed-by: Sudeep Holla Signed-off-by: Mostafa Saleh Signed-off-by: Sebastian Ene Link: https://patch.msgid.link/20260702103848.1647249-3-sebastianene@google.com Signed-off-by: Marc Zyngier --- drivers/firmware/arm_ffa/driver.c | 20 +++++++++++++++----- include/linux/arm_ffa.h | 2 +- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c index b700b2e93e72..0ad323decada 100644 --- a/drivers/firmware/arm_ffa/driver.c +++ b/drivers/firmware/arm_ffa/driver.c @@ -685,19 +685,30 @@ ffa_setup_and_transmit(u32 func_id, void *buffer, u32 max_fragsize, struct ffa_composite_mem_region *composite; struct ffa_mem_region_addr_range *constituents; struct ffa_mem_region_attributes *ep_mem_access; - u32 idx, frag_len, length, buf_sz = 0, num_entries = sg_nents(args->sg); + u32 idx, frag_len, length, buf_sz = 0, num_entries = sg_nents(args->sg), ep_offset; + u32 emad_end, emad_size = ffa_emad_size_get(drv_info->version); mem_region->tag = args->tag; mem_region->flags = args->flags; mem_region->sender_id = drv_info->vm_id; mem_region->attributes = ffa_memory_attributes_get(func_id); + + ffa_mem_region_additional_setup(drv_info->version, mem_region); composite_offset = ffa_mem_desc_offset(buffer, args->nattrs, drv_info->version); + if (composite_offset + sizeof(*composite) > max_fragsize) + return -ENXIO; for (idx = 0; idx < args->nattrs; idx++) { - ep_mem_access = buffer + - ffa_mem_desc_offset(buffer, idx, drv_info->version); - memset(ep_mem_access, 0, ffa_emad_size_get(drv_info->version)); + ep_offset = ffa_mem_desc_offset(buffer, idx, drv_info->version); + if (check_add_overflow(ep_offset, emad_size, &emad_end)) + return -ENXIO; + + if (emad_end > max_fragsize) + return -ENXIO; + + ep_mem_access = buffer + ep_offset; + memset(ep_mem_access, 0, emad_size); ep_mem_access->receiver = args->attrs[idx].receiver; ep_mem_access->attrs = args->attrs[idx].attrs; ep_mem_access->composite_off = composite_offset; @@ -707,7 +718,6 @@ ffa_setup_and_transmit(u32 func_id, void *buffer, u32 max_fragsize, } mem_region->handle = 0; mem_region->ep_count = args->nattrs; - ffa_mem_region_additional_setup(drv_info->version, mem_region); composite = buffer + composite_offset; composite->total_pg_cnt = ffa_get_num_pages_sg(args->sg); diff --git a/include/linux/arm_ffa.h b/include/linux/arm_ffa.h index 3c91d4c4153c..c6a9ffd564f7 100644 --- a/include/linux/arm_ffa.h +++ b/include/linux/arm_ffa.h @@ -452,7 +452,7 @@ ffa_mem_desc_offset(struct ffa_mem_region *buf, int count, u32 ffa_version) if (!FFA_MEM_REGION_HAS_EP_MEM_OFFSET(ffa_version)) offset += offsetof(struct ffa_mem_region, ep_mem_offset); else - offset += sizeof(struct ffa_mem_region); + offset += buf->ep_mem_offset; return offset; } From 335f7860846732477a9ce24f0db04dd253ba268c Mon Sep 17 00:00:00 2001 From: Matthew Schwartz Date: Mon, 6 Jul 2026 17:56:14 -0700 Subject: [PATCH 0674/1054] ALSA: hda/realtek: Fix headphone output on ASUS ROG Ally X The ASUS ROG Ally X (RC72LA, PCI SSID 0x1043:0x1eb3) with an ALC294 codec produces no sound from the headphone jack, and unplugging the headphones can leave the speakers muted too. The device uses ALC287_FIXUP_TAS2781_I2C, which chains into the ThinkPad fixup chain. alc285_fixup_thinkpad_x1_gen7() makes the headphone (pin 0x21) and speaker (pin 0x17) share DAC 0x03. That is correct for a ThinkPad, but here the shared DAC is exposed as a "Line Out" control that defaults to zero, so the headphones stay silent when plugged in. Add a dedicated fixup chain for the Ally X that sets up the TAS2781 amplifier and the headset jack without the ThinkPad DAC sharing, and routes the speaker to DAC 0x02 so the headphone and speaker get independent DACs. Also, fixup the wrong product code (it's RC72LA, not RCLA72). Signed-off-by: Matthew Schwartz Link: https://patch.msgid.link/20260707005615.52889-1-matthew.schwartz@linux.dev Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index b6b502450a95..8d409ccacad4 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -4117,6 +4117,8 @@ enum { ALC236_FIXUP_DELL_DUAL_CODECS, ALC287_FIXUP_CS35L41_I2C_2_THINKPAD_ACPI, ALC287_FIXUP_TAS2781_I2C, + ALC287_FIXUP_ASUS_ALLY_X, + ALC287_FIXUP_ASUS_ALLY_X_I2C, ALC295_FIXUP_DELL_TAS2781_I2C, ALC245_FIXUP_TAS2781_SPI_2, ALC287_FIXUP_TXNW2781_I2C, @@ -6476,6 +6478,18 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC285_FIXUP_THINKPAD_HEADSET_JACK, }, + [ALC287_FIXUP_ASUS_ALLY_X] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc285_fixup_speaker2_to_dac1, + .chained = true, + .chain_id = ALC287_FIXUP_ASUS_ALLY_X_I2C, + }, + [ALC287_FIXUP_ASUS_ALLY_X_I2C] = { + .type = HDA_FIXUP_FUNC, + .v.func = tas2781_fixup_tias_i2c, + .chained = true, + .chain_id = ALC225_FIXUP_HEADSET_JACK, + }, [ALC245_FIXUP_TAS2781_SPI_2] = { .type = HDA_FIXUP_FUNC, .v.func = tas2781_fixup_spi, @@ -7461,7 +7475,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1e83, "ASUS GA605W", ALC285_FIXUP_ASUS_GU605_SPI_SPEAKER2_TO_DAC1), SND_PCI_QUIRK(0x1043, 0x1e8e, "ASUS Zephyrus G15", ALC289_FIXUP_ASUS_GA401), SND_PCI_QUIRK(0x1043, 0x1e93, "ASUS ExpertBook B9403CVAR", ALC294_FIXUP_ASUS_HPE), - SND_PCI_QUIRK(0x1043, 0x1eb3, "ASUS Ally RCLA72", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x1043, 0x1eb3, "ASUS Ally RC72LA", ALC287_FIXUP_ASUS_ALLY_X), SND_PCI_QUIRK(0x1043, 0x1ed3, "ASUS HN7306W", ALC287_FIXUP_CS35L41_I2C_2), HDA_CODEC_QUIRK(0x1043, 0x1ee2, "ASUS UM6702RA/RC", ALC285_FIXUP_ASUS_I2C_SPEAKER2_TO_DAC1), SND_PCI_QUIRK(0x1043, 0x1ee2, "ASUS UM6702RA/RC", ALC287_FIXUP_CS35L41_I2C_2), From 883d0cae6d5f840629c5bcacf029c6aa9a9106f4 Mon Sep 17 00:00:00 2001 From: Matthew Schwartz Date: Mon, 6 Jul 2026 17:56:15 -0700 Subject: [PATCH 0675/1054] ALSA: hda/realtek: Fix headset mic on ASUS ROG Ally X The ASUS ROG Ally X (RC72LA, PCI SSID 0x1043:0x1eb3) leaves the combo jack microphone pin (0x19) unconfigured, so a headset microphone is neither detected nor captured. Only the internal microphone is available. Add a pin override that configures node 0x19 as an external headset mic, the same way the original ROG Ally does. The headphone output fixup chain is preserved, so speaker and headphone routing are unchanged. Signed-off-by: Matthew Schwartz Link: https://patch.msgid.link/20260707005615.52889-2-matthew.schwartz@linux.dev Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 8d409ccacad4..984868e3d8bb 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -4118,6 +4118,7 @@ enum { ALC287_FIXUP_CS35L41_I2C_2_THINKPAD_ACPI, ALC287_FIXUP_TAS2781_I2C, ALC287_FIXUP_ASUS_ALLY_X, + ALC287_FIXUP_ASUS_ALLY_X_SPEAKER, ALC287_FIXUP_ASUS_ALLY_X_I2C, ALC295_FIXUP_DELL_TAS2781_I2C, ALC245_FIXUP_TAS2781_SPI_2, @@ -6479,6 +6480,15 @@ static const struct hda_fixup alc269_fixups[] = { .chain_id = ALC285_FIXUP_THINKPAD_HEADSET_JACK, }, [ALC287_FIXUP_ASUS_ALLY_X] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x03a11050 }, /* headset mic */ + { } + }, + .chained = true, + .chain_id = ALC287_FIXUP_ASUS_ALLY_X_SPEAKER, + }, + [ALC287_FIXUP_ASUS_ALLY_X_SPEAKER] = { .type = HDA_FIXUP_FUNC, .v.func = alc285_fixup_speaker2_to_dac1, .chained = true, From dba0bfded433d412506eb674d6af77cdca82e259 Mon Sep 17 00:00:00 2001 From: Longlong Xia Date: Mon, 6 Jul 2026 12:26:39 +0900 Subject: [PATCH 0676/1054] ALSA: firewire: Drop redundant mod_devicetable.h includes The ALSA FireWire driver headers include , which already provides struct ieee1394_device_id via . Drop the now-redundant direct includes from the per-device headers. Signed-off-by: Longlong Xia Reviewed-by: Takashi Sakamoto Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260706032639.273929-1-xialonglong2025@163.com --- sound/firewire/bebob/bebob.h | 1 - sound/firewire/dice/dice.h | 1 - sound/firewire/digi00x/digi00x.h | 1 - sound/firewire/fireface/ff.h | 1 - sound/firewire/fireworks/fireworks.h | 1 - sound/firewire/motu/motu.h | 1 - sound/firewire/oxfw/oxfw.h | 1 - sound/firewire/tascam/tascam.h | 1 - 8 files changed, 8 deletions(-) diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h index 4d73ecb30d79..41e019b9e24e 100644 --- a/sound/firewire/bebob/bebob.h +++ b/sound/firewire/bebob/bebob.h @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h index 7744ea6a0791..5abae26d2bba 100644 --- a/sound/firewire/dice/dice.h +++ b/sound/firewire/dice/dice.h @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include diff --git a/sound/firewire/digi00x/digi00x.h b/sound/firewire/digi00x/digi00x.h index 82b647d383c5..60d17a6fddd1 100644 --- a/sound/firewire/digi00x/digi00x.h +++ b/sound/firewire/digi00x/digi00x.h @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include diff --git a/sound/firewire/fireface/ff.h b/sound/firewire/fireface/ff.h index 7e42f5778a8a..c9c35c67db27 100644 --- a/sound/firewire/fireface/ff.h +++ b/sound/firewire/fireface/ff.h @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include diff --git a/sound/firewire/fireworks/fireworks.h b/sound/firewire/fireworks/fireworks.h index c8d5879efe28..a8aadf754e72 100644 --- a/sound/firewire/fireworks/fireworks.h +++ b/sound/firewire/fireworks/fireworks.h @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include diff --git a/sound/firewire/motu/motu.h b/sound/firewire/motu/motu.h index c66be0a89ccf..7333a76b50aa 100644 --- a/sound/firewire/motu/motu.h +++ b/sound/firewire/motu/motu.h @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include diff --git a/sound/firewire/oxfw/oxfw.h b/sound/firewire/oxfw/oxfw.h index 39ea9a6dde33..aeb83e1595b7 100644 --- a/sound/firewire/oxfw/oxfw.h +++ b/sound/firewire/oxfw/oxfw.h @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include diff --git a/sound/firewire/tascam/tascam.h b/sound/firewire/tascam/tascam.h index d07ffcb27be6..9c42c9b48f21 100644 --- a/sound/firewire/tascam/tascam.h +++ b/sound/firewire/tascam/tascam.h @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include From af05b4e06279bbe34f8044b1e82f0725b5da91b7 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Tue, 16 Jun 2026 18:55:38 +0100 Subject: [PATCH 0677/1054] ring-buffer: Fix ring_buffer_read_page() copying only one event per page Commit 8928e4a3be34 ("ring-buffer: Show persistent buffer dropped events in trace_pipe file") split the "commit" variable in ring_buffer_read_page() into "commit" (raw) and "size" (masked page size), but the inner copy loop's terminator was changed to compare rpos against "event_size" instead of "size". rpos is the cumulative read offset within the page; event_size is the length of the single event just copied. The loop thus breaks after the first event, so only one event is copied per call. This regresses the per-event memcpy path (partial reads, the active commit page, and mapped/remote buffers) used by splice/trace_pipe_raw and mmap consumers into a one-event-at-a-time read. Compare rpos against the page size as the original code did. Link: https://patch.msgid.link/20260616175538.111628-1-devnexen@gmail.com Fixes: 8928e4a3be34 ("ring-buffer: Show persistent buffer dropped events in trace_pipe file") Signed-off-by: David Carlier Signed-off-by: Steven Rostedt --- kernel/trace/ring_buffer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index d9af2bbaf9c0..12a83d9ffd0a 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -7175,7 +7175,7 @@ int ring_buffer_read_page(struct trace_buffer *buffer, rpos = reader->read; pos += event_size; - if (rpos >= event_size) + if (rpos >= size) break; event = rb_reader_event(cpu_buffer); From f67be28fdf8b5d31ac1cc1152bb17250f9f8f513 Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Tue, 7 Jul 2026 21:24:19 +0800 Subject: [PATCH 0678/1054] ALSA: hda: Fix cached processing coefficient verbs Intel HD Audio defines Coefficient Index and Processing Coefficient as separate audio widget controls in the Audio Widget Verb Definitions: Coefficient Index selects the coefficient slot, while Processing Coefficient accesses the value at the selected slot. hda_reg_read_coef() selects the slot with AC_VERB_SET_COEF_INDEX, but then uses AC_VERB_GET_COEF_INDEX for the value read. That reads back the selected index instead of the coefficient value. hda_reg_write_coef() has the same issue and builds the value write from AC_VERB_GET_COEF_INDEX instead of AC_VERB_SET_PROC_COEF. This only affects the regmap coefficient cache path used by codecs that set codec->cache_coef. Direct coefficient helpers already use the normal SET_COEF_INDEX followed by GET_PROC_COEF or SET_PROC_COEF sequence, which is likely why this has not been noticed widely. Use AC_VERB_GET_PROC_COEF for cached coefficient reads and AC_VERB_SET_PROC_COEF for cached coefficient writes. Fixes: 40ba66a702b8 ("ALSA: hda - Add cache support for COEF read/write") Cc: stable@vger.kernel.org Signed-off-by: Xu Rao Link: https://patch.msgid.link/DB9023BF2920BA99+20260707132419.1731342-1-raoxu@uniontech.com Signed-off-by: Takashi Iwai --- sound/hda/core/regmap.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/hda/core/regmap.c b/sound/hda/core/regmap.c index e7b866fc52c1..d6eb17aa9e08 100644 --- a/sound/hda/core/regmap.c +++ b/sound/hda/core/regmap.c @@ -214,7 +214,7 @@ static int hda_reg_read_coef(struct hdac_device *codec, unsigned int reg, err = snd_hdac_exec_verb(codec, verb, 0, NULL); if (err < 0) return err; - verb = (reg & ~0xfffff) | (AC_VERB_GET_COEF_INDEX << 8); + verb = (reg & ~0xfffff) | (AC_VERB_GET_PROC_COEF << 8); return snd_hdac_exec_verb(codec, verb, 0, val); } @@ -232,7 +232,7 @@ static int hda_reg_write_coef(struct hdac_device *codec, unsigned int reg, err = snd_hdac_exec_verb(codec, verb, 0, NULL); if (err < 0) return err; - verb = (reg & ~0xfffff) | (AC_VERB_GET_COEF_INDEX << 8) | + verb = (reg & ~0xfffff) | (AC_VERB_SET_PROC_COEF << 8) | (val & 0xffff); return snd_hdac_exec_verb(codec, verb, 0, NULL); } From d5c6785f94ead7cd76b38af301ac63cb673485bc Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Wed, 17 Jun 2026 11:58:22 +0100 Subject: [PATCH 0679/1054] tracing: Make tracepoint_printk static as not exported The tracepoint_printk symbol is not exported, so make it static to remove the following sparse warning: kernel/trace/trace.c:90:5: warning: symbol 'tracepoint_printk' was not declared. Should it be static? Fixes: dd293df6395a2 ("tracing: Move trace sysctls into trace.c") Link: https://patch.msgid.link/20260617105822.904164-1-ben.dooks@codethink.co.uk Signed-off-by: Ben Dooks Signed-off-by: Steven Rostedt --- kernel/trace/trace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 299f5ab630b9..18710c190c92 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -87,7 +87,7 @@ void __init disable_tracing_selftest(const char *reason) /* Pipe tracepoints to printk */ static struct trace_iterator *tracepoint_print_iter; -int tracepoint_printk; +static int tracepoint_printk; static bool tracepoint_printk_stop_on_boot __initdata; static bool traceoff_after_boot __initdata; static DEFINE_STATIC_KEY_FALSE(tracepoint_printk_key); From e5d8524108fef14970db85451b7d06e5f6fa51ce Mon Sep 17 00:00:00 2001 From: Yudistira Putra Date: Sun, 21 Jun 2026 05:51:53 -0400 Subject: [PATCH 0680/1054] samples: ftrace: Fix typos in benchmark comment Fix two typos in the ftrace operations sample benchmark comment. Link: https://patch.msgid.link/20260621095153.93762-1-pyudistira519@gmail.com Signed-off-by: Yudistira Putra Signed-off-by: Steven Rostedt --- samples/ftrace/ftrace-ops.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/ftrace/ftrace-ops.c b/samples/ftrace/ftrace-ops.c index 68d6685c80bd..152ffc1a30b6 100644 --- a/samples/ftrace/ftrace-ops.c +++ b/samples/ftrace/ftrace-ops.c @@ -232,8 +232,8 @@ static int __init ftrace_ops_sample_init(void) ops_destroy(ops_irrelevant, nr_ops_irrelevant); /* - * The benchmark completed sucessfully, but there's no reason to keep - * the module around. Return an error do the user doesn't have to + * The benchmark completed successfully, but there's no reason to keep + * the module around. Return an error so the user doesn't have to * manually unload the module. */ return -EINVAL; From c3e94604675e3db186111b8942650d86577df9b0 Mon Sep 17 00:00:00 2001 From: Yuanhe Shu Date: Wed, 24 Jun 2026 14:17:15 +0800 Subject: [PATCH 0681/1054] tracing: Fix NULL pointer dereference in func_set_flag() func_set_flag() dereferences tr->current_trace_flags before verifying that the current tracer is actually the function tracer. When the active tracer has been switched away from "function" (e.g., to "wakeup_rt"), tr->current_trace_flags can be NULL, leading to a NULL pointer dereference and kernel crash. The call chain that triggers this is: trace_options_write() -> __set_tracer_option() -> trace->set_flag() /* func_set_flag */ In func_set_flag(), the first operation is: if (!!set == !!(tr->current_trace_flags->val & bit)) This dereferences tr->current_trace_flags unconditionally. The safety check that guards against a non-function tracer: if (tr->current_trace != &function_trace) return 0; is placed *after* the dereference, which is too late. This was observed with the following crash dump: BUG: unable to handle page fault at 0000000000000000 RIP: func_set_flag+0xd Call Trace: __set_tracer_option+0x27 trace_options_write+0x75 vfs_write+0x12a ksys_write+0x66 do_syscall_64+0x5b RIP: ffffffff914c973d RSP: ff67ec88b01dfdf0 RFLAGS: 00010202 RAX: 0000000000000000 RBX: ff3a826e80354580 RCX: 0000000000000001 RDX: 0000000000000001 RSI: 0000000000000000 RDI: ffffffff93918080 The disassembly confirms the fault: func_set_flag+0: mov 0x1f08(%rdi), %rax ; RAX = tr->current_trace_flags = NULL func_set_flag+13: mov (%rax), %eax ; page fault: dereference NULL At the time of the crash: tr->current_trace_flags = 0x0 (NULL) tr->current_trace = wakeup_rt_tracer (not function_trace) The scenario is that a process opens a function tracer option file (such as "func_stack_trace"), then the current tracer is switched to another tracer (e.g., "wakeup_rt"), which sets current_trace_flags to NULL. When the process subsequently writes to the option file, func_set_flag() is invoked and crashes on the NULL dereference. Fix this by moving the current_trace check before the current_trace_flags dereference, so that func_set_flag() returns early when the function tracer is not active. Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260624061715.1445655-1-xiangzao@linux.alibaba.com Fixes: 76680d0d2825 ("tracing: Have function tracer define options per instance") Signed-off-by: Yuanhe Shu Signed-off-by: Steven Rostedt --- kernel/trace/trace_functions.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/kernel/trace/trace_functions.c b/kernel/trace/trace_functions.c index f283391a4dc8..cd37f2013758 100644 --- a/kernel/trace/trace_functions.c +++ b/kernel/trace/trace_functions.c @@ -458,12 +458,12 @@ func_set_flag(struct trace_array *tr, u32 old_flags, u32 bit, int set) ftrace_func_t func; u32 new_flags; - /* Do nothing if already set. */ - if (!!set == !!(tr->current_trace_flags->val & bit)) + /* We can change this flag only when current tracer is function. */ + if (tr->current_trace != &function_trace) return 0; - /* We can change this flag only when not running. */ - if (tr->current_trace != &function_trace) + /* Do nothing if already set. */ + if (!!set == !!(tr->current_trace_flags->val & bit)) return 0; new_flags = (tr->current_trace_flags->val & ~bit) | (set ? bit : 0); From 535fcf4b8a261fbb8cc4f91e4597343c135a90f2 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 30 Jun 2026 18:54:12 -0400 Subject: [PATCH 0682/1054] ufs: core: tracing: Do not dereference pointers in TP_printk() The trace events in drivers/ufs/core/ufs_trace.h were converted to take a pointer to the hba structure as an argument for the tracepoint and then in TP_printk() the printing of the dev_name from the ring buffer was converted to using the dev dereferenced pointer from the hba saved pointer. This is not allowed as the TP_printk() is executed at the time the trace event is read from /sys/kernel/tracing/trace file. That can happen literally, seconds, minutes, hours, weeks, days, or even months later! There is no guarantee that the hba pointer will still exist by the time it is dereferenced when the "trace" file is read. Instead, save the device name from the hba pointer at the time the tracepoint is called and place it into the ring buffer event. Then the TP_printk() can read the name directly from the ring buffer and remove the possibility that it will read a freed pointer and crash the kernel. This was detected when testing the trace event code that looks for TP_printk() parameters doing illegal derferences[1] [1] https://lore.kernel.org/all/20260630184836.74d477b6@gandalf.local.home/ Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260630185412.283c26c5@gandalf.local.home Fixes: 583e518e71003 ("scsi: ufs: core: Add hba parameter to trace events") Reviewed-by: Peter Wang Reviewed-by: Bart Van Assche Signed-off-by: Steven Rostedt --- drivers/ufs/core/ufs_trace.h | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/drivers/ufs/core/ufs_trace.h b/drivers/ufs/core/ufs_trace.h index 309ae51b4906..377a3c54b9f5 100644 --- a/drivers/ufs/core/ufs_trace.h +++ b/drivers/ufs/core/ufs_trace.h @@ -89,16 +89,18 @@ TRACE_EVENT(ufshcd_clk_gating, TP_STRUCT__entry( __field(struct ufs_hba *, hba) + __string(dev_name, dev_name(hba->dev)) __field(int, state) ), TP_fast_assign( + __assign_str(dev_name); __entry->hba = hba; __entry->state = state; ), TP_printk("%s: gating state changed to %s", - dev_name(__entry->hba->dev), + __get_str(dev_name), __print_symbolic(__entry->state, UFSCHD_CLK_GATING_STATES)) ); @@ -111,6 +113,7 @@ TRACE_EVENT(ufshcd_clk_scaling, TP_STRUCT__entry( __field(struct ufs_hba *, hba) + __string(dev_name, dev_name(hba->dev)) __string(state, state) __string(clk, clk) __field(u32, prev_state) @@ -119,6 +122,7 @@ TRACE_EVENT(ufshcd_clk_scaling, TP_fast_assign( __entry->hba = hba; + __assign_str(dev_name); __assign_str(state); __assign_str(clk); __entry->prev_state = prev_state; @@ -126,7 +130,7 @@ TRACE_EVENT(ufshcd_clk_scaling, ), TP_printk("%s: %s %s from %u to %u Hz", - dev_name(__entry->hba->dev), __get_str(state), __get_str(clk), + __get_str(dev_name), __get_str(state), __get_str(clk), __entry->prev_state, __entry->curr_state) ); @@ -138,16 +142,18 @@ TRACE_EVENT(ufshcd_auto_bkops_state, TP_STRUCT__entry( __field(struct ufs_hba *, hba) + __string(dev_name, dev_name(hba->dev)) __string(state, state) ), TP_fast_assign( __entry->hba = hba; + __assign_str(dev_name); __assign_str(state); ), TP_printk("%s: auto bkops - %s", - dev_name(__entry->hba->dev), __get_str(state)) + __get_str(dev_name), __get_str(state)) ); DECLARE_EVENT_CLASS(ufshcd_profiling_template, @@ -158,6 +164,7 @@ DECLARE_EVENT_CLASS(ufshcd_profiling_template, TP_STRUCT__entry( __field(struct ufs_hba *, hba) + __string(dev_name, dev_name(hba->dev)) __string(profile_info, profile_info) __field(s64, time_us) __field(int, err) @@ -165,13 +172,14 @@ DECLARE_EVENT_CLASS(ufshcd_profiling_template, TP_fast_assign( __entry->hba = hba; + __assign_str(dev_name); __assign_str(profile_info); __entry->time_us = time_us; __entry->err = err; ), TP_printk("%s: %s: took %lld usecs, err %d", - dev_name(__entry->hba->dev), __get_str(profile_info), + __get_str(dev_name), __get_str(profile_info), __entry->time_us, __entry->err) ); @@ -200,6 +208,7 @@ DECLARE_EVENT_CLASS(ufshcd_template, __field(s64, usecs) __field(int, err) __field(struct ufs_hba *, hba) + __string(dev_name, dev_name(hba->dev)) __field(int, dev_state) __field(int, link_state) ), @@ -208,13 +217,14 @@ DECLARE_EVENT_CLASS(ufshcd_template, __entry->usecs = usecs; __entry->err = err; __entry->hba = hba; + __assign_str(dev_name); __entry->dev_state = dev_state; __entry->link_state = link_state; ), TP_printk( "%s: took %lld usecs, dev_state: %s, link_state: %s, err %d", - dev_name(__entry->hba->dev), + __get_str(dev_name), __entry->usecs, __print_symbolic(__entry->dev_state, UFS_PWR_MODES), __print_symbolic(__entry->link_state, UFS_LINK_STATES), @@ -279,6 +289,7 @@ TRACE_EVENT(ufshcd_command, TP_STRUCT__entry( __field(struct scsi_device *, sdev) __field(struct ufs_hba *, hba) + __string(dev_name, dev_name(&sdev->sdev_dev)) __field(enum ufs_trace_str_t, str_t) __field(unsigned int, tag) __field(u32, doorbell) @@ -291,6 +302,7 @@ TRACE_EVENT(ufshcd_command, ), TP_fast_assign( + __assign_str(dev_name); __entry->sdev = sdev; __entry->hba = hba; __entry->str_t = str_t; @@ -307,7 +319,7 @@ TRACE_EVENT(ufshcd_command, TP_printk( "%s: %s: tag: %u, DB: 0x%x, size: %d, IS: %u, LBA: %llu, opcode: 0x%x (%s), group_id: 0x%x, hwq_id: %d", show_ufs_cmd_trace_str(__entry->str_t), - dev_name(&__entry->sdev->sdev_dev), __entry->tag, + __get_str(dev_name), __entry->tag, __entry->doorbell, __entry->transfer_len, __entry->intr, __entry->lba, (u32)__entry->opcode, str_opcode(__entry->opcode), (u32)__entry->group_id, __entry->hwq_id @@ -322,6 +334,7 @@ TRACE_EVENT(ufshcd_uic_command, TP_STRUCT__entry( __field(struct ufs_hba *, hba) + __string(dev_name, dev_name(hba->dev)) __field(enum ufs_trace_str_t, str_t) __field(u32, cmd) __field(u32, arg1) @@ -331,6 +344,7 @@ TRACE_EVENT(ufshcd_uic_command, TP_fast_assign( __entry->hba = hba; + __assign_str(dev_name); __entry->str_t = str_t; __entry->cmd = cmd; __entry->arg1 = arg1; @@ -340,7 +354,7 @@ TRACE_EVENT(ufshcd_uic_command, TP_printk( "%s: %s: cmd: 0x%x, arg1: 0x%x, arg2: 0x%x, arg3: 0x%x", - show_ufs_cmd_trace_str(__entry->str_t), dev_name(__entry->hba->dev), + show_ufs_cmd_trace_str(__entry->str_t), __get_str(dev_name), __entry->cmd, __entry->arg1, __entry->arg2, __entry->arg3 ) ); @@ -353,6 +367,7 @@ TRACE_EVENT(ufshcd_upiu, TP_STRUCT__entry( __field(struct ufs_hba *, hba) + __string(dev_name, dev_name(hba->dev)) __field(enum ufs_trace_str_t, str_t) __array(unsigned char, hdr, 12) __array(unsigned char, tsf, 16) @@ -361,6 +376,7 @@ TRACE_EVENT(ufshcd_upiu, TP_fast_assign( __entry->hba = hba; + __assign_str(dev_name); __entry->str_t = str_t; memcpy(__entry->hdr, hdr, sizeof(__entry->hdr)); memcpy(__entry->tsf, tsf, sizeof(__entry->tsf)); @@ -369,7 +385,7 @@ TRACE_EVENT(ufshcd_upiu, TP_printk( "%s: %s: HDR:%s, %s:%s", - show_ufs_cmd_trace_str(__entry->str_t), dev_name(__entry->hba->dev), + show_ufs_cmd_trace_str(__entry->str_t), __get_str(dev_name), __print_hex(__entry->hdr, sizeof(__entry->hdr)), show_ufs_cmd_trace_tsf(__entry->tsf_t), __print_hex(__entry->tsf, sizeof(__entry->tsf)) @@ -384,16 +400,18 @@ TRACE_EVENT(ufshcd_exception_event, TP_STRUCT__entry( __field(struct ufs_hba *, hba) + __string(dev_name, dev_name(hba->dev)) __field(u16, status) ), TP_fast_assign( __entry->hba = hba; + __assign_str(dev_name); __entry->status = status; ), TP_printk("%s: status 0x%x", - dev_name(__entry->hba->dev), __entry->status + __get_str(dev_name), __entry->status ) ); From 0a6070839b1ef276d5b05bedfb787743e140fb17 Mon Sep 17 00:00:00 2001 From: Huihui Huang Date: Wed, 1 Jul 2026 18:28:46 +0800 Subject: [PATCH 0683/1054] tracing: Prevent out-of-bounds read in glob matching String event fields are not necessarily NUL-terminated, so the filter predicate functions (filter_pred_string(), filter_pred_strloc() and filter_pred_strrelloc()) pass the field length to the regex match callbacks, and the length-aware matchers honour it. regex_match_glob() was the exception: it ignored the length and called glob_match(), which scans the string until it hits a NUL byte. Some string fields are not NUL-terminated. One example is the dynamic char array of the xfs_* namespace tracepoints, which is copied without a trailing NUL. For such a field, glob matching reads past the end of the event field, causing a KASAN slab-out-of-bounds read in glob_match(), reached via regex_match_glob() and filter_match_preds() from the xfs_lookup tracepoint. Add a length-bounded glob_match_len() and use it from regex_match_glob() so glob matching always stops at the field boundary. The matching loop is factored into a shared helper so glob_match() keeps its behaviour. Fixes: 60f1d5e3bac4 ("ftrace: Support full glob matching") Cc: stable@vger.kernel.org Link: https://patch.msgid.link/da1aaf125fc3b63320b0c540fd6afa7c3d5b4f1a.1782836943.git.hhhuang@smu.edu.sg 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: Huihui Huang Signed-off-by: Ren Wei Acked-by: Masami Hiramatsu (Google) Signed-off-by: Steven Rostedt --- include/linux/glob.h | 1 + kernel/trace/trace_events_filter.c | 6 ++---- lib/glob.c | 31 ++++++++++++++++++++++++++++-- 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/include/linux/glob.h b/include/linux/glob.h index 861327b33e41..91595e750936 100644 --- a/include/linux/glob.h +++ b/include/linux/glob.h @@ -6,5 +6,6 @@ #include /* For __pure */ bool __pure glob_match(char const *pat, char const *str); +bool __pure glob_match_len(char const *pat, char const *str, size_t len); #endif /* _LINUX_GLOB_H */ diff --git a/kernel/trace/trace_events_filter.c b/kernel/trace/trace_events_filter.c index 609325f57942..6385cd662d8d 100644 --- a/kernel/trace/trace_events_filter.c +++ b/kernel/trace/trace_events_filter.c @@ -1056,11 +1056,9 @@ static int regex_match_end(char *str, struct regex *r, int len) return 0; } -static int regex_match_glob(char *str, struct regex *r, int len __maybe_unused) +static int regex_match_glob(char *str, struct regex *r, int len) { - if (glob_match(r->pattern, str)) - return 1; - return 0; + return glob_match_len(r->pattern, str, len) ? 1 : 0; } /** diff --git a/lib/glob.c b/lib/glob.c index 7aca76c25bcb..c80d9dd736b4 100644 --- a/lib/glob.c +++ b/lib/glob.c @@ -11,6 +11,9 @@ MODULE_DESCRIPTION("glob(7) matching"); MODULE_LICENSE("Dual MIT/GPL"); +static bool __pure glob_match_str(char const *pat, char const *str, + char const *str_end); + /** * glob_match - Shell-style pattern matching, like !fnmatch(pat, str, 0) * @pat: Shell-style pattern to match, e.g. "*.[ch]". @@ -40,6 +43,29 @@ MODULE_LICENSE("Dual MIT/GPL"); * An opening bracket without a matching close is matched literally. */ bool __pure glob_match(char const *pat, char const *str) +{ + return glob_match_str(pat, str, NULL); +} +EXPORT_SYMBOL(glob_match); + +/** + * glob_match_len - glob match against a length-bounded string + * @pat: Shell-style pattern to match. + * @str: String to match. Need not be NUL-terminated. + * @len: Number of bytes of @str that may be read. + * + * Like glob_match(), but @str is only read up to @len bytes, so it can be + * used on buffers that are not NUL-terminated (e.g. trace event fields). + * A NUL byte within @len still terminates the string. + */ +bool __pure glob_match_len(char const *pat, char const *str, size_t len) +{ + return glob_match_str(pat, str, str + len); +} +EXPORT_SYMBOL(glob_match_len); + +static bool __pure glob_match_str(char const *pat, char const *str, + char const *str_end) { /* * Backtrack to previous * on mismatch and retry starting one @@ -55,9 +81,11 @@ bool __pure glob_match(char const *pat, char const *str) * on mismatch, or true after matching the trailing nul bytes. */ for (;;) { - unsigned char c = *str++; + unsigned char c = (str_end && str >= str_end) ? '\0' : *str; unsigned char d = *pat++; + str++; + switch (d) { case '?': /* Wildcard: anything but nul */ if (c == '\0') @@ -125,4 +153,3 @@ bool __pure glob_match(char const *pat, char const *str) } } } -EXPORT_SYMBOL(glob_match); From 1e67bb60bb03518d3bdd59a48c65d69e6d773bb8 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Wed, 1 Jul 2026 13:27:44 -0400 Subject: [PATCH 0684/1054] tracing: Add a no-rcu-check version of trace_##event##_enabled() Tracepoints require that RCU is watching. To prevent them from being used in places that RCU is not watching, the trace_##event() macro always calls rcu_is_watching() even when the event is not enabled and warns if RCU is not watching. This is to make sure a warning is triggered even if the tracepoint is never enabled (as it is only a bug when it is). It was noticed that tracepoints could be hidden within trace_#event#_enabled() calls, which are used to do extra work for the tracepoint only if the tracepoint is enabled. But this also can hide the fact that a tracepoint is placed in a location that can be called when RCU is not watching. Commit 9764e731ef6ab ("tracepoint: Add lockdep rcu_is_watching() check to trace_##name##_enabled()") added a check to the trace_##event##_enabled() macro to make sure RCU is watching when it is called to make sure not to hide the bug of a tracepoint being called when RCU is not watching. There is one case in the irq_disable tracepoint where it is within a trace_irq_disable_enabled() block, but it checks if RCU is watching, and if it isn't, it makes a call to ct_irq_enter() that makes RCU watch again. But because trace_irq_disable_enabled() now checks if RCU is watching and will trigger if it isn't. This is a false warning as the code within the block handles this case. Add a new internal macro __trace_##event##_enabled() that doesn't check if RCU is watching, and convert the irq_enable/disable tracepoints over to it. Link: https://patch.msgid.link/20260701132744.6a7fc68b@robin Reported-by: Geert Uytterhoeven Closes: https://lore.kernel.org/all/CAMuHMdXud_RpWag_hFqa2ByBGRxg6KnxGL1ObCWZrpTsk3TfAw@mail.gmail.com/ Fixes: 9764e731ef6ab ("tracepoint: Add lockdep rcu_is_watching() check to trace_##name##_enabled()") Tested-by: Geert Uytterhoeven Signed-off-by: Steven Rostedt --- include/linux/tracepoint.h | 12 +++++++++++- kernel/trace/trace_preemptirq.c | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/include/linux/tracepoint.h b/include/linux/tracepoint.h index 4a0c36f40fe2..e0d838c9ce93 100644 --- a/include/linux/tracepoint.h +++ b/include/linux/tracepoint.h @@ -292,13 +292,18 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) { \ } \ static inline bool \ + __trace_##name##_enabled(void) \ + { \ + return static_branch_unlikely(&__tracepoint_##name.key);\ + } \ + static inline bool \ trace_##name##_enabled(void) \ { \ if (IS_ENABLED(CONFIG_LOCKDEP)) { \ WARN_ONCE(!rcu_is_watching(), \ "RCU not watching for tracepoint"); \ } \ - return static_branch_unlikely(&__tracepoint_##name.key);\ + return __trace_##name##_enabled(); \ } #define __DECLARE_TRACE(name, proto, args, cond, data_proto) \ @@ -457,6 +462,11 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) { \ } \ static inline bool \ + __trace_##name##_enabled(void) \ + { \ + return false; \ + } \ + static inline bool \ trace_##name##_enabled(void) \ { \ return false; \ diff --git a/kernel/trace/trace_preemptirq.c b/kernel/trace/trace_preemptirq.c index 0c42b15c3800..b63e3558948f 100644 --- a/kernel/trace/trace_preemptirq.c +++ b/kernel/trace/trace_preemptirq.c @@ -30,7 +30,7 @@ #else #define trace(point, args) \ do { \ - if (trace_##point##_enabled()) { \ + if (__trace_##point##_enabled()) { \ bool exit_rcu = false; \ if (in_nmi()) \ break; \ From 596b3678326d3d1aed7c19423b6746f1ce09688a Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Tue, 23 Jun 2026 07:15:05 -0700 Subject: [PATCH 0685/1054] x86/video: Only fall back to vga_default_device() without screen info Some multi GPU systems may have a VGA compatible device, but that might not be used for display. If due to enumeration order this device is found before the one actually used for display then multiple devices may show the boot_display attribute, confusing userspace. When screen info is valid, use it exclusively to find the primary device so that only the device backing the framebuffer is reported. Only when no framebuffer has been set up does it make sense to fall back to the default VGA device. This ensures at most one primary graphics device, preferably the one with the framebuffer. Fixes: ad90860bd10ee ("fbcon: Use screen info to find primary device") Closes: https://lore.kernel.org/linux-pci/20260618081803.2790848-1-aaron.ma@canonical.com/#t Reported-by: Aaron Ma Suggested-by: Thomas Zimmermann Signed-off-by: Mario Limonciello Signed-off-by: Borislav Petkov (AMD) Reviewed-by: Thomas Zimmermann Tested-by: Aaron Ma Cc: Link: https://patch.msgid.link/20260623141505.1816786-1-mario.limonciello@amd.com --- arch/x86/video/video-common.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/arch/x86/video/video-common.c b/arch/x86/video/video-common.c index 152789f00fcd..8ed82fff7638 100644 --- a/arch/x86/video/video-common.c +++ b/arch/x86/video/video-common.c @@ -43,21 +43,26 @@ bool video_is_primary_device(struct device *dev) if (!pci_is_display(pdev)) return false; - if (pdev == vga_default_device()) - return true; - #ifdef CONFIG_SCREEN_INFO numres = screen_info_resources(si, res, ARRAY_SIZE(res)); - for (i = 0; i < numres; ++i) { - if (!(res[i].flags & IORESOURCE_MEM)) - continue; + if (numres > 0) { + for (i = 0; i < numres; ++i) { + if (!(res[i].flags & IORESOURCE_MEM)) + continue; - if (pci_find_resource(pdev, &res[i])) - return true; + if (pci_find_resource(pdev, &res[i])) + return true; + } + + return false; } #endif - return false; + /* + * No framebuffer was set up by the firmware/bootloader, so fall back + * to the default VGA device. + */ + return pdev == vga_default_device(); } EXPORT_SYMBOL(video_is_primary_device); From a483b1a91b33b7533280e7c3efd2bc1275caef18 Mon Sep 17 00:00:00 2001 From: Tapio Reijonen Date: Mon, 15 Jun 2026 06:38:40 +0000 Subject: [PATCH 0686/1054] serial: max310x: implement gpio_chip::get_direction() It's strongly recommended for GPIO drivers to always implement the .get_direction() callback - even when the direction is tracked in software. The GPIO core emits a warning when the callback is missing and a user reads the direction of a line, e.g. via /sys/kernel/debug/gpio. The MAX310X keeps the GPIO direction in the GPIOCFG register (a set bit selects output), which the existing direction_input/output callbacks already program, so the current direction can be read back directly. Fixes: f65444187a66 ("serial: New serial driver MAX310X") Cc: stable Signed-off-by: Tapio Reijonen Reviewed-by: Linus Walleij Reviewed-by: Bartosz Golaszewski Reviewed-by: Hugo Villeneuve Link: https://patch.msgid.link/20260615-b4-serial-max310x-gpio-get-direction-v2-1-4704ba2b181a@vaisala.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/max310x.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index e28e3065c99d..92af02a39c7a 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -1244,6 +1244,17 @@ static int max310x_gpio_set(struct gpio_chip *chip, unsigned int offset, return 0; } +static int max310x_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) +{ + struct max310x_port *s = gpiochip_get_data(chip); + struct uart_port *port = &s->p[offset / 4].port; + unsigned int val; + + val = max310x_port_read(port, MAX310X_GPIOCFG_REG); + + return val & BIT(offset % 4) ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN; +} + static int max310x_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) { struct max310x_port *s = gpiochip_get_data(chip); @@ -1447,6 +1458,7 @@ static int max310x_probe(struct device *dev, const struct max310x_devtype *devty s->gpio.owner = THIS_MODULE; s->gpio.parent = dev; s->gpio.label = devtype->name; + s->gpio.get_direction = max310x_gpio_get_direction; s->gpio.direction_input = max310x_gpio_direction_input; s->gpio.get = max310x_gpio_get; s->gpio.direction_output= max310x_gpio_direction_output; From 22dd2777e6c180e1c945b00f6d18550979436324 Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Mon, 6 Jul 2026 20:03:32 +0200 Subject: [PATCH 0687/1054] serial: msm: Disable DMA for kernel console UART At the moment, concurrent writes from userspace and the kernel to the console can trigger a race condition that results in an infinite loop of the same messages printed over and over again. This is most likely to happen during system startup or shutdown when the init system starts/stops a large number of system services that interact with various kernel code. When userspace writes to the TTY device, the driver initiates an asynchronous DMA transfer and releases the port lock. At the same moment, the kernel printk path might grab the port lock and re-configure the UART controller for PIO, without waiting for the DMA operation to complete. It seems like this collision results in zero progress being reported for the DMA engine, so the same text is printed to the console over and over again. For the kernel console, we want a reliable output path that will be functional even during crashes etc. So rather than implementing complex code to synchronize the kernel console write routines with the userspace DMA write routines, simply disable DMA for the console UART instance. Similar checks exist in many other serial drivers, e.g. 8250_port.c, imx.c, sh-sci.c etc. Cc: stable Fixes: 3a878c430fd6 ("tty: serial: msm: Add TX DMA support") Signed-off-by: Stephan Gerhold Acked-by: Konrad Dybcio Link: https://patch.msgid.link/20260706-serial-msm-console-dma-collision-v1-1-3179b8cb1d89@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/msm_serial.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index 2e999cb9c974..bfa44b01c3e9 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -1228,7 +1228,8 @@ static int msm_startup(struct uart_port *port) data |= MSM_UART_MR1_AUTO_RFR_LEVEL0 & rfr_level; msm_write(port, data, MSM_UART_MR1); - if (msm_port->is_uartdm) { + /* Disable DMA for console to prevent PIO/DMA collisions */ + if (msm_port->is_uartdm && !uart_console(port)) { msm_request_tx_dma(msm_port, msm_port->uart.mapbase); msm_request_rx_dma(msm_port, msm_port->uart.mapbase); } From 4c2509f3b79756679a02bea649c6a7501b58f52c Mon Sep 17 00:00:00 2001 From: "Tycho Andersen (AMD)" Date: Tue, 7 Jul 2026 09:00:33 -0600 Subject: [PATCH 0688/1054] x86/virt/sev: Revert "Drop WBINVD before setting MSR_AMD64_SYSCFG_SNP_EN" Revert 99cf1fb58e68 ("x86/virt/sev: Drop WBINVD before setting MSR_AMD64_SYSCFG_SNP_EN"). Section 8.8 of the SNP spec says: Before invoking SNP_INIT_EX with INIT_RMP set to 1, software must ensure that no CPUs contain dirty cache lines for the memory containing the RMP. Cachelines can be moved from cache to cache in a dirty state. The wbinvd_on_all_cpus() before SNP_INIT_EX flushes the caches for each CPU, but if the IPIs for WBINVD race with this dirty cacheline movement, it is possible that they may not get flushed, violating the firmware requirement. Doing wbinvd_on_all_cpus() before setting SNPEn is safer since the RMP table is not yet in use. [ Heroically bisected by Srikanth. ] [ bp: Massage commit message. ] Fixes: 99cf1fb58e68 ("x86/virt/sev: Drop WBINVD before setting MSR_AMD64_SYSCFG_SNP_EN") Reported-by: Srikanth Aithal Signed-off-by: Tycho Andersen (AMD) Signed-off-by: Borislav Petkov (AMD) Tested-by: Srikanth Aithal Reviewed-by: Tom Lendacky Cc: Link: https://patch.msgid.link/20260707150033.2364758-1-tycho@kernel.org --- arch/x86/virt/svm/sev.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/x86/virt/svm/sev.c b/arch/x86/virt/svm/sev.c index 8bcdce98f6dc..cff285d8ad8e 100644 --- a/arch/x86/virt/svm/sev.c +++ b/arch/x86/virt/svm/sev.c @@ -536,6 +536,8 @@ int snp_prepare(void) goto unlock; } + wbinvd_on_all_cpus(); + /* * MtrrFixDramModEn is not shared between threads on a core, * therefore it must be set on all CPUs prior to enabling SNP. From 8d187d4b33c262c0f3e44842553521151d8629e8 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 7 Jul 2026 17:29:35 +0100 Subject: [PATCH 0689/1054] KVM: arm64: Fix propagation of TLBI level in kvm_pgtable_stage2_relax_perms() Assigning the invalidation level (an s8 value) with TLBI_TTL_UNKNOWN (a 32bit signed value) is not ideal, to say the least. Instead of this, only pass TLBI_TTL_UNKNOWN to __kvm_tlb_flush_vmid_ipa_nsh() when we know for sure that we don't have a provided level. Fixes: 100baf0184896 ("KVM: arm64: Ensure level is always initialized when relaxing perms") Reported-by: Mark Brown Reviewed-by: Oliver Upton Link: https://lore.kernel.org/r/akztC7H2IsEKaq4i@sirena.org.uk Link: https://patch.msgid.link/20260707162935.1900874-1-maz@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kvm/hyp/pgtable.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/arch/arm64/kvm/hyp/pgtable.c b/arch/arm64/kvm/hyp/pgtable.c index 8754c99c22f2..70dceb20dfad 100644 --- a/arch/arm64/kvm/hyp/pgtable.c +++ b/arch/arm64/kvm/hyp/pgtable.c @@ -1356,7 +1356,7 @@ int kvm_pgtable_stage2_relax_perms(struct kvm_pgtable *pgt, u64 addr, enum kvm_pgtable_prot prot, enum kvm_pgtable_walk_flags flags) { kvm_pte_t xn = 0, set = 0, clr = 0; - s8 level = TLBI_TTL_UNKNOWN; + s8 level; int ret; if (prot & KVM_PTE_LEAF_ATTR_HI_SW) @@ -1379,7 +1379,8 @@ int kvm_pgtable_stage2_relax_perms(struct kvm_pgtable *pgt, u64 addr, ret = stage2_update_leaf_attrs(pgt, addr, 1, set, clr, NULL, &level, flags); if (!ret || ret == -EAGAIN) - kvm_call_hyp(__kvm_tlb_flush_vmid_ipa_nsh, pgt->mmu, addr, level); + kvm_call_hyp(__kvm_tlb_flush_vmid_ipa_nsh, pgt->mmu, addr, + (ret == -EAGAIN) ? TLBI_TTL_UNKNOWN : level); return ret; } From 14f172eff9c19f8043a9858845f33cd034f3a41e Mon Sep 17 00:00:00 2001 From: Lizhi Hou Date: Mon, 6 Jul 2026 15:12:28 -0700 Subject: [PATCH 0690/1054] accel/amdxdna: Fix potential amdxdna_umap lifetime race amdxdna_umap_release() calls the blocking mmu_interval_notifier_remove() before removing the object from abo->mem.umap_list. If aie2_populate_range() runs concurrently, it may obtain a reference to an amdxdna_umap that is being released, leading to a potential use-after-free. Use kref_get_unless_zero() in aie2_populate_range() when acquiring a reference. If the reference count has already dropped to zero, release is in progress and the entry is skipped. Fixes: e486147c912f ("accel/amdxdna: Add BO import and export") Reviewed-by: Max Zhen Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/20260706221228.434099-1-lizhi.hou@amd.com --- drivers/accel/amdxdna/aie2_ctx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/accel/amdxdna/aie2_ctx.c b/drivers/accel/amdxdna/aie2_ctx.c index 408ff7e2a272..7bf635634e64 100644 --- a/drivers/accel/amdxdna/aie2_ctx.c +++ b/drivers/accel/amdxdna/aie2_ctx.c @@ -1039,7 +1039,7 @@ static int aie2_populate_range(struct amdxdna_gem_obj *abo) found = false; down_write(&xdna->notifier_lock); list_for_each_entry(mapp, &abo->mem.umap_list, node) { - if (mapp->invalid) { + if (mapp->invalid && kref_get_unless_zero(&mapp->refcnt)) { found = true; break; } @@ -1050,7 +1050,7 @@ static int aie2_populate_range(struct amdxdna_gem_obj *abo) up_write(&xdna->notifier_lock); return 0; } - kref_get(&mapp->refcnt); + up_write(&xdna->notifier_lock); mm = mapp->notifier.mm; From 66ff5c0eee02c4be67f8ba7fb6c63709ef1c92a3 Mon Sep 17 00:00:00 2001 From: Lizhi Hou Date: Mon, 6 Jul 2026 22:56:58 -0700 Subject: [PATCH 0691/1054] accel/amdxdna: fix open_ref leak and stale client pointer on dma map failure amdxdna_gem_obj_open() increments open_ref before attempting to set up the DMA address mapping. When amdxdna_dma_map_bo() fails, the function returned immediately without rolling back either change made on the first open (open_ref == 1 path). Fix it by decrementing open_ref and clearing abo->client on the error path. Fixes: ece3e8980907 ("accel/amdxdna: Allow forcing IOVA-based DMA via module parameter") Reviewed-by: Max Zhen Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/20260707055658.479049-1-lizhi.hou@amd.com --- drivers/accel/amdxdna/amdxdna_gem.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/accel/amdxdna/amdxdna_gem.c b/drivers/accel/amdxdna/amdxdna_gem.c index 0c10ec0cc5e4..1275f91ca705 100644 --- a/drivers/accel/amdxdna/amdxdna_gem.c +++ b/drivers/accel/amdxdna/amdxdna_gem.c @@ -664,8 +664,11 @@ static int amdxdna_gem_obj_open(struct drm_gem_object *gobj, struct drm_file *fi /* No need to set up dma addr mapping in PASID mode. */ if (!amdxdna_pasid_on(abo->client)) { ret = amdxdna_dma_map_bo(xdna, abo); - if (ret) + if (ret) { + abo->open_ref--; + abo->client = NULL; return ret; + } } amdxdna_gem_add_bo_usage(abo); From c8d2530791cb53602ac06ec2db6287d99f51cdbc Mon Sep 17 00:00:00 2001 From: Lizhi Hou Date: Mon, 6 Jul 2026 22:57:32 -0700 Subject: [PATCH 0692/1054] accel/amdxdna: Fix deadlock on debug BO command timeout Both amdxdna_hwctx_sync_debug_bo() and amdxdna_drm_config_hwctx_ioctl() hold xdna->dev_lock while invoking backend operations. If the hardware hangs, aie2_cmd_wait() blocks waiting for a firmware response. When the DRM scheduler timeout expires, aie2_sched_job_timedout() is invoked to reset the hardware. However, the timeout handler also attempts to acquire dev_lock, resulting in a deadlock. Avoid this by releasing dev_lock before waiting for the firmware response and reacquiring it after the wait completes. This allows the timeout handler to proceed with device recovery when a debug BO command times out. Fixes: 7ea046838021 ("accel/amdxdna: Support firmware debug buffer") Reviewed-by: Max Zhen Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/20260707055732.479103-1-lizhi.hou@amd.com --- drivers/accel/amdxdna/aie2_ctx.c | 5 ++++- drivers/accel/amdxdna/amdxdna_ctx.c | 6 ++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/accel/amdxdna/aie2_ctx.c b/drivers/accel/amdxdna/aie2_ctx.c index 7bf635634e64..30ccb8d5e23d 100644 --- a/drivers/accel/amdxdna/aie2_ctx.c +++ b/drivers/accel/amdxdna/aie2_ctx.c @@ -900,13 +900,16 @@ static int aie2_hwctx_cu_config(struct amdxdna_hwctx *hwctx, void *buf, u32 size static void aie2_cmd_wait(struct amdxdna_hwctx *hwctx, u64 seq) { struct dma_fence *out_fence = aie2_cmd_get_out_fence(hwctx, seq); + struct amdxdna_dev *xdna = hwctx->client->xdna; if (!out_fence) { - XDNA_ERR(hwctx->client->xdna, "Failed to get fence"); + XDNA_ERR(xdna, "Failed to get fence"); return; } + mutex_unlock(&xdna->dev_lock); dma_fence_wait_timeout(out_fence, false, MAX_SCHEDULE_TIMEOUT); + mutex_lock(&xdna->dev_lock); dma_fence_put(out_fence); } diff --git a/drivers/accel/amdxdna/amdxdna_ctx.c b/drivers/accel/amdxdna/amdxdna_ctx.c index 67a2abcf173e..9ae19393e488 100644 --- a/drivers/accel/amdxdna/amdxdna_ctx.c +++ b/drivers/accel/amdxdna/amdxdna_ctx.c @@ -310,6 +310,7 @@ int amdxdna_drm_destroy_hwctx_ioctl(struct drm_device *dev, void *data, struct d if (!drm_dev_enter(dev, &idx)) return -ENODEV; + mutex_lock(&xdna->client_lock); mutex_lock(&xdna->dev_lock); hwctx = xa_erase(&client->hwctx_xa, args->handle); if (!hwctx) { @@ -328,6 +329,7 @@ int amdxdna_drm_destroy_hwctx_ioctl(struct drm_device *dev, void *data, struct d XDNA_DBG(xdna, "PID %d destroyed HW context %d", client->pid, args->handle); out: mutex_unlock(&xdna->dev_lock); + mutex_unlock(&xdna->client_lock); drm_dev_exit(idx); return ret; } @@ -388,6 +390,7 @@ int amdxdna_drm_config_hwctx_ioctl(struct drm_device *dev, void *data, struct dr goto free_buf; } + mutex_lock(&xdna->client_lock); mutex_lock(&xdna->dev_lock); hwctx = xa_load(&client->hwctx_xa, args->handle); if (!hwctx) { @@ -400,6 +403,7 @@ int amdxdna_drm_config_hwctx_ioctl(struct drm_device *dev, void *data, struct dr unlock: mutex_unlock(&xdna->dev_lock); + mutex_unlock(&xdna->client_lock); amdxdna_pm_suspend_put(xdna); free_buf: kfree(buf); @@ -428,6 +432,7 @@ int amdxdna_hwctx_sync_debug_bo(struct amdxdna_client *client, u32 debug_bo_hdl) } abo = to_xdna_obj(gobj); + mutex_lock(&xdna->client_lock); mutex_lock(&xdna->dev_lock); hwctx = xa_load(&client->hwctx_xa, abo->assigned_hwctx); if (!hwctx) { @@ -439,6 +444,7 @@ int amdxdna_hwctx_sync_debug_bo(struct amdxdna_client *client, u32 debug_bo_hdl) unlock: mutex_unlock(&xdna->dev_lock); + mutex_unlock(&xdna->client_lock); amdxdna_pm_suspend_put(xdna); put_obj: drm_gem_object_put(gobj); From 928caf71e566ddc5f303f15fb9c33f16a9915f30 Mon Sep 17 00:00:00 2001 From: Lizhi Hou Date: Mon, 6 Jul 2026 22:58:18 -0700 Subject: [PATCH 0693/1054] accel/amdxdna: Fix hardware context race in amdxdna_update_heap() amdxdna_update_heap() iterates over hardware contexts while holding xdna->dev_lock. During the iteration, amdxdna_pm_resume_get_locked() may temporarily release and reacquire the lock, allowing hardware contexts to be modified concurrently. Fix the race by calling amdxdna_pm_resume_get_locked() before iterating over hardware contexts. Fixes: dbc8fd7a03cb ("accel/amdxdna: Add expandable device heap support") Reviewed-by: Max Zhen Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/20260707055818.479165-1-lizhi.hou@amd.com --- drivers/accel/amdxdna/aie2_ctx.c | 6 ------ drivers/accel/amdxdna/amdxdna_ctx.c | 24 +++++++++++++++--------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/accel/amdxdna/aie2_ctx.c b/drivers/accel/amdxdna/aie2_ctx.c index 30ccb8d5e23d..101f324ee178 100644 --- a/drivers/accel/amdxdna/aie2_ctx.c +++ b/drivers/accel/amdxdna/aie2_ctx.c @@ -1222,10 +1222,6 @@ int aie2_hwctx_heap_expand(struct amdxdna_hwctx *hwctx, u64 addr; int ret; - ret = amdxdna_pm_resume_get_locked(xdna); - if (ret) - return ret; - addr = amdxdna_obj_dma_addr(heap); ret = aie2_add_host_buf(xdna->dev_handle, hwctx->fw_ctx_id, addr, heap->mem.size); @@ -1234,7 +1230,5 @@ int aie2_hwctx_heap_expand(struct amdxdna_hwctx *hwctx, hwctx->name, heap->mem.size, ret); } - amdxdna_pm_suspend_put(xdna); - return ret; } diff --git a/drivers/accel/amdxdna/amdxdna_ctx.c b/drivers/accel/amdxdna/amdxdna_ctx.c index 9ae19393e488..8f8df9d04ec5 100644 --- a/drivers/accel/amdxdna/amdxdna_ctx.c +++ b/drivers/accel/amdxdna/amdxdna_ctx.c @@ -472,9 +472,7 @@ static int amdxdna_hwctx_expand_heap(struct amdxdna_hwctx *hwctx) break; } - mutex_unlock(&client->mm_lock); ret = xdna->dev_info->ops->hwctx_heap_expand(hwctx, heap); - mutex_lock(&client->mm_lock); if (ret) { amdxdna_gem_unpin(heap); drm_gem_object_put(to_gobj(heap)); @@ -493,18 +491,26 @@ int amdxdna_update_heap(struct amdxdna_client *client, struct amdxdna_hwctx *hwc unsigned long hwctx_id; int ret; - guard(mutex)(&client->mm_lock); + ret = amdxdna_pm_resume_get_locked(client->xdna); + if (ret) + return ret; - if (hwctx) - return amdxdna_hwctx_expand_heap(hwctx); + mutex_lock(&client->mm_lock); - amdxdna_for_each_hwctx(client, hwctx_id, hwctx) { + if (hwctx) { ret = amdxdna_hwctx_expand_heap(hwctx); - if (ret) - return ret; + } else { + amdxdna_for_each_hwctx(client, hwctx_id, hwctx) { + ret = amdxdna_hwctx_expand_heap(hwctx); + if (ret) + break; + } } + mutex_unlock(&client->mm_lock); - return 0; + amdxdna_pm_suspend_put(client->xdna); + + return ret; } static void From cdadb298b182d4615521380a520eb2c7cb762843 Mon Sep 17 00:00:00 2001 From: Marco Scardovi Date: Tue, 30 Jun 2026 16:26:35 +0200 Subject: [PATCH 0694/1054] platform/x86: asus-armoury: update power limits for G614PR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previously integrated power limits for the ASUS ROG Strix G16 G614PR laptop model were incorrect and too low compared to the windows counterparts. Update the power limits to the correct specifications based on the platform's hardware capabilities: - Increase AC PL1/SPL max limit from 90W to 120W. - Increase AC PL2/SPPT default/max from 110W/125W to 140W/145W. - Increase AC PL3/FPPT default/max from 110W/125W to 140W/145W. Fixes: 6b3bbe770f4ca0439710b7c42f88b9f6eeebabd0 (platform/x86: asus-armoury: add support for G614PR) Fixes: https://lore.kernel.org/platform-driver-x86/20260610152130.25892-1-scardracs@disroot.org/ Signed-off-by: Marco Scardovi Link: https://patch.msgid.link/20260630142957.7751-1-scardracs@disroot.org Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/asus-armoury.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/platform/x86/asus-armoury.h b/drivers/platform/x86/asus-armoury.h index 65166b50a2c3..322bfa647c2d 100644 --- a/drivers/platform/x86/asus-armoury.h +++ b/drivers/platform/x86/asus-armoury.h @@ -1997,13 +1997,13 @@ static const struct dmi_system_id power_limits[] = { .driver_data = &(struct power_data) { .ac_data = &(struct power_limits) { .ppt_pl1_spl_min = 30, - .ppt_pl1_spl_max = 90, + .ppt_pl1_spl_max = 120, .ppt_pl2_sppt_min = 65, - .ppt_pl2_sppt_def = 110, - .ppt_pl2_sppt_max = 125, + .ppt_pl2_sppt_def = 140, + .ppt_pl2_sppt_max = 145, .ppt_pl3_fppt_min = 65, - .ppt_pl3_fppt_def = 110, - .ppt_pl3_fppt_max = 125, + .ppt_pl3_fppt_def = 140, + .ppt_pl3_fppt_max = 145, .nv_temp_target_min = 75, .nv_temp_target_max = 87, .nv_dynamic_boost_min = 5, From 2726b5758f80a546a4ddeec5019e72035a7fa166 Mon Sep 17 00:00:00 2001 From: Shyam Sundar S K Date: Tue, 7 Jul 2026 17:58:52 +0530 Subject: [PATCH 0695/1054] platform/x86: amd-pmc: Use correct IP block table for AMD 1Ah M80H SoC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PMFW reports the S0i3 subsystem accounting per SoC, and the set of IP blocks and their bit ordering differ across SoC generations. Family 1Ah, Model 80h accounts for a distinct set of 19 IP blocks, which does not match the ordering in soc15_ip_blk[]. Commit 043af31c8d30 ("platform/x86/amd/pmc: Add PMC driver support for AMD 1Ah M80H SoC") wired amd_1ah_m80_cpu_info to soc15_ip_blk[], so M80H has been reporting incorrect S0i3 accounting via debugfs. Add soc15_ip_blk_v3[] with the correct ordering and point amd_1ah_m80_cpu_info at it. Fixes: 043af31c8d30 ("platform/x86/amd/pmc: Add PMC driver support for AMD 1Ah M80H SoC") Signed-off-by: Shyam Sundar S K Reviewed-by: Mario Limonciello (AMD) Link: https://patch.msgid.link/20260707122852.2066987-1-Shyam-sundar.S-k@amd.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/amd/pmc/pmc.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/amd/pmc/pmc.c b/drivers/platform/x86/amd/pmc/pmc.c index 347c3f6c5ae7..a37083cdb908 100644 --- a/drivers/platform/x86/amd/pmc/pmc.c +++ b/drivers/platform/x86/amd/pmc/pmc.c @@ -33,6 +33,28 @@ #include "pmc.h" +static const struct amd_pmc_bit_map soc15_ip_blk_v3[] = { + {"VDDCR", BIT(0)}, + {"VDDCR_LP", BIT(1)}, + {"LSOCV", BIT(2)}, + {"DISPLAY", BIT(3)}, + {"VCN", BIT(4)}, + {"JPEG", BIT(5)}, + {"UMSCH", BIT(6)}, + {"VPE", BIT(7)}, + {"MPM", BIT(8)}, + {"NPU", BIT(9)}, + {"USB_HC0", BIT(10)}, + {"eUSB_HC0", BIT(11)}, + {"RT0_ADP_HC1", BIT(12)}, + {"RT1_ADP_HC1", BIT(13)}, + {"RT2_ADP_HC2", BIT(14)}, + {"USB4_RT0", BIT(15)}, + {"USB4_RT1", BIT(16)}, + {"USB4-RT2", BIT(17)}, + {"LAPIC", BIT(18)}, +}; + static const struct amd_pmc_bit_map soc15_ip_blk_v2[] = { {"DISPLAY", BIT(0)}, {"CPU", BIT(1)}, @@ -159,9 +181,9 @@ static const struct amd_pmc_cpu_info amd_1ah_m80_cpu_info = { .smu_msg = AMD_PMC_REGISTER_MSG_1AH_80H, .smu_arg = AMD_PMC_REGISTER_ARG_1AH_80H, .smu_rsp = AMD_PMC_REGISTER_RSP_1AH_80H, - .num_ips = ARRAY_SIZE(soc15_ip_blk), + .num_ips = ARRAY_SIZE(soc15_ip_blk_v3), .scratch_reg = AMD_PMC_SCRATCH_REG_1AH, - .ips_ptr = soc15_ip_blk, + .ips_ptr = soc15_ip_blk_v3, .os_hint = MSG_OS_HINT_RN, }; From 50fd6dd755c6e48a38af2fa4621167eea56829c2 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Tue, 7 Jul 2026 12:59:11 -0400 Subject: [PATCH 0696/1054] tracing/user_events: Fix use-after-free in user_event_mm_dup() user_event_mm_dup() walks the parent mm's enabler list locklessly under rcu_read_lock() during fork() (from copy_process()); it does not take event_mutex: rcu_read_lock(); list_for_each_entry_rcu(enabler, &old_mm->enablers, mm_enablers_link) enabler->event = user_event_get(orig->event); user_event_enabler_destroy() removes an enabler from that list with list_del_rcu() and then, without waiting for a grace period, drops the enabler's user_event reference with user_event_put() and frees the enabler with kfree(). A reader that loaded the enabler before the list_del_rcu() can still be walking it, which leads to two use-after-frees: - kfree(enabler) frees the enabler while that reader dereferences enabler->event. - user_event_put() may drop the last reference to the user_event, which is then freed (via delayed_destroy_user_event() on a work queue), while the same reader does user_event_get(orig->event) on it. Both are reachable by an unprivileged task that can open user_events_data: one multithreaded process that registers an enabler and then concurrently unregisters it and calls fork() triggers the race. KASAN reports a slab-use-after-free in user_event_mm_dup() during clone(), with a "refcount_t: addition on 0" warning when the user_event is freed. The enabler use-after-free was found first; the user_event one was reported by XIAO WU, and the earlier enabler-only fix did not address it. Defer both the user_event_put() and the kfree(enabler) to a work item queued with queue_rcu_work(), so they run only after an RCU grace period, once all readers walking the enabler list have finished. The put must run in process context because user_event_put() takes event_mutex on the last reference, so a work queue is used rather than call_rcu(). The now-unlocked put lets the locked argument of user_event_enabler_destroy() be removed; all callers are updated. Fixes: 7235759084a4 ("tracing/user_events: Use remote writes for event enablement") Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260707165912.2560537-2-michael.bommarito@gmail.com Reported-by: XIAO WU Closes: https://lore.kernel.org/all/tencent_89647CE40DC452B891C65C94D1B271DE8E07@qq.com/ Suggested-by: Beau Belgrave Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Michael Bommarito Signed-off-by: Steven Rostedt --- kernel/trace/trace_events_user.c | 43 +++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/kernel/trace/trace_events_user.c b/kernel/trace/trace_events_user.c index c4ba484f7b38..8c82ecb735f4 100644 --- a/kernel/trace/trace_events_user.c +++ b/kernel/trace/trace_events_user.c @@ -109,6 +109,9 @@ struct user_event_enabler { /* Track enable bit, flags, etc. Aligned for bitops. */ unsigned long values; + + /* Defer the event put and enabler free past an RCU grace period. */ + struct rcu_work put_rwork; }; /* Bits 0-5 are for the bit to update upon enable/disable (0-63 allowed) */ @@ -396,15 +399,37 @@ static struct user_event_group *user_event_group_create(void) return NULL; }; -static void user_event_enabler_destroy(struct user_event_enabler *enabler, - bool locked) +static void delayed_user_event_enabler_put(struct work_struct *work) +{ + struct user_event_enabler *enabler = container_of(to_rcu_work(work), + struct user_event_enabler, put_rwork); + + /* No longer tracking the event via the enabler */ + user_event_put(enabler->event, false); + + /* Run from queue_rcu_work(), the RCU grace period has elapsed */ + kfree(enabler); +} + +static void user_event_enabler_destroy(struct user_event_enabler *enabler) { list_del_rcu(&enabler->mm_enablers_link); - /* No longer tracking the event via the enabler */ - user_event_put(enabler->event, locked); - - kfree(enabler); + /* + * The enabler is removed from an RCU-traversed list + * (user_event_mm_dup() walks mm->enablers under rcu_read_lock() only), + * and readers there dereference enabler->event and take a new ref on + * it. Both the put of that event reference and the free of the enabler + * therefore have to wait for a grace period so no reader can be looking + * at the enabler or racing the last put of its event. + * + * The put itself must not run in RCU context: when it drops the last + * reference user_event_put() takes event_mutex, which cannot be taken + * from a softirq/RCU callback. Defer both to a work item scheduled + * after a grace period via queue_rcu_work(). + */ + INIT_RCU_WORK(&enabler->put_rwork, delayed_user_event_enabler_put); + queue_rcu_work(system_percpu_wq, &enabler->put_rwork); } static int user_event_mm_fault_in(struct user_event_mm *mm, unsigned long uaddr, @@ -464,7 +489,7 @@ static void user_event_enabler_fault_fixup(struct work_struct *work) /* User asked for enabler to be removed during fault */ if (test_bit(ENABLE_VAL_FREEING_BIT, ENABLE_BITOPS(enabler))) { - user_event_enabler_destroy(enabler, true); + user_event_enabler_destroy(enabler); goto out; } @@ -764,7 +789,7 @@ static void user_event_mm_destroy(struct user_event_mm *mm) struct user_event_enabler *enabler, *next; list_for_each_entry_safe(enabler, next, &mm->enablers, mm_enablers_link) - user_event_enabler_destroy(enabler, false); + user_event_enabler_destroy(enabler); mmdrop(mm->mm); kfree(mm); @@ -2645,7 +2670,7 @@ static long user_events_ioctl_unreg(unsigned long uarg) flags |= enabler->values & ENABLE_VAL_COMPAT_MASK; if (!test_bit(ENABLE_VAL_FAULTING_BIT, ENABLE_BITOPS(enabler))) - user_event_enabler_destroy(enabler, true); + user_event_enabler_destroy(enabler); /* Removed at least one */ ret = 0; From 05074bb90af94f2acbbc8f8ffaa507f914c273c8 Mon Sep 17 00:00:00 2001 From: Yu Peng Date: Wed, 3 Jun 2026 14:25:33 +0800 Subject: [PATCH 0697/1054] tracing/synthetic: Free type string on error path parse_synth_field() builds a "__data_loc ..." type string before assigning it to field->type. If the seq_buf check fails, the common cleanup cannot free the temporary string. Free it before leaving. Link: https://patch.msgid.link/20260603062533.1096320-2-pengyu@kylinos.cn Signed-off-by: Yu Peng Signed-off-by: Steven Rostedt --- kernel/trace/trace_events_synth.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kernel/trace/trace_events_synth.c b/kernel/trace/trace_events_synth.c index cdd5b9332835..dc15658a887c 100644 --- a/kernel/trace/trace_events_synth.c +++ b/kernel/trace/trace_events_synth.c @@ -839,8 +839,10 @@ static struct synth_field *parse_synth_field(int argc, char **argv, seq_buf_puts(&s, "__data_loc "); seq_buf_puts(&s, field->type); - if (WARN_ON_ONCE(!seq_buf_buffer_left(&s))) + if (WARN_ON_ONCE(!seq_buf_buffer_left(&s))) { + kfree(type); goto free; + } s.buffer[s.len] = '\0'; kfree(field->type); From 2f8b8593c7832fad655290cef9e99af05b1b52b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ADra=20Canal?= Date: Fri, 3 Jul 2026 12:33:35 -0300 Subject: [PATCH 0698/1054] drm/v3d: Reject invalid indirect BO handle in indirect CSD setup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit v3d_get_cpu_indirect_csd_params() looks up the indirect buffer object from a userspace-supplied handle but never checks the result. A bogus or stale handle makes drm_gem_object_lookup() return NULL, which is then stored in info->indirect and only dereferenced later when the indirect CSD job runs, turning a userspace mistake into a NULL pointer dereference in the kernel. Bail out with -ENOENT as soon as the lookup fails, so the bad handle is rejected at submission time. Fixes: 18b8413b25b7 ("drm/v3d: Create a CPU job extension for a indirect CSD job") Reviewed-by: Iago Toral Quiroga Signed-off-by: Maíra Canal Link: https://patch.msgid.link/20260703-v3d-cpu-job-fixes-v3-2-bc51b1f3eeb5@igalia.com --- drivers/gpu/drm/v3d/v3d_submit.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/v3d/v3d_submit.c b/drivers/gpu/drm/v3d/v3d_submit.c index 1db43c6a078d..7682b24f13ec 100644 --- a/drivers/gpu/drm/v3d/v3d_submit.c +++ b/drivers/gpu/drm/v3d/v3d_submit.c @@ -495,6 +495,8 @@ v3d_get_cpu_indirect_csd_params(struct drm_file *file_priv, sizeof(indirect_csd.wg_uniform_offsets)); info->indirect = drm_gem_object_lookup(file_priv, indirect_csd.indirect); + if (!info->indirect) + return -ENOENT; return v3d_setup_csd_jobs_and_bos(file_priv, v3d, &indirect_csd.submit, &info->job, &info->clean_job, From 6b59c53c8adc2b522327407af5e1793a65b67e4b Mon Sep 17 00:00:00 2001 From: Christian Hewitt Date: Sat, 27 Jun 2026 13:12:05 +0000 Subject: [PATCH 0699/1054] ASoC: meson: aiu: fifo-spdif: soft reset the S/PDIF datapath on start/stop The I2S FIFO soft-resets its fast domain on start (AIU_RST_SOFT bit 0 + AIU_I2S_SYNC read in aiu_fifo_i2s_trigger), mirroring the downstream vendor driver's audio_out_i2s_enable(). The S/PDIF FIFO has no equivalent: it only toggles the IEC958 DCU, so a stale datapath FIFO can be replayed, producing the "machine gun noise" buffer underrun - on start when switching outputs, and on stop when playback ends. The latter is audible on devices with an always-on S/PDIF-fed DAC (e.g. the ES7144 on the WeTek Play2). The vendor driver resets the IEC958 fast domain (AIU_RST_SOFT bit 2) on both enable and disable (audio_hw_958_enable), and when reconfiguring (audio_hw_958_reset clears AIU_958_DCU_FF_CTRL then resets). Do the same: reset before enabling the DCU on start, and before disabling on stop. Fixes: 6ae9ca9ce986bf ("ASoC: meson: aiu: add i2s and spdif support") Signed-off-by: Christian Hewitt Reviewed-by: Martin Blumenstingl Link: https://patch.msgid.link/20260627131205.808800-1-christianshewitt@gmail.com Signed-off-by: Mark Brown --- sound/soc/meson/aiu-fifo-spdif.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/soc/meson/aiu-fifo-spdif.c b/sound/soc/meson/aiu-fifo-spdif.c index e0e00ec026dc..a9861c5d6637 100644 --- a/sound/soc/meson/aiu-fifo-spdif.c +++ b/sound/soc/meson/aiu-fifo-spdif.c @@ -24,6 +24,7 @@ #define AIU_MEM_IEC958_CONTROL_MODE_16BIT BIT(7) #define AIU_MEM_IEC958_CONTROL_MODE_LINEAR BIT(8) #define AIU_MEM_IEC958_BUF_CNTL_INIT BIT(0) +#define AIU_RST_SOFT_958_FAST BIT(2) #define AIU_FIFO_SPDIF_BLOCK 8 @@ -68,11 +69,15 @@ static int fifo_spdif_trigger(struct snd_pcm_substream *substream, int cmd, case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + snd_soc_component_write(component, AIU_RST_SOFT, + AIU_RST_SOFT_958_FAST); fifo_spdif_dcu_enable(component, true); break; case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_STOP: + snd_soc_component_write(component, AIU_RST_SOFT, + AIU_RST_SOFT_958_FAST); fifo_spdif_dcu_enable(component, false); break; default: From dedf4ccbcbd249e2435344d137247d94c0aa10ee Mon Sep 17 00:00:00 2001 From: Baojun Xu Date: Thu, 2 Jul 2026 18:44:29 +0800 Subject: [PATCH 0700/1054] ASoC: amd: acp: amd-acp70-acpi-match: Add tas2783 support Add driver data and ACPI match table entries for the TAS2783 on ACP7.0 and ACP7.1 platforms using SoundWire link 0. Signed-off-by: Baojun Xu Link: https://patch.msgid.link/20260702104429.1157-1-baojun.xu@ti.com Signed-off-by: Mark Brown --- sound/soc/amd/acp/amd-acp70-acpi-match.c | 45 ++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/sound/soc/amd/acp/amd-acp70-acpi-match.c b/sound/soc/amd/acp/amd-acp70-acpi-match.c index 9e97a805d0df..ccd01152c87d 100644 --- a/sound/soc/amd/acp/amd-acp70-acpi-match.c +++ b/sound/soc/amd/acp/amd-acp70-acpi-match.c @@ -553,6 +553,46 @@ static const struct snd_soc_acpi_link_adr acp70_rt722_l0_rt1320_l1[] = { {} }; +static const struct snd_soc_acpi_adr_device tas2783_2_adr[] = { + { + /* left */ + .adr = 0x00003c0102000001ull, + .num_endpoints = 1, + .endpoints = &spk_l_endpoint, + .name_prefix = "tas2783-1", + }, + { + /* right */ + .adr = 0x00003d0102000001ull, + .num_endpoints = 1, + .endpoints = &spk_l_endpoint, + .name_prefix = "tas2783-2", + }, + { + /* left */ + .adr = 0x0000390102000001ull, + .num_endpoints = 1, + .endpoints = &spk_r_endpoint, + .name_prefix = "tas2783-3", + }, + { + /* right */ + .adr = 0x00003a0102000001ull, + .num_endpoints = 1, + .endpoints = &spk_r_endpoint, + .name_prefix = "tas2783-4", + }, +}; + +static const struct snd_soc_acpi_link_adr acp70_tas2783_2[] = { + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(tas2783_2_adr), + .adr_d = tas2783_2_adr, + }, + {} +}; + static const struct snd_soc_acpi_adr_device rt1320_0_single_adr[] = { { .adr = 0x000030025D132001ull, @@ -659,6 +699,11 @@ static const struct snd_soc_acpi_link_adr acp70_rt721_only[] = { }; struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sdw_machines[] = { + { + .link_mask = BIT(0), + .links = acp70_tas2783_2, + .drv_name = "amd_sdw", + }, { .link_mask = BIT(0) | BIT(1), .links = acp70_rt1320_l0_rt722_l1, From 030db7005efe6a0705ddf07fced494c364a1c915 Mon Sep 17 00:00:00 2001 From: Liang Luo Date: Tue, 7 Jul 2026 17:45:38 +0800 Subject: [PATCH 0701/1054] sched_ext: Documentation: Fix ops table header reference The "Where to Look" and "ABI Instability" sections state that the ops table is defined in include/linux/sched/ext.h. However, struct sched_ext_ops is actually defined in kernel/sched/ext/internal.h, along with the SCX_OPS_* flags; include/linux/sched/ext.h holds the core data structures (struct sched_ext_entity, struct scx_dispatch_q, ...) and the DSQ constants. Point the ops table references to the correct header. Signed-off-by: Liang Luo Reviewed-by: Andrea Righi Signed-off-by: Tejun Heo --- Documentation/scheduler/sched-ext.rst | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Documentation/scheduler/sched-ext.rst b/Documentation/scheduler/sched-ext.rst index 4b1ffd03f516..2771ea4cc14a 100644 --- a/Documentation/scheduler/sched-ext.rst +++ b/Documentation/scheduler/sched-ext.rst @@ -493,8 +493,9 @@ a freshly woken up task gets on a CPU. Where to Look ============= -* ``include/linux/sched/ext.h`` defines the core data structures, ops table - and constants. +* ``include/linux/sched/ext.h`` defines the core data structures and + constants, while the ops table (``struct sched_ext_ops``) is defined in + ``kernel/sched/ext/internal.h``. * ``kernel/sched/ext/ext.c`` contains sched_ext core implementation and helpers. The functions prefixed with ``scx_bpf_`` can be called from the BPF @@ -555,7 +556,8 @@ ABI Instability =============== The APIs provided by sched_ext to BPF schedulers programs have no stability -guarantees. This includes the ops table callbacks and constants defined in +guarantees. This includes the ops table callbacks defined in +``kernel/sched/ext/internal.h`` and the constants defined in ``include/linux/sched/ext.h``, as well as the ``scx_bpf_`` kfuncs defined in ``kernel/sched/ext/ext.c`` and ``kernel/sched/ext/idle.c``. From 5aa31cb1dfb08f19408c9c33da71f46525f6cba9 Mon Sep 17 00:00:00 2001 From: Guopeng Zhang Date: Tue, 7 Jul 2026 18:21:46 +0800 Subject: [PATCH 0702/1054] Docs/admin-guide/cgroup-v2: drop stale misc interface file count The Miscellaneous controller documentation states it "provides 3 interface files", but misc_cg_files[] actually registers six (max, current, peak, capacity, events, events.local). Drop the stale count and let the file list that follows speak for itself. Signed-off-by: Guopeng Zhang Signed-off-by: Tejun Heo --- Documentation/admin-guide/cgroup-v2.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/admin-guide/cgroup-v2.rst b/Documentation/admin-guide/cgroup-v2.rst index ce6741f78f4f..7ce60fefc7ad 100644 --- a/Documentation/admin-guide/cgroup-v2.rst +++ b/Documentation/admin-guide/cgroup-v2.rst @@ -2934,7 +2934,8 @@ include/linux/misc_cgroup.h. Misc Interface Files ~~~~~~~~~~~~~~~~~~~~ -Miscellaneous controller provides 3 interface files. If two misc resources (res_a and res_b) are registered then: +Miscellaneous controller provides the following interface files. If two misc +resources (res_a and res_b) are registered then: misc.capacity A read-only flat-keyed file shown only in the root cgroup. It shows From 451a7467570d9578b5f7fba9aa6e0ed1ac1fcfec Mon Sep 17 00:00:00 2001 From: Guopeng Zhang Date: Tue, 7 Jul 2026 18:21:47 +0800 Subject: [PATCH 0703/1054] Docs/admin-guide/cgroup-v1: document rdma.peak, rdma.events and rdma.events.local The v1 RDMA controller documentation only describes rdma.max and rdma.current, but the controller exposes three more files -- rdma.peak, rdma.events and rdma.events.local -- which are already documented for v2. Mirror the v2 wording so the v1 documentation matches the files actually visible on a v1 mount. Co-developed-by: Tao Cui Signed-off-by: Tao Cui Signed-off-by: Guopeng Zhang Signed-off-by: Tejun Heo --- Documentation/admin-guide/cgroup-v1/rdma.rst | 66 ++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/Documentation/admin-guide/cgroup-v1/rdma.rst b/Documentation/admin-guide/cgroup-v1/rdma.rst index e69369b7252e..abddf34d2667 100644 --- a/Documentation/admin-guide/cgroup-v1/rdma.rst +++ b/Documentation/admin-guide/cgroup-v1/rdma.rst @@ -9,6 +9,7 @@ RDMA Controller 1-2. Why RDMA controller needed? 1-3. How is RDMA controller implemented? 2. Usage Examples + 3. RDMA Interface Files 1. Overview =========== @@ -115,3 +116,68 @@ Following resources can be accounted by rdma controller. (d) Delete resource limit:: echo mlx4_0 hca_handle=max hca_object=max > /sys/fs/cgroup/rdma/1/rdma.max + +3. RDMA Interface Files +======================== + +The following interface files are available in each non-root RDMA cgroup. + + rdma.max + A read-write file which describes the configured resource limit + for an RDMA/IB device. See the Usage Examples above. + + rdma.current + A read-only file which describes the current resource usage. + + rdma.peak + A read-only nested-keyed file which shows the historical high + watermark of resource usage per device since the cgroup was created. + + An example for mlx4 and ocrdma device follows:: + + mlx4_0 hca_handle=1 hca_object=20 + ocrdma1 hca_handle=0 hca_object=23 + + rdma.events + A read-only nested-keyed file which exists on non-root cgroups + and contains the following keys: + + max + The number of times a process in this cgroup or its + descendants attempted an RDMA resource allocation that + was rejected because a rdma.max limit in the subtree + was reached. This is a hierarchical counter propagated + upward to all ancestor cgroups. A value change in this + file generates a file modified event. + + alloc_fail + The number of RDMA resource allocation attempts that + originated in this cgroup or its descendants and failed + due to a rdma.max limit being reached. This is a + hierarchical counter propagated upward. + + An example for mlx4 device follows:: + + mlx4_0 hca_handle.max=5 hca_handle.alloc_fail=3 hca_object.max=0 hca_object.alloc_fail=0 + + rdma.events.local + Similar to rdma.events but the fields are local to the cgroup, + i.e. not hierarchical. The file modified event generated on this + file reflects only the local events. + + The following nested keys are defined. + + max + The number of times a process in this cgroup or its + descendants attempted an RDMA resource allocation that + was rejected because this cgroup's own rdma.max limit + was reached. + + alloc_fail + The number of RDMA resource allocation attempts + originating from this cgroup that failed due to this + cgroup's or an ancestor's rdma.max limit. + + An example for mlx4 device follows:: + + mlx4_0 hca_handle.max=5 hca_handle.alloc_fail=0 hca_object.max=0 hca_object.alloc_fail=0 From 97fef602584458b100d383afed9d176c0bc689ab Mon Sep 17 00:00:00 2001 From: Guopeng Zhang Date: Tue, 7 Jul 2026 18:21:48 +0800 Subject: [PATCH 0704/1054] Docs/admin-guide/cgroup-v2: note blkcg_debug_stats gates io.latency stats The io.stat section says that enabling the io.latency controller exposes the depth, avg_lat and win stats in addition to the normal ones. However, these io.latency-specific stats are debug stats and are only emitted when the blkcg_debug_stats module parameter is enabled, which is disabled by default. Make this explicit so users do not expect these fields to appear in io.stat by default, and qualify the usage text that suggests using avg_lat to pick an io.latency target. Signed-off-by: Guopeng Zhang Signed-off-by: Tejun Heo --- Documentation/admin-guide/cgroup-v2.rst | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/Documentation/admin-guide/cgroup-v2.rst b/Documentation/admin-guide/cgroup-v2.rst index 7ce60fefc7ad..14b8c571c0d1 100644 --- a/Documentation/admin-guide/cgroup-v2.rst +++ b/Documentation/admin-guide/cgroup-v2.rst @@ -2257,10 +2257,11 @@ groups D and F will influence each other. Group G will influence nobody:: So the ideal way to configure this is to set io.latency in groups A, B, and C. Generally you do not want to set a value lower than the latency your device supports. Experiment to find the value that works best for your workload. -Start at higher than the expected latency for your device and watch the -avg_lat value in io.stat for your workload group to get an idea of the -latency you see during normal operation. Use the avg_lat value as a basis for -your real setting, setting at 10-15% higher than the value in io.stat. +Start at higher than the expected latency for your device and, with +blkcg_debug_stats enabled, watch the avg_lat value in io.stat for your +workload group to get an idea of the latency you see during normal operation. +Use the avg_lat value as a basis for your real setting, setting at 10-15% +higher than the value in io.stat. How IO Latency Throttling Works ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -2298,7 +2299,9 @@ IO Latency Interface Files io.stat If the controller is enabled you will see extra stats in io.stat in - addition to the normal ones. + addition to the normal ones. These debug stats are only emitted when + the blkcg_debug_stats module parameter is enabled (it is disabled by + default). depth This is the current queue depth for the group. From 5893013efabb056399a01e267f410cf76eba25eb Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Tue, 7 Jul 2026 11:29:36 +0530 Subject: [PATCH 0705/1054] ASoC: amd: ps: disable MSI on resume in ACP PCI driver BIOS/firmware may re-enable MSI in PCI config space during system level resume even though this driver only uses legacy INTx interrupts. If MSI is left enabled with stale address/data registers, the device will write interrupts to a bogus address causing IOMMU IO_PAGE_FAULT and interrupt delivery failure. Clear the MSI Enable bit before reinitializing the ACP hardware on system level resume. Fixes: 491628388005 ("ASoC: amd: ps: add callback functions for acp pci driver pm ops") Signed-off-by: Vijendar Mukunda Reviewed-by: Mario Limonciello (AMD) Link: https://patch.msgid.link/20260707060130.2514138-2-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- sound/soc/amd/ps/pci-ps.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/sound/soc/amd/ps/pci-ps.c b/sound/soc/amd/ps/pci-ps.c index 4ecda224157b..635832da45f9 100644 --- a/sound/soc/amd/ps/pci-ps.c +++ b/sound/soc/amd/ps/pci-ps.c @@ -693,8 +693,37 @@ static int snd_acp_runtime_resume(struct device *dev) return acp_hw_runtime_resume(dev); } +static void acp_disable_msi_on_resume(struct pci_dev *pdev) +{ + u16 control; + + if (!pdev->msi_cap) + return; + + pci_read_config_word(pdev, pdev->msi_cap + PCI_MSI_FLAGS, &control); + if (control & PCI_MSI_FLAGS_ENABLE) { + dev_warn(&pdev->dev, + "ACP: MSI unexpectedly enabled after resume (flags=0x%04x), disabling\n", + control); + control &= ~PCI_MSI_FLAGS_ENABLE; + pci_write_config_word(pdev, pdev->msi_cap + PCI_MSI_FLAGS, control); + } +} + static int snd_acp_resume(struct device *dev) { + struct pci_dev *pdev = to_pci_dev(dev); + + /* + * BIOS/firmware may re-enable MSI in PCI config space during + * system resume even though this driver only uses legacy INTx + * interrupts. If MSI is left enabled with stale address/data + * registers, the device will write interrupts to a bogus address + * causing IOMMU IO_PAGE_FAULT and interrupt delivery failure. + * Explicitly clear the MSI Enable bit before reinitializing + * the ACP hardware. + */ + acp_disable_msi_on_resume(pdev); return acp_hw_resume(dev); } From f7697ecf6eab9d4887dd731038b3dc405c7e755e Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Tue, 7 Jul 2026 11:29:37 +0530 Subject: [PATCH 0706/1054] ASoC: amd: ps: fix wrong ACP version string in pci_request_regions() The driver handles ACP6.3/7.0/7.1/7.2 platforms but the region was claimed with the stale name "AMD ACP6.2 audio" left over from the original ACP6.2 driver. Correct it to "AMD ACP6.3 audio". Fixes: 95e43a170bb1 ("ASoC: amd: add Pink Sardine ACP PCI driver") Signed-off-by: Vijendar Mukunda Reviewed-by: Mario Limonciello (AMD) Link: https://patch.msgid.link/20260707060130.2514138-3-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- sound/soc/amd/ps/pci-ps.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/amd/ps/pci-ps.c b/sound/soc/amd/ps/pci-ps.c index 635832da45f9..1162d13d8505 100644 --- a/sound/soc/amd/ps/pci-ps.c +++ b/sound/soc/amd/ps/pci-ps.c @@ -602,7 +602,7 @@ static int snd_acp63_probe(struct pci_dev *pci, return -ENODEV; } - ret = pci_request_regions(pci, "AMD ACP6.2 audio"); + ret = pci_request_regions(pci, "AMD ACP6.3 audio"); if (ret < 0) { dev_err(&pci->dev, "pci_request_regions failed\n"); goto disable_pci; From dec5aaa27603e1d7b426ce3504af6d1a62e4d444 Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Tue, 7 Jul 2026 11:29:38 +0530 Subject: [PATCH 0707/1054] ASoC: amd: ps: replace bitwise OR with logical OR in IRQ return check The condition 'irq_flag | wake_irq_flag' uses bitwise OR to combine two integer flags that are used as booleans. Replace with logical OR '||' to correctly express the intended boolean check. Signed-off-by: Vijendar Mukunda Reviewed-by: Mario Limonciello (AMD) Fixes: 7f91f012c1df0 ("ASoC: amd: ps: fix for irq handler return status") Link: https://patch.msgid.link/20260707060130.2514138-4-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- sound/soc/amd/ps/pci-ps.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/amd/ps/pci-ps.c b/sound/soc/amd/ps/pci-ps.c index 1162d13d8505..729f9aaba69e 100644 --- a/sound/soc/amd/ps/pci-ps.c +++ b/sound/soc/amd/ps/pci-ps.c @@ -248,7 +248,7 @@ static irqreturn_t acp63_irq_handler(int irq, void *dev_id) if (sdw_dma_irq_flag) return IRQ_WAKE_THREAD; - if (irq_flag | wake_irq_flag) + if (irq_flag || wake_irq_flag) return IRQ_HANDLED; else return IRQ_NONE; From 70b439e46effd782097f640c0b64f5f8b0e79913 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Tue, 7 Jul 2026 17:38:27 +0200 Subject: [PATCH 0708/1054] selftests: riscv: Bypass libc in inactive vector ptrace test The ptrace_v_not_enabled test expects the child to reach its ebreak before it has used the vector extension. That is not guaranteed when using fork(), because libc may run child atfork handlers before returning to the test code. In those cases PTRACE_GETREGSET for NT_RISCV_VECTOR then succeeds instead of returning ENODATA for inactive vector state. Use the raw clone syscall with SIGCHLD to keep fork-like semantics while bypassing libc's fork wrapper and atfork handler chain. Cc: Andy Chiu Assisted-by: Claude:claude-sonnet-4-6 Signed-off-by: Andrew Jones Link: https://patch.msgid.link/20260707153827.175245-1-andrew.jones@oss.qualcomm.com Signed-off-by: Paul Walmsley --- tools/testing/selftests/riscv/vector/validate_v_ptrace.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/riscv/vector/validate_v_ptrace.c b/tools/testing/selftests/riscv/vector/validate_v_ptrace.c index 74b6f6bcf067..b038e2175c80 100644 --- a/tools/testing/selftests/riscv/vector/validate_v_ptrace.c +++ b/tools/testing/selftests/riscv/vector/validate_v_ptrace.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only #include +#include #include #include #include @@ -25,9 +26,9 @@ TEST(ptrace_v_not_enabled) SKIP(return, "Vector not supported"); chld_lock = 1; - pid = fork(); + pid = (pid_t)syscall(SYS_clone, SIGCHLD, 0, NULL, 0, NULL); ASSERT_LE(0, pid) - TH_LOG("fork: %m"); + TH_LOG("clone: %m"); if (pid == 0) { while (chld_lock == 1) From 0f092793a7b527dfb2cde323d4e5630d43447b84 Mon Sep 17 00:00:00 2001 From: Lizhi Hou Date: Tue, 7 Jul 2026 10:23:07 -0700 Subject: [PATCH 0709/1054] accel/amdxdna: Check drmm_mutex_init() return value drmm_mutex_init() may fail and return an error. Check the return value and abort initialization if mutex creation fails. Fixes: 8c9ff1b181ba ("accel/amdxdna: Add a new driver for AMD AI Engine") Reviewed-by: Max Zhen Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/20260707172307.539676-1-lizhi.hou@amd.com --- drivers/accel/amdxdna/amdxdna_pci_drv.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/accel/amdxdna/amdxdna_pci_drv.c b/drivers/accel/amdxdna/amdxdna_pci_drv.c index e94d8290a807..86e9c230875a 100644 --- a/drivers/accel/amdxdna/amdxdna_pci_drv.c +++ b/drivers/accel/amdxdna/amdxdna_pci_drv.c @@ -373,7 +373,10 @@ static int amdxdna_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (ret) return ret; - drmm_mutex_init(ddev, &xdna->dev_lock); + ret = drmm_mutex_init(ddev, &xdna->dev_lock); + if (ret) + return ret; + init_rwsem(&xdna->notifier_lock); INIT_LIST_HEAD(&xdna->client_list); pci_set_drvdata(pdev, xdna); From 44d8fddf1c87d6bb6b65983041a0ce6c2af66bb9 Mon Sep 17 00:00:00 2001 From: Lizhi Hou Date: Tue, 7 Jul 2026 10:23:23 -0700 Subject: [PATCH 0710/1054] accel/amdxdna: Check init_srcu_struct() return value The return value of init_srcu_struct() is currently ignored. If initialization fails, subsequent use of hwctx_srcu may result in invalid memory accesses. Check the return value of init_srcu_struct() and propagate the error to the caller. Fixes: aac243092b70 ("accel/amdxdna: Add command execution") Reviewed-by: Mario Limonciello (AMD) Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/20260707172323.539721-1-lizhi.hou@amd.com --- drivers/accel/amdxdna/amdxdna_pci_drv.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/accel/amdxdna/amdxdna_pci_drv.c b/drivers/accel/amdxdna/amdxdna_pci_drv.c index 86e9c230875a..bb339e641416 100644 --- a/drivers/accel/amdxdna/amdxdna_pci_drv.c +++ b/drivers/accel/amdxdna/amdxdna_pci_drv.c @@ -109,11 +109,16 @@ static int amdxdna_drm_open(struct drm_device *ddev, struct drm_file *filp) { struct amdxdna_dev *xdna = to_xdna_dev(ddev); struct amdxdna_client *client; + int ret; client = kzalloc_obj(*client); if (!client) return -ENOMEM; + ret = init_srcu_struct(&client->hwctx_srcu); + if (ret) + goto free_client; + client->pid = pid_nr(rcu_access_pointer(filp->pid)); client->xdna = xdna; client->pasid = IOMMU_PASID_INVALID; @@ -125,13 +130,12 @@ static int amdxdna_drm_open(struct drm_device *ddev, struct drm_file *filp) XDNA_WARN(xdna, "PASID not available for pid %d", client->pid); if (!amdxdna_use_carveout(xdna)) { XDNA_ERR(xdna, "PASID unavailable and carveout not configured"); - kfree(client); - return -EINVAL; + ret = -EINVAL; + goto cleanup_srcu; } } } mmgrab(client->mm); - init_srcu_struct(&client->hwctx_srcu); xa_init_flags(&client->hwctx_xa, XA_FLAGS_ALLOC); xa_init_flags(&client->dev_heap_xa, XA_FLAGS_ALLOC); drm_mm_init(&client->dev_heap_mm, xdna->dev_info->dev_mem_base, @@ -149,6 +153,12 @@ static int amdxdna_drm_open(struct drm_device *ddev, struct drm_file *filp) XDNA_DBG(xdna, "pid %d opened", client->pid); return 0; + +cleanup_srcu: + cleanup_srcu_struct(&client->hwctx_srcu); +free_client: + kfree(client); + return ret; } static void amdxdna_client_cleanup(struct amdxdna_client *client) From c69dbbf0212734e22219dfc31c0922bd7c9ffbb0 Mon Sep 17 00:00:00 2001 From: Lizhi Hou Date: Tue, 7 Jul 2026 13:15:56 -0700 Subject: [PATCH 0711/1054] accel/amdxdna: Fix potential NULL pointer dereference of abo->client Closing a BO handle clears abo->client, while the underlying GEM object may remain alive due to internal kernel references. As a result, code executed after the BO handle is closed may dereference a NULL abo->client pointer. Remove accesses to abo->client from code paths that may execute after the BO handle has been closed. Fixes: d76856beb4a4 ("accel/amdxdna: Refactor GEM BO handling and add helper APIs for address retrieval") Reviewed-by: Max Zhen Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/20260707201556.562191-1-lizhi.hou@amd.com --- drivers/accel/amdxdna/aie2_message.c | 4 ++-- drivers/accel/amdxdna/amdxdna_gem.c | 11 +++++++++-- drivers/accel/amdxdna/amdxdna_gem.h | 11 +++++++++-- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/drivers/accel/amdxdna/aie2_message.c b/drivers/accel/amdxdna/aie2_message.c index c4b364801cc0..dfe0fbdf066d 100644 --- a/drivers/accel/amdxdna/aie2_message.c +++ b/drivers/accel/amdxdna/aie2_message.c @@ -840,7 +840,7 @@ static struct aie2_exec_msg_ops npu_exec_message_ops = { static int aie2_init_exec_req(void *req, struct amdxdna_gem_obj *cmd_abo, size_t *size, u32 *msg_op) { - struct amdxdna_dev *xdna = cmd_abo->client->xdna; + struct amdxdna_dev *xdna = to_xdna_dev(to_gobj(cmd_abo)->dev); int ret; u32 op; @@ -874,7 +874,7 @@ static int aie2_cmdlist_fill_slot(void *slot, struct amdxdna_gem_obj *cmd_abo, size_t *size, u32 *cmd_op) { - struct amdxdna_dev *xdna = cmd_abo->client->xdna; + struct amdxdna_dev *xdna = to_xdna_dev(to_gobj(cmd_abo)->dev); int ret; u32 op; diff --git a/drivers/accel/amdxdna/amdxdna_gem.c b/drivers/accel/amdxdna/amdxdna_gem.c index 1275f91ca705..4628a2787265 100644 --- a/drivers/accel/amdxdna/amdxdna_gem.c +++ b/drivers/accel/amdxdna/amdxdna_gem.c @@ -198,6 +198,7 @@ amdxdna_gem_destroy_obj(struct amdxdna_gem_obj *abo) */ void *amdxdna_gem_vmap(struct amdxdna_gem_obj *abo) { + struct amdxdna_dev *xdna = to_xdna_dev(to_gobj(abo)->dev); struct iosys_map map = IOSYS_MAP_INIT_VADDR(NULL); int ret; @@ -210,7 +211,7 @@ void *amdxdna_gem_vmap(struct amdxdna_gem_obj *abo) if (!abo->mem.kva) { ret = drm_gem_vmap(to_gobj(abo), &map); if (ret) - XDNA_ERR(abo->client->xdna, "Vmap bo failed, ret %d", ret); + XDNA_ERR(xdna, "Vmap bo failed, ret %d", ret); else abo->mem.kva = map.vaddr; } @@ -354,7 +355,13 @@ static int amdxdna_hmm_register(struct amdxdna_gem_obj *abo, unsigned long nr_pages; int ret; - if (!amdxdna_pasid_on(abo->client)) { + /* + * When PASID is off, amdxdna_gem_obj_open() called amdxdna_dma_map_bo() + * and mem.dma_addr is valid; use the DMA address directly and skip HMM. + * Avoid dereferencing abo->client which may be NULL (cleared in close()) + * while internal kernel references are still held. + */ + if (abo->mem.dma_addr != AMDXDNA_INVALID_ADDR) { /* Need to set uva for heap uva validation */ abo->mem.uva = addr; return 0; diff --git a/drivers/accel/amdxdna/amdxdna_gem.h b/drivers/accel/amdxdna/amdxdna_gem.h index a35d2f15d32c..1e90e32bf3cd 100644 --- a/drivers/accel/amdxdna/amdxdna_gem.h +++ b/drivers/accel/amdxdna/amdxdna_gem.h @@ -88,12 +88,19 @@ u64 amdxdna_gem_dev_addr(struct amdxdna_gem_obj *abo); static inline u64 amdxdna_dev_bo_offset(struct amdxdna_gem_obj *abo) { - return amdxdna_gem_dev_addr(abo) - abo->client->xdna->dev_info->dev_mem_base; + return amdxdna_gem_dev_addr(abo) - to_xdna_dev(to_gobj(abo)->dev)->dev_info->dev_mem_base; } static inline u64 amdxdna_obj_dma_addr(struct amdxdna_gem_obj *abo) { - return amdxdna_pasid_on(abo->client) ? amdxdna_gem_uva(abo) : abo->mem.dma_addr; + /* + * amdxdna_gem_obj_open() calls amdxdna_dma_map_bo() only when PASID is + * off, leaving mem.dma_addr at AMDXDNA_INVALID_ADDR when PASID is on. + * Avoid dereferencing abo->client, which is cleared to NULL by + * amdxdna_gem_obj_close() while internal kernel references remain. + */ + return (abo->mem.dma_addr != AMDXDNA_INVALID_ADDR) ? + abo->mem.dma_addr : amdxdna_gem_uva(abo); } void amdxdna_umap_put(struct amdxdna_umap *mapp); From 917d0a4b95ea7ba01ed6296fb808f752d5d81107 Mon Sep 17 00:00:00 2001 From: TJ Adams Date: Mon, 6 Jul 2026 14:00:53 -0700 Subject: [PATCH 0712/1054] ata: libata-core: Skip HPA resize for locked drives Skip HPA resize in ata_hpa_resize() if the drive is security locked. If the drive is locked, the command to read the native max address fails with -EACCES, which currently causes the sticky quirk ATA_QUIRK_BROKEN_HPA to be set on the device. Setting this sticky quirk causes subsequent revalidations (after the drive is unlocked) to bypass HPA checks, preventing the unlocked drive from exposing its full native capacity without a reboot or device removal. Cc: stable@vger.kernel.org Signed-off-by: Terrence Adams Signed-off-by: Damien Le Moal --- drivers/ata/libata-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index bdc88cf74709..3c06a15952f8 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -1338,7 +1338,7 @@ static int ata_hpa_resize(struct ata_device *dev) /* do we need to do it? */ if ((dev->class != ATA_DEV_ATA && dev->class != ATA_DEV_ZAC) || !ata_id_has_lba(dev->id) || !ata_id_hpa_enabled(dev->id) || - (dev->quirks & ATA_QUIRK_BROKEN_HPA)) + (dev->quirks & ATA_QUIRK_BROKEN_HPA) || ata_id_is_locked(dev->id)) return 0; /* read native max address */ From 793bf193b18e9bff6c4280268bbffd16a5b533e5 Mon Sep 17 00:00:00 2001 From: TJ Adams Date: Mon, 6 Jul 2026 14:00:54 -0700 Subject: [PATCH 0713/1054] ata: libata-core: Allow capacity transition to zero for locked drives Commit 91842ed844a0 ("ata: libata-core: Set capacity to zero for a security locked drive") introduced setting the device capacity (n_sectors) to zero in ata_dev_configure() if the drive is security locked. However, during runtime revalidation, ata_dev_revalidate() compares the new capacity (now 0) with the old capacity (>0) and detects a mismatch. Since it does not consider the locked status, it returns -ENODEV. This revalidation failure can occur when doing a reset of the PHY (e.g. hard reset) for a controller that has I/Os in flight. The timed out I/Os trigger the SCSI Error Handling (EH) path, which in turn invokes libata device revalidation. If the drive is locked at runtime (e.g. it lost power during reset and relocked), revalidation sees the capacity transition to zero and fails, eventually disabling the device. Fix this by allowing the capacity transition to zero in ata_dev_revalidate() if the drive is reported as security locked by ata_id_is_locked(). Fixes: 91842ed844a0 ("ata: libata-core: Set capacity to zero for a security locked drive") Cc: stable@vger.kernel.org Assisted-by: Antigravity:gemini-3.5-flash Signed-off-by: Terrence Adams Signed-off-by: Damien Le Moal --- drivers/ata/libata-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 3c06a15952f8..c43bd28b20b1 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -3992,7 +3992,7 @@ int ata_dev_revalidate(struct ata_device *dev, unsigned int new_class, /* verify n_sectors hasn't changed */ if (dev->class != ATA_DEV_ATA || !n_sectors || - dev->n_sectors == n_sectors) + dev->n_sectors == n_sectors || ata_id_is_locked(dev->id)) return 0; /* n_sectors has changed */ From d3386e17393bec1341cfeedb9d08d6846ccd6fb2 Mon Sep 17 00:00:00 2001 From: Gao Xiang Date: Wed, 8 Jul 2026 11:18:45 +0800 Subject: [PATCH 0714/1054] erofs: relax sanity check for tail pclusters due to ztailpacking If the tail data can be inlined into the inode meta block, it should be converted into a regular tail pcluster. In principle, it should be converted into an uncompressed pcluster if there is not enough gain to use compression (map->m_llen < map->m_plen); but since there are various shipped images, relax the condition for ztailpacking tail pcluster fallback instead of reporting corruption incorrectly. Reported-and-tested-by: Yifan Zhao Reported-by: Alberto Salvia Novella Closes: https://github.com/erofs/erofs-utils/issues/51 Fixes: a5242d37c83a ("erofs: error out obviously illegal extents in advance") Signed-off-by: Gao Xiang --- fs/erofs/zmap.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/erofs/zmap.c b/fs/erofs/zmap.c index bab521613552..5811556a7b71 100644 --- a/fs/erofs/zmap.c +++ b/fs/erofs/zmap.c @@ -732,7 +732,8 @@ static int z_erofs_map_sanity_check(struct inode *inode, map->m_algorithmformat, EROFS_I(inode)->nid); return -EFSCORRUPTED; } - if (EROFS_MAP_FULL(map->m_flags) && map->m_llen < map->m_plen) { + if (EROFS_MAP_FULL(map->m_flags) && map->m_llen < map->m_plen && + map->m_la + map->m_llen < inode->i_size) { erofs_err(inode->i_sb, "too much compressed data @ la %llu of nid %llu", map->m_la, EROFS_I(inode)->nid); return -EFSCORRUPTED; From 95e19a4d8dd3072d51209081a29467d887c84158 Mon Sep 17 00:00:00 2001 From: Gao Xiang Date: Tue, 7 Jul 2026 21:39:39 +0800 Subject: [PATCH 0715/1054] erofs: get rid of erofs_is_ishare_inode() helper Just open-code it for simplicity since FS_ONDEMAND no longer exists. Signed-off-by: Gao Xiang --- fs/erofs/ishare.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/fs/erofs/ishare.c b/fs/erofs/ishare.c index 0868c12fc15b..a1a20a5ba548 100644 --- a/fs/erofs/ishare.c +++ b/fs/erofs/ishare.c @@ -12,12 +12,6 @@ static struct vfsmount *erofs_ishare_mnt; -static inline bool erofs_is_ishare_inode(struct inode *inode) -{ - /* assumed FS_ONDEMAND is excluded with FS_PAGE_CACHE_SHARE feature */ - return inode->i_sb->s_type == &erofs_anon_fs_type; -} - static int erofs_ishare_iget5_eq(struct inode *inode, void *data) { struct erofs_inode_fingerprint *fp1 = &EROFS_I(inode)->fingerprint; @@ -179,7 +173,7 @@ struct inode *erofs_real_inode(struct inode *inode, bool *need_iput) struct inode *realinode; *need_iput = false; - if (!erofs_is_ishare_inode(inode)) + if (inode->i_sb != erofs_ishare_mnt->mnt_sb) return inode; vi_share = EROFS_I(inode); From e0776dde101ac9eae7e1421147f3a6ae7a556da7 Mon Sep 17 00:00:00 2001 From: Guodong Xu Date: Wed, 1 Jul 2026 08:52:14 -0400 Subject: [PATCH 0716/1054] dt-bindings: riscv: sort multi-letter Z extensions alphanumerically The multi-letter extension enum is documented as being sorted alphanumerically (see the "multi-letter extensions, sorted alphanumerically" comment), but several Z entries have drifted out of order. Reorder the affected entries so the multi-letter Z list is sorted alphanumerically again. Acked-by: Conor Dooley Signed-off-by: Guodong Xu Link: https://patch.msgid.link/20260701-rva23u64-hwprobe-v2-v5-1-2c61f94a695a@gmail.com Signed-off-by: Paul Walmsley --- .../devicetree/bindings/riscv/extensions.yaml | 184 +++++++++--------- 1 file changed, 92 insertions(+), 92 deletions(-) diff --git a/Documentation/devicetree/bindings/riscv/extensions.yaml b/Documentation/devicetree/bindings/riscv/extensions.yaml index 2b0a8a93bb21..5ffc40d599c0 100644 --- a/Documentation/devicetree/bindings/riscv/extensions.yaml +++ b/Documentation/devicetree/bindings/riscv/extensions.yaml @@ -457,6 +457,13 @@ properties: merged in the riscv-isa-manual by commit dbc79cf28a2 ("Initial seed of zc.adoc to src tree."). + - const: zclsd + description: + The Zclsd extension implements the compressed (16-bit) version of the + Load/Store Pair for RV32. As with Zilsd, this extension was ratified + in commit f88abf1 ("Integrating load/store pair for RV32 with the + main manual") of riscv-isa-manual. + - const: zcmop description: The standard Zcmop extension version 1.0, as ratified in commit @@ -487,6 +494,22 @@ properties: in commit 64074bc ("Update version numbers for Zfh/Zfinx") of riscv-isa-manual. + - const: zicbom + description: + The standard Zicbom extension for base cache management operations as + ratified in commit 3dd606f ("Create cmobase-v1.0.pdf") of riscv-CMOs. + + - const: zicbop + description: + The standard Zicbop extension for cache-block prefetch instructions + as ratified in commit 3dd606f ("Create cmobase-v1.0.pdf") of + riscv-CMOs. + + - const: zicboz + description: + The standard Zicboz extension for cache-block zeroing as ratified + in commit 3dd606f ("Create cmobase-v1.0.pdf") of riscv-CMOs. + - const: ziccamoa description: The standard Ziccamoa extension for main memory (cacheability and @@ -514,6 +537,66 @@ properties: guarantee on LR/SC sequences, as ratified in commit b1d806605f87 ("Updated to ratified state.") of the riscv profiles specification. + - const: zicfilp + description: | + The standard Zicfilp extension for enforcing forward edge + control-flow integrity as ratified in commit 3f8e450 ("merge + pull request #227 from ved-rivos/0709") of riscv-cfi + github repo. + + - const: zicfiss + description: | + The standard Zicfiss extension for enforcing backward edge + control-flow integrity as ratified in commit 3f8e450 ("merge + pull request #227 from ved-rivos/0709") of riscv-cfi + github repo. + + - const: zicntr + description: + The standard Zicntr extension for base counters and timers, as + ratified in the 20191213 version of the unprivileged ISA + specification. + + - const: zicond + description: + The standard Zicond extension for conditional arithmetic and + conditional-select/move operations as ratified in commit 95cf1f9 + ("Add changes requested by Ved during signoff") of riscv-zicond. + + - const: zicsr + description: | + The standard Zicsr extension for control and status register + instructions, as ratified in the 20191213 version of the + unprivileged ISA specification. + + This does not include Chapter 10, "Counters", which documents + special case read-only CSRs, that were moved into the Zicntr and + Zihpm extensions after the ratification of the 20191213 version of + the unprivileged specification. + + - const: zifencei + description: + The standard Zifencei extension for instruction-fetch fence, as + ratified in the 20191213 version of the unprivileged ISA + specification. + + - const: zihintntl + description: + The standard Zihintntl extension for non-temporal locality hints, as + ratified in commit 0dc91f5 ("Zihintntl is ratified") of the + riscv-isa-manual. + + - const: zihintpause + description: + The standard Zihintpause extension for pause hints, as ratified in + commit d8ab5c7 ("Zihintpause is ratified") of the riscv-isa-manual. + + - const: zihpm + description: + The standard Zihpm extension for hardware performance counters, as + ratified in the 20191213 version of the unprivileged ISA + specification. + - const: zilsd description: The standard Zilsd extension which provides support for aligned @@ -521,12 +604,10 @@ properties: encodings, as ratified in commit f88abf1 ("Integrating load/store pair for RV32 with the main manual") of riscv-isa-manual. - - const: zclsd + - const: zimop description: - The Zclsd extension implements the compressed (16-bit) version of the - Load/Store Pair for RV32. As with Zilsd, this extension was ratified - in commit f88abf1 ("Integrating load/store pair for RV32 with the - main manual") of riscv-isa-manual. + The standard Zimop extension version 1.0, as ratified in commit + 58220614a5f ("Zimop is ratified/1.0") of the riscv-isa-manual. - const: zk description: @@ -590,87 +671,6 @@ properties: in version 1.0 of RISC-V Cryptography Extensions Volume I specification. - - const: zicbom - description: - The standard Zicbom extension for base cache management operations as - ratified in commit 3dd606f ("Create cmobase-v1.0.pdf") of riscv-CMOs. - - - const: zicbop - description: - The standard Zicbop extension for cache-block prefetch instructions - as ratified in commit 3dd606f ("Create cmobase-v1.0.pdf") of - riscv-CMOs. - - - const: zicboz - description: - The standard Zicboz extension for cache-block zeroing as ratified - in commit 3dd606f ("Create cmobase-v1.0.pdf") of riscv-CMOs. - - - const: zicfilp - description: | - The standard Zicfilp extension for enforcing forward edge - control-flow integrity as ratified in commit 3f8e450 ("merge - pull request #227 from ved-rivos/0709") of riscv-cfi - github repo. - - - const: zicfiss - description: | - The standard Zicfiss extension for enforcing backward edge - control-flow integrity as ratified in commit 3f8e450 ("merge - pull request #227 from ved-rivos/0709") of riscv-cfi - github repo. - - - const: zicntr - description: - The standard Zicntr extension for base counters and timers, as - ratified in the 20191213 version of the unprivileged ISA - specification. - - - const: zicond - description: - The standard Zicond extension for conditional arithmetic and - conditional-select/move operations as ratified in commit 95cf1f9 - ("Add changes requested by Ved during signoff") of riscv-zicond. - - - const: zicsr - description: | - The standard Zicsr extension for control and status register - instructions, as ratified in the 20191213 version of the - unprivileged ISA specification. - - This does not include Chapter 10, "Counters", which documents - special case read-only CSRs, that were moved into the Zicntr and - Zihpm extensions after the ratification of the 20191213 version of - the unprivileged specification. - - - const: zifencei - description: - The standard Zifencei extension for instruction-fetch fence, as - ratified in the 20191213 version of the unprivileged ISA - specification. - - - const: zihintpause - description: - The standard Zihintpause extension for pause hints, as ratified in - commit d8ab5c7 ("Zihintpause is ratified") of the riscv-isa-manual. - - - const: zihintntl - description: - The standard Zihintntl extension for non-temporal locality hints, as - ratified in commit 0dc91f5 ("Zihintntl is ratified") of the - riscv-isa-manual. - - - const: zihpm - description: - The standard Zihpm extension for hardware performance counters, as - ratified in the 20191213 version of the unprivileged ISA - specification. - - - const: zimop - description: - The standard Zimop extension version 1.0, as ratified in commit - 58220614a5f ("Zimop is ratified/1.0") of the riscv-isa-manual. - - const: ztso description: The standard Ztso extension for total store ordering, as ratified @@ -809,18 +809,18 @@ properties: instructions, as ratified in commit 56ed795 ("Update riscv-crypto-spec-vector.adoc") of riscv-crypto. - - const: zvksh - description: | - The standard Zvksh extension for ShangMi suite: SM3 secure hash - instructions, as ratified in commit 56ed795 ("Update - riscv-crypto-spec-vector.adoc") of riscv-crypto. - - const: zvksg description: The standard Zvksg extension for ShangMi algorithm suite with GCM instructions, as ratified in commit 56ed795 ("Update riscv-crypto-spec-vector.adoc") of riscv-crypto. + - const: zvksh + description: | + The standard Zvksh extension for ShangMi suite: SM3 secure hash + instructions, as ratified in commit 56ed795 ("Update + riscv-crypto-spec-vector.adoc") of riscv-crypto. + - const: zvkt description: The standard Zvkt extension for vector data-independent execution From dbff3646369cb9add901b6c1b66d44cfb4a91ac5 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 1 Jul 2026 08:52:15 -0400 Subject: [PATCH 0717/1054] riscv: hwprobe.rst: Make indentation consistent A handful of vendor-extension entries indent continuation lines with a tab character, while the rest of hwprobe.rst uses spaces. In addition, many list items align their continuation lines under the 'm' of ':c:macro:' (column 7) rather than under the item text (column 4), so the file mixes several indentation styles. Replace the tabs with spaces and align every list item's continuation lines under the item text, giving the whole file one consistent style. Whitespace-only change, no functional change. [Guodong: extend from tabs->spaces to normalizing all continuation-line indentation across the file] Signed-off-by: Andrew Jones Signed-off-by: Guodong Xu Link: https://patch.msgid.link/20260701-rva23u64-hwprobe-v2-v5-2-2c61f94a695a@gmail.com Signed-off-by: Paul Walmsley --- Documentation/arch/riscv/hwprobe.rst | 194 +++++++++++++-------------- 1 file changed, 97 insertions(+), 97 deletions(-) diff --git a/Documentation/arch/riscv/hwprobe.rst b/Documentation/arch/riscv/hwprobe.rst index c420a8349bc6..a09a8f16bd16 100644 --- a/Documentation/arch/riscv/hwprobe.rst +++ b/Documentation/arch/riscv/hwprobe.rst @@ -82,121 +82,121 @@ The following keys are defined: version 1.0 of the RISC-V Vector extension manual. * :c:macro:`RISCV_HWPROBE_EXT_ZBA`: The Zba address generation extension is - supported, as defined in version 1.0 of the Bit-Manipulation ISA - extensions. + supported, as defined in version 1.0 of the Bit-Manipulation ISA + extensions. * :c:macro:`RISCV_HWPROBE_EXT_ZBB`: The Zbb extension is supported, as defined - in version 1.0 of the Bit-Manipulation ISA extensions. + in version 1.0 of the Bit-Manipulation ISA extensions. * :c:macro:`RISCV_HWPROBE_EXT_ZBS`: The Zbs extension is supported, as defined - in version 1.0 of the Bit-Manipulation ISA extensions. + in version 1.0 of the Bit-Manipulation ISA extensions. * :c:macro:`RISCV_HWPROBE_EXT_ZICBOZ`: The Zicboz extension is supported, as - ratified in commit 3dd606f ("Create cmobase-v1.0.pdf") of riscv-CMOs. + ratified in commit 3dd606f ("Create cmobase-v1.0.pdf") of riscv-CMOs. * :c:macro:`RISCV_HWPROBE_EXT_ZBC` The Zbc extension is supported, as defined - in version 1.0 of the Bit-Manipulation ISA extensions. + in version 1.0 of the Bit-Manipulation ISA extensions. * :c:macro:`RISCV_HWPROBE_EXT_ZBKB` The Zbkb extension is supported, as - defined in version 1.0 of the Scalar Crypto ISA extensions. + defined in version 1.0 of the Scalar Crypto ISA extensions. * :c:macro:`RISCV_HWPROBE_EXT_ZBKC` The Zbkc extension is supported, as - defined in version 1.0 of the Scalar Crypto ISA extensions. + defined in version 1.0 of the Scalar Crypto ISA extensions. * :c:macro:`RISCV_HWPROBE_EXT_ZBKX` The Zbkx extension is supported, as - defined in version 1.0 of the Scalar Crypto ISA extensions. + defined in version 1.0 of the Scalar Crypto ISA extensions. * :c:macro:`RISCV_HWPROBE_EXT_ZKND` The Zknd extension is supported, as - defined in version 1.0 of the Scalar Crypto ISA extensions. + defined in version 1.0 of the Scalar Crypto ISA extensions. * :c:macro:`RISCV_HWPROBE_EXT_ZKNE` The Zkne extension is supported, as - defined in version 1.0 of the Scalar Crypto ISA extensions. + defined in version 1.0 of the Scalar Crypto ISA extensions. * :c:macro:`RISCV_HWPROBE_EXT_ZKNH` The Zknh extension is supported, as - defined in version 1.0 of the Scalar Crypto ISA extensions. + defined in version 1.0 of the Scalar Crypto ISA extensions. * :c:macro:`RISCV_HWPROBE_EXT_ZKSED` The Zksed extension is supported, as - defined in version 1.0 of the Scalar Crypto ISA extensions. + defined in version 1.0 of the Scalar Crypto ISA extensions. * :c:macro:`RISCV_HWPROBE_EXT_ZKSH` The Zksh extension is supported, as - defined in version 1.0 of the Scalar Crypto ISA extensions. + defined in version 1.0 of the Scalar Crypto ISA extensions. * :c:macro:`RISCV_HWPROBE_EXT_ZKT` The Zkt extension is supported, as defined - in version 1.0 of the Scalar Crypto ISA extensions. + in version 1.0 of the Scalar Crypto ISA extensions. * :c:macro:`RISCV_HWPROBE_EXT_ZVBB`: The Zvbb extension is supported as - defined in version 1.0 of the RISC-V Cryptography Extensions Volume II. + defined in version 1.0 of the RISC-V Cryptography Extensions Volume II. * :c:macro:`RISCV_HWPROBE_EXT_ZVBC`: The Zvbc extension is supported as - defined in version 1.0 of the RISC-V Cryptography Extensions Volume II. + defined in version 1.0 of the RISC-V Cryptography Extensions Volume II. * :c:macro:`RISCV_HWPROBE_EXT_ZVKB`: The Zvkb extension is supported as - defined in version 1.0 of the RISC-V Cryptography Extensions Volume II. + defined in version 1.0 of the RISC-V Cryptography Extensions Volume II. * :c:macro:`RISCV_HWPROBE_EXT_ZVKG`: The Zvkg extension is supported as - defined in version 1.0 of the RISC-V Cryptography Extensions Volume II. + defined in version 1.0 of the RISC-V Cryptography Extensions Volume II. * :c:macro:`RISCV_HWPROBE_EXT_ZVKNED`: The Zvkned extension is supported as - defined in version 1.0 of the RISC-V Cryptography Extensions Volume II. + defined in version 1.0 of the RISC-V Cryptography Extensions Volume II. * :c:macro:`RISCV_HWPROBE_EXT_ZVKNHA`: The Zvknha extension is supported as - defined in version 1.0 of the RISC-V Cryptography Extensions Volume II. + defined in version 1.0 of the RISC-V Cryptography Extensions Volume II. * :c:macro:`RISCV_HWPROBE_EXT_ZVKNHB`: The Zvknhb extension is supported as - defined in version 1.0 of the RISC-V Cryptography Extensions Volume II. + defined in version 1.0 of the RISC-V Cryptography Extensions Volume II. * :c:macro:`RISCV_HWPROBE_EXT_ZVKSED`: The Zvksed extension is supported as - defined in version 1.0 of the RISC-V Cryptography Extensions Volume II. + defined in version 1.0 of the RISC-V Cryptography Extensions Volume II. * :c:macro:`RISCV_HWPROBE_EXT_ZVKSH`: The Zvksh extension is supported as - defined in version 1.0 of the RISC-V Cryptography Extensions Volume II. + defined in version 1.0 of the RISC-V Cryptography Extensions Volume II. * :c:macro:`RISCV_HWPROBE_EXT_ZVKT`: The Zvkt extension is supported as - defined in version 1.0 of the RISC-V Cryptography Extensions Volume II. + defined in version 1.0 of the RISC-V Cryptography Extensions Volume II. * :c:macro:`RISCV_HWPROBE_EXT_ZFH`: The Zfh extension version 1.0 is supported - as defined in the RISC-V ISA manual. + as defined in the RISC-V ISA manual. * :c:macro:`RISCV_HWPROBE_EXT_ZFHMIN`: The Zfhmin extension version 1.0 is - supported as defined in the RISC-V ISA manual. + supported as defined in the RISC-V ISA manual. * :c:macro:`RISCV_HWPROBE_EXT_ZIHINTNTL`: The Zihintntl extension version 1.0 - is supported as defined in the RISC-V ISA manual. + is supported as defined in the RISC-V ISA manual. * :c:macro:`RISCV_HWPROBE_EXT_ZVFH`: The Zvfh extension is supported as - defined in the RISC-V Vector manual starting from commit e2ccd0548d6c - ("Remove draft warnings from Zvfh[min]"). + defined in the RISC-V Vector manual starting from commit e2ccd0548d6c + ("Remove draft warnings from Zvfh[min]"). * :c:macro:`RISCV_HWPROBE_EXT_ZVFHMIN`: The Zvfhmin extension is supported as - defined in the RISC-V Vector manual starting from commit e2ccd0548d6c - ("Remove draft warnings from Zvfh[min]"). + defined in the RISC-V Vector manual starting from commit e2ccd0548d6c + ("Remove draft warnings from Zvfh[min]"). * :c:macro:`RISCV_HWPROBE_EXT_ZFA`: The Zfa extension is supported as - defined in the RISC-V ISA manual starting from commit 056b6ff467c7 - ("Zfa is ratified"). + defined in the RISC-V ISA manual starting from commit 056b6ff467c7 + ("Zfa is ratified"). * :c:macro:`RISCV_HWPROBE_EXT_ZTSO`: The Ztso extension is supported as - defined in the RISC-V ISA manual starting from commit 5618fb5a216b - ("Ztso is now ratified.") + defined in the RISC-V ISA manual starting from commit 5618fb5a216b + ("Ztso is now ratified.") * :c:macro:`RISCV_HWPROBE_EXT_ZACAS`: The Zacas extension is supported as - defined in the Atomic Compare-and-Swap (CAS) instructions manual starting - from commit 5059e0ca641c ("update to ratified"). + defined in the Atomic Compare-and-Swap (CAS) instructions manual starting + from commit 5059e0ca641c ("update to ratified"). * :c:macro:`RISCV_HWPROBE_EXT_ZICNTR`: The Zicntr extension version 2.0 - is supported as defined in the RISC-V ISA manual. + is supported as defined in the RISC-V ISA manual. * :c:macro:`RISCV_HWPROBE_EXT_ZICOND`: The Zicond extension is supported as - defined in the RISC-V Integer Conditional (Zicond) operations extension - manual starting from commit 95cf1f9 ("Add changes requested by Ved - during signoff") + defined in the RISC-V Integer Conditional (Zicond) operations extension + manual starting from commit 95cf1f9 ("Add changes requested by Ved + during signoff") * :c:macro:`RISCV_HWPROBE_EXT_ZIHINTPAUSE`: The Zihintpause extension is - supported as defined in the RISC-V ISA manual starting from commit - d8ab5c78c207 ("Zihintpause is ratified"). + supported as defined in the RISC-V ISA manual starting from commit + d8ab5c78c207 ("Zihintpause is ratified"). * :c:macro:`RISCV_HWPROBE_EXT_ZIHPM`: The Zihpm extension version 2.0 - is supported as defined in the RISC-V ISA manual. + is supported as defined in the RISC-V ISA manual. * :c:macro:`RISCV_HWPROBE_EXT_ZVE32X`: The Vector sub-extension Zve32x is supported, as defined by version 1.0 of the RISC-V Vector extension manual. @@ -214,84 +214,84 @@ The following keys are defined: supported, as defined by version 1.0 of the RISC-V Vector extension manual. * :c:macro:`RISCV_HWPROBE_EXT_ZIMOP`: The Zimop May-Be-Operations extension is - supported as defined in the RISC-V ISA manual starting from commit - 58220614a5f ("Zimop is ratified/1.0"). + supported as defined in the RISC-V ISA manual starting from commit + 58220614a5f ("Zimop is ratified/1.0"). * :c:macro:`RISCV_HWPROBE_EXT_ZCA`: The Zca extension part of Zc* standard - extensions for code size reduction, as ratified in commit 8be3419c1c0 - ("Zcf doesn't exist on RV64 as it contains no instructions") of - riscv-code-size-reduction. + extensions for code size reduction, as ratified in commit 8be3419c1c0 + ("Zcf doesn't exist on RV64 as it contains no instructions") of + riscv-code-size-reduction. * :c:macro:`RISCV_HWPROBE_EXT_ZCB`: The Zcb extension part of Zc* standard - extensions for code size reduction, as ratified in commit 8be3419c1c0 - ("Zcf doesn't exist on RV64 as it contains no instructions") of - riscv-code-size-reduction. + extensions for code size reduction, as ratified in commit 8be3419c1c0 + ("Zcf doesn't exist on RV64 as it contains no instructions") of + riscv-code-size-reduction. * :c:macro:`RISCV_HWPROBE_EXT_ZCD`: The Zcd extension part of Zc* standard - extensions for code size reduction, as ratified in commit 8be3419c1c0 - ("Zcf doesn't exist on RV64 as it contains no instructions") of - riscv-code-size-reduction. + extensions for code size reduction, as ratified in commit 8be3419c1c0 + ("Zcf doesn't exist on RV64 as it contains no instructions") of + riscv-code-size-reduction. * :c:macro:`RISCV_HWPROBE_EXT_ZCF`: The Zcf extension part of Zc* standard - extensions for code size reduction, as ratified in commit 8be3419c1c0 - ("Zcf doesn't exist on RV64 as it contains no instructions") of - riscv-code-size-reduction. + extensions for code size reduction, as ratified in commit 8be3419c1c0 + ("Zcf doesn't exist on RV64 as it contains no instructions") of + riscv-code-size-reduction. * :c:macro:`RISCV_HWPROBE_EXT_ZCMOP`: The Zcmop May-Be-Operations extension is - supported as defined in the RISC-V ISA manual starting from commit - c732a4f39a4 ("Zcmop is ratified/1.0"). + supported as defined in the RISC-V ISA manual starting from commit + c732a4f39a4 ("Zcmop is ratified/1.0"). * :c:macro:`RISCV_HWPROBE_EXT_ZAWRS`: The Zawrs extension is supported as - ratified in commit 98918c844281 ("Merge pull request #1217 from - riscv/zawrs") of riscv-isa-manual. + ratified in commit 98918c844281 ("Merge pull request #1217 from + riscv/zawrs") of riscv-isa-manual. * :c:macro:`RISCV_HWPROBE_EXT_ZAAMO`: The Zaamo extension is supported as - defined in the in the RISC-V ISA manual starting from commit e87412e621f1 - ("integrate Zaamo and Zalrsc text (#1304)"). + defined in the in the RISC-V ISA manual starting from commit e87412e621f1 + ("integrate Zaamo and Zalrsc text (#1304)"). * :c:macro:`RISCV_HWPROBE_EXT_ZALASR`: The Zalasr extension is supported as - frozen at commit 194f0094 ("Version 0.9 for freeze") of riscv-zalasr. + frozen at commit 194f0094 ("Version 0.9 for freeze") of riscv-zalasr. * :c:macro:`RISCV_HWPROBE_EXT_ZALRSC`: The Zalrsc extension is supported as - defined in the in the RISC-V ISA manual starting from commit e87412e621f1 - ("integrate Zaamo and Zalrsc text (#1304)"). + defined in the in the RISC-V ISA manual starting from commit e87412e621f1 + ("integrate Zaamo and Zalrsc text (#1304)"). * :c:macro:`RISCV_HWPROBE_EXT_SUPM`: The Supm extension is supported as - defined in version 1.0 of the RISC-V Pointer Masking extensions. + defined in version 1.0 of the RISC-V Pointer Masking extensions. * :c:macro:`RISCV_HWPROBE_EXT_ZFBFMIN`: The Zfbfmin extension is supported as - defined in the RISC-V ISA manual starting from commit 4dc23d6229de - ("Added Chapter title to BF16"). + defined in the RISC-V ISA manual starting from commit 4dc23d6229de + ("Added Chapter title to BF16"). * :c:macro:`RISCV_HWPROBE_EXT_ZVFBFMIN`: The Zvfbfmin extension is supported as - defined in the RISC-V ISA manual starting from commit 4dc23d6229de - ("Added Chapter title to BF16"). + defined in the RISC-V ISA manual starting from commit 4dc23d6229de + ("Added Chapter title to BF16"). * :c:macro:`RISCV_HWPROBE_EXT_ZVFBFWMA`: The Zvfbfwma extension is supported as - defined in the RISC-V ISA manual starting from commit 4dc23d6229de - ("Added Chapter title to BF16"). + defined in the RISC-V ISA manual starting from commit 4dc23d6229de + ("Added Chapter title to BF16"). * :c:macro:`RISCV_HWPROBE_EXT_ZICBOM`: The Zicbom extension is supported, as - ratified in commit 3dd606f ("Create cmobase-v1.0.pdf") of riscv-CMOs. + ratified in commit 3dd606f ("Create cmobase-v1.0.pdf") of riscv-CMOs. * :c:macro:`RISCV_HWPROBE_EXT_ZABHA`: The Zabha extension is supported as - ratified in commit 49f49c842ff9 ("Update to Rafified state") of - riscv-zabha. + ratified in commit 49f49c842ff9 ("Update to Rafified state") of + riscv-zabha. * :c:macro:`RISCV_HWPROBE_EXT_ZICBOP`: The Zicbop extension is supported, as - ratified in commit 3dd606f ("Create cmobase-v1.0.pdf") of riscv-CMOs. + ratified in commit 3dd606f ("Create cmobase-v1.0.pdf") of riscv-CMOs. * :c:macro:`RISCV_HWPROBE_EXT_ZILSD`: The Zilsd extension is supported as - defined in the RISC-V ISA manual starting from commit f88abf1 ("Integrating - load/store pair for RV32 with the main manual") of the riscv-isa-manual. + defined in the RISC-V ISA manual starting from commit f88abf1 ("Integrating + load/store pair for RV32 with the main manual") of the riscv-isa-manual. * :c:macro:`RISCV_HWPROBE_EXT_ZCLSD`: The Zclsd extension is supported as - defined in the RISC-V ISA manual starting from commit f88abf1 ("Integrating - load/store pair for RV32 with the main manual") of the riscv-isa-manual. + defined in the RISC-V ISA manual starting from commit f88abf1 ("Integrating + load/store pair for RV32 with the main manual") of the riscv-isa-manual. * :c:macro:`RISCV_HWPROBE_KEY_CPUPERF_0`: Deprecated. Returns similar values to - :c:macro:`RISCV_HWPROBE_KEY_MISALIGNED_SCALAR_PERF`, but the key was - mistakenly classified as a bitmask rather than a value. + :c:macro:`RISCV_HWPROBE_KEY_MISALIGNED_SCALAR_PERF`, but the key was + mistakenly classified as a bitmask rather than a value. * :c:macro:`RISCV_HWPROBE_KEY_MISALIGNED_SCALAR_PERF`: An enum value describing the performance of misaligned scalar native word accesses on the selected set @@ -326,7 +326,7 @@ The following keys are defined: * :c:macro:`RISCV_HWPROBE_KEY_TIME_CSR_FREQ`: Frequency (in Hz) of `time CSR`. * :c:macro:`RISCV_HWPROBE_KEY_MISALIGNED_VECTOR_PERF`: An enum value describing the - performance of misaligned vector accesses on the selected set of processors. + performance of misaligned vector accesses on the selected set of processors. * :c:macro:`RISCV_HWPROBE_MISALIGNED_VECTOR_UNKNOWN`: The performance of misaligned vector accesses is unknown. @@ -348,7 +348,7 @@ The following keys are defined: * MIPS * :c:macro:`RISCV_HWPROBE_VENDOR_EXT_XMIPSEXECTL`: The xmipsexectl vendor - extension is supported in the MIPS ISA extensions spec. + extension is supported in the MIPS ISA extensions spec. * :c:macro:`RISCV_HWPROBE_KEY_VENDOR_EXT_THEAD_0`: A bitmask containing the thead vendor extensions that are compatible with the @@ -357,8 +357,8 @@ The following keys are defined: * T-HEAD * :c:macro:`RISCV_HWPROBE_VENDOR_EXT_XTHEADVECTOR`: The xtheadvector vendor - extension is supported in the T-Head ISA extensions spec starting from - commit a18c801634 ("Add T-Head VECTOR vendor extension. "). + extension is supported in the T-Head ISA extensions spec starting from + commit a18c801634 ("Add T-Head VECTOR vendor extension. "). * :c:macro:`RISCV_HWPROBE_KEY_ZICBOM_BLOCK_SIZE`: An unsigned int which represents the size of the Zicbom block in bytes. @@ -370,20 +370,20 @@ The following keys are defined: * SIFIVE * :c:macro:`RISCV_HWPROBE_VENDOR_EXT_XSFVQMACCDOD`: The Xsfqmaccdod vendor - extension is supported in version 1.1 of SiFive Int8 Matrix Multiplication - Extensions Specification. + extension is supported in version 1.1 of SiFive Int8 Matrix Multiplication + Extensions Specification. * :c:macro:`RISCV_HWPROBE_VENDOR_EXT_XSFVQMACCQOQ`: The Xsfqmaccqoq vendor - extension is supported in version 1.1 of SiFive Int8 Matrix Multiplication - Instruction Extensions Specification. + extension is supported in version 1.1 of SiFive Int8 Matrix Multiplication + Instruction Extensions Specification. * :c:macro:`RISCV_HWPROBE_VENDOR_EXT_XSFVFNRCLIPXFQF`: The Xsfvfnrclipxfqf - vendor extension is supported in version 1.0 of SiFive FP32-to-int8 Ranged - Clip Instructions Extensions Specification. + vendor extension is supported in version 1.0 of SiFive FP32-to-int8 Ranged + Clip Instructions Extensions Specification. * :c:macro:`RISCV_HWPROBE_VENDOR_EXT_XSFVFWMACCQQQ`: The Xsfvfwmaccqqq - vendor extension is supported in version 1.0 of Matrix Multiply Accumulate - Instruction Extensions Specification. + vendor extension is supported in version 1.0 of Matrix Multiply Accumulate + Instruction Extensions Specification. * :c:macro:`RISCV_HWPROBE_KEY_ZICBOP_BLOCK_SIZE`: An unsigned int which represents the size of the Zicbop block in bytes. From a914034334c48a85303a716b106a9a3167439876 Mon Sep 17 00:00:00 2001 From: Guodong Xu Date: Wed, 1 Jul 2026 08:52:16 -0400 Subject: [PATCH 0718/1054] riscv: hwprobe.rst: Document EXT_ZICFISS and EXT_ZICFILP RISCV_HWPROBE_EXT_ZICFISS and RISCV_HWPROBE_EXT_ZICFILP are defined in the hwprobe uAPI but are not documented in Documentation/arch/riscv/hwprobe.rst. Add documentation for them. Link: https://github.com/riscv/riscv-cfi/commit/302a2d45c2435940d9a63571c66bc038adc74133 Reviewed-by: Andrew Jones Signed-off-by: Guodong Xu Link: https://patch.msgid.link/20260701-rva23u64-hwprobe-v2-v5-3-2c61f94a695a@gmail.com Signed-off-by: Paul Walmsley --- Documentation/arch/riscv/hwprobe.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Documentation/arch/riscv/hwprobe.rst b/Documentation/arch/riscv/hwprobe.rst index a09a8f16bd16..d9928641deb9 100644 --- a/Documentation/arch/riscv/hwprobe.rst +++ b/Documentation/arch/riscv/hwprobe.rst @@ -289,6 +289,11 @@ The following keys are defined: defined in the RISC-V ISA manual starting from commit f88abf1 ("Integrating load/store pair for RV32 with the main manual") of the riscv-isa-manual. + * :c:macro:`RISCV_HWPROBE_EXT_ZICFILP`: The Zicfilp extension is supported, + as defined in version 1.0 of the RISC-V Control-flow Integrity (CFI) + extensions specification, ratified in commit 302a2d45c243 + ("Update build-pdf.yml") of riscv-cfi. + * :c:macro:`RISCV_HWPROBE_KEY_CPUPERF_0`: Deprecated. Returns similar values to :c:macro:`RISCV_HWPROBE_KEY_MISALIGNED_SCALAR_PERF`, but the key was mistakenly classified as a bitmask rather than a value. @@ -391,3 +396,8 @@ The following keys are defined: * :c:macro:`RISCV_HWPROBE_KEY_IMA_EXT_1`: A bitmask containing additional extensions that are compatible with the :c:macro:`RISCV_HWPROBE_BASE_BEHAVIOR_IMA`: base system behavior. + + * :c:macro:`RISCV_HWPROBE_EXT_ZICFISS`: The Zicfiss extension is supported, + as defined in version 1.0 of the RISC-V Control-flow Integrity (CFI) + extensions specification, ratified in commit 302a2d45c243 + ("Update build-pdf.yml") of riscv-cfi. From 4ad805f0e41acf56ac54855c666d34c59ad66fe7 Mon Sep 17 00:00:00 2001 From: Cihan Karadag Date: Tue, 7 Jul 2026 17:57:05 -0600 Subject: [PATCH 0719/1054] selftests: gpio: add gpio-cdev-uaf to .gitignore Commit c7f92042d3f3 ("selftests: gpio: Add gpio-cdev-uaf tests") added the gpio-cdev-uaf binary to TEST_GEN_PROGS_EXTENDED but never added it to .gitignore. Building it with: make -C tools/testing/selftests/gpio TARGETS=gpio leaves gpio-cdev-uaf as an untracked file. Fixes: c7f92042d3f3 ("selftests: gpio: Add gpio-cdev-uaf tests") Signed-off-by: Cihan Karadag Reviewed-by: Tzung-Bi Shih Link: https://patch.msgid.link/20260707235707.1349969-1-cihan.cihan@gmail.com Signed-off-by: Bartosz Golaszewski --- tools/testing/selftests/gpio/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/testing/selftests/gpio/.gitignore b/tools/testing/selftests/gpio/.gitignore index ededb077a3a6..16f74de479f1 100644 --- a/tools/testing/selftests/gpio/.gitignore +++ b/tools/testing/selftests/gpio/.gitignore @@ -2,3 +2,4 @@ gpio-mockup-cdev gpio-chip-info gpio-line-name +gpio-cdev-uaf From 1a3267a8c9ecabb8e27f5cbda6d19295d5e41beb Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Wed, 1 Jul 2026 20:26:52 -0700 Subject: [PATCH 0720/1054] net: mdio: select REGMAP_MMIO instead of depending on it REGMAP_MMIO is a hidden (non-user-visible) tristate symbol. Using depends on it is incorrect because there is no way for the user to enable it directly. Change to select, which is the convention used by every other driver in the tree that needs REGMAP_MMIO. Fixes: 8057cbb8335c ("net: mdio: mscc-miim: Add depend of REGMAP_MMIO on MDIO_MSCC_MIIM") Assisted-by: opencode:big-pickle Signed-off-by: Rosen Penev Reviewed-by: Andrew Lunn Link: https://patch.msgid.link/20260702032653.1580616-1-rosenp@gmail.com Signed-off-by: Paolo Abeni --- drivers/net/mdio/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/mdio/Kconfig b/drivers/net/mdio/Kconfig index c591eec8e97a..e57121019153 100644 --- a/drivers/net/mdio/Kconfig +++ b/drivers/net/mdio/Kconfig @@ -122,7 +122,8 @@ config MDIO_MVUSB config MDIO_MSCC_MIIM tristate "Microsemi MIIM interface support" - depends on HAS_IOMEM && REGMAP_MMIO + depends on HAS_IOMEM + select REGMAP_MMIO help This driver supports the MIIM (MDIO) interface found in the network switches of the Microsemi SoCs; it is recommended to switch on From cd9993d22a577339a93dcb401574e874ea29b143 Mon Sep 17 00:00:00 2001 From: Bastian Blank Date: Sun, 5 Jul 2026 12:49:17 +0200 Subject: [PATCH 0721/1054] rtla: Also link in ctype.c rtla started to only link parts of the tools library. It now misses the ctype information used by all the related string operations. Just add another single file to make it build again. Signed-off-by: Bastian Blank Fixes: 48209d763c22 ("rtla: Add libsubcmd dependency") Link: https://lore.kernel.org/r/ako2S4mzIqWwYuas@steamhammer.waldi.eu.org [ remove duplicated spaces in commit message ] Signed-off-by: Tomas Glozar --- tools/tracing/rtla/Makefile | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/tools/tracing/rtla/Makefile b/tools/tracing/rtla/Makefile index 60a102538988..387bc6cc18f0 100644 --- a/tools/tracing/rtla/Makefile +++ b/tools/tracing/rtla/Makefile @@ -45,6 +45,9 @@ else LIB_OUTPUT = $(CURDIR)/lib endif +LIB_CTYPE = $(LIB_OUTPUT)/ctype.o +LIB_CTYPE_SRC = $(srctree)/tools/lib/ctype.c + LIB_STRING = $(LIB_OUTPUT)/string.o LIB_STRING_SRC = $(srctree)/tools/lib/string.c @@ -117,12 +120,12 @@ tests/bpf/bpf_action_map.o: tests/bpf/bpf_action_map.c $(Q)echo "BPF skeleton support is disabled, skipping tests/bpf/bpf_action_map.o" endif -$(RTLA): $(RTLA_IN) $(LIBSUBCMD) $(LIB_STRING) $(LIB_STR_ERROR_R) - $(QUIET_LINK)$(CC) $(LDFLAGS) -o $(RTLA) $(RTLA_IN) $(LIBSUBCMD) $(LIB_STRING) $(LIB_STR_ERROR_R) $(EXTLIBS) +$(RTLA): $(RTLA_IN) $(LIBSUBCMD) $(LIB_CTYPE) $(LIB_STRING) $(LIB_STR_ERROR_R) + $(QUIET_LINK)$(CC) $(LDFLAGS) -o $(RTLA) $(RTLA_IN) $(LIBSUBCMD) $(LIB_CTYPE) $(LIB_STRING) $(LIB_STR_ERROR_R) $(EXTLIBS) -static: $(RTLA_IN) $(LIBSUBCMD) $(LIB_STRING) $(LIB_STR_ERROR_R) +static: $(RTLA_IN) $(LIBSUBCMD) $(LIB_CTYPE) $(LIB_STRING) $(LIB_STR_ERROR_R) $(eval LDFLAGS += -static) - $(QUIET_LINK)$(CC) -static $(LDFLAGS) -o $(RTLA)-static $(RTLA_IN) $(LIBSUBCMD) $(LIB_STRING) $(LIB_STR_ERROR_R) $(EXTLIBS) + $(QUIET_LINK)$(CC) -static $(LDFLAGS) -o $(RTLA)-static $(RTLA_IN) $(LIBSUBCMD) $(LIB_CTYPE) $(LIB_STRING) $(LIB_STR_ERROR_R) $(EXTLIBS) rtla.%: fixdep FORCE make -f $(srctree)/tools/build/Makefile.build dir=. $@ @@ -150,6 +153,9 @@ $(LIB_STR_ERROR_R): $(LIB_STR_ERROR_R_SRC) | $(LIB_OUTPUT) $(LIB_STRING): $(LIB_STRING_SRC) | $(LIB_OUTPUT) $(QUIET_CC)$(CC) $(CFLAGS) -c -o $@ $< +$(LIB_CTYPE): $(LIB_CTYPE_SRC) | $(LIB_OUTPUT) + $(QUIET_CC)$(CC) $(CFLAGS) -c -o $@ $< + libsubcmd-clean: $(call QUIET_CLEAN, libsubcmd) $(Q)$(RM) -r -- $(LIBSUBCMD_OUTPUT) From b7f97cae7ec1b6c3c32843c42be218690d310467 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Thu, 2 Jul 2026 00:07:59 +0000 Subject: [PATCH 0722/1054] net/sched: cake: reject overhead values that underflow length MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CAKE accepts signed overhead values and stores them in an s16, but the adjusted packet length calculation uses unsigned arithmetic. A negative effective length can therefore wrap to a large value. Such configurations make rate accounting depend on integer wraparound rather than on the packet size userspace intended to model. A static netlink lower bound is not enough because packets reaching CAKE can be smaller than any reasonable manual-overhead allowance. Fold the signed overhead adjustment into the existing datapath MPU clamp so negative adjusted lengths are clamped before link-layer framing adjustments. Fixes: a729b7f0bd5b ("sch_cake: Add overhead compensation support to the rate shaper") Assisted-by: Codex:gpt-5.5-cyber-preview Signed-off-by: Samuel Moelius Acked-by: Toke Høiland-Jørgensen Link: https://patch.msgid.link/20260702000758.297407.e5c888d9d99d.cake-overhead-underflow@trailofbits.com Signed-off-by: Paolo Abeni --- net/sched/sch_cake.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/net/sched/sch_cake.c b/net/sched/sch_cake.c index a3c185505afc..f78f8e950776 100644 --- a/net/sched/sch_cake.c +++ b/net/sched/sch_cake.c @@ -1389,10 +1389,7 @@ static u32 cake_calc_overhead(struct cake_sched_data *qd, u32 len, u32 off) if (qd->min_netlen > len) WRITE_ONCE(qd->min_netlen, len); - len += q->rate_overhead; - - if (len < q->rate_mpu) - len = q->rate_mpu; + len = max((s32)len + q->rate_overhead, (s32)q->rate_mpu); if (q->atm_mode == CAKE_ATM_ATM) { len += 47; From 235acadd310533ba386ae61ad155b72bee381559 Mon Sep 17 00:00:00 2001 From: Suman Ghosh Date: Thu, 2 Jul 2026 09:04:51 +0530 Subject: [PATCH 0723/1054] octeontx2-pf: check DMAC extraction support before filtering Currently, configuring a VF MAC address via the PF (e.g., 'ip link set vf 0 mac ') blindly attempts to install a DMAC-based hardware filter. However, the hardware parser profile might not support DMAC extraction. Check if the hardware parsing profile supports DMAC extraction before adding the filter. Additionally, emit a warning message to inform the operator if the MAC filter installation fails due to missing DMAC extraction support. Update config->mac only after hardware programming succeeds in otx2_set_vf_mac(). Fixes: f0c2982aaf98 ("octeontx2-pf: Add support for SR-IOV management functions") Signed-off-by: Suman Ghosh Signed-off-by: Nitin Shetty J Reviewed-by: Harshitha Ramamurthy Link: https://patch.msgid.link/20260702033451.2969880-1-nshettyj@marvell.com Signed-off-by: Paolo Abeni --- .../ethernet/marvell/octeontx2/nic/otx2_pf.c | 43 ++++++++++++++++--- 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c index 88ac85354445..2e33b33ec993 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c @@ -2516,10 +2516,42 @@ EXPORT_SYMBOL(otx2_config_hwtstamp_set); static int otx2_do_set_vf_mac(struct otx2_nic *pf, int vf, const u8 *mac) { + struct npc_get_field_status_req *freq; + struct npc_get_field_status_rsp *frsp; struct npc_install_flow_req *req; int err; mutex_lock(&pf->mbox.lock); + + /* Skip installing the DMAC filter if the hardware parser profile + * does not support DMAC extraction. + */ + freq = otx2_mbox_alloc_msg_npc_get_field_status(&pf->mbox); + if (!freq) { + err = -ENOMEM; + goto out; + } + + freq->field = NPC_DMAC; + err = otx2_sync_mbox_msg(&pf->mbox); + if (err) + goto out; + + frsp = (struct npc_get_field_status_rsp *)otx2_mbox_get_rsp + (&pf->mbox.mbox, 0, &freq->hdr); + if (IS_ERR(frsp)) { + err = PTR_ERR(frsp); + goto out; + } + + if (!frsp->enable) { + netdev_warn(pf->netdev, + "VF %d MAC filter not installed: DMAC extraction not supported by parser profile\n", + vf); + err = -EOPNOTSUPP; + goto out; + } + req = otx2_mbox_alloc_msg_npc_install_flow(&pf->mbox); if (!req) { err = -ENOMEM; @@ -2558,13 +2590,12 @@ static int otx2_set_vf_mac(struct net_device *netdev, int vf, u8 *mac) if (!is_valid_ether_addr(mac)) return -EINVAL; - config = &pf->vf_configs[vf]; - ether_addr_copy(config->mac, mac); - ret = otx2_do_set_vf_mac(pf, vf, mac); - if (ret == 0) - dev_info(&pdev->dev, - "Load/Reload VF driver\n"); + if (ret == 0) { + config = &pf->vf_configs[vf]; + ether_addr_copy(config->mac, mac); + dev_info(&pdev->dev, "Load/Reload VF driver\n"); + } return ret; } From 07c60dda9c059c09f83d42a3ebda2e7cc1cf3bc2 Mon Sep 17 00:00:00 2001 From: Sandipan Das Date: Wed, 8 Jul 2026 16:02:04 +0530 Subject: [PATCH 0724/1054] perf/x86/amd/core: Avoid enabling BRS from the SVM reload path Branch Sampling (BRS) and Last Branch Record (LBR) are mutually exclusive hardware features, and users of both are tracked via cpuc->lbr_users. When SVM is toggled on a CPU, the host perf events are reprogrammed to update the HostOnly filter bit (set when virtualization is enabled, cleared when it is disabled). On PerfMonV2-capable processors, this reprogramming is performed by calling amd_pmu_enable_all() to rewrite the event selectors. However, amd_pmu_enable_all() also calls amd_brs_enable_all(), which enables BRS whenever cpuc->lbr_users > 0. Having active LBR events satisfies this gating on processors that have LBR but not BRS. The kernel then tries to set the BRS enable bit in DebugExtnCfg (MSR 0xc000010f). Since that bit is deprecated on such hardware, the write results in a #GP: Call Trace: amd_pmu_enable_all+0x1d/0x90 amd_pmu_disable_virt+0x62/0xb0 kvm_arch_disable_virtualization_cpu+0xa/0x40 [kvm] hardware_disable_nolock+0x1a/0x30 [kvm] __flush_smp_call_function_queue+0x9b/0x410 __sysvec_call_function+0x18/0xc0 sysvec_call_function+0x69/0x90 asm_sysvec_call_function+0x16/0x20 RIP: 0010:cpuidle_enter_state+0xc4/0x450 ? cpuidle_enter_state+0xb7/0x450 cpuidle_enter+0x29/0x40 cpuidle_idle_call+0xf5/0x160 do_idle+0x7b/0xe0 cpu_startup_entry+0x26/0x30 start_secondary+0x115/0x140 secondary_startup_64_no_verify+0x194/0x19b Fix this by ensuring that BRS is not enabled from the event selector reprogramming path even when cpuc->lbr_users > 0. Fixes: bae19fdd7e9e ("perf/x86/amd/core: Fix reloading events for SVM") Signed-off-by: Sandipan Das Signed-off-by: Ingo Molnar Cc: Peter Zijlstra Link: https://patch.msgid.link/702fa204d574b03d14e3664c7d4b201db048bbfd.1783506528.git.sandipan.das@amd.com --- arch/x86/events/amd/core.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/arch/x86/events/amd/core.c b/arch/x86/events/amd/core.c index 6569048a8c1c..a787409f5a62 100644 --- a/arch/x86/events/amd/core.c +++ b/arch/x86/events/amd/core.c @@ -754,13 +754,11 @@ static void amd_pmu_enable_event(struct perf_event *event) x86_pmu_enable_event(event); } -static void amd_pmu_enable_all(int added) +static void __amd_pmu_enable_all(void) { struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); int idx; - amd_brs_enable_all(); - for_each_set_bit(idx, x86_pmu.cntr_mask, X86_PMC_IDX_MAX) { /* only activate events which are marked as active */ if (!test_bit(idx, cpuc->active_mask)) @@ -775,6 +773,12 @@ static void amd_pmu_enable_all(int added) } } +static void amd_pmu_enable_all(int added) +{ + amd_brs_enable_all(); + __amd_pmu_enable_all(); +} + static void amd_pmu_v2_enable_event(struct perf_event *event) { struct hw_perf_event *hwc = &event->hw; @@ -1561,7 +1565,7 @@ static inline void amd_pmu_reload_virt(void) * set global enable bits once again */ amd_pmu_v2_disable_all(); - amd_pmu_enable_all(0); + __amd_pmu_enable_all(); amd_pmu_v2_enable_all(0); return; } From 2d184128f45afad7bfdc17d946339765555dbd38 Mon Sep 17 00:00:00 2001 From: Agustin Luzardo Date: Sun, 5 Jul 2026 15:42:27 -0300 Subject: [PATCH 0725/1054] ALSA: usb-audio: Add quirk for Redragon H510-PRO Wireless headset The device with USB ID 040b:0897 (Weltrend Semiconductor chipset, sold rebranded as the Redragon H510-PRO Wireless headset, reporting "XiiSound Technology Corporation" in its USB string descriptors) reports a constant value on GET_CUR for its PCM Playback Volume control while still supporting an actually tunable volume. This trips the sticky-value detection in check_sticky_volume_control(), which disables the mixer control entirely: usb 1-4: 5:0: sticky mixer values (0/100/1 => 80), disabling As a result, the device boots with playback volume effectively muted and provides no way to raise it through the normal ALSA/PipeWire mixer path. Apply QUIRK_FLAG_MIXER_GET_CUR_BROKEN so the sticky check marks the control as get_cur_broken and relies on the cached value instead of disabling the mixer control outright. Tested by backporting this quirk flag and the supporting get_cur_broken logic onto a Linux 7.1.2-zen kernel build that does not yet carry it, and confirming that after applying the flag the kernel log changes from usb 1-4: 5:0: sticky mixer values (0/100/1 => 80), disabling to usb 1-4: 5:0: broken mixer GET_CUR (0/100/1 => 80) with the control usable via the driver's cached value afterward. Signed-off-by: Agustin Luzardo Link: https://patch.msgid.link/20260705184227.113588-1-agustinluzardo09@gmail.com Signed-off-by: Takashi Iwai --- sound/usb/quirks.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 1cb588691e16..2949a0d2d961 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -2210,6 +2210,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY), DEVICE_FLG(0x03f0, 0x654a, /* HP 320 FHD Webcam */ QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_MIC_RES_16), + DEVICE_FLG(0x040b, 0x0897, /* Weltrend Semiconductor, sold as Redragon H510-PRO Wireless headset */ + QUIRK_FLAG_MIXER_GET_CUR_BROKEN), DEVICE_FLG(0x041e, 0x3000, /* Creative SB Extigy */ QUIRK_FLAG_IGNORE_CTL_ERROR), DEVICE_FLG(0x041e, 0x4080, /* Creative Live Cam VF0610 */ From 5c3f4b583c25911fad9890b463632761291f65ab Mon Sep 17 00:00:00 2001 From: Rohit Sinha Date: Tue, 7 Jul 2026 02:48:34 +0530 Subject: [PATCH 0726/1054] ALSA: hda/realtek - Add quirk for HP Victus 15-fa0xxx (MB 8A50) The mute LED on HP Victus 15-fa0xxx (board ID 8A50) uses ALC245 but requires a quirk to work. This patch enables the existing ALC245_FIXUP_HP_MUTE_LED_COEFBIT quirk for the device. Tested on my HP Victus 15-fa0xxx. The LED behaviour works as intended. Signed-off-by: Rohit Sinha Link: https://patch.msgid.link/20260706211834.384022-1-rohitsinha12043@gmail.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 984868e3d8bb..5d9cbf5b7b40 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -7146,6 +7146,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8a36, "HP Pavilion Plus 14-eh0xxx", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), SND_PCI_QUIRK(0x103c, 0x8a3d, "HP Victus 15-fb0xxx (MB 8A3D)", ALC245_FIXUP_HP_MUTE_LED_V2_COEFBIT), SND_PCI_QUIRK(0x103c, 0x8a4f, "HP Victus 15-fa0xxx (MB 8A4F)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), + SND_PCI_QUIRK(0x103c, 0x8a50, "HP Victus 15-fa0xxx (MB 8A50)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), SND_PCI_QUIRK(0x103c, 0x8a6e, "HP EDNA 360", ALC287_FIXUP_CS35L41_I2C_4), SND_PCI_QUIRK(0x103c, 0x8a74, "HP ProBook 440 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8a75, "HP ProBook 450 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED), From 0752734163a20c9185a12c0d0786995751ab7426 Mon Sep 17 00:00:00 2001 From: Kshamendra Kumar Mishra Date: Tue, 7 Jul 2026 22:07:44 +0530 Subject: [PATCH 0727/1054] ALSA: hda/realtek: Add mic mute LED quirk for HP Laptop 15-fd0xxx The HP Laptop 15-fd0xxx uses GPIO bit 0 to control the mic mute LED, but currently the quirk only enables the speaker mute LED (via coef bit). Switch to the existing ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO fixup which additionally registers a GPIO-based mic mute LED, mapping to the same verbs (SET_GPIO_DATA/SET_GPIO_DIRECTION bit 0) that the platform firmware uses. Signed-off-by: Kshamendra Kumar Mishra Link: https://patch.msgid.link/DJSOWKFBTJH8.2Z7EPWX514QXM@gmail.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 5d9cbf5b7b40..02cc3f6b378d 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -7294,7 +7294,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8da8, "HP 16 Piston OmniBook X", ALC245_FIXUP_HP_ENVY_X360_15_FH0XXX), SND_PCI_QUIRK(0x103c, 0x8dc9, "HP Laptop 15-fc0xxx", ALC236_FIXUP_HP_DMIC), SND_PCI_QUIRK(0x103c, 0x8dd4, "HP EliteStudio 8 AIO", ALC274_FIXUP_HP_AIO_BIND_DACS), - SND_PCI_QUIRK(0x103c, 0x8dd7, "HP Laptop 15-fd0xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), + SND_PCI_QUIRK(0x103c, 0x8dd7, "HP Laptop 15-fd0xxx", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO), SND_PCI_QUIRK(0x103c, 0x8de8, "HP Gemtree", ALC245_FIXUP_TAS2781_SPI_2), SND_PCI_QUIRK(0x103c, 0x8de9, "HP Gemtree", ALC245_FIXUP_TAS2781_SPI_2), SND_PCI_QUIRK(0x103c, 0x8dec, "HP EliteBook 640 G12", ALC236_FIXUP_HP_GPIO_LED), From bc0d4444f384d183e2113b72c34643069f93ab63 Mon Sep 17 00:00:00 2001 From: Ravi Hothi Date: Fri, 3 Jul 2026 13:04:32 +0530 Subject: [PATCH 0728/1054] ASoC: dt-bindings: qcom: Add Eliza LPASS macro codecs Add bindings for Qualcomm Eliza LPASS RX, TX, VA and WSA macro codecs, which are compatible with the existing SM8550 generation. Signed-off-by: Ravi Hothi Link: https://patch.msgid.link/20260703073434.2589657-2-ravi.hothi@oss.qualcomm.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/qcom,lpass-rx-macro.yaml | 1 + Documentation/devicetree/bindings/sound/qcom,lpass-tx-macro.yaml | 1 + Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml | 1 + .../devicetree/bindings/sound/qcom,lpass-wsa-macro.yaml | 1 + 4 files changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/qcom,lpass-rx-macro.yaml b/Documentation/devicetree/bindings/sound/qcom,lpass-rx-macro.yaml index 2eed2277511f..4988e7ed6e34 100644 --- a/Documentation/devicetree/bindings/sound/qcom,lpass-rx-macro.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,lpass-rx-macro.yaml @@ -21,6 +21,7 @@ properties: - qcom,sc8280xp-lpass-rx-macro - items: - enum: + - qcom,eliza-lpass-rx-macro - qcom,kaanapali-lpass-rx-macro - qcom,sm8650-lpass-rx-macro - qcom,sm8750-lpass-rx-macro diff --git a/Documentation/devicetree/bindings/sound/qcom,lpass-tx-macro.yaml b/Documentation/devicetree/bindings/sound/qcom,lpass-tx-macro.yaml index e5e65e226a02..d8682ff2e82c 100644 --- a/Documentation/devicetree/bindings/sound/qcom,lpass-tx-macro.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,lpass-tx-macro.yaml @@ -21,6 +21,7 @@ properties: - qcom,sc8280xp-lpass-tx-macro - items: - enum: + - qcom,eliza-lpass-tx-macro - qcom,kaanapali-lpass-tx-macro - qcom,sm8650-lpass-tx-macro - qcom,sm8750-lpass-tx-macro diff --git a/Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml b/Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml index 5c42b2b323ee..aea31fbdad37 100644 --- a/Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml @@ -21,6 +21,7 @@ properties: - qcom,sc8280xp-lpass-va-macro - items: - enum: + - qcom,eliza-lpass-va-macro - qcom,glymur-lpass-va-macro - qcom,kaanapali-lpass-va-macro - qcom,sm8650-lpass-va-macro diff --git a/Documentation/devicetree/bindings/sound/qcom,lpass-wsa-macro.yaml b/Documentation/devicetree/bindings/sound/qcom,lpass-wsa-macro.yaml index d5f22b5cf021..9fedd80532e2 100644 --- a/Documentation/devicetree/bindings/sound/qcom,lpass-wsa-macro.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,lpass-wsa-macro.yaml @@ -20,6 +20,7 @@ properties: - qcom,sc8280xp-lpass-wsa-macro - items: - enum: + - qcom,eliza-lpass-wsa-macro - qcom,glymur-lpass-wsa-macro - qcom,kaanapali-lpass-wsa-macro - qcom,sm8650-lpass-wsa-macro From 4214220c314435bc1b8ca492b7657ae5ad52145e Mon Sep 17 00:00:00 2001 From: Ravi Hothi Date: Fri, 3 Jul 2026 13:04:33 +0530 Subject: [PATCH 0729/1054] ASoC: dt-bindings: qcom,sm8250: Add Eliza sound card Add bindings for Eliza sound card, which is compatible with the existing SM8450. Signed-off-by: Ravi Hothi Acked-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260703073434.2589657-3-ravi.hothi@oss.qualcomm.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/qcom,sm8250.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml index 15f38622b98b..dae440ecab59 100644 --- a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml @@ -23,6 +23,7 @@ properties: - const: qcom,sdm845-sndcard - items: - enum: + - qcom,eliza-sndcard - qcom,kaanapali-sndcard - qcom,sm8550-sndcard - qcom,sm8650-sndcard From 0d037985725a2123b9d92dbf98ccb5f81e2bb8b4 Mon Sep 17 00:00:00 2001 From: Ravi Hothi Date: Fri, 3 Jul 2026 13:04:34 +0530 Subject: [PATCH 0730/1054] ASoC: qcom: sc8280xp: Add support for Eliza Add compatible for sound card on Qualcomm Eliza boards. Signed-off-by: Ravi Hothi Link: https://patch.msgid.link/20260703073434.2589657-4-ravi.hothi@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/qcom/sc8280xp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/qcom/sc8280xp.c b/sound/soc/qcom/sc8280xp.c index 7925aa3f63ba..98b15a527e37 100644 --- a/sound/soc/qcom/sc8280xp.c +++ b/sound/soc/qcom/sc8280xp.c @@ -164,6 +164,7 @@ static int sc8280xp_platform_probe(struct platform_device *pdev) } static const struct of_device_id snd_sc8280xp_dt_match[] = { + {.compatible = "qcom,eliza-sndcard", "eliza"}, {.compatible = "qcom,kaanapali-sndcard", "kaanapali"}, {.compatible = "qcom,qcm6490-idp-sndcard", "qcm6490"}, {.compatible = "qcom,qcs615-sndcard", "qcs615"}, From 735a461d060d4eeb2f9732aba1295bc32a3982e2 Mon Sep 17 00:00:00 2001 From: Madhu M Date: Fri, 19 Jun 2026 21:03:11 +0530 Subject: [PATCH 0731/1054] usb: typec: ucsi: Pass full DP config payload in SET_NEW_CAM for DP alt mode In the UCSI Specification Revision 3.1 RC1, bits 32-63 of the SET_NEW_CAM command hold the 32-bit Alternate Mode Specific (AMSpecific) field. For DisplayPort Alternate Mode, this field must contain the full 32-bit DisplayPort configuration VDO payload that the OPM wants the connector to operate in, rather than just the pin assignment value. This AMSpecific value follows the DisplayPort Configurations defined in the DisplayPort Alt Mode on USB Type-C Specification v2.1a, Table 5-13: SOP DisplayPort Configurations. Fixes: af8622f6a585 ("usb: typec: ucsi: Support for DisplayPort alt mode") Cc: stable Signed-off-by: Madhu M Reviewed-by: Jameson Thies Reviewed-by: Andrei Kuchynski Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/20260619153311.3526083-1-madhu.m@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/displayport.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/typec/ucsi/displayport.c b/drivers/usb/typec/ucsi/displayport.c index c44da2fae81f..7067f2561b84 100644 --- a/drivers/usb/typec/ucsi/displayport.c +++ b/drivers/usb/typec/ucsi/displayport.c @@ -185,13 +185,12 @@ static int ucsi_displayport_status_update(struct ucsi_dp *dp) static int ucsi_displayport_configure(struct ucsi_dp *dp) { - u32 pins = DP_CONF_GET_PIN_ASSIGN(dp->data.conf); u64 command; if (!dp->override) return 0; - command = UCSI_CMD_SET_NEW_CAM(dp->con->num, 1, dp->offset, pins); + command = UCSI_CMD_SET_NEW_CAM(dp->con->num, 1, dp->offset, dp->data.conf); return ucsi_send_command(dp->con->ucsi, command, NULL, 0); } From 195e667c8719480c320cd48ac0fbf1cb81d6ffe0 Mon Sep 17 00:00:00 2001 From: Yichong Chen Date: Wed, 17 Jun 2026 10:06:13 +0800 Subject: [PATCH 0732/1054] usbip: tools: support SuperSpeedPlus devices USB devices running at SuperSpeedPlus report "10000" or "20000" in their sysfs speed attribute. usbip currently maps only "5000" to USB_SPEED_SUPER, so a SuperSpeedPlus device is imported as USB_SPEED_UNKNOWN. The attach request is then rejected by vhci_hcd: vhci_hcd: Failed attach request for unsupported USB speed: UNKNOWN Map the SuperSpeedPlus sysfs speed values to USB_SPEED_SUPER_PLUS, use the SuperSpeed VHCI hub for SuperSpeedPlus devices, and recognize the gadget current_speed string used by the kernel. Fixes: b2316645ca5e ("usb: show speed "10000" in sysfs for USB 3.1 SuperSpeedPlus devices") Cc: stable Signed-off-by: Yichong Chen Reviewed-by: Shuah Khan Link: https://patch.msgid.link/00C828F338E43447+20260617020613.199086-1-chenyichong@uniontech.com Signed-off-by: Greg Kroah-Hartman --- tools/usb/usbip/libsrc/usbip_common.c | 2 ++ tools/usb/usbip/libsrc/usbip_device_driver.c | 4 ++++ tools/usb/usbip/libsrc/vhci_driver.c | 1 + 3 files changed, 7 insertions(+) diff --git a/tools/usb/usbip/libsrc/usbip_common.c b/tools/usb/usbip/libsrc/usbip_common.c index b8d7d480595a..f4734f552d31 100644 --- a/tools/usb/usbip/libsrc/usbip_common.c +++ b/tools/usb/usbip/libsrc/usbip_common.c @@ -29,6 +29,8 @@ static const struct speed_string speed_strings[] = { { USB_SPEED_HIGH, "480", "High Speed(480Mbps)" }, { USB_SPEED_WIRELESS, "53.3-480", "Wireless"}, { USB_SPEED_SUPER, "5000", "Super Speed(5000Mbps)" }, + { USB_SPEED_SUPER_PLUS, "10000", "Super Speed Plus(10000Mbps)" }, + { USB_SPEED_SUPER_PLUS, "20000", "Super Speed Plus(20000Mbps)" }, { 0, NULL, NULL } }; diff --git a/tools/usb/usbip/libsrc/usbip_device_driver.c b/tools/usb/usbip/libsrc/usbip_device_driver.c index 1dfbb76ab26c..c9b3619d86f3 100644 --- a/tools/usb/usbip/libsrc/usbip_device_driver.c +++ b/tools/usb/usbip/libsrc/usbip_device_driver.c @@ -57,6 +57,10 @@ static struct { .speed = USB_SPEED_SUPER, .name = "super-speed", }, + { + .speed = USB_SPEED_SUPER_PLUS, + .name = "super-speed-plus", + }, }; static diff --git a/tools/usb/usbip/libsrc/vhci_driver.c b/tools/usb/usbip/libsrc/vhci_driver.c index 8159fd98680b..4ca3783ee5b7 100644 --- a/tools/usb/usbip/libsrc/vhci_driver.c +++ b/tools/usb/usbip/libsrc/vhci_driver.c @@ -338,6 +338,7 @@ int usbip_vhci_get_free_port(uint32_t speed) switch (speed) { case USB_SPEED_SUPER: + case USB_SPEED_SUPER_PLUS: if (vhci_driver->idev[i].hub != HUB_SPEED_SUPER) continue; break; From c5371e0b91b24159a3ebaa61e70b0980bcf03c0a Mon Sep 17 00:00:00 2001 From: Sam Day Date: Fri, 26 Jun 2026 14:29:10 +1000 Subject: [PATCH 0733/1054] usbip: vudc: fix NULL deref in vep_dequeue() vep_alloc_request() wasn't initializing vrequest->udc, so cancellations on the FunctionFS AIO path were arriving in vep_dequeue without a valid UDC reference. Since vrequest->udc is never actually properly used anywhere, we opt to remove it, and update vep_dequeue to obtain a reference to the udc with ep_to_vudc(), consistent with the other vep_ ops. AFAICT this bug has existed for ~10 years. Seems that nobody has really stressed the FunctionFS AIO path on usbip's vudc. I tested this fix in a QEMU aarch64 guest driving FunctionFS endpoints via AIO. Before the fix, running `usbip attach` from the host would cause the guest to oops with the following backtrace: Call trace: vep_dequeue+0x1c/0xe4 (P) usb_ep_dequeue+0x14/0x20 ffs_aio_cancel+0x24/0x34 __arm64_sys_io_cancel+0xb0/0x124 do_el0_svc+0x68/0x100 el0_svc+0x18/0x5c el0t_64_sync_handler+0x98/0xdc el0t_64_sync+0x154/0x158 Assisted-by: opencode:openai/gpt-5.5 Cc: stable Fixes: b6a0ca111867 ("usbip: vudc: Add UDC specific ops") Reviewed-by: Igor Kotrasinski Signed-off-by: Sam Day Link: https://patch.msgid.link/20260626-usbip-vudc-deque-fix-v3-1-98c2dc4d6a48@samcday.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/usbip/vudc.h | 1 - drivers/usb/usbip/vudc_dev.c | 4 +--- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/usb/usbip/vudc.h b/drivers/usb/usbip/vudc.h index faf61c9c6a98..5ef0e7d9b23a 100644 --- a/drivers/usb/usbip/vudc.h +++ b/drivers/usb/usbip/vudc.h @@ -38,7 +38,6 @@ struct vep { struct vrequest { struct usb_request req; - struct vudc *udc; struct list_head req_entry; /* Request queue */ }; diff --git a/drivers/usb/usbip/vudc_dev.c b/drivers/usb/usbip/vudc_dev.c index c5f079c5a1ea..5ef88117965d 100644 --- a/drivers/usb/usbip/vudc_dev.c +++ b/drivers/usb/usbip/vudc_dev.c @@ -333,7 +333,6 @@ static int vep_queue(struct usb_ep *_ep, struct usb_request *_req, static int vep_dequeue(struct usb_ep *_ep, struct usb_request *_req) { struct vep *ep; - struct vrequest *req; struct vudc *udc; struct vrequest *lst; unsigned long flags; @@ -343,8 +342,7 @@ static int vep_dequeue(struct usb_ep *_ep, struct usb_request *_req) return ret; ep = to_vep(_ep); - req = to_vrequest(_req); - udc = req->udc; + udc = ep_to_vudc(ep); if (!udc->driver) return -ESHUTDOWN; From 30adce93d5c4a5a1ec29d9249e3fdfcc391d406b Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Fri, 26 Jun 2026 14:46:17 +0800 Subject: [PATCH 0734/1054] usb: gadget: f_printer: take kref only for successful open printer_open() returns -EBUSY when the character device is already open, but it increments dev->kref regardless of the return value. VFS does not call ->release() for a failed open, so every rejected second open permanently leaks one reference. Move kref_get() into the successful-open branch. Fixes: e8d5f92b8d30 ("usb: gadget: function: printer: fix use-after-free in __lock_acquire") Cc: stable Signed-off-by: Xu Rao Link: https://patch.msgid.link/80295742B820DA9B+20260626064617.4090626-1-raoxu@uniontech.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_printer.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/gadget/function/f_printer.c b/drivers/usb/gadget/function/f_printer.c index e4f7828ae75d..837f753d0cae 100644 --- a/drivers/usb/gadget/function/f_printer.c +++ b/drivers/usb/gadget/function/f_printer.c @@ -363,12 +363,11 @@ printer_open(struct inode *inode, struct file *fd) ret = 0; /* Change the printer status to show that it's on-line. */ dev->printer_status |= PRINTER_SELECTED; + kref_get(&dev->kref); } spin_unlock_irqrestore(&dev->lock, flags); - kref_get(&dev->kref); - return ret; } From 5fc3f333c001f1e308bbcdeecdec0d054d24338b Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Fri, 26 Jun 2026 15:06:07 +0800 Subject: [PATCH 0735/1054] USB: usb-storage: ene_ub6250: restore media-ready check Commit 1892bf90677a ("USB: usb-storage: Fix use of bitfields for hardware data in ene_ub6250.c") converted the media status fields from bitfields to bit masks. The original ene_transport() test called ene_init() only when neither media type was ready: !(sd_ready || ms_ready) The converted test became: !sd_ready || ms_ready This is not equivalent. Restore the original semantics by testing that both ready bits are clear before calling ene_init(). Fixes: 1892bf90677a ("USB: usb-storage: Fix use of bitfields for hardware data in ene_ub6250.c") Cc: stable Signed-off-by: Xu Rao Reviewed-by: Alan Stern Link: https://patch.msgid.link/F42641386E32404F+20260626070607.4119527-1-raoxu@uniontech.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/ene_ub6250.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/storage/ene_ub6250.c b/drivers/usb/storage/ene_ub6250.c index 8770de01a384..ed49a3bc859c 100644 --- a/drivers/usb/storage/ene_ub6250.c +++ b/drivers/usb/storage/ene_ub6250.c @@ -2305,7 +2305,8 @@ static int ene_transport(struct scsi_cmnd *srb, struct us_data *us) /*US_DEBUG(usb_stor_show_command(us, srb)); */ scsi_set_resid(srb, 0); - if (unlikely(!(info->SD_Status & SD_Ready) || (info->MS_Status & MS_Ready))) + if (unlikely(!(info->SD_Status & SD_Ready) && + !(info->MS_Status & MS_Ready))) result = ene_init(us); if (result == USB_STOR_XFER_GOOD) { result = USB_STOR_TRANSPORT_ERROR; From b9399d25fbb34a05bbe76eeedd730f62ff2670e9 Mon Sep 17 00:00:00 2001 From: Dawei Feng Date: Tue, 30 Jun 2026 15:14:19 +0800 Subject: [PATCH 0736/1054] usb: free iso schedules on failed submit EHCI and FOTG210 isochronous submits build an ehci_iso_sched before linking the URB to the endpoint queue, and keep the staged schedule in urb->hcpriv until iso_stream_schedule() and the link helpers consume it. If the controller is no longer accessible, or usb_hcd_link_urb_to_ep() fails, submit jumps to done_not_linked before that handoff happens and leaks the staged schedule still attached to urb->hcpriv. Free the staged schedule from done_not_linked when submit fails before the URB is linked and clear urb->hcpriv after the free. 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.1. An x86_64 allyesconfig build showed no new warnings. As we do not have an EHCI host controller with a USB isochronous device to test with, no runtime testing was able to be performed. Fixes: 8de98402652c ("[PATCH] USB: Fix USB suspend/resume crasher (#2)") Fixes: e9df41c5c589 ("USB: make HCDs responsible for managing endpoint queues") Fixes: 7d50195f6c50 ("usb: host: Faraday fotg210-hcd driver") Cc: stable Signed-off-by: Dawei Feng Reviewed-by: Alan Stern Link: https://patch.msgid.link/20260630071419.349161-1-dawei.feng@seu.edu.cn Signed-off-by: Greg Kroah-Hartman --- drivers/usb/fotg210/fotg210-hcd.c | 6 ++++-- drivers/usb/host/ehci-sched.c | 11 +++++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/drivers/usb/fotg210/fotg210-hcd.c b/drivers/usb/fotg210/fotg210-hcd.c index 1a48329a4e08..956be5b56510 100644 --- a/drivers/usb/fotg210/fotg210-hcd.c +++ b/drivers/usb/fotg210/fotg210-hcd.c @@ -4267,8 +4267,6 @@ static int iso_stream_schedule(struct fotg210_hcd *fotg210, struct urb *urb, return 0; fail: - iso_sched_free(stream, sched); - urb->hcpriv = NULL; return status; } @@ -4562,6 +4560,10 @@ static int itd_submit(struct fotg210_hcd *fotg210, struct urb *urb, else usb_hcd_unlink_urb_from_ep(fotg210_to_hcd(fotg210), urb); done_not_linked: + if (status < 0) { + iso_sched_free(stream, urb->hcpriv); + urb->hcpriv = NULL; + } spin_unlock_irqrestore(&fotg210->lock, flags); done: return status; diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index a241337c9af8..57d07d1c2dfa 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -1623,6 +1623,7 @@ iso_stream_schedule( status = 1; /* and give it back immediately */ iso_sched_free(stream, sched); sched = NULL; + urb->hcpriv = NULL; } } urb->error_count = skip / period; @@ -1653,8 +1654,6 @@ iso_stream_schedule( return status; fail: - iso_sched_free(stream, sched); - urb->hcpriv = NULL; return status; } @@ -1966,6 +1965,10 @@ static int itd_submit(struct ehci_hcd *ehci, struct urb *urb, usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb); } done_not_linked: + if (status < 0) { + iso_sched_free(stream, urb->hcpriv); + urb->hcpriv = NULL; + } spin_unlock_irqrestore(&ehci->lock, flags); done: return status; @@ -2343,6 +2346,10 @@ static int sitd_submit(struct ehci_hcd *ehci, struct urb *urb, usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb); } done_not_linked: + if (status < 0) { + iso_sched_free(stream, urb->hcpriv); + urb->hcpriv = NULL; + } spin_unlock_irqrestore(&ehci->lock, flags); done: return status; From 95f90eea070837f7c72207d5520f805bdefc3bc5 Mon Sep 17 00:00:00 2001 From: Griffin Kroah-Hartman Date: Wed, 8 Jul 2026 13:08:58 +0200 Subject: [PATCH 0737/1054] usb: gadget: function: rndis: add length check to response query Add variable representations for BufLength and BufOffset in rndis_query_response(), and perform a length check on them. This is identical to how rndis_set_response() handles these parameters. Assisted-by: gkh_clanker_2000 Cc: stable Signed-off-by: Griffin Kroah-Hartman Link: https://patch.msgid.link/20260708-usb-gadget-rndis-v1-1-e77e026dcc6a@kroah.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/rndis.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/function/rndis.c b/drivers/usb/gadget/function/rndis.c index 3da54a7d7aba..fe2018ff071a 100644 --- a/drivers/usb/gadget/function/rndis.c +++ b/drivers/usb/gadget/function/rndis.c @@ -591,6 +591,7 @@ static int rndis_init_response(struct rndis_params *params, static int rndis_query_response(struct rndis_params *params, rndis_query_msg_type *buf) { + u32 BufLength, BufOffset; rndis_query_cmplt_type *resp; rndis_resp_t *r; @@ -598,6 +599,13 @@ static int rndis_query_response(struct rndis_params *params, if (!params->dev) return -ENOTSUPP; + BufLength = le32_to_cpu(buf->InformationBufferLength); + BufOffset = le32_to_cpu(buf->InformationBufferOffset); + if ((BufLength > RNDIS_MAX_TOTAL_SIZE) || + (BufOffset > RNDIS_MAX_TOTAL_SIZE) || + (BufOffset + 8 >= RNDIS_MAX_TOTAL_SIZE)) + return -EINVAL; + /* * we need more memory: * gen_ndis_query_resp expects enough space for @@ -614,10 +622,8 @@ static int rndis_query_response(struct rndis_params *params, resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ if (gen_ndis_query_resp(params, le32_to_cpu(buf->OID), - le32_to_cpu(buf->InformationBufferOffset) - + 8 + (u8 *)buf, - le32_to_cpu(buf->InformationBufferLength), - r)) { + BufOffset + 8 + (u8 *)buf, + BufLength, r)) { /* OID not supported */ resp->Status = cpu_to_le32(RNDIS_STATUS_NOT_SUPPORTED); resp->MessageLength = cpu_to_le32(sizeof *resp); From 21b5bf155435008e0fb0736795289788e63d426f Mon Sep 17 00:00:00 2001 From: Griffin Kroah-Hartman Date: Wed, 8 Jul 2026 13:08:59 +0200 Subject: [PATCH 0738/1054] usb: gadget: function: rndis: add length check for header Add a length check for the rndis header in rndis_rm_hdr, to ensure that MessageType, MessageLength, DataOffset, and DataLength fields are present before they are accessed. Assisted-by: gkh_clanker_2000 Cc: stable Signed-off-by: Griffin Kroah-Hartman Link: https://patch.msgid.link/20260708-usb-gadget-rndis-v1-2-e77e026dcc6a@kroah.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/rndis.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/usb/gadget/function/rndis.c b/drivers/usb/gadget/function/rndis.c index fe2018ff071a..a2fd239b7ad3 100644 --- a/drivers/usb/gadget/function/rndis.c +++ b/drivers/usb/gadget/function/rndis.c @@ -1080,6 +1080,12 @@ int rndis_rm_hdr(struct gether *port, /* tmp points to a struct rndis_packet_msg_type */ __le32 *tmp = (void *)skb->data; + /* Need at least MessageType, MessageLength, DataOffset, DataLength */ + if (skb->len < 16) { + dev_kfree_skb_any(skb); + return -EINVAL; + } + /* MessageType, MessageLength */ if (cpu_to_le32(RNDIS_MSG_PACKET) != get_unaligned(tmp++)) { From b4ecbdc4f8830f5586c4a5cfc384c00f20f8f8b3 Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Tue, 7 Jul 2026 00:10:49 +0900 Subject: [PATCH 0739/1054] USB: misc: uss720: unregister parport on probe failure uss720_probe() registers a parport before reading the 1284 register used to detect unsupported Belkin F5U002 adapters. If get_1284_register() fails, the error path drops the driver private data and the USB device reference, but leaves the parport device registered. Leaving the port registered is more than a private allocation leak: parport_register_port() has already reserved a parport number and registered the parport bus device, while pp->private_data still points at the private data that the common error path is about to release. Undo the pre-announce registration in the get_1284_register() failure branch before jumping to the common private-data cleanup path. Clear priv->pp first, matching the disconnect path and avoiding a stale pointer in the private data. This issue was identified during our ongoing static-analysis research while reviewing kernel code. Fixes: 3295f1b866bf ("usb: misc: uss720: check for incompatible versions of the Belkin F5U002") Cc: stable Co-developed-by: Ijae Kim Signed-off-by: Ijae Kim Signed-off-by: Myeonghun Pak Reviewed-by: Alex Henrie Link: https://patch.msgid.link/20260706151049.63470-1-mhun512@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/uss720.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/usb/misc/uss720.c b/drivers/usb/misc/uss720.c index b7d3c44b970e..1ce48f5832d7 100644 --- a/drivers/usb/misc/uss720.c +++ b/drivers/usb/misc/uss720.c @@ -732,8 +732,11 @@ static int uss720_probe(struct usb_interface *intf, * here. */ ret = get_1284_register(pp, 0, ®, GFP_KERNEL); dev_dbg(&intf->dev, "reg: %7ph\n", priv->reg); - if (ret < 0) + if (ret < 0) { + priv->pp = NULL; + parport_del_port(pp); goto probe_abort; + } ret = usb_find_last_int_in_endpoint(interface, &epd); if (!ret) { From e0f844d9d74200d311c6438a0f04270834ba5365 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Fri, 3 Jul 2026 17:20:33 +0100 Subject: [PATCH 0740/1054] usb: dwc3: fix dwc3_readl() and dwc3_writel() calls in dwc3_ulpi_setup() The dwc3_ulpi_setup() calls the register read and write calls with dwc3->regs when both these calls take the dwc3 structure directly. Chnage these two calls to fix the following sparse warning, and possibly a nasty bug in the dwc3_ulpi_setup() code: drivers/usb/dwc3/core.c:796:45: warning: incorrect type in argument 1 (different address spaces) drivers/usb/dwc3/core.c:796:45: expected struct dwc3 *dwc drivers/usb/dwc3/core.c:796:45: got void [noderef] __iomem *regs drivers/usb/dwc3/core.c:798:40: warning: incorrect type in argument 1 (different address spaces) drivers/usb/dwc3/core.c:798:40: expected struct dwc3 *dwc drivers/usb/dwc3/core.c:798:40: got void [noderef] __iomem *regs Cc: stable Fixes: 9accc68b1cf0 ("usb: dwc3: Add dwc pointer to dwc3_readl/writel") Acked-by: Thinh Nguyen Signed-off-by: Ben Dooks Link: https://patch.msgid.link/20260703162033.2847599-1-ben.dooks@codethink.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 517aa7f1486d..ceb49f2f8004 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -789,9 +789,9 @@ static void dwc3_ulpi_setup(struct dwc3 *dwc) if (dwc->enable_usb2_transceiver_delay) { for (index = 0; index < dwc->num_usb2_ports; index++) { - reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(index)); + reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(index)); reg |= DWC3_GUSB2PHYCFG_XCVRDLY; - dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(index), reg); + dwc3_writel(dwc, DWC3_GUSB2PHYCFG(index), reg); } } } From 0ef7cc27da8b9e315a4a5a665c68c44206f5e559 Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Wed, 1 Jul 2026 20:40:06 +0900 Subject: [PATCH 0741/1054] usb: typec: anx7411: use devm_pm_runtime_enable() anx7411_i2c_probe() enables runtime PM before returning successfully, but anx7411_i2c_remove() tears down the Type-C partner state, workqueue, dummy I2C device, mux, switch and port without disabling runtime PM. Use devm_pm_runtime_enable() so runtime PM is disabled automatically on driver detach. Since devres action registration can fail, route that failure through the existing probe unwind path. This issue was identified during our ongoing static-analysis research while reviewing kernel code. Fixes: fe6d8a9c8e64 ("usb: typec: anx7411: Add Analogix PD ANX7411 support") Cc: stable Co-developed-by: Ijae Kim Signed-off-by: Ijae Kim Signed-off-by: Myeonghun Pak Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/20260701114006.75738-1-mhun512@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/anx7411.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/usb/typec/anx7411.c b/drivers/usb/typec/anx7411.c index 604868ebf422..41df115912b9 100644 --- a/drivers/usb/typec/anx7411.c +++ b/drivers/usb/typec/anx7411.c @@ -1537,7 +1537,9 @@ static int anx7411_i2c_probe(struct i2c_client *client) if (anx7411_typec_check_connection(plat)) dev_err(dev, "check status\n"); - pm_runtime_enable(dev); + ret = devm_pm_runtime_enable(dev); + if (ret) + goto free_wq; return 0; From 4e8ba83ac4d311992e6a4c21de5dd705010df06e Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Wed, 1 Jul 2026 21:16:25 +0900 Subject: [PATCH 0742/1054] usb: sl811-hcd: disable controller wakeup on remove sl811h_probe() enables the HCD controller device as a wakeup source after usb_add_hcd() succeeds, but sl811h_remove() removes the HCD and releases the driver resources without disabling that wakeup source. Disable controller wakeup after usb_remove_hcd() and before usb_put_hcd() so the wakeup source object is detached while the controller device pointer is still available. This issue was identified during our ongoing static-analysis research while reviewing kernel code. Fixes: 3c9740a117d4 ("usb: hcd: move controller wakeup setting initialization to individual driver") Cc: stable Co-developed-by: Ijae Kim Signed-off-by: Ijae Kim Signed-off-by: Myeonghun Pak Link: https://patch.msgid.link/20260701121625.96815-1-mhun512@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/sl811-hcd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c index 4ae47edd4b8b..b044977f6f56 100644 --- a/drivers/usb/host/sl811-hcd.c +++ b/drivers/usb/host/sl811-hcd.c @@ -1591,6 +1591,7 @@ sl811h_remove(struct platform_device *dev) remove_debug_file(sl811); usb_remove_hcd(hcd); + device_wakeup_disable(hcd->self.controller); /* some platforms may use IORESOURCE_IO */ res = platform_get_resource(dev, IORESOURCE_MEM, 1); From 010382937fb69892b3469ac4d30af072262f59e8 Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Fri, 12 Jun 2026 13:20:05 +0800 Subject: [PATCH 0743/1054] usb: dwc3: run gadget disconnect from sleepable suspend context dwc3_gadget_suspend() takes dwc->lock with IRQs disabled and then calls dwc3_disconnect_gadget(). For async callbacks that helper only uses plain spin_unlock()/spin_lock(), so the gadget ->disconnect() callback still runs with IRQs disabled and any sleepable callback trips Lockdep. This issue was found by our static analysis tool and then manually reviewed against the current tree. The grounded PoC kept the dwc3_gadget_suspend() -> dwc3_disconnect_gadget() -> gadget_driver->disconnect() chain, and Lockdep reported: BUG: sleeping function called from invalid context gadget_disconnect+0x21/0x39 [vuln_msv] dwc3_gadget_suspend.constprop.0+0x2b/0x42 [vuln_msv] Keep the disconnect callback selection in one common helper, but add a sleepable suspend-side wrapper which snapshots the callback under dwc->lock and then runs it after spin_unlock_irqrestore(). The regular event path still uses the existing spin_unlock()/spin_lock() window. Fixes: c8540870af4c ("usb: dwc3: gadget: Improve dwc3_gadget_suspend() and dwc3_gadget_resume()") Cc: stable Signed-off-by: Runyu Xiao Acked-by: Thinh Nguyen Link: https://patch.msgid.link/20260612052005.3849659-1-runyu.xiao@seu.edu.cn Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/gadget.c | 43 ++++++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 3d4ca68e584c..1082e9c9afaa 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -3934,15 +3934,48 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc, } } +static bool dwc3_prepare_disconnect_gadget(struct dwc3 *dwc, + struct usb_gadget_driver **driver, + struct usb_gadget **gadget) +{ + if (!dwc->async_callbacks || !dwc->gadget_driver || + !dwc->gadget_driver->disconnect) + return false; + + *driver = dwc->gadget_driver; + *gadget = dwc->gadget; + + return true; +} + static void dwc3_disconnect_gadget(struct dwc3 *dwc) { - if (dwc->async_callbacks && dwc->gadget_driver->disconnect) { + struct usb_gadget_driver *driver; + struct usb_gadget *gadget; + + if (dwc3_prepare_disconnect_gadget(dwc, &driver, &gadget)) { spin_unlock(&dwc->lock); - dwc->gadget_driver->disconnect(dwc->gadget); + driver->disconnect(gadget); spin_lock(&dwc->lock); } } +static void dwc3_disconnect_gadget_sleepable(struct dwc3 *dwc) +{ + struct usb_gadget_driver *driver; + struct usb_gadget *gadget; + unsigned long flags; + + spin_lock_irqsave(&dwc->lock, flags); + if (!dwc3_prepare_disconnect_gadget(dwc, &driver, &gadget)) { + spin_unlock_irqrestore(&dwc->lock, flags); + return; + } + + spin_unlock_irqrestore(&dwc->lock, flags); + driver->disconnect(gadget); +} + static void dwc3_suspend_gadget(struct dwc3 *dwc) { if (dwc->async_callbacks && dwc->gadget_driver->suspend) { @@ -4838,7 +4871,6 @@ EXPORT_SYMBOL_GPL(dwc3_gadget_exit); int dwc3_gadget_suspend(struct dwc3 *dwc) { - unsigned long flags; int ret; ret = dwc3_gadget_soft_disconnect(dwc); @@ -4852,10 +4884,7 @@ int dwc3_gadget_suspend(struct dwc3 *dwc) return -EAGAIN; } - spin_lock_irqsave(&dwc->lock, flags); - if (dwc->gadget_driver) - dwc3_disconnect_gadget(dwc); - spin_unlock_irqrestore(&dwc->lock, flags); + dwc3_disconnect_gadget_sleepable(dwc); return 0; } From 67e511d2989eb1c8c588b599ce2fcc6bb8e6f7ea Mon Sep 17 00:00:00 2001 From: Jimmy Hu Date: Thu, 25 Jun 2026 15:37:04 +0800 Subject: [PATCH 0744/1054] usb: gadget: udc: Fix use-after-free in gadget_match_driver The udc structure acts as the management structure for the gadget, but their lifecycles are decoupled. A race condition exists where usb_del_gadget() frees the udc memory (e.g., via mode-switch work) while gadget_match_driver() concurrently accesses the freed udc memory (e.g., via configfs), causing a Use-After-Free (UAF) that triggers a NULL pointer dereference when the freed memory is zeroed: [39430.908615][ T1171] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000000 [39430.911397][ T1171] pc : __pi_strcmp+0x20/0x140 [39430.911441][ T1171] lr : gadget_match_driver+0x34/0x60 ... [39430.911890][ T1171] usb_gadget_register_driver_owner+0x50/0xf8 [39430.911910][ T1171] gadget_dev_desc_UDC_store+0xf4/0x140 [39430.931308][ T1171] configfs_write_iter+0xec/0x134 [39430.957058][ T1171] Workqueue: events_freezable __dwc3_set_mode [39430.957287][ T1171] dwc3_gadget_exit+0x34/0x8c [39430.957304][ T1171] __dwc3_set_mode+0xc0/0x664 Fix this by ensuring the udc structure remains allocated until the gadget is released. To achieve this, introduce a new usb_gadget_release() routine to the core. When the gadget is added, usb_add_gadget() stores the gadget's release routine in the udc structure and takes a reference to the udc. When the gadget is released, usb_gadget_release() drops the reference to the udc and then calls the gadget's release routine. Suggested-by: Alan Stern Cc: stable Signed-off-by: Jimmy Hu Reviewed-by: Alan Stern Link: https://patch.msgid.link/20260625073705.803880-1-hhhuuu@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/core.c | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c index 60340ff9edbf..f6da12b553a0 100644 --- a/drivers/usb/gadget/udc/core.c +++ b/drivers/usb/gadget/udc/core.c @@ -31,8 +31,9 @@ static const struct bus_type gadget_bus_type; /** * struct usb_udc - describes one usb device controller * @driver: the gadget driver pointer. For use by the class code - * @dev: the child device to the actual controller * @gadget: the gadget. For use by the class code + * @gadget_release: the gadget's release routine + * @dev: the child device to the actual controller * @list: for use by the udc class driver * @vbus: for udcs who care about vbus status, this value is real vbus status; * for udcs who do not care about vbus status, this value is always true @@ -53,6 +54,7 @@ static const struct bus_type gadget_bus_type; struct usb_udc { struct usb_gadget_driver *driver; struct usb_gadget *gadget; + void (*gadget_release)(struct device *dev); struct device dev; struct list_head list; bool vbus; @@ -1362,6 +1364,17 @@ static void usb_udc_nop_release(struct device *dev) dev_vdbg(dev, "%s\n", __func__); } +static void usb_gadget_release(struct device *dev) +{ + struct usb_gadget *gadget = dev_to_usb_gadget(dev); + struct usb_udc *udc = gadget->udc; + /* Cache the gadget's release routine to prevent UAF */ + void (*release)(struct device *dev) = udc->gadget_release; + + put_device(&udc->dev); + release(dev); +} + /** * usb_initialize_gadget - initialize a gadget and its embedded struct device * @parent: the parent device to this udc. Usually the controller driver's @@ -1418,6 +1431,14 @@ int usb_add_gadget(struct usb_gadget *gadget) mutex_init(&udc->connect_lock); udc->started = false; + /* + * Align decoupled lifecycles: take a UDC reference to ensure it + * remains allocated until the gadget is released, requiring an + * override of the gadget's release routine to drop it. + */ + udc->gadget_release = gadget->dev.release; + gadget->dev.release = usb_gadget_release; + get_device(&udc->dev); mutex_lock(&udc_lock); list_add_tail(&udc->list, &udc_list); @@ -1462,6 +1483,12 @@ int usb_add_gadget(struct usb_gadget *gadget) mutex_lock(&udc_lock); list_del(&udc->list); mutex_unlock(&udc_lock); + /* + * Revert the override and drop the UDC reference to prevent + * leaking the UDC if the gadget was statically allocated. + */ + gadget->dev.release = udc->gadget_release; + put_device(&udc->dev); err_put_udc: put_device(&udc->dev); From 49f6e3c3ef19f04f6657ed8dce550e36c763abb8 Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Fri, 3 Jul 2026 17:40:32 +0300 Subject: [PATCH 0745/1054] xhci: sideband: fix ring sg table pages leak xhci_ring_to_sgtable() allocates a temporary pages array and uses it to build the returned sg_table with sg_alloc_table_from_pages(). The error paths free the pages array, but the success path returns the sg_table without freeing it. This leaks the temporary array every time a sideband client gets an endpoint or event ring buffer. Free the pages array after sg_alloc_table_from_pages() succeeds. The returned sg_table has its own scatterlist entries and does not depend on the temporary array after construction. Fixes: de66754e9f80 ("xhci: sideband: add initial api to register a secondary interrupter entity") Cc: stable Signed-off-by: Xu Rao Signed-off-by: Mathias Nyman Link: https://patch.msgid.link/20260703144033.483286-2-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-sideband.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/host/xhci-sideband.c b/drivers/usb/host/xhci-sideband.c index 23153e136d4b..a5deeee4d5dc 100644 --- a/drivers/usb/host/xhci-sideband.c +++ b/drivers/usb/host/xhci-sideband.c @@ -58,6 +58,8 @@ xhci_ring_to_sgtable(struct xhci_sideband *sb, struct xhci_ring *ring) if (sg_alloc_table_from_pages(sgt, pages, n_pages, 0, sz, GFP_KERNEL)) goto err; + kvfree(pages); + /* * Save first segment dma address to sg dma_address field for the sideband * client to have access to the IOVA of the ring. From 42c37c4b75d38b51d84f31a8e29427f5e06a7c2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=83=A1=E8=BF=9E=E5=8B=A4?= Date: Fri, 3 Jul 2026 17:40:33 +0300 Subject: [PATCH 0746/1054] usb: xhci: Fix sleep in atomic context in xhci_free_streams() When a USB device with active stream endpoints is disconnected, xhci_free_streams() is called from the hub_event workqueue to free the stream resources. It calls xhci_free_stream_info() while holding xhci->lock with irqs disabled. xhci_free_stream_info() invokes xhci_free_stream_ctx(), which calls dma_free_coherent() for large stream context arrays. dma_free_coherent() can sleep (e.g. via vunmap), triggering a BUG when called from atomic context. Call trace: dma_free_attrs+0x174/0x220 xhci_free_stream_info+0xd0/0x11c xhci_free_streams+0x278/0x37c usb_free_streams+0x98/0xc0 usb_unbind_interface+0x1b8/0x2f8 device_release_driver_internal+0x1d4/0x2cc device_release_driver+0x18/0x28 bus_remove_device+0x160/0x1a4 device_del+0x1ec/0x350 usb_disable_device+0x98/0x214 usb_disconnect+0xf0/0x35c hub_event+0xab4/0x19ec process_one_work+0x278/0x63c Fix this by saving the stream_info pointers and clearing the ep references under the lock, then calling xhci_free_stream_info() outside the lock where sleeping is allowed. Fixes: 8df75f42f8e6 ("USB: xhci: Add memory allocation for USB3 bulk streams.") Cc: stable Signed-off-by: Lianqin Hu Signed-off-by: Mathias Nyman Link: https://patch.msgid.link/20260703144033.483286-3-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 6922cc5496c1..f44ccee5fa07 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -3785,6 +3785,7 @@ static int xhci_free_streams(struct usb_hcd *hcd, struct usb_device *udev, struct xhci_virt_device *vdev; struct xhci_command *command; struct xhci_input_control_ctx *ctrl_ctx; + struct xhci_stream_info *stream_info[EP_CTX_PER_DEV]; unsigned int ep_index; unsigned long flags; u32 changed_ep_bitmask; @@ -3845,10 +3846,15 @@ static int xhci_free_streams(struct usb_hcd *hcd, struct usb_device *udev, if (ret < 0) return ret; + /* + * dma_free_coherent() called by xhci_free_stream_info() may sleep, + * so save stream_info pointers and clear references under lock, + * then free the memory outside lock. + */ spin_lock_irqsave(&xhci->lock, flags); for (i = 0; i < num_eps; i++) { ep_index = xhci_get_endpoint_index(&eps[i]->desc); - xhci_free_stream_info(xhci, vdev->eps[ep_index].stream_info); + stream_info[i] = vdev->eps[ep_index].stream_info; vdev->eps[ep_index].stream_info = NULL; /* FIXME Unset maxPstreams in endpoint context and * update deq ptr to point to normal string ring. @@ -3858,6 +3864,9 @@ static int xhci_free_streams(struct usb_hcd *hcd, struct usb_device *udev, } spin_unlock_irqrestore(&xhci->lock, flags); + for (i = 0; i < num_eps; i++) + xhci_free_stream_info(xhci, stream_info[i]); + return 0; } From b229b22b0a945d52dee887c856991ad08744d08e Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Mon, 1 Jun 2026 15:55:02 +0200 Subject: [PATCH 0747/1054] usb: typec: ps883x: Fix DP+USB3 configuration Commit 6bebd9b77726 ("usb: typec: ps883x: Rework ps883x_set()") introduced two regressions: 1. The CONN_STATUS_0_USB_3_1_CONNECTED bit is mistakenly written to the wrong configuration register (cfg1 instead of cfg0). This breaks USB3 when using USB3+DP adapters. 2. The switch-case fallthrough block is inverted: Currently, TYPEC_DP_STATE_C (DP-only) inherits the USB3 configuration, while TYPEC_DP_STATE_D (DP+USB3) is missing the necessary DP sink flags. Fix these by writing the USB3 bit to the correct register and swapping the case statement order so both states get their correct bits assigned. Cc: stable Fixes: 6bebd9b77726 ("usb: typec: ps883x: Rework ps883x_set()") Signed-off-by: Stephan Gerhold Reviewed-by: Heikki Krogerus Tested-by: Jens Glathe Reviewed-by: Konrad Dybcio Link: https://patch.msgid.link/20260601-ps883x-usb3dp-fixes-v1-1-d19bec3a6d26@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/mux/ps883x.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/usb/typec/mux/ps883x.c b/drivers/usb/typec/mux/ps883x.c index f52443638ee2..64e0a61b776a 100644 --- a/drivers/usb/typec/mux/ps883x.c +++ b/drivers/usb/typec/mux/ps883x.c @@ -206,12 +206,12 @@ static int ps883x_set(struct ps883x_retimer *retimer, struct typec_retimer_state CONN_STATUS_1_DP_HPD_LEVEL; switch (state->mode) { + case TYPEC_DP_STATE_D: + cfg0 |= CONN_STATUS_0_USB_3_1_CONNECTED; + fallthrough; case TYPEC_DP_STATE_C: cfg1 |= CONN_STATUS_1_DP_SINK_REQUESTED | CONN_STATUS_1_DP_PIN_ASSIGNMENT_C_D; - fallthrough; - case TYPEC_DP_STATE_D: - cfg1 |= CONN_STATUS_0_USB_3_1_CONNECTED; break; default: /* MODE_E */ break; From 43ae2f90b70cda374c487c1639a01d0f14e5d583 Mon Sep 17 00:00:00 2001 From: Shuangpeng Bai Date: Thu, 2 Jul 2026 15:13:29 -0400 Subject: [PATCH 0748/1054] usb: typec: class: drop PD lookup reference usb_power_delivery_find() wraps class_find_device_by_name(). That helper returns a device reference that must be released by the caller. select_usb_power_delivery_store() only needs this reference while calling the pd_set callback. Drop it once the callback returns. Otherwise the sysfs write can pin the selected USB Power Delivery object and prevent it from being released on unregister. Fixes: a7cff92f0635 ("usb: typec: USB Power Delivery helpers for ports and partners") Cc: stable Signed-off-by: Shuangpeng Bai Link: https://patch.msgid.link/20260702191329.2648043-1-shuangpeng.kernel@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/class.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 0977581ad1b6..0595e8cb83aa 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -1619,6 +1619,7 @@ static ssize_t select_usb_power_delivery_store(struct device *dev, return -EINVAL; ret = port->ops->pd_set(port, pd); + put_device(&pd->dev); if (ret) return ret; From 7c4a234bd31a64a8dbd0140dc812da592c5e0787 Mon Sep 17 00:00:00 2001 From: Paul Menzel Date: Fri, 3 Jul 2026 13:07:37 +0200 Subject: [PATCH 0749/1054] usb: typec: ucsi: cancel pending work on system suspend On a Dell XPS 13 9360 (BIOS 2.21.0), entering system suspend (deep/S3) races a pending UCSI connector-change worker against the ACPI EC teardown. The worker evaluates the UCSI _DSM (GET_CONNECTOR_STATUS), whose AML accesses the Embedded Controller. By that point the ACPI EC has already been stopped for suspend, so the EC address space handler rejects the access with AE_BAD_PARAMETER, aborting the AML and failing the connector query: [22314.689495] ACPI: EC: interrupt blocked [22314.711981] ACPI: PM: Preparing to enter system sleep state S3 [22314.743260] ACPI: EC: event blocked [22314.743265] ACPI: EC: EC stopped [22314.743267] ACPI: PM: Saving platform NVS memory [22314.744241] ACPI Error: AE_BAD_PARAMETER, Returned by Handler for [EmbeddedControl] (20260408/evregion-303) [22314.744432] ACPI Error: Aborting method \_SB.PCI0.LPCB.ECDV.ECW1 due to previous error (AE_BAD_PARAMETER) (20260408/psparse-543) [22314.744673] ACPI Error: Aborting method \ECWB due to previous error (AE_BAD_PARAMETER) (20260408/psparse-543) [22314.745201] ACPI Error: Aborting method \_SB.UBTC._DSM due to previous error (AE_BAD_PARAMETER) (20260408/psparse-543) [22314.745394] ACPI: \_SB_.UBTC: failed to evaluate _DSM c298836f-a47c-e411-ad36-631042b5008f rev:1 func:1 (0x1001) [22314.745414] ucsi_acpi USBC000:00: ucsi_acpi_dsm: failed to evaluate _DSM 1 [22314.745424] ucsi_acpi USBC000:00: ucsi_handle_connector_change: GET_CONNECTOR_STATUS failed (-5) ucsi_acpi implements a resume callback but no suspend callback, so nothing cancels the connector-change work before the firmware/EC is torn down. Add a `ucsi_suspend()` core helper that cancels the pending init and connector-change work, and wire it into ucsi_acpi's PM ops. The connector state is re-read on resume by `ucsi_resume()`, so cancelling the work loses nothing. Fixes: 4e3a50293c2b ("usb: typec: ucsi: acpi: Implement resume callback") Cc: stable Signed-off-by: Paul Menzel Assisted-by: Claude Opus 4.8 Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/20260703110738.8457-2-pmenzel@molgen.mpg.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi.c | 20 ++++++++++++++++++++ drivers/usb/typec/ucsi/ucsi.h | 1 + drivers/usb/typec/ucsi/ucsi_acpi.c | 10 +++++++++- 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 92166a3725b1..6a6723e8fb12 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -2017,6 +2017,26 @@ static void ucsi_resume_work(struct work_struct *work) } } +int ucsi_suspend(struct ucsi *ucsi) +{ + int i; + + /* + * Cancel pending work so it cannot access the firmware after the ACPI + * EC is stopped for suspend; state is re-read on resume. + */ + cancel_delayed_work_sync(&ucsi->work); + + if (!ucsi->connector) + return 0; + + for (i = 0; i < ucsi->cap.num_connectors; i++) + cancel_work_sync(&ucsi->connector[i].work); + + return 0; +} +EXPORT_SYMBOL_GPL(ucsi_suspend); + int ucsi_resume(struct ucsi *ucsi) { if (ucsi->connector) diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h index 325ed1e5ca80..6e1608d88ec3 100644 --- a/drivers/usb/typec/ucsi/ucsi.h +++ b/drivers/usb/typec/ucsi/ucsi.h @@ -582,6 +582,7 @@ int ucsi_write_message_out_command(struct ucsi *ucsi, u64 command, void *msg_out, size_t msg_out_size); void ucsi_altmode_update_active(struct ucsi_connector *con); +int ucsi_suspend(struct ucsi *ucsi); int ucsi_resume(struct ucsi *ucsi); void ucsi_notify_common(struct ucsi *ucsi, u32 cci); diff --git a/drivers/usb/typec/ucsi/ucsi_acpi.c b/drivers/usb/typec/ucsi/ucsi_acpi.c index 60b12961e1a4..18286d3e9cc5 100644 --- a/drivers/usb/typec/ucsi/ucsi_acpi.c +++ b/drivers/usb/typec/ucsi/ucsi_acpi.c @@ -263,6 +263,13 @@ static void ucsi_acpi_remove(struct platform_device *pdev) ucsi_acpi_notify); } +static int ucsi_acpi_suspend(struct device *dev) +{ + struct ucsi_acpi *ua = dev_get_drvdata(dev); + + return ucsi_suspend(ua->ucsi); +} + static int ucsi_acpi_resume(struct device *dev) { struct ucsi_acpi *ua = dev_get_drvdata(dev); @@ -270,7 +277,8 @@ static int ucsi_acpi_resume(struct device *dev) return ucsi_resume(ua->ucsi); } -static DEFINE_SIMPLE_DEV_PM_OPS(ucsi_acpi_pm_ops, NULL, ucsi_acpi_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(ucsi_acpi_pm_ops, ucsi_acpi_suspend, + ucsi_acpi_resume); static const struct acpi_device_id ucsi_acpi_match[] = { { "PNP0CA0", 0 }, From 9cff680e47632b7723cb19f9c5e63669063c3417 Mon Sep 17 00:00:00 2001 From: Andy Yan Date: Thu, 4 Jun 2026 18:50:24 +0800 Subject: [PATCH 0750/1054] usb: typec: tcpm: Fix VDM type for Enter Mode commands VDO() second parameter is VDM type (bit 15): 1 for SVDM, 0 for UVDM. Using 'vdo ? 2 : 1' corrupts SVID low bit when vdo is non-NULL (2 << 15 = BIT(16)). Enter Mode is always SVDM, hardcode to 1. Fixes: 8face9aa57c8 ("usb: typec: Add parameter for the VDO to typec_altmode_enter()") Cc: stable Signed-off-by: Andy Yan Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/20260604105059.18750-1-andyshrk@163.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index bc531923b1ca..89eec20a2064 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -3093,7 +3093,7 @@ static int tcpm_altmode_enter(struct typec_altmode *altmode, u32 *vdo) if (svdm_version < 0) return svdm_version; - header = VDO(altmode->svid, vdo ? 2 : 1, svdm_version, CMD_ENTER_MODE); + header = VDO(altmode->svid, 1, svdm_version, CMD_ENTER_MODE); header |= VDO_OPOS(altmode->mode); return tcpm_queue_vdm_unlocked(port, header, vdo, vdo ? 1 : 0, TCPC_TX_SOP); @@ -3141,7 +3141,7 @@ static int tcpm_cable_altmode_enter(struct typec_altmode *altmode, enum typec_pl if (svdm_version < 0) return svdm_version; - header = VDO(altmode->svid, vdo ? 2 : 1, svdm_version, CMD_ENTER_MODE); + header = VDO(altmode->svid, 1, svdm_version, CMD_ENTER_MODE); header |= VDO_OPOS(altmode->mode); return tcpm_queue_vdm_unlocked(port, header, vdo, vdo ? 1 : 0, TCPC_TX_SOP_PRIME); From e8da46d99d3710106e7c44db14566bf9b57386b5 Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Mon, 6 Jul 2026 23:53:12 +0900 Subject: [PATCH 0751/1054] usb: typec: tcpci_rt1711h: unregister TCPCI port with devres rt1711h_probe() registers the TCPCI port before requesting the interrupt and enabling alert interrupts. If either of those later steps fails, the probe function returns without unregistering the TCPCI port. The explicit unregister currently only happens from the remove callback. Register a devres action immediately after tcpci_register_port() succeeds, so tcpci_unregister_port() runs on later probe failures and on driver detach. Drop the remove callback to avoid unregistering the same port twice. This issue was identified during our ongoing static-analysis research while reviewing kernel code. Fixes: 302c570bf36e ("usb: typec: tcpci_rt1711h: avoid screaming irq causing boot hangs") Cc: stable Co-developed-by: Ijae Kim Signed-off-by: Ijae Kim Signed-off-by: Myeonghun Pak Link: https://patch.msgid.link/20260706145312.37260-1-mhun512@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpci_rt1711h.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/usb/typec/tcpm/tcpci_rt1711h.c b/drivers/usb/typec/tcpm/tcpci_rt1711h.c index a8726da6fc71..20037ef130ca 100644 --- a/drivers/usb/typec/tcpm/tcpci_rt1711h.c +++ b/drivers/usb/typec/tcpm/tcpci_rt1711h.c @@ -295,6 +295,8 @@ static int rt1711h_sw_reset(struct rt1711h_chip *chip) return 0; } +static void rt1711h_unregister_tcpci_port(void *tcpci); + static int rt1711h_probe(struct i2c_client *client) { int ret; @@ -340,6 +342,10 @@ static int rt1711h_probe(struct i2c_client *client) if (IS_ERR_OR_NULL(chip->tcpci)) return PTR_ERR(chip->tcpci); + ret = devm_add_action_or_reset(chip->dev, rt1711h_unregister_tcpci_port, chip->tcpci); + if (ret) + return ret; + ret = devm_request_threaded_irq(chip->dev, client->irq, NULL, rt1711h_irq, IRQF_ONESHOT | IRQF_TRIGGER_LOW, @@ -357,11 +363,9 @@ static int rt1711h_probe(struct i2c_client *client) return 0; } -static void rt1711h_remove(struct i2c_client *client) +static void rt1711h_unregister_tcpci_port(void *tcpci) { - struct rt1711h_chip *chip = i2c_get_clientdata(client); - - tcpci_unregister_port(chip->tcpci); + tcpci_unregister_port(tcpci); } static const struct rt1711h_chip_info rt1711h = { @@ -394,7 +398,6 @@ static struct i2c_driver rt1711h_i2c_driver = { .of_match_table = rt1711h_of_match, }, .probe = rt1711h_probe, - .remove = rt1711h_remove, .id_table = rt1711h_id, }; module_i2c_driver(rt1711h_i2c_driver); From b11c513ad943f35cf5e8007d3a56279c79b7ed4b Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Tue, 7 Jul 2026 16:23:58 -0700 Subject: [PATCH 0752/1054] gpio: mvebu: free generic chips on unbind irq_alloc_domain_generic_chips() allocates generic chip data that must be freed via irq_domain_remove_generic_chips(). The devres action mvebu_gpio_remove_irq_domain() only called irq_domain_remove(), which only frees the generic chips if IRQ_DOMAIN_FLAG_DESTROY_GC is set. Call irq_domain_remove_generic_chips() explicitly before irq_domain_remove() instead. Fixes: 812d47889a8e ("gpio/mvebu: Use irq_domain_add_linear") Assisted-by: opencode:big-pickle Signed-off-by: Rosen Penev Link: https://patch.msgid.link/20260707232358.1218077-1-rosenp@gmail.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-mvebu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c index 689dc6354c2d..a010604e5ff7 100644 --- a/drivers/gpio/gpio-mvebu.c +++ b/drivers/gpio/gpio-mvebu.c @@ -1110,6 +1110,7 @@ static void mvebu_gpio_remove_irq_domain(void *data) { struct irq_domain *domain = data; + irq_domain_remove_generic_chips(domain); irq_domain_remove(domain); } From c7eaea5c6eeb391d445583fa6419c957ca74a86b Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 18 Jun 2026 16:33:14 +0200 Subject: [PATCH 0753/1054] usb: ucsi: huawei_gaokun: move typec_altmode off stack The typec_altmode structure contains a 'struct device' object that cannot be allocated on the stack because of its size, even when ignoring the lifetime rules: drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c:326:13: error: stack frame size (1456) exceeds limit (1280) in 'gaokun_ucsi_usb_notify_ind' [-Werror,-Wframe-larger-than] 326 | static void gaokun_ucsi_usb_notify_ind(struct gaokun_ucsi *uec) Since the altmode is always associated with a port here, move it into the port object and avoid at least the stack allocation issue. Fixes: 1c2b66a7d725 ("usb: ucsi: huawei_gaokun: support mode switching") Signed-off-by: Arnd Bergmann Reviewed-by: Pengyu Luo Link: https://patch.msgid.link/20260618143341.1900221-1-arnd@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c b/drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c index ad669d2f8b9c..ca1b534cb183 100644 --- a/drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c +++ b/drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c @@ -84,6 +84,8 @@ struct gaokun_ucsi_port { struct auxiliary_device *bridge; struct typec_mux *typec_mux; + struct typec_mux_state state; + struct typec_altmode dp_alt; int idx; enum gaokun_ucsi_ccx ccx; @@ -292,24 +294,22 @@ static int gaokun_ucsi_refresh(struct gaokun_ucsi *uec) static void gaokun_ucsi_handle_usb_mode(struct gaokun_ucsi_port *port) { struct gaokun_ucsi *uec = port->ucsi; - struct typec_mux_state state = {}; - struct typec_altmode dp_alt = {}; int idx = port->idx, ret; /* * For every typec port on this platform, the only mode-switch is * controlled by its qmp combo phy which consumes svid and mode only. */ - dp_alt.svid = port->svid; - state.mode = port->mode; - state.alt = &dp_alt; + port->dp_alt.svid = port->svid; + port->state.mode = port->mode; + port->state.alt = &port->dp_alt; if (idx >= uec->num_ports) { dev_warn(uec->dev, "altmode port out of range: %d\n", idx); return; } - ret = typec_mux_set(port->typec_mux, &state); + ret = typec_mux_set(port->typec_mux, &port->state); if (ret) dev_err(uec->dev, "failed to set mux %d\n", ret); From 7b19c0f81ed1fdaec6bc522569be367199a9edf3 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sun, 5 Jul 2026 18:17:54 +0000 Subject: [PATCH 0754/1054] ipv4: igmp: Fix potential UAF in igmp_gq_start_timer() A race condition exists between device teardown (inetdev_destroy) and incoming IGMP query processing (igmp_rcv), leading to a Use-After-Free in the IGMP timer callback. During device destruction, inetdev_destroy() drops the primary reference to in_device, which can drop its refcount to 0. The actual freeing of in_device memory is deferred via RCU (using call_rcu()). Concurrently, igmp_rcv() runs under RCU read lock and obtains the in_device pointer. Because the memory is RCU-protected, CPU-0 can safely dereference in_device even if its refcount has hit 0. However, if CPU-0 calls igmp_gq_start_timer() and re-arms the timer, it attempts to acquire a reference using in_dev_hold(). This increments the refcount from 0 to 1, triggering a "refcount_t: addition on 0" warning. Since the in_device memory is still scheduled to be freed after the RCU grace period (as the free callback does not check the refcount again), the device is freed while the timer is still armed. When the timer expires, it accesses the freed memory, causing a kernel panic. Fix this by using refcount_inc_not_zero() (via a new helper in_dev_hold_safe()) to prevent acquiring a reference if the device is already being destroyed. If the refcount is 0, we do not arm the timer. A similar issue in IPv6 MLD is fixed in a subsequent patch. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reported-by: Zero Day Initiative Signed-off-by: Eric Dumazet Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260705181756.963063-2-edumazet@google.com Signed-off-by: Paolo Abeni --- include/linux/inetdevice.h | 5 +++++ net/ipv4/igmp.c | 14 +++++++++----- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/include/linux/inetdevice.h b/include/linux/inetdevice.h index dccbeb25f701..6032eea2539a 100644 --- a/include/linux/inetdevice.h +++ b/include/linux/inetdevice.h @@ -293,6 +293,11 @@ static inline void in_dev_put(struct in_device *idev) #define __in_dev_put(idev) refcount_dec(&(idev)->refcnt) #define in_dev_hold(idev) refcount_inc(&(idev)->refcnt) +static inline bool in_dev_hold_safe(struct in_device *idev) +{ + return refcount_inc_not_zero(&idev->refcnt); +} + #endif /* __KERNEL__ */ static __inline__ __be32 inet_make_mask(int logmask) diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index d520ea4f6d14..3a1cb2a827f3 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -248,16 +248,20 @@ static void igmp_gq_start_timer(struct in_device *in_dev) return; in_dev->mr_gq_running = 1; - if (!mod_timer(&in_dev->mr_gq_timer, exp)) - in_dev_hold(in_dev); + if (in_dev_hold_safe(in_dev)) { + if (mod_timer(&in_dev->mr_gq_timer, exp)) + in_dev_put(in_dev); + } } static void igmp_ifc_start_timer(struct in_device *in_dev, int delay) { - int tv = get_random_u32_below(delay); + if (in_dev_hold_safe(in_dev)) { + int tv = get_random_u32_below(delay); - if (!mod_timer(&in_dev->mr_ifc_timer, jiffies+tv+2)) - in_dev_hold(in_dev); + if (mod_timer(&in_dev->mr_ifc_timer, jiffies + tv + 2)) + in_dev_put(in_dev); + } } static void igmp_mod_timer(struct ip_mc_list *im, int max_delay) From 9b26518b6896a16b809b1e42986f4ebac7bccc1e Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sun, 5 Jul 2026 18:17:55 +0000 Subject: [PATCH 0755/1054] ipv6: mcast: Fix potential UAF in MLD delayed work A race condition exists between device teardown and incoming MLD query processing, leading to a Use-After-Free in the MLD delayed work. During device destruction, the primary reference to inet6_dev is dropped, which can drop its refcount to 0. The actual freeing of inet6_dev memory is deferred via RCU. Concurrently, the packet receive path runs under RCU read lock and obtains the inet6_dev pointer. Because the memory is RCU-protected, CPU-0 can safely dereference inet6_dev even if its refcount has hit 0. However, if CPU-0 calls igmp6_event_query() and schedules delayed work, it attempts to acquire a reference using in6_dev_hold(). This increments the refcount from 0 to 1, triggering a "refcount_t: addition on 0" warning. Since the inet6_dev memory is still scheduled to be freed after the RCU grace period, the device is freed while the work is still scheduled. When the work runs, it accesses the freed memory, causing a kernel panic. Fix this by using refcount_inc_not_zero() (via a new helper in6_dev_hold_safe()) to prevent acquiring a reference if the device is already being destroyed. If the refcount is 0, we do not schedule the work. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Eric Dumazet Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260705181756.963063-3-edumazet@google.com Signed-off-by: Paolo Abeni --- include/net/addrconf.h | 5 +++++ net/ipv6/mcast.c | 40 ++++++++++++++++++++++++++++------------ 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/include/net/addrconf.h b/include/net/addrconf.h index 539bbbe54b14..8ced27a8229b 100644 --- a/include/net/addrconf.h +++ b/include/net/addrconf.h @@ -446,6 +446,11 @@ static inline void in6_dev_hold(struct inet6_dev *idev) refcount_inc(&idev->refcnt); } +static inline bool in6_dev_hold_safe(struct inet6_dev *idev) +{ + return refcount_inc_not_zero(&idev->refcnt); +} + /* called with rcu_read_lock held */ static inline bool ip6_ignore_linkdown(const struct net_device *dev) { diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index 04b811b3be97..4d2b9377ba2d 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -1083,8 +1083,10 @@ static void mld_gq_start_work(struct inet6_dev *idev) mc_assert_locked(idev); idev->mc_gq_running = 1; - if (!mod_delayed_work(mld_wq, &idev->mc_gq_work, tv + 2)) - in6_dev_hold(idev); + if (in6_dev_hold_safe(idev)) { + if (mod_delayed_work(mld_wq, &idev->mc_gq_work, tv + 2)) + in6_dev_put(idev); + } } static void mld_gq_stop_work(struct inet6_dev *idev) @@ -1102,8 +1104,10 @@ static void mld_ifc_start_work(struct inet6_dev *idev, unsigned long delay) mc_assert_locked(idev); - if (!mod_delayed_work(mld_wq, &idev->mc_ifc_work, tv + 2)) - in6_dev_hold(idev); + if (in6_dev_hold_safe(idev)) { + if (mod_delayed_work(mld_wq, &idev->mc_ifc_work, tv + 2)) + in6_dev_put(idev); + } } static void mld_ifc_stop_work(struct inet6_dev *idev) @@ -1121,8 +1125,10 @@ static void mld_dad_start_work(struct inet6_dev *idev, unsigned long delay) mc_assert_locked(idev); - if (!mod_delayed_work(mld_wq, &idev->mc_dad_work, tv + 2)) - in6_dev_hold(idev); + if (in6_dev_hold_safe(idev)) { + if (mod_delayed_work(mld_wq, &idev->mc_dad_work, tv + 2)) + in6_dev_put(idev); + } } static void mld_dad_stop_work(struct inet6_dev *idev) @@ -1395,18 +1401,23 @@ static void mld_process_v2(struct inet6_dev *idev, struct mld2_query *mld, void igmp6_event_query(struct sk_buff *skb) { struct inet6_dev *idev = __in6_dev_get(skb->dev); + bool put = false; if (!idev || idev->dead) goto out; spin_lock_bh(&idev->mc_query_lock); - if (skb_queue_len(&idev->mc_query_queue) < MLD_MAX_SKBS) { + if (skb_queue_len(&idev->mc_query_queue) < MLD_MAX_SKBS && + in6_dev_hold_safe(idev)) { __skb_queue_tail(&idev->mc_query_queue, skb); - if (!mod_delayed_work(mld_wq, &idev->mc_query_work, 0)) - in6_dev_hold(idev); + if (mod_delayed_work(mld_wq, &idev->mc_query_work, 0)) + put = true; skb = NULL; } spin_unlock_bh(&idev->mc_query_lock); + + if (put) + in6_dev_put(idev); out: kfree_skb(skb); } @@ -1570,18 +1581,23 @@ static void mld_query_work(struct work_struct *work) void igmp6_event_report(struct sk_buff *skb) { struct inet6_dev *idev = __in6_dev_get(skb->dev); + bool put = false; if (!idev || idev->dead) goto out; spin_lock_bh(&idev->mc_report_lock); - if (skb_queue_len(&idev->mc_report_queue) < MLD_MAX_SKBS) { + if (skb_queue_len(&idev->mc_report_queue) < MLD_MAX_SKBS && + in6_dev_hold_safe(idev)) { __skb_queue_tail(&idev->mc_report_queue, skb); - if (!mod_delayed_work(mld_wq, &idev->mc_report_work, 0)) - in6_dev_hold(idev); + if (mod_delayed_work(mld_wq, &idev->mc_report_work, 0)) + put = true; skb = NULL; } spin_unlock_bh(&idev->mc_report_lock); + + if (put) + in6_dev_put(idev); out: kfree_skb(skb); } From 3546deaa0c30a14c7cdb5dc8f2432cb428f0cd36 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sun, 5 Jul 2026 18:17:56 +0000 Subject: [PATCH 0756/1054] ipv4: igmp: Fix potential memory leaks in igmp_mod_timer() and igmp_stop_timer() When a timer is deleted and not re-armed in igmp_mod_timer(), or stopped in igmp_stop_timer(), the code currently decrements the reference counter of the multicast list entry @im using refcount_dec(&im->refcnt). However, both functions can be called from the RCU reader path: - igmp_mod_timer() via igmp_heard_query() -> for_each_pmc_rcu() - igmp_stop_timer() via igmp_rcv() -> igmp_heard_report() If the group im was concurrently removed from the list by ip_mc_dec_group(), its reference count might have already been decremented to 1. In this case, timer_delete() succeeds, and refcount_dec() decrements the refcount from 1 to 0. Since refcount_dec() does not free the object when it hits 0 (unlike ip_ma_put()), the im structure is leaked. Fix this by using ip_ma_put(im) instead of refcount_dec(&im->refcnt), and deferring the put until after the spinlock is released. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Eric Dumazet Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260705181756.963063-4-edumazet@google.com Signed-off-by: Paolo Abeni --- net/ipv4/igmp.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index 3a1cb2a827f3..bb2d4441a492 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -217,13 +217,18 @@ static void ip_sf_list_clear_all(struct ip_sf_list *psf) static void igmp_stop_timer(struct ip_mc_list *im) { + bool put = false; + spin_lock_bh(&im->lock); if (timer_delete(&im->timer)) - refcount_dec(&im->refcnt); + put = true; WRITE_ONCE(im->tm_running, 0); WRITE_ONCE(im->reporter, 0); im->unsolicit_count = 0; spin_unlock_bh(&im->lock); + + if (put) + ip_ma_put(im); } /* It must be called with locked im->lock */ @@ -266,6 +271,8 @@ static void igmp_ifc_start_timer(struct in_device *in_dev, int delay) static void igmp_mod_timer(struct ip_mc_list *im, int max_delay) { + bool put = false; + spin_lock_bh(&im->lock); im->unsolicit_count = 0; if (timer_delete(&im->timer)) { @@ -275,10 +282,13 @@ static void igmp_mod_timer(struct ip_mc_list *im, int max_delay) spin_unlock_bh(&im->lock); return; } - refcount_dec(&im->refcnt); + put = true; } igmp_start_timer(im, max_delay); spin_unlock_bh(&im->lock); + + if (put) + ip_ma_put(im); } From f47b6b313df9a17010a72b3f389f0a5e9e49db3b Mon Sep 17 00:00:00 2001 From: Greg Ungerer Date: Tue, 7 Jul 2026 22:17:14 +1000 Subject: [PATCH 0757/1054] m68k: coldfire: fix breakage of missed IO access updates Commit e1f3a00670d1 ("m68k: coldfire: use ColdFire specifc IO access in SoC code") incorrectly updated a couple of local IO access uses. They use "read8" when they should be using the new "mcf_read8". Fix them. This causes compile time breakage for two specific SoC types, the ColdFire 5235 and 5282. They got missed in original testing due to not having any defconfigs for these specific parts. Fixes: e1f3a00670d1 ("m68k: coldfire: use ColdFire specifc IO access in SoC code") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202607040536.BiSGmESw-lkp@intel.com/ Acked-by: Arnd Bergmann Signed-off-by: Greg Ungerer --- arch/m68k/coldfire/m523x.c | 2 +- arch/m68k/coldfire/m528x.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/m68k/coldfire/m523x.c b/arch/m68k/coldfire/m523x.c index 11d7423ef646..ec04c32bb03c 100644 --- a/arch/m68k/coldfire/m523x.c +++ b/arch/m68k/coldfire/m523x.c @@ -79,7 +79,7 @@ static void __init m523x_i2c_init(void) static void __init m523x_fec_init(void) { /* Set multi-function pins to ethernet use */ - mcf_write8(read8(MCFGPIO_PAR_FECI2C) | 0xf0, MCFGPIO_PAR_FECI2C); + mcf_write8(mcf_read8(MCFGPIO_PAR_FECI2C) | 0xf0, MCFGPIO_PAR_FECI2C); } /***************************************************************************/ diff --git a/arch/m68k/coldfire/m528x.c b/arch/m68k/coldfire/m528x.c index b244c9ba40a7..3383b1ba106a 100644 --- a/arch/m68k/coldfire/m528x.c +++ b/arch/m68k/coldfire/m528x.c @@ -113,11 +113,11 @@ void wildfiremod_halt(void) mcf_write16(read16(MCFGPIO_PEPAR) & ~(1 << (5 * 2)), MCFGPIO_PEPAR); /* Make portE.5 an output */ - mcf_write8(read8(MCFGPIO_PDDR_E) | (1 << 5), MCFGPIO_PDDR_E); + mcf_write8(mcf_read8(MCFGPIO_PDDR_E) | (1 << 5), MCFGPIO_PDDR_E); /* Now toggle portE.5 from low to high */ - mcf_write8(read8(MCFGPIO_PODR_E) & ~(1 << 5), MCFGPIO_PODR_E); - mcf_write8(read8(MCFGPIO_PODR_E) | (1 << 5), MCFGPIO_PODR_E); + mcf_write8(mcf_read8(MCFGPIO_PODR_E) & ~(1 << 5), MCFGPIO_PODR_E); + mcf_write8(mcf_read8(MCFGPIO_PODR_E) | (1 << 5), MCFGPIO_PODR_E); printk(KERN_EMERG "Failed to hibernate. Halting!\n"); } From 2094dab19d45c487285617b7b68913d0cc0c1211 Mon Sep 17 00:00:00 2001 From: Weiming Shi Date: Thu, 2 Jul 2026 09:20:00 -0700 Subject: [PATCH 0758/1054] xfs: fail recovery on a committed log item with no regions If the first op of a transaction is a bare transaction header (len == sizeof(struct xfs_trans_header)), xlog_recover_add_to_trans() adds an item but no region, leaving it on r_itemq with ri_cnt == 0 and ri_buf == NULL. The header can be split across op records, so later ops may still add regions; the item is only invalid if the transaction commits with none. The runtime commit path never emits such a transaction, so this only happens on a crafted log. It came from an AI-assisted code audit of the recovery parser. xlog_recover_reorder_trans() calls ITEM_TYPE() on the item, which reads *(unsigned short *)item->ri_buf[0].iov_base and faults on the NULL ri_buf. Reject it there, before the commit handlers that also read ri_buf[0]. KASAN: null-ptr-deref in range [0x0000000000000000-0x0000000000000007] RIP: 0010:xlog_recover_reorder_trans (fs/xfs/xfs_log_recover.c:1836) xlog_recover_commit_trans (fs/xfs/xfs_log_recover.c:2043) xlog_recover_process_data (fs/xfs/xfs_log_recover.c:2501) xlog_do_recovery_pass (fs/xfs/xfs_log_recover.c:3244) xlog_recover (fs/xfs/xfs_log_recover.c:3493) xfs_log_mount (fs/xfs/xfs_log.c:618) xfs_mountfs (fs/xfs/xfs_mount.c:1034) xfs_fs_fill_super (fs/xfs/xfs_super.c:1938) vfs_get_tree (fs/super.c:1695) path_mount (fs/namespace.c:4161) __x64_sys_mount (fs/namespace.c:4367) Fixes: 89cebc847729 ("xfs: validate transaction header length on log recovery") Cc: stable@vger.kernel.org # v4.3 Reported-by: Xiang Mei Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Weiming Shi Reviewed-by: Christoph Hellwig Reviewed-by: "Darrick J. Wong" Signed-off-by: Carlos Maiolino --- fs/xfs/xfs_log_recover.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c index 103b2a79667b..fdb011e6ef60 100644 --- a/fs/xfs/xfs_log_recover.c +++ b/fs/xfs/xfs_log_recover.c @@ -1907,6 +1907,15 @@ xlog_recover_reorder_trans( list_for_each_entry_safe(item, n, &sort_list, ri_list) { enum xlog_recover_reorder fate = XLOG_REORDER_ITEM_LIST; + /* a committed item with no regions has a NULL ri_buf[0] */ + if (!item->ri_cnt || !item->ri_buf) { + xfs_warn(log->l_mp, + "%s: committed log item has no regions", + __func__); + error = -EFSCORRUPTED; + break; + } + item->ri_ops = xlog_find_item_ops(item); if (!item->ri_ops) { xfs_warn(log->l_mp, From d128ffd2baa708da0362b562f0b7f9df96e43653 Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Sun, 5 Jul 2026 12:23:58 +0800 Subject: [PATCH 0759/1054] xfs: tie zoned sysfs lifetime to zone info The zoned sysfs directory is currently registered as part of the generic per-mount sysfs setup, but the data exposed by nr_open_zones has a narrower lifetime. mp->m_zone_info is allocated by xfs_mount_zones() and freed by xfs_unmount_zones(), while the zoned sysfs kobject remained registered until xfs_mount_sysfs_del(). A read of nr_open_zones can therefore enter through the still-live sysfs kobject after xfs_unmount_zones() has freed mp->m_zone_info, leading to a use-after-free in nr_open_zones_show(). Make the zoned sysfs lifetime match the zone-info lifetime inside the zone allocator. Create the zoned sysfs directory from xfs_mount_zones() after the zone allocator has finished setting up, and remove it as the first step of xfs_unmount_zones(), before any zone allocator teardown can free m_zone_info. Sysfs removal deactivates the kernfs nodes and waits for active callbacks to drain before returning, so this also protects a reader that has already entered nr_open_zones_show() but has not yet dereferenced m_zone_info. Validation reproduced this kernel report: BUG: KASAN: slab-use-after-free in nr_open_zones_show+0x86/0x90 The buggy address belongs to the object at ffff88810b177800 which belongs to the cache kmalloc-1k of size 1024 The buggy address is located 160 bytes inside of freed 1024-byte region [ffff88810b177800, ffff88810b177c00) Read of size 4 Call trace: print_report+0xcd/0x620 nr_open_zones_show+0x86/0x90 (fs/xfs/xfs_sysfs.c:724) srso_alias_return_thunk+0x5/0xfbef5 __virt_addr_valid+0x20c/0x410 kasan_report+0xdd/0x110 sysfs_kf_seq_show+0x1bd/0x380 seq_read_iter+0x40f/0x11b0 lock_release+0xba/0x260 mark_held_locks+0x40/0x70 vfs_read+0x717/0xce0 __up_read+0x319/0x900 ksys_read+0xf8/0x1c0 do_user_addr_fault+0x3d0/0xbc0 trace_hardirqs_on_prepare+0x23/0xf0 do_syscall_64+0xc8/0x530 (arch/x86/entry/syscall_64.c:87) entry_SYSCALL_64_after_hwframe+0x74/0x7c Allocated by task stack: kasan_save_stack+0x33/0x60 kasan_save_track+0x14/0x30 __kasan_kmalloc+0xaa/0xb0 __kmalloc_cache_noprof+0x205/0x460 xfs_mount_zones+0x34c/0x2650 xfs_mountfs+0x1b97/0x1eb0 xfs_fs_fill_super+0xf2b/0x18a0 get_tree_bdev_flags+0x310/0x590 vfs_get_tree+0x8d/0x2e0 __x64_sys_fsconfig+0x61c/0xbc0 do_syscall_64+0xc8/0x530 (arch/x86/entry/syscall_64.c:87) entry_SYSCALL_64_after_hwframe+0x74/0x7c Freed by task stack: kasan_save_stack+0x33/0x60 kasan_save_track+0x14/0x30 kasan_save_free_info+0x3b/0x60 __kasan_slab_free+0x5f/0x80 kfree+0x20e/0x4c0 xfs_unmountfs+0x2fd/0x390 xfs_fs_put_super+0x60/0x110 generic_shutdown_super+0x143/0x4b0 kill_block_super+0x3b/0x90 xfs_kill_sb+0x12/0x50 deactivate_locked_super+0xa7/0x160 cleanup_mnt+0x218/0x420 task_work_run+0x11a/0x1f0 exit_to_user_mode_loop+0x13c/0x4f0 do_syscall_64+0x4a9/0x530 (arch/x86/entry/syscall_64.c:87) entry_SYSCALL_64_after_hwframe+0x74/0x7c Fixes: 62c89988dc19 ("xfs: expose the number of open zones in sysfs") Assisted-by: Codex:gpt-5.5 Signed-off-by: Cen Zhang Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/xfs_sysfs.c | 28 +++++++++++++++++----------- fs/xfs/xfs_sysfs.h | 2 ++ fs/xfs/xfs_zone_alloc.c | 8 ++++++++ 3 files changed, 27 insertions(+), 11 deletions(-) diff --git a/fs/xfs/xfs_sysfs.c b/fs/xfs/xfs_sysfs.c index 676777064c2d..b62712187324 100644 --- a/fs/xfs/xfs_sysfs.c +++ b/fs/xfs/xfs_sysfs.c @@ -780,6 +780,23 @@ static const struct kobj_type xfs_zoned_ktype = { .default_groups = xfs_zoned_groups, }; +int +xfs_zoned_sysfs_init(struct xfs_mount *mp) +{ + if (!IS_ENABLED(CONFIG_XFS_RT) || !xfs_has_zoned(mp)) + return 0; + + return xfs_sysfs_init(&mp->m_zoned_kobj, &xfs_zoned_ktype, + &mp->m_kobj, "zoned"); +} + +void +xfs_zoned_sysfs_del(struct xfs_mount *mp) +{ + if (IS_ENABLED(CONFIG_XFS_RT) && xfs_has_zoned(mp)) + xfs_sysfs_del(&mp->m_zoned_kobj); +} + int xfs_mount_sysfs_init( struct xfs_mount *mp) @@ -820,14 +837,6 @@ xfs_mount_sysfs_init( if (error) goto out_remove_error_dir; - if (IS_ENABLED(CONFIG_XFS_RT) && xfs_has_zoned(mp)) { - /* .../xfs//zoned/ */ - error = xfs_sysfs_init(&mp->m_zoned_kobj, &xfs_zoned_ktype, - &mp->m_kobj, "zoned"); - if (error) - goto out_remove_error_dir; - } - return 0; out_remove_error_dir: @@ -846,9 +855,6 @@ xfs_mount_sysfs_del( struct xfs_error_cfg *cfg; int i, j; - if (IS_ENABLED(CONFIG_XFS_RT) && xfs_has_zoned(mp)) - xfs_sysfs_del(&mp->m_zoned_kobj); - for (i = 0; i < XFS_ERR_CLASS_MAX; i++) { for (j = 0; j < XFS_ERR_ERRNO_MAX; j++) { cfg = &mp->m_error_cfg[i][j]; diff --git a/fs/xfs/xfs_sysfs.h b/fs/xfs/xfs_sysfs.h index 1622fe80ad3e..25e5f8fae2f3 100644 --- a/fs/xfs/xfs_sysfs.h +++ b/fs/xfs/xfs_sysfs.h @@ -53,6 +53,8 @@ xfs_sysfs_del( } int xfs_mount_sysfs_init(struct xfs_mount *mp); +int xfs_zoned_sysfs_init(struct xfs_mount *mp); +void xfs_zoned_sysfs_del(struct xfs_mount *mp); void xfs_mount_sysfs_del(struct xfs_mount *mp); #endif /* __XFS_SYSFS_H__ */ diff --git a/fs/xfs/xfs_zone_alloc.c b/fs/xfs/xfs_zone_alloc.c index 08d8b34f467e..7d13fa7ab30a 100644 --- a/fs/xfs/xfs_zone_alloc.c +++ b/fs/xfs/xfs_zone_alloc.c @@ -21,6 +21,7 @@ #include "xfs_rtbitmap.h" #include "xfs_rtrmap_btree.h" #include "xfs_zone_alloc.h" +#include "xfs_sysfs.h" #include "xfs_zone_priv.h" #include "xfs_zones.h" #include "xfs_trace.h" @@ -1420,11 +1421,17 @@ xfs_mount_zones( if (error) goto out_free_zone_info; + error = xfs_zoned_sysfs_init(mp); + if (error) + goto out_zone_gc_unmount; + xfs_info(mp, "%u zones of %u blocks (%u max open zones)", mp->m_sb.sb_rgcount, iz.zone_capacity, mp->m_max_open_zones); trace_xfs_zones_mount(mp); return 0; +out_zone_gc_unmount: + xfs_zone_gc_unmount(mp); out_free_zone_info: xfs_free_zone_info(mp->m_zone_info); return error; @@ -1434,6 +1441,7 @@ void xfs_unmount_zones( struct xfs_mount *mp) { + xfs_zoned_sysfs_del(mp); xfs_zone_gc_unmount(mp); xfs_free_zone_info(mp->m_zone_info); } From f88caa121eac3b6a050262ac3de0c133ee17f7b2 Mon Sep 17 00:00:00 2001 From: Jinliang Zheng Date: Thu, 2 Jul 2026 19:56:11 +0800 Subject: [PATCH 0760/1054] xfs: use xfs_csn_t for xlog_cil_push_now() push_seq parameter The push_seq argument to xlog_cil_push_now() carries a CIL checkpoint sequence number, not a log sequence number (LSN). Change the parameter type from xfs_lsn_t to xfs_csn_t to correctly reflect its semantics and match the surrounding types. Both types are int64_t under the hood, so this is a type-annotation fix with no behavioural change. Signed-off-by: Jinliang Zheng Reviewed-by: "Darrick J. Wong" Signed-off-by: Carlos Maiolino --- fs/xfs/xfs_log_cil.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/xfs/xfs_log_cil.c b/fs/xfs/xfs_log_cil.c index edc368938f30..639f875a8fb2 100644 --- a/fs/xfs/xfs_log_cil.c +++ b/fs/xfs/xfs_log_cil.c @@ -1710,7 +1710,7 @@ xlog_cil_push_background( static void xlog_cil_push_now( struct xlog *log, - xfs_lsn_t push_seq, + xfs_csn_t push_seq, bool async) { struct xfs_cil *cil = log->l_cilp; From 53b7c271f06be4dd5cfc8c6ef552a8355c891a7f Mon Sep 17 00:00:00 2001 From: Shoichiro Miyamoto Date: Tue, 7 Jul 2026 20:23:58 +0900 Subject: [PATCH 0761/1054] smb: client: restrict implied bcc[0] exemption to responses without data area smb2_check_message() has a long-standing quirk that accepts a response whose calculated length is one byte larger than the bytes actually received ("server can return one byte more due to implied bcc[0]"). This was introduced to accommodate servers that omit the trailing bcc[0] overlap byte when no data area is present. However, the exemption is applied unconditionally, regardless of whether the command actually carries a data area (has_smb2_data_area[]). When a response with a data area is subject to the +1 exemption, the reported data can extend one byte beyond the bytes actually received, yet smb2_check_message() still accepts it. The subsequent decoder then reads past the end of the receive buffer. This is reachable during NEGOTIATE and SESSION_SETUP, before the session is established. The resulting out-of-bounds reads are visible under KASAN when mounting against a non-conforming server; both the SPNEGO/negTokenInit and the NTLMSSP challenge decoders are affected: BUG: KASAN: slab-out-of-bounds in asn1_ber_decoder+0x16a7/0x1b00 Read of size 1 at addr ffff8880084d67c0 by task mount.cifs/81 CPU: 1 UID: 0 PID: 81 Comm: mount.cifs Not tainted 7.1.0-rc6 #1 Call Trace: dump_stack_lvl+0x4e/0x70 print_report+0x157/0x4c9 kasan_report+0xce/0x100 asn1_ber_decoder+0x16a7/0x1b00 decode_negTokenInit+0x19/0x30 SMB2_negotiate+0x31d9/0x4c90 cifs_negotiate_protocol+0x1f2/0x3f0 cifs_get_smb_ses+0x93f/0x17e0 cifs_mount_get_session+0x7f/0x3a0 cifs_mount+0xb4/0xcf0 cifs_smb3_do_mount+0x23a/0x1500 smb3_get_tree+0x3b0/0x630 vfs_get_tree+0x82/0x2d0 fc_mount+0x10/0x1b0 path_mount+0x50d/0x1de0 __x64_sys_mount+0x20b/0x270 do_syscall_64+0xee/0x590 entry_SYSCALL_64_after_hwframe+0x77/0x7f Allocated by task 85: kmem_cache_alloc_noprof+0x106/0x380 mempool_alloc_noprof+0x116/0x1e0 cifs_small_buf_get+0x31/0x80 allocate_buffers+0x10d/0x2b0 cifs_demultiplex_thread+0x1d5/0x1d50 kthread+0x2c6/0x390 ret_from_fork+0x36e/0x5a0 ret_from_fork_asm+0x1a/0x30 The buggy address is located 0 bytes to the right of allocated 448-byte region [ffff8880084d6600, ffff8880084d67c0) which belongs to the cache cifs_small_rq of size 448 BUG: KASAN: slab-out-of-bounds in kmemdup_noprof+0x36/0x50 Read of size 329 at addr ffff88800726c678 by task mount.cifs/89 CPU: 0 UID: 0 PID: 89 Comm: mount.cifs Tainted: G B 7.1.0-rc6 #1 Call Trace: dump_stack_lvl+0x4e/0x70 print_report+0x157/0x4c9 kasan_report+0xce/0x100 kasan_check_range+0x10f/0x1e0 __asan_memcpy+0x23/0x60 kmemdup_noprof+0x36/0x50 decode_ntlmssp_challenge+0x457/0x680 SMB2_sess_auth_rawntlmssp_negotiate+0x6f0/0xcb0 SMB2_sess_setup+0x219/0x4f0 cifs_setup_session+0x248/0xaf0 cifs_get_smb_ses+0xf79/0x17e0 cifs_mount_get_session+0x7f/0x3a0 cifs_mount+0xb4/0xcf0 cifs_smb3_do_mount+0x23a/0x1500 smb3_get_tree+0x3b0/0x630 vfs_get_tree+0x82/0x2d0 fc_mount+0x10/0x1b0 path_mount+0x50d/0x1de0 __x64_sys_mount+0x20b/0x270 do_syscall_64+0xee/0x590 entry_SYSCALL_64_after_hwframe+0x77/0x7f Allocated by task 93: kmem_cache_alloc_noprof+0x106/0x380 mempool_alloc_noprof+0x116/0x1e0 cifs_small_buf_get+0x31/0x80 allocate_buffers+0x10d/0x2b0 cifs_demultiplex_thread+0x1d5/0x1d50 kthread+0x2c6/0x390 ret_from_fork+0x36e/0x5a0 ret_from_fork_asm+0x1a/0x30 The buggy address is located 120 bytes inside of allocated 448-byte region [ffff88800726c600, ffff88800726c7c0) which belongs to the cache cifs_small_rq of size 448 Restrict the +1 exemption to responses that have no data area, so that it still covers the bcc[0] omission it was meant for. When a data area is present, the +1 discrepancy instead means the reported data length overruns the received buffer, so the response must be rejected. Fixes: 093b2bdad322 ("CIFS: Make demultiplex_thread work with SMB2 code") Cc: stable@vger.kernel.org Signed-off-by: Shoichiro Miyamoto Signed-off-by: Steve French --- fs/smb/client/smb2misc.c | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/fs/smb/client/smb2misc.c b/fs/smb/client/smb2misc.c index 2a7355ce1a07..6270b33147d2 100644 --- a/fs/smb/client/smb2misc.c +++ b/fs/smb/client/smb2misc.c @@ -19,6 +19,8 @@ #include "nterr.h" #include "cached_dir.h" +static unsigned int __smb2_calc_size(void *buf, bool *have_data); + static int check_smb2_hdr(struct smb2_hdr *shdr, __u64 mid) { @@ -145,6 +147,7 @@ smb2_check_message(char *buf, unsigned int pdu_len, unsigned int len, int command; __u32 calc_len; /* calculated length */ __u64 mid; + bool have_data; /* If server is a channel, select the primary channel */ pserver = SERVER_IS_CHAN(server) ? server->primary_server : server; @@ -228,7 +231,8 @@ smb2_check_message(char *buf, unsigned int pdu_len, unsigned int len, } } - calc_len = smb2_calc_size(buf); + have_data = false; + calc_len = __smb2_calc_size(buf, &have_data); /* For SMB2_IOCTL, OutputOffset and OutputLength are optional, so might * be 0, and not a real miscalculation */ @@ -247,8 +251,13 @@ smb2_check_message(char *buf, unsigned int pdu_len, unsigned int len, /* Windows 7 server returns 24 bytes more */ if (calc_len + 24 == len && command == SMB2_OPLOCK_BREAK_HE) return 0; - /* server can return one byte more due to implied bcc[0] */ - if (calc_len == len + 1) + /* + * Server can return one byte more due to implied bcc[0]. + * Allow it only when there is no data area; if data_length > 0 + * the +1 gap indicates an overreported data length rather than + * the bcc[0] omission. + */ + if (calc_len == len + 1 && !have_data) return 0; /* @@ -409,14 +418,17 @@ smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *shdr) /* * Calculate the size of the SMB message based on the fixed header * portion, the number of word parameters and the data portion of the message. + * If have_data is non-NULL, it is set to true when a non-empty data area was + * found (data_length > 0), allowing callers to distinguish the implied bcc[0] + * case (no data area) from an overreported data length. */ -unsigned int -smb2_calc_size(void *buf) +static unsigned int +__smb2_calc_size(void *buf, bool *have_data) { struct smb2_pdu *pdu = buf; struct smb2_hdr *shdr = &pdu->hdr; int offset; /* the offset from the beginning of SMB to data area */ - int data_length; /* the length of the variable length data area */ + int data_length = 0; /* the length of the variable length data area */ /* Structure Size has already been checked to make sure it is 64 */ int len = le16_to_cpu(shdr->StructureSize); @@ -449,9 +461,17 @@ smb2_calc_size(void *buf) } calc_size_exit: cifs_dbg(FYI, "SMB2 len %d\n", len); + if (have_data) + *have_data = (data_length > 0); return len; } +unsigned int +smb2_calc_size(void *buf) +{ + return __smb2_calc_size(buf, NULL); +} + /* Note: caller must free return buffer */ __le16 * cifs_convert_path_to_utf16(const char *from, struct cifs_sb_info *cifs_sb) From 3b08fed5b7e0d5e3a25d73ef3ba09cd33ade16c9 Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Sun, 5 Jul 2026 16:36:29 -0700 Subject: [PATCH 0762/1054] netfilter: nf_conntrack_reasm: guard mac_header adjustment after IPv6 defrag nf_ct_frag6_reasm() slides the packet head forward to drop the IPv6 fragment header and then unconditionally advances skb->mac_header: skb->mac_header += sizeof(struct frag_hdr); On the NF_INET_LOCAL_OUT defrag path the skb has no link-layer header yet, so skb->mac_header is still the "not set" sentinel (u16)~0U. Adding sizeof(struct frag_hdr) wraps it to a small value (0xffff + 8 == 7), after which skb_mac_header_was_set() wrongly reports a MAC header is present and skb_mac_header() points into the headroom. The reassembler has done this unconditional add since it was introduced; it was harmless while mac_header was a bare pointer, but wrong once mac_header became a u16 offset whose unset state is the ~0U sentinel tested by skb_mac_header_was_set(). The sibling net/ipv6/reassembly.c does the same relocation and does guard the adjustment; mirror the guard here. Fixes: 9fb9cbb1082d ("[NETFILTER]: Add nf_conntrack subsystem.") Cc: stable@vger.kernel.org Reported-by: Weiming Shi Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Xiang Mei Signed-off-by: Florian Westphal --- net/ipv6/netfilter/nf_conntrack_reasm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c index 64ab23ff559b..3637b20d3fa4 100644 --- a/net/ipv6/netfilter/nf_conntrack_reasm.c +++ b/net/ipv6/netfilter/nf_conntrack_reasm.c @@ -348,7 +348,8 @@ static int nf_ct_frag6_reasm(struct frag_queue *fq, struct sk_buff *skb, skb_network_header(skb)[fq->nhoffset] = skb_transport_header(skb)[0]; memmove(skb->head + sizeof(struct frag_hdr), skb->head, (skb->data - skb->head) - sizeof(struct frag_hdr)); - skb->mac_header += sizeof(struct frag_hdr); + if (skb_mac_header_was_set(skb)) + skb->mac_header += sizeof(struct frag_hdr); skb->network_header += sizeof(struct frag_hdr); skb_reset_transport_header(skb); From a622d2e9608c9dff47fc2e5759ac7aa3a836b45d Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Sun, 5 Jul 2026 14:58:00 -0700 Subject: [PATCH 0763/1054] netfilter: ebtables: terminate table name before find_table_lock() update_counters() and compat_update_counters() forward a user-supplied 32-byte table name to find_table_lock() without NUL-terminating it. On a lookup miss, find_inlist_lock() calls try_then_request_module(..., "%s%s", "ebtable_", name), and vsnprintf() reads past the name field and the stack object until it hits a zero byte. BUG: KASAN: stack-out-of-bounds in string (lib/vsprintf.c:648 lib/vsprintf.c:730) Read of size 1 at addr ffff8880119dfb20 by task exploit/147 Call Trace: ... string (lib/vsprintf.c:648 lib/vsprintf.c:730) vsnprintf (lib/vsprintf.c:2945) __request_module (kernel/module/kmod.c:150) do_update_counters.isra.0 (net/bridge/netfilter/ebtables.c:371 net/bridge/netfilter/ebtables.c:380) update_counters (net/bridge/netfilter/ebtables.c:1440) do_ebt_set_ctl (net/bridge/netfilter/ebtables.c:2573) nf_setsockopt (net/netfilter/nf_sockopt.c:101) ip_setsockopt (net/ipv4/ip_sockglue.c:1424) raw_setsockopt (net/ipv4/raw.c:847) __sys_setsockopt (net/socket.c:2393) ... compat_do_replace() shares the same unterminated name via compat_copy_ebt_replace_from_user(); terminate it there too so all find_table_lock() callers behave alike. The other callers already terminate the name after the copy. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Fixes: 81e675c227ec ("netfilter: ebtables: add CONFIG_COMPAT support") Cc: stable@vger.kernel.org Reported-by: Weiming Shi Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Xiang Mei Signed-off-by: Florian Westphal --- net/bridge/netfilter/ebtables.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index f20c039e44c8..5b74ff827493 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -1434,6 +1434,8 @@ static int update_counters(struct net *net, sockptr_t arg, unsigned int len) if (copy_from_sockptr(&hlp, arg, sizeof(hlp))) return -EFAULT; + hlp.name[sizeof(hlp.name) - 1] = '\0'; + if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter)) return -EINVAL; @@ -2273,6 +2275,8 @@ static int compat_copy_ebt_replace_from_user(struct ebt_replace *repl, memcpy(repl, &tmp, offsetof(struct ebt_replace, hook_entry)); + repl->name[sizeof(repl->name) - 1] = '\0'; + /* starting with hook_entry, 32 vs. 64 bit structures are different */ for (i = 0; i < NF_BR_NUMHOOKS; i++) repl->hook_entry[i] = compat_ptr(tmp.hook_entry[i]); @@ -2395,6 +2399,8 @@ static int compat_update_counters(struct net *net, sockptr_t arg, if (copy_from_sockptr(&hlp, arg, sizeof(hlp))) return -EFAULT; + hlp.name[sizeof(hlp.name) - 1] = '\0'; + /* try real handler in case userland supplied needed padding */ if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter)) return update_counters(net, arg, len); From cbfe53599eebffd188938ab6774cc41794f6f9d5 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Sat, 4 Jul 2026 10:23:31 +0200 Subject: [PATCH 0764/1054] netfilter: ebtables: zero chainstack array sashiko reports: looking at ebtables table translation, could a sparse cpu_possible_mask lead to an uninitialized pointer free? If cpu_possible_mask is sparse (for example, CPU 0 and CPU 2 are possible, but CPU 1 is not), the allocation loop skips CPU 1. If vmalloc_node() fails at CPU 2, the cleanup loop will blindly decrement and call vfree() on newinfo->chainstack[1]. Not a real-world bug, such allocation isn't expected to fail in the first place. Cc: stable@vger.kernel.org Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Florian Westphal --- net/bridge/netfilter/ebtables.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index 5b74ff827493..48187598cdd0 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -921,8 +921,7 @@ static int translate_table(struct net *net, const char *name, * if an error occurs */ newinfo->chainstack = - vmalloc_array(nr_cpu_ids, - sizeof(*(newinfo->chainstack))); + vcalloc(nr_cpu_ids, sizeof(*(newinfo->chainstack))); if (!newinfo->chainstack) return -ENOMEM; for_each_possible_cpu(i) { From 084d23f818321390509e9738a0b08bbf46df6425 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Sat, 4 Jul 2026 12:05:15 +0200 Subject: [PATCH 0765/1054] netfilter: ebtables: module names must be null-terminated We need to explicitly check the length, else we may pass non-null terminated string to request_module(). Cc: stable@vger.kernel.org Fixes: bcf493428840 ("netfilter: ebtables: Fix extension lookup with identical name") Signed-off-by: Florian Westphal --- net/bridge/netfilter/ebtables.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index 48187598cdd0..96c9a8f57c87 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -403,6 +403,9 @@ ebt_check_match(struct ebt_entry_match *m, struct xt_mtchk_param *par, left - sizeof(struct ebt_entry_match) < m->match_size) return -EINVAL; + if (strnlen(m->u.name, XT_EXTENSION_MAXNAMELEN) == XT_EXTENSION_MAXNAMELEN) + return -EINVAL; + match = xt_find_match(NFPROTO_BRIDGE, m->u.name, m->u.revision); if (IS_ERR(match) || match->family != NFPROTO_BRIDGE) { if (!IS_ERR(match)) From 42e74d8f21ce448fe12ba3dd59db817b3e977f49 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Tue, 7 Jul 2026 14:02:40 -0400 Subject: [PATCH 0766/1054] selftests/user_events: Wait for deferred event teardown after unregister Unregistering a user event now defers the drop of the enabler's event reference (and the freeing of the enabler) past an RCU grace period. As a result DIAG_IOCSDEL can transiently fail with -EBUSY while that last reference is still being dropped, where it previously succeeded immediately. Two tests assumed the delete takes effect the instant the unregister returns: - abi_test "flags" deletes the event right after disabling it. - perf_test's fixture teardown clear() deletes __test_event before the next test registers the same name; a stale event makes the following registration fail with -EADDRINUSE. Retry the delete until it succeeds (or the event is already gone) with a bounded wait, matching the existing wait_for_delete() idiom in the same suite, so the tests are robust to the deferred teardown. Link: https://patch.msgid.link/20260707180240.2887081-1-michael.bommarito@gmail.com Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Michael Bommarito Signed-off-by: Steven Rostedt --- .../testing/selftests/user_events/abi_test.c | 29 ++++++++++++++++++- .../testing/selftests/user_events/perf_test.c | 26 +++++++++++++++-- 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/tools/testing/selftests/user_events/abi_test.c b/tools/testing/selftests/user_events/abi_test.c index 85892b3b719c..b71813eaf5c0 100644 --- a/tools/testing/selftests/user_events/abi_test.c +++ b/tools/testing/selftests/user_events/abi_test.c @@ -132,6 +132,33 @@ static int event_delete(void) return ret; } +/* + * Deleting an event drops its last reference, but an unregister may defer + * that put (and the freeing of the associated enabler) past an RCU grace + * period. The delete can therefore transiently fail with -EBUSY while the + * previous reference is still being dropped. Retry only on that transient + * failure; treat an already-deleted event (-ENOENT) as success and return + * any other error immediately rather than spinning for the full timeout. + */ +static int wait_for_event_delete(void) +{ + int i, ret; + + for (i = 0; i < 10000; ++i) { + ret = event_delete(); + + if (ret == 0 || errno == ENOENT) + return 0; + + if (errno != EBUSY) + return ret; + + usleep(1000); + } + + return ret; +} + static int reg_enable_multi(void *enable, int size, int bit, int flags, char *args) { @@ -262,7 +289,7 @@ TEST_F(user, flags) { ASSERT_TRUE(event_exists()); /* Ensure we can delete it */ - ASSERT_EQ(0, event_delete()); + ASSERT_EQ(0, wait_for_event_delete()); /* USER_EVENT_REG_MAX or above is not allowed */ ASSERT_EQ(-1, reg_enable_flags(&self->check, sizeof(int), 0, diff --git a/tools/testing/selftests/user_events/perf_test.c b/tools/testing/selftests/user_events/perf_test.c index cafec0e52eb3..5727cb5b914c 100644 --- a/tools/testing/selftests/user_events/perf_test.c +++ b/tools/testing/selftests/user_events/perf_test.c @@ -85,6 +85,7 @@ static int get_offset(void) static int clear(int *check) { struct user_unreg unreg = {0}; + int i, ret; unreg.size = sizeof(unreg); unreg.disable_bit = 31; @@ -99,13 +100,32 @@ static int clear(int *check) if (errno != ENOENT) return -1; - if (ioctl(fd, DIAG_IOCSDEL, "__test_event") == -1) - if (errno != ENOENT) + /* + * Deleting the event drops its last reference, but the unregister + * above defers that put (and the freeing of the enabler) past an RCU + * grace period. The delete can therefore transiently fail with -EBUSY + * until that reference is dropped. Retry for up to ~10 seconds so the + * event is actually gone before the next test registers the same name. + */ + for (i = 0; i < 10000; ++i) { + ret = ioctl(fd, DIAG_IOCSDEL, "__test_event"); + + if (ret == 0 || errno == ENOENT) { + ret = 0; + break; + } + + if (errno != EBUSY) { + close(fd); return -1; + } + + usleep(1000); + } close(fd); - return 0; + return ret; } FIXTURE(user) { From e6107a4c74b54cb33e3bce162a63048ae5a6b198 Mon Sep 17 00:00:00 2001 From: Tamaki Yanagawa Date: Fri, 3 Jul 2026 16:22:57 +0000 Subject: [PATCH 0767/1054] netfilter: nft_lookup: fix catchall element handling with inverted lookups nft_lookup_eval() decides whether a lookup matched (`found`) from the direct set lookup and priv->invert before falling back to the catchall element used by interval sets (e.g. nft_set_rbtree) for the open-ended default range. Since `found` is never recomputed after `ext` is replaced by the catchall lookup, inverted lookups (NFT_LOOKUP_F_INV, "!= @set") can wrongly match or wrongly skip the catchall element, producing the wrong verdict. Fold the catchall lookup into `ext` before computing `found`, matching the order already used by nft_objref_map_eval(). Fixes: aaa31047a6d2 ("netfilter: nftables: add catch-all set element support") Signed-off-by: Tamaki Yanagawa Assisted-by: Claude:claude-sonnet-5 Signed-off-by: Florian Westphal --- net/netfilter/nft_lookup.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/net/netfilter/nft_lookup.c b/net/netfilter/nft_lookup.c index ba512e94b402..19887439847d 100644 --- a/net/netfilter/nft_lookup.c +++ b/net/netfilter/nft_lookup.c @@ -103,13 +103,13 @@ void nft_lookup_eval(const struct nft_expr *expr, bool found; ext = nft_set_do_lookup(net, set, ®s->data[priv->sreg]); + if (!ext) + ext = nft_set_catchall_lookup(net, set); + found = !!ext ^ priv->invert; if (!found) { - ext = nft_set_catchall_lookup(net, set); - if (!ext) { - regs->verdict.code = NFT_BREAK; - return; - } + regs->verdict.code = NFT_BREAK; + return; } if (ext) { From 5d0c22e73656d050daffad10a2ba8765ce8441c8 Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Thu, 2 Jul 2026 15:46:57 +0200 Subject: [PATCH 0768/1054] netfilter: ipset: mark the rcu locked areas properly When we bump the uref counter, there's no need to keep the rcu lock because the referred hash table can't disappear. Also, from the same reason in mtype_gc we need the rcu lock and not a spinlock. Signed-off-by: Jozsef Kadlecsik Signed-off-by: Florian Westphal --- net/netfilter/ipset/ip_set_hash_gen.h | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h index dedf59b661dd..c9a071766243 100644 --- a/net/netfilter/ipset/ip_set_hash_gen.h +++ b/net/netfilter/ipset/ip_set_hash_gen.h @@ -569,9 +569,10 @@ mtype_gc(struct work_struct *work) set = gc->set; h = set->data; - spin_lock_bh(&set->lock); - t = ipset_dereference_set(h->table, set); + rcu_read_lock_bh(); + t = rcu_dereference_bh(h->table); atomic_inc(&t->uref); + rcu_read_unlock_bh(); numof_locks = ahash_numof_locks(t->htable_bits); r = gc->region++; if (r >= numof_locks) { @@ -580,7 +581,6 @@ mtype_gc(struct work_struct *work) next_run = (IPSET_GC_PERIOD(set->timeout) * HZ) / numof_locks; if (next_run < HZ/10) next_run = HZ/10; - spin_unlock_bh(&set->lock); mtype_gc_do(set, h, t, r); @@ -860,15 +860,13 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext, key = HKEY(value, h->initval, t->htable_bits); r = ahash_region(key); atomic_inc(&t->uref); + rcu_read_unlock_bh(); elements = t->hregion[r].elements; maxelem = t->maxelem; if (elements >= maxelem) { u32 e; - if (SET_WITH_TIMEOUT(set)) { - rcu_read_unlock_bh(); + if (SET_WITH_TIMEOUT(set)) mtype_gc_do(set, h, t, r); - rcu_read_lock_bh(); - } maxelem = h->maxelem; elements = 0; for (e = 0; e < ahash_numof_locks(t->htable_bits); e++) @@ -876,7 +874,6 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext, if (elements >= maxelem && SET_WITH_FORCEADD(set)) forceadd = true; } - rcu_read_unlock_bh(); spin_lock_bh(&t->hregion[r].lock); n = rcu_dereference_bh(hbucket(t, key)); From cffcf57bf03cb7f7e83d10f760b5f34e5c51d9b3 Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Thu, 2 Jul 2026 15:46:58 +0200 Subject: [PATCH 0769/1054] netfilter: ipset: exclude gc when resize is in progress Zhengchuan Liang and Eulgyu Kim reported that because resize does not copy the comment extension into the resized set but uses it's pointer, ongoing gc can free the extension in the original set which then results stale pointer in the resized one. The proposed patch was to recreate the extensions for every element in the resized set. It is both expensive and wastes memory, so better exclude gc when resizing in progress detected: resizing will destroy the original set anyway, so doing gc on it is unnecessary. Introduce a new spinlock to exclude parallel gc and resize. Because we just set and check a bool value, there's no need for the parameter to be atomic_t and rename it for better readability. Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Xin Liu Reported by: Zhengchuan Liang Reported by: Eulgyu Kim Signed-off-by: Jozsef Kadlecsik Signed-off-by: Florian Westphal --- net/netfilter/ipset/ip_set_hash_gen.h | 31 +++++++++++++++++---------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h index c9a071766243..8104dbac02fa 100644 --- a/net/netfilter/ipset/ip_set_hash_gen.h +++ b/net/netfilter/ipset/ip_set_hash_gen.h @@ -75,12 +75,13 @@ struct hbucket { struct htable_gc { struct delayed_work dwork; struct ip_set *set; /* Set the gc belongs to */ + spinlock_t lock; /* Lock to exclude gc and resize */ u32 region; /* Last gc run position */ }; /* The hash table: the table size stored here in order to make resizing easy */ struct htable { - atomic_t ref; /* References for resizing */ + bool resizing; /* Mark ongoing resize */ atomic_t uref; /* References for dumping and gc */ u8 htable_bits; /* size of hash table == 2^htable_bits */ u32 maxelem; /* Maxelem per region */ @@ -582,9 +583,12 @@ mtype_gc(struct work_struct *work) if (next_run < HZ/10) next_run = HZ/10; - mtype_gc_do(set, h, t, r); + spin_lock_bh(&gc->lock); + if (!t->resizing) + mtype_gc_do(set, h, t, r); + spin_unlock_bh(&gc->lock); - if (atomic_dec_and_test(&t->uref) && atomic_read(&t->ref)) { + if (atomic_dec_and_test(&t->uref) && t->resizing) { pr_debug("Table destroy after resize by expire: %p\n", t); mtype_ahash_destroy(set, t, false); } @@ -672,11 +676,13 @@ mtype_resize(struct ip_set *set, bool retried) spin_lock_init(&t->hregion[i].lock); /* There can't be another parallel resizing, - * but dumping, gc, kernel side add/del are possible + * but dumping and kernel side add/del are possible */ orig = ipset_dereference_bh_nfnl(h->table); - atomic_set(&orig->ref, 1); atomic_inc(&orig->uref); + spin_lock_bh(&h->gc.lock); + orig->resizing = true; + spin_unlock_bh(&h->gc.lock); pr_debug("attempt to resize set %s from %u to %u, t %p\n", set->name, orig->htable_bits, htable_bits, orig); for (r = 0; r < ahash_numof_locks(orig->htable_bits); r++) { @@ -792,7 +798,9 @@ mtype_resize(struct ip_set *set, bool retried) cleanup: rcu_read_unlock_bh(); - atomic_set(&orig->ref, 0); + spin_lock_bh(&h->gc.lock); + orig->resizing = false; + spin_unlock_bh(&h->gc.lock); atomic_dec(&orig->uref); mtype_ahash_destroy(set, t, false); if (ret == -EAGAIN) @@ -1000,7 +1008,7 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext, ret = 0; resize: spin_unlock_bh(&t->hregion[r].lock); - if (atomic_read(&t->ref) && ext->target) { + if (t->resizing && ext && ext->target) { /* Resize is in process and kernel side add, save values */ struct mtype_resize_ad *x; @@ -1027,7 +1035,7 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext, unlock: spin_unlock_bh(&t->hregion[r].lock); out: - if (atomic_dec_and_test(&t->uref) && atomic_read(&t->ref)) { + if (atomic_dec_and_test(&t->uref) && t->resizing) { pr_debug("Table destroy after resize by add: %p\n", t); mtype_ahash_destroy(set, t, false); } @@ -1090,7 +1098,7 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext, #endif ip_set_ext_destroy(set, data); - if (atomic_read(&t->ref) && ext->target) { + if (t->resizing && ext && ext->target) { /* Resize is in process and kernel side del, * save values */ @@ -1141,7 +1149,7 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext, list_add(&x->list, &h->ad); spin_unlock_bh(&set->lock); } - if (atomic_dec_and_test(&t->uref) && atomic_read(&t->ref)) { + if (atomic_dec_and_test(&t->uref) && t->resizing) { pr_debug("Table destroy after resize by del: %p\n", t); mtype_ahash_destroy(set, t, false); } @@ -1350,7 +1358,7 @@ mtype_uref(struct ip_set *set, struct netlink_callback *cb, bool start) rcu_read_unlock_bh(); } else if (cb->args[IPSET_CB_PRIVATE]) { t = (struct htable *)cb->args[IPSET_CB_PRIVATE]; - if (atomic_dec_and_test(&t->uref) && atomic_read(&t->ref)) { + if (atomic_dec_and_test(&t->uref) && t->resizing) { pr_debug("Table destroy after resize " " by dump: %p\n", t); mtype_ahash_destroy(set, t, false); @@ -1590,6 +1598,7 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set, return -ENOMEM; } h->gc.set = set; + spin_lock_init(&h->gc.lock); for (i = 0; i < ahash_numof_locks(hbits); i++) spin_lock_init(&t->hregion[i].lock); h->maxelem = maxelem; From 672321302ed682ccb903004f435bbdb353534a9c Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Thu, 2 Jul 2026 15:46:59 +0200 Subject: [PATCH 0770/1054] netfilter: ipset: cleanup the add/del backlog when resize failed Sashiko pointed out that the add/del backlog was not cleaned up when resize failed. Fix it in the corresponding error path. Also, make sure that the add/del backlog is htable-specific so when resize creates a new htable, old/new backlog can't be mixed up. Signed-off-by: Jozsef Kadlecsik Signed-off-by: Florian Westphal --- net/netfilter/ipset/ip_set_hash_gen.h | 28 +++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h index 8104dbac02fa..c0132d0f4cc0 100644 --- a/net/netfilter/ipset/ip_set_hash_gen.h +++ b/net/netfilter/ipset/ip_set_hash_gen.h @@ -85,6 +85,7 @@ struct htable { atomic_t uref; /* References for dumping and gc */ u8 htable_bits; /* size of hash table == 2^htable_bits */ u32 maxelem; /* Maxelem per region */ + struct list_head ad; /* Resize add|del backlist */ struct ip_set_region *hregion; /* Region locks and ext sizes */ struct hbucket __rcu *bucket[]; /* hashtable buckets */ }; @@ -302,7 +303,6 @@ struct htype { u8 netmask; /* netmask value for subnets to store */ union nf_inet_addr bitmask; /* stores bitmask */ #endif - struct list_head ad; /* Resize add|del backlist */ struct mtype_elem next; /* temporary storage for uadd */ #ifdef IP_SET_HASH_WITH_NETS struct net_prefixes nets[NLEN]; /* book-keeping of prefixes */ @@ -452,13 +452,14 @@ static void mtype_destroy(struct ip_set *set) { struct htype *h = set->data; + struct htable *t = (__force struct htable *)h->table; struct list_head *l, *lt; - mtype_ahash_destroy(set, (__force struct htable *)h->table, true); - list_for_each_safe(l, lt, &h->ad) { + list_for_each_safe(l, lt, &t->ad) { list_del(l); kfree(l); } + mtype_ahash_destroy(set, t, true); kfree(h); set->data = NULL; @@ -672,6 +673,7 @@ mtype_resize(struct ip_set *set, bool retried) } t->htable_bits = htable_bits; t->maxelem = h->maxelem / ahash_numof_locks(htable_bits); + INIT_LIST_HEAD(&t->ad); for (i = 0; i < ahash_numof_locks(htable_bits); i++) spin_lock_init(&t->hregion[i].lock); @@ -774,7 +776,7 @@ mtype_resize(struct ip_set *set, bool retried) * Kernel-side add cannot trigger a resize and userspace actions * are serialized by the mutex. */ - list_for_each_safe(l, lt, &h->ad) { + list_for_each_safe(l, lt, &orig->ad) { x = list_entry(l, struct mtype_resize_ad, list); if (x->ad == IPSET_ADD) { mtype_add(set, &x->d, &x->ext, &x->mext, x->flags); @@ -801,10 +803,21 @@ mtype_resize(struct ip_set *set, bool retried) spin_lock_bh(&h->gc.lock); orig->resizing = false; spin_unlock_bh(&h->gc.lock); + /* Make sure parallel readers see that orig->resizing is false + * before we decrement uref */ + synchronize_rcu(); atomic_dec(&orig->uref); mtype_ahash_destroy(set, t, false); if (ret == -EAGAIN) goto retry; + + /* Cleanup the backlog of ADD/DEL elements */ + spin_lock_bh(&set->lock); + list_for_each_safe(l, lt, &orig->ad) { + list_del(l); + kfree(l); + } + spin_unlock_bh(&set->lock); goto out; hbwarn: @@ -1022,7 +1035,7 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext, memcpy(&x->mext, mext, sizeof(struct ip_set_ext)); x->flags = flags; spin_lock_bh(&set->lock); - list_add_tail(&x->list, &h->ad); + list_add_tail(&x->list, &t->ad); spin_unlock_bh(&set->lock); } goto out; @@ -1146,7 +1159,7 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext, spin_unlock_bh(&t->hregion[r].lock); if (x) { spin_lock_bh(&set->lock); - list_add(&x->list, &h->ad); + list_add(&x->list, &t->ad); spin_unlock_bh(&set->lock); } if (atomic_dec_and_test(&t->uref) && t->resizing) { @@ -1625,9 +1638,8 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set, } t->htable_bits = hbits; t->maxelem = h->maxelem / ahash_numof_locks(hbits); + INIT_LIST_HEAD(&t->ad); RCU_INIT_POINTER(h->table, t); - - INIT_LIST_HEAD(&h->ad); set->data = h; #ifndef IP_SET_PROTO_UNDEF if (set->family == NFPROTO_IPV4) { From 724f32699aeabcbd294377904b40b456fd5c67eb Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Thu, 2 Jul 2026 15:47:00 +0200 Subject: [PATCH 0771/1054] netfilter: ipset: allocate the proper memory for the generic hash structure Because a single create function is emitted for every hash type, from the IPv4 and IPv6 generic hash structure definitions the last one, i.e. the IPv6 was in effect for IPv4 too. Use the proper size when allocating the structure. Comment properly that because create() refers to elements of the generic hash structure, all referred ones must come before the IPv4/IPv6 dependent 'next' member. Signed-off-by: Jozsef Kadlecsik Signed-off-by: Florian Westphal --- net/netfilter/ipset/ip_set_hash_gen.h | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h index c0132d0f4cc0..8231317b0f1f 100644 --- a/net/netfilter/ipset/ip_set_hash_gen.h +++ b/net/netfilter/ipset/ip_set_hash_gen.h @@ -303,10 +303,13 @@ struct htype { u8 netmask; /* netmask value for subnets to store */ union nf_inet_addr bitmask; /* stores bitmask */ #endif - struct mtype_elem next; /* temporary storage for uadd */ #ifdef IP_SET_HASH_WITH_NETS struct net_prefixes nets[NLEN]; /* book-keeping of prefixes */ #endif + /* Because 'next' is IPv4/IPv6 dependent, no elements of this + * structure and referred in create() may come after 'next'. + */ + struct mtype_elem next; /* temporary storage for uadd */ }; /* ADD|DEL entries saved during resize */ @@ -1584,7 +1587,13 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set, if (tb[IPSET_ATTR_MAXELEM]) maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]); - hsize = sizeof(*h); +#ifdef IP_SET_PROTO_UNDEF + hsize = sizeof(struct htype); +#else + hsize = set->family == NFPROTO_IPV6 ? + sizeof(struct IPSET_TOKEN(HTYPE, 6)) : + sizeof(struct IPSET_TOKEN(HTYPE, 4)); +#endif h = kzalloc(hsize, GFP_KERNEL); if (!h) return -ENOMEM; From c328b90c17fc5fa7786503695152880b2afb9326 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 30 Jun 2026 11:40:54 +0200 Subject: [PATCH 0772/1054] netfilter: flowtable: use dst in this direction when pushing IPIP header When pushing the IPIP header, the route of the other direction is used to calculate the headroom, use the route in this direction. Accessing the other tuple to set the IP source and destination is fine because this tuple does not provide such information to avoid storing redundant information. However, this tuple already provides the dst for this direction, this went unnoticed because this bug affects headroom and iph->frag_off only at this stage. Fixes: d30301ba4b07 ("netfilter: flowtable: Add IPIP tx sw acceleration") Fixes: 93cf357fa797 ("netfilter: flowtable: Add IP6IP6 tx sw acceleration") Cc: stable@vger.kernel.org Acked-by: Lorenzo Bianconi Signed-off-by: Pablo Neira Ayuso Signed-off-by: Florian Westphal --- net/netfilter/nf_flow_table_ip.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/net/netfilter/nf_flow_table_ip.c b/net/netfilter/nf_flow_table_ip.c index 29e93ac1e2e4..089f2bc19972 100644 --- a/net/netfilter/nf_flow_table_ip.c +++ b/net/netfilter/nf_flow_table_ip.c @@ -590,10 +590,10 @@ static int nf_flow_pppoe_push(struct sk_buff *skb, u16 id, static int nf_flow_tunnel_ipip_push(struct net *net, struct sk_buff *skb, struct flow_offload_tuple *tuple, - __be32 *ip_daddr) + struct dst_entry *dst, __be32 *ip_daddr) { struct iphdr *iph = (struct iphdr *)skb_network_header(skb); - struct rtable *rt = dst_rtable(tuple->dst_cache); + struct rtable *rt = dst_rtable(dst); u8 tos = iph->tos, ttl = iph->ttl; __be16 frag_off = iph->frag_off; u32 headroom = sizeof(*iph); @@ -636,21 +636,22 @@ static int nf_flow_tunnel_ipip_push(struct net *net, struct sk_buff *skb, static int nf_flow_tunnel_v4_push(struct net *net, struct sk_buff *skb, struct flow_offload_tuple *tuple, - __be32 *ip_daddr) + struct dst_entry *dst, __be32 *ip_daddr) { if (tuple->tun_num) - return nf_flow_tunnel_ipip_push(net, skb, tuple, ip_daddr); + return nf_flow_tunnel_ipip_push(net, skb, tuple, dst, ip_daddr); return 0; } static int nf_flow_tunnel_ip6ip6_push(struct net *net, struct sk_buff *skb, struct flow_offload_tuple *tuple, + struct dst_entry *dst, struct in6_addr **ip6_daddr) { struct ipv6hdr *ip6h = (struct ipv6hdr *)skb_network_header(skb); - struct rtable *rt = dst_rtable(tuple->dst_cache); __u8 dsfield = ipv6_get_dsfield(ip6h); + struct rtable *rt = dst_rtable(dst); struct flowi6 fl6 = { .daddr = tuple->tun.src_v6, .saddr = tuple->tun.dst_v6, @@ -696,10 +697,11 @@ static int nf_flow_tunnel_ip6ip6_push(struct net *net, struct sk_buff *skb, static int nf_flow_tunnel_v6_push(struct net *net, struct sk_buff *skb, struct flow_offload_tuple *tuple, + struct dst_entry *dst, struct in6_addr **ip6_daddr) { if (tuple->tun_num) - return nf_flow_tunnel_ip6ip6_push(net, skb, tuple, ip6_daddr); + return nf_flow_tunnel_ip6ip6_push(net, skb, tuple, dst, ip6_daddr); return 0; } @@ -842,7 +844,8 @@ nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb, other_tuple = &flow->tuplehash[!dir].tuple; ip_daddr = other_tuple->src_v4.s_addr; - if (nf_flow_tunnel_v4_push(state->net, skb, other_tuple, &ip_daddr) < 0) + if (nf_flow_tunnel_v4_push(state->net, skb, other_tuple, + tuplehash->tuple.dst_cache, &ip_daddr) < 0) return NF_DROP; switch (tuplehash->tuple.xmit_type) { @@ -1158,6 +1161,7 @@ nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb, ip6_daddr = &other_tuple->src_v6; if (nf_flow_tunnel_v6_push(state->net, skb, other_tuple, + tuplehash->tuple.dst_cache, &ip6_daddr) < 0) return NF_DROP; From 6c5dcab95f4cd42a1648739ec9300fbb4b1a021f Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 30 Jun 2026 11:40:55 +0200 Subject: [PATCH 0773/1054] netfilter: flowtable: IPIP tunnel hardware offload is not yet support No driver supports for IPIP tunnels yet, give up early on setting up the hardware offload for this scenario. This patch adds a stub that can be enhanced to add more configuration that are currently not supported. As of now, the offload work is enqueued to the worker, then ignored if the hardware offload configuration is not supported. Check the NF_FLOW_HW flag to know if this entry was already tried once to be offloaded so this is not retried on refresh when unsupported. Move NF_FLOW_HW flag check to nf_flow_offload_add(). If this NF_FLOW_HW flag is unset the _del and _stats variants are never called. This can be updated later on to skip hardware offload work to be queued in case hardware offload does not support it. Fixes: d98103575dcd ("netfilter: flowtable: Add IP6IP6 rx sw acceleration") Fixes: ab427db17885 ("netfilter: flowtable: Add IPIP rx sw acceleration") Cc: stable@vger.kernel.org Reported-by: Yuan Tan Reported-by: Xin Liu Reported-by: Zhengyang Chen Signed-off-by: Pablo Neira Ayuso Acked-by: Lorenzo Bianconi Signed-off-by: Florian Westphal --- include/net/netfilter/nf_flow_table.h | 2 ++ net/netfilter/nf_flow_table_core.c | 7 +++---- net/netfilter/nf_flow_table_offload.c | 22 ++++++++++++++++++++-- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/include/net/netfilter/nf_flow_table.h b/include/net/netfilter/nf_flow_table.h index 7b23b245a5a8..dc5c9b48e65a 100644 --- a/include/net/netfilter/nf_flow_table.h +++ b/include/net/netfilter/nf_flow_table.h @@ -357,6 +357,8 @@ static inline int nf_flow_register_bpf(void) void nf_flow_offload_add(struct nf_flowtable *flowtable, struct flow_offload *flow); +void nf_flow_offload_refresh(struct nf_flowtable *flowtable, + struct flow_offload *flow); void nf_flow_offload_del(struct nf_flowtable *flowtable, struct flow_offload *flow); void nf_flow_offload_stats(struct nf_flowtable *flowtable, diff --git a/net/netfilter/nf_flow_table_core.c b/net/netfilter/nf_flow_table_core.c index 99c5b9d671a0..d06ce0848b68 100644 --- a/net/netfilter/nf_flow_table_core.c +++ b/net/netfilter/nf_flow_table_core.c @@ -345,10 +345,8 @@ int flow_offload_add(struct nf_flowtable *flow_table, struct flow_offload *flow) nf_ct_refresh(flow->ct, NF_CT_DAY); - if (nf_flowtable_hw_offload(flow_table)) { - __set_bit(NF_FLOW_HW, &flow->flags); + if (nf_flowtable_hw_offload(flow_table)) nf_flow_offload_add(flow_table, flow); - } return 0; } @@ -369,7 +367,8 @@ void flow_offload_refresh(struct nf_flowtable *flow_table, test_bit(NF_FLOW_CLOSING, &flow->flags)) return; - nf_flow_offload_add(flow_table, flow); + if (test_bit(NF_FLOW_HW, &flow->flags)) + nf_flow_offload_refresh(flow_table, flow); } EXPORT_SYMBOL_GPL(flow_offload_refresh); diff --git a/net/netfilter/nf_flow_table_offload.c b/net/netfilter/nf_flow_table_offload.c index 002ec15d988b..801a3dd9ceea 100644 --- a/net/netfilter/nf_flow_table_offload.c +++ b/net/netfilter/nf_flow_table_offload.c @@ -1101,9 +1101,17 @@ nf_flow_offload_work_alloc(struct nf_flowtable *flowtable, return offload; } +static bool nf_flow_offload_unsupported(struct flow_offload *flow) +{ + if (flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.tun_num || + flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.tun_num) + return true; -void nf_flow_offload_add(struct nf_flowtable *flowtable, - struct flow_offload *flow) + return false; +} + +void nf_flow_offload_refresh(struct nf_flowtable *flowtable, + struct flow_offload *flow) { struct flow_offload_work *offload; @@ -1114,6 +1122,16 @@ void nf_flow_offload_add(struct nf_flowtable *flowtable, flow_offload_queue_work(offload); } +void nf_flow_offload_add(struct nf_flowtable *flowtable, + struct flow_offload *flow) +{ + if (nf_flow_offload_unsupported(flow)) + return; + + set_bit(NF_FLOW_HW, &flow->flags); + nf_flow_offload_refresh(flowtable, flow); +} + void nf_flow_offload_del(struct nf_flowtable *flowtable, struct flow_offload *flow) { From fa7395c02d95e51bad2952325d2d6503bfbad437 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 30 Jun 2026 11:40:56 +0200 Subject: [PATCH 0774/1054] netfilter: flowtable: support IPIP tunnel with direct xmit The combination of IPIP tunnel with direct xmit, eg. bridge device, breaks because no dst_entry is provided to check the skb headroom and to set the iph->frag_off field. This leads to invalid dst usage and can trigger a crash in the tunnel transmit path. Fix this by moving dst_cache and dst_cookie out of the runtime union so that they can be shared by neighbour, xfrm, and direct tunnel flows. For FLOW_OFFLOAD_XMIT_DIRECT tuples carrying tunnel metadata, preserve route state in these shared fields and release it through the common dst release path. Since dst_entry is now available to the three supported xmit modes and dst_release() already deals with NULL dst, remove the xmit type check in nft_flow_dst_release(). Moreover, skip the check if the dst entry is NULL in nf_flow_dst_check() which is now the case for the direct xmit case. Based on patch from Rein Wei . Fixes: d30301ba4b07 ("netfilter: flowtable: Add IPIP tx sw acceleration") Cc: stable@vger.kernel.org Reported-by: Yuan Tan Reported-by: Xin Liu Reported-by: Zhengyang Chen Reported-by: Ren Wei Signed-off-by: Pablo Neira Ayuso Acked-by: Lorenzo Bianconi Signed-off-by: Florian Westphal --- include/net/netfilter/nf_flow_table.h | 5 +++-- net/netfilter/nf_flow_table_core.c | 12 ++++++++---- net/netfilter/nf_flow_table_ip.c | 3 +-- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/include/net/netfilter/nf_flow_table.h b/include/net/netfilter/nf_flow_table.h index dc5c9b48e65a..ce414118962f 100644 --- a/include/net/netfilter/nf_flow_table.h +++ b/include/net/netfilter/nf_flow_table.h @@ -155,11 +155,12 @@ struct flow_offload_tuple { tun_num:2, in_vlan_ingress:2; u16 mtu; + u32 dst_cookie; + struct dst_entry *dst_cache; + union { struct { - struct dst_entry *dst_cache; u32 ifidx; - u32 dst_cookie; }; struct { u32 ifidx; diff --git a/net/netfilter/nf_flow_table_core.c b/net/netfilter/nf_flow_table_core.c index d06ce0848b68..2a829b5e8240 100644 --- a/net/netfilter/nf_flow_table_core.c +++ b/net/netfilter/nf_flow_table_core.c @@ -127,12 +127,18 @@ static int flow_offload_fill_route(struct flow_offload *flow, switch (route->tuple[dir].xmit_type) { case FLOW_OFFLOAD_XMIT_DIRECT: + if (flow_tuple->tun_num) { + flow_tuple->dst_cache = dst; + flow_tuple->dst_cookie = + flow_offload_dst_cookie(flow_tuple); + } memcpy(flow_tuple->out.h_dest, route->tuple[dir].out.h_dest, ETH_ALEN); memcpy(flow_tuple->out.h_source, route->tuple[dir].out.h_source, ETH_ALEN); flow_tuple->out.ifidx = route->tuple[dir].out.ifindex; - dst_release(dst); + if (!flow_tuple->tun_num) + dst_release(dst); break; case FLOW_OFFLOAD_XMIT_XFRM: case FLOW_OFFLOAD_XMIT_NEIGH: @@ -152,9 +158,7 @@ static int flow_offload_fill_route(struct flow_offload *flow, static void nft_flow_dst_release(struct flow_offload *flow, enum flow_offload_tuple_dir dir) { - if (flow->tuplehash[dir].tuple.xmit_type == FLOW_OFFLOAD_XMIT_NEIGH || - flow->tuplehash[dir].tuple.xmit_type == FLOW_OFFLOAD_XMIT_XFRM) - dst_release(flow->tuplehash[dir].tuple.dst_cache); + dst_release(flow->tuplehash[dir].tuple.dst_cache); } void flow_offload_route_init(struct flow_offload *flow, diff --git a/net/netfilter/nf_flow_table_ip.c b/net/netfilter/nf_flow_table_ip.c index 089f2bc19972..0b78decce8a9 100644 --- a/net/netfilter/nf_flow_table_ip.c +++ b/net/netfilter/nf_flow_table_ip.c @@ -299,8 +299,7 @@ static bool nf_flow_exceeds_mtu(const struct sk_buff *skb, unsigned int mtu) static inline bool nf_flow_dst_check(struct flow_offload_tuple *tuple) { - if (tuple->xmit_type != FLOW_OFFLOAD_XMIT_NEIGH && - tuple->xmit_type != FLOW_OFFLOAD_XMIT_XFRM) + if (!tuple->dst_cache) return true; return dst_check(tuple->dst_cache, tuple->dst_cookie); From da5b58478a9c1b85608c9e40a3b8432d071b409e Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Sun, 5 Jul 2026 15:29:13 +0200 Subject: [PATCH 0775/1054] netfilter: handle unreadable frags sashiko reports: When an skb with unreadable fragments (such as from devmem TCP, where skb_frags_readable(skb) returns false) is processed by the u32 module, skb_copy_bits() will safely return a negative error code [..] xt_u32: bail out with hotdrop in this case. gather_frags: return -1, just as if we had no fragment header. nfnetlink_queue: restrict to the linear part. nfnetlink_log: restrict to the linear part. v2: - skb_zerocopy helpers don't copy readable flag, i.e. nfnetlink_queue is broken too xt_u32 shouldn't return true if hotdrop was set. Fixes: 65249feb6b3d ("net: add support for skbs with unreadable frags") Cc: stable@vger.kernel.org Acked-by: Mina Almasry Signed-off-by: Florian Westphal --- net/ipv6/netfilter/nf_conntrack_reasm.c | 2 +- net/netfilter/nfnetlink_log.c | 26 ++++++++++++++++--------- net/netfilter/nfnetlink_queue.c | 16 +++++++++++---- net/netfilter/xt_u32.c | 16 ++++++++++----- 4 files changed, 41 insertions(+), 19 deletions(-) diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c index 3637b20d3fa4..599c49bf0a0a 100644 --- a/net/ipv6/netfilter/nf_conntrack_reasm.c +++ b/net/ipv6/netfilter/nf_conntrack_reasm.c @@ -419,7 +419,7 @@ find_prev_fhdr(struct sk_buff *skb, u8 *prevhdrp, int *prevhoff, int *fhoff) return -1; } if (skb_copy_bits(skb, start, &hdr, sizeof(hdr))) - BUG(); + return -1; if (nexthdr == NEXTHDR_AUTH) hdrlen = ipv6_authlen(&hdr); else diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c index fa3657599861..5fee61b3813c 100644 --- a/net/netfilter/nfnetlink_log.c +++ b/net/netfilter/nfnetlink_log.c @@ -676,7 +676,7 @@ __build_packet_message(struct nfnl_log_net *log, goto nla_put_failure; if (skb_copy_bits(skb, 0, nla_data(nla), data_len)) - BUG(); + goto nla_put_failure; } nlh->nlmsg_len = inst->skb->tail - old_tail; @@ -698,6 +698,21 @@ static const struct nf_loginfo default_loginfo = { }, }; +static unsigned int nfulnl_get_copy_len(const struct nf_loginfo *li, + const struct sk_buff *skb, + unsigned int copy_len) +{ + unsigned int len = skb->len; + + if ((li->u.ulog.flags & NF_LOG_F_COPY_LEN) && + li->u.ulog.copy_len < copy_len) + copy_len = li->u.ulog.copy_len; + if (!skb_frags_readable(skb)) + len = skb_headlen(skb); + + return min(len, copy_len); +} + /* log handler for internal netfilter logging api */ static void nfulnl_log_packet(struct net *net, @@ -790,14 +805,7 @@ nfulnl_log_packet(struct net *net, break; case NFULNL_COPY_PACKET: - data_len = inst->copy_range; - if ((li->u.ulog.flags & NF_LOG_F_COPY_LEN) && - (li->u.ulog.copy_len < data_len)) - data_len = li->u.ulog.copy_len; - - if (data_len > skb->len) - data_len = skb->len; - + data_len = nfulnl_get_copy_len(li, skb, inst->copy_range); size += nla_total_size(data_len); break; diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c index 35d4c6c628ff..b8aaf39cb4d8 100644 --- a/net/netfilter/nfnetlink_queue.c +++ b/net/netfilter/nfnetlink_queue.c @@ -690,6 +690,17 @@ static int nfqnl_put_master_ifindex(struct sk_buff *nlskb, int attr, } #endif +static unsigned int nfqnl_get_data_len(const struct sk_buff *entskb, + unsigned int copy_range) +{ + unsigned int data_len = entskb->len; + + if (!skb_frags_readable(entskb)) + data_len = skb_headlen(entskb); + + return min(data_len, copy_range); +} + static struct sk_buff * nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue, struct nf_queue_entry *entry, @@ -755,10 +766,7 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue, nf_queue_checksum_help(entskb)) return NULL; - data_len = READ_ONCE(queue->copy_range); - if (data_len > entskb->len) - data_len = entskb->len; - + data_len = nfqnl_get_data_len(entskb, READ_ONCE(queue->copy_range)); hlen = skb_zerocopy_headlen(entskb); hlen = min_t(unsigned int, hlen, data_len); size += sizeof(struct nlattr) + hlen; diff --git a/net/netfilter/xt_u32.c b/net/netfilter/xt_u32.c index ec1a21e3b6e2..dabbaa742874 100644 --- a/net/netfilter/xt_u32.c +++ b/net/netfilter/xt_u32.c @@ -14,8 +14,8 @@ #include #include -static bool u32_match_it(const struct xt_u32 *data, - const struct sk_buff *skb) +static int u32_match_it(const struct xt_u32 *data, + const struct sk_buff *skb) { const struct xt_u32_test *ct; unsigned int testind; @@ -40,7 +40,8 @@ static bool u32_match_it(const struct xt_u32 *data, return false; if (skb_copy_bits(skb, pos, &n, sizeof(n)) < 0) - BUG(); + return -1; + val = ntohl(n); nnums = ct->nnums; @@ -68,7 +69,7 @@ static bool u32_match_it(const struct xt_u32 *data, if (skb_copy_bits(skb, at + pos, &n, sizeof(n)) < 0) - BUG(); + return -1; val = ntohl(n); break; } @@ -90,9 +91,14 @@ static bool u32_match_it(const struct xt_u32 *data, static bool u32_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct xt_u32 *data = par->matchinfo; - bool ret; + int ret; ret = u32_match_it(data, skb); + if (ret < 0) { + par->hotdrop = true; + return false; + } + return ret ^ data->invert; } From bae7ce7bafb59e42dc0e0e2999fdd9d1cffe3866 Mon Sep 17 00:00:00 2001 From: Yizhou Zhao Date: Mon, 6 Jul 2026 18:16:22 +0800 Subject: [PATCH 0776/1054] ipvs: pass parsed transport offset to state handlers IPVS callers already parse the packet into struct ip_vs_iphdr before updating connection state. For IPv6 this records the real transport-header offset after extension headers in iph.len. Pass this parsed transport offset through ip_vs_set_state() and the protocol state_transition() callback so protocol handlers can use the same packet context as scheduling and NAT handling. This patch only changes the common callback plumbing and adapts the protocol callback signatures; TCP and SCTP start using the value in follow-up patches. Signed-off-by: Yizhou Zhao Acked-by: Julian Anastasov Signed-off-by: Florian Westphal --- include/net/ip_vs.h | 3 ++- net/netfilter/ipvs/ip_vs_core.c | 10 +++++----- net/netfilter/ipvs/ip_vs_proto_sctp.c | 3 ++- net/netfilter/ipvs/ip_vs_proto_tcp.c | 3 ++- net/netfilter/ipvs/ip_vs_proto_udp.c | 3 ++- 5 files changed, 13 insertions(+), 9 deletions(-) diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index 49297fec448a..417ff51f62fc 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -752,7 +752,8 @@ struct ip_vs_protocol { void (*state_transition)(struct ip_vs_conn *cp, int direction, const struct sk_buff *skb, - struct ip_vs_proto_data *pd); + struct ip_vs_proto_data *pd, + unsigned int iph_len); int (*register_app)(struct netns_ipvs *ipvs, struct ip_vs_app *inc); diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index 906f2c361676..f79c09869636 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -398,10 +398,10 @@ ip_vs_conn_stats(struct ip_vs_conn *cp, struct ip_vs_service *svc) static inline void ip_vs_set_state(struct ip_vs_conn *cp, int direction, const struct sk_buff *skb, - struct ip_vs_proto_data *pd) + struct ip_vs_proto_data *pd, unsigned int iph_len) { if (likely(pd->pp->state_transition)) - pd->pp->state_transition(cp, direction, skb, pd); + pd->pp->state_transition(cp, direction, skb, pd, iph_len); } static inline int @@ -803,7 +803,7 @@ int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb, ip_vs_in_stats(cp, skb); /* set state */ - ip_vs_set_state(cp, IP_VS_DIR_INPUT, skb, pd); + ip_vs_set_state(cp, IP_VS_DIR_INPUT, skb, pd, iph->len); /* transmit the first SYN packet */ ret = cp->packet_xmit(skb, cp, pd->pp, iph); @@ -1484,7 +1484,7 @@ handle_response(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd, after_nat: ip_vs_out_stats(cp, skb); - ip_vs_set_state(cp, IP_VS_DIR_OUTPUT, skb, pd); + ip_vs_set_state(cp, IP_VS_DIR_OUTPUT, skb, pd, iph->len); skb->ipvs_property = 1; if (!(cp->flags & IP_VS_CONN_F_NFCT)) ip_vs_notrack(skb); @@ -2233,7 +2233,7 @@ ip_vs_in_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state IP_VS_DBG_PKT(11, af, pp, skb, iph.off, "Incoming packet"); ip_vs_in_stats(cp, skb); - ip_vs_set_state(cp, IP_VS_DIR_INPUT, skb, pd); + ip_vs_set_state(cp, IP_VS_DIR_INPUT, skb, pd, iph.len); if (cp->packet_xmit) ret = cp->packet_xmit(skb, cp, pp, &iph); /* do not touch skb anymore */ diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c index 63c78a1f3918..394367b7b388 100644 --- a/net/netfilter/ipvs/ip_vs_proto_sctp.c +++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c @@ -468,7 +468,8 @@ set_sctp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp, static void sctp_state_transition(struct ip_vs_conn *cp, int direction, - const struct sk_buff *skb, struct ip_vs_proto_data *pd) + const struct sk_buff *skb, struct ip_vs_proto_data *pd, + unsigned int iph_len) { spin_lock_bh(&cp->lock); set_sctp_state(pd, cp, direction, skb); diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c index 8cc0a8ce6241..2d3f6aeafe52 100644 --- a/net/netfilter/ipvs/ip_vs_proto_tcp.c +++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c @@ -579,7 +579,8 @@ set_tcp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp, static void tcp_state_transition(struct ip_vs_conn *cp, int direction, const struct sk_buff *skb, - struct ip_vs_proto_data *pd) + struct ip_vs_proto_data *pd, + unsigned int iph_len) { struct tcphdr _tcph, *th; diff --git a/net/netfilter/ipvs/ip_vs_proto_udp.c b/net/netfilter/ipvs/ip_vs_proto_udp.c index f9de632e38cd..58f9e255927e 100644 --- a/net/netfilter/ipvs/ip_vs_proto_udp.c +++ b/net/netfilter/ipvs/ip_vs_proto_udp.c @@ -444,7 +444,8 @@ static const char * udp_state_name(int state) static void udp_state_transition(struct ip_vs_conn *cp, int direction, const struct sk_buff *skb, - struct ip_vs_proto_data *pd) + struct ip_vs_proto_data *pd, + unsigned int iph_len) { if (unlikely(!pd)) { pr_err("UDP no ns data\n"); From 2500fa3958b1ba51c2b065e39db1b04dfa7e23a2 Mon Sep 17 00:00:00 2001 From: Yizhou Zhao Date: Mon, 6 Jul 2026 18:16:23 +0800 Subject: [PATCH 0777/1054] ipvs: use parsed transport offset in TCP state lookup TCP state handling reparses the skb to find the TCP header. For IPv6 it uses sizeof(struct ipv6hdr), while the surrounding IPVS code already parsed the packet with ip_vs_fill_iph_skb() and has the real transport-header offset in iph.len. This makes TCP state handling look at the wrong bytes when an IPv6 packet carries extension headers. Use the parsed transport offset passed down from ip_vs_set_state() when reading the TCP header. For IPv4 and for IPv6 packets without extension headers, the passed offset matches the previous value. Fixes: 0bbdd42b7efa6 ("IPVS: Extend protocol DNAT/SNAT and state handlers") Link: https://lore.kernel.org/netdev/20260705125659.37744-1-zhaoyz24@mails.tsinghua.edu.cn/ Reported-by: Yizhou Zhao Reported-by: Yuxiang Yang Reported-by: Ao Wang Reported-by: Xuewei Feng Reported-by: Qi Li Reported-by: Ke Xu Assisted-by: Claude Code:GLM-5.2 Signed-off-by: Yizhou Zhao Acked-by: Julian Anastasov Signed-off-by: Florian Westphal --- net/netfilter/ipvs/ip_vs_proto_tcp.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c index 2d3f6aeafe52..f86b763efcc4 100644 --- a/net/netfilter/ipvs/ip_vs_proto_tcp.c +++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c @@ -584,13 +584,7 @@ tcp_state_transition(struct ip_vs_conn *cp, int direction, { struct tcphdr _tcph, *th; -#ifdef CONFIG_IP_VS_IPV6 - int ihl = cp->af == AF_INET ? ip_hdrlen(skb) : sizeof(struct ipv6hdr); -#else - int ihl = ip_hdrlen(skb); -#endif - - th = skb_header_pointer(skb, ihl, sizeof(_tcph), &_tcph); + th = skb_header_pointer(skb, iph_len, sizeof(_tcph), &_tcph); if (th == NULL) return; From 2f75c0faa3361b28e36cc0512b3299e163e25789 Mon Sep 17 00:00:00 2001 From: Yizhou Zhao Date: Mon, 6 Jul 2026 18:16:24 +0800 Subject: [PATCH 0778/1054] ipvs: use parsed transport offset in SCTP state lookup set_sctp_state() reads the SCTP chunk header again in order to drive the IPVS SCTP state table. For IPv6 it computes the offset with sizeof(struct ipv6hdr), while the surrounding IPVS code uses iph.len from ip_vs_fill_iph_skb(), where ipv6_find_hdr() has already skipped extension headers and found the real transport header. This makes the state machine read from the wrong offset for IPv6 SCTP packets that carry extension headers. For example, an INIT packet with an 8-byte destination options header can be scheduled correctly by sctp_conn_schedule(), but set_sctp_state() reads the first byte of the SCTP verification tag as a DATA chunk type. The connection then moves from NONE to ESTABLISHED instead of INIT1, gets the longer established timeout, and updates the active/inactive destination counters incorrectly. This happens even though the SCTP handshake has not completed. Use the parsed transport offset passed down from ip_vs_set_state() for the SCTP chunk-header lookup. For IPv4 and IPv6 packets without extension headers this preserves the existing offset. Fixes: 2906f66a5682 ("ipvs: SCTP Trasport Loadbalancing Support") Cc: stable@vger.kernel.org Link: https://lore.kernel.org/netdev/20260705123040.35755-1-zhaoyz24@mails.tsinghua.edu.cn/ Reported-by: Yizhou Zhao Reported-by: Yuxiang Yang Reported-by: Ao Wang Reported-by: Xuewei Feng Reported-by: Qi Li Reported-by: Ke Xu Assisted-by: Claude Code:GLM-5.2 Signed-off-by: Yizhou Zhao Acked-by: Julian Anastasov Signed-off-by: Florian Westphal --- net/netfilter/ipvs/ip_vs_proto_sctp.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c index 394367b7b388..c67317be17df 100644 --- a/net/netfilter/ipvs/ip_vs_proto_sctp.c +++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c @@ -372,20 +372,15 @@ static const char *sctp_state_name(int state) static inline void set_sctp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp, - int direction, const struct sk_buff *skb) + int direction, const struct sk_buff *skb, + unsigned int iph_len) { struct sctp_chunkhdr _sctpch, *sch; unsigned char chunk_type; int event, next_state; - int ihl, cofs; + int cofs; -#ifdef CONFIG_IP_VS_IPV6 - ihl = cp->af == AF_INET ? ip_hdrlen(skb) : sizeof(struct ipv6hdr); -#else - ihl = ip_hdrlen(skb); -#endif - - cofs = ihl + sizeof(struct sctphdr); + cofs = iph_len + sizeof(struct sctphdr); sch = skb_header_pointer(skb, cofs, sizeof(_sctpch), &_sctpch); if (sch == NULL) return; @@ -472,7 +467,7 @@ sctp_state_transition(struct ip_vs_conn *cp, int direction, unsigned int iph_len) { spin_lock_bh(&cp->lock); - set_sctp_state(pd, cp, direction, skb); + set_sctp_state(pd, cp, direction, skb, iph_len); spin_unlock_bh(&cp->lock); } From 3f7a535ff0fa627a0132803e4c2f903ceffcbc1c Mon Sep 17 00:00:00 2001 From: Julian Anastasov Date: Tue, 7 Jul 2026 21:25:46 +0300 Subject: [PATCH 0779/1054] ipvs: ensure inner headers in ICMP errors are in headroom Sashiko points out that after stripping the outer headers with pskb_pull() we should ensure the inner IP headers in ICMP errors from tunnels are present in the skb headroom for functions like ipv4_update_pmtu(), icmp_send() and IP_VS_DBG(). Also, add more checks for the length of the inner headers. Fixes: f2edb9f7706d ("ipvs: implement passive PMTUD for IPIP packets") Link: https://sashiko.dev/#/patchset/20260702073430.67680-1-zhaoyz24%40mails.tsinghua.edu.cn Signed-off-by: Julian Anastasov Signed-off-by: Florian Westphal --- net/netfilter/ipvs/ip_vs_core.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index f79c09869636..35cbe821c259 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -1767,6 +1767,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, bool tunnel, new_cp = false; union nf_inet_addr *raddr; char *outer_proto = "IPIP"; + unsigned int hlen_ipip; int ulen = 0; *related = 1; @@ -1804,9 +1805,10 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, /* Now find the contained IP header */ offset += sizeof(_icmph); cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph); - if (cih == NULL) + if (!(cih && cih->version == 4 && cih->ihl >= 5)) return NF_ACCEPT; /* The packet looks wrong, ignore */ raddr = (union nf_inet_addr *)&cih->daddr; + hlen_ipip = cih->ihl * 4; /* Special case for errors for IPIP/UDP/GRE tunnel packets */ tunnel = false; @@ -1822,9 +1824,9 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, /* Only for known tunnel */ if (!dest || dest->tun_type != IP_VS_CONN_F_TUNNEL_TYPE_IPIP) return NF_ACCEPT; - offset += cih->ihl * 4; + offset += hlen_ipip; cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph); - if (cih == NULL) + if (!(cih && cih->version == 4 && cih->ihl >= 5)) return NF_ACCEPT; /* The packet looks wrong, ignore */ tunnel = true; } else if ((cih->protocol == IPPROTO_UDP || /* Can be UDP encap */ @@ -1836,7 +1838,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, /* Non-first fragment has no UDP/GRE header */ if (unlikely(cih->frag_off & htons(IP_OFFSET))) return NF_ACCEPT; - offset2 = offset + cih->ihl * 4; + offset2 = offset + hlen_ipip; if (cih->protocol == IPPROTO_UDP) { ulen = ipvs_udp_decap(ipvs, skb, offset2, AF_INET, raddr, &iproto); @@ -1905,6 +1907,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, } if (tunnel) { + unsigned int hlen_orig = cih->ihl * 4; __be32 info = ic->un.gateway; __u8 type = ic->type; __u8 code = ic->code; @@ -1921,6 +1924,9 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, goto ignore_tunnel; offset2 -= ihl + sizeof(_icmph); skb_reset_network_header(skb); + /* Ensure the IP header is present in headroom */ + if (!pskb_may_pull(skb, hlen_ipip)) + goto ignore_tunnel; IP_VS_DBG(12, "ICMP for %s %pI4->%pI4: mtu=%u\n", outer_proto, &ip_hdr(skb)->saddr, &ip_hdr(skb)->daddr, mtu); @@ -1936,8 +1942,8 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, if (dest_dst) mtu = dst_mtu(dest_dst->dst_cache); } - if (mtu > 68 + sizeof(struct iphdr) + ulen) - mtu -= sizeof(struct iphdr) + ulen; + if (mtu > 68 + hlen_ipip + ulen) + mtu -= hlen_ipip + ulen; info = htonl(mtu); } /* Strip outer IP, ICMP and IPIP/UDP/GRE, go to IP header of @@ -1946,6 +1952,9 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, if (pskb_pull(skb, offset2) == NULL) goto ignore_tunnel; skb_reset_network_header(skb); + /* Ensure the IP header is present in headroom */ + if (!pskb_may_pull(skb, hlen_orig)) + goto ignore_tunnel; IP_VS_DBG(12, "Sending ICMP for %pI4->%pI4: t=%u, c=%u, i=%u\n", &ip_hdr(skb)->saddr, &ip_hdr(skb)->daddr, type, code, ntohl(info)); From abf76d3239dee97b66e7241ad04811f1ce562e28 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 9 Jun 2026 13:37:36 -0400 Subject: [PATCH 0780/1054] USB: chaoskey: Fix slab-use-after-free in chaoskey_release() The chaoskey driver has a use-after-free bug in its release routine. If the user closes the device file after the USB device has been unplugged, a debugging log statement will try to access the usb_interface structure after it has been deallocated: BUG: KASAN: slab-use-after-free in dev_driver_string (drivers/base/core.c:2406) Read of size 8 at addr ffff888168e8a0b8 by task chaoskey_raw_re/10106 Hardware name: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX, arch_caps fix, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014 Call Trace: dump_stack_lvl (lib/dump_stack.c:94 lib/dump_stack.c:120) print_report (mm/kasan/report.c:378 mm/kasan/report.c:482) kasan_report (mm/kasan/report.c:595) dev_driver_string (drivers/base/core.c:2406) __dynamic_dev_dbg (lib/dynamic_debug.c:906) chaoskey_release (drivers/usb/misc/chaoskey.c:323) __fput (fs/file_table.c:510) fput_close_sync (fs/file_table.c:615) __x64_sys_close (fs/open.c:1507 fs/open.c:1492 fs/open.c:1492) 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) The driver's last reference to the interface structure is dropped in the chaoskey_free() routine, so the code must not use the interface -- even in a debugging statement -- after that routine returns. (Exception: If we know that another reference is held by someone else, such as the device core while the disconnect routine runs, there's no problem. Thanks to Johan Hovold for pointing this out.) Since the bad access is part of an unimportant debugging statement, we can fix the problem simply by removing the whole statement. Reported-by: Shuangpeng Bai Closes: https://lore.kernel.org/linux-usb/20EC9664-054E-438B-B411-2145D347F97B@gmail.com/ Tested-by: Shuangpeng Bai Signed-off-by: Alan Stern Fixes: 66e3e591891d ("usb: Add driver for Altus Metrum ChaosKey device (v2)") Cc: stable Reviewed-by: Johan Hovold Link: https://patch.msgid.link/bb5b1dc6-eb59-43e1-8d26-51e658e88bbe@rowland.harvard.edu Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/chaoskey.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/usb/misc/chaoskey.c b/drivers/usb/misc/chaoskey.c index d8016540953f..9c06f7775301 100644 --- a/drivers/usb/misc/chaoskey.c +++ b/drivers/usb/misc/chaoskey.c @@ -320,7 +320,6 @@ static int chaoskey_release(struct inode *inode, struct file *file) mutex_unlock(&dev->lock); destruction: mutex_unlock(&chaoskey_list_lock); - usb_dbg(interface, "release success"); return rv; } From af791d295737ea6b6ff2c8d8488462a49c14af01 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 6 Jul 2026 21:48:26 -0700 Subject: [PATCH 0781/1054] iio: event: Fix event FIFO reset race MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `iio_event_getfd()` creates the event file descriptor with `anon_inode_getfd()`, which allocates a new fd, creates the anonymous file and installs it in the process fd table before returning to the caller. The IIO code resets the event FIFO after `anon_inode_getfd()` has returned, but before `IIO_GET_EVENT_FD_IOCTL` has copied the fd number to userspace. But since fd tables are shared between threads, another thread can guess the newly allocated fd number and issue a `read()` on it as soon as the fd has been installed. This means the `kfifo_to_user()` in `iio_event_chrdev_read()` can run in parallel with the `kfifo_reset_out()` in `iio_event_getfd()`. The kfifo documentation says that `kfifo_reset_out()` is only safe when it is called from the reader thread and there is only one concurrent reader. Otherwise it is dangerous and must be handled in the same way as `kfifo_reset()`. If that happens, `kfifo_to_user()` can advance the FIFO `out` index based on state from before the reset, after the reset has already moved the `out` index to the current `in` index. That can leave the FIFO with an `out` index past the `in` index. A later `read()` can then see an underflowed FIFO length and copy more data than the event FIFO buffer contains. This can result in an out-of-bounds read and leak adjacent kernel memory to userspace. Move the FIFO reset before `anon_inode_getfd()`. At that point the event fd is marked busy, but the new fd has not been installed yet, so userspace cannot access it while the FIFO is reset. Fixes: b91accafbb10 ("iio:event: Fix and cleanup locking") Reported-by: Codex:gpt-5.5 Signed-off-by: Lars-Peter Clausen Reviewed-by: Nuno Sá Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-event.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/iio/industrialio-event.c b/drivers/iio/industrialio-event.c index a0d6fcf2a9c9..e6730f52262a 100644 --- a/drivers/iio/industrialio-event.c +++ b/drivers/iio/industrialio-event.c @@ -207,6 +207,8 @@ static int iio_event_getfd(struct iio_dev *indio_dev) goto unlock; } + kfifo_reset_out(&ev_int->det_events); + iio_device_get(indio_dev); fd = anon_inode_getfd("iio:event", &iio_event_chrdev_fileops, @@ -214,10 +216,7 @@ static int iio_event_getfd(struct iio_dev *indio_dev) if (fd < 0) { clear_bit(IIO_BUSY_BIT_POS, &ev_int->flags); iio_device_put(indio_dev); - } else { - kfifo_reset_out(&ev_int->det_events); } - unlock: mutex_unlock(&iio_dev_opaque->mlock); return fd; From b7577fe4c47a31ca7c99714c53244a44af03cdfe Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Mon, 22 Jun 2026 16:31:24 +0200 Subject: [PATCH 0782/1054] s390/diag: Add missing array_index_nospec() call to memtop_get_page_count() 'level' is user space controlled and used to read from an array. Add the missing array_index_nospec() call to prevent speculative execution. Cc: stable@vger.kernel.org Fixes: 0d30871739ab ("s390/diag: Add memory topology information via diag310") Signed-off-by: Heiko Carstens Reviewed-by: Mete Durlu Signed-off-by: Vasily Gorbik --- arch/s390/kernel/diag/diag310.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/arch/s390/kernel/diag/diag310.c b/arch/s390/kernel/diag/diag310.c index f411562aa7f6..c68ec9d28513 100644 --- a/arch/s390/kernel/diag/diag310.c +++ b/arch/s390/kernel/diag/diag310.c @@ -190,17 +190,18 @@ static int memtop_get_stride_len(unsigned long *res) static int memtop_get_page_count(unsigned long *res, unsigned long level) { static unsigned long memtop_pages[DIAG310_LEVELMAX]; - unsigned long pages; + unsigned long pages, idx; int rc; if (level > DIAG310_LEVELMAX || level < DIAG310_LEVELMIN) return -EINVAL; - pages = READ_ONCE(memtop_pages[level - 1]); + idx = array_index_nospec(level - 1, ARRAY_SIZE(memtop_pages)); + pages = READ_ONCE(memtop_pages[idx]); if (!pages) { rc = diag310_get_memtop_size(&pages, level); if (rc) return rc; - WRITE_ONCE(memtop_pages[level - 1], pages); + WRITE_ONCE(memtop_pages[idx], pages); } *res = pages; return 0; From ec84aad4c3594307d103af563991b4415ac5c8ab Mon Sep 17 00:00:00 2001 From: Gerald Schaefer Date: Tue, 30 Jun 2026 19:32:14 +0200 Subject: [PATCH 0783/1054] s390/mm: Fix type mismatch in get_align_mask(). Commit 86f48f922ba79 ("s390/mmap: disable mmap alignment when randomize_va_space = 0") introduced get_align_mask() with return type of 'int', while the target field 'info.align_mask' in struct vm_unmapped_area_info is 'unsigned long'. With currently used masks, this should not cause truncation issues, but fix it and return 'unsigned long' to avoid future problems. Fixes: 86f48f922ba79 ("s390/mmap: disable mmap alignment when randomize_va_space = 0") Cc: stable@vger.kernel.org # v6.9+ Signed-off-by: Gerald Schaefer Reviewed-by: Sven Schnelle Signed-off-by: Vasily Gorbik --- arch/s390/mm/mmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/s390/mm/mmap.c b/arch/s390/mm/mmap.c index 2a222a7e14f4..ef7bfc87758c 100644 --- a/arch/s390/mm/mmap.c +++ b/arch/s390/mm/mmap.c @@ -64,7 +64,7 @@ static inline unsigned long mmap_base(unsigned long rnd, return PAGE_ALIGN(STACK_TOP - gap - rnd); } -static int get_align_mask(struct file *filp, unsigned long flags) +static unsigned long get_align_mask(struct file *filp, unsigned long flags) { if (filp && is_file_hugepages(filp)) return huge_page_mask_align(filp); From 575f87b9d4882b0d621192fdd754d7e09dbd5789 Mon Sep 17 00:00:00 2001 From: Rongguang Wei Date: Fri, 26 Jun 2026 13:58:20 +0800 Subject: [PATCH 0784/1054] s390/zcrypt: Remove the empty file The files has no real user because CEX2 and CEX3 device drivers are removed, also remove these empty files. Fixes: 5ac8c72462cd ("s390/zcrypt: remove CEX2 and CEX3 device drivers") Signed-off-by: Rongguang Wei Reviewed-by: Holger Dengler Signed-off-by: Vasily Gorbik --- drivers/s390/crypto/zcrypt_cex2a.c | 0 drivers/s390/crypto/zcrypt_cex2a.h | 0 drivers/s390/crypto/zcrypt_cex2c.c | 0 drivers/s390/crypto/zcrypt_cex2c.h | 0 4 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 drivers/s390/crypto/zcrypt_cex2a.c delete mode 100644 drivers/s390/crypto/zcrypt_cex2a.h delete mode 100644 drivers/s390/crypto/zcrypt_cex2c.c delete mode 100644 drivers/s390/crypto/zcrypt_cex2c.h diff --git a/drivers/s390/crypto/zcrypt_cex2a.c b/drivers/s390/crypto/zcrypt_cex2a.c deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/s390/crypto/zcrypt_cex2a.h b/drivers/s390/crypto/zcrypt_cex2a.h deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/s390/crypto/zcrypt_cex2c.c b/drivers/s390/crypto/zcrypt_cex2c.c deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/s390/crypto/zcrypt_cex2c.h b/drivers/s390/crypto/zcrypt_cex2c.h deleted file mode 100644 index e69de29bb2d1..000000000000 From 7d5c2f679147859c1db97d9f66366ff16909abee Mon Sep 17 00:00:00 2001 From: Bastian Blank Date: Thu, 18 Jun 2026 18:12:42 +0200 Subject: [PATCH 0785/1054] s390: Add build salt to the vDSO The vDSO needs to have a unique build id in a similar manner to the kernel and modules. Use the build salt macro. Signed-off-by: Bastian Blank Reviewed-by: Heiko Carstens Signed-off-by: Alexander Gordeev Signed-off-by: Vasily Gorbik --- arch/s390/kernel/vdso/note.S | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/s390/kernel/vdso/note.S b/arch/s390/kernel/vdso/note.S index db19d0680a0a..d215781f26b5 100644 --- a/arch/s390/kernel/vdso/note.S +++ b/arch/s390/kernel/vdso/note.S @@ -4,6 +4,7 @@ * Here we can supply some information useful to userland. */ +#include #include #include #include @@ -11,3 +12,5 @@ ELFNOTE_START(Linux, 0, "a") .long LINUX_VERSION_CODE ELFNOTE_END + +BUILD_SALT From 6aa38ef0eab32df5409b72d62509de6ba09cea50 Mon Sep 17 00:00:00 2001 From: Guoniu Zhou Date: Tue, 9 Jun 2026 14:26:40 +0800 Subject: [PATCH 0786/1054] dt-bindings: power: imx93: Add MIPI PHY power domain Add MIPI PHY power domain for shared PHY resources used by both MIPI DSI and CSI blocks. Signed-off-by: Guoniu Zhou Reviewed-by: Frank Li Acked-by: Krzysztof Kozlowski Reviewed-by: Peng Fan Fixes: e9aa77d413c9 ("soc: imx: add i.MX93 media blk ctrl driver") Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson --- include/dt-bindings/power/fsl,imx93-power.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/dt-bindings/power/fsl,imx93-power.h b/include/dt-bindings/power/fsl,imx93-power.h index 17f9f015bf7d..071221fe5c57 100644 --- a/include/dt-bindings/power/fsl,imx93-power.h +++ b/include/dt-bindings/power/fsl,imx93-power.h @@ -11,5 +11,6 @@ #define IMX93_MEDIABLK_PD_PXP 2 #define IMX93_MEDIABLK_PD_LCDIF 3 #define IMX93_MEDIABLK_PD_ISI 4 +#define IMX93_MEDIABLK_PD_MIPI_PHY 5 #endif From 99611233f8cda833169fa6487d5dacdf189e5cb0 Mon Sep 17 00:00:00 2001 From: Guoniu Zhou Date: Tue, 9 Jun 2026 14:26:41 +0800 Subject: [PATCH 0787/1054] pmdomain: imx93-blk-ctrl: Extract PHY as shared domain for DSI/CSI The MIPI DSI and CSI domains share control bits for clock and reset, which can lead to incorrect behavior if one domain disables the shared resource while the other is still active. To fix the issue, introduce a shared MIPI PHY power domain to own the common resources and make DSI and CSI its subdomains. This ensures the shared bits are properly managed and not disabled while still in use. Fixes: e9aa77d413c9 ("soc: imx: add i.MX93 media blk ctrl driver") Cc: stable@vger.kernel.org Signed-off-by: Guoniu Zhou Reviewed-by: Frank Li Reviewed-by: Peng Fan Signed-off-by: Ulf Hansson --- drivers/pmdomain/imx/imx93-blk-ctrl.c | 60 ++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/drivers/pmdomain/imx/imx93-blk-ctrl.c b/drivers/pmdomain/imx/imx93-blk-ctrl.c index 1afc78b034fa..243ce939ba68 100644 --- a/drivers/pmdomain/imx/imx93-blk-ctrl.c +++ b/drivers/pmdomain/imx/imx93-blk-ctrl.c @@ -48,6 +48,8 @@ #define PRIO(X) (X) +#define BLK_CTRL_NO_PARENT UINT_MAX + struct imx93_blk_ctrl_domain; struct imx93_blk_ctrl { @@ -68,12 +70,18 @@ struct imx93_blk_ctrl_qos { u32 cfg_prio; }; +struct imx93_blk_ctrl_subdomain_link { + struct generic_pm_domain *parent; + struct generic_pm_domain *subdomain; +}; + struct imx93_blk_ctrl_domain_data { const char *name; const char * const *clk_names; int num_clks; u32 rst_mask; u32 clk_mask; + u32 parent; int num_qos; struct imx93_blk_ctrl_qos qos[DOMAIN_MAX_QOS]; }; @@ -203,6 +211,13 @@ static void imx93_release_pm_genpd(void *data) pm_genpd_remove(genpd); } +static void imx93_release_subdomain(void *data) +{ + struct imx93_blk_ctrl_subdomain_link *link = data; + + pm_genpd_remove_subdomain(link->parent, link->subdomain); +} + static struct lock_class_key blk_ctrl_genpd_lock_class; static int imx93_blk_ctrl_probe(struct platform_device *pdev) @@ -302,6 +317,34 @@ static int imx93_blk_ctrl_probe(struct platform_device *pdev) bc->onecell_data.domains[i] = &domain->genpd; } + for (i = 0; i < bc_data->num_domains; i++) { + struct imx93_blk_ctrl_domain *domain = &bc->domains[i]; + const struct imx93_blk_ctrl_domain_data *data = domain->data; + struct imx93_blk_ctrl_subdomain_link *link; + + if (bc_data->skip_mask & BIT(i) || + data->parent == BLK_CTRL_NO_PARENT) + continue; + + link = devm_kzalloc(dev, sizeof(*link), GFP_KERNEL); + if (!link) + return -ENOMEM; + + link->parent = &bc->domains[data->parent].genpd; + link->subdomain = &domain->genpd; + + ret = pm_genpd_add_subdomain(&bc->domains[data->parent].genpd, + &domain->genpd); + if (ret) + return dev_err_probe(dev, ret, "failed to add subdomain %s\n", + domain->genpd.name); + + ret = devm_add_action_or_reset(dev, imx93_release_subdomain, link); + if (ret) + return dev_err_probe(dev, ret, + "failed to add subdomain release callback\n"); + } + ret = devm_pm_runtime_enable(dev); if (ret) return dev_err_probe(dev, ret, "failed to enable pm-runtime\n"); @@ -326,8 +369,9 @@ static const struct imx93_blk_ctrl_domain_data imx93_media_blk_ctl_domain_data[] .name = "mediablk-mipi-dsi", .clk_names = (const char *[]){ "dsi" }, .num_clks = 1, - .rst_mask = BIT(11) | BIT(12), - .clk_mask = BIT(11) | BIT(12), + .rst_mask = BIT(11), + .clk_mask = BIT(11), + .parent = IMX93_MEDIABLK_PD_MIPI_PHY, }, [IMX93_MEDIABLK_PD_MIPI_CSI] = { .name = "mediablk-mipi-csi", @@ -335,6 +379,7 @@ static const struct imx93_blk_ctrl_domain_data imx93_media_blk_ctl_domain_data[] .num_clks = 2, .rst_mask = BIT(9) | BIT(10), .clk_mask = BIT(9) | BIT(10), + .parent = IMX93_MEDIABLK_PD_MIPI_PHY, }, [IMX93_MEDIABLK_PD_PXP] = { .name = "mediablk-pxp", @@ -342,6 +387,7 @@ static const struct imx93_blk_ctrl_domain_data imx93_media_blk_ctl_domain_data[] .num_clks = 1, .rst_mask = BIT(7) | BIT(8), .clk_mask = BIT(7) | BIT(8), + .parent = BLK_CTRL_NO_PARENT, .num_qos = 2, .qos = { { @@ -363,6 +409,7 @@ static const struct imx93_blk_ctrl_domain_data imx93_media_blk_ctl_domain_data[] .num_clks = 2, .rst_mask = BIT(4) | BIT(5) | BIT(6), .clk_mask = BIT(4) | BIT(5) | BIT(6), + .parent = BLK_CTRL_NO_PARENT, .num_qos = 1, .qos = { { @@ -379,6 +426,7 @@ static const struct imx93_blk_ctrl_domain_data imx93_media_blk_ctl_domain_data[] .num_clks = 1, .rst_mask = BIT(2) | BIT(3), .clk_mask = BIT(2) | BIT(3), + .parent = BLK_CTRL_NO_PARENT, .num_qos = 4, .qos = { { @@ -404,6 +452,14 @@ static const struct imx93_blk_ctrl_domain_data imx93_media_blk_ctl_domain_data[] } } }, + [IMX93_MEDIABLK_PD_MIPI_PHY] = { + .name = "mediablk-mipi-phy", + .clk_names = NULL, + .num_clks = 0, + .rst_mask = BIT(12), + .clk_mask = BIT(12), + .parent = BLK_CTRL_NO_PARENT, + }, }; static const struct regmap_range imx93_media_blk_ctl_yes_ranges[] = { From 6d07e4f7144b636b112fbe177d32e1cf85e2558e Mon Sep 17 00:00:00 2001 From: Fredric Cover Date: Tue, 7 Jul 2026 13:15:26 -0700 Subject: [PATCH 0788/1054] smb: client: use GFP_KERNEL for DFS cache allocations In dfs_cache.c, the helper functions alloc_target(), setup_referral(), and update_cache_entry_locked() currently utilize GFP_ATOMIC to allocate memory. However, all of these functions are executed in sleepable conditions. Use GFP_KERNEL instead, to reduce the risk of allocation failure and stop putting unnecessary pressure on emergency memory pools in low-memory scenarios. Signed-off-by: Fredric Cover Acked-by: Paulo Alcantara (Red Hat) Signed-off-by: Steve French --- fs/smb/client/dfs_cache.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/fs/smb/client/dfs_cache.c b/fs/smb/client/dfs_cache.c index 83f8cf2f8d2b..44409ba44e1d 100644 --- a/fs/smb/client/dfs_cache.c +++ b/fs/smb/client/dfs_cache.c @@ -363,10 +363,10 @@ static struct cache_dfs_tgt *alloc_target(const char *name, int path_consumed) { struct cache_dfs_tgt *t; - t = kmalloc_obj(*t, GFP_ATOMIC); + t = kmalloc_obj(*t, GFP_KERNEL); if (!t) return ERR_PTR(-ENOMEM); - t->name = kstrdup(name, GFP_ATOMIC); + t->name = kstrdup(name, GFP_KERNEL); if (!t->name) { kfree(t); return ERR_PTR(-ENOMEM); @@ -626,7 +626,7 @@ static int update_cache_entry_locked(struct cache_entry *ce, const struct dfs_in target = READ_ONCE(ce->tgthint); if (target) { - th = kstrdup(target->name, GFP_ATOMIC); + th = kstrdup(target->name, GFP_KERNEL); if (!th) return -ENOMEM; } @@ -760,11 +760,11 @@ static int setup_referral(const char *path, struct cache_entry *ce, memset(ref, 0, sizeof(*ref)); - ref->path_name = kstrdup(path, GFP_ATOMIC); + ref->path_name = kstrdup(path, GFP_KERNEL); if (!ref->path_name) return -ENOMEM; - ref->node_name = kstrdup(target, GFP_ATOMIC); + ref->node_name = kstrdup(target, GFP_KERNEL); if (!ref->node_name) { rc = -ENOMEM; goto err_free_path; From 027a84ac6b50c12ef767c15abfc58aa865820e9e Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Wed, 8 Jul 2026 19:27:10 +0800 Subject: [PATCH 0789/1054] cifs: validate DFS referral string offsets parse_dfs_referrals() validates that the response header and referral array fit in the received buffer, but each referral also contains string offsets supplied by the server. Those offsets are used to compute the DfsPath and NetworkAddress string pointers without checking whether they still point inside the response buffer. A malformed referral can therefore make the computed pointer exceed the end of the buffer. The resulting negative max_len is then passed to cifs_strndup_from_utf16(), and the non-Unicode path forwards it to kstrndup() as a size_t, allowing strnlen() to read out of bounds. Validate each string offset before deriving the string pointer. Fixes: 4ecce920e13a ("CIFS: move DFS response parsing out of SMB1 code") Signed-off-by: Guangshuo Li Signed-off-by: Steve French --- fs/smb/client/misc.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/fs/smb/client/misc.c b/fs/smb/client/misc.c index 0c54b9b79a2c..ee1728eec8aa 100644 --- a/fs/smb/client/misc.c +++ b/fs/smb/client/misc.c @@ -752,6 +752,10 @@ parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size, node->ref_flag = le16_to_cpu(ref->ReferralEntryFlags); /* copy DfsPath */ + if (le16_to_cpu(ref->DfsPathOffset) > data_end - (char *)ref) { + rc = -EINVAL; + goto parse_DFS_referrals_exit; + } temp = (char *)ref + le16_to_cpu(ref->DfsPathOffset); max_len = data_end - temp; node->path_name = cifs_strndup_from_utf16(temp, max_len, @@ -762,6 +766,10 @@ parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size, } /* copy link target UNC */ + if (le16_to_cpu(ref->NetworkAddressOffset) > data_end - (char *)ref) { + rc = -EINVAL; + goto parse_DFS_referrals_exit; + } temp = (char *)ref + le16_to_cpu(ref->NetworkAddressOffset); max_len = data_end - temp; node->node_name = cifs_strndup_from_utf16(temp, max_len, From 72422525f641f68bed6ca3389d29ee3f41fdea33 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Wed, 10 Jun 2026 22:39:10 +0800 Subject: [PATCH 0790/1054] pmdomain: imx: Fix i.MX8MP power notifier Using imx8mm_vpu_power_notifier() for i.MX8MP is wrong, as it ungates the VPU clocks to provide the ADB clock, which is necessary on i.MX8MM, but on i.MX8MP there is a separate gate (bit 3) for the NoC. So add imx8mp_vpu_power_notifier() for i.MX8MP. Fixes: a1a5f15f7f6cb ("soc: imx: imx8m-blk-ctrl: add i.MX8MP VPU blk ctrl") Cc: stable@vger.kernel.org Reviewed-by: Frank Li Signed-off-by: Peng Fan Signed-off-by: Ulf Hansson --- drivers/pmdomain/imx/imx8m-blk-ctrl.c | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/drivers/pmdomain/imx/imx8m-blk-ctrl.c b/drivers/pmdomain/imx/imx8m-blk-ctrl.c index 19e992d2ee3b..e13a47eeed75 100644 --- a/drivers/pmdomain/imx/imx8m-blk-ctrl.c +++ b/drivers/pmdomain/imx/imx8m-blk-ctrl.c @@ -514,9 +514,34 @@ static const struct imx8m_blk_ctrl_domain_data imx8mp_vpu_blk_ctl_domain_data[] }, }; +static int imx8mp_vpu_power_notifier(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct imx8m_blk_ctrl *bc = container_of(nb, struct imx8m_blk_ctrl, + power_nb); + + if (action == GENPD_NOTIFY_ON) { + /* + * On power up we have no software backchannel to the GPC to + * wait for the ADB handshake to happen, so we just delay for a + * bit. On power down the GPC driver waits for the handshake. + */ + + udelay(5); + + /* set "fuse" bits to enable the VPUs */ + regmap_set_bits(bc->regmap, 0x8, 0xffffffff); + regmap_set_bits(bc->regmap, 0xc, 0xffffffff); + regmap_set_bits(bc->regmap, 0x10, 0xffffffff); + regmap_set_bits(bc->regmap, 0x14, 0xffffffff); + } + + return NOTIFY_OK; +} + static const struct imx8m_blk_ctrl_data imx8mp_vpu_blk_ctl_dev_data = { .max_reg = 0x18, - .power_notifier_fn = imx8mm_vpu_power_notifier, + .power_notifier_fn = imx8mp_vpu_power_notifier, .domains = imx8mp_vpu_blk_ctl_domain_data, .num_domains = ARRAY_SIZE(imx8mp_vpu_blk_ctl_domain_data), }; From 25e252bcf1593b420b12a7231d9dd64b885a2ae2 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Wed, 10 Jun 2026 22:39:11 +0800 Subject: [PATCH 0791/1054] pmdomain: imx: Fix i.MX8MP VC8000E power up sequence Per errata[1]: ERR050531: VPU_NOC power down handshake may hang during VC8000E/VPUMIX power up/down cycling. Description: VC8000E reset de-assertion edge and AXI clock may have a timing issue. Workaround: Set bit2 (vc8000e_clk_en) of BLK_CLK_EN_CSR to 0 to gate off both AXI clock and VC8000E clock sent to VC8000E and AXI clock sent to VPU_NOC m_v_2 interface during VC8000E power up(VC8000E reset is de-asserted by HW) Add a bool variable is_errata_err050531 in 'struct imx8m_blk_ctrl_domain_data' to represent whether the workaround is needed. If is_errata_err050531 is true, first clear the clk before powering up gpc, then enable the clk after powering up gpc. [1] https://www.nxp.com/webapp/Download?colCode=IMX8MP_1P33A Fixes: a1a5f15f7f6cb ("soc: imx: imx8m-blk-ctrl: add i.MX8MP VPU blk ctrl") Cc: stable@vger.kernel.org Signed-off-by: Peng Fan Reviewed-by: Frank Li Signed-off-by: Ulf Hansson --- drivers/pmdomain/imx/imx8m-blk-ctrl.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/drivers/pmdomain/imx/imx8m-blk-ctrl.c b/drivers/pmdomain/imx/imx8m-blk-ctrl.c index e13a47eeed75..99d100e1d923 100644 --- a/drivers/pmdomain/imx/imx8m-blk-ctrl.c +++ b/drivers/pmdomain/imx/imx8m-blk-ctrl.c @@ -54,6 +54,15 @@ struct imx8m_blk_ctrl_domain_data { * register. */ u32 mipi_phy_rst_mask; + + /* + * VC8000E reset de-assertion edge and AXI clock may have a timing issue. + * Workaround: Set bit2 (vc8000e_clk_en) of BLK_CLK_EN_CSR to 0 to gate off + * both AXI clock and VC8000E clock sent to VC8000E and AXI clock sent to + * VPU_NOC m_v_2 interface during VC8000E power up(VC8000E reset is + * de-asserted by HW) + */ + bool is_errata_err050531; }; #define DOMAIN_MAX_CLKS 4 @@ -108,7 +117,11 @@ static int imx8m_blk_ctrl_power_on(struct generic_pm_domain *genpd) dev_err(bc->dev, "failed to enable clocks\n"); goto bus_put; } - regmap_set_bits(bc->regmap, BLK_CLK_EN, data->clk_mask); + + if (data->is_errata_err050531) + regmap_clear_bits(bc->regmap, BLK_CLK_EN, data->clk_mask); + else + regmap_set_bits(bc->regmap, BLK_CLK_EN, data->clk_mask); /* power up upstream GPC domain */ ret = pm_runtime_get_sync(domain->power_dev); @@ -117,6 +130,9 @@ static int imx8m_blk_ctrl_power_on(struct generic_pm_domain *genpd) goto clk_disable; } + if (data->is_errata_err050531) + regmap_set_bits(bc->regmap, BLK_CLK_EN, data->clk_mask); + /* wait for reset to propagate */ udelay(5); @@ -511,6 +527,7 @@ static const struct imx8m_blk_ctrl_domain_data imx8mp_vpu_blk_ctl_domain_data[] .clk_mask = BIT(2), .path_names = (const char *[]){"vc8000e"}, .num_paths = 1, + .is_errata_err050531 = true, }, }; From f576c75f95a52c71b30167d7efb6d47148f9c279 Mon Sep 17 00:00:00 2001 From: Jens Glathe Date: Sat, 30 May 2026 10:20:22 +0200 Subject: [PATCH 0792/1054] Revert "usb: typec: mux: avoid duplicated mux switches" This reverts commit b145c3f29d62f71cc9d2d714e2d4ae4c8d3f863d. The deduplication logic appears to cause issues with separate SBU muxes. The mode-switch call on these (like gpio-sbu-mux) never appeared, so no successful mode-switch happened. The more high-end Parade PS883X redrivers are not affected due to being retimer-switch. The revert fixes dp altmode mode-switch for both. Tested on: Lenovo Thinkbook 16 G7 QOY Lenovo Ideapad 5 2in1 14Q8X9 Microsoft Windows Dev Kit 2023 (Blackrock) Lenovo Thinkpad T14s G6 Fixes: b145c3f29d62 ("usb: typec: mux: avoid duplicated mux switches") Cc: stable Signed-off-by: Jens Glathe Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/20260530-typc-mux-modeset-v1-1-64b0281e2cd6@oldschoolsolutions.biz Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/mux.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/usb/typec/mux.c b/drivers/usb/typec/mux.c index db5e4a4c0a99..9b908c46bd7d 100644 --- a/drivers/usb/typec/mux.c +++ b/drivers/usb/typec/mux.c @@ -275,9 +275,7 @@ static int mux_fwnode_match(struct device *dev, const void *fwnode) static void *typec_mux_match(const struct fwnode_handle *fwnode, const char *id, void *data) { - struct typec_mux_dev **mux_devs = data; struct device *dev; - int i; /* * Device graph (OF graph) does not give any means to identify the @@ -293,14 +291,6 @@ static void *typec_mux_match(const struct fwnode_handle *fwnode, dev = class_find_device(&typec_mux_class, NULL, fwnode, mux_fwnode_match); - /* Skip duplicates */ - for (i = 0; i < TYPEC_MUX_MAX_DEVS; i++) - if (to_typec_mux_dev(dev) == mux_devs[i]) { - put_device(dev); - return NULL; - } - - return dev ? to_typec_mux_dev(dev) : ERR_PTR(-EPROBE_DEFER); } @@ -326,8 +316,7 @@ struct typec_mux *fwnode_typec_mux_get(struct fwnode_handle *fwnode) return ERR_PTR(-ENOMEM); count = fwnode_connection_find_matches(fwnode, "mode-switch", - (void **)mux_devs, - typec_mux_match, + NULL, typec_mux_match, (void **)mux_devs, ARRAY_SIZE(mux_devs)); if (count <= 0) { From 0bfeec21984fedd32987f4e4c0cde34b445af404 Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Thu, 18 Jun 2026 20:40:29 +0800 Subject: [PATCH 0793/1054] usb: misc: usbio: fix disconnect UAF in client teardown usbio_disconnect() walks usbio->cli_list in reverse and uninitializes each auxiliary device. auxiliary_device_uninit() drops the device reference, and for an unbound child that can run usbio_auxdev_release() and free the containing struct usbio_client. list_for_each_entry_reverse() advances after the loop body by reading client->link.prev. If the current client is freed by auxiliary_device_uninit(), the iterator dereferences freed memory. Use list_for_each_entry_safe_reverse() so the previous client is cached before the body can drop the final reference. This preserves reverse teardown order while keeping the next iterator cursor independent of the current client's lifetime. Validation reproduced this kernel report: BUG: KASAN: slab-use-after-free in usbio_disconnect+0x12e/0x150 Call Trace: dump_stack_lvl+0x66/0xa0 print_report+0xce/0x630 ? usbio_disconnect+0x12e/0x150 ? srso_alias_return_thunk+0x5/0xfbef5 ? __virt_addr_valid+0x188/0x320 ? usbio_disconnect+0x12e/0x150 kasan_report+0xe0/0x110 ? usbio_disconnect+0x12e/0x150 usbio_disconnect+0x12e/0x150 usb_unbind_interface+0xf3/0x400 really_probe+0x316/0x660 __driver_probe_device+0x106/0x240 driver_probe_device+0x4a/0x110 __device_attach_driver+0xf1/0x1a0 ? __pfx___device_attach_driver+0x10/0x10 bus_for_each_drv+0xf9/0x160 ? __pfx_bus_for_each_drv+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? trace_hardirqs_on+0x18/0x130 ? srso_alias_return_thunk+0x5/0xfbef5 ? _raw_spin_unlock_irqrestore+0x44/0x60 __device_attach+0x133/0x2a0 ? __pfx___device_attach+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? do_raw_spin_unlock+0x9a/0x100 ? srso_alias_return_thunk+0x5/0xfbef5 device_initial_probe+0x55/0x70 bus_probe_device+0x4a/0xd0 device_add+0x9b9/0xc10 ? __pfx_device_add+0x10/0x10 ? _raw_spin_unlock_irqrestore+0x44/0x60 ? srso_alias_return_thunk+0x5/0xfbef5 ? lockdep_hardirqs_on_prepare+0xea/0x1a0 ? srso_alias_return_thunk+0x5/0xfbef5 ? usb_enable_lpm+0x3c/0x260 usb_set_configuration+0xb64/0xf20 usb_generic_driver_probe+0x5f/0x90 usb_probe_device+0x71/0x1b0 really_probe+0x46b/0x660 __driver_probe_device+0x106/0x240 driver_probe_device+0x4a/0x110 __device_attach_driver+0xf1/0x1a0 ? __pfx___device_attach_driver+0x10/0x10 bus_for_each_drv+0xf9/0x160 ? __pfx_bus_for_each_drv+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? trace_hardirqs_on+0x18/0x130 ? srso_alias_return_thunk+0x5/0xfbef5 ? _raw_spin_unlock_irqrestore+0x44/0x60 __device_attach+0x133/0x2a0 ? __pfx___device_attach+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? do_raw_spin_unlock+0x9a/0x100 ? srso_alias_return_thunk+0x5/0xfbef5 device_initial_probe+0x55/0x70 bus_probe_device+0x4a/0xd0 device_add+0x9b9/0xc10 ? __pfx_device_add+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? add_device_randomness+0xb7/0xf0 usb_new_device+0x492/0x870 hub_event+0x1b10/0x29c0 ? __pfx_hub_event+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? lock_acquire+0x187/0x300 ? process_one_work+0x475/0xb90 ? srso_alias_return_thunk+0x5/0xfbef5 ? lock_release+0xc8/0x290 ? srso_alias_return_thunk+0x5/0xfbef5 process_one_work+0x4d7/0xb90 ? __pfx_process_one_work+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? srso_alias_return_thunk+0x5/0xfbef5 ? __list_add_valid_or_report+0x37/0xf0 ? __pfx_hub_event+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 worker_thread+0x2d8/0x570 ? __pfx_worker_thread+0x10/0x10 kthread+0x1ad/0x1f0 ? __pfx_kthread+0x10/0x10 ret_from_fork+0x3c9/0x540 ? __pfx_ret_from_fork+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? __switch_to+0x2e9/0x730 ? __pfx_kthread+0x10/0x10 ret_from_fork_asm+0x1a/0x30 Fixes: 121a0f839dbb ("usb: misc: Add Intel USBIO bridge driver") Cc: stable Assisted-by: Codex:gpt-5.5 Signed-off-by: Cen Zhang Reviewed-by: Hans de Goede Acked-by: Sakari Ailus Link: https://patch.msgid.link/20260618124029.3704089-1-zzzccc427@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/usbio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/misc/usbio.c b/drivers/usb/misc/usbio.c index 24c4cd0df829..3c2474dca810 100644 --- a/drivers/usb/misc/usbio.c +++ b/drivers/usb/misc/usbio.c @@ -522,7 +522,7 @@ static int usbio_resume(struct usb_interface *intf) static void usbio_disconnect(struct usb_interface *intf) { struct usbio_device *usbio = usb_get_intfdata(intf); - struct usbio_client *client; + struct usbio_client *client, *next; /* Wakeup any clients waiting for a reply */ usbio->rxdat_len = 0; @@ -539,7 +539,7 @@ static void usbio_disconnect(struct usb_interface *intf) usb_kill_urb(usbio->urb); usb_free_urb(usbio->urb); - list_for_each_entry_reverse(client, &usbio->cli_list, link) { + list_for_each_entry_safe_reverse(client, next, &usbio->cli_list, link) { auxiliary_device_delete(&client->auxdev); auxiliary_device_uninit(&client->auxdev); } From 6df47500b557e01737eef6f6b07b12f97a35d841 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Fri, 5 Jun 2026 11:00:58 +0200 Subject: [PATCH 0794/1054] USB: core: ratelimit cabling message If a cable is bad, it stays bad. There is no need to flood the log with messages about it. So go for a ratelimited version. Signed-off-by: Oliver Neukum Link: https://patch.msgid.link/20260605090110.1514785-1-oneukum@suse.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 24960ba9caa9..5262e11c12cd 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -3148,7 +3148,7 @@ static int hub_port_reset(struct usb_hub *hub, int port1, delay = HUB_LONG_RESET_TIME; } - dev_err(&port_dev->dev, "Cannot enable. Maybe the USB cable is bad?\n"); + dev_err_ratelimited(&port_dev->dev, "Cannot enable. Maybe the USB cable is bad?\n"); done: if (status == 0) { From f74e6e15485b68b92b2807071e822db6309b7e38 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 8 Jul 2026 11:34:30 +0100 Subject: [PATCH 0795/1054] ASoC: cs42l43: Correct report for forced microphone jack Currently if the jack is forced to the microphone mode, it will report as line in. Correct the report to microphone. Fixes: fc918cbe874e ("ASoC: cs42l43: Add support for the cs42l43") Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20260708103430.1395207-1-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs42l43-jack.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/cs42l43-jack.c b/sound/soc/codecs/cs42l43-jack.c index 934666295ee3..dbf45cabfffe 100644 --- a/sound/soc/codecs/cs42l43-jack.c +++ b/sound/soc/codecs/cs42l43-jack.c @@ -310,6 +310,7 @@ irqreturn_t cs42l43_bias_detect_clamp(int irq, void *data) #define CS42L43_JACK_ABSENT 0x0 #define CS42L43_JACK_OPTICAL (SND_JACK_MECHANICAL | SND_JACK_AVOUT) +#define CS42L43_JACK_MICROPHONE (SND_JACK_MECHANICAL | SND_JACK_MICROPHONE) #define CS42L43_JACK_HEADPHONE (SND_JACK_MECHANICAL | SND_JACK_HEADPHONE) #define CS42L43_JACK_HEADSET (SND_JACK_MECHANICAL | SND_JACK_HEADSET) #define CS42L43_JACK_LINEOUT (SND_JACK_MECHANICAL | SND_JACK_LINEOUT) @@ -871,7 +872,7 @@ static const struct cs42l43_jack_override_mode { .hsdet_mode = CS42L43_JACK_3_POLE_SWITCHES, .mic_ctrl = (0x3 << CS42L43_JACK_STEREO_CONFIG_SHIFT) | CS42L43_HS1_BIAS_EN_MASK | CS42L43_HS2_BIAS_EN_MASK, - .report = CS42L43_JACK_LINEIN, + .report = CS42L43_JACK_MICROPHONE, }, [CS42L43_JACK_RAW_OPTICAL] = { .hsdet_mode = CS42L43_JACK_3_POLE_SWITCHES, From a3320469e3c8a58c808a090f3e1b5bfccb4c59c6 Mon Sep 17 00:00:00 2001 From: Jia Wang Date: Mon, 27 Apr 2026 09:32:10 +0800 Subject: [PATCH 0796/1054] riscv: add UltraRISC SoC family Kconfig support The first SoC in the UltraRISC series is UR-DP1000, containing octa UltraRISC CP100 cores. Signed-off-by: Jia Wang Acked-by: Conor Dooley Link: https://patch.msgid.link/20260427-ultrarisc-pcie-v4-1-98935f6cdfb5@ultrarisc.com Signed-off-by: Paul Walmsley --- arch/riscv/Kconfig.socs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/riscv/Kconfig.socs b/arch/riscv/Kconfig.socs index c174ac0ec46b..429e07589306 100644 --- a/arch/riscv/Kconfig.socs +++ b/arch/riscv/Kconfig.socs @@ -84,6 +84,12 @@ config ARCH_THEAD help This enables support for the RISC-V based T-HEAD SoCs. +config ARCH_ULTRARISC + bool "UltraRISC RISC-V SoCs" + help + This enables support for UltraRISC SoC platform hardware, + including boards based on the UR-DP1000. + config ARCH_VIRT bool "QEMU Virt Machine" select POWER_RESET From 3fb29495b486b1ec61708248cb388745fc607278 Mon Sep 17 00:00:00 2001 From: Jia Wang Date: Fri, 15 May 2026 09:18:05 +0800 Subject: [PATCH 0797/1054] riscv: defconfig: enable ARCH_ULTRARISC Enable `ARCH_ULTRARISC` in the default RISC-V defconfig. Signed-off-by: Jia Wang Link: https://patch.msgid.link/20260515-ultrarisc-pinctrl-v1-9-bf559589ea8a@ultrarisc.com Signed-off-by: Paul Walmsley --- arch/riscv/configs/defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/riscv/configs/defconfig b/arch/riscv/configs/defconfig index 74ba5acc12a4..ed605b5e3162 100644 --- a/arch/riscv/configs/defconfig +++ b/arch/riscv/configs/defconfig @@ -33,6 +33,7 @@ CONFIG_SOC_STARFIVE=y CONFIG_ARCH_SUNXI=y CONFIG_ARCH_TENSTORRENT=y CONFIG_ARCH_THEAD=y +CONFIG_ARCH_ULTRARISC=y CONFIG_ARCH_VIRT=y CONFIG_ARCH_CANAAN=y CONFIG_SMP=y From ed446e8aa894883c08892cfee69782fdf8f6c3ca Mon Sep 17 00:00:00 2001 From: leixiang Date: Mon, 22 Jun 2026 15:51:01 +0800 Subject: [PATCH 0798/1054] KVM: x86: Nullify irqfd->producer if updating IRTE for bypass fails Nullify irqfd->producer if updating the IRTE for bypass fails, as leaving a dangling pointer will result in a use-after-free if the irqfd is reachable through KVM's routing, but the producer is freed separately. E.g. for VFIO PCI, the producer is embedded in struct "vfio_pci_irq_ctx" and freed when the vector is disabled, which can happen independent of routing updates. Fixes: 77e1b8332d1d ("KVM: x86: Decouple device assignment from IRQ bypass") Cc: stable@vger.kernel.org Signed-off-by: leixiang Link: https://patch.msgid.link/1782119051448443.14545.seg@mailgw.kylinos.cn [sean: drop PPC change, massage changelog] Signed-off-by: Sean Christopherson --- arch/x86/kvm/irq.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/x86/kvm/irq.c b/arch/x86/kvm/irq.c index 8c62c6d4d5c1..cb8ac4b9b0d7 100644 --- a/arch/x86/kvm/irq.c +++ b/arch/x86/kvm/irq.c @@ -488,8 +488,10 @@ int kvm_arch_irq_bypass_add_producer(struct irq_bypass_consumer *cons, if (irqfd->irq_entry.type == KVM_IRQ_ROUTING_MSI) { ret = kvm_pi_update_irte(irqfd, &irqfd->irq_entry); - if (ret) + if (ret) { kvm->arch.nr_possible_bypass_irqs--; + irqfd->producer = NULL; + } } spin_unlock_irq(&kvm->irqfds.lock); From a2ac823d8a228e392bc6a79ea63b550ab84a55b2 Mon Sep 17 00:00:00 2001 From: Hui Wang Date: Tue, 7 Jul 2026 16:23:48 +0800 Subject: [PATCH 0799/1054] selftests/rseq: Fix a building error for riscv arch RISC-V rseq selftests include asm/fence.h from tools/arch/riscv, but the rseq Makefile only adds tools/include in the CFLAGS, this results in the building failure both for native and cross build: In file included from rseq.h:131, from rseq.c:37: rseq-riscv.h:11:10: fatal error: asm/fence.h: No such file or directory To fix it, add the matching tools/arch/$(ARCH)/include path in the CFLAGS and derive ARCH from SUBARCH for standalone native builds where ARCH is not set. Fixes: c92786e179e0 ("KVM: riscv: selftests: Use the existing RISCV_FENCE macro in `rseq-riscv.h`") Cc: stable@vger.kernel.org Signed-off-by: Hui Wang Link: https://patch.msgid.link/20260707082348.36896-1-hui.wang@canonical.com Signed-off-by: Paul Walmsley --- tools/testing/selftests/rseq/Makefile | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/rseq/Makefile b/tools/testing/selftests/rseq/Makefile index 50d69e22ee7a..aba6317f6cb8 100644 --- a/tools/testing/selftests/rseq/Makefile +++ b/tools/testing/selftests/rseq/Makefile @@ -5,9 +5,13 @@ CLANG_FLAGS += -no-integrated-as endif top_srcdir = ../../../.. +include $(top_srcdir)/scripts/subarch.include +ARCH ?= $(SUBARCH) +LINUX_TOOL_ARCH_INCLUDE = $(top_srcdir)/tools/arch/$(ARCH)/include CFLAGS += -O2 -Wall -g -I./ $(KHDR_INCLUDES) -L$(OUTPUT) -Wl,-rpath=./ \ - $(CLANG_FLAGS) -I$(top_srcdir)/tools/include + $(CLANG_FLAGS) -I$(top_srcdir)/tools/include \ + -I$(LINUX_TOOL_ARCH_INCLUDE) LDLIBS += -lpthread -ldl # Own dependencies because we only want to build against 1st prerequisite, but From 9285e4070df2c40585c3d7ec9571faa7a2b97e17 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 24 Jun 2026 15:05:16 -0700 Subject: [PATCH 0800/1054] KVM: x86: Ignore pending PV EOI if the vCPU has since disabled PV EOIs Ignore KVM's internal "service pending PV EOI" request if the vCPU has disabled PV EOIs since the request was made. Asserting that PV EOIs are enabled can fail if reading guest memory in pv_eoi_get_user() fails, i.e. if pv_eoi_test_and_clr_pending() bails early, *and* the vCPU also disables PV EOIs. kernel BUG at arch/x86/kvm/lapic.c:3338! Oops: invalid opcode: 0000 [#1] SMP CPU: 4 UID: 1000 PID: 890 Comm: pv_eoi_test Not tainted 7.0.0-d585aa5894d8-vm #337 PREEMPT Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 0.0.0 02/06/2015 RIP: 0010:kvm_lapic_sync_from_vapic+0x12b/0x140 [kvm] Call Trace: kvm_arch_vcpu_ioctl_run+0x1075/0x1c30 [kvm] kvm_vcpu_ioctl+0x2d5/0x980 [kvm] __x64_sys_ioctl+0x8a/0xd0 do_syscall_64+0xb5/0xb40 entry_SYSCALL_64_after_hwframe+0x4b/0x53 Modules linked in: kvm_intel kvm irqbypass ---[ end trace 0000000000000000 ]--- Fixes: ae7a2a3fb6f8 ("KVM: host side for eoi optimization") Cc: stable@vger.kernel.org Reviewed-by: Kai Huang Link: https://patch.msgid.link/20260624220516.3033391-1-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/lapic.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c index 6f30bbdddb5a..38bba9a1114c 100644 --- a/arch/x86/kvm/lapic.c +++ b/arch/x86/kvm/lapic.c @@ -3371,6 +3371,12 @@ static void apic_sync_pv_eoi_from_guest(struct kvm_vcpu *vcpu, struct kvm_lapic *apic) { int vector; + + if (unlikely(!pv_eoi_enabled(vcpu))) { + __clear_bit(KVM_APIC_PV_EOI_PENDING, &vcpu->arch.apic_attention); + return; + } + /* * PV EOI state is derived from KVM_APIC_PV_EOI_PENDING in host * and KVM_PV_EOI_ENABLED in guest memory as follows: @@ -3382,8 +3388,6 @@ static void apic_sync_pv_eoi_from_guest(struct kvm_vcpu *vcpu, * KVM_APIC_PV_EOI_PENDING is set, KVM_PV_EOI_ENABLED is unset: * -> host enabled PV EOI, guest executed EOI. */ - BUG_ON(!pv_eoi_enabled(vcpu)); - if (pv_eoi_test_and_clr_pending(vcpu)) return; vector = apic_set_eoi(apic); From ebdac7554abb347ca4197be241116842161acd9b Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 12 Jun 2026 07:56:41 -0700 Subject: [PATCH 0801/1054] KVM: nVMX: Move vTPR vs. TPR Threshold consistency check into "normal" checks Move the off-by-default consistency check for vmcs12.tpr_threshold vs. the virtual APIC vTPR into the "normal" controls checks, as waiting until KVM has loaded some amount of state is unnecessary and actively dangerous. Specifically, failure to unwind vmcs01.GUEST_CR3 to KVM's value when EPT is disabled results in KVM running L1 with an L1-controlled CR3, not with KVM's CR3! Alternatively, KVM could simply reset the MMU to force a reload of vmcs01.GUEST_CR3, but the _only_ reason the check was shoved into a "late" flow was to wait until the vmcs12 pages were retrieved. Rather than build up more crusty code, simply access vTPR using a regular guest memory access (performance isn't a concern). To circumvent the restrictions that led to KVM deferring nested_get_vmcs12_pages(), (a) use a VM-scoped API to read guest memory so that it always hits non-SMM memslots (for RSM), and (b) skip the check (since its off-by-default anyways) when the vCPU doesn't want to run, i.e. when userspace is restoring/stuffing state. If reading guest memory fails, simply skip the consistency check, as KVM's de facto ABI is that VMX instruction accesses to non-existent memory get PCI Bus Error semantics, where reads return 0xFFs. And if vTPR=0xFF, then the vTPR is guaranteed to be greater than or equal to TPR_THRESHOLD. Fixes: 1100e4910ad2 ("KVM: nVMX: Add an off-by-default module param to WARN on missed consistency checks") Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260612145642.452392-2-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/vmx/nested.c | 66 +++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 37 deletions(-) diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c index 6957bb6f5cf7..4fc4349810a3 100644 --- a/arch/x86/kvm/vmx/nested.c +++ b/arch/x86/kvm/vmx/nested.c @@ -582,6 +582,9 @@ static int nested_vmx_check_msr_bitmap_controls(struct kvm_vcpu *vcpu, static int nested_vmx_check_tpr_shadow_controls(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12) { + gpa_t vtpr_gpa = vmcs12->virtual_apic_page_addr + APIC_TASKPRI; + u32 vtpr; + if (!nested_cpu_has(vmcs12, CPU_BASED_TPR_SHADOW)) return 0; @@ -591,6 +594,32 @@ static int nested_vmx_check_tpr_shadow_controls(struct kvm_vcpu *vcpu, if (CC(!nested_cpu_has_vid(vmcs12) && vmcs12->tpr_threshold >> 4)) return -EINVAL; + /* + * Do the illegal vTPR vs. TPR Threshold consistency check if and only + * if KVM is configured to WARN on missed consistency checks, otherwise + * it's a waste of time. KVM needs to rely on hardware to fully detect + * an illegal combination due to the vTPR being writable by L1 at all + * times (it's an in-memory value, not a VMCS field). I.e. even if the + * check passes now, it might fail at the actual VM-Enter. + * + * If reading guest memory fails, skip the check as KVM's de facto ABI + * for VMX instruction accesses to non-existent memory is to provide + * PCI Bus Error semantics (reads return 0xFFs), in which case the vTPR + * is guaranteed to greater than or equal to the threshold. + * + * Note! Deliberately use the VM-scoped API when reading guest memory, + * to ensure the read doesn't hit SMRAM when restoring L2 state on RSM, + * and only perform the check when in KVM_RUN, to avoid a false failure + * if userspace hasn't yet configured memslots during state restore. + */ + if (warn_on_missed_cc && vcpu->wants_to_run && + nested_cpu_has(vmcs12, CPU_BASED_TPR_SHADOW) && + !nested_cpu_has_vid(vmcs12) && + !nested_cpu_has2(vmcs12, SECONDARY_EXEC_VIRTUALIZE_APIC_ACCESSES) && + !kvm_read_guest(vcpu->kvm, vtpr_gpa, &vtpr, sizeof(vtpr)) && + CC((vmcs12->tpr_threshold & GENMASK(3, 0)) > ((vtpr >> 4) & GENMASK(3, 0)))) + return -EINVAL; + return 0; } @@ -3104,38 +3133,6 @@ static int nested_vmx_check_controls(struct kvm_vcpu *vcpu, return 0; } -static int nested_vmx_check_controls_late(struct kvm_vcpu *vcpu, - struct vmcs12 *vmcs12) -{ - void *vapic = to_vmx(vcpu)->nested.virtual_apic_map.hva; - u32 vtpr = vapic ? (*(u32 *)(vapic + APIC_TASKPRI)) >> 4 : 0; - - /* - * Don't bother with the consistency checks if KVM isn't configured to - * WARN on missed consistency checks, as KVM needs to rely on hardware - * to fully detect an illegal vTPR vs. TRP Threshold combination due to - * the vTPR being writable by L1 at all times (it's an in-memory value, - * not a VMCS field). I.e. even if the check passes now, it might fail - * at the actual VM-Enter. - * - * Keying off the module param also allows treating an invalid vAPIC - * mapping as a consistency check failure without increasing the risk - * of breaking a "real" VM. - */ - if (!warn_on_missed_cc) - return 0; - - if ((exec_controls_get(to_vmx(vcpu)) & CPU_BASED_TPR_SHADOW) && - nested_cpu_has(vmcs12, CPU_BASED_TPR_SHADOW) && - !nested_cpu_has_vid(vmcs12) && - !nested_cpu_has2(vmcs12, SECONDARY_EXEC_VIRTUALIZE_APIC_ACCESSES) && - (CC(!vapic) || - CC((vmcs12->tpr_threshold & GENMASK(3, 0)) > (vtpr & GENMASK(3, 0))))) - return -EINVAL; - - return 0; -} - static int nested_vmx_check_address_space_size(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12) { @@ -3685,11 +3682,6 @@ enum nvmx_vmentry_status nested_vmx_enter_non_root_mode(struct kvm_vcpu *vcpu, return NVMX_VMENTRY_KVM_INTERNAL_ERROR; } - if (nested_vmx_check_controls_late(vcpu, vmcs12)) { - vmx_switch_vmcs(vcpu, &vmx->vmcs01); - return NVMX_VMENTRY_VMFAIL; - } - if (nested_vmx_check_guest_state(vcpu, vmcs12, &entry_failure_code)) { exit_reason.basic = EXIT_REASON_INVALID_STATE; From 3e6dd2b9b7b2884743ed7a0873b8743cc0df6d40 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 12 Jun 2026 07:56:42 -0700 Subject: [PATCH 0802/1054] KVM: nVMX: Don't use vmcs01.GUEST_CR3 to snapshot L1's CR3 when EPT is disabled Add a dedicated field in "struct nested_vmx" to track L1's pre-VM-Enter CR3 instead of using vmcs01.GUEST_CR3, which isn't anywhere near as safe as the comment purports it to be. E.g. in addition to the warn_on_missed_cc bug (that was fixed by relocating the consistency check), if getting vmcs12 pages (during actual nested VM-Entry) fails and EPT is disabled (in KVM), KVM will return control to userspace with vmcs01.GUEST_CR3 holding a guest- controlled value. Alternatively, KVM could force a reload of vmcs01.GUEST_CR3 by resetting the MMU context in the error path, but as above, the safety of the vmcs01 approach is extremely questionable, e.g. it took all of ~4 months for the code to break. Fixes: 671ddc700fd0 ("KVM: nVMX: Don't leak L1 MMIO regions to L2") Cc: stable@vger.kernel.org Cc: Jim Mattson Link: https://patch.msgid.link/20260612145642.452392-3-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/vmx/nested.c | 21 ++++++++------------- arch/x86/kvm/vmx/vmx.h | 7 +++++++ 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c index 4fc4349810a3..bb0eb40b4448 100644 --- a/arch/x86/kvm/vmx/nested.c +++ b/arch/x86/kvm/vmx/nested.c @@ -3658,19 +3658,14 @@ enum nvmx_vmentry_status nested_vmx_enter_non_root_mode(struct kvm_vcpu *vcpu, &vmx->nested.pre_vmenter_ssp_tbl); /* - * Overwrite vmcs01.GUEST_CR3 with L1's CR3 if EPT is disabled. In the - * event of a "late" VM-Fail, i.e. a VM-Fail detected by hardware but - * not KVM, KVM must unwind its software model to the pre-VM-Entry host - * state. When EPT is disabled, GUEST_CR3 holds KVM's shadow CR3, not - * L1's "real" CR3, which causes nested_vmx_restore_host_state() to - * corrupt vcpu->arch.cr3. Stuffing vmcs01.GUEST_CR3 results in the - * unwind naturally setting arch.cr3 to the correct value. Smashing - * vmcs01.GUEST_CR3 is safe because nested VM-Exits, and the unwind, - * reset KVM's MMU, i.e. vmcs01.GUEST_CR3 is guaranteed to be - * overwritten with a shadow CR3 prior to re-entering L1. + * Stash L1's CR3, so that in the event of a "late" VM-Fail, i.e. a + * VM-Fail detected by hardware but not KVM, KVM can unwind its + * software model to the pre-VM-Entry host state. When EPT is + * disabled, GUEST_CR3 holds KVM's shadow CR3, not L1's "real" CR3, + * and so simply restoring from vmcs01.GUEST_CR3 would corrupt + * vcpu->arch.cr3. */ - if (!enable_ept) - vmcs_writel(GUEST_CR3, vcpu->arch.cr3); + vmx->nested.pre_vmenter_cr3 = kvm_read_cr3(vcpu); vmx_switch_vmcs(vcpu, &vmx->nested.vmcs02); @@ -4982,7 +4977,7 @@ static void nested_vmx_restore_host_state(struct kvm_vcpu *vcpu) vmx_set_cr4(vcpu, vmcs_readl(CR4_READ_SHADOW)); nested_ept_uninit_mmu_context(vcpu); - vcpu->arch.cr3 = vmcs_readl(GUEST_CR3); + vcpu->arch.cr3 = vmx->nested.pre_vmenter_cr3; kvm_register_mark_available(vcpu, VCPU_REG_CR3); /* diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h index de9de0d2016c..dc8517f15bc4 100644 --- a/arch/x86/kvm/vmx/vmx.h +++ b/arch/x86/kvm/vmx/vmx.h @@ -159,6 +159,13 @@ struct nested_vmx { bool has_preemption_timer_deadline; bool preemption_timer_expired; + /* + * Used to restore L1's CR3 if hardware detects a VM-Fail Consistency + * Check that KVM does not, in which case KVM needs to unwind CR3 back + * to its pre-VM-Enter state, NOT to vmcs01.HOST_CR3. + */ + unsigned long pre_vmenter_cr3; + /* * Used to snapshot MSRs that are conditionally loaded on VM-Enter in * order to propagate the guest's pre-VM-Enter value into vmcs02. For From 29bacb096d43bd04062f6655e50ceeed27fe6d2f Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 4 May 2026 06:28:18 -0400 Subject: [PATCH 0803/1054] SUNRPC: release lower rpc_clnt if killed waiting for XPRT_LOCKED xs_tcp_tls_setup_socket() creates a temporary "lower" rpc_clnt with rpc_create() to drive the inner TLS handshake, then waits for XPRT_LOCKED on its xprt with TASK_KILLABLE so a stuck handshake can be aborted by signal. When the wait is interrupted, the function jumps to out_unlock without releasing lower_clnt. The success path and the out_close error path both call rpc_shutdown_client(lower_clnt); only the killed-wait path skips it, leaking the clnt and its underlying xprt. Call rpc_shutdown_client() on this path before joining out_unlock. xprt_release_write() is not needed here because XPRT_LOCKED was never acquired. Fixes: 26e8bfa30dac ("SUNRPC/TLS: Lock the lower_xprt during the tls handshake") Signed-off-by: Chuck Lever Tested-by: Michael Nemanov Reviewed-by: Michael Nemanov Signed-off-by: Anna Schumaker --- net/sunrpc/xprtsock.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index 2e1fe6013361..3eccd4923e6c 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -2734,8 +2734,11 @@ static void xs_tcp_tls_setup_socket(struct work_struct *work) lower_xprt = rcu_dereference(lower_clnt->cl_xprt); rcu_read_unlock(); - if (wait_on_bit_lock(&lower_xprt->state, XPRT_LOCKED, TASK_KILLABLE)) + if (wait_on_bit_lock(&lower_xprt->state, XPRT_LOCKED, TASK_KILLABLE)) { + /* XPRT_LOCKED was never acquired. */ + rpc_shutdown_client(lower_clnt); goto out_unlock; + } status = xs_tls_handshake_sync(lower_xprt, &upper_xprt->xprtsec); if (status) { From 46bc86c833956219bbfd246c1ffd832a479c5199 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 4 May 2026 06:28:19 -0400 Subject: [PATCH 0804/1054] SUNRPC: pin upper rpc_clnt across the TLS connect_worker The TLS connect path has a use-after-free: nothing pins the upper rpc_clnt across the delayed connect_worker. xs_connect() stores task->tk_client in sock_xprt::clnt as a raw pointer and queues the worker; for TLS-secured transports that worker is xs_tcp_tls_setup_socket(), which reads several fields out of the saved pointer (cl_timeout, cl_program, cl_prog, cl_vers, cl_cred, cl_stats) to construct the args for the inner handshake rpc_clnt. The xprt does not reference the rpc_clnt; the rpc_clnt references the xprt. xs_destroy() does cancel the connect_worker, but it runs only when the xprt's refcount drops to zero, which cannot happen until the rpc_clnt releases its cl_xprt reference in rpc_free_client_work(). When a TLS handshake fails fatally (for example, an mTLS mount whose client cert does not match the server), the connecting task is woken with -EACCES and exits, the mount caller invokes rpc_shutdown_client(), and the upper rpc_clnt is freed before the queued connect_worker fires. xs_tcp_tls_setup_socket() then dereferences the freed clnt, producing the refcount_t underflow Michael Nemanov reported. Take a reference on the upper rpc_clnt in xs_connect() for TLS transports via a new rpc_hold_client() helper, and drop it in the connect_worker's exit path with rpc_release_client(). The xprt_lock_connect() / xprt_unlock_connect() pairing already serialises xs_connect() with xs_tcp_tls_setup_socket(), so the take and release are balanced one-for-one. The non-TLS connect worker (xs_tcp_setup_socket) never reads sock_xprt::clnt, so leave that path alone and avoid the clnt-holds-xprt-holds-clnt cycle that would otherwise prevent xprt destruction. Reported-by: Michael Nemanov Closes: https://lore.kernel.org/linux-nfs/40e3d522-dfcf-4fc1-9c55-b5e81f1536d5@vastdata.com/ Fixes: 75eb6af7acdf ("SUNRPC: Add a TCP-with-TLS RPC transport class") Signed-off-by: Chuck Lever Tested-by: Michael Nemanov Reviewed-by: Michael Nemanov Signed-off-by: Anna Schumaker --- include/linux/sunrpc/clnt.h | 1 + net/sunrpc/clnt.c | 19 +++++++++++++++++-- net/sunrpc/xprtsock.c | 11 ++++++++++- 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h index f8b406b0a1af..3c2b8c355ab3 100644 --- a/include/linux/sunrpc/clnt.h +++ b/include/linux/sunrpc/clnt.h @@ -190,6 +190,7 @@ int rpc_switch_client_transport(struct rpc_clnt *, const struct rpc_timeout *); void rpc_shutdown_client(struct rpc_clnt *); +void rpc_hold_client(struct rpc_clnt *); void rpc_release_client(struct rpc_clnt *); void rpc_task_release_transport(struct rpc_task *); void rpc_task_release_client(struct rpc_task *); diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index bc8ca470718b..efa26899bc7d 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -1026,8 +1026,23 @@ rpc_free_auth(struct rpc_clnt *clnt) return NULL; } -/* - * Release reference to the RPC client +/** + * rpc_hold_client - acquire a reference on an rpc_clnt + * @clnt: rpc_clnt to pin + * + * Pairs with rpc_release_client(). + */ +void rpc_hold_client(struct rpc_clnt *clnt) +{ + refcount_inc(&clnt->cl_count); +} + +/** + * rpc_release_client - release a reference on an rpc_clnt + * @clnt: rpc_clnt to release + * + * Pairs with rpc_hold_client(). The rpc_clnt's resources are + * freed once its reference count drops to zero. */ void rpc_release_client(struct rpc_clnt *clnt) diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index 3eccd4923e6c..359407aae03e 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -2761,6 +2761,7 @@ static void xs_tcp_tls_setup_socket(struct work_struct *work) out_unlock: current_restore_flags(pflags, PF_MEMALLOC); upper_transport->clnt = NULL; + rpc_release_client(upper_clnt); xprt_unlock_connect(upper_xprt, upper_transport); return; @@ -2808,7 +2809,15 @@ static void xs_connect(struct rpc_xprt *xprt, struct rpc_task *task) } else dprintk("RPC: xs_connect scheduled xprt %p\n", xprt); - transport->clnt = task->tk_client; + /* + * Only the TLS connect_worker reads transport->clnt; pinning + * the upper rpc_clnt unconditionally would form a cycle with + * cl_xprt and prevent xprt destruction. + */ + if (xprt->xprtsec.policy != RPC_XPRTSEC_NONE) { + rpc_hold_client(task->tk_client); + transport->clnt = task->tk_client; + } queue_delayed_work(xprtiod_workqueue, &transport->connect_worker, delay); From 18d62044cda7a2b40f59d910659c0b0d6accad37 Mon Sep 17 00:00:00 2001 From: Andrea Righi Date: Wed, 8 Jul 2026 10:43:27 +0200 Subject: [PATCH 0805/1054] sched_ext: Preserve rq tracking across local DSQ dispatch dispatch_to_local_dsq() can run from scx_bpf_dsq_move_to_local() while ops.dispatch() has recorded the current rq. Moving a task to a local DSQ may switch to the source or destination rq before synchronously invoking ops.dequeue() through the following path: SCX_CALL_OP(dispatch, rq) ops.dispatch() scx_bpf_dsq_move_to_local() scx_flush_dispatch_buf() finish_dispatch() dispatch_to_local_dsq() scx_dispatch_enqueue() local_dsq_post_enq() call_task_dequeue() SCX_CALL_OP_TASK(dequeue, locked_rq, ...) The nested callback saves the recorded rq and restores it on return. If the rq tracking does not follow the lock switch, update_locked_rq() can trigger the following lockdep assertion while restoring an rq which is no longer held: WARNING: kernel/sched/sched.h:1641 at call_task_dequeue+0x160/0x170 Call Trace: scx_dispatch_enqueue+0x2b0/0x460 dispatch_to_local_dsq+0x138/0x230 scx_flush_dispatch_buf+0x1af/0x220 scx_bpf_dsq_move_to_local___v2+0xe2/0x1c0 bpf__sched_ext_ops_dispatch+0x4b/0xa7 do_pick_task_scx+0x3b6/0x910 __pick_next_task+0x105/0x1f0 __schedule+0x3e7/0x1980 Introduce switch_rq_lock() to update the tracking state together with each rq lock handoff. Use it in dispatch_to_local_dsq(), move_remote_task_to_local_dsq() and the in-balance paths of scx_dsq_move(), ensuring that scx_locked_rq() consistently refers to the rq whose lock is actually held throughout the lock dance. Fixes: 7fb39e4eb4c3 ("sched_ext: Save and restore scx_locked_rq across SCX_CALL_OP") Cc: stable@vger.kernel.org # 7.1+ Signed-off-by: Andrea Righi Signed-off-by: Tejun Heo --- kernel/sched/ext/ext.c | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/kernel/sched/ext/ext.c b/kernel/sched/ext/ext.c index e75e2fd5ab7e..ee66dacfc92c 100644 --- a/kernel/sched/ext/ext.c +++ b/kernel/sched/ext/ext.c @@ -479,6 +479,18 @@ static bool rq_is_open(struct rq *rq, u64 enq_flags) */ DEFINE_PER_CPU(struct rq *, scx_locked_rq_state); +static void switch_rq_lock(struct rq *from, struct rq *to) +{ + bool tracked = scx_locked_rq() == from; + + if (tracked) + update_locked_rq(NULL); + raw_spin_rq_unlock(from); + raw_spin_rq_lock(to); + if (tracked) + update_locked_rq(to); +} + /* * Flipped on enable per sch->is_cid_type. Declared in internal.h so * subsystem inlines can read it. @@ -2274,8 +2286,7 @@ static void move_remote_task_to_local_dsq(struct task_struct *p, u64 enq_flags, deactivate_task(src_rq, p, 0); set_task_cpu(p, cpu_of(dst_rq)); - raw_spin_rq_unlock(src_rq); - raw_spin_rq_lock(dst_rq); + switch_rq_lock(src_rq, dst_rq); /* * We want to pass scx-specific enq_flags but activate_task() will @@ -2608,9 +2619,8 @@ static void dispatch_to_local_dsq(struct scx_sched *sch, struct rq *rq, /* switch to @src_rq lock */ if (locked_rq != src_rq) { - raw_spin_rq_unlock(locked_rq); + switch_rq_lock(locked_rq, src_rq); locked_rq = src_rq; - raw_spin_rq_lock(src_rq); } /* task_rq couldn't have changed if we're still the holding cpu */ @@ -2644,10 +2654,8 @@ static void dispatch_to_local_dsq(struct scx_sched *sch, struct rq *rq, } /* switch back to @rq lock */ - if (locked_rq != rq) { - raw_spin_rq_unlock(locked_rq); - raw_spin_rq_lock(rq); - } + if (locked_rq != rq) + switch_rq_lock(locked_rq, rq); } /** @@ -8825,10 +8833,8 @@ static bool scx_dsq_move(struct bpf_iter_scx_dsq_kern *kit, in_balance = this_rq->scx.flags & SCX_RQ_IN_BALANCE; if (in_balance) { - if (this_rq != src_rq) { - raw_spin_rq_unlock(this_rq); - raw_spin_rq_lock(src_rq); - } + if (this_rq != src_rq) + switch_rq_lock(this_rq, src_rq); } else { raw_spin_rq_lock(src_rq); } @@ -8860,10 +8866,8 @@ static bool scx_dsq_move(struct bpf_iter_scx_dsq_kern *kit, dispatched = true; out: if (in_balance) { - if (this_rq != locked_rq) { - raw_spin_rq_unlock(locked_rq); - raw_spin_rq_lock(this_rq); - } + if (this_rq != locked_rq) + switch_rq_lock(locked_rq, this_rq); } else { raw_spin_rq_unlock_irqrestore(locked_rq, flags); } From 65dfde57d1e29ce2b76fc23dd565eccd5c0bc0f0 Mon Sep 17 00:00:00 2001 From: Ricardo Robaina Date: Thu, 2 Jul 2026 11:04:11 -0300 Subject: [PATCH 0806/1054] audit: fix potential integer overflow in audit_log_n_hex() The function calculates new_len as len << 1 for hex encoding. This has two overflow risks: the shift itself can overflow when len is large, and the result can be truncated when assigned to new_len (declared as int) from the size_t calculation. Fix by using check_shl_overflow() to catch shift overflow and changing new_len and loop counter i to size_t to prevent truncation. Cc: stable@vger.kernel.org Fixes: 168b7173959f ("AUDIT: Clean up logging of untrusted strings") Reviewed-by: Richard Guy Briggs Signed-off-by: Ricardo Robaina [PM: remove vertical whitspace noise] Signed-off-by: Paul Moore --- kernel/audit.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/kernel/audit.c b/kernel/audit.c index feaa4e2271d7..562476937fa7 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -62,6 +62,7 @@ #include #include #include +#include #include "audit.h" @@ -2080,7 +2081,8 @@ void audit_log_format(struct audit_buffer *ab, const char *fmt, ...) void audit_log_n_hex(struct audit_buffer *ab, const unsigned char *buf, size_t len) { - int i, avail, new_len; + int avail; + size_t i, new_len; unsigned char *ptr; struct sk_buff *skb; @@ -2090,7 +2092,12 @@ void audit_log_n_hex(struct audit_buffer *ab, const unsigned char *buf, BUG_ON(!ab->skb); skb = ab->skb; avail = skb_tailroom(skb); - new_len = len<<1; + + if (check_shl_overflow(len, 1, &new_len)) { + audit_log_format(ab, "?"); + return; + } + if (new_len >= avail) { /* Round the buffer request up to the next multiple */ new_len = AUDIT_BUFSIZ*(((new_len-avail)/AUDIT_BUFSIZ) + 1); From 4ec10f38ff901dc10503d57cbdcf941248419ac1 Mon Sep 17 00:00:00 2001 From: Andrea Righi Date: Wed, 8 Jul 2026 09:46:48 +0200 Subject: [PATCH 0807/1054] sched_ext: Enable tick for finite slices on nohz_full set_next_task_scx() updates the tick dependency before __schedule() updates rq->curr. When switching from a non-EXT task, such as idle, to an EXT task with a finite slice, sched_update_tick_dependency() checks the outgoing task and can allow the tick to remain stopped. The dependency can also be lost without a slice-type transition. After a finite-slice task leaves the CPU idle, the enqueue path can clear the dependency against the idle rq->curr. SCX_RQ_CAN_STOP_TICK still records a finite slice, so another finite task skips the transition block and can run without the ticks needed to expire its slice. The reverse mismatch can also happen when the last finite-slice EXT task is dequeued: sub_nr_running() updates the dependency before rq->curr changes, so the outgoing task state can keep the dependency set after the CPU goes idle. Fix this by unconditionally enabling the scheduler tick whenever a finite-slice EXT task is selected on a nohz_full CPU. Moreover, when the last runnable EXT task leaves, ignore the outgoing EXT slice state so the generic scheduler can correctly re-evaluate and clear the tick dependency. Fixes: 22a920209ab6 ("sched_ext: Implement tickless support") Signed-off-by: Andrea Righi Signed-off-by: Tejun Heo --- kernel/sched/ext/ext.c | 47 +++++++++++++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/kernel/sched/ext/ext.c b/kernel/sched/ext/ext.c index ee66dacfc92c..2dc6977d7984 100644 --- a/kernel/sched/ext/ext.c +++ b/kernel/sched/ext/ext.c @@ -2986,24 +2986,38 @@ static void set_next_task_scx(struct rq *rq, struct task_struct *p, bool first) /* * @p is getting newly scheduled or got kicked after someone updated its - * slice. Refresh whether tick can be stopped. See scx_can_stop_tick(). + * slice. Update SCX_RQ_CAN_STOP_TICK to reflect whether the tick can be + * stopped. See scx_can_stop_tick(). + * + * Moreover, refresh the load_avgs just when transitioning in and out of + * nohz. In the future, we might want to add a mechanism to update + * load_avgs periodically on tick-stopped CPUs. */ - if ((p->scx.slice == SCX_SLICE_INF) != - (bool)(rq->scx.flags & SCX_RQ_CAN_STOP_TICK)) { - if (p->scx.slice == SCX_SLICE_INF) + if (p->scx.slice == SCX_SLICE_INF) { + if (!(rq->scx.flags & SCX_RQ_CAN_STOP_TICK)) { + /* + * Bypass mode always assigns finite slices, so @p + * can't have an infinite slice while bypassing. + * Therefore, sched_update_tick_dependency() can safely + * evaluate the outgoing task. + */ rq->scx.flags |= SCX_RQ_CAN_STOP_TICK; - else - rq->scx.flags &= ~SCX_RQ_CAN_STOP_TICK; + sched_update_tick_dependency(rq); - sched_update_tick_dependency(rq); + update_other_load_avgs(rq); + } + } else { + if (rq->scx.flags & SCX_RQ_CAN_STOP_TICK) { + rq->scx.flags &= ~SCX_RQ_CAN_STOP_TICK; + update_other_load_avgs(rq); + } /* - * For now, let's refresh the load_avgs just when transitioning - * in and out of nohz. In the future, we might want to add a - * mechanism which calls the following periodically on - * tick-stopped CPUs. + * @rq still references the outgoing scheduling context. A finite + * slice is sufficient by itself to require the tick. */ - update_other_load_avgs(rq); + if (tick_nohz_full_cpu(cpu_of(rq))) + tick_nohz_dep_set_cpu(cpu_of(rq), TICK_DEP_BIT_SCHED); } } @@ -4329,6 +4343,15 @@ bool scx_can_stop_tick(struct rq *rq) if (p->sched_class != &ext_sched_class) return true; + /* + * @rq->curr may still reference an outgoing EXT task after it has been + * dequeued. If no EXT tasks are accounted on @rq, ignore its stale + * slice state. If another task is dispatched from a DSQ, + * set_next_task_scx() will update the dependency for the incoming task. + */ + if (!rq->scx.nr_running) + return true; + if (scx_bypassing(sch, cpu_of(rq))) return false; From cfe950d79f524e72bc263f2b153a6d905a75a794 Mon Sep 17 00:00:00 2001 From: Andrea Righi Date: Wed, 8 Jul 2026 09:46:49 +0200 Subject: [PATCH 0808/1054] selftests/sched_ext: Verify nohz_full tick behavior Finite-slice EXT tasks need the periodic scheduler tick to expire their slices even when nohz_full is enabled. Add a regression test that selects a nohz_full CPU and exercises both infinite-to-finite and finite-to-finite slice transitions across an idle interval. For each finite task, verify that its ops.tick() callback is invoked. Skip the test when an allowed nohz_full CPU and a separate housekeeping CPU are not available. Signed-off-by: Andrea Righi Signed-off-by: Tejun Heo --- tools/testing/selftests/sched_ext/Makefile | 1 + .../selftests/sched_ext/nohz_tick.bpf.c | 65 ++++ tools/testing/selftests/sched_ext/nohz_tick.c | 347 ++++++++++++++++++ 3 files changed, 413 insertions(+) create mode 100644 tools/testing/selftests/sched_ext/nohz_tick.bpf.c create mode 100644 tools/testing/selftests/sched_ext/nohz_tick.c diff --git a/tools/testing/selftests/sched_ext/Makefile b/tools/testing/selftests/sched_ext/Makefile index 5d2dffca0e91..3cfe90e0f34f 100644 --- a/tools/testing/selftests/sched_ext/Makefile +++ b/tools/testing/selftests/sched_ext/Makefile @@ -176,6 +176,7 @@ auto-test-targets := \ maybe_null \ minimal \ non_scx_kfunc_deny \ + nohz_tick \ numa \ allowed_cpus \ peek_dsq \ diff --git a/tools/testing/selftests/sched_ext/nohz_tick.bpf.c b/tools/testing/selftests/sched_ext/nohz_tick.bpf.c new file mode 100644 index 000000000000..6998c5dd6bcb --- /dev/null +++ b/tools/testing/selftests/sched_ext/nohz_tick.bpf.c @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES + * + * Exercise tick dependency transitions between infinite and finite slices. + */ +#include + +char _license[] SEC("license") = "GPL"; + +const volatile s32 test_cpu; +bool finite_phase; +u64 nr_inf_running; +u64 nr_finite_running; +u64 nr_finite_ticks; + +UEI_DEFINE(uei); + +s32 BPF_STRUCT_OPS(nohz_tick_select_cpu, struct task_struct *p, s32 prev_cpu, + u64 wake_flags) +{ + return prev_cpu; +} + +void BPF_STRUCT_OPS(nohz_tick_enqueue, struct task_struct *p, u64 enq_flags) +{ + u64 slice = finite_phase ? 1000000ULL : SCX_SLICE_INF; + + scx_bpf_dsq_insert(p, SCX_DSQ_GLOBAL, slice, enq_flags); + if (enq_flags & SCX_ENQ_LAST) + scx_bpf_kick_cpu(test_cpu, SCX_KICK_IDLE); +} + +void BPF_STRUCT_OPS(nohz_tick_running, struct task_struct *p) +{ + if (bpf_get_smp_processor_id() != test_cpu) + return; + + if (finite_phase) + __sync_fetch_and_add(&nr_finite_running, 1); + else + __sync_fetch_and_add(&nr_inf_running, 1); +} + +void BPF_STRUCT_OPS(nohz_tick_tick, struct task_struct *p) +{ + if (bpf_get_smp_processor_id() == test_cpu && finite_phase) + __sync_fetch_and_add(&nr_finite_ticks, 1); +} + +void BPF_STRUCT_OPS(nohz_tick_exit, struct scx_exit_info *ei) +{ + UEI_RECORD(uei, ei); +} + +SEC(".struct_ops.link") +struct sched_ext_ops nohz_tick_ops = { + .select_cpu = (void *)nohz_tick_select_cpu, + .enqueue = (void *)nohz_tick_enqueue, + .running = (void *)nohz_tick_running, + .tick = (void *)nohz_tick_tick, + .exit = (void *)nohz_tick_exit, + .name = "nohz_tick", + .timeout_ms = 1000U, +}; diff --git a/tools/testing/selftests/sched_ext/nohz_tick.c b/tools/testing/selftests/sched_ext/nohz_tick.c new file mode 100644 index 000000000000..028f54391c2c --- /dev/null +++ b/tools/testing/selftests/sched_ext/nohz_tick.c @@ -0,0 +1,347 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES + * + * Validate that a finite-slice EXT task restarts the scheduler tick when it + * follows an infinite-slice EXT task and an idle interval on a NOHZ_FULL CPU. + */ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "nohz_tick.bpf.skel.h" +#include "scx_test.h" + +#ifndef SCHED_EXT +#define SCHED_EXT 7 +#endif + +#define MIN_FINITE_TICKS 3 +#define PHASE_TIMEOUT_MS 1000 + +struct nohz_tick_ctx { + struct nohz_tick *skel; + cpu_set_t original_mask; + int test_cpu; +}; + +static int first_allowed_cpu(const cpu_set_t *mask, int first, int last) +{ + int cpu; + + for (cpu = first; cpu <= last && cpu < CPU_SETSIZE; cpu++) + if (CPU_ISSET(cpu, mask)) + return cpu; + + return -1; +} + +static int find_nohz_full_cpu(const cpu_set_t *allowed) +{ + char buf[4096], *cur, *end; + FILE *file; + + file = fopen("/sys/devices/system/cpu/nohz_full", "r"); + if (!file) + return -1; + if (!fgets(buf, sizeof(buf), file)) { + fclose(file); + return -1; + } + fclose(file); + + cur = buf; + while (*cur) { + long first, last; + int cpu; + + while (*cur == ' ' || *cur == '\t' || *cur == ',') + cur++; + if (*cur < '0' || *cur > '9') + break; + + errno = 0; + first = strtol(cur, &end, 10); + if (errno || end == cur || first < 0 || first >= CPU_SETSIZE) + return -1; + cur = end; + last = first; + if (*cur == '-') { + cur++; + errno = 0; + last = strtol(cur, &end, 10); + if (errno || end == cur || last < first) + return -1; + cur = end; + } + + cpu = first_allowed_cpu(allowed, first, last); + if (cpu >= 0) + return cpu; + } + + return -1; +} + +static pid_t start_worker(int cpu) +{ + struct sched_param param = {}; + cpu_set_t mask; + pid_t parent; + pid_t pid; + + parent = getpid(); + pid = fork(); + if (pid != 0) + return pid; + if (prctl(PR_SET_PDEATHSIG, SIGKILL) || getppid() != parent) + _exit(1); + + /* + * Become EXT before touching the target so it stays idle until wakeup. + */ + if (sched_setscheduler(0, SCHED_EXT, ¶m)) + _exit(1); + + CPU_ZERO(&mask); + CPU_SET(cpu, &mask); + if (sched_setaffinity(0, sizeof(mask), &mask)) + _exit(1); + + for (;;) + asm volatile("" ::: "memory"); +} + +static void stop_worker(pid_t pid) +{ + if (pid <= 0) + return; + + kill(pid, SIGKILL); + waitpid(pid, NULL, 0); +} + +static int pause_worker(pid_t pid) +{ + int status; + + if (kill(pid, SIGSTOP)) + return -errno; + if (waitpid(pid, &status, WUNTRACED) != pid) + return -errno; + if (!WIFSTOPPED(status)) + return -ECHILD; + + return 0; +} + +static bool wait_for_counter(const u64 *counter, u64 value, int timeout_ms) +{ + int elapsed; + + for (elapsed = 0; elapsed < timeout_ms; elapsed++) { + if (__atomic_load_n(counter, __ATOMIC_RELAXED) >= value) + return true; + usleep(1000); + } + + return false; +} + +static enum scx_test_status setup(void **ctx_ptr) +{ + struct nohz_tick_ctx *ctx; + cpu_set_t controller_mask; + int cpu; + + ctx = calloc(1, sizeof(*ctx)); + SCX_FAIL_IF(!ctx, "Failed to allocate context"); + if (sched_getaffinity(0, sizeof(ctx->original_mask), + &ctx->original_mask)) { + free(ctx); + SCX_FAIL("Failed to get affinity (%d)", errno); + } + + cpu = find_nohz_full_cpu(&ctx->original_mask); + if (cpu < 0) { + fprintf(stderr, "SKIP: no allowed NOHZ_FULL CPU\n"); + free(ctx); + return SCX_TEST_SKIP; + } + + controller_mask = ctx->original_mask; + CPU_CLR(cpu, &controller_mask); + if (CPU_COUNT(&controller_mask) == 0) { + fprintf(stderr, "SKIP: no housekeeping CPU available\n"); + free(ctx); + return SCX_TEST_SKIP; + } + + ctx->test_cpu = cpu; + ctx->skel = nohz_tick__open(); + if (!ctx->skel) { + free(ctx); + SCX_FAIL("Failed to open skeleton"); + } + + SCX_ENUM_INIT(ctx->skel); + ctx->skel->rodata->test_cpu = cpu; + ctx->skel->struct_ops.nohz_tick_ops->flags |= SCX_OPS_SWITCH_PARTIAL | + SCX_OPS_ENQ_LAST; + if (nohz_tick__load(ctx->skel)) { + nohz_tick__destroy(ctx->skel); + free(ctx); + SCX_FAIL("Failed to load skeleton"); + } + + if (sched_setaffinity(0, sizeof(controller_mask), &controller_mask)) { + nohz_tick__destroy(ctx->skel); + free(ctx); + SCX_FAIL("Failed to move controller off CPU %d (%d)", cpu, errno); + } + + *ctx_ptr = ctx; + return SCX_TEST_PASS; +} + +static enum scx_test_status run(void *ctx_ptr) +{ + struct nohz_tick_ctx *ctx = ctx_ptr; + struct nohz_tick *skel = ctx->skel; + struct bpf_link *link = NULL; + enum scx_test_status status = SCX_TEST_FAIL; + pid_t finite_worker = -1; + pid_t inf_worker = -1; + u64 finite_running; + u64 finite_ticks; + int ret; + + link = bpf_map__attach_struct_ops(skel->maps.nohz_tick_ops); + if (!link) { + SCX_ERR("Failed to attach scheduler"); + goto out; + } + + /* + * Establish SCX_RQ_CAN_STOP_TICK with an infinite-slice task. + */ + inf_worker = start_worker(ctx->test_cpu); + if (inf_worker < 0) { + SCX_ERR("Failed to start infinite-slice worker (%d)", errno); + goto out; + } + if (!wait_for_counter(&skel->bss->nr_inf_running, 1, + PHASE_TIMEOUT_MS)) { + SCX_ERR("Infinite-slice worker was not scheduled"); + goto out; + } + + /* Block without exiting so the rq retains the infinite-slice state. */ + ret = pause_worker(inf_worker); + if (ret) { + SCX_ERR("Failed to stop infinite-slice worker (%d)", ret); + goto out; + } + + /* Let the target enter idle with its tick stopped. */ + usleep(100000); + + /* + * The next EXT task receives a finite slice and must restart the tick. + */ + __atomic_store_n(&skel->bss->finite_phase, true, __ATOMIC_RELEASE); + finite_worker = start_worker(ctx->test_cpu); + if (finite_worker < 0) { + SCX_ERR("Failed to start finite-slice worker (%d)", errno); + goto out; + } + if (!wait_for_counter(&skel->bss->nr_finite_running, 1, + PHASE_TIMEOUT_MS)) { + SCX_ERR("Finite-slice worker was not scheduled"); + goto out; + } + if (!wait_for_counter(&skel->bss->nr_finite_ticks, MIN_FINITE_TICKS, + PHASE_TIMEOUT_MS)) { + SCX_ERR("Finite-slice worker received only %llu scheduler ticks", + (unsigned long long)skel->bss->nr_finite_ticks); + goto out; + } + stop_worker(finite_worker); + finite_worker = -1; + + /* + * Leave the CPU idle after a finite-slice task. The next finite-slice + * task must restart the tick even though the slice type is unchanged. + */ + usleep(100000); + finite_running = __atomic_load_n(&skel->bss->nr_finite_running, + __ATOMIC_RELAXED); + finite_ticks = __atomic_load_n(&skel->bss->nr_finite_ticks, + __ATOMIC_RELAXED); + + finite_worker = start_worker(ctx->test_cpu); + if (finite_worker < 0) { + SCX_ERR("Failed to start second finite-slice worker (%d)", errno); + goto out; + } + if (!wait_for_counter(&skel->bss->nr_finite_running, + finite_running + 1, PHASE_TIMEOUT_MS)) { + SCX_ERR("Second finite-slice worker was not scheduled"); + goto out; + } + if (!wait_for_counter(&skel->bss->nr_finite_ticks, + finite_ticks + MIN_FINITE_TICKS, + PHASE_TIMEOUT_MS)) { + SCX_ERR("Second finite-slice worker received only %llu scheduler ticks", + (unsigned long long)(skel->bss->nr_finite_ticks - + finite_ticks)); + goto out; + } + + if (skel->data->uei.kind != EXIT_KIND(SCX_EXIT_NONE)) { + SCX_ERR("Scheduler exited unexpectedly (kind=%llu code=%lld)", + (unsigned long long)skel->data->uei.kind, + (long long)skel->data->uei.exit_code); + goto out; + } + + fprintf(stderr, "CPU %d received %llu finite-slice ticks\n", + ctx->test_cpu, + (unsigned long long)skel->bss->nr_finite_ticks); + status = SCX_TEST_PASS; +out: + stop_worker(finite_worker); + stop_worker(inf_worker); + if (link) + bpf_link__destroy(link); + return status; +} + +static void cleanup(void *ctx_ptr) +{ + struct nohz_tick_ctx *ctx = ctx_ptr; + + sched_setaffinity(0, sizeof(ctx->original_mask), &ctx->original_mask); + nohz_tick__destroy(ctx->skel); + free(ctx); +} + +struct scx_test nohz_tick = { + .name = "nohz_tick", + .description = "Verify finite EXT slices restart the NOHZ_FULL tick", + .setup = setup, + .run = run, + .cleanup = cleanup, +}; +REGISTER_SCX_TEST(&nohz_tick) From 91f0063ca99eb6d96c85bfe6b65405570511b99f Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 20:29:26 +0800 Subject: [PATCH 0809/1054] regulator: mt6316: add missing MODULE_DEVICE_TABLE() The driver has an OF match table wired to .of_match_table, but does not export the table with MODULE_DEVICE_TABLE(). Add the missing MODULE_DEVICE_TABLE(of, ...) entry so module alias information is generated for OF based module autoloading. This is a source-level fix. It does not claim dynamic hardware reproduction; the evidence is the driver-owned match table, its use by the platform driver, and the missing module alias publication. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260704122926.21586-1-pengpeng@iscas.ac.cn Signed-off-by: Mark Brown --- drivers/regulator/mt6316-regulator.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/regulator/mt6316-regulator.c b/drivers/regulator/mt6316-regulator.c index 952852bbe923..b170506eec57 100644 --- a/drivers/regulator/mt6316-regulator.c +++ b/drivers/regulator/mt6316-regulator.c @@ -329,6 +329,7 @@ static const struct of_device_id mt6316_regulator_match[] = { { .compatible = "mediatek,mt6316d-regulator", .data = (void *)MT6316_TYPE_4PHASE }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, mt6316_regulator_match); static struct spmi_driver mt6316_regulator_driver = { .driver = { From 87063bab451963c2e424154437cd14c926127c42 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 20:43:52 +0800 Subject: [PATCH 0810/1054] regulator: mt6363: add missing MODULE_DEVICE_TABLE() The driver has an OF match table wired to .of_match_table, but does not export the table with MODULE_DEVICE_TABLE(). Add the missing MODULE_DEVICE_TABLE(of, ...) entry so module alias information is generated for OF based module autoloading. This is a source-level fix. It does not claim dynamic hardware reproduction; the evidence is the driver-owned match table, its use by the platform driver, and the missing module alias publication. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260704124352.7981-1-pengpeng@iscas.ac.cn Signed-off-by: Mark Brown --- drivers/regulator/mt6363-regulator.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/regulator/mt6363-regulator.c b/drivers/regulator/mt6363-regulator.c index 0aebcbda0a19..aa6a8eb7ac4b 100644 --- a/drivers/regulator/mt6363-regulator.c +++ b/drivers/regulator/mt6363-regulator.c @@ -927,6 +927,7 @@ static const struct of_device_id mt6363_regulator_match[] = { { .compatible = "mediatek,mt6363-regulator" }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, mt6363_regulator_match); static struct platform_driver mt6363_regulator_driver = { .driver = { From 5140f099ecd8a2f2808b7f7b720ee1bad8468974 Mon Sep 17 00:00:00 2001 From: Benjamin Coddington Date: Thu, 11 Jun 2026 17:02:15 -0400 Subject: [PATCH 0811/1054] NFSv4: include MAY_WRITE in open permission mask for O_TRUNC POSIX requires write permission to truncate a file, so an open() that specifies O_TRUNC must be authorized for write access regardless of the O_ACCMODE access mode. nfs_open_permission_mask() builds the access mask passed to nfs_may_open(), which is the local authorization gate for OPENs the client serves itself from a cached write delegation via the can_open_delegated() path in nfs4_try_open_cached(). The mask is derived from O_ACCMODE alone, so an open(O_RDONLY | O_TRUNC) against a file the caller cannot write requests only MAY_READ and passes the local check. The OPEN is then satisfied locally and the truncation is issued to the server as a SETATTR(size=0) over the delegation stateid, which the server accepts under standard write-delegation semantics. POSIX requires that this open fail with EACCES. Include MAY_WRITE in the mask whenever O_TRUNC is set so the local check matches the access the server would have enforced. Suggested-by: Trond Myklebust Fixes: af22f94ae02a ("NFSv4: Simplify _nfs4_do_access()") Cc: stable@vger.kernel.org Signed-off-by: Benjamin Coddington Signed-off-by: Anna Schumaker --- fs/nfs/dir.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index c7b723c18620..c7caffb31935 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -3346,6 +3346,8 @@ static int nfs_open_permission_mask(int openflags) mask |= MAY_READ; if ((openflags & O_ACCMODE) != O_RDONLY) mask |= MAY_WRITE; + if (openflags & O_TRUNC) + mask |= MAY_WRITE; } return mask; From 27934d02cbeb8a957dd11c985a579e58d30c5270 Mon Sep 17 00:00:00 2001 From: Benjamin Coddington Date: Tue, 7 Jul 2026 11:07:16 -0400 Subject: [PATCH 0812/1054] NFS: Charge unstable writes by request size, not folio size nfs_folio_mark_unstable() and nfs_folio_clear_commit() charge and uncharge NR_WRITEBACK/WB_WRITEBACK by folio_nr_pages(folio) once per *request* added to or removed from a commit list. This is correct only when a folio has a single associated request. When pg_test splits a folio into N sub-folio requests (e.g. pNFS flexfiles striping with a stripe unit smaller than the folio size, or plain wsize-limited splitting), each of the N requests independently charges the whole folio's page count, inflating the accounting by a factor of N per folio. With large folios and small stripe units this reaches multiple orders of magnitude: a 2 MiB folio split into 512 4 KiB requests can charge up to 512x its real size, pushing global dirty+writeback accounting past the system's dirty threshold and forcing every buffered writer on the host into the hard-throttle path, including unrelated in-kernel NFS server threads sharing the box. Charge each request only for the pages it actually covers. Fixes: 0c493b5cf16e ("NFS: Convert buffered writes to use folios") Cc: stable@vger.kernel.org Signed-off-by: Benjamin Coddington Assisted-By: Claude Sonnet 5 Signed-off-by: Anna Schumaker --- fs/nfs/internal.h | 12 +++++++----- fs/nfs/pnfs_nfs.c | 2 +- fs/nfs/write.c | 14 ++++++++------ 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index acaeff7ddfdf..e4533f583632 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -851,17 +851,19 @@ void nfs_super_set_maxbytes(struct super_block *sb, __u64 maxfilesize) } /* - * Record the page as unstable (an extra writeback period) and mark its - * inode as dirty. + * Record the request's range as unstable (an extra writeback period) and + * mark its inode as dirty. */ -static inline void nfs_folio_mark_unstable(struct folio *folio, +static inline void nfs_folio_mark_unstable(struct nfs_page *req, struct nfs_commit_info *cinfo) { + struct folio *folio = nfs_page_to_folio(req); + if (folio && !cinfo->dreq) { struct inode *inode = folio->mapping->host; - long nr = folio_nr_pages(folio); + long nr = DIV_ROUND_UP(req->wb_bytes, PAGE_SIZE); - /* This page is really still in write-back - just that the + /* This range is really still in write-back - just that the * writeback is happening on the server now. */ node_stat_mod_folio(folio, NR_WRITEBACK, nr); diff --git a/fs/nfs/pnfs_nfs.c b/fs/nfs/pnfs_nfs.c index 0ff43dbcb7cd..648c95b78eea 100644 --- a/fs/nfs/pnfs_nfs.c +++ b/fs/nfs/pnfs_nfs.c @@ -1199,7 +1199,7 @@ pnfs_layout_mark_request_commit(struct nfs_page *req, nfs_request_add_commit_list_locked(req, list, cinfo); mutex_unlock(&NFS_I(cinfo->inode)->commit_mutex); - nfs_folio_mark_unstable(nfs_page_to_folio(req), cinfo); + nfs_folio_mark_unstable(req, cinfo); return; out_resched: mutex_unlock(&NFS_I(cinfo->inode)->commit_mutex); diff --git a/fs/nfs/write.c b/fs/nfs/write.c index fcffb8c9e9df..d2b03ceaeb4f 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -807,7 +807,7 @@ nfs_request_add_commit_list(struct nfs_page *req, struct nfs_commit_info *cinfo) mutex_lock(&NFS_I(cinfo->inode)->commit_mutex); nfs_request_add_commit_list_locked(req, &cinfo->mds->list, cinfo); mutex_unlock(&NFS_I(cinfo->inode)->commit_mutex); - nfs_folio_mark_unstable(nfs_page_to_folio(req), cinfo); + nfs_folio_mark_unstable(req, cinfo); } EXPORT_SYMBOL_GPL(nfs_request_add_commit_list); @@ -866,10 +866,12 @@ nfs_mark_request_commit(struct nfs_page *req, struct pnfs_layout_segment *lseg, nfs_request_add_commit_list(req, cinfo); } -static void nfs_folio_clear_commit(struct folio *folio) +static void nfs_folio_clear_commit(struct nfs_page *req) { + struct folio *folio = nfs_page_to_folio(req); + if (folio) { - long nr = folio_nr_pages(folio); + long nr = DIV_ROUND_UP(req->wb_bytes, PAGE_SIZE); node_stat_mod_folio(folio, NR_WRITEBACK, -nr); bdi_wb_stat_mod(folio->mapping->host, WB_WRITEBACK, -nr); @@ -889,7 +891,7 @@ static void nfs_clear_request_commit(struct nfs_commit_info *cinfo, nfs_request_remove_commit_list(req, cinfo); } mutex_unlock(&NFS_I(inode)->commit_mutex); - nfs_folio_clear_commit(nfs_page_to_folio(req)); + nfs_folio_clear_commit(req); } } @@ -1741,7 +1743,7 @@ void nfs_retry_commit(struct list_head *page_list, req = nfs_list_entry(page_list->next); nfs_list_remove_request(req); nfs_mark_request_commit(req, lseg, cinfo, ds_commit_idx); - nfs_folio_clear_commit(nfs_page_to_folio(req)); + nfs_folio_clear_commit(req); nfs_unlock_and_release_request(req); } } @@ -1813,7 +1815,7 @@ static void nfs_commit_release_pages(struct nfs_commit_data *data) req = nfs_list_entry(data->pages.next); nfs_list_remove_request(req); folio = nfs_page_to_folio(req); - nfs_folio_clear_commit(folio); + nfs_folio_clear_commit(req); dprintk("NFS: commit (%s/%llu %d@%lld)", nfs_req_openctx(req)->dentry->d_sb->s_id, From 58aad119f4d65a8f91262687db302046a94fa9ac Mon Sep 17 00:00:00 2001 From: Kanala Ramalingeswara Reddy Date: Wed, 17 Jun 2026 18:04:36 +0530 Subject: [PATCH 0813/1054] drm/amdgpu: add support for PSP version 15.0.9 Initialize PSP Version 15_0_9 Signed-off-by: Kanala Ramalingeswara Reddy Signed-off-by: Granthali Vinodkumar Dhandar Reviewed-by: Mario Limonciello Signed-off-by: Alex Deucher (cherry picked from commit ef71f00173228904763552b7405169023f8034a8) --- drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c | 1 + drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c index 853365dee2a7..bb0ee17b4cbf 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c @@ -2304,6 +2304,7 @@ static int amdgpu_discovery_set_psp_ip_blocks(struct amdgpu_device *adev) amdgpu_device_ip_block_add(adev, &psp_v14_0_ip_block); break; case IP_VERSION(15, 0, 0): + case IP_VERSION(15, 0, 9): amdgpu_device_ip_block_add(adev, &psp_v15_0_ip_block); break; case IP_VERSION(15, 0, 8): diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c index 96e1b72b9e1c..e0c0d7872e45 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c @@ -275,6 +275,7 @@ static int psp_early_init(struct amdgpu_ip_block *ip_block) psp->boot_time_tmr = false; break; case IP_VERSION(15, 0, 0): + case IP_VERSION(15, 0, 9): psp_v15_0_0_set_psp_funcs(psp); psp->boot_time_tmr = false; break; @@ -3475,7 +3476,9 @@ static int psp_load_non_psp_fw(struct psp_context *psp) amdgpu_ip_version(adev, MP0_HWIP, 0) == IP_VERSION(15, 0, 0) || amdgpu_ip_version(adev, MP0_HWIP, 0) == - IP_VERSION(15, 0, 8)) && + IP_VERSION(15, 0, 8) || + amdgpu_ip_version(adev, MP0_HWIP, 0) == + IP_VERSION(15, 0, 9)) && (ucode->ucode_id == AMDGPU_UCODE_ID_SDMA1 || ucode->ucode_id == AMDGPU_UCODE_ID_SDMA2 || ucode->ucode_id == AMDGPU_UCODE_ID_SDMA3)) From e2469bde3fcaa087dcfeccbc6306a9e76a79344e Mon Sep 17 00:00:00 2001 From: Kanala Ramalingeswara Reddy Date: Wed, 17 Jun 2026 18:04:53 +0530 Subject: [PATCH 0814/1054] drm/amdgpu: add support for SMU version 15.0.9 Initialize SMU Version 15_0_9 Signed-off-by: Kanala Ramalingeswara Reddy Signed-off-by: Granthali Vinodkumar Dhandar Reviewed-by: Mario Limonciello Signed-off-by: Alex Deucher (cherry picked from commit 1dfd4e84b5beec353a81d61af9eaf4e5a56e0c57) --- drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c | 1 + drivers/gpu/drm/amd/amdgpu/soc21.c | 1 + drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c | 1 + drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0.c | 1 + drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.c | 3 ++- 5 files changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c index bb0ee17b4cbf..7b9bb998906d 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c @@ -2376,6 +2376,7 @@ static int amdgpu_discovery_set_smu_ip_blocks(struct amdgpu_device *adev) case IP_VERSION(15, 0, 0): case IP_VERSION(15, 0, 5): case IP_VERSION(15, 0, 8): + case IP_VERSION(15, 0, 9): amdgpu_device_ip_block_add(adev, &smu_v15_0_ip_block); break; default: diff --git a/drivers/gpu/drm/amd/amdgpu/soc21.c b/drivers/gpu/drm/amd/amdgpu/soc21.c index 1677e88a4e36..a2733d1637f0 100644 --- a/drivers/gpu/drm/amd/amdgpu/soc21.c +++ b/drivers/gpu/drm/amd/amdgpu/soc21.c @@ -406,6 +406,7 @@ soc21_asic_reset_method(struct amdgpu_device *adev) case IP_VERSION(14, 0, 4): case IP_VERSION(14, 0, 5): case IP_VERSION(15, 0, 0): + case IP_VERSION(15, 0, 9): return AMD_RESET_METHOD_MODE2; default: if (amdgpu_dpm_is_baco_supported(adev)) diff --git a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c index 208a2fba6d40..762ec3cede96 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c +++ b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c @@ -802,6 +802,7 @@ static int smu_set_funcs(struct amdgpu_device *adev) break; case IP_VERSION(15, 0, 0): case IP_VERSION(15, 0, 5): + case IP_VERSION(15, 0, 9): smu_v15_0_0_set_ppt_funcs(smu); break; case IP_VERSION(15, 0, 8): diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0.c b/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0.c index a1318409e4b5..8fc99e93ac53 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0.c @@ -664,6 +664,7 @@ int smu_v15_0_gfx_off_control(struct smu_context *smu, bool enable) switch (amdgpu_ip_version(adev, MP1_HWIP, 0)) { case IP_VERSION(15, 0, 0): + case IP_VERSION(15, 0, 9): if (!(adev->pm.pp_feature & PP_GFXOFF_MASK)) return 0; if (enable) diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.c index a214ddbd4c86..bb8d09e73c7d 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.c @@ -1177,7 +1177,8 @@ static int smu_v15_0_common_get_dpm_profile_freq(struct smu_context *smu, smu_v15_0_common_get_dpm_ultimate_freq(smu, SMU_SOCCLK, NULL, &clk_limit); break; case SMU_FCLK: - if (amdgpu_ip_version(smu->adev, MP1_HWIP, 0) == IP_VERSION(15, 0, 0)) + if (amdgpu_ip_version(smu->adev, MP1_HWIP, 0) == IP_VERSION(15, 0, 0) || + amdgpu_ip_version(smu->adev, MP1_HWIP, 0) == IP_VERSION(15, 0, 9)) smu_v15_0_common_get_dpm_ultimate_freq(smu, SMU_FCLK, NULL, &clk_limit); else clk_limit = SMU_15_0_UMD_PSTATE_FCLK; From 9c809e489802e40d7845f4daf5a9d828830a8724 Mon Sep 17 00:00:00 2001 From: Suresh Guttula Date: Fri, 26 Jun 2026 11:39:51 +0530 Subject: [PATCH 0815/1054] drm/amdgpu: Disable JDPG on VCN5_3 JDPG does not support on VCN5 This patch will disable JDPG, because DPG is not correctly copying the JRBC Read/Write Pointers (R/WPTR) from the PG (Power Gating) block to JRBC. Signed-off-by: Suresh Guttula Reviewed-by: Mario Limonciello Signed-off-by: Alex Deucher (cherry picked from commit ea3fdd1eda088030d8925f023613728969f55955) --- drivers/gpu/drm/amd/amdgpu/soc21.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/soc21.c b/drivers/gpu/drm/amd/amdgpu/soc21.c index a2733d1637f0..e0b80abcd075 100644 --- a/drivers/gpu/drm/amd/amdgpu/soc21.c +++ b/drivers/gpu/drm/amd/amdgpu/soc21.c @@ -862,7 +862,6 @@ static int soc21_common_early_init(struct amdgpu_ip_block *ip_block) AMD_CG_SUPPORT_BIF_LS; adev->pg_flags = AMD_PG_SUPPORT_VCN_DPG | AMD_PG_SUPPORT_VCN | - AMD_PG_SUPPORT_JPEG_DPG | AMD_PG_SUPPORT_JPEG | AMD_PG_SUPPORT_GFX_PG; adev->external_rev_id = adev->rev_id + 0xF; @@ -890,7 +889,6 @@ static int soc21_common_early_init(struct amdgpu_ip_block *ip_block) AMD_CG_SUPPORT_BIF_LS; adev->pg_flags = AMD_PG_SUPPORT_VCN_DPG | AMD_PG_SUPPORT_VCN | - AMD_PG_SUPPORT_JPEG_DPG | AMD_PG_SUPPORT_JPEG | AMD_PG_SUPPORT_GFX_PG; adev->external_rev_id = adev->rev_id + 0x40; From 18a7826aea6fd09f2d371c02cec70c7234fc4879 Mon Sep 17 00:00:00 2001 From: Kenneth Feng Date: Thu, 25 Jun 2026 17:48:22 +0800 Subject: [PATCH 0816/1054] drm/amd/amdgpu: disable ASPM on VI if pcie dpm is disabled Disable ASPM on VI if PCIE dpm is disabled. Fixes: bb00bf17328d ("drm/amd/amdgpu: decouple ASPM with pcie dpm") Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/5370 Signed-off-by: Kenneth Feng Reviewed-by: Yang Wang Signed-off-by: Alex Deucher (cherry picked from commit 873a8d6b3c0a386408c891e4ff1c684fa11783e1) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index 8d6502a94306..53335ca96b1d 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -1333,7 +1333,8 @@ static bool amdgpu_device_aspm_support_quirk(struct amdgpu_device *adev) * It's unclear if this is a platform-specific or GPU-specific issue. * Disable ASPM on SI for the time being. */ - if (adev->family == AMDGPU_FAMILY_SI) + if (adev->family == AMDGPU_FAMILY_SI || + (!(adev->pm.pp_feature & PP_PCIE_DPM_MASK) && adev->family == AMDGPU_FAMILY_VI)) return true; #if IS_ENABLED(CONFIG_X86) From 614e8a989d462876c4f6a659f56e1075e72ad515 Mon Sep 17 00:00:00 2001 From: Jesse Zhang Date: Wed, 1 Jul 2026 18:17:03 +0800 Subject: [PATCH 0817/1054] drm/amdgpu: trigger GPU recovery when userq destroy fails to unmap a hung queue Destroying a hung user queue issues a MES REMOVE_QUEUE that times out, The destroy path only logged the error and freed the queue, so the next userq submission failed and forced a GPU reset attributed to an innocent workload. Kick the userq reset work when unmap fails so the GPU is recovered at destroy time. Acked-by: Alex Deucher Signed-off-by: Jesse Zhang Signed-off-by: Alex Deucher (cherry picked from commit 8396b9de4198a54ec4760a94a179347540a9764d) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c index ef3f0213cc46..d854343b3734 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c @@ -523,6 +523,15 @@ amdgpu_userq_destroy(struct amdgpu_userq_mgr *uq_mgr, struct amdgpu_usermode_que amdgpu_userq_cleanup(queue); mutex_unlock(&uq_mgr->userq_mutex); + /* + * A failed unmap means MES could not remove the hung queue and is now + * unresponsive. Recover the GPU here so the wedged MES does not fail + * the next, unrelated queue submission and trigger a reset attributed + * to an innocent workload. + */ + if (r) + queue_work(adev->reset_domain->wq, &uq_mgr->reset_work); + cancel_delayed_work_sync(&queue->hang_detect_work); uq_funcs->mqd_destroy(queue); queue->userq_mgr = NULL; From 04cc4aa3617b0ed67e859f91f09de5d896a46f3a Mon Sep 17 00:00:00 2001 From: Shahyan Soltani Date: Mon, 6 Jul 2026 08:15:21 -0400 Subject: [PATCH 0818/1054] drm/amdgpu: fix lifetime issue of amdgpu_vm_get_task_info_pasid() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The vm pointer returned from amdgpu_vm_get_vm_from_pasid() is only valid while the lock is still being held. Once xa_unlock_irqrestore is called and returned, the pointer is no longer under lock and is subject to modification. Since, the caller still dereferences vm->task_info in amdgpu_vm_get_task_info_vm() after the lock is removed, this causes a use after unlock problem. Remove the lifetime issue present in amdgpu_vm_get_task_info_pasid() through removing the amdgpu_vm_get_vm_from_pasid() function from amdgpu_vm.c and making the relevant code inline to hold the lock while it is still in use. Signed-off-by: Shahyan Soltani Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit 9d01579f3f868b333acc901815972685989092c7) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c index f317f888b59f..f1dcf4f5bb78 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c @@ -2460,19 +2460,6 @@ static void amdgpu_vm_destroy_task_info(struct kref *kref) kfree(ti); } -static inline struct amdgpu_vm * -amdgpu_vm_get_vm_from_pasid(struct amdgpu_device *adev, u32 pasid) -{ - struct amdgpu_vm *vm; - unsigned long flags; - - xa_lock_irqsave(&adev->vm_manager.pasids, flags); - vm = xa_load(&adev->vm_manager.pasids, pasid); - xa_unlock_irqrestore(&adev->vm_manager.pasids, flags); - - return vm; -} - /** * amdgpu_vm_put_task_info - reference down the vm task_info ptr * @@ -2519,8 +2506,16 @@ amdgpu_vm_get_task_info_vm(struct amdgpu_vm *vm) struct amdgpu_task_info * amdgpu_vm_get_task_info_pasid(struct amdgpu_device *adev, u32 pasid) { - return amdgpu_vm_get_task_info_vm( - amdgpu_vm_get_vm_from_pasid(adev, pasid)); + struct amdgpu_task_info *ti; + struct amdgpu_vm *vm; + unsigned long flags; + + xa_lock_irqsave(&adev->vm_manager.pasids, flags); + vm = xa_load(&adev->vm_manager.pasids, pasid); + ti = amdgpu_vm_get_task_info_vm(vm); + xa_unlock_irqrestore(&adev->vm_manager.pasids, flags); + + return ti; } static int amdgpu_vm_create_task_info(struct amdgpu_vm *vm) From 9743132a41f4d9d0e54c5f2adcb821b04796bab1 Mon Sep 17 00:00:00 2001 From: Benjamin Marzinski Date: Thu, 2 Jul 2026 21:43:39 -0400 Subject: [PATCH 0819/1054] dm-log: fix a bitset_size overflow on 32bit machines Commit c20e36b7631d ("dm log: fix out-of-bounds write due to region_count overflow") made sure that region_count could fit in an unsigned int. But the bitmap memory isn't allocated based on region_count. It uses bitset_size (a size_t variable). The first step of calculating bitset_size is to set it to region_count, rounded up to a multiple of BITS_PER_LONG. If region_size is less than BITS_PER_LONG smaller than UINT_MAX, it will get rounded up to 2^32. On a 32bit architecture, this will make bitset_size wrap around to 0 and fail, despite region_count being valid. Since bitset_size gets divided by 8, it can hold any valid region_count. It just needs a special case to handle the rollover. If it is 0, the value rolled over, and bitset size should be set to the number of bytes needed to hold 2^32 bits. Signed-off-by: Benjamin Marzinski Signed-off-by: Mikulas Patocka Fixes: c20e36b7631d ("dm log: fix out-of-bounds write due to region_count overflow") Cc: stable@vger.kernel.org --- drivers/md/dm-log.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/md/dm-log.c b/drivers/md/dm-log.c index d316757a328b..2ddeb4250c59 100644 --- a/drivers/md/dm-log.c +++ b/drivers/md/dm-log.c @@ -425,6 +425,9 @@ static int create_log_context(struct dm_dirty_log *log, struct dm_target *ti, */ bitset_size = dm_round_up(region_count, BITS_PER_LONG); bitset_size >>= BYTE_SHIFT; + /* Handle dm_round_up rollover on 32-bit systems */ + if (!bitset_size) + bitset_size = 1UL << (BITS_PER_LONG - BYTE_SHIFT); lc->bitset_uint32_count = bitset_size / sizeof(*lc->clean_bits); From a868196f03c2b19418ae3d2b69e195d668a271e5 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Thu, 2 Jul 2026 00:27:35 +0000 Subject: [PATCH 0820/1054] dm era: fix out-of-bounds memory access for non-zero start sector dm-era tracks writes in target-relative blocks, but era_map() calculates the writeset block before applying the target offset. Tables with a non-zero start sector can therefore pass an absolute mapped-device block to metadata_current_marked(). If the absolute block is beyond the current writeset size, writeset_marked() tests past the end of the in-core bitset. KASAN reports this as a vmalloc-out-of-bounds access. Apply the target offset before calculating the era block so writeset lookups use the target-relative block number. Assisted-by: Codex:gpt-5.5-cyber-preview Signed-off-by: Samuel Moelius Reviewed-by: Ming-Hung Tsai Signed-off-by: Mikulas Patocka Cc: stable@vger.kernel.org Fixes: eec40579d848 ("dm: add era target") --- drivers/md/dm-era-target.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/md/dm-era-target.c b/drivers/md/dm-era-target.c index 05285c04ff2c..18aed0e2a508 100644 --- a/drivers/md/dm-era-target.c +++ b/drivers/md/dm-era-target.c @@ -1229,6 +1229,7 @@ static dm_block_t get_block(struct era *era, struct bio *bio) static void remap_to_origin(struct era *era, struct bio *bio) { bio_set_dev(bio, era->origin_dev->bdev); + bio->bi_iter.bi_sector = dm_target_offset(era->ti, bio->bi_iter.bi_sector); } /* @@ -1560,7 +1561,7 @@ static void era_dtr(struct dm_target *ti) static int era_map(struct dm_target *ti, struct bio *bio) { struct era *era = ti->private; - dm_block_t block = get_block(era, bio); + dm_block_t block; /* * All bios get remapped to the origin device. We do this now, but @@ -1568,6 +1569,7 @@ static int era_map(struct dm_target *ti, struct bio *bio) * block is marked in this era. */ remap_to_origin(era, bio); + block = get_block(era, bio); /* * REQ_PREFLUSH bios carry no data, so we're not interested in them. From 31d6e6c0ba8d5a7bd59660035a089307100c5e8e Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Wed, 1 Jul 2026 18:13:50 +0200 Subject: [PATCH 0821/1054] dm-verity: fix buffer overflow in FEC calculation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There's a buffer overflow in dm-verity-fec: if (neras && *neras <= v->fec->roots) fio->erasures[(*neras)++] = i; This allows *neras to reach roots + 1 (the post-increment pushes it past roots). This value is then passed as no_eras to decode_rs8(). Inside the RS decoder (lib/reed_solomon/decode_rs.c:113-121), the erasure locator polynomial loop writes lambda[j] where j can reach nroots + 1 — one element past the end of lambda[] (which is sized nroots + 1, valid indices 0..nroots). The out-of-bounds write lands on syn[0], corrupting the syndrome buffer. Signed-off-by: Mikulas Patocka Assisted-by: Claude:claude-opus-4-6 Cc: stable@vger.kernel.org Fixes: a739ff3f543a ("dm verity: add support for forward error correction") Reviewed-by: Sami Tolvanen Signed-off-by: Mikulas Patocka --- drivers/md/dm-verity-fec.c | 4 ++-- drivers/md/dm-verity-fec.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/md/dm-verity-fec.c b/drivers/md/dm-verity-fec.c index 85ad9dc210ff..c79f60df3a90 100644 --- a/drivers/md/dm-verity-fec.c +++ b/drivers/md/dm-verity-fec.c @@ -220,7 +220,7 @@ static int fec_read_bufs(struct dm_verity *v, struct dm_verity_io *io, PTR_ERR(bbuf)); /* assume the block is corrupted */ - if (neras && *neras <= v->fec->roots) + if (neras && *neras < v->fec->roots) fio->erasures[(*neras)++] = i; continue; @@ -238,7 +238,7 @@ static int fec_read_bufs(struct dm_verity *v, struct dm_verity_io *io, * skip if we have already found the theoretical * maximum number (i.e. fec->roots) of erasures */ - if (neras && *neras <= v->fec->roots && + if (neras && *neras < v->fec->roots && fec_is_erasure(v, io, want_digest, bbuf)) fio->erasures[(*neras)++] = i; } diff --git a/drivers/md/dm-verity-fec.h b/drivers/md/dm-verity-fec.h index 50b5e187d5cc..3885b514fc23 100644 --- a/drivers/md/dm-verity-fec.h +++ b/drivers/md/dm-verity-fec.h @@ -47,7 +47,7 @@ struct dm_verity_fec { /* per-bio data */ struct dm_verity_fec_io { struct rs_control *rs; /* Reed-Solomon state */ - int erasures[DM_VERITY_FEC_MAX_ROOTS + 1]; /* erasures for decode_rs8 */ + int erasures[DM_VERITY_FEC_MAX_ROOTS]; /* erasures for decode_rs8 */ u8 *output; /* buffer for corrected output */ unsigned int level; /* recursion level */ unsigned int nbufs; /* number of buffers allocated */ From 5bcd4d3058ebaf46ad2e163829d87dd4870c7a45 Mon Sep 17 00:00:00 2001 From: Ming-Hung Tsai Date: Tue, 30 Jun 2026 20:17:44 +0800 Subject: [PATCH 0822/1054] dm thin metadata: fix metadata snapshot consistency on commit failure __reserve_metadata_snap() and __release_metadata_snap() modify the superblock's held_root directly in the block_manager's buffer. If the subsequent metadata commit fails, the held_root gets flushed to disk through the abort_transaction path, resulting in inconsistent metadata. Reproducer 1: __reserve_metadata_snap() 1. Create a 2 MiB metadata device and make the region after the 14th block inaccessible, to trigger metadata commit failure in the subsequent reserve_metadata_snap operation. The 14th block will be the shadow destination for the index block. dmsetup create tmeta --table "0 112 linear /dev/sdc 0 112 3984 error" 2. Create a 16 MiB thin-pool dmsetup create tdata --table "0 32768 zero" dd if=/dev/zero of=/dev/mapper/tmeta bs=4k count=1 dmsetup create tpool --table "0 32768 thin-pool /dev/mapper/tmeta \ /dev/mapper/tdata 128 0 1 skip_block_zeroing" 3. Take a metadata snapshot to trigger metadata commit failure and transaction abort. However, the held_root is written to disk, breaking metadata consistency. dmsetup message tpool 0 "reserve_metadata_snap" thin_check v1.2.2 result: Bad reference count for metadata block 6. Expected 2, but space map contains 1. Bad reference count for metadata block 7. Expected 2, but space map contains 1. Bad reference count for metadata block 13. Expected 1, but space map contains 0. Reproducer 2: __release_metadata_snap() 1. Create a 2 MiB metadata device and make the region after the 16th block inaccessible, to trigger metadata commit failure in the subsequent release_metadata_snap operation. The 16th block will be the shadow destination for the index block. dmsetup create tmeta --table "0 128 linear /dev/sdc 0 128 3968 error" 2. Create a 16 MiB thin-pool dmsetup create tdata --table "0 32768 zero" dd if=/dev/zero of=/dev/mapper/tmeta bs=4k count=1 dmsetup create tpool --table "0 32768 thin-pool /dev/mapper/tmeta \ /dev/mapper/tdata 128 0 1 skip_block_zeroing" 3. Reserve then release the metadata snapshot, to trigger metadata commit failure and transaction abort. The held_root gets removed from the on-disk superblock, causing inconsistent metadata. dmsetup message tpool 0 "reserve_metadata_snap" dmsetup message tpool 0 "release_metadata_snap" thin_check v1.2.2 result: Bad reference count for metadata block 6. Expected 1, but space map contains 2. Bad reference count for metadata block 7. Expected 1, but space map contains 2. 1 metadata blocks have leaked. Fix by deferring the held_root update to commit time. Additionally, move the existing-snapshot check in __reserve_metadata_snap before the shadow operation to avoid unnecessary work. In __release_metadata_snap, clear pmd->held_root before btree deletion so partial failure leaks blocks rather than leaving a stale reference, and unlock the snapshot block before decrementing its refcount. Fixes: 991d9fa02da0 ("dm: add thin provisioning target") Cc: stable@vger.kernel.org Signed-off-by: Ming-Hung Tsai Signed-off-by: Mikulas Patocka --- drivers/md/dm-thin-metadata.c | 63 ++++++++++------------------------- 1 file changed, 18 insertions(+), 45 deletions(-) diff --git a/drivers/md/dm-thin-metadata.c b/drivers/md/dm-thin-metadata.c index b6a2d2081a24..a010ff1e5c92 100644 --- a/drivers/md/dm-thin-metadata.c +++ b/drivers/md/dm-thin-metadata.c @@ -186,6 +186,7 @@ struct dm_pool_metadata { uint32_t time; dm_block_t root; dm_block_t details_root; + dm_block_t held_root; struct list_head thin_devices; uint64_t trans_id; unsigned long flags; @@ -748,6 +749,7 @@ static int __open_metadata(struct dm_pool_metadata *pmd) */ pmd->root = le64_to_cpu(disk_super->data_mapping_root); pmd->details_root = le64_to_cpu(disk_super->device_details_root); + pmd->held_root = le64_to_cpu(disk_super->held_root); __setup_btree_details(pmd); dm_bm_unlock(sblock); @@ -838,6 +840,7 @@ static int __begin_transaction(struct dm_pool_metadata *pmd) pmd->time = le32_to_cpu(disk_super->time); pmd->root = le64_to_cpu(disk_super->data_mapping_root); pmd->details_root = le64_to_cpu(disk_super->device_details_root); + pmd->held_root = le64_to_cpu(disk_super->held_root); pmd->trans_id = le64_to_cpu(disk_super->trans_id); pmd->flags = le32_to_cpu(disk_super->flags); pmd->data_block_size = le32_to_cpu(disk_super->data_block_size); @@ -928,6 +931,7 @@ static int __commit_transaction(struct dm_pool_metadata *pmd) disk_super->time = cpu_to_le32(pmd->time); disk_super->data_mapping_root = cpu_to_le64(pmd->root); disk_super->device_details_root = cpu_to_le64(pmd->details_root); + disk_super->held_root = cpu_to_le64(pmd->held_root); disk_super->trans_id = cpu_to_le64(pmd->trans_id); disk_super->flags = cpu_to_le32(pmd->flags); @@ -1333,9 +1337,14 @@ static int __reserve_metadata_snap(struct dm_pool_metadata *pmd) { int r, inc; struct thin_disk_superblock *disk_super; - struct dm_block *copy, *sblock; + struct dm_block *copy; dm_block_t held_root; + if (pmd->held_root) { + DMWARN("Pool metadata snapshot already exists: release this before taking another."); + return -EBUSY; + } + /* * We commit to ensure the btree roots which we increment in a * moment are up to date. @@ -1361,14 +1370,6 @@ static int __reserve_metadata_snap(struct dm_pool_metadata *pmd) held_root = dm_block_location(copy); disk_super = dm_block_data(copy); - if (le64_to_cpu(disk_super->held_root)) { - DMWARN("Pool metadata snapshot already exists: release this before taking another."); - - dm_tm_dec(pmd->tm, held_root); - dm_tm_unlock(pmd->tm, copy); - return -EBUSY; - } - /* * Wipe the spacemap since we're not publishing this. */ @@ -1384,18 +1385,8 @@ static int __reserve_metadata_snap(struct dm_pool_metadata *pmd) dm_tm_inc(pmd->tm, le64_to_cpu(disk_super->device_details_root)); dm_tm_unlock(pmd->tm, copy); - /* - * Write the held root into the superblock. - */ - r = superblock_lock(pmd, &sblock); - if (r) { - dm_tm_dec(pmd->tm, held_root); - return r; - } + pmd->held_root = held_root; - disk_super = dm_block_data(sblock); - disk_super->held_root = cpu_to_le64(held_root); - dm_bm_unlock(sblock); return 0; } @@ -1415,18 +1406,10 @@ static int __release_metadata_snap(struct dm_pool_metadata *pmd) { int r; struct thin_disk_superblock *disk_super; - struct dm_block *sblock, *copy; + struct dm_block *copy; dm_block_t held_root; - r = superblock_lock(pmd, &sblock); - if (r) - return r; - - disk_super = dm_block_data(sblock); - held_root = le64_to_cpu(disk_super->held_root); - disk_super->held_root = cpu_to_le64(0); - - dm_bm_unlock(sblock); + held_root = pmd->held_root; if (!held_root) { DMWARN("No pool metadata snapshot found: nothing to release."); @@ -1437,13 +1420,15 @@ static int __release_metadata_snap(struct dm_pool_metadata *pmd) if (r) return r; + pmd->held_root = 0; + disk_super = dm_block_data(copy); dm_btree_del(&pmd->info, le64_to_cpu(disk_super->data_mapping_root)); dm_btree_del(&pmd->details_info, le64_to_cpu(disk_super->device_details_root)); - dm_sm_dec_block(pmd->metadata_sm, held_root); - dm_tm_unlock(pmd->tm, copy); + dm_sm_dec_block(pmd->metadata_sm, held_root); + return 0; } @@ -1462,19 +1447,7 @@ int dm_pool_release_metadata_snap(struct dm_pool_metadata *pmd) static int __get_metadata_snap(struct dm_pool_metadata *pmd, dm_block_t *result) { - int r; - struct thin_disk_superblock *disk_super; - struct dm_block *sblock; - - r = dm_bm_read_lock(pmd->bm, THIN_SUPERBLOCK_LOCATION, - &sb_validator, &sblock); - if (r) - return r; - - disk_super = dm_block_data(sblock); - *result = le64_to_cpu(disk_super->held_root); - - dm_bm_unlock(sblock); + *result = pmd->held_root; return 0; } From d9c631e3fbd44246a2be781d26cfacbb9b8ec127 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Mon, 29 Jun 2026 15:47:40 +0000 Subject: [PATCH 0823/1054] dm-pcache: reject option groups without values The pcache target parses optional arguments as name/value pairs. A table that advertises one optional argument and supplies only a recognized option name, for example "cache_mode", reaches parse_cache_opts() with argc == 1. The parser consumes the name, decrements argc to zero, then calls dm_shift_arg() again for the value. dm_shift_arg() returns NULL when no arguments remain, and the following strcmp() dereferences that NULL pointer. Check that each recognized option has a value before consuming it. This keeps valid "cache_mode writeback" and "data_crc true/false" tables unchanged while making malformed tables fail during target construction with a precise missing-value error. Assisted-by: Codex:gpt-5.5-cyber-preview Signed-off-by: Samuel Moelius Reviewed-by: Zheng Gu Signed-off-by: Mikulas Patocka Fixes: 1d57628ff95b ("dm-pcache: add persistent cache target in device-mapper") Cc: stable@vger.kernel.org --- drivers/md/dm-pcache/dm_pcache.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/md/dm-pcache/dm_pcache.c b/drivers/md/dm-pcache/dm_pcache.c index 81c795c0400e..d5cfd162c063 100644 --- a/drivers/md/dm-pcache/dm_pcache.c +++ b/drivers/md/dm-pcache/dm_pcache.c @@ -168,6 +168,10 @@ static int parse_cache_opts(struct dm_pcache *pcache, struct dm_arg_set *as, argc--; if (!strcmp(arg, "cache_mode")) { + if (!argc) { + *error = "Missing value for cache_mode"; + return -EINVAL; + } arg = dm_shift_arg(as); if (!strcmp(arg, "writeback")) { opts->cache_mode = PCACHE_CACHE_MODE_WRITEBACK; @@ -177,6 +181,10 @@ static int parse_cache_opts(struct dm_pcache *pcache, struct dm_arg_set *as, } argc--; } else if (!strcmp(arg, "data_crc")) { + if (!argc) { + *error = "Missing value for data_crc"; + return -EINVAL; + } arg = dm_shift_arg(as); if (!strcmp(arg, "true")) { opts->data_crc = true; From da991cbd6767282000e995247c7dad39d22874c2 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 20 Jun 2026 21:33:03 +0200 Subject: [PATCH 0824/1054] dm-inlinecrypt: Fix an error handling path in inlinecrypt_ctr() All error handling paths, except but this one, branch to the 'bad' label in the error handling path. If not done, there is a memory leak and some sensitive data may be kept around. So, fix this error path and also do the needed clean-up. Also, fix missing goto in the "Wrong alignment of iv_offset sector" path. Fixes: e7f57d2c47e2 ("dm-inlinecrypt: add target for inline block device encryption") Signed-off-by: Christophe JAILLET Reviewed-by: Eric Biggers Signed-off-by: Mikulas Patocka --- drivers/md/dm-inlinecrypt.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/md/dm-inlinecrypt.c b/drivers/md/dm-inlinecrypt.c index be1b4aa8f28b..41293c18d10f 100644 --- a/drivers/md/dm-inlinecrypt.c +++ b/drivers/md/dm-inlinecrypt.c @@ -347,7 +347,8 @@ static int inlinecrypt_ctr(struct dm_target *ti, unsigned int argc, char **argv) err = get_key_size(&argv[1]); if (err < 0) { ti->error = "Cannot parse key size"; - return -EINVAL; + err = -EINVAL; + goto bad; } ctx->key_size = err; @@ -398,6 +399,7 @@ static int inlinecrypt_ctr(struct dm_target *ti, unsigned int argc, char **argv) if (ctx->iv_offset & ((ctx->sector_size >> SECTOR_SHIFT) - 1)) { ti->error = "Wrong alignment of iv_offset sector"; err = -EINVAL; + goto bad; } ctx->max_dun = (ctx->iv_offset + ti->len - 1) >> From 981ccd97f7153d310dfa92a534525bbaf46752c2 Mon Sep 17 00:00:00 2001 From: Ingo Blechschmidt Date: Wed, 17 Jun 2026 16:33:42 +0200 Subject: [PATCH 0825/1054] dm: avoid leaking the caller's thread keyring via the table device file The refactoring in commit a28d893eb327 ("md: port block device access to file") accidentally causes the caller's thread keyring to be kept alive long beyond the caller's lifetime. As a result, "cryptsetup luksSuspend" silently fails to wipe the LUKS volume key from memory. In detail: "cryptsetup luksOpen" uses its supposedly ephemeral thread keyring to pass the volume key to the kernel. dm-crypt's crypt_set_keyring_key() copies the key material into its own crypt_config structure and then drops its own reference to the key in the keyring with key_put(). With this fix, restoring pre-v6.9 behavior, the copy in the thread keyring is then promptly garbage collected, such that exactly one copy of the volume key remains. This single copy is correctly wiped from memory on "cryptsetup luksSuspend". Without this fix, the thread keyring and the volume key in it remains. This second copy is only freed on "luksClose". "luksSuspend" neither knows about this copy nor has any way to remove it, so the key remains recoverable from RAM after a suspend that is documented to have wiped it. This fix should not introduce new security problems, as the code is anyway gated by CAP_SYS_ADMIN. The device-mapper core, not the calling task, is the legitimate owner of this long-lived file. Fixes: a28d893eb327 ("md: port block device access to file") Closes: https://gitlab.com/cryptsetup/cryptsetup/-/work_items/993 Link: https://www.speicherleck.de/iblech/cryptsetup-luksSuspend-issue-reproduction/ Signed-off-by: Ingo Blechschmidt Signed-off-by: Mikulas Patocka Cc: stable@vger.kernel.org Tested-by: Ondrej Kozina --- drivers/md/dm.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 7287bed6eb64..d413bfaf3527 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -735,7 +735,16 @@ static struct table_device *open_table_device(struct mapped_device *md, return ERR_PTR(-ENOMEM); refcount_set(&td->count, 1); - bdev_file = bdev_file_open_by_dev(dev, mode, _dm_claim_ptr, NULL); + /* + * Open the backing device with kernel rather than caller + * credentials. Otherwise the caller's credentials would be + * pinned in bdev_file->f_cred until the table device is closed. + * That would keep the caller's thread keyring alive long beyond the + * lifetime of the caller, breaking userspace expectation (e.g. + * cryptsetup(8) leaking the LUKS volume key). + */ + scoped_with_kernel_creds() + bdev_file = bdev_file_open_by_dev(dev, mode, _dm_claim_ptr, NULL); if (IS_ERR(bdev_file)) { r = PTR_ERR(bdev_file); goto out_free_td; From 9ae672606c17891d90b282e3490b817620549599 Mon Sep 17 00:00:00 2001 From: Cao Guanghui Date: Wed, 17 Jun 2026 14:00:52 +0800 Subject: [PATCH 0826/1054] dm era: fix NULL pointer dereference in metadata_open() metadata_open() returns NULL when kzalloc_obj() fails, but the caller era_ctr() only checks IS_ERR(md). Since IS_ERR(NULL) returns false, the NULL pointer is treated as a valid result and later assigned to era->md, leading to a NULL pointer dereference when the metadata is accessed. Fix this by returning ERR_PTR(-ENOMEM) on allocation failure, consistent with dm-cache-metadata.c, dm-thin-metadata.c, and dm-clone-metadata.c which all use ERR_PTR(-ENOMEM) for the same pattern. Fixes: eec40579d848 ("dm: add era target") Signed-off-by: Cao Guanghui Reviewed-by: Su Yue Reviewed-by: Ming-Hung Tsai Signed-off-by: Mikulas Patocka --- drivers/md/dm-era-target.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/md/dm-era-target.c b/drivers/md/dm-era-target.c index 18aed0e2a508..cef288194804 100644 --- a/drivers/md/dm-era-target.c +++ b/drivers/md/dm-era-target.c @@ -810,8 +810,10 @@ static struct era_metadata *metadata_open(struct block_device *bdev, int r; struct era_metadata *md = kzalloc_obj(*md); - if (!md) - return NULL; + if (!md) { + DMERR("could not allocate metadata struct"); + return ERR_PTR(-ENOMEM); + } md->bdev = bdev; md->block_size = block_size; From 8d4dd2db7f4f3af0d3cd51111d050301c1f00a5c Mon Sep 17 00:00:00 2001 From: Cao Guanghui Date: Wed, 17 Jun 2026 14:00:53 +0800 Subject: [PATCH 0827/1054] dm era: fix error code propagation in era_ctr() era_ctr() replaces the actual error codes returned by dm_get_device() and dm_set_target_max_io_len() with hardcoded -EINVAL, discarding the real reason for the failure (e.g. -ENODEV, -ENOMEM). This makes it harder for users to diagnose problems and is inconsistent with other dm targets (dm-thin, dm-verity, dm-flakey, dm-ebs) which propagate the original error. Fix all three sites to return 'r' instead of -EINVAL. Signed-off-by: Cao Guanghui Reviewed-by: Su Yue Reviewed-by: Ming-Hung Tsai Signed-off-by: Mikulas Patocka --- drivers/md/dm-era-target.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/md/dm-era-target.c b/drivers/md/dm-era-target.c index cef288194804..7fe4d19ade4f 100644 --- a/drivers/md/dm-era-target.c +++ b/drivers/md/dm-era-target.c @@ -1489,7 +1489,7 @@ static int era_ctr(struct dm_target *ti, unsigned int argc, char **argv) if (r) { ti->error = "Error opening metadata device"; era_destroy(era); - return -EINVAL; + return r; } r = dm_get_device(ti, argv[1], BLK_OPEN_READ | BLK_OPEN_WRITE, @@ -1497,7 +1497,7 @@ static int era_ctr(struct dm_target *ti, unsigned int argc, char **argv) if (r) { ti->error = "Error opening data device"; era_destroy(era); - return -EINVAL; + return r; } r = sscanf(argv[2], "%u%c", &era->sectors_per_block, &dummy); @@ -1511,7 +1511,7 @@ static int era_ctr(struct dm_target *ti, unsigned int argc, char **argv) if (r) { ti->error = "could not set max io len"; era_destroy(era); - return -EINVAL; + return r; } if (!valid_block_size(era->sectors_per_block)) { From f0262c3a3f14d60140f6b826d40d44edf62c36d6 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Fri, 26 Jun 2026 16:29:13 -0400 Subject: [PATCH 0828/1054] drm/gfx10: Program DB_RING_CONTROL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is needed to allocate occlusion counters across both gfx pipes. Fixes: b7a1a0ef12b8 ("drm/amd/amdgpu: add pipe1 hardware support") Reviewed-by: Timur Kristóf Signed-off-by: Alex Deucher (cherry picked from commit 6807352cbabb74b61ba42888769283af72191f66) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c index b4b27e4c495d..a9961d504833 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c @@ -5350,6 +5350,15 @@ static void gfx_v10_0_constants_init(struct amdgpu_device *adev) gfx_v10_0_get_tcc_info(adev); adev->gfx.config.pa_sc_tile_steering_override = gfx_v10_0_init_pa_sc_tile_steering_override(adev); + /* Program DB_RING_CONTROL for multiple GFX pipes + * Default power up value is 1. + * Possible values: + * 0 - split occlusion counters between gfx pipes + * 1 - all occlusion counters to pipe 0 + * 2 - all occlusion counters to pipe 1 + */ + WREG32_FIELD15(GC, 0, DB_RING_CONTROL, COUNTER_CONTROL, + (adev->gfx.me.num_pipe_per_me > 1) ? 0 : 1); /* XXX SH_MEM regs */ /* where to put LDS, scratch, GPUVM in FSA64 space */ From 3888a656e8143e4b40f01143648c768fecbfefc6 Mon Sep 17 00:00:00 2001 From: David Francis Date: Tue, 12 May 2026 14:49:41 -0400 Subject: [PATCH 0829/1054] drm/amdkfd: Check bounds on CRIU restore event id The valid amdkfd event ids go from 0 to KFD_SIGNAL_EVENT_LIMIT - 1. During CRIU restore, ensure that the provided event ids are in that range. v2: No need for lower bound check since idr_alloc rejects negative inputs v3: Also change error message to reflect new error condition Reviewed-by: David Yat Sin Signed-off-by: David Francis Signed-off-by: Alex Deucher (cherry picked from commit 5c6c247992d4d9200e073b83f4ec6c703c096845) --- drivers/gpu/drm/amd/amdkfd/kfd_events.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_events.c b/drivers/gpu/drm/amd/amdkfd/kfd_events.c index 81900b49d9d5..8cb43010ab1c 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_events.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_events.c @@ -204,7 +204,7 @@ static int create_signal_event(struct file *devkfd, struct kfd_process *p, ret = allocate_event_notification_slot(p, ev, restore_id); if (ret) { - pr_warn("Signal event wasn't created because out of kernel memory\n"); + pr_warn("Failed to create signal event notification slot\n"); return ret; } From 8a93f77aec65574b8152c88eb73613336f92f86b Mon Sep 17 00:00:00 2001 From: David Francis Date: Tue, 30 Jun 2026 09:58:18 -0400 Subject: [PATCH 0830/1054] drm/amdkfd: Don't acquire buffers during CRIU queue restore. kfd_criu_restore_queue's call of kfd_queue_acquire_buffers was failing for multiple reasons - The ctl_stack_size set by the CRIU plugin doesn't match what is expected by acquire_buffers - The svm buffer cannot be acquired at this point because CRIU may not have restored it, or may have restored it to a different address. The only reason acquire_buffers was necessary here was to avoid a null ptr dereference in init_user_queue. Just put in a check for that dereference; it doesn't appear to come up in real use cases right now. That is, there is no usage of CRIU with shared MES. This is a partial revert of commit 20a5e7ffdfec ("drm/amdkfd: Properly acquire queue buffers in CRIU restore") Fixes: 20a5e7ffdfec ("drm/amdkfd: Properly acquire queue buffers in CRIU restore") Reviewed-by: David Yat Sin Signed-off-by: David Francis Signed-off-by: Alex Deucher (cherry picked from commit 1cafa8b29e029eac3ddf64604f891b35dbf6262b) Cc: stable@vger.kernel.org --- .../gpu/drm/amd/amdkfd/kfd_process_queue_manager.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c index 0ac35789b239..b8c36907d536 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c @@ -265,6 +265,11 @@ static int init_user_queue(struct process_queue_manager *pqm, (*q)->process = pqm->process; if (dev->kfd->shared_resources.enable_mes) { + if (!q_properties->wptr_bo) { + pr_debug("Queue initialization with shared MES requires queue buffers to be initialized\n"); + return -EINVAL; + } + retval = amdgpu_amdkfd_alloc_kernel_mem(dev->adev, AMDGPU_MES_GANG_CTX_SIZE, AMDGPU_GEM_DOMAIN_GTT, @@ -1042,18 +1047,10 @@ int kfd_criu_restore_queue(struct kfd_process *p, memset(&qp, 0, sizeof(qp)); set_queue_properties_from_criu(&qp, q_data, NUM_XCC(pdd->dev->adev->gfx.xcc_mask)); - ret = kfd_queue_acquire_buffers(pdd, &qp); - if (ret) { - pr_debug("failed to acquire user queue buffers for CRIU\n"); - goto exit; - } - print_queue_properties(&qp); ret = pqm_create_queue(&p->pqm, pdd->dev, &qp, &queue_id, q_data, mqd, ctl_stack, NULL); if (ret) { - kfd_queue_unref_bo_vas(pdd, &qp); - kfd_queue_release_buffers(pdd, &qp); pr_err("Failed to create new queue err:%d\n", ret); goto exit; } From a88419af8dbd5abf8b1dfdfd9346376bce5413b4 Mon Sep 17 00:00:00 2001 From: Zhu Lingshan Date: Wed, 1 Jul 2026 16:08:12 +0800 Subject: [PATCH 0831/1054] amdkfd: properly free secondary context id Function kfd_process_free_id() should skip over the primary kfd process because its context id is fixed assigned, not allocated through the ida table. This function should only work on secondary contexts. Fixes: fac682a1d1af ("amdkfd: identify a secondary kfd process by its id") Signed-off-by: Zhu Lingshan Reviewed-by: Felix Kuehling Signed-off-by: Alex Deucher (cherry picked from commit 8799ba6fb6a48438aea20c82e74c2f2a3d2b2e7a) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdkfd/kfd_process.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_process.c b/drivers/gpu/drm/amd/amdkfd/kfd_process.c index ca71fa726e32..5fb3679e4e85 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_process.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_process.c @@ -910,7 +910,7 @@ static void kfd_process_free_id(struct kfd_process *process) { struct kfd_process *primary_process; - if (process->context_id != KFD_CONTEXT_ID_PRIMARY) + if (process->context_id == KFD_CONTEXT_ID_PRIMARY) return; primary_process = kfd_lookup_process_by_mm(process->lead_thread->mm); From bb52249fbbe948875155ccd45cd8d74bf4ae747b Mon Sep 17 00:00:00 2001 From: David Francis Date: Thu, 21 May 2026 09:18:59 -0400 Subject: [PATCH 0832/1054] drm/amdkfd: Check bounds in allocate_event_notification_slot The valid event ids go from 0 to KFD_SIGNAL_EVENT_LIMIT allocate_event_notification_slot has an option to specify an event id to allocate at, used by CRIU. We weren't checking the bounds on that value. Check them. v2: Lower bounds check is unecessary because of idr_alloc already rejecting negative numbers. Upper bounds check should be KFD_SIGNAL_EVENT_LIMIT since the signal mode mappings might not yet exist Signed-off-by: David Francis Reviewed-by: David Yat Sin Signed-off-by: Alex Deucher (cherry picked from commit 6853f1f6cbbeb3f53ebbbd7286536aeb2c5d5f50) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdkfd/kfd_events.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_events.c b/drivers/gpu/drm/amd/amdkfd/kfd_events.c index 8cb43010ab1c..2e97da597b3d 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_events.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_events.c @@ -107,6 +107,9 @@ static int allocate_event_notification_slot(struct kfd_process *p, } if (restore_id) { + if (*restore_id >= KFD_SIGNAL_EVENT_LIMIT) + return -EINVAL; + id = idr_alloc(&p->event_idr, ev, *restore_id, *restore_id + 1, GFP_KERNEL); } else { From e987eabc02646920cd13ab75902693e99735eca0 Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Mon, 6 Jul 2026 09:29:24 +0800 Subject: [PATCH 0833/1054] drm/amd/pm: fix smu14 power limit range calculation SMU14 derives the default PPT limit from SocketPowerLimitAc/Dc, but MsgLimits.Power may expose a different firmware limit for the same PPT0 throttler. Using those values independently as fixed min/max bases can report an incorrect configurable power range. Keep the socket power limit as the default value and as the fallback for current-limit queries. Calculate the reported range from both firmware values instead, using the lower value as the minimum base and the higher value as the maximum base before applying OD percentages. Signed-off-by: Yang Wang Reviewed-by: Kenneth Feng Signed-off-by: Alex Deucher (cherry picked from commit c936b8126b444401318fcbeb1828488cc5312dee) Cc: stable@vger.kernel.org --- .../drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) 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 fdc1456b885c..a6a88e7b2668 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 @@ -1621,19 +1621,23 @@ static int smu_v14_0_2_get_power_limit(struct smu_context *smu, table_context->power_play_table; PPTable_t *pptable = table_context->driver_pptable; CustomSkuTable_t *skutable = &pptable->CustomSkuTable; - int16_t od_percent_upper = 0, od_percent_lower = 0; + uint32_t pp_limit = smu->adev->pm.ac_power ? + skutable->SocketPowerLimitAc[PPT_THROTTLER_PPT0] : + skutable->SocketPowerLimitDc[PPT_THROTTLER_PPT0]; uint32_t msg_limit = pptable->SkuTable.MsgLimits.Power[PPT_THROTTLER_PPT0][POWER_SOURCE_AC]; - uint32_t power_limit; + uint32_t min_limit = min_t(uint32_t, pp_limit, msg_limit); + uint32_t max_limit = max_t(uint32_t, pp_limit, msg_limit); + int16_t od_percent_upper = 0, od_percent_lower = 0; + int ret; - if (smu_v14_0_get_current_power_limit(smu, &power_limit)) - power_limit = smu->adev->pm.ac_power ? - skutable->SocketPowerLimitAc[PPT_THROTTLER_PPT0] : - skutable->SocketPowerLimitDc[PPT_THROTTLER_PPT0]; + if (current_power_limit) { + ret = smu_v14_0_get_current_power_limit(smu, current_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 && @@ -1647,15 +1651,15 @@ static int smu_v14_0_2_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 = max_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 = min_limit * (100 + od_percent_lower); *min_power_limit /= 100; } From 47ea05f246bebc81c7796f56265cffd812cf0601 Mon Sep 17 00:00:00 2001 From: David Francis Date: Mon, 6 Jul 2026 10:19:04 -0400 Subject: [PATCH 0834/1054] drm/amdkfd: Check bounds on CRIU restore queue type and mqd size We weren't checking whether the values provided in the private data in kfd CRIU restore were within bounds. For queue type, add a KFD_QUEUE_TYPE_MAX and ensure the provided type is less than it. For mqd_size, add new function mqd_size_from_queue_type and confirm that the provided mqd_size matches expectations. Reviewed-by: David Yat Sin Signed-off-by: David Francis Signed-off-by: Alex Deucher (cherry picked from commit f19d8086f6644083c913d70bfdeee20e1b6f46a5) Cc: stable@vger.kernel.org --- .../drm/amd/amdkfd/kfd_device_queue_manager.c | 6 +++++ .../drm/amd/amdkfd/kfd_device_queue_manager.h | 2 ++ drivers/gpu/drm/amd/amdkfd/kfd_priv.h | 3 ++- .../amd/amdkfd/kfd_process_queue_manager.c | 24 +++++++++++++------ 4 files changed, 27 insertions(+), 8 deletions(-) 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 2e010c1f8828..678ec611a4f2 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c @@ -3818,6 +3818,12 @@ bool kfd_dqm_is_queue_in_process(struct device_queue_manager *dqm, dqm_unlock(dqm); return r; } + +size_t mqd_size_from_queue_type(struct device_queue_manager *dqm, enum kfd_queue_type type) +{ + return dqm->mqd_mgrs[get_mqd_type_from_queue_type(type)]->mqd_size; +} + #if defined(CONFIG_DEBUG_FS) static void seq_reg_dump(struct seq_file *m, diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.h b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.h index e0b6a47e7722..641b8ada82a0 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.h +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.h @@ -333,6 +333,8 @@ int debug_refresh_runlist(struct device_queue_manager *dqm); bool kfd_dqm_is_queue_in_process(struct device_queue_manager *dqm, struct qcm_process_device *qpd, int doorbell_off, u32 *queue_format); +size_t mqd_size_from_queue_type(struct device_queue_manager *dqm, + enum kfd_queue_type type); static inline unsigned int get_sh_mem_bases_32(struct kfd_process_device *pdd) { diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h index acd0e41e744c..4d65f94da4d8 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h +++ b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h @@ -440,7 +440,8 @@ enum kfd_queue_type { KFD_QUEUE_TYPE_SDMA, KFD_QUEUE_TYPE_HIQ, KFD_QUEUE_TYPE_SDMA_XGMI, - KFD_QUEUE_TYPE_SDMA_BY_ENG_ID + KFD_QUEUE_TYPE_SDMA_BY_ENG_ID, + KFD_QUEUE_TYPE_MAX, }; enum kfd_queue_format { diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c index b8c36907d536..9ccbc6e5b27b 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c @@ -1008,6 +1008,23 @@ int kfd_criu_restore_queue(struct kfd_process *p, goto exit; } + pdd = kfd_process_device_data_by_id(p, q_data->gpu_id); + if (!pdd) { + pr_err("Failed to get pdd\n"); + ret = -EINVAL; + goto exit; + } + + if (q_data->type >= KFD_QUEUE_TYPE_MAX) { + ret = -EINVAL; + goto exit; + } + + if (q_data->mqd_size != mqd_size_from_queue_type(pdd->dev->dqm, q_data->type)) { + ret = -EINVAL; + goto exit; + } + *priv_data_offset += sizeof(*q_data); q_extra_data_size = (uint64_t)q_data->ctl_stack_size + q_data->mqd_size; @@ -1030,13 +1047,6 @@ int kfd_criu_restore_queue(struct kfd_process *p, *priv_data_offset += q_extra_data_size; - pdd = kfd_process_device_data_by_id(p, q_data->gpu_id); - if (!pdd) { - pr_err("Failed to get pdd\n"); - ret = -EINVAL; - goto exit; - } - /* * data stored in this order: * mqd[xcc0], mqd[xcc1],..., ctl_stack[xcc0], ctl_stack[xcc1]... From 3238c634725afbb2a137fdda762208510828f71d Mon Sep 17 00:00:00 2001 From: Uday Khare Date: Mon, 6 Jul 2026 21:01:09 +0530 Subject: [PATCH 0835/1054] ASoC: tas2562: fix deprecated 'shut-down' GPIO always cleared after lookup In tas2562_parse_dt(), the fallback lookup for the deprecated "shut-down" GPIO property is broken due to a missing pair of braces. The code intends to reset sdz_gpio to NULL only when the lookup returns an error that is not -EPROBE_DEFER (so the driver gracefully continues without a GPIO). However, without braces the statement: tas2562->sdz_gpio = NULL; falls outside the IS_ERR() check and is executed unconditionally for every path through the if block, including a successful GPIO lookup. This means any device using the deprecated 'shut-down' DT property will always have sdz_gpio == NULL after probe, making the GPIO completely non-functional. Fix this by adding the missing braces to scope the NULL assignment inside the IS_ERR() branch, matching the pattern already used for the primary 'shutdown' GPIO lookup above. Fixes: f78a97003b8b ("ASoC: tas2562: Update shutdown GPIO property") Signed-off-by: Uday Khare Link: https://patch.msgid.link/20260706153109.10953-1-udaykhare77@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/tas2562.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/tas2562.c b/sound/soc/codecs/tas2562.c index 2f7cfc2be970..e1d62f30418a 100644 --- a/sound/soc/codecs/tas2562.c +++ b/sound/soc/codecs/tas2562.c @@ -675,11 +675,12 @@ static int tas2562_parse_dt(struct tas2562_data *tas2562) if (tas2562->sdz_gpio == NULL) { tas2562->sdz_gpio = devm_gpiod_get_optional(dev, "shut-down", GPIOD_OUT_HIGH); - if (IS_ERR(tas2562->sdz_gpio)) + if (IS_ERR(tas2562->sdz_gpio)) { if (PTR_ERR(tas2562->sdz_gpio) == -EPROBE_DEFER) return -EPROBE_DEFER; - tas2562->sdz_gpio = NULL; + tas2562->sdz_gpio = NULL; + } } if (tas2562->model_id == TAS2110) From 0b043279e73880bee21d3b1f221bafda5af1b27e Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Tue, 7 Jul 2026 21:30:17 +0800 Subject: [PATCH 0836/1054] smb: client: fix atime clamp check in read completion cifs_rreq_done() updates the inode atime to current_time(inode) after a netfs read. It then preserves the CIFS rule that atime should not be older than mtime, because some applications break if atime is less than mtime. That rule only requires clamping when atime < mtime. The current check uses the raw non-zero result of timespec64_compare(). It therefore takes the clamp path for both atime < mtime and atime > mtime. The latter is the normal case when reading an older file: the newly recorded atime is newer than the file mtime. The completion handler then immediately moves atime back to mtime, losing the access time that was just recorded. Userspace tools that rely on atime, such as stat, find -atime, backup tools or cold-data classifiers, can therefore see a recently read CIFS file as not recently accessed. This is easy to miss because the bug is silent: read I/O still succeeds, no error is reported, and many systems either do not check atime after reads or mount with policies such as relatime/noatime. It becomes visible when a CIFS file has an mtime older than the current time, the file is read, and the local inode atime is inspected before a later revalidation replaces the cached timestamps. Clamp only when atime is actually older than mtime. This matches the same atime/mtime rule used when applying CIFS inode attributes. Fixes: 69c3c023af25 ("cifs: Implement netfslib hooks") Cc: stable@vger.kernel.org Signed-off-by: Xu Rao Signed-off-by: Steve French --- fs/smb/client/file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c index 5a25635bc62a..6a4aa57d58c3 100644 --- a/fs/smb/client/file.c +++ b/fs/smb/client/file.c @@ -302,7 +302,7 @@ static void cifs_rreq_done(struct netfs_io_request *rreq) /* we do not want atime to be less than mtime, it broke some apps */ atime = inode_set_atime_to_ts(inode, current_time(inode)); mtime = inode_get_mtime(inode); - if (timespec64_compare(&atime, &mtime)) + if (timespec64_compare(&atime, &mtime) < 0) inode_set_atime_to_ts(inode, inode_get_mtime(inode)); } From dde3929e8d901ae9a7c29d0577ce9ca5cb4db35d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Tue, 7 Jul 2026 11:03:18 +0200 Subject: [PATCH 0837/1054] cifs: Fix support for creating SFU socket MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SFU sockets are natively supported by Interix 3.0 subsystem and also by later versions. It is part of Microsoft SFU (Windows Services for UNIX) and Microsoft SUA (Subsystem for UNIX-based Applications). They can be created and existing (stored on local disk or remote SMB share) can be recognized. SFU sockets are recognized also by NFS server included in Windows Server. Windows NFS server versions since Windows Server 2012 uses new reparse point format for storing new sockets, but still can recognize this old format (also in the latest Windows Server 2022 version). SFU-style socket is a regular file which has system attribute set and content of the file is one zero byte. These SFU-style sockets are already recognized by Linux SMB client. But Linux SMB client is currently creating new SFU socket in different format which is not compatible with all those SFU applications. Fix this by creating new sockets in correct SFU format which would be recognized by all SFU, SUA, NFS and existing Linux SMB clients. This change affects only creating new sockets when mount option -o sfu is used. Signed-off-by: Pali Rohár Acked-by: Paulo Alcantara (Red Hat) Signed-off-by: Steve French --- fs/smb/client/smb2ops.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c index 06e9322a762a..b4fa43f32e51 100644 --- a/fs/smb/client/smb2ops.c +++ b/fs/smb/client/smb2ops.c @@ -5286,10 +5286,9 @@ int __cifs_sfu_make_node(unsigned int xid, struct inode *inode, data = (u8 *)symname_utf16; break; case S_IFSOCK: - type_len = 8; - strscpy(type, "LnxSOCK"); - data = (u8 *)&pdev; - data_len = sizeof(pdev); + /* SFU socket is system file with one zero byte */ + type_len = 1; + type[0] = '\0'; break; case S_IFIFO: type_len = 8; From 608362facd2d0f2667f68b7f42207707d59a0071 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Tue, 7 Jul 2026 11:03:56 +0200 Subject: [PATCH 0838/1054] cifs: Fix support for creating SFU fifo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SFU fifos are natively supported (created and recognized) at least by: - Microsoft POSIX subsystem - OpenNT/Interix subsystem - Microsoft SFU (Windows Services for UNIX) - Microsoft SUA (Subsystem for UNIX-based Applications) - Windows NFS server (up to the Windows Server 2008 R2) Windows NFS server since Windows Server 2012 uses new reparse point format for storing new fifos, but still can recognize this old format (also in the latest Windows Server 2022 version). SFU-style fifo is empty regular file which has system attribute set. These SFU-style fifos are already recognized by Linux SMB client. But Linux SMB client is currently creating new SFU fifos in different format which is not compatible with all those SFU-style consumers. Fix this by creating new fifos in correct SFU format which would be recognized by all those applications and also by existing Linux SMB clients. This change affects only creating new fifos when mount option -o sfu is used. Signed-off-by: Pali Rohár Acked-by: Paulo Alcantara (Red Hat) Signed-off-by: Steve French --- fs/smb/client/smb2ops.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c index b4fa43f32e51..15b3ae45c833 100644 --- a/fs/smb/client/smb2ops.c +++ b/fs/smb/client/smb2ops.c @@ -5291,10 +5291,8 @@ int __cifs_sfu_make_node(unsigned int xid, struct inode *inode, type[0] = '\0'; break; case S_IFIFO: - type_len = 8; - strscpy(type, "LnxFIFO"); - data = (u8 *)&pdev; - data_len = sizeof(pdev); + /* SFU fifo is system file which is empty */ + type_len = 0; break; default: rc = -EPERM; From 75f5c412fa867efa0bf9b646bffe0d912109e84a Mon Sep 17 00:00:00 2001 From: Zizhi Wo Date: Sat, 4 Jul 2026 09:53:20 +0800 Subject: [PATCH 0839/1054] smb: client: fix busy dentry warning on unmount after DIO Commit c68337442f03 ("cifs: Fix busy dentry used after unmounting") fixed the issue in cifs where deferred close of a file led to a dentry reference count not being released in umount, by flushing deferredclose_wq in cifs_kill_sb() to solve it. However, the cifs DIO path suffers from the same busy-dentry problem caused by a delayed dentry reference-count release: [dio] [cifsd] [close + umount] netfs_unbuffered_write_iter_locked ... cifs_demultiplex_thread netfs_unbuffered_write cifs_issue_write netfs_wait_for_in_progress_stream [1] ... netfs_write_subrequest_terminated netfs_subreq_clear_in_progress netfs_wake_collector // wake [1] netfs_put_subrequest netfs_put_request queue_work(system_dfl_wq, xxx) [2] // dio write return cifs_close _cifsFileInfo_put // cfile->count 2->1 --cfile->count [3] // umount cifs_kill_sb kill_anon_super // warning triggered! shrink_dcache_for_umount [4] [system_dfl_wq] [5] netfs_free_request ... _cifsFileInfo_put // cfile->count 1->0 --cfile->count queue_work(fileinfo_put_wq, xxx) [fileinfo_put_wq] [6] cifsFileInfo_put_work cifsFileInfo_put_final dput If the umount path is triggered before [5], it results warning: BUG: Dentry 00000000eab1f070{i=9a917b66ae404fec,n=test} still in use (1) [unmount of cifs cifs] The existing per-inode ictx->io_count wait in cifs_evict_inode() does not help: it lives in the inode eviction path, which runs after shrink_dcache_for_umount() has already warned about the busy dentries. Fix it by adding a per-superblock outstanding-rreq counter that is incremented in cifs_init_request() and decremented in cifs_free_request(). In cifs_kill_sb(), before kill_anon_super(), wait for this counter to reach 0 - which guarantees that all cleanup_work for this sb have run and thus all relevant cfile puts are queued on fileinfo_put_wq or serverclose_wq. Then drain the workqueue so the dentry refs are dropped. This is a targeted wait, not a flush of the system-wide system_dfl_wq. Fixes: 340cea84f691c ("cifs: open files should not hold ref on superblock") Signed-off-by: Zizhi Wo Signed-off-by: Steve French --- fs/smb/client/cifs_fs_sb.h | 1 + fs/smb/client/cifsfs.c | 12 ++++++++++++ fs/smb/client/connect.c | 1 + fs/smb/client/file.c | 5 +++++ 4 files changed, 19 insertions(+) diff --git a/fs/smb/client/cifs_fs_sb.h b/fs/smb/client/cifs_fs_sb.h index 84e7e366b0ff..d6494e1d93cc 100644 --- a/fs/smb/client/cifs_fs_sb.h +++ b/fs/smb/client/cifs_fs_sb.h @@ -56,6 +56,7 @@ struct cifs_sb_info { struct smb3_fs_context *ctx; atomic_t active; atomic_t mnt_cifs_flags; + atomic_t outstanding_rreq; /* nr of rreqs not yet fully deinitialized */ struct delayed_work prune_tlinks; struct rcu_head rcu; diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c index ea4fc0fa68ca..4df6ca03a8de 100644 --- a/fs/smb/client/cifsfs.c +++ b/fs/smb/client/cifsfs.c @@ -311,6 +311,18 @@ static void cifs_kill_sb(struct super_block *sb) /* Wait for all opened files to release */ flush_workqueue(deferredclose_wq); + /* + * Wait for all in-flight netfs I/O requests to finish their + * cleanup_work so that any cifsFileInfo final puts they queue + * to fileinfo_put_wq/serverclose_wq have been queued, then + * drain the workqueue so the cfile dentry refs are dropped to + * avoid the busy dentry warning. + */ + wait_var_event(&cifs_sb->outstanding_rreq, + !atomic_read(&cifs_sb->outstanding_rreq)); + flush_workqueue(serverclose_wq); + flush_workqueue(fileinfo_put_wq); + /* finally release root dentry */ dput(cifs_sb->root); cifs_sb->root = NULL; diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c index 85aec302c89e..a187398fbabd 100644 --- a/fs/smb/client/connect.c +++ b/fs/smb/client/connect.c @@ -3485,6 +3485,7 @@ int cifs_setup_cifs_sb(struct cifs_sb_info *cifs_sb) spin_lock_init(&cifs_sb->tlink_tree_lock); cifs_sb->tlink_tree = RB_ROOT; + atomic_set(&cifs_sb->outstanding_rreq, 0); cifs_dbg(FYI, "file mode: %04ho dir mode: %04ho\n", ctx->file_mode, ctx->dir_mode); diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c index 6a4aa57d58c3..968740e7c9c3 100644 --- a/fs/smb/client/file.c +++ b/fs/smb/client/file.c @@ -288,6 +288,7 @@ static int cifs_init_request(struct netfs_io_request *rreq, struct file *file) return smb_EIO1(smb_eio_trace_not_netfs_writeback, rreq->origin); } + atomic_inc(&cifs_sb->outstanding_rreq); return 0; } @@ -309,9 +310,13 @@ static void cifs_rreq_done(struct netfs_io_request *rreq) static void cifs_free_request(struct netfs_io_request *rreq) { struct cifs_io_request *req = container_of(rreq, struct cifs_io_request, rreq); + struct cifs_sb_info *cifs_sb = CIFS_SB(rreq->inode->i_sb); if (req->cfile) cifsFileInfo_put(req->cfile); + + if (atomic_dec_and_test(&cifs_sb->outstanding_rreq)) + wake_up_var(&cifs_sb->outstanding_rreq); } static void cifs_free_subrequest(struct netfs_io_subrequest *subreq) From d38f8bd771c4999b797d7074b348cf201414bd34 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Wed, 8 Jul 2026 18:57:22 -0500 Subject: [PATCH 0840/1054] regulator: core: regulator_lock_two() should test for EDEADLK not EDEADLOCK Compare against -EDEADLK, which is what ww_mutex_lock() actually returns and what every other deadlock check in this file already uses. Function regulator_lock_two() acquires two regulators via regulator_lock_nested() -> ww_mutex_lock(). On contention, ww_mutex_lock() returns -EDEADLK, which is the caller's signal to drop the lock it holds and retry the acquisition in the canonical order. However, regulator_lock_two() tests the return value against -EDEADLOCK rather than -EDEADLK. On most architectures, EDEADLK and EDEADLOCK are the same value, so the comparison happens to be correct and the bug is invisible. But on MIPS, SPARC, and PowerPC, those two errors have different values. The test is wrong: a genuine -EDEADLK backoff no longer matches -EDEADLOCK, so instead of unlocking and retrying, the code falls into WARN_ON(ret) and returns with only one of the two regulators locked. In practice, this is a bug only on MIPS, because the regulator core is not built or used on the other two platforms. In general, EDEADLK is preferred over EDEADLOCK for new code. Fixes: cba6cfdc7c3f ("regulator: core: Avoid lockdep reports when resolving supplies") Signed-off-by: Timur Tabi Link: https://patch.msgid.link/20260708235722.2953579-1-ttabi@nvidia.com Signed-off-by: Mark Brown --- drivers/regulator/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index dc5d67767336..1797929dfe56 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -248,7 +248,7 @@ static void regulator_lock_two(struct regulator_dev *rdev1, ret = regulator_lock_nested(rdev1, ww_ctx); WARN_ON(ret); ret = regulator_lock_nested(rdev2, ww_ctx); - if (ret != -EDEADLOCK) { + if (ret != -EDEADLK) { WARN_ON(ret); goto exit; } @@ -264,7 +264,7 @@ static void regulator_lock_two(struct regulator_dev *rdev1, swap(held, contended); ret = regulator_lock_nested(contended, ww_ctx); - if (ret != -EDEADLOCK) { + if (ret != -EDEADLK) { WARN_ON(ret); break; } From 06c2b8d7ea2bcb014dd974fc3bc6d128d65d7477 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Thu, 2 Jul 2026 16:50:13 +0200 Subject: [PATCH 0841/1054] drm/fb-helper: Only consider active CRTCs for vblank sync Only synchronize fbdev output to the vblank of an active CRTC. Go over the list of CRTCs and pick the first that matches. Fixes warnings as the one shown below [ 77.201354] WARNING: drivers/gpu/drm/drm_vblank.c:1320 at drm_crtc_wait_one_vblank+0x194/0x1cc [drm], CPU#1: kworker/1:7/1867 [ 77.201354] omapdrm omapdrm.0: [drm] vblank wait timed out on crtc 0 This currently happens if the fbdev output is not on CRTC 0. Atomic and non-atomic drivers require distinct code paths. As for other fbdev operations, implement both and select the correct one at runtime. Not finding an active CRTC is not a bug. Do not wait in this case, but flush the display update as before. v4: - avoid possible deadlocks with locking context (Sashiko) v3: - drop excessive state validation (Jani) - acquire plane and CRTC mutices (Sashiko) v2: - move look-up code into separate helper - support drivers with legacy modesetting v1: - see https://lore.kernel.org/dri-devel/1c9e0e24-9c4a-4259-8700-cf9e5fd60ca3@suse.de/ Co-authored-by: H. Nikolaus Schaller Signed-off-by: Thomas Zimmermann Fixes: d8c4bddcd8bcb ("drm/fb-helper: Synchronize dirty worker with vblank") Tested-by: Icenowy Zheng Tested-by: H. Nikolaus Schaller Closes: https://bugs.debian.org/1138033 Acked-by: Maxime Ripard Link: https://patch.msgid.link/20260702145021.226932-1-tzimmermann@suse.de --- drivers/gpu/drm/drm_fb_helper.c | 92 ++++++++++++++++++++++++++++++++- 1 file changed, 91 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 7b11a582f8ec..80ca785bdb26 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -225,16 +225,106 @@ static void drm_fb_helper_resume_worker(struct work_struct *work) console_unlock(); } +static int find_crtc_index_atomic(struct drm_fb_helper *helper) +{ + struct drm_device *dev = helper->dev; + int crtc_index = -EINVAL; + struct drm_modeset_acquire_ctx ctx; + struct drm_plane *plane; + int ret = 0; + + drm_modeset_acquire_init(&ctx, 0); + +retry: + drm_for_each_plane(plane, dev) { + const struct drm_plane_state *plane_state; + + if (plane->type != DRM_PLANE_TYPE_PRIMARY) + continue; + + ret = drm_modeset_lock(&plane->mutex, &ctx); + if (ret) + goto err_drm_modeset_lock; + plane_state = plane->state; + + if (plane_state->fb == helper->fb && plane_state->crtc) { + struct drm_crtc *crtc = plane_state->crtc; + + ret = drm_modeset_lock(&crtc->mutex, &ctx); + if (ret) + goto err_drm_modeset_lock; + if (crtc->state->active) + crtc_index = crtc->index; + drm_modeset_unlock(&crtc->mutex); + } + drm_modeset_unlock(&plane->mutex); + + if (crtc_index >= 0) + break; + } + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); + + return crtc_index; + +err_drm_modeset_lock: + if (ret == -EDEADLK) { + drm_modeset_backoff(&ctx); + goto retry; + } + return ret; +} + +static int find_crtc_index_legacy(struct drm_fb_helper *helper) +{ + struct drm_device *dev = helper->dev; + struct drm_crtc *crtc; + + drm_for_each_crtc(crtc, dev) { + struct drm_plane *plane = crtc->primary; + + if (!crtc->enabled) + continue; + if (!plane || plane->fb != helper->fb) + continue; /* CRTC doesn't display fbdev emulation */ + + return crtc->index; + } + + return -EINVAL; +} + +static int drm_fb_helper_find_crtc_index(struct drm_fb_helper *helper) +{ + struct drm_device *dev = helper->dev; + int crtc_index; + + mutex_lock(&dev->mode_config.mutex); + + if (drm_drv_uses_atomic_modeset(dev)) + crtc_index = find_crtc_index_atomic(helper); + else + crtc_index = find_crtc_index_legacy(helper); + + mutex_unlock(&dev->mode_config.mutex); + + return crtc_index; +} + static void drm_fb_helper_fb_dirty(struct drm_fb_helper *helper) { struct drm_device *dev = helper->dev; struct drm_clip_rect *clip = &helper->damage_clip; struct drm_clip_rect clip_copy; + int crtc_index; unsigned long flags; int ret; mutex_lock(&helper->lock); - drm_client_modeset_wait_for_vblank(&helper->client, 0); + crtc_index = drm_fb_helper_find_crtc_index(helper); + if (crtc_index >= 0) + drm_client_modeset_wait_for_vblank(&helper->client, crtc_index); mutex_unlock(&helper->lock); if (drm_WARN_ON_ONCE(dev, !helper->funcs->fb_dirty)) From 7b69729046a4c58f4cb457184e5ac4aaa179bff4 Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Wed, 24 Jun 2026 14:19:10 +0800 Subject: [PATCH 0842/1054] KVM: s390: pci: Fix GISC refcount leak on AIF enable failure kvm_s390_gisc_register() registers the guest ISC before pinning the guest interrupt forwarding pages and allocating the AISB bit. If any of the later setup steps fails, the function unwinds the pinned pages and other local state, but does not unregister the GISC reference. Add the missing kvm_s390_gisc_unregister() to the error unwind path. Fixes: 3c5a1b6f0a18 ("KVM: s390: pci: provide routines for enabling/disabling interrupt forwarding") Cc: stable@vger.kernel.org Signed-off-by: Haoxiang Li Reviewed-by: Matthew Rosato Tested-by: Matthew Rosato Acked-by: Claudio Imbrenda Reviewed-by: Christian Borntraeger Signed-off-by: Claudio Imbrenda Message-ID: <20260624061910.2794734-1-haoxiang_li2024@163.com> Signed-off-by: Christian Borntraeger --- arch/s390/kvm/pci.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/s390/kvm/pci.c b/arch/s390/kvm/pci.c index 5b075c38998e..686113be0530 100644 --- a/arch/s390/kvm/pci.c +++ b/arch/s390/kvm/pci.c @@ -328,6 +328,7 @@ static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib, unpin1: unpin_user_page(aibv_page); out: + kvm_s390_gisc_unregister(kvm, fib->fmt0.isc); return rc; } From 866d03de6def89c386cdfd457b28a1f566e02565 Mon Sep 17 00:00:00 2001 From: Claudio Imbrenda Date: Thu, 2 Jul 2026 17:23:59 +0200 Subject: [PATCH 0843/1054] KVM: s390: vsie: Avoid potential deadlock with real spaces The natural lock ordering is mmu_lock -> children_lock, but in gmap_create_shadow() the reverse order is used when handling shadowing of real address spaces. Convert the inner locking of kvm->mmu_lock to a trylock; return -EAGAIN if the lock is busy, and let the caller try again. This path is not expected to happen in real-life scenarios, so its performance is not important. Fixes: a2c17f9270cc ("KVM: s390: New gmap code") Signed-off-by: Claudio Imbrenda Reviewed-by: Christian Borntraeger Signed-off-by: Christian Borntraeger --- arch/s390/kvm/gmap.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/arch/s390/kvm/gmap.c b/arch/s390/kvm/gmap.c index 298fbaecec28..8abb4f55b306 100644 --- a/arch/s390/kvm/gmap.c +++ b/arch/s390/kvm/gmap.c @@ -1374,8 +1374,13 @@ struct gmap *gmap_create_shadow(struct kvm_s390_mmu_cache *mc, struct gmap *pare /* Only allow one real-space gmap shadow. */ list_for_each_entry(sg, &parent->children, list) { if (sg->guest_asce.r) { - scoped_guard(write_lock, &parent->kvm->mmu_lock) + if (write_trylock(&parent->kvm->mmu_lock)) { gmap_unshadow(sg); + write_unlock(&parent->kvm->mmu_lock); + } else { + gmap_put(new); + return ERR_PTR(-EAGAIN); + } break; } } From 4d4a21e38f1b87a76b3e63d4f837ff4e9b52d5a6 Mon Sep 17 00:00:00 2001 From: Claudio Imbrenda Date: Thu, 2 Jul 2026 17:24:05 +0200 Subject: [PATCH 0844/1054] KVM: s390: Fix dat_crste_walk_range() early return If a walk entry handler for a lower level returns a value, dat_crste_walk_range() will not return immediately, but instead loop again and move to the next entry. This means that some entries are potentially skipped, and early return is ignored. Skipped entries might lead to all kinds of issues, given that the caller expects them to not be skipped. Early return is often used to interrupt a walk when a rescheduling is needed; if it is ignored it can lead to stalls. Fix by breaking from the loop immediately if the walk to a lower level returned non-zero. Fixes: 2db149a0a6c5 ("KVM: s390: KVM page table management functions: walks") Signed-off-by: Claudio Imbrenda Reviewed-by: Christian Borntraeger Signed-off-by: Christian Borntraeger --- arch/s390/kvm/dat.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/s390/kvm/dat.c b/arch/s390/kvm/dat.c index 5f1960ec982d..ed4259d17629 100644 --- a/arch/s390/kvm/dat.c +++ b/arch/s390/kvm/dat.c @@ -570,6 +570,8 @@ static long dat_crste_walk_range(gfn_t start, gfn_t end, struct crst_table *tabl else if (walk->ops->pte_entry) rc = dat_pte_walk_range(max(start, cur), min(end, next), dereference_pmd(crste.pmd), walk); + if (rc) + break; } } return rc; From 9489220fe0e69d2ca141e5062dd3ef3e2e55959f Mon Sep 17 00:00:00 2001 From: Claudio Imbrenda Date: Thu, 2 Jul 2026 17:24:06 +0200 Subject: [PATCH 0845/1054] KVM: s390: Improve kvm_s390_vm_stop_migration() There is no need to clear cmma-dirty state if the VM is not using CMMA. Skip the CMMA-related code if CMMA is not in use. Fixes: 6cfd47f91f6a ("KVM: s390: Fix cmma dirty tracking") Fixes: 190df4a212a7 ("KVM: s390: CMMA tracking, ESSA emulation, migration mode") Signed-off-by: Claudio Imbrenda Reviewed-by: Christian Borntraeger Signed-off-by: Christian Borntraeger --- arch/s390/kvm/kvm-s390.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 23c817595e28..150b5dd2170e 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -1280,8 +1280,10 @@ static int kvm_s390_vm_stop_migration(struct kvm *kvm) * PGSTEs might have cmma_d set. */ WRITE_ONCE(kvm->arch.migration_mode, 0); - if (kvm->arch.use_cmma) - kvm_s390_sync_request_broadcast(kvm, KVM_REQ_STOP_MIGRATION); + if (!kvm->arch.use_cmma) + return 0; + + kvm_s390_sync_request_broadcast(kvm, KVM_REQ_STOP_MIGRATION); /* Clear cmma_d on all existing PGSTEs and set cmma_dirty_pages to 0. */ gmap_set_cmma_all_clean(kvm->arch.gmap); atomic64_set(&kvm->arch.cmma_dirty_pages, 0); From f4ef35efbb49527293309f668ea73ec5de9b8e7a Mon Sep 17 00:00:00 2001 From: Wang Yan Date: Thu, 2 Jul 2026 10:59:49 +0800 Subject: [PATCH 0846/1054] selftests/net: fix EVP_MD_CTX leak in tcp_mmap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In tcp_mmap.c, both child_thread() and main() allocate an EVP_MD_CTX via EVP_MD_CTX_new() when integrity checking is enabled, but neither function releases the context. child_thread() misses the free in its common cleanup block, and main() returns without freeing the context. This results in a SHA256 context leak on every run that uses the ‑i (integrity) option. Add the missing EVP_MD_CTX_free() calls to the appropriate cleanup paths to fix the leak. Fixes: 5c5945dc695c ("selftests/net: Add SHA256 computation over data sent in tcp_mmap") Signed-off-by: Wang Yan Link: https://patch.msgid.link/20260702025949.442523-1-wangyan01@kylinos.cn Signed-off-by: Paolo Abeni --- tools/testing/selftests/net/tcp_mmap.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/testing/selftests/net/tcp_mmap.c b/tools/testing/selftests/net/tcp_mmap.c index 4fcce5150850..2544ae35d07a 100644 --- a/tools/testing/selftests/net/tcp_mmap.c +++ b/tools/testing/selftests/net/tcp_mmap.c @@ -313,6 +313,8 @@ void *child_thread(void *arg) tcp_info_get_rcv_mss(fd)); } error: + if (ctx) + EVP_MD_CTX_free(ctx); munmap(buffer, buffer_sz); close(fd); if (zflg) @@ -606,6 +608,8 @@ int main(int argc, char *argv[]) EVP_DigestFinal_ex(ctx, digest, &digest_len); send(fd, digest, (size_t)SHA256_DIGEST_LENGTH, 0); } + if (ctx) + EVP_MD_CTX_free(ctx); close(fd); munmap(buffer, buffer_sz); return 0; From 2e2a83b4998af4384e677d3b2ac08565274279bf Mon Sep 17 00:00:00 2001 From: Dexuan Cui Date: Wed, 1 Jul 2026 21:12:36 -0700 Subject: [PATCH 0847/1054] net: mana: Validate the packet length reported by the NIC Validate the packet length reported in the RX CQE before passing it to skb processing. The CQE is supplied by the NIC device and should not be blindly trusted. Cc: stable@vger.kernel.org Reviewed-by: Haiyang Zhang Signed-off-by: Dexuan Cui Fixes: ca9c54d2d6a5 ("net: mana: Add a driver for Microsoft Azure Network Adapter (MANA)") Link: https://patch.msgid.link/20260702041237.617719-2-decui@microsoft.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/microsoft/mana/mana_en.c | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c index 7438ea6b3f26..dd4d1c0c582e 100644 --- a/drivers/net/ethernet/microsoft/mana/mana_en.c +++ b/drivers/net/ethernet/microsoft/mana/mana_en.c @@ -2246,12 +2246,25 @@ static void mana_process_rx_cqe(struct mana_rxq *rxq, struct mana_cq *cq, rxbuf_oob = &rxq->rx_oobs[curr]; WARN_ON_ONCE(rxbuf_oob->wqe_inf.wqe_size_in_bu != 1); - mana_refill_rx_oob(dev, rxq, rxbuf_oob, &old_buf, &old_fp); + if (unlikely(pktlen > rxq->datasize)) { + /* Increase it even if mana_rx_skb() isn't called. */ + rxq->rx_cq.work_done++; - /* Unsuccessful refill will have old_buf == NULL. - * In this case, mana_rx_skb() will drop the packet. - */ - mana_rx_skb(old_buf, old_fp, oob, rxq, i); + ++ndev->stats.rx_dropped; + netdev_warn_once(ndev, + "Dropped oversized RX packet: len=%u, datasize=%u\n", + pktlen, rxq->datasize); + + /* Reuse the RX buffer since rxbuf_oob is unchanged. */ + } else { + + mana_refill_rx_oob(dev, rxq, rxbuf_oob, &old_buf, &old_fp); + + /* Unsuccessful refill will have old_buf == NULL. + * In this case, mana_rx_skb() will drop the packet. + */ + mana_rx_skb(old_buf, old_fp, oob, rxq, i); + } mana_move_wq_tail(rxq->gdma_rq, rxbuf_oob->wqe_inf.wqe_size_in_bu); From c72a0f09c57f92113df69f9b902d11c9e4b132f5 Mon Sep 17 00:00:00 2001 From: Dexuan Cui Date: Wed, 1 Jul 2026 21:12:37 -0700 Subject: [PATCH 0848/1054] net: mana: Sync page pool RX frags for CPU MANA allocates RX buffers from page pool fragments when frag_count is greater than 1. In that case the buffers remain DMA mapped by page pool and the RX completion path does not call dma_unmap_single(). As a result, the implicit sync-for-CPU normally performed by dma_unmap_single() is missing before the packet data is passed to the networking stack. This breaks RX on configurations which require explicit DMA syncing, for example when booted with swiotlb=force. Fix this by recording the page pool page and DMA sync offset when the RX buffer is allocated, and syncing the received packet range for CPU access before handing the RX buffer to the stack. Fixes: 730ff06d3f5c ("net: mana: Use page pool fragments for RX buffers instead of full pages to improve memory efficiency.") Cc: stable@vger.kernel.org Reviewed-by: Haiyang Zhang Signed-off-by: Dexuan Cui Link: https://patch.msgid.link/20260702041237.617719-3-decui@microsoft.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/microsoft/mana/mana_en.c | 40 +++++++++++++++---- include/net/mana/mana.h | 8 ++++ 2 files changed, 41 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c index dd4d1c0c582e..9d9bfd116dab 100644 --- a/drivers/net/ethernet/microsoft/mana/mana_en.c +++ b/drivers/net/ethernet/microsoft/mana/mana_en.c @@ -2120,12 +2120,16 @@ static void mana_rx_skb(void *buf_va, bool from_pool, } static void *mana_get_rxfrag(struct mana_rxq *rxq, struct device *dev, - dma_addr_t *da, bool *from_pool) + dma_addr_t *da, bool *from_pool, + struct page **pp_page, u32 *dma_sync_offset) { struct page *page; u32 offset; void *va; + *from_pool = false; + *pp_page = NULL; + *dma_sync_offset = 0; /* Don't use fragments for jumbo frames or XDP where it's 1 fragment * per page. @@ -2163,31 +2167,47 @@ static void *mana_get_rxfrag(struct mana_rxq *rxq, struct device *dev, va = page_to_virt(page) + offset; *da = page_pool_get_dma_addr(page) + offset + rxq->headroom; *from_pool = true; + *pp_page = page; + *dma_sync_offset = offset + rxq->headroom; return va; } /* Allocate frag for rx buffer, and save the old buf */ static void mana_refill_rx_oob(struct device *dev, struct mana_rxq *rxq, - struct mana_recv_buf_oob *rxoob, void **old_buf, - bool *old_fp) + struct mana_recv_buf_oob *rxoob, u32 pktlen, + void **old_buf, bool *old_fp) { + struct page *pp_page; + u32 dma_sync_offset; bool from_pool; dma_addr_t da; void *va; - va = mana_get_rxfrag(rxq, dev, &da, &from_pool); + va = mana_get_rxfrag(rxq, dev, &da, &from_pool, &pp_page, + &dma_sync_offset); if (!va) return; - if (!rxoob->from_pool || rxq->frag_count == 1) + if (!rxoob->from_pool || rxq->frag_count == 1) { dma_unmap_single(dev, rxoob->sgl[0].address, rxq->datasize, DMA_FROM_DEVICE); + } else { + /* The page pool maps the whole page and only syncs for device + * automatically (PP_FLAG_DMA_SYNC_DEV). Sync the received bytes + * for the CPU before they are read: this is required if DMA + * is incoherent or bounce buffers are used. + */ + page_pool_dma_sync_for_cpu(rxq->page_pool, rxoob->pp_page, + rxoob->dma_sync_offset, pktlen); + } *old_buf = rxoob->buf_va; *old_fp = rxoob->from_pool; rxoob->buf_va = va; rxoob->sgl[0].address = da; rxoob->from_pool = from_pool; + rxoob->pp_page = pp_page; + rxoob->dma_sync_offset = dma_sync_offset; } static void mana_process_rx_cqe(struct mana_rxq *rxq, struct mana_cq *cq, @@ -2258,7 +2278,8 @@ static void mana_process_rx_cqe(struct mana_rxq *rxq, struct mana_cq *cq, /* Reuse the RX buffer since rxbuf_oob is unchanged. */ } else { - mana_refill_rx_oob(dev, rxq, rxbuf_oob, &old_buf, &old_fp); + mana_refill_rx_oob(dev, rxq, rxbuf_oob, pktlen, + &old_buf, &old_fp); /* Unsuccessful refill will have old_buf == NULL. * In this case, mana_rx_skb() will drop the packet. @@ -2668,6 +2689,8 @@ static int mana_fill_rx_oob(struct mana_recv_buf_oob *rx_oob, u32 mem_key, struct mana_rxq *rxq, struct device *dev) { struct mana_port_context *mpc = netdev_priv(rxq->ndev); + struct page *pp_page = NULL; + u32 dma_sync_offset = 0; bool from_pool = false; dma_addr_t da; void *va; @@ -2675,13 +2698,16 @@ static int mana_fill_rx_oob(struct mana_recv_buf_oob *rx_oob, u32 mem_key, if (mpc->rxbufs_pre) va = mana_get_rxbuf_pre(rxq, &da); else - va = mana_get_rxfrag(rxq, dev, &da, &from_pool); + va = mana_get_rxfrag(rxq, dev, &da, &from_pool, &pp_page, + &dma_sync_offset); if (!va) return -ENOMEM; rx_oob->buf_va = va; rx_oob->from_pool = from_pool; + rx_oob->pp_page = pp_page; + rx_oob->dma_sync_offset = dma_sync_offset; rx_oob->sgl[0].address = da; rx_oob->sgl[0].size = rxq->datasize; diff --git a/include/net/mana/mana.h b/include/net/mana/mana.h index 13c87baf018e..04acb6791dbd 100644 --- a/include/net/mana/mana.h +++ b/include/net/mana/mana.h @@ -305,6 +305,14 @@ struct mana_recv_buf_oob { void *buf_va; bool from_pool; /* allocated from a page pool */ + /* head page of the page_pool fragment; valid only when + * from_pool && frag_count > 1. + */ + struct page *pp_page; + /* Fragment offset plus rxq->headroom, passed to + * page_pool_dma_sync_for_cpu(). + */ + u32 dma_sync_offset; /* SGL of the buffer going to be sent as part of the work request. */ u32 num_sge; From 27f575836cfebbf872dec020428742b10650a955 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Lebrun?= Date: Thu, 2 Jul 2026 17:37:02 +0200 Subject: [PATCH 0849/1054] net: macb: drop in-flight Tx SKBs on close MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The MACB driver has since forever leaked the outgoing SKBs that have not yet been marked as completed. They live in queue->tx_skb which gets freed without remorse nor checking. macb_free_consistent() gets called in a few codepaths, but only close will trigger the added expressions. In macb_open() and macb_alloc_consistent() failure cases, queues' tx_skb just got allocated and are empty. Fixes: 89e5785fc8a6 ("[PATCH] Atmel MACB ethernet driver") Cc: stable@vger.kernel.org Reviewed-by: Nicolai Buchwitz Signed-off-by: Théo Lebrun Link: https://patch.msgid.link/20260702-macb-drop-tx-v4-1-1c833eebdbc8@bootlin.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/cadence/macb_main.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index fd282a1700fb..d394f1f43b68 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -2668,8 +2668,25 @@ static void macb_free_consistent(struct macb *bp) dma_free_coherent(dev, size, bp->queues[0].rx_ring, bp->queues[0].rx_ring_dma); for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) { - kfree(queue->tx_skb); - queue->tx_skb = NULL; + if (queue->tx_skb) { + unsigned int dropped = 0, tail; + + for (tail = queue->tx_tail; tail != queue->tx_head; + tail++) { + if (macb_tx_skb(queue, tail)->skb) + dropped++; + macb_tx_unmap(bp, macb_tx_skb(queue, tail), 0); + } + + queue->stats.tx_dropped += dropped; + bp->dev->stats.tx_dropped += dropped; + + kfree(queue->tx_skb); + queue->tx_skb = NULL; + } + + queue->tx_head = 0; + queue->tx_tail = 0; queue->tx_ring = NULL; queue->rx_ring = NULL; } From 97272a5704bf13369bba43951ad9fe8f806ccc7f Mon Sep 17 00:00:00 2001 From: Kailang Yang Date: Thu, 9 Jul 2026 14:57:06 +0800 Subject: [PATCH 0850/1054] ALSA: hda/realtek - Fixed Headphone noise issue for Dell QCM1255 This platform booted with Ubuntu 24.04 with Pipewire audio server. So, it has pop noise with headphone. But it's normal with Pulseaudio server. This patch was the workaround. Connect the headphones to DAC 0x2. The popping sound will disappear. Signed-off-by: Kailang Yang Link: https://lore.kernel.org/34b990cb56914148ba02fa8e9d176479@realtek.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 02cc3f6b378d..2fc25be99ca6 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -4172,6 +4172,7 @@ enum { ALC256_FIXUP_HONOR_MRB_XXX_M1020_AUDIO, ALC245_FIXUP_HP_ENVY_X360_15_FH0XXX, ALC287_FIXUP_ACER_MICMUTE_LED, + ALC236_FIXUP_DELL_HP_POP_NOISE, }; /* A special fixup for Lenovo C940 and Yoga Duet 7; @@ -6779,6 +6780,10 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC2XX_FIXUP_HEADSET_MIC, }, + [ALC236_FIXUP_DELL_HP_POP_NOISE] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc285_fixup_invalidate_dacs, + }, }; static const struct hda_quirk alc269_fixup_tbl[] = { @@ -6930,6 +6935,8 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1028, 0x0cc3, "Dell Oasis 14 Low Weight MTL-U", ALC289_FIXUP_DELL_CS35L41_SPI_2), SND_PCI_QUIRK(0x1028, 0x0cc4, "Dell Oasis 16 MTL-H/U", ALC289_FIXUP_DELL_CS35L41_SPI_2), SND_PCI_QUIRK(0x1028, 0x0cc5, "Dell Oasis 14", ALC289_FIXUP_RTK_AMP_DUAL_SPK), + SND_PCI_QUIRK(0x1028, 0x0e6b, "Dell Pro QCM1255", ALC236_FIXUP_DELL_HP_POP_NOISE), + SND_PCI_QUIRK(0x1028, 0x0e6d, "Dell Pro Micro QCM1255", ALC236_FIXUP_DELL_HP_POP_NOISE), SND_PCI_QUIRK(0x1028, 0x164a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x164b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2), From d595255241e5fec0c94adeebf2565524398e37c5 Mon Sep 17 00:00:00 2001 From: Eckhart Mohr Date: Wed, 8 Jul 2026 15:21:06 +0200 Subject: [PATCH 0851/1054] ALSA: hda/realtek: Add quirk for TongFang X6xx45xU Fix microphone detection on built in headphone jack for some devices. Signed-off-by: Eckhart Mohr Cc: stable@vger.kernel.org Signed-off-by: Werner Sembach Link: https://patch.msgid.link/20260708132135.102680-1-wse@tuxedocomputers.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 2fc25be99ca6..b47177d734c7 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -7918,6 +7918,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1d05, 0x300f, "TongFang X6AR5xxY", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1d05, 0x3019, "TongFang X6FR5xxY", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1d05, 0x3031, "TongFang X6AR55xU", ALC2XX_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x1d05, 0x3034, "TongFang X6xx45xU", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1d17, 0x3288, "Haier Boyue G42", ALC269VC_FIXUP_ACER_VCOPPERBOX_PINS), SND_PCI_QUIRK(0x1d72, 0x1602, "RedmiBook", ALC255_FIXUP_XIAOMI_HEADSET_MIC), SND_PCI_QUIRK(0x1d72, 0x1701, "XiaomiNotebook Pro", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE), From c914307e1d41c2cb7bcdcbfde4cd2f214f6aa027 Mon Sep 17 00:00:00 2001 From: Li RongQing Date: Fri, 3 Jul 2026 22:14:23 +0800 Subject: [PATCH 0852/1054] net/mlx5: Fix L3 tunnel entropy refcount leak mlx5_tun_entropy_refcount_inc() counts both VXLAN and L2-to-L3 tunnel reformat entries as entropy-enabling users. The matching decrement path only handled VXLAN, leaving L2-to-L3 tunnel entries counted after release. Handle MLX5_REFORMAT_TYPE_L2_TO_L3_TUNNEL in mlx5_tun_entropy_refcount_dec() as well so the enabling entry refcount remains balanced. Fixes: f828ca6a2fb6 ("net/mlx5e: Add support for hw encapsulation of MPLS over UDP") Signed-off-by: Li RongQing Reviewed-by: Simon Horman Reviewed-by: Tariq Toukan Link: https://patch.msgid.link/20260703141423.1723-1-lirongqing@baidu.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/mellanox/mlx5/core/lib/port_tun.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/port_tun.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/port_tun.c index 4571c56ec3c9..97f6097d4c70 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/port_tun.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/port_tun.c @@ -176,7 +176,8 @@ void mlx5_tun_entropy_refcount_dec(struct mlx5_tun_entropy *tun_entropy, int reformat_type) { mutex_lock(&tun_entropy->lock); - if (reformat_type == MLX5_REFORMAT_TYPE_L2_TO_VXLAN) + if (reformat_type == MLX5_REFORMAT_TYPE_L2_TO_VXLAN || + reformat_type == MLX5_REFORMAT_TYPE_L2_TO_L3_TUNNEL) tun_entropy->num_enabling_entries--; else if (reformat_type == MLX5_REFORMAT_TYPE_L2_TO_NVGRE && --tun_entropy->num_disabling_entries == 0) From b62869a81a7ce388d1fbb0fac5fa8300ea614d81 Mon Sep 17 00:00:00 2001 From: Gal Pressman Date: Mon, 6 Jul 2026 08:50:17 +0300 Subject: [PATCH 0853/1054] ethtool: rss: Fix hfunc and input_xfrm parsing on big endian ETHTOOL_A_RSS_HFUNC and ETHTOOL_A_RSS_INPUT_XFRM are NLA_U32 attributes, but ethnl_rss_set() and ethnl_rss_create_doit() parse them with ethnl_update_u8(), which reads a single byte. On little endian this happens to read the least significant byte and works as long as the value fits in a byte. On big endian it reads the most significant byte, so the requested value is parsed incorrectly. The destination fields in struct ethtool_rxfh_param are u8, so the attribute can't be read directly with ethnl_update_u32(). Cap the hfunc policy at U8_MAX so an out of range value is rejected instead of being silently truncated into the u8 field, and add ethnl_update_u8_u32() to read the full u32 and narrow it into the u8 destination. Fixes: 82ae67cbc423 ("ethtool: rss: support setting hfunc via Netlink") Fixes: d3e2c7bab124 ("ethtool: rss: support setting input-xfrm via Netlink") Fixes: a166ab7816c5 ("ethtool: rss: support creating contexts via Netlink") Reviewed-by: Dragos Tatulea Reviewed-by: Nimrod Oren Signed-off-by: Gal Pressman Link: https://patch.msgid.link/20260706055017.3355806-1-gal@nvidia.com Signed-off-by: Paolo Abeni --- net/ethtool/netlink.h | 28 ++++++++++++++++++++++++++++ net/ethtool/rss.c | 14 ++++++++------ 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h index 4ca2eca2e94b..3e969a070f9f 100644 --- a/net/ethtool/netlink.h +++ b/net/ethtool/netlink.h @@ -114,6 +114,34 @@ static inline void ethnl_update_u8(u8 *dst, const struct nlattr *attr, *mod = true; } +/** + * ethnl_update_u8_u32() - update u8 value from an NLA_U32 attribute + * @dst: value to update + * @attr: netlink attribute with new value or null + * @mod: pointer to bool for modification tracking + * + * Some attributes are NLA_U32 on the wire but are stored in a u8. Read the + * full 32-bit value from NLA_U32 netlink attribute @attr and narrow it into + * the u8 pointed to by @dst; do nothing if @attr is null. + * Bool pointed to by @mod is set to true if this function changed the value + * of *dst, otherwise it is left as is. + */ +static inline void ethnl_update_u8_u32(u8 *dst, const struct nlattr *attr, + bool *mod) +{ + u32 val; + + if (!attr) + return; + val = nla_get_u32(attr); + DEBUG_NET_WARN_ON_ONCE(val > U8_MAX); + if (*dst == val) + return; + + *dst = val; + *mod = true; +} + /** * ethnl_update_bool32() - update u32 used as bool from NLA_U8 attribute * @dst: value to update diff --git a/net/ethtool/rss.c b/net/ethtool/rss.c index d8adc78e3775..d4a1a4724b67 100644 --- a/net/ethtool/rss.c +++ b/net/ethtool/rss.c @@ -570,7 +570,7 @@ static const struct nla_policy ethnl_rss_flows_policy[] = { const struct nla_policy ethnl_rss_set_policy[ETHTOOL_A_RSS_FLOW_HASH + 1] = { [ETHTOOL_A_RSS_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy), [ETHTOOL_A_RSS_CONTEXT] = { .type = NLA_U32, }, - [ETHTOOL_A_RSS_HFUNC] = NLA_POLICY_MIN(NLA_U32, 1), + [ETHTOOL_A_RSS_HFUNC] = NLA_POLICY_RANGE(NLA_U32, 1, U8_MAX), [ETHTOOL_A_RSS_INDIR] = { .type = NLA_BINARY, }, [ETHTOOL_A_RSS_HKEY] = NLA_POLICY_MIN(NLA_BINARY, 1), [ETHTOOL_A_RSS_INPUT_XFRM] = @@ -851,7 +851,7 @@ ethnl_rss_set(struct ethnl_req_info *req_info, struct genl_info *info) indir_mod = !!tb[ETHTOOL_A_RSS_INDIR]; rxfh.hfunc = data.hfunc; - ethnl_update_u8(&rxfh.hfunc, tb[ETHTOOL_A_RSS_HFUNC], &mod); + ethnl_update_u8_u32(&rxfh.hfunc, tb[ETHTOOL_A_RSS_HFUNC], &mod); if (rxfh.hfunc == data.hfunc) rxfh.hfunc = ETH_RSS_HASH_NO_CHANGE; @@ -860,7 +860,8 @@ ethnl_rss_set(struct ethnl_req_info *req_info, struct genl_info *info) goto exit_free_indir; rxfh.input_xfrm = data.input_xfrm; - ethnl_update_u8(&rxfh.input_xfrm, tb[ETHTOOL_A_RSS_INPUT_XFRM], &mod); + ethnl_update_u8_u32(&rxfh.input_xfrm, tb[ETHTOOL_A_RSS_INPUT_XFRM], + &mod); xfrm_sym = rxfh.input_xfrm || data.input_xfrm; if (rxfh.input_xfrm == data.input_xfrm) rxfh.input_xfrm = RXH_XFRM_NO_CHANGE; @@ -934,7 +935,7 @@ const struct ethnl_request_ops ethnl_rss_request_ops = { const struct nla_policy ethnl_rss_create_policy[ETHTOOL_A_RSS_INPUT_XFRM + 1] = { [ETHTOOL_A_RSS_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy), [ETHTOOL_A_RSS_CONTEXT] = NLA_POLICY_MIN(NLA_U32, 1), - [ETHTOOL_A_RSS_HFUNC] = NLA_POLICY_MIN(NLA_U32, 1), + [ETHTOOL_A_RSS_HFUNC] = NLA_POLICY_RANGE(NLA_U32, 1, U8_MAX), [ETHTOOL_A_RSS_INDIR] = NLA_POLICY_MIN(NLA_BINARY, 1), [ETHTOOL_A_RSS_HKEY] = NLA_POLICY_MIN(NLA_BINARY, 1), [ETHTOOL_A_RSS_INPUT_XFRM] = @@ -1048,14 +1049,15 @@ int ethnl_rss_create_doit(struct sk_buff *skb, struct genl_info *info) goto exit_clean_data; indir_user_size = ret; - ethnl_update_u8(&rxfh.hfunc, tb[ETHTOOL_A_RSS_HFUNC], &mod); + ethnl_update_u8_u32(&rxfh.hfunc, tb[ETHTOOL_A_RSS_HFUNC], &mod); ret = rss_set_prep_hkey(dev, info, &data, &rxfh, &mod); if (ret) goto exit_free_indir; rxfh.input_xfrm = RXH_XFRM_NO_CHANGE; - ethnl_update_u8(&rxfh.input_xfrm, tb[ETHTOOL_A_RSS_INPUT_XFRM], &mod); + ethnl_update_u8_u32(&rxfh.input_xfrm, tb[ETHTOOL_A_RSS_INPUT_XFRM], + &mod); ctx = ethtool_rxfh_ctx_alloc(ops, data.indir_size, data.hkey_size); if (!ctx) { From fabb881df322da25442f98d23f5fa371e3c78ec4 Mon Sep 17 00:00:00 2001 From: Harman Kalra Date: Thu, 2 Jul 2026 10:26:16 +0530 Subject: [PATCH 0854/1054] octeontx2-af: fix VF bringup affecting PF promiscuous state Mbox handling of nix_set_rx_mode for a VF with promiscuous and all_multi flags set to false causes deletion of the PF's promiscuous and allmulti MCAM rules. This occurs because the APIs that enable/disable these rules operate only on the PF, even when the mbox request is made via a VF interface. Guard both rvu_npc_enable_allmulti_entry() and rvu_npc_enable_promisc_entry() disable paths with an is_vf() check so that a VF bringing up or tearing down its interface cannot inadvertently clear the PF's MCAM rules. Fixes: 967db3529eca ("octeontx2-af: add support for multicast/promisc packet replication feature") Signed-off-by: Harman Kalra Signed-off-by: Nitin Shetty J Link: https://patch.msgid.link/20260702045616.3002773-2-nshettyj@marvell.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c index 0297c7ab0614..6a0ce2665031 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c @@ -4580,7 +4580,7 @@ int rvu_mbox_handler_nix_set_rx_mode(struct rvu *rvu, struct nix_rx_mode *req, rvu_npc_install_allmulti_entry(rvu, pcifunc, nixlf, pfvf->rx_chan_base); } else { - if (!nix_rx_multicast) + if (!nix_rx_multicast && !is_vf(pcifunc)) rvu_npc_enable_allmulti_entry(rvu, pcifunc, nixlf, false); } @@ -4590,7 +4590,7 @@ int rvu_mbox_handler_nix_set_rx_mode(struct rvu *rvu, struct nix_rx_mode *req, pfvf->rx_chan_base, pfvf->rx_chan_cnt); else - if (!nix_rx_multicast) + if (!nix_rx_multicast && !is_vf(pcifunc)) rvu_npc_enable_promisc_entry(rvu, pcifunc, nixlf, false); return 0; From 34a4dd45cf210c04fee773b0dbc350aec285f03c Mon Sep 17 00:00:00 2001 From: Matthew Brost Date: Wed, 1 Jul 2026 18:24:34 -0700 Subject: [PATCH 0855/1054] drm/xe: Fix PTE index in xe_vm_populate_pgtable() for chunked binds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit xe_vm_populate_pgtable() indexed the source PTE array (update->pt_entries) by the per-call loop counter, assuming each call starts at the first entry of the update. That holds for the CPU bind path (xe_migrate_update_pgtables_cpu), which populates a whole update in a single call, but not for the GPU bind path: write_pgtable() splits an update into MAX_PTE_PER_SDI (510) sized MI_STORE_DATA_IMM chunks, invoking the populate callback once per chunk with an advancing qword_ofs but a fresh command- buffer destination pointer. As a result, every chunk after the first re-read pt_entries from index 0 instead of from its true offset, so PTEs beyond the first 510 entries of a single update were programmed with the wrong physical pages, shifting the mapping by exactly MAX_PTE_PER_SDI pages. This stayed latent because a single update only exceeds 510 qwords when a large (e.g. 2M) region is bound as individual 4K PTEs rather than a single huge-page entry, which happens when the backing store is sufficiently fragmented. It was surfaced by the BO defrag path, which deliberately rebinds such fragmented ranges via the GPU bind path, producing deterministic data corruption offset by 510 pages. Index pt_entries by the chunk's absolute offset relative to update->ofs so both the CPU and GPU paths pick the correct entries. Fixes: dd08ebf6c352 ("drm/xe: Introduce a new DRM driver for Intel GPUs") Cc: stable@vger.kernel.org Assisted-by: GitHub_Copilot:claude-opus-4.8 Signed-off-by: Matthew Brost Reviewed-by: Matthew Auld Link: https://patch.msgid.link/20260702012434.3861171-1-matthew.brost@intel.com (cherry picked from commit e6f2d0b757c4fb577a513c577140109d1d292a9a) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_pt.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_pt.c b/drivers/gpu/drm/xe/xe_pt.c index 670bc2206fea..781b956cd2bc 100644 --- a/drivers/gpu/drm/xe/xe_pt.c +++ b/drivers/gpu/drm/xe/xe_pt.c @@ -1026,12 +1026,22 @@ xe_vm_populate_pgtable(struct xe_migrate_pt_update *pt_update, struct xe_tile *t u64 *ptr = data; u32 i; + /* + * @qword_ofs is the absolute entry offset within the page table, while + * @ptes is indexed relative to @update->ofs (its first entry). The GPU + * path (write_pgtable) splits a single update into MAX_PTE_PER_SDI-sized + * chunks, calling this with an advancing @qword_ofs but a fresh @data + * pointer per chunk, so translate back into a @ptes index rather than + * assuming the chunk starts at ptes[0]. + */ for (i = 0; i < num_qwords; i++) { + u32 idx = qword_ofs - update->ofs + i; + if (map) xe_map_wr(tile_to_xe(tile), map, (qword_ofs + i) * - sizeof(u64), u64, ptes[i].pte); + sizeof(u64), u64, ptes[idx].pte); else - ptr[i] = ptes[i].pte; + ptr[i] = ptes[idx].pte; } } From af80e2bfde9312c76b60cf9274248dce0410b30d Mon Sep 17 00:00:00 2001 From: Matthew Brost Date: Thu, 2 Jul 2026 14:58:05 -0700 Subject: [PATCH 0856/1054] drm/xe: Wait on external BO kernel fences in exec IOCTL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before arming a user job, xe_exec_ioctl() only added the VM's dma-resv KERNEL slot as a dependency. That slot covers rebinds and the kernel operations of the VM's private BOs, but not external BOs (bo->vm == NULL), which carry their kernel operations (evictions, moves, ...) in their own dma-resv KERNEL slot. The DMA_RESV_USAGE_KERNEL slot is the cross-driver contract for memory management operations that must complete before the BO or its backing store may be used: any accessor is required to wait on the KERNEL fences before touching the resv. By skipping the external BOs' KERNEL slots, the exec path violated that contract and could schedule a user job while a kernel operation on an external BO mapped by the VM was still in flight, racing against it and potentially reading or writing memory that was being moved. Replace the VM-only dependency with an iteration over every object locked by the exec, adding each object's KERNEL slot as a job dependency. This covers the VM resv (rebinds and private BOs) as well as every external BO, mirroring the drm_gpuvm_resv_add_fence() call that later publishes the job fence to the same set of objects. Long-running mode continues to skip this, as before. Fixes: dd08ebf6c352 ("drm/xe: Introduce a new DRM driver for Intel GPUs") Cc: stable@vger.kernel.org Assisted-by: GitHub_Copilot:claude-opus-4.8 Signed-off-by: Matthew Brost Reviewed-by: Matthew Auld Link: https://patch.msgid.link/20260702215805.4011228-1-matthew.brost@intel.com (cherry picked from commit a6b842acf3ddd1efc53a56de9260cfa718fb35e7) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_exec.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_exec.c b/drivers/gpu/drm/xe/xe_exec.c index e05dabfcd43c..d5293bc33a67 100644 --- a/drivers/gpu/drm/xe/xe_exec.c +++ b/drivers/gpu/drm/xe/xe_exec.c @@ -292,13 +292,23 @@ int xe_exec_ioctl(struct drm_device *dev, void *data, struct drm_file *file) goto err_exec; } - /* Wait behind rebinds */ + /* + * Wait behind rebinds and any kernel operations (evictions, defrag + * moves, ...) on the VM and all external BOs. The VM's private BOs + * carry their kernel ops in the VM dma-resv KERNEL slot, while each + * external BO carries them in its own dma-resv KERNEL slot; both are + * covered by iterating every object locked by the exec, mirroring the + * drm_gpuvm_resv_add_fence() below. + */ if (!xe_vm_in_lr_mode(vm)) { - err = xe_sched_job_add_deps(job, - xe_vm_resv(vm), - DMA_RESV_USAGE_KERNEL); - if (err) - goto err_put_job; + struct drm_gem_object *obj; + + drm_exec_for_each_locked_object(exec, obj) { + err = xe_sched_job_add_deps(job, obj->resv, + DMA_RESV_USAGE_KERNEL); + if (err) + goto err_put_job; + } } for (i = 0; i < num_syncs && !err; i++) From 91426ce50d14a49bde53b3ad1e48393556ba92cd Mon Sep 17 00:00:00 2001 From: Anas Khan Date: Thu, 2 Jul 2026 16:58:20 +0530 Subject: [PATCH 0857/1054] drm/xe: remove duplicate include MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit xe_pci.c includes twice, separated only by the include. Drop the redundant second include; this is a non-functional cleanup flagged by scripts/checkincludes.pl. Fixes: 6cad22853cb8 ("drm/xe/kunit: Add stub to read_gmdid") Signed-off-by: Anas Khan Link: https://patch.msgid.link/20260702112820.34675-1-anxkhn28@gmail.com Signed-off-by: Rodrigo Vivi (cherry picked from commit 84ed5b0a925721aaf069d36e18a99db966ff4e80) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/tests/xe_pci.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpu/drm/xe/tests/xe_pci.c b/drivers/gpu/drm/xe/tests/xe_pci.c index 9240aff779da..c2c686aed1cb 100644 --- a/drivers/gpu/drm/xe/tests/xe_pci.c +++ b/drivers/gpu/drm/xe/tests/xe_pci.c @@ -9,7 +9,6 @@ #include #include -#include #include #define PLATFORM_CASE(platform__, graphics_step__) \ From 14abbed336a2d1bbd726c25d148d2ec0ff928073 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Wed, 8 Jul 2026 15:34:22 +0800 Subject: [PATCH 0858/1054] drm/xe: free madvise VMA array on L2 flush failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit xe_vm_madvise_ioctl() allocates madvise_range.vmas in get_vmas(). After get_vmas() succeeds with at least one VMA, error paths must go through free_vmas so the array is released before the madvise details are destroyed. The L2 flush validation path added for PAT madvise rejects some SVM/userptr ranges after get_vmas() has succeeded, but jumps directly to madv_fini. This skips kfree(madvise_range.vmas), leaking the VMA array on each failed ioctl. Jump to free_vmas instead, matching the other validation failure paths after get_vmas() has succeeded. Fixes: 4f39a194d41e ("drm/xe/xe3p_lpg: Restrict UAPI to enable L2 flush optimization") Signed-off-by: Guangshuo Li Reviewed-by: Rodrigo Vivi Link: https://patch.msgid.link/20260708073422.725186-1-lgs201920130244@gmail.com Signed-off-by: Rodrigo Vivi (cherry picked from commit c3a1c3579b1250060da73507a4acef712974c78a) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_vm_madvise.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/xe/xe_vm_madvise.c b/drivers/gpu/drm/xe/xe_vm_madvise.c index c4fb29004195..246fe1843142 100644 --- a/drivers/gpu/drm/xe/xe_vm_madvise.c +++ b/drivers/gpu/drm/xe/xe_vm_madvise.c @@ -643,7 +643,7 @@ int xe_vm_madvise_ioctl(struct drm_device *dev, void *data, struct drm_file *fil xe_device_is_l2_flush_optimized(xe) && (pat_index != 19 && coh_mode != XE_COH_2WAY))) { err = -EINVAL; - goto madv_fini; + goto free_vmas; } } From f5ef65adf81da3dbce4e692e48c1754c0bb95da0 Mon Sep 17 00:00:00 2001 From: Shuicheng Lin Date: Tue, 30 Jun 2026 19:22:21 +0000 Subject: [PATCH 0859/1054] drm/xe/userptr: Stub notifier_lock helpers when DRM_GPUSVM=n MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When CONFIG_DRM_GPUSVM=n (e.g. um-allyesconfig), the only caller of xe_pt_svm_userptr_notifier_lock() is compiled out, triggering: drivers/gpu/drm/xe/xe_pt.c:1418:13: warning: 'xe_pt_svm_userptr_notifier_lock' defined but not used [-Wunused-function] The helpers cannot simply be removed in this case: the matching xe_pt_svm_userptr_notifier_unlock() is also referenced from xe_pt_update_ops_run(), which lives outside any DRM_GPUSVM ifdef and is gated only at runtime by pt_update_ops->needs_svm_lock. The symbol must exist in all builds. Provide empty static inline stubs for !DRM_GPUSVM, matching the pattern used by xe_svm_notifier_lock()/_unlock() in xe_svm.h. Fixes: dca6e08c923a ("drm/xe/userptr: Hold notifier_lock for write on inject test path") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202606302210.QqcLbOEN-lkp@intel.com/ Reviewed-by: Matthew Brost Link: https://patch.msgid.link/20260630192221.2998168-1-shuicheng.lin@intel.com Signed-off-by: Shuicheng Lin (cherry picked from commit 3359422bf0a1140e96d783a19a397686e580a3ca) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_pt.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/gpu/drm/xe/xe_pt.c b/drivers/gpu/drm/xe/xe_pt.c index 781b956cd2bc..e787c0c27c42 100644 --- a/drivers/gpu/drm/xe/xe_pt.c +++ b/drivers/gpu/drm/xe/xe_pt.c @@ -1418,6 +1418,7 @@ static int xe_pt_pre_commit(struct xe_migrate_pt_update *pt_update) pt_update_ops, rftree); } +#if IS_ENABLED(CONFIG_DRM_GPUSVM) /* * Acquire/release the svm notifier_lock around xe_pt_svm_userptr_pre_commit() * and the matching late release in xe_pt_update_ops_run(). Read mode by @@ -1444,6 +1445,10 @@ static void xe_pt_svm_userptr_notifier_unlock(struct xe_vm *vm) xe_svm_notifier_unlock(vm); #endif } +#else +static inline void xe_pt_svm_userptr_notifier_lock(struct xe_vm *vm) { } +static inline void xe_pt_svm_userptr_notifier_unlock(struct xe_vm *vm) { } +#endif #if IS_ENABLED(CONFIG_DRM_GPUSVM) #ifdef CONFIG_DRM_XE_USERPTR_INVAL_INJECT From 78237e3c0720fcc6eb9b87e90fd70f63eeca886f Mon Sep 17 00:00:00 2001 From: Dust Li Date: Tue, 7 Jul 2026 15:43:18 +0800 Subject: [PATCH 0860/1054] dibs: loopback: validate offset and size in move_data() The loopback move_data() performs a memcpy into the registered DMB without checking whether offset + size exceeds the DMB length. Unlike real ISM hardware, which enforces memory region bounds natively, the software loopback has no such protection. A peer-supplied out-of-bounds offset or oversized write would result in an OOB write past the allocated kernel buffer. Add an explicit bounds check before the memcpy to reject such requests with -EINVAL. Fixes: f7a22071dbf3 ("net/smc: implement DMB-related operations of loopback-ism") Cc: stable@vger.kernel.org Reported-by: Federico Kirschbaum Signed-off-by: Dust Li Reported-by: Baul Lee Link: https://patch.msgid.link/20260707074318.1448662-1-dust.li@linux.alibaba.com Signed-off-by: Paolo Abeni --- drivers/dibs/dibs_loopback.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/dibs/dibs_loopback.c b/drivers/dibs/dibs_loopback.c index ec3b48cb0e87..0f2e09311152 100644 --- a/drivers/dibs/dibs_loopback.c +++ b/drivers/dibs/dibs_loopback.c @@ -254,6 +254,11 @@ static int dibs_lo_move_data(struct dibs_dev *dibs, u64 dmb_tok, read_unlock_bh(&ldev->dmb_ht_lock); return -EINVAL; } + if ((u64)offset + size > rmb_node->len) { + read_unlock_bh(&ldev->dmb_ht_lock); + return -EINVAL; + } + memcpy((char *)rmb_node->cpu_addr + offset, data, size); sba_idx = rmb_node->sba_idx; read_unlock_bh(&ldev->dmb_ht_lock); From f5089008f90c0a7c5520dff3934e0af00adf322d Mon Sep 17 00:00:00 2001 From: Daehyeon Ko <4ncienth@gmail.com> Date: Fri, 3 Jul 2026 17:36:33 +0900 Subject: [PATCH 0861/1054] macsec: don't read an unset MAC header in macsec_encrypt() macsec_encrypt() reads the Ethernet header via eth_hdr(skb) (skb->head + skb->mac_header) to memmove() the 12 source/destination MAC bytes forward and make room for the SecTAG. On the AF_PACKET SOCK_RAW + PACKET_QDISC_BYPASS transmit path the skb reaches the macsec ndo_start_xmit() with the MAC header unset, so eth_hdr(skb) resolves to skb->head + (u16)~0 and the read is out of bounds: a 12-byte heap over-read that is also emitted on the wire as the frame's outer source/destination MAC. KASAN reports a slab-out-of-bounds read in macsec_start_xmit() on 6.0; on current mainline a CONFIG_DEBUG_NET build flags it as an unset mac header in skb_mac_header(). On the TX path the L2 header is at skb->data, so use skb_eth_hdr(), added by commit 96cc4b69581d ("macvlan: do not assume mac_header is set in macvlan_broadcast()") for exactly this purpose. Fixes: c09440f7dcb3 ("macsec: introduce IEEE 802.1AE driver") Cc: stable@vger.kernel.org Signed-off-by: Daehyeon Ko <4ncienth@gmail.com> Reviewed-by: Sabrina Dubroca Link: https://patch.msgid.link/20260703083634.2035145-1-4ncienth@gmail.com Signed-off-by: Paolo Abeni --- drivers/net/macsec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c index fb009120a924..dd89282f0179 100644 --- a/drivers/net/macsec.c +++ b/drivers/net/macsec.c @@ -646,7 +646,7 @@ static struct sk_buff *macsec_encrypt(struct sk_buff *skb, } unprotected_len = skb->len; - eth = eth_hdr(skb); + eth = skb_eth_hdr(skb); sci_present = macsec_send_sci(secy); hh = skb_push(skb, macsec_extra_len(sci_present)); memmove(hh, eth, 2 * ETH_ALEN); From a4f27ad055392fa164f5649e89a3637b033c5fcc Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Wed, 8 Jul 2026 20:35:20 +0800 Subject: [PATCH 0862/1054] smb: client: fix overflow in passthrough ioctl bounds check smb2_ioctl_query_info() validates the PASSTHRU_FSCTL response payload before copying it to userspace. The payload offset and length both come from 32-bit fields. The bounds check currently adds OutputOffset and qi.input_buffer_length directly, so the addition can wrap in 32-bit arithmetic before the result is compared against the response buffer length. A malicious server can use a large OutputOffset and a small OutputCount to make the wrapped sum pass the bounds check. The later copy_to_user() then reads from io_rsp + OutputOffset, outside the response buffer. Use size_add() for the offset plus length check so overflow is treated as out of bounds. Fixes: 2b1116bbe898 ("CIFS: Use common error handling code in smb2_ioctl_query_info()") Signed-off-by: Guangshuo Li Signed-off-by: Steve French --- fs/smb/client/smb2ops.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c index 15b3ae45c833..1d60a431494b 100644 --- a/fs/smb/client/smb2ops.c +++ b/fs/smb/client/smb2ops.c @@ -1772,8 +1772,8 @@ smb2_ioctl_query_info(const unsigned int xid, if (le32_to_cpu(io_rsp->OutputCount) < qi.input_buffer_length) qi.input_buffer_length = le32_to_cpu(io_rsp->OutputCount); if (qi.input_buffer_length > 0 && - le32_to_cpu(io_rsp->OutputOffset) + qi.input_buffer_length - > rsp_iov[1].iov_len) { + size_add(le32_to_cpu(io_rsp->OutputOffset), + qi.input_buffer_length) > rsp_iov[1].iov_len) { rc = -EFAULT; goto out; } From fc8789bb57e625e5f32ac57ca2e7d3e7b7fda225 Mon Sep 17 00:00:00 2001 From: ChenXiaoSong Date: Thu, 9 Jul 2026 10:57:00 +0800 Subject: [PATCH 0863/1054] smb/client: use stack-allocated smb2_file_all_info in smb3_query_mf_symlink() SMB2_open() only fills the fixed fields, so a stack-allocated smb2_file_all_info is sufficient here. Signed-off-by: ChenXiaoSong Signed-off-by: Steve French --- fs/smb/client/link.c | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/fs/smb/client/link.c b/fs/smb/client/link.c index dd127917a340..0014523d6511 100644 --- a/fs/smb/client/link.c +++ b/fs/smb/client/link.c @@ -320,7 +320,7 @@ smb3_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, int buf_type = CIFS_NO_BUFFER; __le16 *utf16_path; __u8 oplock = SMB2_OPLOCK_LEVEL_NONE; - struct smb2_file_all_info *pfile_info = NULL; + struct smb2_file_all_info file_info = {}; oparms = (struct cifs_open_parms) { .tcon = tcon, @@ -336,20 +336,12 @@ smb3_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, if (utf16_path == NULL) return -ENOMEM; - pfile_info = kzalloc(sizeof(struct smb2_file_all_info) + PATH_MAX * 2, - GFP_KERNEL); - - if (pfile_info == NULL) { - kfree(utf16_path); - return -ENOMEM; - } - - rc = SMB2_open(xid, &oparms, utf16_path, &oplock, pfile_info, NULL, + rc = SMB2_open(xid, &oparms, utf16_path, &oplock, &file_info, NULL, NULL, NULL); if (rc) goto qmf_out_open_fail; - if (pfile_info->EndOfFile != cpu_to_le64(CIFS_MF_SYMLINK_FILE_SIZE)) { + if (file_info.EndOfFile != cpu_to_le64(CIFS_MF_SYMLINK_FILE_SIZE)) { /* it's not a symlink */ rc = -ENOENT; /* Is there a better rc to return? */ goto qmf_out; @@ -367,7 +359,6 @@ smb3_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); qmf_out_open_fail: kfree(utf16_path); - kfree(pfile_info); return rc; } From 1f551e407bb49dce0bbd34ed6a499dad69e18020 Mon Sep 17 00:00:00 2001 From: ChenXiaoSong Date: Thu, 9 Jul 2026 10:57:01 +0800 Subject: [PATCH 0864/1054] smb/client: pass cifs_open_info_data to SMB2_open() Let SMB2_open() fill the smb2_file_all_info embedded in cifs_open_info_data directly. This removes the temporary smb2_file_all_info copy in smb2_open_file(). Signed-off-by: ChenXiaoSong Signed-off-by: Steve French --- fs/smb/client/link.c | 6 +++--- fs/smb/client/smb2file.c | 19 ++++++++----------- fs/smb/client/smb2pdu.c | 25 +++++++++++++------------ fs/smb/client/smb2proto.h | 2 +- 4 files changed, 25 insertions(+), 27 deletions(-) diff --git a/fs/smb/client/link.c b/fs/smb/client/link.c index 0014523d6511..b60dc407815d 100644 --- a/fs/smb/client/link.c +++ b/fs/smb/client/link.c @@ -320,7 +320,7 @@ smb3_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, int buf_type = CIFS_NO_BUFFER; __le16 *utf16_path; __u8 oplock = SMB2_OPLOCK_LEVEL_NONE; - struct smb2_file_all_info file_info = {}; + struct cifs_open_info_data data = {}; oparms = (struct cifs_open_parms) { .tcon = tcon, @@ -336,12 +336,12 @@ smb3_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, if (utf16_path == NULL) return -ENOMEM; - rc = SMB2_open(xid, &oparms, utf16_path, &oplock, &file_info, NULL, + rc = SMB2_open(xid, &oparms, utf16_path, &oplock, &data, NULL, NULL, NULL); if (rc) goto qmf_out_open_fail; - if (file_info.EndOfFile != cpu_to_le64(CIFS_MF_SYMLINK_FILE_SIZE)) { + if (data.fi.EndOfFile != cpu_to_le64(CIFS_MF_SYMLINK_FILE_SIZE)) { /* it's not a symlink */ rc = -ENOENT; /* Is there a better rc to return? */ goto qmf_out; diff --git a/fs/smb/client/smb2file.c b/fs/smb/client/smb2file.c index 6860eff31693..5ef919bce52d 100644 --- a/fs/smb/client/smb2file.c +++ b/fs/smb/client/smb2file.c @@ -154,8 +154,6 @@ int smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *smb2_path; __u8 smb2_oplock; struct cifs_open_info_data *data = buf; - struct smb2_file_all_info file_info = {}; - struct smb2_file_all_info *smb2_data = data ? &file_info : NULL; struct kvec err_iov = {}; int err_buftype = CIFS_NO_BUFFER; struct cifs_fid *fid = oparms->fid; @@ -182,14 +180,14 @@ int smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms, } smb2_oplock = SMB2_OPLOCK_LEVEL_BATCH; - rc = SMB2_open(xid, oparms, smb2_path, &smb2_oplock, smb2_data, NULL, &err_iov, + rc = SMB2_open(xid, oparms, smb2_path, &smb2_oplock, data, NULL, &err_iov, &err_buftype); if (rc == -EACCES && retry_without_read_attributes) { free_rsp_buf(err_buftype, err_iov.iov_base); memset(&err_iov, 0, sizeof(err_iov)); err_buftype = CIFS_NO_BUFFER; oparms->desired_access &= ~FILE_READ_ATTRIBUTES; - rc = SMB2_open(xid, oparms, smb2_path, &smb2_oplock, smb2_data, NULL, &err_iov, + rc = SMB2_open(xid, oparms, smb2_path, &smb2_oplock, data, NULL, &err_iov, &err_buftype); } if (rc && data) { @@ -202,9 +200,9 @@ int smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms, oparms->path, &data->symlink_target); if (!rc) { - memset(smb2_data, 0, sizeof(*smb2_data)); + memset(&data->fi, 0, sizeof(data->fi)); oparms->create_options |= OPEN_REPARSE_POINT; - rc = SMB2_open(xid, oparms, smb2_path, &smb2_oplock, smb2_data, + rc = SMB2_open(xid, oparms, smb2_path, &smb2_oplock, data, NULL, NULL, NULL); oparms->create_options &= ~OPEN_REPARSE_POINT; } @@ -238,23 +236,22 @@ int smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms, rc = 0; } - if (smb2_data) { + if (data) { /* if open response does not have IndexNumber field - get it */ - if (smb2_data->IndexNumber == 0) { + if (data->fi.IndexNumber == 0) { rc = SMB2_get_srv_num(xid, oparms->tcon, fid->persistent_fid, fid->volatile_fid, - &smb2_data->IndexNumber); + &data->fi.IndexNumber); if (rc) { /* * let get_inode_info disable server inode * numbers */ - smb2_data->IndexNumber = 0; + data->fi.IndexNumber = 0; rc = 0; } } - memcpy(&data->fi, smb2_data, sizeof(data->fi)); } *oplock = smb2_oplock; diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index 95c0efe9d43b..bc77170458b3 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -3287,7 +3287,7 @@ SMB2_open_free(struct smb_rqst *rqst) int SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, - __u8 *oplock, struct smb2_file_all_info *buf, + __u8 *oplock, struct cifs_open_info_data *buf, struct create_posix_rsp *posix, struct kvec *err_iov, int *buftype) { @@ -3302,6 +3302,7 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, int rc = 0; int flags = 0; int retries = 0, cur_sleep = 0; + struct smb2_file_all_info *file_info = buf ? &buf->fi : NULL; replay_again: /* reinitialize for possible replay */ @@ -3370,21 +3371,21 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, oparms->fid->mid = le64_to_cpu(rsp->hdr.MessageId); #endif /* CIFS_DEBUG2 */ - if (buf) { - buf->CreationTime = rsp->CreationTime; - buf->LastAccessTime = rsp->LastAccessTime; - buf->LastWriteTime = rsp->LastWriteTime; - buf->ChangeTime = rsp->ChangeTime; - buf->AllocationSize = rsp->AllocationSize; - buf->EndOfFile = rsp->EndofFile; - buf->Attributes = rsp->FileAttributes; - buf->NumberOfLinks = cpu_to_le32(1); - buf->DeletePending = 0; /* successful open = not delete pending */ + if (file_info) { + file_info->CreationTime = rsp->CreationTime; + file_info->LastAccessTime = rsp->LastAccessTime; + file_info->LastWriteTime = rsp->LastWriteTime; + file_info->ChangeTime = rsp->ChangeTime; + file_info->AllocationSize = rsp->AllocationSize; + file_info->EndOfFile = rsp->EndofFile; + file_info->Attributes = rsp->FileAttributes; + file_info->NumberOfLinks = cpu_to_le32(1); + file_info->DeletePending = 0; /* successful open = not delete pending */ } rc = smb2_parse_contexts(server, &rsp_iov, &oparms->fid->epoch, - oparms->fid->lease_key, oplock, buf, posix); + oparms->fid->lease_key, oplock, file_info, posix); trace_smb3_open_done(xid, rsp->PersistentFileId, tcon->tid, ses->Suid, oparms->create_options, oparms->desired_access, diff --git a/fs/smb/client/smb2proto.h b/fs/smb/client/smb2proto.h index 78a4e1c340f9..e01effe45ae2 100644 --- a/fs/smb/client/smb2proto.h +++ b/fs/smb/client/smb2proto.h @@ -136,7 +136,7 @@ int SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree, struct cifs_tcon *tcon, const struct nls_table *cp); int SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon); int SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, - __le16 *path, __u8 *oplock, struct smb2_file_all_info *buf, + __le16 *path, __u8 *oplock, struct cifs_open_info_data *buf, struct create_posix_rsp *posix, struct kvec *err_iov, int *buftype); int SMB2_open_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server, From 8fce4cf4369c766a3293a05419500cbfde72e60d Mon Sep 17 00:00:00 2001 From: ChenXiaoSong Date: Thu, 9 Jul 2026 10:57:02 +0800 Subject: [PATCH 0865/1054] smb/client: zero-initialize stack-allocated cifs_open_info_data Stack-allocated cifs_open_info_data may contain random data. This can make some fields have wrong value if they are not set later. Signed-off-by: ChenXiaoSong Signed-off-by: Steve French --- fs/smb/client/link.c | 2 +- fs/smb/client/smb1ops.c | 2 +- fs/smb/client/smb2ops.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/smb/client/link.c b/fs/smb/client/link.c index b60dc407815d..8d5d6aca742a 100644 --- a/fs/smb/client/link.c +++ b/fs/smb/client/link.c @@ -234,7 +234,7 @@ cifs_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, struct cifs_open_parms oparms; struct cifs_io_parms io_parms = {0}; int buf_type = CIFS_NO_BUFFER; - struct cifs_open_info_data query_data; + struct cifs_open_info_data query_data = {}; oparms = (struct cifs_open_parms) { .tcon = tcon, diff --git a/fs/smb/client/smb1ops.c b/fs/smb/client/smb1ops.c index d34b3d99f6ed..f72879af12c9 100644 --- a/fs/smb/client/smb1ops.c +++ b/fs/smb/client/smb1ops.c @@ -949,7 +949,7 @@ smb_set_file_info(struct inode *inode, const char *full_path, struct cifs_open_parms oparms; struct cifsFileInfo *open_file; FILE_BASIC_INFO new_buf; - struct cifs_open_info_data query_data; + struct cifs_open_info_data query_data = {}; __le64 write_time = buf->LastWriteTime; struct cifsInodeInfo *cinode = CIFS_I(inode); struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c index 1d60a431494b..6bce44b171e4 100644 --- a/fs/smb/client/smb2ops.c +++ b/fs/smb/client/smb2ops.c @@ -5237,7 +5237,7 @@ int __cifs_sfu_make_node(unsigned int xid, struct inode *inode, { struct TCP_Server_Info *server = tcon->ses->server; struct cifs_open_parms oparms; - struct cifs_open_info_data idata; + struct cifs_open_info_data idata = {}; struct cifs_io_parms io_parms = {}; struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); struct cifs_fid fid; From 9dd1964ac59d293c3684e71b4bbcd10e28f04bb4 Mon Sep 17 00:00:00 2001 From: ChenXiaoSong Date: Thu, 9 Jul 2026 10:57:03 +0800 Subject: [PATCH 0866/1054] smb/client: fix incorrect nlink returned by fstat() Reproducer: 1. mount -t cifs //${server_ip}/export /mnt 2. touch /mnt/file1; ln /mnt/file1 /mnt/file2; ln /mnt/file1 /mnt/file3 3. C program: int fd = open("/mnt/file1", O_RDONLY); 4. C program: struct stat stbuf; fstat(fd, &stbuf); stbuf.st_nlink is always 1, should be 3 Setting `unknown_nlink` to true in `SMB2_open()` triggers the `CIFS_FATTR_UNKNOWN_NLINK` flag in `cifs_open_info_to_fattr()`, which safely preserves the existing i_nlink in `cifs_nlink_fattr_to_inode()`. See the detailed procedure below: path_openat open_last_lookups lookup_open atomic_open cifs_atomic_open // dir->i_op->atomic_open cifs_lookup cifs_get_inode_info cifs_get_fattr smb2_query_path_info // server->ops->query_path_info smb2_compound_op SMB2_open_init case SMB2_OP_QUERY_INFO SMB2_query_info_init(FILE_ALL_INFORMATION,) cifs_open_info_to_fattr fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks) update_inode_info cifs_iget cifs_fattr_to_inode cifs_nlink_fattr_to_inode set_nlink(inode, fattr->cf_nlink) do_open vfs_open do_dentry_open cifs_open cifs_nt_open smb2_open_file // server->ops->open SMB2_open buf->unknown_nlink = true cifs_get_inode_info cifs_get_fattr cifs_open_info_to_fattr if (data->unknown_nlink) // true fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK update_inode_info cifs_fattr_to_inode cifs_nlink_fattr_to_inode if (fattr->cf_flags & CIFS_FATTR_UNKNOWN_NLINK) // true return // do not modify nlink Signed-off-by: ChenXiaoSong Signed-off-by: Steve French --- fs/smb/client/cifsglob.h | 1 + fs/smb/client/inode.c | 2 ++ fs/smb/client/smb2pdu.c | 1 + 3 files changed, 4 insertions(+) diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h index 99f9e6dca62b..08e94633a9c1 100644 --- a/fs/smb/client/cifsglob.h +++ b/fs/smb/client/cifsglob.h @@ -250,6 +250,7 @@ struct cifs_open_info_data { bool adjust_tz; bool reparse_point; bool contains_posix_file_info; + bool unknown_nlink; struct { /* ioctl response buffer */ struct { diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c index 2ed1c79c1132..16a3ddff060f 100644 --- a/fs/smb/client/inode.c +++ b/fs/smb/client/inode.c @@ -909,6 +909,8 @@ static void cifs_open_info_to_fattr(struct cifs_fattr *fattr, struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); memset(fattr, 0, sizeof(*fattr)); + if (data->unknown_nlink) + fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK; fattr->cf_cifsattrs = le32_to_cpu(info->Attributes); if (info->DeletePending) fattr->cf_flags |= CIFS_FATTR_DELETE_PENDING; diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index bc77170458b3..8f83ab377db1 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -3380,6 +3380,7 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, file_info->EndOfFile = rsp->EndofFile; file_info->Attributes = rsp->FileAttributes; file_info->NumberOfLinks = cpu_to_le32(1); + buf->unknown_nlink = true; file_info->DeletePending = 0; /* successful open = not delete pending */ } From 179e3ee9856d2b5f0ccf2f24e2334bbe76efefa9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Mon, 6 Jul 2026 20:29:36 +0200 Subject: [PATCH 0867/1054] cifs: Show reason why autodisabling serverino support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extend cifs_autodisable_serverino() function to print also text message why the function was called. The text message is printed just once for mount then autodisabling serverino support. Once the serverino support is disabled for mount it will not be re-enabled. So those text messages do not cause flooding logs. This change allows to debug issues why cifs.ko decide to turn off server inode number support and hence disable support for detection of hardlinks. Signed-off-by: Pali Rohár Signed-off-by: Steve French --- fs/smb/client/cifsproto.h | 2 +- fs/smb/client/connect.c | 2 +- fs/smb/client/dfs_cache.c | 2 +- fs/smb/client/inode.c | 7 ++++--- fs/smb/client/misc.c | 6 +++++- fs/smb/client/readdir.c | 4 ++-- 6 files changed, 14 insertions(+), 9 deletions(-) diff --git a/fs/smb/client/cifsproto.h b/fs/smb/client/cifsproto.h index c4ababcb51a3..00168839c123 100644 --- a/fs/smb/client/cifsproto.h +++ b/fs/smb/client/cifsproto.h @@ -317,7 +317,7 @@ int generate_smb311signingkey(struct cifs_ses *ses, #ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY #endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ -void cifs_autodisable_serverino(struct cifs_sb_info *cifs_sb); +void cifs_autodisable_serverino(struct cifs_sb_info *cifs_sb, const char *reason, int rc); bool couldbe_mf_symlink(const struct cifs_fattr *fattr); int check_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr, diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c index a187398fbabd..ba749ec25a59 100644 --- a/fs/smb/client/connect.c +++ b/fs/smb/client/connect.c @@ -3876,7 +3876,7 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx) * After reconnecting to a different server, unique ids won't match anymore, so we disable * serverino. This prevents dentry revalidation to think the dentry are stale (ESTALE). */ - cifs_autodisable_serverino(cifs_sb); + cifs_autodisable_serverino(cifs_sb, "DFS failover may potentially connect to a different server, inode numbers won't match anymore", 0); /* * Force the use of prefix path to support failover on DFS paths that resolve to targets * that have different prefix paths. diff --git a/fs/smb/client/dfs_cache.c b/fs/smb/client/dfs_cache.c index 44409ba44e1d..8cd93cd2f00f 100644 --- a/fs/smb/client/dfs_cache.c +++ b/fs/smb/client/dfs_cache.c @@ -1328,7 +1328,7 @@ int dfs_cache_remount_fs(struct cifs_sb_info *cifs_sb) * After reconnecting to a different server, unique ids won't match anymore, so we disable * serverino. This prevents dentry revalidation to think the dentry are stale (ESTALE). */ - cifs_autodisable_serverino(cifs_sb); + cifs_autodisable_serverino(cifs_sb, "DFS failover may potentially connect to a different server, inode numbers won't match anymore", 0); /* * Force the use of prefix path to support failover on DFS paths that resolve to targets * that have different prefix paths. diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c index 16a3ddff060f..deed04dd9b91 100644 --- a/fs/smb/client/inode.c +++ b/fs/smb/client/inode.c @@ -1147,7 +1147,7 @@ static void cifs_set_fattr_ino(int xid, struct cifs_tcon *tcon, struct super_blo fattr->cf_uniqueid = CIFS_I(*inode)->uniqueid; else { fattr->cf_uniqueid = iunique(sb, ROOT_I); - cifs_autodisable_serverino(cifs_sb); + cifs_autodisable_serverino(cifs_sb, "Cannot retrieve inode number via get_srv_inum", rc); } return; } @@ -1644,7 +1644,7 @@ cifs_iget(struct super_block *sb, struct cifs_fattr *fattr) fattr->cf_flags &= ~CIFS_FATTR_INO_COLLISION; if (inode_has_hashed_dentries(inode)) { - cifs_autodisable_serverino(CIFS_SB(sb)); + cifs_autodisable_serverino(CIFS_SB(sb), "Inode number collision detected", 0); iput(inode); fattr->cf_uniqueid = iunique(sb, ROOT_I); goto retry_iget5_locked; @@ -1710,8 +1710,9 @@ struct inode *cifs_root_iget(struct super_block *sb) iget_root: if (!rc) { if (fattr.cf_flags & CIFS_FATTR_JUNCTION) { + cifs_dbg(VFS, "Removing junction mark and disabling 'serverino' to prevent inode collisions\n"); fattr.cf_flags &= ~CIFS_FATTR_JUNCTION; - cifs_autodisable_serverino(cifs_sb); + cifs_autodisable_serverino(cifs_sb, "Cannot retrieve attributes for junction point", rc); } inode = cifs_iget(sb, &fattr); } diff --git a/fs/smb/client/misc.c b/fs/smb/client/misc.c index ee1728eec8aa..e4bac2a0b85d 100644 --- a/fs/smb/client/misc.c +++ b/fs/smb/client/misc.c @@ -278,7 +278,7 @@ dump_smb(void *buf, int smb_buf_length) } void -cifs_autodisable_serverino(struct cifs_sb_info *cifs_sb) +cifs_autodisable_serverino(struct cifs_sb_info *cifs_sb, const char *reason, int rc) { unsigned int sbflags = cifs_sb_flags(cifs_sb); @@ -290,6 +290,10 @@ cifs_autodisable_serverino(struct cifs_sb_info *cifs_sb) atomic_andnot(CIFS_MOUNT_SERVER_INUM, &cifs_sb->mnt_cifs_flags); cifs_sb->mnt_cifs_serverino_autodisabled = true; + if (rc) + cifs_dbg(VFS, "%s: %d\n", reason, rc); + else + cifs_dbg(VFS, "%s\n", reason); cifs_dbg(VFS, "Autodisabling the use of server inode numbers on %s\n", tcon ? tcon->tree_name : "new server"); cifs_dbg(VFS, "The server doesn't seem to support them properly or the files might be on different servers (DFS)\n"); diff --git a/fs/smb/client/readdir.c b/fs/smb/client/readdir.c index a50c86bbe60f..ee5996e6d7d8 100644 --- a/fs/smb/client/readdir.c +++ b/fs/smb/client/readdir.c @@ -415,7 +415,7 @@ _initiate_cifs_search(const unsigned int xid, struct file *file, if (rc == 0) { cifsFile->invalidHandle = false; } else if (rc == -EOPNOTSUPP && (sbflags & CIFS_MOUNT_SERVER_INUM)) { - cifs_autodisable_serverino(cifs_sb); + cifs_autodisable_serverino(cifs_sb, "Cannot retrieve inode number via query_dir_first", rc); goto ffirst_retry; } error_exit: @@ -1029,7 +1029,7 @@ static int cifs_filldir(char *find_entry, struct file *file, fattr.cf_uniqueid = de.ino; } else { fattr.cf_uniqueid = iunique(sb, ROOT_I); - cifs_autodisable_serverino(cifs_sb); + cifs_autodisable_serverino(cifs_sb, "Cannot retrieve inode number from readdir", 0); } if ((sbflags & CIFS_MOUNT_MF_SYMLINKS) && couldbe_mf_symlink(&fattr)) From 56bc6384314fb9ae98975fb2af8b143097ede3dc Mon Sep 17 00:00:00 2001 From: Arunpravin Paneer Selvam Date: Thu, 9 Jul 2026 18:40:50 +0530 Subject: [PATCH 0868/1054] gpu/buddy: bail out of try_harder when alignment cannot be honoured MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The try_harder contiguous fallback could return a range whose start offset did not match the caller's min_block_size. When a candidate's start is misaligned, realign it: free the misaligned run and reallocate exactly @size at the next lower min_block_size boundary. This keeps the returned size unchanged with no surplus to trim, and rejects the request only when no aligned candidate fits. v2: align misaligned candidates down to min_block_size instead of bailing out, for both the RHS and LHS paths (Matthew). Fixes: 0a1844bf0b53 ("drm/buddy: Improve contiguous memory allocation") Suggested-by: Christian König Cc: Matthew Auld Cc: Christian König Cc: Timur Kristóf Cc: stable@vger.kernel.org Reviewed-by: Matthew Auld Tested-by: John Olender Signed-off-by: Arunpravin Paneer Selvam Link: https://patch.msgid.link/20260709131050.1022759-1-Arunpravin.PaneerSelvam@amd.com --- drivers/gpu/buddy.c | 67 +++++++++++++++++++++++++++++++-------------- 1 file changed, 46 insertions(+), 21 deletions(-) diff --git a/drivers/gpu/buddy.c b/drivers/gpu/buddy.c index eb1457376307..b12d3a2ac630 100644 --- a/drivers/gpu/buddy.c +++ b/drivers/gpu/buddy.c @@ -1084,22 +1084,30 @@ static int __gpu_buddy_alloc_range(struct gpu_buddy *mm, blocks, total_allocated_on_err); } +static int __alloc_contig_aligned_retry(struct gpu_buddy *mm, + u64 unaligned_offset, + u64 size, + u64 min_block_size, + struct list_head *blocks) +{ + u64 aligned_offset = round_down(unaligned_offset, min_block_size); + + return __gpu_buddy_alloc_range(mm, aligned_offset, size, NULL, blocks); +} + static int __alloc_contig_try_harder(struct gpu_buddy *mm, u64 size, u64 min_block_size, struct list_head *blocks) { - u64 rhs_offset, lhs_offset, lhs_size, filled; + u64 rhs_offset, lhs_offset, filled; struct gpu_buddy_block *block; unsigned int tree, order; - LIST_HEAD(blocks_lhs); - unsigned long pages; u64 modify_size; int err; modify_size = rounddown_pow_of_two(size); - pages = modify_size >> ilog2(mm->chunk_size); - order = fls(pages) - 1; + order = ilog2(modify_size) - ilog2(mm->chunk_size); if (order == 0) return -ENOSPC; @@ -1115,31 +1123,48 @@ static int __alloc_contig_try_harder(struct gpu_buddy *mm, while (iter) { block = rbtree_get_free_block(iter); - /* Allocate blocks traversing RHS */ rhs_offset = gpu_buddy_block_offset(block); + + /* Allocate blocks traversing RHS */ err = __gpu_buddy_alloc_range(mm, rhs_offset, size, &filled, blocks); - if (!err || err != -ENOSPC) + if (err && err != -ENOSPC) return err; - - lhs_size = max((size - filled), min_block_size); - if (!IS_ALIGNED(lhs_size, min_block_size)) - lhs_size = round_up(lhs_size, min_block_size); - - /* Allocate blocks traversing LHS */ - lhs_offset = gpu_buddy_block_offset(block) - lhs_size; - err = __gpu_buddy_alloc_range(mm, lhs_offset, lhs_size, - NULL, &blocks_lhs); - if (!err) { - list_splice(&blocks_lhs, blocks); + if (!err && IS_ALIGNED(rhs_offset, min_block_size)) return 0; - } else if (err != -ENOSPC) { + if (!err) { + /* Allocate the unaligned RHS offset using round_down */ + gpu_buddy_free_list_internal(mm, blocks); + err = __alloc_contig_aligned_retry(mm, rhs_offset, + size, + min_block_size, + blocks); + if (!err) + return 0; + if (err != -ENOSPC) { + gpu_buddy_free_list_internal(mm, blocks); + return err; + } + goto next; + } + + if (size - filled > rhs_offset) + goto next; + + lhs_offset = rhs_offset - (size - filled); + + /* Allocate the unaligned LHS offset using round_down */ + gpu_buddy_free_list_internal(mm, blocks); + err = __alloc_contig_aligned_retry(mm, lhs_offset, size, + min_block_size, blocks); + if (!err) + return 0; + if (err != -ENOSPC) { gpu_buddy_free_list_internal(mm, blocks); return err; } - /* Free blocks for the next iteration */ +next: gpu_buddy_free_list_internal(mm, blocks); - iter = rb_prev(iter); } } From bf1deecccf210d1dd84e85cd4a45070888583984 Mon Sep 17 00:00:00 2001 From: Steve Dunnagan Date: Wed, 1 Jul 2026 15:59:20 -0400 Subject: [PATCH 0869/1054] firmware: arm_scmi: Use 64-bit division for clock rate rounding SCMI clock range descriptors report rates as 64-bit values. When handling a range clock, scmi_clock_determine_rate() rounds the requested rate up to the next supported step using the SCMI RATE_STEP value. The current code uses div64_ul() for this calculation. Since div64_ul() takes an unsigned long divisor, the 64-bit RATE_STEP value can be truncated on 32-bit builds. In the worst case, a non-zero 64-bit step can be narrowed to zero before the division. Store RATE_STEP in a u64, reject a malformed zero step, and use DIV64_U64_ROUND_UP() so the divisor is handled as a 64-bit value. This does not change behavior for valid firmware reporting a non-zero step that fits in unsigned long. Tested on Xunlong Orange Pi 5 Plus / RK3588 with SCMI over SMC. SCMI clocks probed successfully before and after the change. SCMI-backed CPU clocks were exercised through cpufreq-dt by switching each CPU policy between its lowest and highest available OPP. Fixes: ecde921eb460 ("firmware: arm_scmi: Add clock determine_rate operation") Signed-off-by: Steve Dunnagan Link: https://patch.msgid.link/20260701195923.444270-1-sdunnaga@redhat.com Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/clock.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c index 42e666a628c7..0278705d809e 100644 --- a/drivers/firmware/arm_scmi/clock.c +++ b/drivers/firmware/arm_scmi/clock.c @@ -718,7 +718,7 @@ static int scmi_clock_rate_set(const struct scmi_protocol_handle *ph, static int scmi_clock_determine_rate(const struct scmi_protocol_handle *ph, u32 clk_id, unsigned long *rate) { - u64 fmin, fmax, ftmp; + u64 fmin, fmax, ftmp, step; struct scmi_clock_info *clk; struct scmi_clock_desc *clkd; struct clock_info *ci = ph->get_priv(ph); @@ -749,11 +749,14 @@ static int scmi_clock_determine_rate(const struct scmi_protocol_handle *ph, return 0; } - ftmp = *rate - fmin; - ftmp += clkd->r.rates[RATE_STEP] - 1; /* to round up */ - ftmp = div64_ul(ftmp, clkd->r.rates[RATE_STEP]); + step = clkd->r.rates[RATE_STEP]; + if (!step) + return -EINVAL; - *rate = ftmp * clkd->r.rates[RATE_STEP] + fmin; + ftmp = *rate - fmin; + ftmp = DIV64_U64_ROUND_UP(ftmp, step); + + *rate = ftmp * step + fmin; return 0; } From a4447c0693830d5ecadd6e755cb7fdc55d86aacc Mon Sep 17 00:00:00 2001 From: Pushpendra Singh Date: Wed, 8 Jul 2026 12:53:39 +0530 Subject: [PATCH 0870/1054] firmware: arm_scmi: Rate-limit queue-full warnings in IRQ context The scmi_notify() function is called from interrupt context to queue received notification events onto a per-protocol kfifo. When the kfifo is full, it logs a warning via dev_warn() for every dropped event. Under conditions where the platform sends a burst of SCMI notifications faster than the deferred worker can drain the queue, this results in a flood of dev_warn() calls from IRQ context. Each call acquires the console lock and may execute blocking console writes, causing the CPU to be held in interrupt context for an extended period and leading to observable system stalls. Fix this by switching to dev_warn_ratelimited() to limit the frequency of log messages when the notification queue is full. This reduces console overhead in interrupt context and prevents CPU stalls caused by excessive logging, while still preserving diagnostic visibility. Fixes: bd31b249692e ("firmware: arm_scmi: Add notification dispatch and delivery") Signed-off-by: Pushpendra Singh Link: https://patch.msgid.link/20260708072339.3021140-1-pushpendra.singh@oss.qualcomm.com Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/notify.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/firmware/arm_scmi/notify.c b/drivers/firmware/arm_scmi/notify.c index 40ec184eedae..0a192cf2deab 100644 --- a/drivers/firmware/arm_scmi/notify.c +++ b/drivers/firmware/arm_scmi/notify.c @@ -600,9 +600,9 @@ int scmi_notify(const struct scmi_handle *handle, u8 proto_id, u8 evt_id, return -EINVAL; } if (kfifo_avail(&r_evt->proto->equeue.kfifo) < sizeof(eh) + len) { - dev_warn(handle->dev, - "queue full, dropping proto_id:%d evt_id:%d ts:%lld\n", - proto_id, evt_id, ktime_to_ns(ts)); + dev_warn_ratelimited(handle->dev, + "queue full, dropping proto_id:%d evt_id:%d ts:%lld\n", + proto_id, evt_id, ktime_to_ns(ts)); return -ENOMEM; } From c804aadc4ce3dcb34d6f2ecc9a159c148210805a Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Fri, 3 Jul 2026 17:23:38 +0100 Subject: [PATCH 0871/1054] drm/imagination: make pvr_fw_trace_init_mask_ops static The pvr_fw_trace_init_mask_ops is not used outside pvr_fw_trace.c so make it static to avoid the following sparse warning: drivers/gpu/drm/imagination/pvr_fw_trace.c:74:31: warning: symbol 'pvr_fw_trace_init_mask_ops' was not declared. Should it be static? Fixes: c6978643ea1c ("drm/imagination: Validate fw trace group_mask") Reviewed-by: Alessio Belle Signed-off-by: Ben Dooks Link: https://patch.msgid.link/20260703162338.2848039-1-ben.dooks@codethink.co.uk Signed-off-by: Alessio Belle --- drivers/gpu/drm/imagination/pvr_fw_trace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/imagination/pvr_fw_trace.c b/drivers/gpu/drm/imagination/pvr_fw_trace.c index 6bb5baa6c41b..805d9f9bc1dd 100644 --- a/drivers/gpu/drm/imagination/pvr_fw_trace.c +++ b/drivers/gpu/drm/imagination/pvr_fw_trace.c @@ -71,7 +71,7 @@ pvr_fw_trace_init_mask_set(const char *val, const struct kernel_param *kp) return 0; } -const struct kernel_param_ops pvr_fw_trace_init_mask_ops = { +static const struct kernel_param_ops pvr_fw_trace_init_mask_ops = { .set = pvr_fw_trace_init_mask_set, .get = param_get_hexint, }; From cf385cf6e713eba0720651174dac0b2d2f5bb8f8 Mon Sep 17 00:00:00 2001 From: Luigi Santivetti Date: Tue, 7 Jul 2026 16:17:16 +0100 Subject: [PATCH 0872/1054] drm/imagination: fix error checking of pvr_vm_context_lookup() Since pvr_vm_context_lookup() returns either NULL or a pointer, then stop using IS_ERR() for checking the return value. Using IS_ERR() leads to the kernel oops reported below. It can be reproduced by passing an invalid VM context handle from userspace to the DRM_IOCTL_PVR_CREATE_CONTEXT ioctl. [ 92.733119] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000148 [ 92.742042] Mem abort info: [ 92.744890] ESR = 0x0000000096000004 [ 92.748686] EC = 0x25: DABT (current EL), IL = 32 bits [ 92.754020] SET = 0, FnV = 0 [ 92.757154] EA = 0, S1PTW = 0 [ 92.760337] FSC = 0x04: level 0 translation fault [ 92.765243] Data abort info: [ 92.768129] ISV = 0, ISS = 0x00000004, ISS2 = 0x00000000 [ 92.773626] CM = 0, WnR = 0, TnD = 0, TagAccess = 0 [ 92.778763] GCS = 0, Overlay = 0, DirtyBit = 0, Xs = 0 [ 92.784098] user pgtable: 4k pages, 48-bit VAs, pgdp=000000088ed23000 [ 92.790550] [0000000000000148] pgd=0000000000000000, p4d=0000000000000000 [ 92.797381] Internal error: Oops: 0000000096000004 [#1] SMP [ 92.803027] Modules linked in: powervr [ 92.852533] CPU: 0 UID: 0 PID: 409 Comm: triangle Not tainted 7.1.0-rc5-g98b46e693b91 #1 PREEMPT [ 92.861385] Hardware name: Texas Instruments AM68 SK (DT) [ 92.866766] pstate: 60000005 (nZCv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--) [ 92.873709] pc : pvr_vm_get_fw_mem_context+0x0/0xc [powervr] [ 92.879376] lr : pvr_queue_create+0x26c/0x440 [powervr] [ 92.884595] sp : ffff8000837fbb00 [ 92.887895] x29: ffff8000837fbb60 x28: 0000000000000000 x27: ffff8000837fbce8 [ 92.895015] x26: ffff000807f61a40 x25: ffff000807f61a00 x24: ffff000807f64400 [ 92.902135] x23: ffff00080a5ab000 x22: ffff800079b24730 x21: ffff000807f61800 [ 92.909254] x20: ffff00080999e680 x19: 0000000000000000 x18: 0000000000000000 [ 92.916373] x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000001 [ 92.923492] x14: 0000000000000000 x13: 0000000000000002 x12: ffff80008145b298 [ 92.930611] x11: ffff8000844e5000 x10: ffff80008165a130 x9 : 0000000000000100 [ 92.937730] x8 : 0000000000000001 x7 : ffff0008076b27e0 x6 : ffff00080ec43b7c [ 92.944850] x5 : ffff00080ec43b78 x4 : 0000000000000000 x3 : ffff00080999e680 [ 92.951968] x2 : 0000000000000000 x1 : 0000000000000000 x0 : 0000000000000000 [ 92.959088] Call trace: [ 92.961521] pvr_vm_get_fw_mem_context+0x0/0xc [powervr] (P) [ 92.967173] pvr_context_create+0x190/0x410 [powervr] [ 92.972218] pvr_ioctl_create_context+0x44/0x8c [powervr] [ 92.977608] drm_ioctl_kernel+0xbc/0x124 [drm] [ 92.982127] drm_ioctl+0x1f8/0x4dc [drm] [ 92.986098] __arm64_sys_ioctl+0xac/0x104 [ 92.990102] invoke_syscall+0x54/0x10c [ 92.993842] el0_svc_common.constprop.0+0x40/0xe0 [ 92.998532] do_el0_svc+0x1c/0x28 [ 93.001835] el0_svc+0x38/0x11c [ 93.004969] el0t_64_sync_handler+0xa0/0xe4 [ 93.009139] el0t_64_sync+0x198/0x19c [ 93.012792] Code: aa1703e0 d2800014 95cb0ba4 17ffffe8 (f940a400) [ 93.018869] ---[ end trace 0000000000000000 ]--- Fixes: d2d79d29bb98 ("drm/imagination: Implement context creation/destruction ioctls") Cc: stable@vger.kernel.org Signed-off-by: Luigi Santivetti Reviewed-by: Alessio Belle Link: https://patch.msgid.link/20260707-staging-ddkopsrc-2435-v1-1-24e160d44476@imgtec.com Signed-off-by: Alessio Belle --- drivers/gpu/drm/imagination/pvr_context.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/imagination/pvr_context.c b/drivers/gpu/drm/imagination/pvr_context.c index 52e16c1e7af0..406e0758e860 100644 --- a/drivers/gpu/drm/imagination/pvr_context.c +++ b/drivers/gpu/drm/imagination/pvr_context.c @@ -309,8 +309,8 @@ int pvr_context_create(struct pvr_file *pvr_file, struct drm_pvr_ioctl_create_co goto err_free_ctx; ctx->vm_ctx = pvr_vm_context_lookup(pvr_file, args->vm_context_handle); - if (IS_ERR(ctx->vm_ctx)) { - err = PTR_ERR(ctx->vm_ctx); + if (!ctx->vm_ctx) { + err = -EINVAL; goto err_free_ctx; } From 3e3aa6da87d30a0064a17b836685cd43c90a3572 Mon Sep 17 00:00:00 2001 From: Matthew Rosato Date: Thu, 9 Jul 2026 09:54:04 -0400 Subject: [PATCH 0873/1054] KVM: s390: pci: Fix handling of AIF enable without AISB When a guest seeks to register IRQs without a summary bit specified, ensure that the associated GAITE then stores 0 for the guest AISB location instead of virt_to_phys(page_address(NULL)). Fixes: 3c5a1b6f0a18 ("KVM: s390: pci: provide routines for enabling/disabling interrupt forwarding") Cc: stable@vger.kernel.org Reviewed-by: Farhan Ali Signed-off-by: Matthew Rosato Signed-off-by: Christian Borntraeger --- arch/s390/kvm/pci.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/arch/s390/kvm/pci.c b/arch/s390/kvm/pci.c index 686113be0530..720bb58cabe2 100644 --- a/arch/s390/kvm/pci.c +++ b/arch/s390/kvm/pci.c @@ -300,9 +300,14 @@ static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib, gaite->gisc = fib->fmt0.isc; gaite->count++; - gaite->aisbo = fib->fmt0.aisbo; - gaite->aisb = virt_to_phys(page_address(aisb_page) + (fib->fmt0.aisb & - ~PAGE_MASK)); + if (fib->fmt0.sum == 1) { + gaite->aisbo = fib->fmt0.aisbo; + gaite->aisb = virt_to_phys(page_address(aisb_page) + + (fib->fmt0.aisb & ~PAGE_MASK)); + } else { + gaite->aisbo = 0; + gaite->aisb = 0; + } aift->kzdev[zdev->aisb] = zdev->kzdev; spin_unlock_irq(&aift->gait_lock); From 8008d6b59a659982b9f10916bb8712bf93bc7c46 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 8 Jul 2026 22:26:51 -0400 Subject: [PATCH 0874/1054] lib/crypto: docs: Fix some sentence fragments Currently, the section about the library API for each algorithm begins with a noun phrase that was intended to serve as an elaboration on the title. It's better to use complete sentences. Suggested-by: Thomas Huth Reviewed-by: Thomas Huth Link: https://patch.msgid.link/20260709022651.44216-1-ebiggers@kernel.org Signed-off-by: Eric Biggers --- .../crypto/libcrypto-blockcipher.rst | 6 +-- Documentation/crypto/libcrypto-hash.rst | 38 ++++++++++--------- Documentation/crypto/libcrypto-signature.rst | 2 +- 3 files changed, 24 insertions(+), 22 deletions(-) diff --git a/Documentation/crypto/libcrypto-blockcipher.rst b/Documentation/crypto/libcrypto-blockcipher.rst index dd5ce2f8b515..fd85e27fab8d 100644 --- a/Documentation/crypto/libcrypto-blockcipher.rst +++ b/Documentation/crypto/libcrypto-blockcipher.rst @@ -6,14 +6,14 @@ Block ciphers AES --- -Support for the AES block cipher. +This API provides support for the AES block cipher. .. kernel-doc:: include/crypto/aes.h DES --- -Support for the DES block cipher. This algorithm is obsolete and is supported -only for backwards compatibility. +This API provides support for the DES block cipher. This algorithm is obsolete +and is supported only for backwards compatibility. .. kernel-doc:: include/crypto/des.h diff --git a/Documentation/crypto/libcrypto-hash.rst b/Documentation/crypto/libcrypto-hash.rst index 4248e6fdc952..fa4c54236af6 100644 --- a/Documentation/crypto/libcrypto-hash.rst +++ b/Documentation/crypto/libcrypto-hash.rst @@ -6,81 +6,83 @@ Hash functions, MACs, and XOFs AES-CMAC and AES-XCBC-MAC ------------------------- -Support for the AES-CMAC and AES-XCBC-MAC message authentication codes. +This API provides support for the AES-CMAC and AES-XCBC-MAC message +authentication codes. .. kernel-doc:: include/crypto/aes-cbc-macs.h BLAKE2b ------- -Support for the BLAKE2b cryptographic hash function. +This API provides support for the BLAKE2b cryptographic hash function. .. kernel-doc:: include/crypto/blake2b.h BLAKE2s ------- -Support for the BLAKE2s cryptographic hash function. +This API provides support for the BLAKE2s cryptographic hash function. .. kernel-doc:: include/crypto/blake2s.h GHASH and POLYVAL ----------------- -Support for the GHASH and POLYVAL universal hash functions. These algorithms -are used only as internal components of other algorithms. +This API provides support for the GHASH and POLYVAL universal hash functions. +These algorithms are used only as internal components of other algorithms. .. kernel-doc:: include/crypto/gf128hash.h MD5 --- -Support for the MD5 cryptographic hash function and HMAC-MD5. This algorithm is -obsolete and is supported only for backwards compatibility. +This API provides support for the MD5 cryptographic hash function and HMAC-MD5. +This algorithm is obsolete and is supported only for backwards compatibility. .. kernel-doc:: include/crypto/md5.h NH -- -Support for the NH universal hash function. This algorithm is used only as an -internal component of other algorithms. +This API provides support for the NH universal hash function. This algorithm is +used only as an internal component of other algorithms. .. kernel-doc:: include/crypto/nh.h Poly1305 -------- -Support for the Poly1305 universal hash function. This algorithm is used only -as an internal component of other algorithms. +This API provides support for the Poly1305 universal hash function. This +algorithm is used only as an internal component of other algorithms. .. kernel-doc:: include/crypto/poly1305.h SHA-1 ----- -Support for the SHA-1 cryptographic hash function and HMAC-SHA1. This algorithm -is obsolete and is supported only for backwards compatibility. +This API provides support for the SHA-1 cryptographic hash function and +HMAC-SHA1. This algorithm is obsolete and is supported only for backwards +compatibility. .. kernel-doc:: include/crypto/sha1.h SHA-2 ----- -Support for the SHA-2 family of cryptographic hash functions, including SHA-224, -SHA-256, SHA-384, and SHA-512. This also includes their corresponding HMACs: -HMAC-SHA224, HMAC-SHA256, HMAC-SHA384, and HMAC-SHA512. +This API provides support for the SHA-2 family of cryptographic hash functions, +including SHA-224, SHA-256, SHA-384, and SHA-512. This also includes their +corresponding HMACs: HMAC-SHA224, HMAC-SHA256, HMAC-SHA384, and HMAC-SHA512. .. kernel-doc:: include/crypto/sha2.h SHA-3 ----- -The SHA-3 functions are documented in :ref:`sha3`. +The SHA-3 API is documented in :ref:`sha3`. SM3 --- -Support for the SM3 cryptographic hash function. +This API provides support for the SM3 cryptographic hash function. .. kernel-doc:: include/crypto/sm3.h diff --git a/Documentation/crypto/libcrypto-signature.rst b/Documentation/crypto/libcrypto-signature.rst index e80d59fa51b6..2a6dc793f0de 100644 --- a/Documentation/crypto/libcrypto-signature.rst +++ b/Documentation/crypto/libcrypto-signature.rst @@ -6,6 +6,6 @@ Digital signature algorithms ML-DSA ------ -Support for the ML-DSA digital signature algorithm. +This API provides support for the ML-DSA digital signature algorithm. .. kernel-doc:: include/crypto/mldsa.h From 9665e22579ee4324a14e035b5b29d3d1fdc929e2 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 8 Jul 2026 22:27:47 -0400 Subject: [PATCH 0875/1054] lib/crypto: docs: Improve introduction sentence Make it clear that lib/crypto/ is a kernel-internal library. It's easy for people to come across this page, especially the HTML version online, without that context. Reviewed-by: Thomas Huth Link: https://patch.msgid.link/20260709022747.44635-1-ebiggers@kernel.org Signed-off-by: Eric Biggers --- Documentation/crypto/libcrypto.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Documentation/crypto/libcrypto.rst b/Documentation/crypto/libcrypto.rst index a1557d45b0e5..0733e603d229 100644 --- a/Documentation/crypto/libcrypto.rst +++ b/Documentation/crypto/libcrypto.rst @@ -4,8 +4,9 @@ Crypto library ============== -``lib/crypto/`` provides faster and easier access to cryptographic algorithms -than the traditional crypto API. +The Linux kernel's crypto library (``lib/crypto/``) provides kernel-internal +users of cryptographic algorithms with faster and easier access to those +algorithms than the traditional kernel crypto API. Each cryptographic algorithm is supported via a set of dedicated functions. "Crypto agility", where needed, is left to calling code. From e073f1238ecaea366f53e98724c40b31856da56a Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 8 Jul 2026 22:29:54 -0400 Subject: [PATCH 0876/1054] crypto: aes - Fix conditions for selecting MAC dependencies Starting in commit 7137cbf2b5c9 ("crypto: aes - Add cmac, xcbc, and cbcmac algorithms using library"), the aes module (CRYPTO_AES) supports CBC based MACs using the corresponding library functions. To avoid including unneeded functionality, that support honors the existing CRYPTO_CMAC, CRYPTO_XCBC, and CRYPTO_CCM kconfig options. The dependencies are selected if at least one of those is enabled. However, the select statements don't correctly handle the case where CRYPTO_AES=y and (for example) CRYPTO_CMAC=m. In that case the dependencies get selected at level 'm', due to how the kconfig language works. That causes a linker error. Fix this by changing the selection conditions to use '!= n'. A similar issue also exists for CRYPTO_LIB_AES's conditional selection of CRYPTO_LIB_UTILS. The same '!= n' would work, but instead just make CRYPTO_LIB_AES always select CRYPTO_LIB_UTILS. CRYPTO_LIB_UTILS is lightweight, and it's needed by most AES modes and many other things. Fixes: 7137cbf2b5c9 ("crypto: aes - Add cmac, xcbc, and cbcmac algorithms using library") Fixes: 309a7e514da7 ("lib/crypto: aes: Add support for CBC-based MACs") Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260709022954.45113-1-ebiggers@kernel.org Signed-off-by: Eric Biggers --- crypto/Kconfig | 4 ++-- lib/crypto/Kconfig | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/crypto/Kconfig b/crypto/Kconfig index f1e372195273..b61401bd3ef6 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig @@ -358,8 +358,8 @@ config CRYPTO_AES tristate "AES (Advanced Encryption Standard)" select CRYPTO_ALGAPI select CRYPTO_LIB_AES - select CRYPTO_LIB_AES_CBC_MACS if CRYPTO_CMAC || CRYPTO_XCBC || CRYPTO_CCM - select CRYPTO_HASH if CRYPTO_CMAC || CRYPTO_XCBC || CRYPTO_CCM + select CRYPTO_LIB_AES_CBC_MACS if CRYPTO_CMAC != n || CRYPTO_XCBC != n || CRYPTO_CCM != n + select CRYPTO_HASH if CRYPTO_CMAC != n || CRYPTO_XCBC != n || CRYPTO_CCM != n help AES cipher algorithms (Rijndael)(FIPS-197, ISO/IEC 18033-3) diff --git a/lib/crypto/Kconfig b/lib/crypto/Kconfig index 591c1c2a7fb3..83d4c95e079e 100644 --- a/lib/crypto/Kconfig +++ b/lib/crypto/Kconfig @@ -8,8 +8,7 @@ config CRYPTO_LIB_UTILS config CRYPTO_LIB_AES tristate - # Select dependencies of modes that are part of libaes. - select CRYPTO_LIB_UTILS if CRYPTO_LIB_AES_CBC_MACS + select CRYPTO_LIB_UTILS config CRYPTO_LIB_AES_ARCH bool From f86a7c96406d406a66f3d7653eca86aa576fcf6b Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Thu, 9 Jul 2026 13:12:24 +0100 Subject: [PATCH 0877/1054] ALSA: hda/cirrus_scodec: Make Kconfig visible if KUNIT Make the Kconfig item for cirrus_scodec visible if CONFIG_KUNIT is enabled. This is so that its KUnit test can be enabled by KUnit scripts without requiring a large amount of irrelevant additional components. The general rule for KUNIT_ALL_TESTS is that it should only enable tests for components that are already selected. However, the UML environment does not support ACPI, which means the HDA codec drivers that use cirrus_scodec cannot be selected. But cirrus_scodec does not need ACPI. By making the Kconfig option visible if CONFIG_KUNIT, the KUnit test can be enabled with only the minimal set of functionality that is required for cirrus_scodec. This is still compliant with the KUNIT_ALL_TESTS rule "only tests for enabled modules" because by default cirrus_scodec will only be enabled if the drivers that use it are enabled. It must be intentionally enabled to force it to be included for testing. Signed-off-by: Richard Fitzgerald Link: https://patch.msgid.link/20260709121224.614350-1-rf@opensource.cirrus.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/side-codecs/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/hda/codecs/side-codecs/Kconfig b/sound/hda/codecs/side-codecs/Kconfig index 2a2e8804bf9e..1cfd83e251e4 100644 --- a/sound/hda/codecs/side-codecs/Kconfig +++ b/sound/hda/codecs/side-codecs/Kconfig @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only config SND_HDA_CIRRUS_SCODEC - tristate + tristate "Cirrus side-codec library" if KUNIT config SND_HDA_CIRRUS_SCODEC_KUNIT_TEST tristate "KUnit test for Cirrus side-codec library" if !KUNIT_ALL_TESTS From 8ca0016a9b799d8366b32da7e729fcf56627516f Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Thu, 9 Jul 2026 13:22:11 +0100 Subject: [PATCH 0878/1054] ALSA: hda: MAINTAINERS: Fix missing cirrus* file reference When the HDA source was reorganized some of the cirrus* files were moved into a new 'side-codecs' subdirectory. But MAINTAINERS wasn't updated to add a cirrus* file reference to cover these moved files. Signed-off-by: Richard Fitzgerald Link: https://patch.msgid.link/20260709122211.615785-1-rf@opensource.cirrus.com Signed-off-by: Takashi Iwai --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 4a8b0fd665ce..bf59602eec39 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6209,6 +6209,7 @@ F: include/dt-bindings/sound/cs* F: include/linux/mfd/cs42l43* F: include/sound/cs* F: sound/hda/codecs/cirrus* +F: sound/hda/codecs/side-codecs/cirrus* F: sound/hda/codecs/side-codecs/cs* F: sound/hda/codecs/side-codecs/hda_component* F: sound/soc/codecs/cs* From 7cbd0c4cebe4c9f678d15e6b9ba975e1155a107f Mon Sep 17 00:00:00 2001 From: Matt Bobrowski Date: Thu, 9 Jul 2026 02:53:16 +0000 Subject: [PATCH 0879/1054] bpf: Fix UAF in sock clone early bailouts Similar to recent commit 9b51a6155d14 ("bpf,fork: wipe ->bpf_storage before bailouts that access it"), sk_clone() performs an initial shallow copy of the socket field ->sk_bpf_storage via sock_copy() for the cloned socket newsk. If sk_clone() bails out early (e.g. if sk_filter_charge() fails) prior to calling bpf_sk_storage_clone(), newsk->sk_bpf_storage still points to the parent socket's BPF local storage. When newsk is subsequently freed via sk_free(), the deallocation path (__sk_destruct() -> bpf_sk_storage_free()) destroys the parent socket's BPF local storage, leading to a use-after-free (UAF) on the parent socket. Fix this by resetting newsk->sk_bpf_storage to NULL immediately after sock_copy() in sk_clone(), and remove the now redundant initialization from bpf_sk_storage_clone(). Fixes: 6ac99e8f23d4 ("bpf: Introduce bpf sk local storage") Fixes: f12dd75959b0 ("bpf: net: Set sk_bpf_storage back to NULL for cloned sk") Signed-off-by: Matt Bobrowski Signed-off-by: Daniel Borkmann Reviewed-by: Kuniyuki Iwashima Acked-by: Daniel Borkmann Link: https://lore.kernel.org/bpf/20260709025316.999913-1-mattbobrowski@google.com --- net/core/bpf_sk_storage.c | 2 -- net/core/sock.c | 3 +++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/net/core/bpf_sk_storage.c b/net/core/bpf_sk_storage.c index ecd659f79fd4..1d295a8769fa 100644 --- a/net/core/bpf_sk_storage.c +++ b/net/core/bpf_sk_storage.c @@ -158,8 +158,6 @@ int bpf_sk_storage_clone(const struct sock *sk, struct sock *newsk) struct bpf_local_storage_elem *selem; int ret = 0; - RCU_INIT_POINTER(newsk->sk_bpf_storage, NULL); - rcu_read_lock_dont_migrate(); sk_storage = rcu_dereference(sk->sk_bpf_storage); diff --git a/net/core/sock.c b/net/core/sock.c index 8a59bfaa8096..498a57f34f5b 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -2492,6 +2492,9 @@ struct sock *sk_clone(const struct sock *sk, const gfp_t priority, sock_copy(newsk, sk); newsk->sk_prot_creator = prot; +#ifdef CONFIG_BPF_SYSCALL + RCU_INIT_POINTER(newsk->sk_bpf_storage, NULL); +#endif /* SANITY */ if (likely(newsk->sk_net_refcnt)) { From e3d9c7160d483fc8f9e225aafad8ecbbc43f3151 Mon Sep 17 00:00:00 2001 From: Norbert Manthey Date: Thu, 9 Jul 2026 15:54:39 +0000 Subject: [PATCH 0880/1054] smb: client: mask server-provided mode to 07777 in modefromsid When modefromsid is active, parse_dacl() applies the server-provided sub_auth[2] value from the NFS mode SID to cf_mode without masking to 07777. Apply the correct masking, same as in the read path. Fixes: e2f8fbfb8d09c ("cifs: get mode bits from special sid on stat") Signed-off-by: Norbert Manthey Assisted-by: Kiro:claude-opus-4.6 Cc: stable@vger.kernel.org Signed-off-by: Steve French --- fs/smb/client/cifsacl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/smb/client/cifsacl.c b/fs/smb/client/cifsacl.c index 07cf0e578233..9424281a7674 100644 --- a/fs/smb/client/cifsacl.c +++ b/fs/smb/client/cifsacl.c @@ -962,7 +962,7 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl, */ fattr->cf_mode &= ~07777; fattr->cf_mode |= - le32_to_cpu(ppace[i]->sid.sub_auth[2]); + le32_to_cpu(ppace[i]->sid.sub_auth[2]) & 07777; break; } else { if (compare_sids(&(ppace[i]->sid), pownersid) == 0) { From 648790e0952789527ec68548edbedbc0fcff43b5 Mon Sep 17 00:00:00 2001 From: Woraphat Khiaodaeng Date: Thu, 9 Jul 2026 10:51:00 +0700 Subject: [PATCH 0881/1054] io_uring: restore RCU read section in io_req_local_work_add() The task-work refactor that moved io_req_local_work_add() out of io_uring.c into the new io_uring/tw.c dropped the whole-body guard(rcu)() that used to cover the function body. For DEFER_TASKRUN rings the ring teardown still relies on that RCU read section pairing with its grace period: /* pairs with RCU read section in io_req_local_work_add() */ if (ctx->flags & IORING_SETUP_DEFER_TASKRUN) synchronize_rcu(); io_ring_ctx_free(ctx); io_req_local_work_add() keeps dereferencing ctx after mpscq_push() has published the request to the work list (ctx->cq_wait_nr, and ctx->submitter_task in the final wake_up_state()), without holding a ctx reference across that window. The RCU read section was the only thing guaranteeing an in-flight adder had finished touching ctx before io_ring_ctx_free() ran; synchronize_rcu() only waits for readers that are actually inside an RCU read-side critical section. With the guard gone the grace period no longer pairs with anything on the add side, so ctx can be freed and reused while io_req_local_work_add() is still using it. Fixes: d46ab2c98aba ("io_uring: switch local task_work to a mpscq") Signed-off-by: Woraphat Khiaodaeng Link: https://patch.msgid.link/20260709035100.2269-1-worapat.kd2@gmail.com Signed-off-by: Jens Axboe --- io_uring/tw.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/io_uring/tw.c b/io_uring/tw.c index a4c872870d81..4f7a0d1079a4 100644 --- a/io_uring/tw.c +++ b/io_uring/tw.c @@ -153,6 +153,9 @@ void io_req_local_work_add(struct io_kiocb *req, unsigned flags) struct io_ring_ctx *ctx = req->ctx; int nr_wait; + /* pairs with synchronize_rcu() in io_ring_exit_work() */ + guard(rcu)(); + /* * We don't know how many requests there are in the link and whether * they can even be queued lazily, fall back to non-lazy. From f3176c8ac4217c88fe1147ab084c47092921ffc4 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Thu, 9 Jul 2026 11:43:07 -0600 Subject: [PATCH 0882/1054] Revert "io_uring: grab RCU read lock marking task run" This reverts commit ed64f5c546b3d5e3a4840f6c055448ce90edf56c. Since commit: 648790e09527 ("io_uring: restore RCU read section in io_req_local_work_add()") io_ctx_mark_taskrun() is only ever called with the RCU read lock already held, like previously. Hence's there's no need for this commit anymore, which grabbed the RCU read lock inside io_ctx_mark_taskrun(). Signed-off-by: Jens Axboe --- io_uring/tw.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/io_uring/tw.c b/io_uring/tw.c index 4f7a0d1079a4..e6ee15571e85 100644 --- a/io_uring/tw.c +++ b/io_uring/tw.c @@ -139,11 +139,11 @@ void tctx_task_work(struct callback_head *cb) */ static void io_ctx_mark_taskrun(struct io_ring_ctx *ctx) { - if (ctx->flags & IORING_SETUP_TASKRUN_FLAG) { - struct io_rings *rings; + lockdep_assert_in_rcu_read_lock(); + + if (ctx->flags & IORING_SETUP_TASKRUN_FLAG) { + struct io_rings *rings = rcu_dereference(ctx->rings_rcu); - guard(rcu)(); - rings = rcu_dereference(ctx->rings_rcu); atomic_or(IORING_SQ_TASKRUN, &rings->sq_flags); } } From 27d80e0f8b8dff97503fc0061754b1d3800cb961 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 7 Jul 2026 19:19:55 +0200 Subject: [PATCH 0883/1054] cpufreq: intel_pstate: Set non-turbo capacity to HWP_GUARANTEED_PERF() Setting cpu->capacity_perf to cpu->pstate.max_pstate_physical in the "no turbo" case is inconsistent with what happens elsewhere in the driver and causes arch_scale_cpu_capacity() to be incorrect. It also skews arch_scale_freq_capacity() which ends up differing from 1024 for the guaranteed P-state. Address that by setting capacity_perf to HWP_GUARANTEED_PERF() in the "no turbo" case. Fixes: 929ebc93ccaa ("cpufreq: intel_pstate: Set asymmetric CPU capacity on hybrid systems") Signed-off-by: Rafael J. Wysocki Tested-by: Ricardo Neri Cc: All applicable Link: https://patch.msgid.link/12928972.O9o76ZdvQC@rafael.j.wysocki --- drivers/cpufreq/intel_pstate.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 5a0eeb84d382..6e984c114d96 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -1058,12 +1058,14 @@ static void hybrid_clear_cpu_capacity(unsigned int cpunum) static void hybrid_get_capacity_perf(struct cpudata *cpu) { + u64 hwp_cap = READ_ONCE(cpu->hwp_cap_cached); + if (READ_ONCE(global.no_turbo)) { - cpu->capacity_perf = cpu->pstate.max_pstate_physical; + cpu->capacity_perf = HWP_GUARANTEED_PERF(hwp_cap); return; } - cpu->capacity_perf = HWP_HIGHEST_PERF(READ_ONCE(cpu->hwp_cap_cached)); + cpu->capacity_perf = HWP_HIGHEST_PERF(hwp_cap); } static void hybrid_set_capacity_of_cpus(void) From d2d5c129d07ea8eb91cd8a8633b5774116c4d171 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 7 Jul 2026 19:25:39 +0200 Subject: [PATCH 0884/1054] cpufreq: Make cpufreq_update_pressure() fall back to cpuinfo.max_freq If arch_scale_freq_ref() is not defined for a given arch (like x86, for example), cpufreq_update_pressure() will always set cpufreq_pressure to zero for all CPUs in the system, which is generally problematic on systems with asymmetric capacity [1]. However, in the absence of arch_scale_freq_ref(), it is reasonable to assume that cpuinfo.max_freq is the maximum sustainable frequency for the given cpufreq policy. Moreover, there are cases in which arch_scale_freq_ref() would need to be defined to return essentially the cpuinfo.max_freq value anyway (for example, intel_pstate on hybrid platforms). For the above reasons, update cpufreq_update_pressure() to fall back to using cpuinfo.max_freq as the reference frequency if zero is returned by arch_scale_freq_ref(). Fixes: 75d659317bb1 ("cpufreq: Add a cpufreq pressure feedback for the scheduler") Link: https://lore.kernel.org/lkml/CAKfTPtBuRLfYNnR4w--cFZYZy-R8gaPEgVwCcaMmbCcJ2H-muQ@mail.gmail.com/ [1] Signed-off-by: Rafael J. Wysocki Acked-by: Viresh Kumar Reviewed-by: Zhongqiu Han Tested-by: Ricardo Neri # cluster scheduling Acked-by: Vincent Guittot Link: https://patch.msgid.link/5086499.GXAFRqVoOG@rafael.j.wysocki --- drivers/cpufreq/cpufreq.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 507224c9ecd3..b898b6544069 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -2586,6 +2586,9 @@ static void cpufreq_update_pressure(struct cpufreq_policy *policy) cpu = cpumask_first(policy->related_cpus); max_freq = arch_scale_freq_ref(cpu); + if (!max_freq) + max_freq = policy->cpuinfo.max_freq; + capped_freq = policy->max; /* From db4e9defd2e8620abee04cfe5809c0bcd6ecf06a Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 9 Jul 2026 11:08:13 -1000 Subject: [PATCH 0885/1054] sched_ext: Record an error on errno-only sub-enable failure scx_sub_enable_workfn() has several failure paths that only return an errno (e.g. -ENOMEM from an allocation) and jump to err_disable without calling scx_error(). scx_flush_disable_work() runs the disable, and thus ops.exit(), only when an error has been recorded, so an errno-only failure leaves the half-initialized sub-scheduler linked. Record an error at the err_disable sink so every errno-only failure runs the disable path. Fixes: ebeca1f930ea ("sched_ext: Introduce cgroup sub-sched support") Signed-off-by: Tejun Heo Reviewed-by: Andrea Righi --- kernel/sched/ext/ext.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/kernel/sched/ext/ext.c b/kernel/sched/ext/ext.c index 2dc6977d7984..40cef77394ac 100644 --- a/kernel/sched/ext/ext.c +++ b/kernel/sched/ext/ext.c @@ -7746,6 +7746,12 @@ static void scx_sub_enable_workfn(struct kthread_work *work) percpu_up_write(&scx_fork_rwsem); err_disable: mutex_unlock(&scx_enable_mutex); + /* + * Some enable failures only return an errno (e.g. -ENOMEM from an + * allocation) without calling scx_error(). Record it so + * scx_flush_disable_work() runs the disable and ops.exit() fires. + */ + scx_error(sch, "scx_sub_enable() failed (%d)", ret); scx_flush_disable_work(sch); cmd->ret = 0; } From 49b3378a750cf85112e656d003145d4b5d0da232 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 9 Jul 2026 11:08:22 -1000 Subject: [PATCH 0886/1054] sched_ext: Fix premature ops->priv publication in scx_alloc_and_add_sched() scx_alloc_and_add_sched() publishes @sch through ops->priv before allocating the cgroup path. If that allocation fails, the unwind path clears ops->priv and frees @sch immediately. scx_prog_sched() callers can dereference ops->priv from RCU context the moment it is set, so freeing without a grace period can use-after-free a concurrent kfunc caller. Move the publication below the cgroup path allocation so that every failure path after publication frees @sch through kobject_put(), whose release path defers the freeing by a grace period. Fixes: 105dcd005be2 ("sched_ext: Introduce scx_prog_sched()") Signed-off-by: Tejun Heo Reviewed-by: Andrea Righi --- kernel/sched/ext/ext.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/kernel/sched/ext/ext.c b/kernel/sched/ext/ext.c index 40cef77394ac..aeee44e016ab 100644 --- a/kernel/sched/ext/ext.c +++ b/kernel/sched/ext/ext.c @@ -6878,11 +6878,6 @@ static struct scx_sched *scx_alloc_and_add_sched(struct scx_enable_cmd *cmd, sch->ops = *cmd->ops; } - rcu_assign_pointer(ops->priv, sch); - - sch->kobj.kset = scx_kset; - INIT_LIST_HEAD(&sch->all); - #ifdef CONFIG_EXT_SUB_SCHED char *buf = kzalloc(PATH_MAX, GFP_KERNEL); if (!buf) { @@ -6900,7 +6895,19 @@ static struct scx_sched *scx_alloc_and_add_sched(struct scx_enable_cmd *cmd, sch->cgrp = cgrp; INIT_LIST_HEAD(&sch->children); INIT_LIST_HEAD(&sch->sibling); +#endif /* CONFIG_EXT_SUB_SCHED */ + /* + * Publishing makes @sch visible to scx_prog_sched() readers. Failure + * paths after this point must free @sch through kobject_put() whose + * release path defers the actual freeing by an RCU grace period. + */ + rcu_assign_pointer(ops->priv, sch); + + sch->kobj.kset = scx_kset; + INIT_LIST_HEAD(&sch->all); + +#ifdef CONFIG_EXT_SUB_SCHED if (parent) { /* * Pin @parent for @sch's lifetime. The kobject hierarchy pins @@ -6955,7 +6962,6 @@ static struct scx_sched *scx_alloc_and_add_sched(struct scx_enable_cmd *cmd, #ifdef CONFIG_EXT_SUB_SCHED err_free_lb_resched: - RCU_INIT_POINTER(ops->priv, NULL); free_cpumask_var(sch->bypass_lb_resched_cpumask); #endif err_free_lb_cpumask: From e6979d05c6a6fe79980f08d63f039f0b27c30a1c Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 9 Jul 2026 11:08:41 -1000 Subject: [PATCH 0887/1054] tools/sched_ext: scx - Fix cmask_subset(), cmask_equal() and cmask_weight() cmask_equal(), cmask_weight() and cmask_subset() bounded their word walks with CMASK_NR_WORDS(nr_cids), which pads by one word and can't tell the last word in use without @base. The walks could thus cover a slack word past the active range, which cmask_reframe() leaves non-zero: a stale bit there gave cmask_equal() a spurious mismatch, cmask_weight() an inflated count, and cmask_subset() a spurious violation. cmask_subset() could also read @b->bits[] one word past its allocation (within the arena's fault-recovered range, so harmless), and deviated from the kernel scx_cmask_subset() by failing any @a range that doesn't nest inside @b's even when the overhanging bits are all clear. Bound the cmask_equal() and cmask_weight() walks by the words the range actually spans, with early returns for empty ranges. Rewrite cmask_subset() to match the kernel semantics: scan @a's overhangs for set bits with cmask_next_set() and walk the words of the range intersection. cmask_subset() moves below cmask_next_set(), which it now uses. Padding bits don't need masking as every cmask helper keeps them clear. Fixes: a58e6b79b432 ("sched_ext: Add cmask, a base-windowed bitmap over cid space") Signed-off-by: Tejun Heo Reviewed-by: Andrea Righi --- tools/sched_ext/include/scx/cid.bpf.h | 88 +++++++++++++++++---------- 1 file changed, 55 insertions(+), 33 deletions(-) diff --git a/tools/sched_ext/include/scx/cid.bpf.h b/tools/sched_ext/include/scx/cid.bpf.h index db247e42fb45..6b0b4e41b288 100644 --- a/tools/sched_ext/include/scx/cid.bpf.h +++ b/tools/sched_ext/include/scx/cid.bpf.h @@ -391,7 +391,9 @@ static __always_inline bool cmask_equal(const struct scx_cmask __arena *a, if (a->base != b->base || a->nr_cids != b->nr_cids) return false; - nr_words = CMASK_NR_WORDS(a->nr_cids); + if (a->nr_cids == 0) + return true; + nr_words = (a->base + a->nr_cids - 1) / 64 - a->base / 64 + 1; bpf_for(i, 0, CMASK_MAX_WORDS) { if (i >= nr_words) @@ -402,36 +404,6 @@ static __always_inline bool cmask_equal(const struct scx_cmask __arena *a, return true; } -/* - * True iff every bit set in @a is also set in @b over the intersection of - * their ranges. Bits of @a outside @b's range fail the test. - */ -static __always_inline bool cmask_subset(const struct scx_cmask __arena *a, - const struct scx_cmask __arena *b) -{ - u32 a_end = a->base + a->nr_cids; - u32 b_end = b->base + b->nr_cids; - u32 a_wbase = a->base / 64; - u32 b_wbase = b->base / 64; - u32 nr_words, i; - - /* any bit of @a outside @b's range is a subset violation */ - if (a->base < b->base || a_end > b_end) - return false; - - nr_words = CMASK_NR_WORDS(a->nr_cids); - bpf_for(i, 0, CMASK_MAX_WORDS) { - u32 wi_b; - - if (i >= nr_words) - break; - wi_b = a_wbase + i - b_wbase; - if (a->bits[i] & ~b->bits[wi_b]) - return false; - } - return true; -} - /** * cmask_next_set - find the first set bit at or after @cid * @m: cmask to search @@ -488,16 +460,66 @@ static __always_inline u32 cmask_first_set(const struct scx_cmask __arena *m) (cid) < (m)->base + (m)->nr_cids; \ (cid) = cmask_next_set((m), (cid) + 1)) +/* + * True iff every bit set in @a is also set in @b. Matches the kernel-side + * scx_cmask_subset(): ranges don't need to nest, and set bits of @a outside + * @b's range fail the test. + */ +static __always_inline bool cmask_subset(const struct scx_cmask __arena *a, + const struct scx_cmask __arena *b) +{ + u32 a_end = a->base + a->nr_cids; + u32 b_end = b->base + b->nr_cids; + u32 a_wbase = a->base / 64; + u32 b_wbase = b->base / 64; + u32 lo = a->base > b->base ? a->base : b->base; + u32 hi = a_end < b_end ? a_end : b_end; + u32 lo_word, hi_word, i; + + /* set bits of @a outside @b's range can't be in @b */ + if (a->base < b->base && + cmask_next_set(a, a->base) < (b->base < a_end ? b->base : a_end)) + return false; + if (a_end > b_end && + cmask_next_set(a, a->base > b_end ? a->base : b_end) < a_end) + return false; + + if (lo >= hi) + return true; + + /* + * Walk the words the range intersection spans. Plain word tests + * suffice: the scans above guarantee @a has no set bit outside @b's + * range and padding bits are kept clear by all cmask helpers. + */ + lo_word = lo / 64; + hi_word = (hi - 1) / 64; + + bpf_for(i, 0, CMASK_MAX_WORDS) { + u32 w = lo_word + i; + + if (w > hi_word) + break; + if (a->bits[w - a_wbase] & ~b->bits[w - b_wbase]) + return false; + } + return true; +} + /* * Population count over [base, base + nr_cids). Padding bits in the head/tail * words are guaranteed zero by the mutating helpers, so a flat popcount over - * all words is correct. + * the words the range spans is correct. */ static __always_inline u32 cmask_weight(const struct scx_cmask __arena *m) { - u32 nr_words = CMASK_NR_WORDS(m->nr_cids), i; + u32 nr_words, i; u32 count = 0; + if (!m->nr_cids) + return 0; + nr_words = (m->base + m->nr_cids - 1) / 64 - m->base / 64 + 1; + bpf_for(i, 0, CMASK_MAX_WORDS) { if (i >= nr_words) break; From 5d72720365f973e2c21fa99505b627521de66c25 Mon Sep 17 00:00:00 2001 From: Doruk Tan Ozturk Date: Thu, 9 Jul 2026 12:08:53 +0200 Subject: [PATCH 0888/1054] xen-blkfront: fix double completion of split requests on resume MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a block request is too large for a single ring entry and the backend does not support indirect descriptors, blkfront splits it across two ring requests. This only happens when the frontend runs on a 64K-page kernel (e.g. arm64): there, even a single-page request may not fit in one ring slot and must be split. blkif_ring_get_request() is called twice and both shadow slots (shadow[id] and shadow[extra_id]) point at the *same* struct request, linked through associated_id. blkif_completion() collapses the pair on the normal completion path, recycling the second slot and completing the request once. The suspend/resume walk in blkfront_resume() does not: it visits every shadow slot with ->request set and calls blk_mq_end_request() or re-queues ->request. For an in-flight split request it therefore processes the shared struct request twice on resume/migration -- a double completion. Skip the secondary slot of a split request in the resume walk so each logical request is processed exactly once. The secondary slot is the linked one (associated_id != NO_ASSOCIATED_ID) that carries no scatter-gather list (num_sg == 0); the first slot always keeps the sg list. The bug is only reachable on suspend/resume or live migration of such a guest, so it has no local reproducer. Fixes: 6cc568339047 ("xen/blkfront: Handle non-indirect grant with 64KB pages") Assisted-by: 0sec:claude-opus-4-8 Signed-off-by: Doruk Tan Ozturk Acked-by: Roger Pau Monné Link: https://patch.msgid.link/20260709100853.7489-1-doruk@0sec.ai Signed-off-by: Jens Axboe --- drivers/block/xen-blkfront.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c index f765970578f9..8dad7bf5f664 100644 --- a/drivers/block/xen-blkfront.c +++ b/drivers/block/xen-blkfront.c @@ -2079,6 +2079,15 @@ static int blkfront_resume(struct xenbus_device *dev) if (!shadow[j].request) continue; + /* + * For requests split across multiple slots, process the + * underlying request only once: skip the linked, sg-less + * secondary slot. + */ + if (shadow[j].associated_id != NO_ASSOCIATED_ID && + shadow[j].num_sg == 0) + continue; + /* * Get the bios in the request so we can re-queue them. */ From 90dd1415a158b99ca16f5fe5862c07683e9ddcec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Mon, 6 Jul 2026 20:48:09 +0200 Subject: [PATCH 0889/1054] cifs: Fix and improve cifs_is_path_accessible() function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Do not call SMBQueryInformation() command for path with SMB wildcard characters on non-UNICODE connection because server expands wildcards. Function cifs_is_path_accessible() needs to check if the real path exists and must not expand wildcard characters. Do not dynamically allocate memory for small FILE_ALL_INFO structure and instead allocate it on the stack. This structure is allocated on stack by all other functions. When CAP_NT_SMBS was not negotiated then do not issue CIFSSMBQPathInfo() command. This command returns failure by non-NT Win9x SMB servers, so there is no need try it. The purpose of cifs_is_path_accessible() function is just to check if the path is accessible, so SMBQueryInformation() for old servers is enough. Signed-off-by: Pali Rohár Signed-off-by: Steve French --- fs/smb/client/smb1ops.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/fs/smb/client/smb1ops.c b/fs/smb/client/smb1ops.c index f72879af12c9..dc5a8c1da623 100644 --- a/fs/smb/client/smb1ops.c +++ b/fs/smb/client/smb1ops.c @@ -505,21 +505,27 @@ static int cifs_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon, struct cifs_sb_info *cifs_sb, const char *full_path) { - int rc; - FILE_ALL_INFO *file_info; + int rc = -EOPNOTSUPP; + FILE_ALL_INFO file_info; - file_info = kmalloc_obj(FILE_ALL_INFO); - if (file_info == NULL) - return -ENOMEM; + if (tcon->ses->capabilities & CAP_NT_SMBS) + rc = CIFSSMBQPathInfo(xid, tcon, full_path, &file_info, + 0 /* not legacy */, cifs_sb->local_nls, + cifs_remap(cifs_sb)); - rc = CIFSSMBQPathInfo(xid, tcon, full_path, file_info, - 0 /* not legacy */, cifs_sb->local_nls, - cifs_remap(cifs_sb)); + /* + * Non-UNICODE variant of fallback functions below expands wildcards, + * so they cannot be used for querying paths with wildcard characters. + * Therefore for such paths returns -ENOENT as they cannot exist. + */ + if ((rc == -EOPNOTSUPP || rc == -EINVAL) && + !(tcon->ses->capabilities & CAP_UNICODE) && + strpbrk(full_path, "*?\"><")) + rc = -ENOENT; if (rc == -EOPNOTSUPP || rc == -EINVAL) - rc = SMBQueryInformation(xid, tcon, full_path, file_info, + rc = SMBQueryInformation(xid, tcon, full_path, &file_info, cifs_sb->local_nls, cifs_remap(cifs_sb)); - kfree(file_info); return rc; } From d2c46c9f7a9baf80a322eb1d4494a70e535b637c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Mon, 6 Jul 2026 20:48:11 +0200 Subject: [PATCH 0890/1054] cifs: Remove CIFSSMBSetPathInfoFB() fallback function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fallback function CIFSSMBSetPathInfoFB() is called only from CIFSSMBSetPathInfo() function. CIFSSMBSetPathInfo() is used in smb_set_file_info() which contains all required fallback code, including fallback via filehandle, since commit f122121796f9 ("cifs: Fix changing times and read-only attr over SMB1 smb_set_file_info() function") and commit 92210ccd877b ("cifs: Add fallback code path for cifs_mkdir_setinfo()"). So the CIFSSMBSetPathInfoFB() is just code duplication, which is not needed anymore. Therefore remove it. Signed-off-by: Pali Rohár Signed-off-by: Steve French --- fs/smb/client/cifssmb.c | 36 ------------------------------------ 1 file changed, 36 deletions(-) diff --git a/fs/smb/client/cifssmb.c b/fs/smb/client/cifssmb.c index d39175cdf1b1..40162d5554ea 100644 --- a/fs/smb/client/cifssmb.c +++ b/fs/smb/client/cifssmb.c @@ -5829,38 +5829,6 @@ CIFSSMBSetFileDisposition(const unsigned int xid, struct cifs_tcon *tcon, return rc; } -static int -CIFSSMBSetPathInfoFB(const unsigned int xid, struct cifs_tcon *tcon, - const char *fileName, const FILE_BASIC_INFO *data, - const struct nls_table *nls_codepage, - struct cifs_sb_info *cifs_sb) -{ - int oplock = 0; - struct cifs_open_parms oparms; - struct cifs_fid fid; - int rc; - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .cifs_sb = cifs_sb, - .desired_access = GENERIC_WRITE, - .create_options = cifs_create_options(cifs_sb, 0), - .disposition = FILE_OPEN, - .path = fileName, - .fid = &fid, - }; - - rc = CIFS_open(xid, &oparms, &oplock, NULL); - if (rc) - goto out; - - rc = CIFSSMBSetFileInfo(xid, tcon, data, fid.netfid, current->tgid); - CIFSSMBClose(xid, tcon, fid.netfid); -out: - - return rc; -} - int CIFSSMBSetPathInfo(const unsigned int xid, struct cifs_tcon *tcon, const char *fileName, const FILE_BASIC_INFO *data, @@ -5939,10 +5907,6 @@ CIFSSMBSetPathInfo(const unsigned int xid, struct cifs_tcon *tcon, if (rc == -EAGAIN) goto SetTimesRetry; - if (rc == -EOPNOTSUPP) - return CIFSSMBSetPathInfoFB(xid, tcon, fileName, data, - nls_codepage, cifs_sb); - return rc; } From 6763a0aea6d658d69b9215ab9151d7bd4c1c314b Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Mon, 15 Jun 2026 14:47:37 +1000 Subject: [PATCH 0891/1054] nouveau/vmm: fix another SPT/LPT race We've had an unknown Turing issue for a while with page faults since large pages and compression. I've got a patch series that syncs all our L2 handling with ogkm and it made this fault happen more. After writing a bunch of debugging patches, I spotted an invalid LPT entry where there should have been a valid one. A 64K MAP succeeds on a range, but a subsequent SPT put drops SPT refs across multiple ranges, We shouldn't assume all ranges where SPTEs go away will have the same sparse/invalid/valid state, just iterate over each instead and do the right thing. Cc: stable@vger.kernel.org Signed-off-by: Dave Airlie Fixes: d19512f5abb1 ("nouveau/vmm: start tracking if the LPT PTE is valid. (v6)") Link: https://patch.msgid.link/20260615044737.3419585-1-airlied@gmail.com [ Properly format commit message. - Danilo ] Signed-off-by: Danilo Krummrich (cherry picked from commit d008141ed4ce924167a03d46fbce9ad1fe4efa29) Signed-off-by: Dave Airlie --- drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c | 31 +++++++++---------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c index 107bdb642f22..190c082b12c8 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c @@ -231,29 +231,26 @@ nvkm_vmm_unref_sptes(struct nvkm_vmm_iter *it, struct nvkm_vmm_pt *pgt, * covered by a number of LPTEs, the LPTEs once again take * control over their address range. * - * Determine how many LPTEs need to transition state. + * Transition each LPTE individually as each may have a + * different target state (sparse, invalid, or valid). */ - pgt->pte[ptei].s.spte_valid = false; - for (ptes = 1, ptei++; ptei < lpti; ptes++, ptei++) { + for (ptei++; ptei < lpti; ptei++) { if (pgt->pte[ptei].s.sptes) break; - pgt->pte[ptei].s.spte_valid = false; } - if (pgt->pte[pteb].s.sparse) { - TRA(it, "LPTE %05x: U -> S %d PTEs", pteb, ptes); - pair->func->sparse(vmm, pgt->pt[0], pteb, ptes); - } else if (!pgt->pte[pteb].s.lpte_valid) { - if (pair->func->invalid) { - /* If the MMU supports it, restore the LPTE to the - * INVALID state to tell the MMU there is no point - * trying to fetch the corresponding SPTEs. - */ - TRA(it, "LPTE %05x: U -> I %d PTEs", pteb, ptes); - pair->func->invalid(vmm, pgt->pt[0], pteb, ptes); + while (pteb < ptei) { + pgt->pte[pteb].s.spte_valid = false; + if (pgt->pte[pteb].s.sparse) { + TRA(it, "LPTE %05x: U -> S", pteb); + pair->func->sparse(vmm, pgt->pt[0], pteb, 1); + } else if (!pgt->pte[pteb].s.lpte_valid) { + if (pair->func->invalid) { + TRA(it, "LPTE %05x: U -> I", pteb); + pair->func->invalid(vmm, pgt->pt[0], pteb, 1); + } } - } else { - TRA(it, "LPTE %05x: V %d PTEs", pteb, ptes); + pteb++; } } } From 5948aaf64f81f217a25dcc2bf6c0779bca19566c Mon Sep 17 00:00:00 2001 From: Lee Jia Jie Date: Thu, 9 Jul 2026 21:56:19 +0800 Subject: [PATCH 0892/1054] perf/aux: Fix page UAF in map_range() map_range() reads rb->aux_pages[], rb->aux_nr_pages and rb->aux_pgoff via perf_mmap_to_page() while holding only event->mmap_mutex. Those fields are serialized by rb->aux_mutex, and mmap_mutex is per event. Thus, two events sharing one rb via PERF_EVENT_IOC_SET_OUTPUT can race rb_alloc_aux() with map_range(), leading to a page-UAF scenario as follows: CPU 0 CPU 1 ===== ===== rb_alloc_aux() map_range() [1]: allocate rb->aux_pages[0] [2]: rb->aux_nr_pages++ [3]: perf_mmap_to_page() returns rb->aux_pages[0] [4]: map it as VM_PFNMAP [5]: rb->aux_pgoff = 1 munmap the page [6]: free rb->aux_pages[0] Pages mapped as VM_PFNMAP have no refcount protection, so CPU 1 holds a mapping to a freed physical frame. Fix this by taking rb->aux_mutex across the page walk in map_range(). Fixes: b709eb872e19 ("perf: map pages in advance") Signed-off-by: Lee Jia Jie Signed-off-by: Ingo Molnar Cc: stable@vger.kernel.org Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Namhyung Kim --- kernel/events/core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kernel/events/core.c b/kernel/events/core.c index d7f3e2c2ecb1..ba5bd6a78fe7 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -7150,6 +7150,8 @@ static int map_range(struct perf_buffer *rb, struct vm_area_struct *vma) int err = 0; unsigned long pagenum; + guard(mutex)(&rb->aux_mutex); + /* * We map this as a VM_PFNMAP VMA. * From 33cb713db0161b54f04fe830e062c9e102c29a04 Mon Sep 17 00:00:00 2001 From: Matthieu Buffet Date: Wed, 1 Jul 2026 23:46:27 +0200 Subject: [PATCH 0893/1054] landlock: Fix TCP Fast Open connection bypass MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The documentation of the socket_connect() LSM hook states that it controls connecting a socket to a remote address. It has not been the case since the addition of TCP Fast Open (RFC 7413) support, which allows opening a TCP connection (thus, setting a socket's destination address) via the MSG_FASTOPEN flag passed to sendto()/sendmsg()/sendmmsg(). The problem then got duplicated into MPTCP. Landlock did not take it into account when its TCP support was added, leaving a bypass of TCP connect policy. Ideally a call to the LSM hook would be added in the fastopen code path, in order to fix this generically. But connect() hooks are designed to run with the socket locked, unlike sendmsg() hooks. Closes: https://github.com/landlock-lsm/linux/issues/41 Fixes: fff69fb03dde ("landlock: Support network rules with TCP bind and connect") Signed-off-by: Matthieu Buffet Link: https://patch.msgid.link/20260701214628.33319-1-matthieu@buffet.re Cc: stable@vger.kernel.org [mic: Wrap commit message] Signed-off-by: Mickaël Salaün --- security/landlock/net.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/security/landlock/net.c b/security/landlock/net.c index cbff59ec3aba..46c17116fcf4 100644 --- a/security/landlock/net.c +++ b/security/landlock/net.c @@ -351,6 +351,14 @@ static int hook_socket_sendmsg(struct socket *const sock, access_mask_t access_request; int ret = 0; + if ((msg->msg_flags & MSG_FASTOPEN) && address && sk_is_tcp(sock->sk)) { + ret = current_check_access_socket( + sock, address, addrlen, LANDLOCK_ACCESS_NET_CONNECT_TCP, + true); + if (ret != 0) + return ret; + } + if (sk_is_udp(sock->sk)) access_request = LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP; else From f4b30e0b1d488e7ffd8ea28d1365b9ba8e551edb Mon Sep 17 00:00:00 2001 From: Matthieu Buffet Date: Wed, 1 Jul 2026 23:46:28 +0200 Subject: [PATCH 0894/1054] selftests/landlock: Add test for TCP fast open MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enforce that TCP Fast Open is controlled by LANDLOCK_ACCESS_NET_CONNECT_TCP. Semantics of connect() and sendmsg(MSG_FASTOPEN) should be identical from Landlock's perspective. Also enforce error code consistency, since UDP sockets ignore the MSG_FASTOPEN flag while Unix sockets reject it. Signed-off-by: Matthieu Buffet Link: https://patch.msgid.link/20260701214628.33319-2-matthieu@buffet.re Cc: stable@vger.kernel.org [mic: Fix formatting] Signed-off-by: Mickaël Salaün --- tools/testing/selftests/landlock/net_test.c | 97 +++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/tools/testing/selftests/landlock/net_test.c b/tools/testing/selftests/landlock/net_test.c index 2ed1f76b7a8b..be2eb88092fb 100644 --- a/tools/testing/selftests/landlock/net_test.c +++ b/tools/testing/selftests/landlock/net_test.c @@ -1281,6 +1281,103 @@ TEST_F(protocol, connect_unspec) EXPECT_EQ(0, close(bind_fd)); } +TEST_F(protocol, tcp_fastopen) +{ + const bool restricted = variant->sandbox == TCP_SANDBOX && + variant->prot.type == SOCK_STREAM && + (variant->prot.protocol == IPPROTO_TCP || + variant->prot.protocol == IPPROTO_IP) && + (variant->prot.domain == AF_INET || + variant->prot.domain == AF_INET6); + const struct landlock_ruleset_attr ruleset_attr = { + .handled_access_net = LANDLOCK_ACCESS_NET_CONNECT_TCP, + }; + int bind_fd, client_fd, status; + char buf; + pid_t child; + + bind_fd = socket_variant(&self->srv0); + ASSERT_LE(0, bind_fd); + EXPECT_EQ(0, bind_variant(bind_fd, &self->srv0)); + if (self->srv0.protocol.type == SOCK_STREAM) + EXPECT_EQ(0, listen(bind_fd, backlog)); + + child = fork(); + ASSERT_LE(0, child); + if (child == 0) { + int connect_fd, ret; + + /* Closes listening socket for the child. */ + EXPECT_EQ(0, close(bind_fd)); + + connect_fd = socket_variant(&self->srv0); + ASSERT_LE(0, connect_fd); + + if (variant->sandbox == TCP_SANDBOX) { + const int ruleset_fd = landlock_create_ruleset( + &ruleset_attr, sizeof(ruleset_attr), 0); + ASSERT_LE(0, ruleset_fd); + + enforce_ruleset(_metadata, ruleset_fd); + EXPECT_EQ(0, close(ruleset_fd)); + } + + /* Fast Open with no address. */ + ret = sendto_variant(connect_fd, NULL, NULL, 0, MSG_FASTOPEN); + if (self->srv0.protocol.domain == AF_UNIX) { + EXPECT_EQ(-ENOTCONN, ret); + } else if (self->srv0.protocol.type == SOCK_DGRAM) { + EXPECT_EQ(-EDESTADDRREQ, ret); + } else { + EXPECT_EQ(-EINVAL, ret); + } + + /* Fast Open to a denied address. */ + ret = sendto_variant(connect_fd, &self->srv0, "A", 1, + MSG_FASTOPEN); + if (restricted) { + EXPECT_EQ(-EACCES, ret); + } else if (self->srv0.protocol.domain == AF_UNIX && + self->srv0.protocol.type == SOCK_STREAM) { + EXPECT_EQ(-EOPNOTSUPP, ret); + } else { + EXPECT_EQ(0, ret); + } + + EXPECT_EQ(0, close(connect_fd)); + _exit(_metadata->exit_code); + return; + } + + client_fd = bind_fd; + if (!restricted && self->srv0.protocol.type == SOCK_STREAM && + self->srv0.protocol.domain != AF_UNIX) { + client_fd = accept(bind_fd, NULL, 0); + ASSERT_LE(0, client_fd); + } + + if (restricted) { + EXPECT_EQ(-1, read(client_fd, &buf, 1)); + EXPECT_EQ(ENOTCONN, errno); + } else if (self->srv0.protocol.domain == AF_UNIX && + self->srv0.protocol.type == SOCK_STREAM) { + EXPECT_EQ(-1, read(client_fd, &buf, 1)); + EXPECT_EQ(EINVAL, errno); + } else { + EXPECT_EQ(1, read(client_fd, &buf, 1)); + EXPECT_EQ('A', buf); + } + + EXPECT_EQ(child, waitpid(child, &status, 0)); + EXPECT_EQ(1, WIFEXITED(status)); + EXPECT_EQ(EXIT_SUCCESS, WEXITSTATUS(status)); + + if (client_fd != bind_fd) + EXPECT_LE(0, close(client_fd)); + + EXPECT_EQ(0, close(bind_fd)); +} + TEST_F(protocol, sendmsg_stream) { int srv0_fd, tmp_fd, client_fd, res; From 592a37889f97d60debf6a442684ba4d13435c817 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Sala=C3=BCn?= Date: Fri, 3 Jul 2026 16:17:09 +0200 Subject: [PATCH 0895/1054] landlock: Fix kernel-doc for the nested quiet layer flag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit kernel-doc emits "Excess struct member 'quiet' description in 'landlock_layer'" because "quiet" is a bitfield inside the named nested struct "flags", but its inline comment used the bare member name "@quiet:", which kernel-doc attributes to the enclosing landlock_layer. Use the canonical dotted notation "@flags.quiet:" so kernel-doc resolves the nested member, and include it in the generated documentation. Cc: Justin Suess Cc: Tingmao Wang Fixes: a260c0055665 ("landlock: Add a place for flags to layer rules") Link: https://patch.msgid.link/20260703141711.2016964-1-mic@digikod.net Signed-off-by: Mickaël Salaün --- security/landlock/ruleset.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h index 61f3c253d5c9..0437adf17428 100644 --- a/security/landlock/ruleset.h +++ b/security/landlock/ruleset.h @@ -35,8 +35,8 @@ struct landlock_layer { */ struct { /** - * @quiet: Suppresses denial logs for the object covered by this - * rule in this domain. For filesystem rules, this inherits + * @flags.quiet: Suppresses denial logs for the object covered by + * this rule in this domain. For filesystem rules, this inherits * down the file hierarchy. */ u8 quiet : 1; From 97c0e344e03818dbf3116e77d3d7fe81f1fbe795 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Sala=C3=BCn?= Date: Wed, 8 Jul 2026 13:06:33 +0200 Subject: [PATCH 0896/1054] landlock: Update formatting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Following commit 99df2a8eba34 ("clang-format: fix formatting of guard() and scoped_guard() statements"), update scoped_guard() formatting. Also, see the related fix [1]. Cc: Günther Noack Cc: Miguel Ojeda Link: https://lore.kernel.org/r/20260708105713.2073335-1-mic@digikod.net [1] Link: https://patch.msgid.link/20260708110635.2083515-1-mic@digikod.net Reviewed-by: Günther Noack Signed-off-by: Mickaël Salaün --- security/landlock/task.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/security/landlock/task.c b/security/landlock/task.c index 7ddf211f75c3..360d226d0f51 100644 --- a/security/landlock/task.c +++ b/security/landlock/task.c @@ -95,8 +95,7 @@ static int hook_ptrace_access_check(struct task_struct *const child, if (!parent_subject) return 0; - scoped_guard(rcu) - { + scoped_guard(rcu) { const struct landlock_ruleset *const child_dom = landlock_get_task_domain(child); err = domain_ptrace(parent_subject->domain, child_dom); @@ -370,8 +369,7 @@ static int hook_task_kill(struct task_struct *const p, if (!subject) return 0; - scoped_guard(rcu) - { + scoped_guard(rcu) { is_scoped = domain_is_scoped(subject->domain, landlock_get_task_domain(p), signal_scope.scope); @@ -422,8 +420,7 @@ static int hook_file_send_sigiotask(struct task_struct *tsk, if (task_tgid(tsk) == landlock_file(fown->file)->fown_tg) return 0; - scoped_guard(rcu) - { + scoped_guard(rcu) { is_scoped = domain_is_scoped(subject->domain, landlock_get_task_domain(tsk), signal_scope.scope); From d793186aa3bb833c878ae6826c87e62c843afaa3 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 9 Jul 2026 18:43:40 +0200 Subject: [PATCH 0897/1054] selftests/landlock: Fix screwed up pointers in the scoped_signal_test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The scoped_signal_test uses pthread_join(..., (void **)&ret)) in a couple of places, i.e. the return value of the thread is stored in the shape of a "void *" into the memory location of &ret. Pointers are 64-bit on modern computers, but the ret variable is declared as a simple "enum thread_return" which is only 32 bits. So the pthread_join() will overflow the ret variable by 4 byte. The problem is very visible on big endian systems like s390x where the test is failing: The least significant byte that carries the return code of the thread is not written into the ret variable here, but somewhere else in the stack frame, so the comparison for the right return code is failing here. Fix it by getting rid of the enum and defining the THREAD_* constants and "ret" variables as proper "void *" pointers. This way we can also get rid of some ugly (void *) castings in a couple of spots. Signed-off-by: Thomas Huth Link: https://patch.msgid.link/20260709164340.339656-1-thuth@redhat.com Cc: stable@vger.kernel.org Fixes: c8994965013e ("selftests/landlock: Test signal scoping for threads") [mic: Add clang-format markups] Signed-off-by: Mickaël Salaün --- .../selftests/landlock/scoped_signal_test.c | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/tools/testing/selftests/landlock/scoped_signal_test.c b/tools/testing/selftests/landlock/scoped_signal_test.c index f24f2c28f62e..3f0cd42a2afe 100644 --- a/tools/testing/selftests/landlock/scoped_signal_test.c +++ b/tools/testing/selftests/landlock/scoped_signal_test.c @@ -249,12 +249,12 @@ TEST_F(scoped_domains, check_access_signal) _metadata->exit_code = KSFT_FAIL; } -enum thread_return { - THREAD_INVALID = 0, - THREAD_SUCCESS = 1, - THREAD_ERROR = 2, - THREAD_TEST_FAILED = 3, -}; +/* clang-format off */ +#define THREAD_INVALID ((void *)0) +#define THREAD_SUCCESS ((void *)1) +#define THREAD_ERROR ((void *)2) +#define THREAD_TEST_FAILED ((void *)3) +/* clang-format on */ static void *thread_sync(void *arg) { @@ -262,15 +262,15 @@ static void *thread_sync(void *arg) char buf; if (read(pipe_read, &buf, 1) != 1) - return (void *)THREAD_ERROR; + return THREAD_ERROR; - return (void *)THREAD_SUCCESS; + return THREAD_SUCCESS; } TEST(signal_scoping_thread_before) { pthread_t no_sandbox_thread; - enum thread_return ret = THREAD_INVALID; + void *ret = THREAD_INVALID; int thread_pipe[2]; drop_caps(_metadata); @@ -285,7 +285,7 @@ TEST(signal_scoping_thread_before) EXPECT_EQ(0, pthread_kill(no_sandbox_thread, 0)); EXPECT_EQ(1, write(thread_pipe[1], ".", 1)); - EXPECT_EQ(0, pthread_join(no_sandbox_thread, (void **)&ret)); + EXPECT_EQ(0, pthread_join(no_sandbox_thread, &ret)); EXPECT_EQ(THREAD_SUCCESS, ret); EXPECT_EQ(0, close(thread_pipe[0])); @@ -295,7 +295,7 @@ TEST(signal_scoping_thread_before) TEST(signal_scoping_thread_after) { pthread_t scoped_thread; - enum thread_return ret = THREAD_INVALID; + void *ret = THREAD_INVALID; int thread_pipe[2]; drop_caps(_metadata); @@ -310,7 +310,7 @@ TEST(signal_scoping_thread_after) EXPECT_EQ(0, pthread_kill(scoped_thread, 0)); EXPECT_EQ(1, write(thread_pipe[1], ".", 1)); - EXPECT_EQ(0, pthread_join(scoped_thread, (void **)&ret)); + EXPECT_EQ(0, pthread_join(scoped_thread, &ret)); EXPECT_EQ(THREAD_SUCCESS, ret); EXPECT_EQ(0, close(thread_pipe[0])); @@ -327,20 +327,20 @@ void *thread_setuid(void *ptr) char buf; if (read(arg->pipe_read, &buf, 1) != 1) - return (void *)THREAD_ERROR; + return THREAD_ERROR; /* libc's setuid() should update all thread's credentials. */ if (getuid() != arg->new_uid) - return (void *)THREAD_TEST_FAILED; + return THREAD_TEST_FAILED; - return (void *)THREAD_SUCCESS; + return THREAD_SUCCESS; } TEST(signal_scoping_thread_setuid) { struct thread_setuid_args arg; pthread_t no_sandbox_thread; - enum thread_return ret = THREAD_INVALID; + void *ret = THREAD_INVALID; int pipe_parent[2]; int prev_uid; @@ -367,7 +367,7 @@ TEST(signal_scoping_thread_setuid) EXPECT_EQ(arg.new_uid, getuid()); EXPECT_EQ(1, write(pipe_parent[1], ".", 1)); - EXPECT_EQ(0, pthread_join(no_sandbox_thread, (void **)&ret)); + EXPECT_EQ(0, pthread_join(no_sandbox_thread, &ret)); EXPECT_EQ(THREAD_SUCCESS, ret); clear_cap(_metadata, CAP_SETUID); @@ -667,20 +667,20 @@ static void *thread_setown_scoped(void *arg) ruleset_fd = landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); if (ruleset_fd < 0) - return (void *)THREAD_ERROR; + return THREAD_ERROR; if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) || landlock_restrict_self(ruleset_fd, 0)) { close(ruleset_fd); - return (void *)THREAD_ERROR; + return THREAD_ERROR; } close(ruleset_fd); /* Makes this process group own the SIGIO source. */ if (fcntl(fd, F_SETSIG, SIGURG) || fcntl(fd, F_SETOWN, -getpgrp()) || fcntl(fd, F_SETFL, O_ASYNC)) - return (void *)THREAD_ERROR; + return THREAD_ERROR; - return (void *)THREAD_SUCCESS; + return THREAD_SUCCESS; } /* @@ -702,7 +702,7 @@ TEST(sigio_to_pgid_self) { int trigger[2]; pthread_t thread; - enum thread_return ret = THREAD_INVALID; + void *ret = THREAD_INVALID; int i; drop_caps(_metadata); @@ -722,7 +722,7 @@ TEST(sigio_to_pgid_self) */ ASSERT_EQ(0, pthread_create(&thread, NULL, thread_setown_scoped, &trigger[0])); - ASSERT_EQ(0, pthread_join(thread, (void **)&ret)); + ASSERT_EQ(0, pthread_join(thread, &ret)); ASSERT_EQ(THREAD_SUCCESS, ret); /* Fans SIGURG out to the process group. */ From 5ab1dc6d110db6bee167a32fd94c53ea0e7ad6d2 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 10 Jul 2026 10:16:42 +0200 Subject: [PATCH 0898/1054] selftests/landlock: Skip scoped_signal subtest with MSG_OOB if not available MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MSG_OOB might be disabled in the kernel for unix sockets (by not selecting CONFIG_AF_UNIX_OOB), and in this case the related tests of the scoped_signal_test are currently failing. Add a runtime probe using socketpair() to detect MSG_OOB support and skip the test gracefully if it is unavailable. Signed-off-by: Thomas Huth Link: https://patch.msgid.link/20260710081642.405916-1-thuth@redhat.com Cc: stable@vger.kernel.org Fixes: f34e9ce5f479 ("selftests/landlock: Test signal created by out-of-bound message") Signed-off-by: Mickaël Salaün --- .../selftests/landlock/scoped_signal_test.c | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tools/testing/selftests/landlock/scoped_signal_test.c b/tools/testing/selftests/landlock/scoped_signal_test.c index 3f0cd42a2afe..2d37d0c06c06 100644 --- a/tools/testing/selftests/landlock/scoped_signal_test.c +++ b/tools/testing/selftests/landlock/scoped_signal_test.c @@ -400,6 +400,24 @@ static int setup_signal_handler(int signal) return sigaction(SIGURG, &sa, NULL); } +/* + * MSG_OOB might be disabled in the kernel via the CONFIG_AF_UNIX_OOB + * switch, so this function can be used for probing for its availability. + */ +static bool has_af_unix_oob(void) +{ + bool available = false; + int sp[2]; + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, sp) == 0) { + available = (send(sp[0], ".", 1, MSG_OOB) == 1); + close(sp[0]); + close(sp[1]); + } + + return available; +} + /* clang-format off */ FIXTURE(fown) {}; /* clang-format on */ @@ -462,6 +480,9 @@ TEST_F(fown, sigurg_socket) int pipe_parent[2], pipe_child[2]; pid_t child; + if (!has_af_unix_oob()) + SKIP(return, "CONFIG_AF_UNIX_OOB / MSG_OOB not available"); + memset(&server_address, 0, sizeof(server_address)); set_unix_address(&server_address, 0); From ec4215683e47424c9c4762fd3c60f552a3119142 Mon Sep 17 00:00:00 2001 From: Norbert Szetei Date: Mon, 6 Jul 2026 11:01:59 +0200 Subject: [PATCH 0899/1054] ppp: defer channel free to an RCU grace period to fix pppol2tp RX UAF pppol2tp_recv() runs in the L2TP UDP-encap softirq RX path: l2tp_udp_encap_recv() -> l2tp_recv_common() -> pppol2tp_recv() -> ppp_input(&po->chan) It runs under rcu_read_lock() holding only an l2tp_session reference and takes NO reference on the internal PPP channel (struct channel, chan->ppp) that ppp_input() dereferences. The pppox socket is SOCK_RCU_FREE, so 'po' and the embedded ppp_channel are RCU-safe. But the internal struct channel is a separate allocation that ppp_release_channel() frees with a plain kfree(): close(data socket) -> pppol2tp_release() -> pppox_unbind_sock() -> ppp_unregister_channel() -> ppp_release_channel() -> kfree(pch) For a channel that is bound (PPPIOCGCHAN) but not attached to a ppp unit (no PPPIOCCONNECT, pch->ppp == NULL) and not bridged, teardown skips both ppp_disconnect_channel()'s synchronize_net() and ppp_unbridge_channels()'s synchronize_rcu(), so the kfree() has no grace period. rcu_read_lock() in pppol2tp_recv() does not protect against a plain kfree(), so an in-flight ppp_input() on one CPU can dereference the channel just freed by close() on another CPU. The bug is reachable by an unprivileged user. Defer the channel free to an RCU callback via call_rcu() so the grace period fences any in-flight ppp_input(). The disconnect and unbridge teardown paths already fence with synchronize_net()/synchronize_rcu(); call_rcu() does the same here without stalling the close() path. Fixes: ee40fb2e1eb5 ("l2tp: protect sock pointer of struct pppol2tp_session with RCU") Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Norbert Szetei Reviewed-by: Qingfang Deng Link: https://patch.msgid.link/E793FCF2-58DE-4387-A983-C7B4BC3158BD@doyensec.com Signed-off-by: Paolo Abeni --- drivers/net/ppp/ppp_generic.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c index 57c68efa5ff8..717c1d3aa953 100644 --- a/drivers/net/ppp/ppp_generic.c +++ b/drivers/net/ppp/ppp_generic.c @@ -184,6 +184,7 @@ struct channel { struct list_head clist; /* link in list of channels per unit */ spinlock_t upl; /* protects `ppp' and 'bridge' */ struct channel __rcu *bridge; /* "bridged" ppp channel */ + struct rcu_head rcu; /* for RCU-deferred free of the channel */ #ifdef CONFIG_PPP_MULTILINK u8 avail; /* flag used in multilink stuff */ u8 had_frag; /* >= 1 fragments have been sent */ @@ -3562,6 +3563,18 @@ ppp_disconnect_channel(struct channel *pch) return err; } +/* Purge after the grace period: a late ppp_input() may still queue an + * skb on pch->file.rq before the last RCU reader drains. + */ +static void ppp_release_channel_free(struct rcu_head *rcu) +{ + struct channel *pch = container_of(rcu, struct channel, rcu); + + skb_queue_purge(&pch->file.xq); + skb_queue_purge(&pch->file.rq); + kfree(pch); +} + /* * Drop a reference to a ppp channel and free its memory if the refcount reaches * zero. @@ -3581,9 +3594,7 @@ static void ppp_release_channel(struct channel *pch) pr_err("ppp: destroying undead channel %p !\n", pch); return; } - skb_queue_purge(&pch->file.xq); - skb_queue_purge(&pch->file.rq); - kfree(pch); + call_rcu(&pch->rcu, ppp_release_channel_free); } static void __exit ppp_cleanup(void) @@ -3596,6 +3607,7 @@ static void __exit ppp_cleanup(void) device_destroy(&ppp_class, MKDEV(PPP_MAJOR, 0)); class_unregister(&ppp_class); unregister_pernet_device(&ppp_net_ops); + rcu_barrier(); /* wait for RCU callbacks before module unload */ } /* From fe179677b6dcb4b658586038a811f87265e97777 Mon Sep 17 00:00:00 2001 From: Gautam Menghani Date: Mon, 15 Jun 2026 14:41:19 +0530 Subject: [PATCH 0900/1054] powerpc/pseries/Kconfig: Enable CONFIG_VPA_PMU to be used with KVM Currently, CONFIG_VPA_PMU is not enabled by default, and consequently cannot be used for KVM guests at all, unless explicitly enabled on host kernel. Mark CONFIG_VPA_PMU as "default m" to ensure it is available when KVM is being used. Cc: stable@vger.kernel.org # v6.13+ Suggested-by: Sean Christopherson Reviewed-by: Amit Machhiwal Reviewed-by: Harsh Prateek Bora Reviewed-by: Ritesh Harjani (IBM) Signed-off-by: Gautam Menghani [Maddy: Changed tag order] Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260615091120.84169-1-gautam@linux.ibm.com --- arch/powerpc/platforms/pseries/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/powerpc/platforms/pseries/Kconfig b/arch/powerpc/platforms/pseries/Kconfig index f7052b131a4c..74910ce3a541 100644 --- a/arch/powerpc/platforms/pseries/Kconfig +++ b/arch/powerpc/platforms/pseries/Kconfig @@ -154,6 +154,7 @@ config HV_PERF_CTRS config VPA_PMU tristate "VPA PMU events" depends on KVM_BOOK3S_64_HV && HV_PERF_CTRS + default m help Enable access to the VPA PMU counters via perf. This enables code that support measurement for KVM on PowerVM(KoP) feature. From 10229a0702b51d0295b53190d34a7de9aa20602c Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Tue, 7 Jul 2026 12:14:14 +0000 Subject: [PATCH 0901/1054] MAINTAINERS: Update SpacemiT SoC git tree repository Due to security concern, switch SpacemiT kernel SoC tree's repository from github.com to kernel.org Signed-off-by: Yixun Lan Link: https://lore.kernel.org/r/20260707-07-spacemit-git-repo-url-v1-1-137697316a4c@kernel.org Signed-off-by: Arnd Bergmann --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 4a8b0fd665ce..1c7242fd18fe 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -23312,7 +23312,7 @@ L: spacemit@lists.linux.dev S: Maintained W: https://github.com/spacemit-com/linux/wiki C: irc://irc.libera.chat/spacemit -T: git https://github.com/spacemit-com/linux +T: https://git.kernel.org/pub/scm/linux/kernel/git/spacemit/linux.git F: arch/riscv/boot/dts/spacemit/ N: spacemit K: spacemit From 24d7e5e39b04c1ef8eee0688ca1527e879b22a40 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Fri, 10 Jul 2026 14:31:25 +0200 Subject: [PATCH 0902/1054] dm-integrity: fix the 'fix_hmac' option When the "fix_hmac" argument is used, dm-integrity is supposed to check the superblock with the journal_mac. However, there was a logic bug in the code - the code only checked the superblock mac if the bit SB_FLAG_FIXED_HMAC was set in the superblock. So, the attacker could clear this bit and bypass the checking trivially. This commit changes dm-integrity so that when the user specified the "fix_hmac" flag and the superblock doesn't have the bit SB_FLAG_FIXED_HMAC set, the activation is aborted with an error. Unfortunatelly, there's a bug in the integritysetup tool that when using the 'open' command it passes the "fix_hmac" argument to the kernel even if the user specified --integrity-legacy-hmac. The bug will be fixed in the upcoming 2.8.7 release. Signed-off-by: Mikulas Patocka Reported-by: Shukai Ni --- drivers/md/dm-integrity.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/md/dm-integrity.c b/drivers/md/dm-integrity.c index 65c30dec8222..dbabb8e46fb7 100644 --- a/drivers/md/dm-integrity.c +++ b/drivers/md/dm-integrity.c @@ -5130,6 +5130,20 @@ static int dm_integrity_ctr(struct dm_target *ti, unsigned int argc, char **argv ti->error = "Journal mac mismatch"; goto bad; } + if (ic->fix_hmac && !(ic->sb->flags & cpu_to_le32(SB_FLAG_FIXED_HMAC)) && ic->journal_mac_alg.key_string) { + /* + * If this happens, it may be either because someone tampered + * with the device or it may be due to a bug in the + * integritysetup tool. + * + * In the latter case, upgrade to integritysetup 2.8.7 and use + * the argument --integrity-legacy-hmac when using the open + * command. + */ + r = -EINVAL; + ti->error = "fix_hmac is on the command line but not in the superblock"; + goto bad; + } get_provided_data_sectors(ic); if (!ic->provided_data_sectors) { From 7bb03b2b01b814a9fc14afbfc2cbb2cca5b34750 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Thu, 9 Jul 2026 21:22:47 +0200 Subject: [PATCH 0903/1054] dm-integrity: fix leaking uninitialized kernel memory If hash size is less than device's tuple size, dm-integrity is supposed to zero the remaining space. There was a bug in the code that zeroing didn't work. This commit fixes it. Signed-off-by: Mikulas Patocka Assisted-by: Claude:claude-opus-4.6 Fixes: fb0987682c62 ("dm-integrity: introduce the Inline mode") Cc: stable@vger.kernel.org --- drivers/md/dm-integrity.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/md/dm-integrity.c b/drivers/md/dm-integrity.c index dbabb8e46fb7..02b2805ed33a 100644 --- a/drivers/md/dm-integrity.c +++ b/drivers/md/dm-integrity.c @@ -2606,7 +2606,7 @@ static int dm_integrity_map_inline(struct dm_integrity_io *dio, bool from_map) struct bio_vec bv = bio_iter_iovec(bio, dio->bio_details.bi_iter); const char *mem = integrity_kmap(ic, bv.bv_page); if (ic->tag_size < ic->tuple_size) - memset(dio->integrity_payload + pos + ic->tag_size, 0, ic->tuple_size - ic->tuple_size); + memset(dio->integrity_payload + pos + ic->tag_size, 0, ic->tuple_size - ic->tag_size); integrity_sector_checksum(ic, &dio->ahash_req, dio->bio_details.bi_iter.bi_sector, mem, bv.bv_offset, dio->integrity_payload + pos); integrity_kunmap(ic, mem); pos += ic->tuple_size; From edf025f083854f80032b73a1aad69a3c90db236f Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Thu, 9 Jul 2026 21:24:09 +0200 Subject: [PATCH 0904/1054] dm-integrity: don't increment hash_offset twice hash_offset is already incremented in the loop "for (i = 0; i < to_copy; i++, ts--)". Do not increment it again. Signed-off-by: Mikulas Patocka Assisted-by: Claude:claude-opus-4.6 Fixes: 84597a44a9d8 ("dm-integrity: dm integrity: add optional discard support") Cc: stable@vger.kernel.org --- drivers/md/dm-integrity.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/md/dm-integrity.c b/drivers/md/dm-integrity.c index 02b2805ed33a..f5508b1bca11 100644 --- a/drivers/md/dm-integrity.c +++ b/drivers/md/dm-integrity.c @@ -1480,9 +1480,6 @@ static int dm_integrity_rw_tag(struct dm_integrity_c *ic, unsigned char *tag, se *metadata_offset = 0; } - if (unlikely(!is_power_of_2(ic->tag_size))) - hash_offset = (hash_offset + to_copy) % ic->tag_size; - total_size -= to_copy; } while (unlikely(total_size)); From 5a266764fadaff8b5c1fe37a186ebf9b09cb953e Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Thu, 9 Jul 2026 21:25:39 +0200 Subject: [PATCH 0905/1054] dm-integrity: fix a bug if the bio is out of limits If dm_integrity_check_limits fails, the code would exit with DM_MAPIO_KILL. However, the range would be already locked at this point, and it wouldn't be unlocked, resulting in a deadlock. Let's move the limit check up, so that when it exits, no resources are leaked. Signed-off-by: Mikulas Patocka Assisted-by: Claude:claude-opus-4.6 Fixes: fb0987682c62 ("dm-integrity: introduce the Inline mode") Cc: stable@vger.kernel.org --- drivers/md/dm-integrity.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/md/dm-integrity.c b/drivers/md/dm-integrity.c index f5508b1bca11..1f2593f113f6 100644 --- a/drivers/md/dm-integrity.c +++ b/drivers/md/dm-integrity.c @@ -2520,6 +2520,9 @@ static int dm_integrity_map_inline(struct dm_integrity_io *dio, bool from_map) if (unlikely((bio->bi_opf & REQ_PREFLUSH) != 0)) return DM_MAPIO_REMAPPED; + if (unlikely(!dm_integrity_check_limits(ic, bio->bi_iter.bi_sector, bio))) + return DM_MAPIO_KILL; + retry: if (!dio->integrity_payload) { unsigned digest_size, extra_size; @@ -2584,10 +2587,6 @@ static int dm_integrity_map_inline(struct dm_integrity_io *dio, bool from_map) dio->bio_details.bi_iter = bio->bi_iter; - if (unlikely(!dm_integrity_check_limits(ic, bio->bi_iter.bi_sector, bio))) { - return DM_MAPIO_KILL; - } - bio->bi_iter.bi_sector += ic->start + SB_SECTORS; bip = bio_integrity_alloc(bio, GFP_NOIO, 1); From 366665416f20527ff7cad548a32d1ddf23195740 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Thu, 9 Jul 2026 21:29:11 +0200 Subject: [PATCH 0906/1054] dm_early_create: fix freeing used table on dm_resume failure If dm_resume fails, the kernel attempts to free table with dm_table_destroy, but the table was already instantiated with dm_swap_table. This commit skips the call to dm_table_destroy in this case. Signed-off-by: Mikulas Patocka Assisted-by: Claude:claude-opus-4.6 Fixes: 6bbc923dfcf5 ("dm: add support to directly boot to a mapped device") Cc: stable@vger.kernel.org --- drivers/md/dm-ioctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index ac77dc0ca225..1fa8bf835be0 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c @@ -2473,7 +2473,7 @@ int __init dm_early_create(struct dm_ioctl *dmi, /* resume device */ r = dm_resume(md); if (r) - goto err_destroy_table; + goto err_hash_remove; DMINFO("%s (%s) is ready", md->disk->disk_name, dmi->name); dm_put(md); From 76c6f845dc0c614304a6e6ee619b552f97cf24b3 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Thu, 9 Jul 2026 21:31:47 +0200 Subject: [PATCH 0907/1054] dm-ioctl: fix a possible overflow in list_version_get_info sizeof(tt->version) is 12 bytes, but the code writes 16 bytes into the output buffer - info->vers->version[0], info->vers->version[1], info->vers->version[2] and info->vers->next. This can cause buffer overflow. Fix this buffer overflow by replacing "sizeof(tt->version)" with "sizeof(struct dm_target_versions)". Signed-off-by: Mikulas Patocka Assisted-by: Claude:claude-opus-4.6 Cc: stable@vger.kernel.org --- drivers/md/dm-ioctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index 1fa8bf835be0..61af2a437a05 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c @@ -785,7 +785,7 @@ static void list_version_get_info(struct target_type *tt, void *param) struct vers_iter *info = param; /* Check space - it might have changed since the first iteration */ - if ((char *)info->vers + sizeof(tt->version) + strlen(tt->name) + 1 > info->end) { + if ((char *)info->vers + sizeof(struct dm_target_versions) + strlen(tt->name) + 1 > info->end) { info->flags = DM_BUFFER_FULL_FLAG; return; } From 72e9ec2fe32b00994f41719cf77423fca67d48b2 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Thu, 9 Jul 2026 21:33:16 +0200 Subject: [PATCH 0908/1054] dm-verity: avoid double increment of &use_bh_wq_enabled verity_parse_opt_args is called twice, first with the only_modifier_opts, first with only_modifier_opts == true and then with only_modifier_opts == false. Thus, the static branch &use_bh_wq_enabled was incremented twice and the destructor verity_dtr would only decrement it once. Fix tihs bug by only incrementing it on the first call, on the second call, when v->use_bh_wq is true, do nothing. Signed-off-by: Mikulas Patocka Assisted-by: Claude:claude-opus-4-6 Cc: stable@vger.kernel.org Fixes: df326e7a0699 ("dm verity: allow optional args to alter primary args handling") --- drivers/md/dm-verity-target.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c index 9a9847f94c46..adfda1fdab6a 100644 --- a/drivers/md/dm-verity-target.c +++ b/drivers/md/dm-verity-target.c @@ -1262,6 +1262,8 @@ static int verity_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v, continue; } else if (!strcasecmp(arg_name, DM_VERITY_OPT_TASKLET_VERIFY)) { + if (v->use_bh_wq) + continue; v->use_bh_wq = true; static_branch_inc(&use_bh_wq_enabled); continue; From e72b793ae440f6900fb17a4b8518c707b5cd3e17 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Thu, 9 Jul 2026 21:35:06 +0200 Subject: [PATCH 0909/1054] dm-verity: fix a possible NULL pointer dereference Fix a possible NULL pointer dereference dm_verity_loadpin_is_bdev_trusted if the device has no table. Signed-off-by: Mikulas Patocka Assisted-by: Claude:claude-opus-4-6 Fixes: b6c1c5745ccc ("dm: Add verity helpers for LoadPin") Cc: stable@vger.kernel.org --- drivers/md/dm-verity-loadpin.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/md/dm-verity-loadpin.c b/drivers/md/dm-verity-loadpin.c index 0666699b6858..9a64f575ae5f 100644 --- a/drivers/md/dm-verity-loadpin.c +++ b/drivers/md/dm-verity-loadpin.c @@ -70,7 +70,7 @@ bool dm_verity_loadpin_is_bdev_trusted(struct block_device *bdev) table = dm_get_live_table(md, &srcu_idx); - if (table->num_targets != 1) + if (!table || table->num_targets != 1) goto out; ti = dm_table_get_target(table, 0); From 88dd117c92a142253fb7a17e791773902b3babc6 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Thu, 9 Jul 2026 21:36:01 +0200 Subject: [PATCH 0910/1054] dm-verity: increase sprintf buffer size The prefix "DM_VERITY_ERR_BLOCK_NR" is 22 chars. Add '=', one digit for type, ',', up to 20 digits for a u64 block number, and a NUL terminator: that's 46 bytes. The buffer is 42 bytes. For block numbers >= 16 decimal digits (devices larger than ~16 EB with 4K blocks), snprintf silently truncates the uevent environment variable. Signed-off-by: Mikulas Patocka Assisted-by: Claude:claude-opus-4.6 Fixes: 65ff5b7ddf05 ("dm verity: add error handling modes for corrupted blocks") Cc: stable@vger.kernel.org --- drivers/md/dm-verity-target.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c index adfda1fdab6a..e63a8290bd3a 100644 --- a/drivers/md/dm-verity-target.c +++ b/drivers/md/dm-verity-target.c @@ -26,7 +26,7 @@ #define DM_MSG_PREFIX "verity" -#define DM_VERITY_ENV_LENGTH 42 +#define DM_VERITY_ENV_LENGTH 46 #define DM_VERITY_ENV_VAR_NAME "DM_VERITY_ERR_BLOCK_NR" #define DM_VERITY_DEFAULT_PREFETCH_SIZE 262144 From 8ec4d9c5a5cf4b61fc087f871465b1f79b393325 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Thu, 9 Jul 2026 21:37:38 +0200 Subject: [PATCH 0911/1054] dm-verity: make error counter atomic The error counter "v->corrupted_errs" was not atomic, thus it could be subject to race conditions. The call to dm_audit_log_target("max-corrupted-errors") may be skipped due to the races. Signed-off-by: Mikulas Patocka Assisted-by: Claude:claude-opus-4.6 Fixes: 65ff5b7ddf05 ("dm verity: add error handling modes for corrupted blocks") Cc: stable@vger.kernel.org --- drivers/md/dm-verity-target.c | 12 +++++++----- drivers/md/dm-verity.h | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c index e63a8290bd3a..1b0763091254 100644 --- a/drivers/md/dm-verity-target.c +++ b/drivers/md/dm-verity-target.c @@ -180,14 +180,16 @@ static int verity_handle_err(struct dm_verity *v, enum verity_block_type type, char *envp[] = { verity_env, NULL }; const char *type_str = ""; struct mapped_device *md = dm_table_get_md(v->ti->table); + int ce; /* Corruption should be visible in device status in all modes */ v->hash_failed = true; - if (v->corrupted_errs >= DM_VERITY_MAX_CORRUPTED_ERRS) - goto out; - - v->corrupted_errs++; + ce = atomic_read(&v->corrupted_errs); + do { + if (ce >= DM_VERITY_MAX_CORRUPTED_ERRS) + goto out; + } while (!atomic_try_cmpxchg(&v->corrupted_errs, &ce, ce + 1)); switch (type) { case DM_VERITY_BLOCK_TYPE_DATA: @@ -203,7 +205,7 @@ static int verity_handle_err(struct dm_verity *v, enum verity_block_type type, DMERR_LIMIT("%s: %s block %llu is corrupted", v->data_dev->name, type_str, block); - if (v->corrupted_errs == DM_VERITY_MAX_CORRUPTED_ERRS) { + if (ce + 1 == DM_VERITY_MAX_CORRUPTED_ERRS) { DMERR("%s: reached maximum errors", v->data_dev->name); dm_audit_log_target(DM_MSG_PREFIX, "max-corrupted-errors", v->ti, 0); } diff --git a/drivers/md/dm-verity.h b/drivers/md/dm-verity.h index 2922263501f6..e104a651c657 100644 --- a/drivers/md/dm-verity.h +++ b/drivers/md/dm-verity.h @@ -68,7 +68,7 @@ struct dm_verity { unsigned int digest_size; /* digest size for the current hash algorithm */ enum verity_mode mode; /* mode for handling verification errors */ enum verity_mode error_mode;/* mode for handling I/O errors */ - unsigned int corrupted_errs;/* Number of errors for corrupted blocks */ + atomic_t corrupted_errs;/* Number of errors for corrupted blocks */ struct workqueue_struct *verify_wq; From f9eff167fefa6f222af87ca605ddd6b6494e390f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Sun, 5 Jul 2026 10:50:00 +0200 Subject: [PATCH 0912/1054] ARM: Don't let ARMv5 platforms select USE_OF MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit USE_OF is already selected by ARM (unless ARCH_FOOTBRIDGE || ARCH_RPC || ARCH_SA1100; these all conflict with ARCH_MULTI_V5). So there is no need for an explicit select and it can be dropped. Signed-off-by: Uwe Kleine-König (The Capable Hub) Acked-by: Arnd Bergmann Link: https://lore.kernel.org/r/20260705085000.3510576-2-u.kleine-koenig@baylibre.com Signed-off-by: Arnd Bergmann --- arch/arm/mach-ixp4xx/Kconfig | 1 - arch/arm/mach-pxa/Kconfig | 3 --- 2 files changed, 4 deletions(-) diff --git a/arch/arm/mach-ixp4xx/Kconfig b/arch/arm/mach-ixp4xx/Kconfig index cb46802f5ce5..7f812020e082 100644 --- a/arch/arm/mach-ixp4xx/Kconfig +++ b/arch/arm/mach-ixp4xx/Kconfig @@ -14,6 +14,5 @@ menuconfig ARCH_IXP4XX select IXP4XX_TIMER select USB_EHCI_BIG_ENDIAN_DESC select USB_EHCI_BIG_ENDIAN_MMIO - select USE_OF help Support for Intel's IXP4XX (XScale) family of processors. diff --git a/arch/arm/mach-pxa/Kconfig b/arch/arm/mach-pxa/Kconfig index 66e26990e2c8..c478fb8a6f78 100644 --- a/arch/arm/mach-pxa/Kconfig +++ b/arch/arm/mach-pxa/Kconfig @@ -22,7 +22,6 @@ config MACH_PXA25X_DT select PINCTRL select POWER_SUPPLY select PXA25x - select USE_OF help Include support for Marvell PXA25x based platforms using the device tree. Needn't select any other machine while @@ -33,7 +32,6 @@ config MACH_PXA27X_DT select PINCTRL select POWER_SUPPLY select PXA27x - select USE_OF help Include support for Marvell PXA27x based platforms using the device tree. Needn't select any other machine while @@ -47,7 +45,6 @@ config MACH_PXA3XX_DT select PINCTRL select POWER_SUPPLY select PXA3xx - select USE_OF help Include support for Marvell PXA3xx based platforms using the device tree. Needn't select any other machine while From e9ad4d5ca309cb517d3f7a85251c3c5328f40f1f Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Thu, 25 Jun 2026 22:48:33 -0400 Subject: [PATCH 0913/1054] vt: fix spurious modifier in CSI/cursor key sequences csi_modifier_param() builds the xterm modifier parameter from shift_state, counting KG_SHIFTL/KG_SHIFTR as Shift, KG_ALTGR as Alt and KG_CTRLL/KG_CTRLR as Ctrl in addition to the canonical KG_SHIFT, KG_ALT and KG_CTRL. That is wrong when those weights are not plain modifiers. Keymaps derived from XKB layouts (by kbd's xkbsupport, and by the console-setup used in Debian, Ubuntu and others) encode the active layout group using KG_SHIFTL/KG_SHIFTR: group 1: - group 2: shiftl group 3: shiftr group 4: shiftl | shiftr So while a non-default layout group is selected, KG_SHIFTL and/or KG_SHIFTR are set in shift_state with no Shift key held. csi_modifier_param() then adds a spurious Shift to every cursor and CSI key: pressing Up while group 2 is active emits ESC[1;2A (Shift+Up) instead of ESC[A. KG_ALTGR has the same problem since it is the standard third-level selector. Normal keymaps bind the physical Shift/Ctrl/Alt keys to KG_SHIFT, KG_CTRL and KG_ALT, leaving the left/right and AltGr weights free for layout and level selection. Count only those canonical weights, so genuine modifiers are still encoded while layout/level selectors are not. Fixes: 4af70f151671 ("vt: add modifier support to cursor keys") Reported-by: Alexey Gladkov Closes: https://lore.kernel.org/kbd/aj2gR0Y7sM6i9s2G@example.org/ Cc: stable Signed-off-by: Nicolas Pitre Link: https://patch.msgid.link/20260626024833.3419086-1-nico@fluxnic.net Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/keyboard.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c index dfdea0842149..763a3f1b7be0 100644 --- a/drivers/tty/vt/keyboard.c +++ b/drivers/tty/vt/keyboard.c @@ -765,16 +765,22 @@ static void k_fn(struct vc_data *vc, unsigned char value, char up_flag) /* * Compute xterm-style modifier parameter for CSI sequences. * Returns 1 + (shift ? 1 : 0) + (alt ? 2 : 0) + (ctrl ? 4 : 0) + * + * Only the canonical modifier weights are counted. The left/right variants + * (KG_SHIFTL, KG_SHIFTR, KG_CTRLL, KG_CTRLR) and KG_ALTGR are commonly + * repurposed as keymap layout-group or level selectors rather than as plain + * modifiers (for instance XKB-derived keymaps select the layout group with + * KG_SHIFTL/KG_SHIFTR), so counting them would encode a spurious modifier. */ static int csi_modifier_param(void) { int mod = 1; - if (shift_state & (BIT(KG_SHIFT) | BIT(KG_SHIFTL) | BIT(KG_SHIFTR))) + if (shift_state & BIT(KG_SHIFT)) mod += 1; - if (shift_state & (BIT(KG_ALT) | BIT(KG_ALTGR))) + if (shift_state & BIT(KG_ALT)) mod += 2; - if (shift_state & (BIT(KG_CTRL) | BIT(KG_CTRLL) | BIT(KG_CTRLR))) + if (shift_state & BIT(KG_CTRL)) mod += 4; return mod; } From 061b627ba534230a18ec4d7251562af12325d06a Mon Sep 17 00:00:00 2001 From: Matthias Feser Date: Tue, 26 May 2026 07:35:09 +0000 Subject: [PATCH 0914/1054] serial: 8250_omap: clear rx_running on zero-length DMA completes On AM33xx RX DMA only triggers when the FIFO reaches the configured threshold (typically 48 bytes). For smaller bursts no DMA request is issued and the FIFO is drained by RX timeout. In this case __dma_rx_do_complete() can legitimately see count == 0. The current code exits early in this case and does not clear dma->rx_running, leaving the DMA state inconsistent. This can prevent RX DMA from restarting and may cause omap_8250_rx_dma_flush() to fail, marking DMA as broken. Fix this by clearing dma->rx_running once the DMA transfer has completed or been terminated, even if no data was transferred. Fixes: a5fd8945a478 ("serial: 8250: 8250_omap.c: Clear DMA RX running status only after DMA termination is done") Cc: stable Signed-off-by: Matthias Feser Reviewed-by: Moteen Shah Link: https://patch.msgid.link/BE3P281MB55155F2F5795E411F5A65282EE0B2@BE3P281MB5515.DEUP281.PROD.OUTLOOK.COM Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_omap.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index c552c6b9a037..3c7775df27ef 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -944,11 +944,12 @@ static void __dma_rx_do_complete(struct uart_8250_port *p) dev_err(p->port.dev, "teardown incomplete\n"); } } + + dma->rx_running = 0; if (!count) goto out; ret = tty_insert_flip_string(tty_port, dma->rx_buf, count); - dma->rx_running = 0; p->port.icount.rx += ret; p->port.icount.buf_overrun += count - ret; out: From b1b4efea05a56c0995e4702a86d6624b4fdff32f Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 26 Jun 2026 11:49:37 +0200 Subject: [PATCH 0915/1054] serial: 8250_mid: Disable DMA for selected platforms In accordance with Errata (specification updates) HSUART May Stop Functioning when DMA is Active. - Denverton document #572409, rev 3.4, DNV60 - Ice Lake Xeon D document #714070, ICXD65 - Snowridge document #731931, SNR44 For a quick fix just disable the respective callbacks during the device probe. Depending on the future development we might remove them completely. Reported-by: micas-opensource Closes: https://lore.kernel.org/linux-serial/20250625031409.2404219-1-opensource@ruijie.com.cn/ Fixes: 6ede6dcd87aa ("serial: 8250_mid: add support for DMA engine handling from UART MMIO") Cc: stable Signed-off-by: Andy Shevchenko Link: https://patch.msgid.link/20260626094937.561776-1-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_mid.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/8250/8250_mid.c b/drivers/tty/serial/8250/8250_mid.c index 8ec03863606e..f88809ff370b 100644 --- a/drivers/tty/serial/8250/8250_mid.c +++ b/drivers/tty/serial/8250/8250_mid.c @@ -10,6 +10,7 @@ #include #include #include +#include #include @@ -368,8 +369,16 @@ static const struct mid8250_board dnv_board = { .freq = 133333333, .base_baud = 115200, .bar = 1, - .setup = dnv_setup, - .exit = dnv_exit, + /* + * Errata: + * HSUART May Stop Functioning when DMA is Active. + * + * - Denverton document #572409, rev 3.4, DNV60 + * - Ice Lake Xeon D document #714070, ICXD65 + * - Snowridge document #731931, SNR44 + */ + .setup = PTR_IF(false, dnv_setup), + .exit = PTR_IF(false, dnv_exit), }; static const struct pci_device_id pci_ids[] = { From 302fbbb4fcbdeac2dc8c63a56c1c4e38c4781958 Mon Sep 17 00:00:00 2001 From: John Ogness Date: Tue, 7 Jul 2026 16:16:04 +0206 Subject: [PATCH 0916/1054] serial: 8250: Ignore flow control on suspend/resume with no_console_suspend If no_console_suspend is specified, on suspend the 8250 console driver uses a scratch register (UART_SCR) to store a special canary value. This is used during the resume path to identify a printk() call before the driver's own ->resume() callback. In this case, serial8250_console_restore() is called to quickly re-init the 8250 for console printing. See commit 4516d50aabed ("serial: 8250: Use canary to restart console after suspend") for the original motivation. Unfortunately, this canary workaround does not work in all cases (such as suspend to mem) because the scratch register will not reset. This has not been a real issue until now because it could simply lead to some garbage characters upon resume. However, with the introduction of console flow control it becomes a real problem because a failed suspend/resume detection when flow control is enabled leads to all characters hitting the flow control timeout. Workaround this issue by temporarily ignoring console flow control when the debug canary suspend/resume detection is active. Fixes: 5e6dfb87b191 ("serial: 8250: Add support for console flow control") Signed-off-by: John Ogness Link: https://patch.msgid.link/20260707141032.5074-1-john.ogness@linutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_port.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index 630deb7dd344..e94a0802cbdd 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -2000,8 +2000,14 @@ static void wait_for_xmitr(struct uart_8250_port *up, int bits) tx_ready = wait_for_lsr(up, bits); - /* Wait up to 1s for flow control if necessary */ - if (uart_console_hwflow_active(&up->port)) { + /* + * Wait up to 1s for flow control if necessary. + * When 'no_console_suspend' is active (in the window between + * suspend() and resume()), flow control is temporarily ignored + * because the canary workaround is not reliable in all situations, + * leading to flow control timeouts for every character. + */ + if (uart_console_hwflow_active(&up->port) && !up->canary) { for (tmout = 1000000; tmout; tmout--) { unsigned int msr = serial_in(up, UART_MSR); up->msr_saved_flags |= msr & MSR_SAVE_FLAGS; From bd910a7660d280595ef94cb6d193951d855d330f Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Thu, 9 Jul 2026 22:28:37 -0400 Subject: [PATCH 0917/1054] drbd: reject data replies with an out-of-range payload size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit recv_dless_read() receives a P_DATA_REPLY from a peer into the bio of an outstanding read request. The peer-supplied payload length reaches it as the signed int data_size, and two peer-controlled inputs can make it negative. With a negotiated data-integrity-alg the digest length is subtracted first, so a reply whose payload is smaller than the digest underflows data_size. With no integrity algorithm (the default) data_size is assigned from the unsigned h95/h100 wire length and drbdd() never bounds it for a payload-carrying command, so a length above INT_MAX casts it negative; this path needs no non-default feature. The bio receive loop then computes expect = min_t(int, data_size, bv_len), which is negative, and drbd_recv_all_warn(mapped, expect) receives with a size_t of SIZE_MAX into the first mapped page. The sibling receive path read_in_block() is not affected: it uses an unsigned size and rejects it against DRBD_MAX_BIO_SIZE before receiving. Reject a data reply whose size is negative after the optional digest subtraction, covering both triggers. Impact: a malicious or man-in-the-middle DRBD peer copies attacker-chosen bytes past a bio page in the receiver, corrupting kernel memory. A node that reads from its peer (a diskless node, or read-balancing to the peer) is exposed in the default configuration; data-integrity-alg is not required. Fixes: b411b3637fa7 ("The DRBD driver") Cc: stable@vger.kernel.org Assisted-by: Codex:gpt-5-5-xhigh Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Michael Bommarito Reviewed-by: Christoph Böhmwalder Link: https://patch.msgid.link/20260710022837.3738461-1-michael.bommarito@gmail.com Signed-off-by: Jens Axboe --- drivers/block/drbd/drbd_receiver.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/block/drbd/drbd_receiver.c b/drivers/block/drbd/drbd_receiver.c index 58b95bf4bdca..2135c14354a8 100644 --- a/drivers/block/drbd/drbd_receiver.c +++ b/drivers/block/drbd/drbd_receiver.c @@ -1810,6 +1810,11 @@ static int recv_dless_read(struct drbd_peer_device *peer_device, struct drbd_req data_size -= digest_size; } + if (data_size < 0) { + drbd_err(peer_device, "Invalid data reply size\n"); + return -EIO; + } + /* optimistically update recv_cnt. if receiving fails below, * we disconnect anyways, and counters will be reset. */ peer_device->device->recv_cnt += data_size>>9; From 2a892294b83f541115c94b0bb637f39bef187657 Mon Sep 17 00:00:00 2001 From: Sandipan Das Date: Fri, 10 Jul 2026 16:15:27 +0530 Subject: [PATCH 0918/1054] perf/x86/amd/lbr: Fix kernel address leakage A user-only branch stack can contain branches that originate from the kernel. As a result, kernel addresses are exposed to user space even when PERF_SAMPLE_BRANCH_USER is requested. On AMD processors supporting X86_FEATURE_AMD_LBR_V2, perf can still report SYSRET/ERET entries for which the branch-from addresses are in the kernel. E.g. $ perf record -e cycles -o - -j any,save_type,u -- \ perf bench syscall basic --loop 1000 | \ perf script -i - -F brstack|tr ' ' '\n'| \ grep -E '0x[89a-f][0-9a-f]{15}' ... 0xffffffff81001268/0x717a90a38f1a/M/-/-/0/ERET/NON_SPEC_CORRECT_PATH 0xffffffff81001268/0x717a90a39157/M/-/-/0/ERET/NON_SPEC_CORRECT_PATH 0xffffffff81001268/0x717a90a2c628/M/-/-/0/ERET/NON_SPEC_CORRECT_PATH 0xffffffff81001268/0x717a90a41b60/M/-/-/0/ERET/NON_SPEC_CORRECT_PATH 0xffffffff81001268/0x717a90a260db/M/-/-/0/ERET/NON_SPEC_CORRECT_PATH 0xffffffff81001268/0x717a90a260db/M/-/-/0/ERET/NON_SPEC_CORRECT_PATH 0xffffffff81001268/0x717a8bef1c30/M/-/-/0/ERET/NON_SPEC_CORRECT_PATH 0xffffffff81001268/0x717a8e4d3c90/M/-/-/0/ERET/NON_SPEC_CORRECT_PATH ... The reason is that the hardware filter only considers the privilege level applicable to the branch target. Extend software filtering to also validate the branch-from addresses against br_sel, so that any branch record whose branch-from address is in the kernel is dropped when PERF_SAMPLE_BRANCH_USER is requested. Fixes: f4f925dae741 ("perf/x86/amd/lbr: Add LbrExtV2 hardware branch filter support") Reported-by: Ian Rogers Signed-off-by: Sandipan Das Signed-off-by: Ingo Molnar Cc: stable@vger.kernel.org Cc: Peter Zijlstra Link: https://patch.msgid.link/a898a29725f6b2f30518354cdc2e432db66c43cf.1783680119.git.sandipan.das@amd.com --- arch/x86/events/amd/lbr.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/x86/events/amd/lbr.c b/arch/x86/events/amd/lbr.c index 5b437dc8e4ce..9d9c961989d5 100644 --- a/arch/x86/events/amd/lbr.c +++ b/arch/x86/events/amd/lbr.c @@ -127,7 +127,8 @@ static void amd_pmu_lbr_filter(void) } /* If type does not correspond, then discard */ - if (type == X86_BR_NONE || (br_sel & type) != type) { + if (type == X86_BR_NONE || (br_sel & type) != type || + (!(br_sel & X86_BR_KERNEL) && kernel_ip(cpuc->lbr_entries[i].from))) { cpuc->lbr_entries[i].from = 0; /* mark invalid */ compress = true; } From f2f152e94a67bc746afaf05a1b2702c195553112 Mon Sep 17 00:00:00 2001 From: Weiming Shi Date: Sat, 4 Jul 2026 10:14:21 -0700 Subject: [PATCH 0919/1054] ipv4: fib: free fib_alias with kfree_rcu() on insert error path fib_table_insert() publishes new_fa into the leaf's fa_list with fib_insert_alias() before calling the fib entry notifiers. When a notifier fails, the error path removes new_fa with fib_remove_alias() (hlist_del_rcu) and frees it right away with kmem_cache_free(). fib_table_lookup() walks that list under rcu_read_lock() only, so a concurrent lookup that already reached new_fa keeps reading it after the free: BUG: KASAN: slab-use-after-free in fib_table_lookup (net/ipv4/fib_trie.c:1601) Read of size 1 at addr ffff88810676d4eb by task exploit/297 Call Trace: fib_table_lookup (net/ipv4/fib_trie.c:1601) ip_route_output_key_hash_rcu (net/ipv4/route.c:2814) ip_route_output_key_hash (net/ipv4/route.c:2705) __ip4_datagram_connect (net/ipv4/datagram.c:49) udp_connect (net/ipv4/udp.c:2144) __sys_connect (net/socket.c:2167) __x64_sys_connect (net/socket.c:2173) do_syscall_64 entry_SYSCALL_64_after_hwframe which belongs to the cache ip_fib_alias of size 56 Triggering the error path needs CAP_NET_ADMIN and a registered fib notifier that can reject a route; a netdevsim device whose IPv4 FIB resource is exhausted is enough. Free new_fa with alias_free_mem_rcu(), as fib_table_delete() already does for a fib_alias removed from the trie. Fixes: a6c76c17df02 ("ipv4: Notify route after insertion to the routing table") Reported-by: Xiang Mei Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Weiming Shi Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260704171421.1786806-1-bestswngs@gmail.com Signed-off-by: Paolo Abeni --- net/ipv4/fib_trie.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index e11dc86ceda0..6badad29593b 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -1385,7 +1385,7 @@ int fib_table_insert(struct net *net, struct fib_table *tb, out_remove_new_fa: fib_remove_alias(t, tp, l, new_fa); out_free_new_fa: - kmem_cache_free(fn_alias_kmem, new_fa); + alias_free_mem_rcu(new_fa); out: fib_release_info(fi); err: From 4fa349156043dc119721d067329714179f501749 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Sun, 5 Jul 2026 22:24:36 -0500 Subject: [PATCH 0920/1054] net/iucv: take a reference on the socket found in afiucv_hs_rcv() afiucv_hs_rcv() looks up the destination socket under iucv_sk_list.lock, drops the lock, and then passes the socket to the afiucv_hs_callback_*() handlers without holding a reference. AF_IUCV sockets are not RCU-protected and are freed synchronously by iucv_sock_kill() -> sock_put(), so a concurrent close can free the socket in the window between read_unlock() and the handler, which then dereferences freed memory (for example sk->sk_data_ready() in afiucv_hs_callback_syn()). Take a reference with sock_hold() while the socket is still on the list and release it with sock_put() once the handler has run. Fixes: 3881ac441f64 ("af_iucv: add HiperSockets transport") Signed-off-by: Bryam Vargas Reviewed-by: Hidayath Khan Link: https://patch.msgid.link/20260705-b4-disp-fc79c0dc-v1-1-d2cdcb57afa9@proton.me Signed-off-by: Paolo Abeni --- net/iucv/af_iucv.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c index fed240b453bd..b85fb9767dec 100644 --- a/net/iucv/af_iucv.c +++ b/net/iucv/af_iucv.c @@ -2089,6 +2089,8 @@ static int afiucv_hs_rcv(struct sk_buff *skb, struct net_device *dev, } } } + if (sk) + sock_hold(sk); read_unlock(&iucv_sk_list.lock); if (!iucv) sk = NULL; @@ -2138,6 +2140,8 @@ static int afiucv_hs_rcv(struct sk_buff *skb, struct net_device *dev, kfree_skb(skb); } + if (sk) + sock_put(sk); return err; } From 5d1a2240935ea47e2673d0ea17fdb058e4dc91dd Mon Sep 17 00:00:00 2001 From: Wyatt Feng Date: Sat, 13 Jun 2026 18:27:15 +0800 Subject: [PATCH 0921/1054] netfilter: xt_nat: reject unsupported target families xt_nat SNAT and DNAT target handlers assume IP-family conntrack state is present and can dereference a NULL pointer when instantiated from an unsupported family through nft_compat. A bridge-family compat rule can therefore trigger a NULL-dereference in nf_nat_setup_info(). Reject non-IP families in xt_nat_checkentry() so unsupported targets cannot be installed. Keep NFPROTO_INET allowed for valid inet NAT compat users and leave the runtime fast path unchanged. [ The crash was fixed via 9dbba7e694ec ("netfilter: nft_compat: ebtables emulation must reject non-bridge targets"), so this patch is no longer critical. Nevertheless, NAT is only relevant for ipv4/ipv6, so this extra family check is a good idea in any case. ] Fixes: c7232c9979cb ("netfilter: add protocol independent NAT core") Cc: stable@vger.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: Florian Westphal --- net/netfilter/xt_nat.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/net/netfilter/xt_nat.c b/net/netfilter/xt_nat.c index b4f7bbc3f3ca..51c7f7ce88d9 100644 --- a/net/netfilter/xt_nat.c +++ b/net/netfilter/xt_nat.c @@ -26,6 +26,15 @@ static int xt_nat_checkentry_v0(const struct xt_tgchk_param *par) static int xt_nat_checkentry(const struct xt_tgchk_param *par) { + switch (par->family) { + case NFPROTO_IPV4: + case NFPROTO_IPV6: + case NFPROTO_INET: + break; + default: + return -EINVAL; + } + return nf_ct_netns_get(par->net, par->family); } From b06163ce52ec0561f202fbfb9b08090cb61f512e Mon Sep 17 00:00:00 2001 From: Yizhou Zhao Date: Tue, 7 Jul 2026 19:00:14 +0800 Subject: [PATCH 0922/1054] netfilter: ecache: fix inverted time_after() check ecache_work_evict_list() redelivers DESTROY events for conntracks that were moved to the per-netns dying_list after event delivery failed. It sets a 10ms deadline: stop = jiffies + ECACHE_MAX_JIFFIES but then tests: time_after(stop, jiffies) This condition is true while the deadline is still in the future, so the worker returns STATE_RESTART after the first successful redelivery in the usual case. ecache_work() maps STATE_RESTART to delay 0, which turns the redelivery path into one dying conntrack per workqueue dispatch and makes the sent > 16 batching/cond_resched() path effectively unreachable. A conntrack netlink listener whose receive queue is congested can make DESTROY event delivery fail with -ENOBUFS. With sustained conntrack churn, entries then accumulate on the dying_list and are only drained at the degraded one-entry-per-dispatch rate once delivery succeeds again, wasting CPU on back-to-back workqueue reschedules and prolonging conntrack memory/resource pressure. In a KASAN QEMU test with CONFIG_NF_CONNTRACK_EVENTS=y and nf_conntrack.enable_hooks=1, a congested DESTROY listener caused 8192 nf_ct_delete() calls to return false and move entries to the dying_list. After closing the listener, the unfixed kernel needed 7670 ecache_work() entries to destroy 7669 conntracks. With this change, the same 8192 entries were destroyed by 2 ecache_work() entries. Swap the comparison so the worker restarts only after the deadline has expired. Fixes: 2ed3bf188b33 ("netfilter: ecache: use dedicated list for event redelivery") Cc: stable@vger.kernel.org Reported-by: Yizhou Zhao Reported-by: Yuxiang Yang Reported-by: Ao Wang Reported-by: Xuewei Feng Reported-by: Qi Li Reported-by: Ke Xu Assisted-by: Claude-Code:GLM-5.2 Signed-off-by: Yizhou Zhao Signed-off-by: Florian Westphal --- net/netfilter/nf_conntrack_ecache.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/nf_conntrack_ecache.c b/net/netfilter/nf_conntrack_ecache.c index 9df159448b89..cc8d8e85169f 100644 --- a/net/netfilter/nf_conntrack_ecache.c +++ b/net/netfilter/nf_conntrack_ecache.c @@ -77,7 +77,7 @@ static enum retry_state ecache_work_evict_list(struct nf_conntrack_net *cnet) hlist_nulls_del_rcu(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode); hlist_nulls_add_head(&ct->tuplehash[IP_CT_DIR_REPLY].hnnode, &evicted_list); - if (time_after(stop, jiffies)) { + if (time_after(jiffies, stop)) { ret = STATE_RESTART; break; } From 86f3ce81dd2b4b0aa2c3016c989a943e4b1b643d Mon Sep 17 00:00:00 2001 From: "Xiang Mei (Microsoft)" Date: Wed, 8 Jul 2026 18:11:50 +0000 Subject: [PATCH 0923/1054] netfilter: bridge: fix stale prevhdr pointer in br_ip6_fragment() br_ip6_fragment() gets prevhdr, a pointer into the skb head, from ip6_find_1stfragopt(), then calls skb_checksum_help(). For a cloned skb skb_checksum_help() reallocates the head via pskb_expand_head(), leaving prevhdr dangling. It is later dereferenced in ip6_frag_next(), causing a use-after-free write. Save prevhdr's offset before skb_checksum_help() and recompute it after, like commit ef0efcd3bd3f ("ipv6: Fix dangling pointer when ipv6 fragment"). BUG: KASAN: slab-use-after-free in ip6_frag_next (net/ipv6/ip6_output.c:857) Write of size 1 at addr ffff888013ff5016 by task exploit/141 Call Trace: ... kasan_report (mm/kasan/report.c:595) ip6_frag_next (net/ipv6/ip6_output.c:857) br_ip6_fragment (net/ipv6/netfilter.c:212) nf_ct_bridge_post (net/bridge/netfilter/nf_conntrack_bridge.c:407) nf_hook_slow (net/netfilter/core.c:619) br_forward_finish (net/bridge/br_forward.c:66) __br_forward (net/bridge/br_forward.c:115) maybe_deliver (net/bridge/br_forward.c:191) br_flood (net/bridge/br_forward.c:245) br_handle_frame_finish (net/bridge/br_input.c:229) br_handle_frame (net/bridge/br_input.c:442) ... packet_sendmsg (net/packet/af_packet.c:3114) ... do_syscall_64 (arch/x86/entry/syscall_64.c:94) entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121) Kernel panic - not syncing: Fatal exception in interrupt Fixes: 764dd163ac92 ("netfilter: nf_conntrack_bridge: add support for IPv6") Cc: stable@vger.kernel.org Reported-by: AutonomousCodeSecurity@microsoft.com Signed-off-by: Xiang Mei (Microsoft) Signed-off-by: Florian Westphal --- net/ipv6/netfilter.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/ipv6/netfilter.c b/net/ipv6/netfilter.c index 6d80f85e55fa..a7025ec87035 100644 --- a/net/ipv6/netfilter.c +++ b/net/ipv6/netfilter.c @@ -120,7 +120,7 @@ int br_ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, ktime_t tstamp = skb->tstamp; struct ip6_frag_state state; u8 *prevhdr, nexthdr = 0; - unsigned int mtu, hlen; + unsigned int mtu, hlen, nexthdr_offset; int hroom, err = 0; __be32 frag_id; @@ -129,6 +129,7 @@ int br_ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, goto blackhole; hlen = err; nexthdr = *prevhdr; + nexthdr_offset = prevhdr - skb_network_header(skb); mtu = skb->dev->mtu; if (frag_max_size > mtu || @@ -147,6 +148,7 @@ int br_ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, (err = skb_checksum_help(skb))) goto blackhole; + prevhdr = skb_network_header(skb) + nexthdr_offset; hroom = LL_RESERVED_SPACE(skb->dev); if (skb_has_frag_list(skb)) { unsigned int first_len = skb_pagelen(skb); From f62c41b4910e65da396ec9a8c40c1fe7fe82e449 Mon Sep 17 00:00:00 2001 From: Yizhou Zhao Date: Wed, 8 Jul 2026 13:27:28 +0800 Subject: [PATCH 0924/1054] netfilter: nf_conncount: fix zone comparison in tuple dedup The "already exists" dedup logic in __nf_conncount_add() decides whether a connection has already been counted and can be skipped instead of incrementing the connlimit count. It compares the conntrack zone of a list entry with the zone of the connection being added using nf_ct_zone_id() and nf_ct_zone_equal(), passing conn->zone.dir or zone->dir as the direction argument. Those helpers take enum ip_conntrack_dir values: IP_CT_DIR_ORIGINAL is 0 and IP_CT_DIR_REPLY is 1. However, zone->dir is a u8 bitmask: NF_CT_ZONE_DIR_ORIG is 1, NF_CT_ZONE_DIR_REPL is 2 and NF_CT_DEFAULT_ZONE_DIR is 3. Passing that bitmask as the enum direction shifts the meaning of every non-zero value. An ORIG-only zone passes 1 and is tested as REPLY, while REPL-only and default zones pass 2 or 3 and test bits beyond the valid direction range. In those cases nf_ct_zone_id() can fall back to NF_CT_DEFAULT_ZONE_ID instead of using the real zone id, so different zones can be treated as equal and dedup collapses to tuple equality alone. nf_conncount stores and compares the original-direction tuple for a connection. If an skb already has an attached conntrack entry, get_ct_or_tuple_from_skb() explicitly copies ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple, regardless of the packet's ctinfo. Therefore the zone comparison in the tuple dedup path must use IP_CT_DIR_ORIGINAL as well; the zone direction bitmask describes where a zone id applies, not which direction this conncount tuple represents. Fix the two dedup comparisons by passing IP_CT_DIR_ORIGINAL directly. Do not special-case NF_CT_DEFAULT_ZONE_DIR and do not compare raw zone ids: using the existing helpers with IP_CT_DIR_ORIGINAL preserves the direction-aware NF_CT_DEFAULT_ZONE_ID fallback. A default bidirectional zone contains the ORIG bit, so it naturally returns the real zone id; reply-only zones continue to fall back for original-direction tuple comparisons. Fixes: 21ba8847f857 ("netfilter: nf_conncount: Fix garbage collection with zones") Fixes: b36e4523d4d5 ("netfilter: nf_conncount: fix garbage collection confirm race") Cc: stable@vger.kernel.org Reported-by: Yizhou Zhao Reported-by: Yuxiang Yang Reported-by: Ao Wang Reported-by: Xuewei Feng Reported-by: Qi Li Reported-by: Ke Xu Assisted-by: Claude-Code:GLM-5.2 Signed-off-by: Yizhou Zhao Signed-off-by: Florian Westphal --- net/netfilter/nf_conncount.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/netfilter/nf_conncount.c b/net/netfilter/nf_conncount.c index 91582069f6d2..e9ea6d9466e7 100644 --- a/net/netfilter/nf_conncount.c +++ b/net/netfilter/nf_conncount.c @@ -211,8 +211,8 @@ static int __nf_conncount_add(struct net *net, /* Not found, but might be about to be confirmed */ if (PTR_ERR(found) == -EAGAIN) { if (nf_ct_tuple_equal(&conn->tuple, &tuple) && - nf_ct_zone_id(&conn->zone, conn->zone.dir) == - nf_ct_zone_id(zone, zone->dir)) + nf_ct_zone_id(&conn->zone, IP_CT_DIR_ORIGINAL) == + nf_ct_zone_id(zone, IP_CT_DIR_ORIGINAL)) goto out_put; /* already exists */ } else { collect++; @@ -223,7 +223,7 @@ static int __nf_conncount_add(struct net *net, found_ct = nf_ct_tuplehash_to_ctrack(found); if (nf_ct_tuple_equal(&conn->tuple, &tuple) && - nf_ct_zone_equal(found_ct, zone, zone->dir)) { + nf_ct_zone_equal(found_ct, zone, IP_CT_DIR_ORIGINAL)) { /* * We should not see tuples twice unless someone hooks * this into a table without "-p tcp --syn". From bd0bdfae1cf0e064b317cd62b3bc29326684cdc6 Mon Sep 17 00:00:00 2001 From: Zhengyang Chen Date: Mon, 22 Jun 2026 18:10:27 +0800 Subject: [PATCH 0925/1054] selftests: netfilter: add bridge tunnel flowtable regression Add a nft_flowtable.sh regression test for the bridge direct-xmit plus IPIP/IP6IP6 underlay configuration that reproduces the reachable DIRECT+tunnel tuple combination exercised by the flowtable fix. The test reuses the existing bridge and tunnel topology, installs flow rules for the tunnel egress and bridge reply path, verifies IPv4 and IPv6 forwarding, and checks the flowtable counters after the transfer. Signed-off-by: Zhengyang Chen Signed-off-by: Ren Wei Signed-off-by: Florian Westphal --- .../selftests/net/netfilter/nft_flowtable.sh | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/tools/testing/selftests/net/netfilter/nft_flowtable.sh b/tools/testing/selftests/net/netfilter/nft_flowtable.sh index 08ad07500e8a..fb1c59d45567 100755 --- a/tools/testing/selftests/net/netfilter/nft_flowtable.sh +++ b/tools/testing/selftests/net/netfilter/nft_flowtable.sh @@ -736,6 +736,61 @@ if ! test_tcp_forwarding_nat "$ns1" "$ns2" 1 "on bridge"; then ret=1 fi +if ip -net "$nsr1" link show tun0 > /dev/null 2>&1 && + ip -net "$nsr2" link show tun0 > /dev/null 2>&1; then + ip -net "$nsr1" route change default via 192.168.100.2 + ip -net "$nsr2" route change default via 192.168.100.1 + ip -6 -net "$nsr1" route delete default + ip -6 -net "$nsr1" route add default via fee1:3::2 + ip -6 -net "$nsr2" route delete default + ip -6 -net "$nsr2" route add default via fee1:3::1 + ip -net "$ns2" route add default via 10.0.2.1 + ip -6 -net "$ns2" route add default via dead:2::1 + + ip netns exec "$nsr1" nft -a insert rule inet filter forward \ + 'meta oif "tun0" tcp dport 12345 ct mark set 1 flow add @f1 counter name routed_orig accept' + ip netns exec "$nsr1" nft -a insert rule inet filter forward \ + 'meta oif "tun6" tcp dport 12345 ct mark set 1 flow add @f1 counter name routed_orig accept' + ip netns exec "$nsr1" nft -a insert rule inet filter forward \ + 'meta oif "veth0" tcp sport 12345 ct mark set 1 flow add @f1 counter name routed_repl accept' + ip netns exec "$nsr1" nft -a insert rule inet filter forward \ + 'meta oif "br0" tcp sport 12345 ct mark set 1 flow add @f1 counter name routed_repl accept' + ip netns exec "$nsr1" nft -a insert rule inet filter forward \ + 'meta oif "tun0" accept' + ip netns exec "$nsr1" nft -a insert rule inet filter forward \ + 'meta oif "tun6" accept' + + ip netns exec "$nsr1" nft reset counters table inet filter >/dev/null + + if test_tcp_forwarding "$ns1" "$ns2" 1 4 10.0.2.99 12345; then + check_counters "bridge + IPIP tunnel" + else + echo "FAIL: flow offload for ns1/ns2 with bridge + IPIP tunnel" 1>&2 + ip netns exec "$nsr1" nft list ruleset + ret=1 + fi + + if test_tcp_forwarding "$ns1" "$ns2" 1 6 "[dead:2::99]" 12345; then + check_counters "bridge + IP6IP6 tunnel" + else + echo "FAIL: flow offload for ns1/ns2 with bridge + IP6IP6 tunnel" 1>&2 + ip netns exec "$nsr1" nft list ruleset + ret=1 + fi + + ip -net "$nsr1" route change default via 192.168.10.2 + ip -net "$nsr2" route change default via 192.168.10.1 + ip -net "$ns2" route del default via 10.0.2.1 + ip -6 -net "$nsr1" route delete default + ip -6 -net "$nsr1" route add default via fee1:2::2 + ip -6 -net "$nsr2" route delete default + ip -6 -net "$nsr2" route add default via fee1:2::1 + ip -6 -net "$ns2" route del default via dead:2::1 +else + echo "SKIP: bridge + tunnel flowtable regression (tun0 missing)" + [ "$ret" -eq 0 ] && ret=$ksft_skip +fi + # Another test: # Add bridge interface br0 to Router1, with NAT and VLAN. From 90941d9c925d66a482c9121919ec3546a6988c16 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 9 Jul 2026 13:40:25 +0200 Subject: [PATCH 0926/1054] netfilter: flowtable: use correct direction to set up tunnel route The layer 2 encapsulation and layer 3 tunnel information in the xmit path is taken from the other tuple, because the tunnel information that is included in the tuple for hashtable lookups is also used to perform the egress encapsulation in the transmit path. This patch uses the correct direction when setting up the tunnel, the original proposed patch to address this fix uses the reversed direction. While at it, remove the redundant check to call dst_release() to drop the reference on the dst that was obtained from the forward path, which is not useful in the direct xmit path unless tunneling is performed. Fixes: fa7395c02d95 ("netfilter: flowtable: support IPIP tunnel with direct xmit") Cc: stable@vger.kernel.org Signed-off-by: Pablo Neira Ayuso Signed-off-by: Florian Westphal --- net/netfilter/nf_flow_table_core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/netfilter/nf_flow_table_core.c b/net/netfilter/nf_flow_table_core.c index 2a829b5e8240..b66e65439341 100644 --- a/net/netfilter/nf_flow_table_core.c +++ b/net/netfilter/nf_flow_table_core.c @@ -127,18 +127,18 @@ static int flow_offload_fill_route(struct flow_offload *flow, switch (route->tuple[dir].xmit_type) { case FLOW_OFFLOAD_XMIT_DIRECT: - if (flow_tuple->tun_num) { + if (route->tuple[!dir].in.num_tuns) { flow_tuple->dst_cache = dst; flow_tuple->dst_cookie = flow_offload_dst_cookie(flow_tuple); + } else { + dst_release(dst); } memcpy(flow_tuple->out.h_dest, route->tuple[dir].out.h_dest, ETH_ALEN); memcpy(flow_tuple->out.h_source, route->tuple[dir].out.h_source, ETH_ALEN); flow_tuple->out.ifidx = route->tuple[dir].out.ifindex; - if (!flow_tuple->tun_num) - dst_release(dst); break; case FLOW_OFFLOAD_XMIT_XFRM: case FLOW_OFFLOAD_XMIT_NEIGH: From a2f57827bf7c695b8c72dc4511cae8e86582369d Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 8 Jul 2026 16:21:30 +0200 Subject: [PATCH 0927/1054] ipvs: reload ip header after head reallocation __ip_vs_get_out_rt() calls skb_ensure_writable() which may reallocate skb->head. Fixes: 8d8e20e2d7bb ("ipvs: Decrement ttl") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-sonnet-4-6 Acked-by: Julian Anastasov Signed-off-by: Florian Westphal --- net/netfilter/ipvs/ip_vs_xmit.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c index ce542ed4b013..9fef4335da13 100644 --- a/net/netfilter/ipvs/ip_vs_xmit.c +++ b/net/netfilter/ipvs/ip_vs_xmit.c @@ -736,13 +736,11 @@ int ip_vs_bypass_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh) { - struct iphdr *iph = ip_hdr(skb); - - if (__ip_vs_get_out_rt(cp->ipvs, cp->af, skb, NULL, iph->daddr, + if (__ip_vs_get_out_rt(cp->ipvs, cp->af, skb, NULL, ip_hdr(skb)->daddr, IP_VS_RT_MODE_NON_LOCAL, NULL, ipvsh) < 0) goto tx_error; - ip_send_check(iph); + ip_send_check(ip_hdr(skb)); /* Another hack: avoid icmp_send in ip_fragment */ skb->ignore_df = 1; From b3fe4cbd583895987935a9bdad01c8f9d3a02310 Mon Sep 17 00:00:00 2001 From: Julian Anastasov Date: Wed, 8 Jul 2026 21:03:15 +0300 Subject: [PATCH 0928/1054] ipvs: fix more places with wrong ipv6 transport offsets Sashiko reports for more incorrect IPv6 transport offsets. The app code for TCP was assuming IPv4 network header even after the ipvsh argument was provided. This can cause problems with apps over IPv6. As for the only official app in the kernel tree (FTP) this problem is harmless because we use Netfilter to mangle the FTP ports and we do not adjust the TCP seq numbers. Also, provide correct offset of the ICMPV6 header in ip_vs_out_icmp_v6() for correct checksum checks when the IPv6 packet has extension headers. Fixes: d12e12299a69 ("ipvs: add ipv6 support to ftp") Fixes: 2a3b791e6e11 ("IPVS: Add/adjust Netfilter hook functions and helpers for v6") Cc: stable@vger.kernel.org Link: https://sashiko.dev/#/patchset/20260706101624.69471-1-zhaoyz24%40mails.tsinghua.edu.cn Signed-off-by: Julian Anastasov Signed-off-by: Florian Westphal --- net/netfilter/ipvs/ip_vs_app.c | 10 ++++------ net/netfilter/ipvs/ip_vs_core.c | 3 +-- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/net/netfilter/ipvs/ip_vs_app.c b/net/netfilter/ipvs/ip_vs_app.c index d54d7da58334..b0e00be85cb1 100644 --- a/net/netfilter/ipvs/ip_vs_app.c +++ b/net/netfilter/ipvs/ip_vs_app.c @@ -361,14 +361,13 @@ static inline int app_tcp_pkt_out(struct ip_vs_conn *cp, struct sk_buff *skb, struct ip_vs_iphdr *ipvsh) { int diff; - const unsigned int tcp_offset = ip_hdrlen(skb); struct tcphdr *th; __u32 seq; - if (skb_ensure_writable(skb, tcp_offset + sizeof(*th))) + if (skb_ensure_writable(skb, ipvsh->len + sizeof(*th))) return 0; - th = (struct tcphdr *)(skb_network_header(skb) + tcp_offset); + th = (struct tcphdr *)(skb_network_header(skb) + ipvsh->len); /* * Remember seq number in case this pkt gets resized @@ -438,14 +437,13 @@ static inline int app_tcp_pkt_in(struct ip_vs_conn *cp, struct sk_buff *skb, struct ip_vs_iphdr *ipvsh) { int diff; - const unsigned int tcp_offset = ip_hdrlen(skb); struct tcphdr *th; __u32 seq; - if (skb_ensure_writable(skb, tcp_offset + sizeof(*th))) + if (skb_ensure_writable(skb, ipvsh->len + sizeof(*th))) return 0; - th = (struct tcphdr *)(skb_network_header(skb) + tcp_offset); + th = (struct tcphdr *)(skb_network_header(skb) + ipvsh->len); /* * Remember seq number in case this pkt gets resized diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index 35cbe821c259..bafab93451d0 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -1219,8 +1219,7 @@ static int ip_vs_out_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, snet.in6 = ciph.saddr.in6; offset = ciph.len; return handle_response_icmp(AF_INET6, skb, &snet, ciph.protocol, cp, - pp, offset, sizeof(struct ipv6hdr), - hooknum); + pp, offset, ipvsh->len, hooknum); } #endif From f468c48d488d0ea2df3422b3e1dfafae1611e853 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Fri, 10 Jul 2026 12:44:41 +0200 Subject: [PATCH 0929/1054] netfilter: xt_physdev: masks are not c-strings ... and must not be subjected to the 'nul terminated' constraint. If the interface name is 15 characters long, the mask is 16-bytes '0xff' (to cover for \0) and the valid device name is rejected. Fixes: 8df772afc9d0 ("netfilter: x_physdev: reject empty or not-nul terminated device names") Cc: stable@vger.kernel.org Closes: https://bugs.launchpad.net/neutron/+bug/2159935 Signed-off-by: Florian Westphal --- net/netfilter/xt_physdev.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/net/netfilter/xt_physdev.c b/net/netfilter/xt_physdev.c index dd98f758176c..a388881c68d4 100644 --- a/net/netfilter/xt_physdev.c +++ b/net/netfilter/xt_physdev.c @@ -130,11 +130,6 @@ static int physdev_mt_check(const struct xt_mtchk_param *par) if (X(physoutdev)) return -ENAMETOOLONG; } - - if (X(in_mask)) - return -ENAMETOOLONG; - if (X(out_mask)) - return -ENAMETOOLONG; #undef X if (!brnf_probed) { From 1cd23ca80784223fa2204e16203f754da4e821f8 Mon Sep 17 00:00:00 2001 From: Weiming Shi Date: Fri, 3 Jul 2026 20:35:46 -0700 Subject: [PATCH 0930/1054] sctp: validate STALE_COOKIE cause length before reading staleness When an ERROR chunk with a STALE_COOKIE cause is received in the COOKIE_ECHOED state, sctp_sf_do_5_2_6_stale() reads the 4-byte Measure of Staleness that follows the cause header: err = (struct sctp_errhdr *)(chunk->skb->data); stale = ntohl(*(__be32 *)((u8 *)err + sizeof(*err))); err is the first cause in the chunk, not the STALE_COOKIE cause that caused the dispatch, and nothing guarantees the staleness field is present. sctp_walk_errors() only requires a cause to be as long as the 4-byte header, so for a STALE_COOKIE cause of length 4 the read runs past the cause, and for a minimal ERROR chunk past skb->tail. The value is echoed to the peer in the Cookie Preservative of the reply INIT, leaking uninitialized memory. sctp_sf_cookie_echoed_err() already walks to the STALE_COOKIE cause, so check its length there and pass it to sctp_sf_do_5_2_6_stale(), which reads that cause instead of the first one. A STALE_COOKIE cause too short to hold the staleness field is discarded. The read is reachable by any peer that can drive an association into COOKIE_ECHOED, including an unprivileged process using a raw SCTP socket in a user and network namespace. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reported-by: Xiang Mei Assisted-by: Claude:claude-opus-4-8 Cc: stable@vger.kernel.org Signed-off-by: Weiming Shi Acked-by: Xin Long Link: https://patch.msgid.link/20260704033545.2438373-2-bestswngs@gmail.com Signed-off-by: Paolo Abeni --- net/sctp/sm_statefuns.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index d23d935e128e..3893b44448b3 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -74,7 +74,8 @@ static enum sctp_disposition sctp_sf_do_5_2_6_stale( const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - struct sctp_cmd_seq *commands); + struct sctp_cmd_seq *commands, + struct sctp_errhdr *err); static enum sctp_disposition sctp_sf_shut_8_4_5( struct net *net, const struct sctp_endpoint *ep, @@ -2529,9 +2530,15 @@ enum sctp_disposition sctp_sf_cookie_echoed_err( * errors. */ sctp_walk_errors(err, chunk->chunk_hdr) { - if (SCTP_ERROR_STALE_COOKIE == err->cause) - return sctp_sf_do_5_2_6_stale(net, ep, asoc, type, - arg, commands); + if (err->cause != SCTP_ERROR_STALE_COOKIE) + continue; + /* The staleness is only meaningful if the cause is long + * enough to hold it; a shorter one is malformed. + */ + if (ntohs(err->length) < sizeof(*err) + sizeof(__be32)) + break; + return sctp_sf_do_5_2_6_stale(net, ep, asoc, type, + arg, commands, err); } /* It is possible to have malformed error causes, and that @@ -2573,13 +2580,13 @@ static enum sctp_disposition sctp_sf_do_5_2_6_stale( const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - struct sctp_cmd_seq *commands) + struct sctp_cmd_seq *commands, + struct sctp_errhdr *err) { int attempts = asoc->init_err_counter + 1; - struct sctp_chunk *chunk = arg, *reply; struct sctp_cookie_preserve_param bht; struct sctp_bind_addr *bp; - struct sctp_errhdr *err; + struct sctp_chunk *reply; u32 stale; if (attempts > asoc->max_init_attempts) { @@ -2590,8 +2597,6 @@ static enum sctp_disposition sctp_sf_do_5_2_6_stale( return SCTP_DISPOSITION_DELETE_TCB; } - err = (struct sctp_errhdr *)(chunk->skb->data); - /* When calculating the time extension, an implementation * SHOULD use the RTT information measured based on the * previous COOKIE ECHO / ERROR exchange, and should add no From 181bb9c9eae4f69fe510a62a42c2932d0314a800 Mon Sep 17 00:00:00 2001 From: Connor Williamson Date: Mon, 15 Jun 2026 13:07:15 +0000 Subject: [PATCH 0931/1054] block: remove redundant GD_NEED_PART_SCAN in add_disk_final() add_disk_final() sets GD_NEED_PART_SCAN before calling bdev_add(), then calls disk_scan_partitions() which sets the flag itself. The early set is redundant and introduces a race. Between bdev_add() and disk_scan_partitions(), concurrent openers (multipathd, blkid, LVM) see the flag in blkdev_get_whole() and trigger bdev_disk_changed(). When disk_scan_partitions() then runs, it calls bdev_disk_changed() again, dropping the partitions the concurrent opener already created before re-adding them, which can result in transient partition disappearances. The race is observable by inserting an msleep() between bdev_add() and disk_scan_partitions() while running concurrent open() calls during device bind. Without artificial delay, it manifests under scheduling pressure during boot on systems with aggressive device scanners (multipathd, systemd-udevd). Therefore, do not set GD_NEED_PART_SCAN in add_disk_final(). Other GD_NEED_PART_SCAN consumers (blkdev_get_whole(), sd_need_revalidate()) should not be affected as the flag is set internally by disk_scan_partitions(). The retry-on-next-open intention from commit e5cfefa97bcc ("block: fix scan partition for exclusively open device again") should also not be affected as the early return paths in disk_scan_partitions() should be unreachable at device registration time (bd_holder is NULL and open_partitions is zero). Fixes: e5cfefa97bcc ("block: fix scan partition for exclusively open device again") Cc: stable@vger.kernel.org Signed-off-by: Connor Williamson Reviewed-by: Christoph Hellwig Link: https://patch.msgid.link/20260615130715.53693-1-connordw@amazon.com Signed-off-by: Jens Axboe --- block/genhd.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/block/genhd.c b/block/genhd.c index 30ac0ffe6517..df2c3c69b467 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -407,10 +407,6 @@ static void add_disk_final(struct gendisk *disk) struct device *ddev = disk_to_dev(disk); if (!(disk->flags & GENHD_FL_HIDDEN)) { - /* Make sure the first partition scan will be proceed */ - if (get_capacity(disk) && disk_has_partscan(disk)) - set_bit(GD_NEED_PART_SCAN, &disk->state); - bdev_add(disk->part0, ddev->devt); if (get_capacity(disk)) disk_scan_partitions(disk, BLK_OPEN_READ); From 58a37e7317b06665e21609a2f867a9962e9e2919 Mon Sep 17 00:00:00 2001 From: Wang Yan Date: Fri, 10 Jul 2026 16:34:37 +0800 Subject: [PATCH 0932/1054] selftests/riscv: ptrace: Fix memory leak of regset_data in vector tests The regset_data buffer allocated with calloc() in the parent process of several vector ptrace tests is never freed before returning, causing memory leaks in: - ptrace_v_not_enabled - ptrace_v_early_debug - ptrace_v_syscall_clobbering - v_csr_invalid/ptrace_v_invalid_values - v_csr_valid/ptrace_v_valid_values Add free(regset_data) before kill(pid, SIGKILL) to release the allocated buffer. Signed-off-by: Wang Yan Reviewed-by: Sergey Matyukevich Link: https://patch.msgid.link/20260710083437.489648-1-wangyan01@kylinos.cn [pjw@kernel.org: Fixed Sergey's E-mail address] Signed-off-by: Paul Walmsley --- .../testing/selftests/riscv/vector/validate_v_ptrace.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tools/testing/selftests/riscv/vector/validate_v_ptrace.c b/tools/testing/selftests/riscv/vector/validate_v_ptrace.c index b038e2175c80..a388b7963d47 100644 --- a/tools/testing/selftests/riscv/vector/validate_v_ptrace.c +++ b/tools/testing/selftests/riscv/vector/validate_v_ptrace.c @@ -75,7 +75,7 @@ TEST(ptrace_v_not_enabled) ASSERT_EQ(-1, ret); /* cleanup */ - + free(regset_data); ASSERT_EQ(0, kill(pid, SIGKILL)); } } @@ -207,7 +207,7 @@ TEST(ptrace_v_early_debug) EXPECT_EQ(vl_csr, regset_data->vl); /* cleanup */ - + free(regset_data); ASSERT_EQ(0, kill(pid, SIGKILL)); } } @@ -331,7 +331,7 @@ TEST(ptrace_v_syscall_clobbering) EXPECT_EQ(0UL, regset_data->vl); /* cleanup */ - + free(regset_data); ASSERT_EQ(0, kill(pid, SIGKILL)); } } @@ -649,7 +649,7 @@ TEST_F(v_csr_invalid, ptrace_v_invalid_values) ASSERT_EQ(ret, -1); /* cleanup */ - + free(regset_data); ASSERT_EQ(0, kill(pid, SIGKILL)); } } @@ -911,7 +911,7 @@ TEST_F(v_csr_valid, ptrace_v_valid_values) EXPECT_EQ(regset_data->vlenb, vlenb); /* cleanup */ - + free(regset_data); ASSERT_EQ(0, kill(pid, SIGKILL)); } } From 6ee4140788234a6fabf59e6a50e38cdb936008cd Mon Sep 17 00:00:00 2001 From: Atish Patra Date: Tue, 2 Jun 2026 15:36:32 -0700 Subject: [PATCH 0933/1054] KVM: SEV: Do not allow intra-host migration/mirroring of SNP VMs The intra-host migration/mirroring feature is not fully implemented for SEV-SNP VMs. The proper migration requires additional SNP-specific state such as guest_req_mutex, guest_req_buf, and guest_resp_buf to be transferred or initialized on the destination. The SNP VM mirroring requires vmsa features to be copied as well otherwise ASID would be bound to SNP range while VM is detected as a SEV VM. Reject SNP source VMs in migration/mirroring until proper SNP state transfer is implemented. Fixes: 1dfe571c12cf ("KVM: SEV: Add initial SEV-SNP support") Reported-by: Chris Mason Reported-by: Sashiko Assisted-by: Claude:claude-opus-4-6 Reviewed-by: Tom Lendacky Signed-off-by: Atish Patra Link: https://patch.msgid.link/20260602-sev_snp_fixes-v3-1-24bfd3ae047c@meta.com Cc: stable@vger.kernel.org [sean: let lines poke past 80 chars, tag for stable] Signed-off-by: Sean Christopherson --- arch/x86/kvm/svm/sev.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index 427229347876..944aaea6501f 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -2129,8 +2129,9 @@ int sev_vm_move_enc_context_from(struct kvm *kvm, unsigned int source_fd) if (ret) return ret; + /* Do not allow SNP VM migration until additional state transfer is implemented */ if (kvm->arch.vm_type != source_kvm->arch.vm_type || - sev_guest(kvm) || !sev_guest(source_kvm)) { + sev_guest(kvm) || !sev_guest(source_kvm) || sev_snp_guest(source_kvm)) { ret = -EINVAL; goto out_unlock; } @@ -2851,8 +2852,9 @@ int sev_vm_copy_enc_context_from(struct kvm *kvm, unsigned int source_fd) * disallow out-of-band SEV/SEV-ES init if the target is already an * SEV guest, or if vCPUs have been created. KVM relies on vCPUs being * created after SEV/SEV-ES initialization, e.g. to init intercepts. + * Also do not allow SNP VM mirroring until additional state transfer is implemented. */ - if (sev_guest(kvm) || !sev_guest(source_kvm) || + if (sev_guest(kvm) || !sev_guest(source_kvm) || sev_snp_guest(source_kvm) || is_mirroring_enc_context(source_kvm) || kvm->created_vcpus) { ret = -EINVAL; goto e_unlock; From df371f2c62446c5fc5c5c1c91b47c219bb0d2100 Mon Sep 17 00:00:00 2001 From: Atish Patra Date: Tue, 2 Jun 2026 15:36:33 -0700 Subject: [PATCH 0934/1054] KVM: selftests: Verify SNP VMs are rejected from migration and mirroring Migration and mirroring of SEV-SNP VMs are not supported yet. Add two selftests that verify KVM rejects intra-host migration and mirroring when the source VM is an SNP VM, so the restriction stays enforced until proper SNP state transfer is implemented. Signed-off-by: Atish Patra Link: https://patch.msgid.link/20260602-sev_snp_fixes-v3-2-24bfd3ae047c@meta.com Signed-off-by: Sean Christopherson --- .../selftests/kvm/x86/sev_migrate_tests.c | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/tools/testing/selftests/kvm/x86/sev_migrate_tests.c b/tools/testing/selftests/kvm/x86/sev_migrate_tests.c index 42bc023d5193..d59abb198d86 100644 --- a/tools/testing/selftests/kvm/x86/sev_migrate_tests.c +++ b/tools/testing/selftests/kvm/x86/sev_migrate_tests.c @@ -313,6 +313,49 @@ static void test_sev_mirror_parameters(void) kvm_vm_free(vm_no_vcpu); } +static void test_sev_snp_migrate_reject(void) +{ + struct kvm_vm *src_vm, *dst_vm; + int ret; + + src_vm = vm_create_barebones_type(KVM_X86_SNP_VM); + snp_vm_init(src_vm); + __vm_vcpu_add(src_vm, 0); + vm_sev_launch(src_vm, snp_default_policy(), NULL); + + dst_vm = vm_create_barebones_type(KVM_X86_SNP_VM); + __vm_vcpu_add(dst_vm, 0); + + ret = __sev_migrate_from(dst_vm, src_vm); + TEST_ASSERT(ret == -1 && errno == EINVAL, + "SNP VM migration should be rejected. ret: %d, errno: %d", + ret, errno); + + kvm_vm_free(src_vm); + kvm_vm_free(dst_vm); +} + +static void test_sev_snp_mirror_reject(void) +{ + struct kvm_vm *src_vm, *dst_vm; + int ret; + + src_vm = vm_create_barebones_type(KVM_X86_SNP_VM); + snp_vm_init(src_vm); + __vm_vcpu_add(src_vm, 0); + vm_sev_launch(src_vm, snp_default_policy(), NULL); + + dst_vm = aux_vm_create(false); + + ret = __sev_mirror_create(dst_vm, src_vm); + TEST_ASSERT(ret == -1 && errno == EINVAL, + "SNP VM mirroring should be rejected. ret: %d, errno: %d", + ret, errno); + + kvm_vm_free(src_vm); + kvm_vm_free(dst_vm); +} + static void test_sev_move_copy(void) { struct kvm_vm *dst_vm, *dst2_vm, *dst3_vm, *sev_vm, *mirror_vm, @@ -384,12 +427,16 @@ int main(int argc, char *argv[]) test_sev_migrate_parameters(); if (kvm_has_cap(KVM_CAP_VM_COPY_ENC_CONTEXT_FROM)) test_sev_move_copy(); + if (kvm_cpu_has(X86_FEATURE_SEV_SNP)) + test_sev_snp_migrate_reject(); } if (kvm_has_cap(KVM_CAP_VM_COPY_ENC_CONTEXT_FROM)) { test_sev_mirror(/* es= */ false); if (have_sev_es) test_sev_mirror(/* es= */ true); test_sev_mirror_parameters(); + if (kvm_cpu_has(X86_FEATURE_SEV_SNP)) + test_sev_snp_mirror_reject(); } return 0; } From 745df2052cf94b8e3348da59924d2eb0e24d4bb7 Mon Sep 17 00:00:00 2001 From: Gary Yang Date: Fri, 26 Jun 2026 17:20:15 +0800 Subject: [PATCH 0935/1054] MAINTAINERS: Update maintainer and git tree for CIX SoC Peter Chen has left CIX Technology. Take over maintenance of the CIX SoC and Update the git tree URL accordingly. Signed-off-by: Gary Yang Reviewed-by: Fugang Duan Acked-by: Peter Chen Signed-off-by: Arnd Bergmann --- MAINTAINERS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 3fd6b8e4cff6..bc08a73e2055 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2767,12 +2767,12 @@ F: arch/arm/mach-ep93xx/ F: drivers/iio/adc/ep93xx_adc.c ARM/CIX SOC SUPPORT -M: Peter Chen +M: Gary Yang M: Fugang Duan R: CIX Linux Kernel Upstream Group L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained -T: git git://git.kernel.org/pub/scm/linux/kernel/git/peter.chen/cix.git +T: git https://github.com/cixtech/linux-mainline.git F: Documentation/devicetree/bindings/arm/cix.yaml F: Documentation/devicetree/bindings/mailbox/cix,sky1-mbox.yaml F: arch/arm64/boot/dts/cix/ From cfbebb55e5127dc162e73fa8956000055a78606c Mon Sep 17 00:00:00 2001 From: Binbin Wu Date: Fri, 10 Jul 2026 11:53:23 +0800 Subject: [PATCH 0936/1054] KVM: TDX: Reject concurrent change to CPUID entry count Reject KVM_TDX_INIT_VM if userspace changes cpuid.nent between the initial read and the subsequent copy of the initialization data. tdx_td_init() first reads user_data->cpuid.nent to size the flexible kvm_tdx_init_vm copy. The copied structure also contains cpuid.nent, and that field can differ from the value used to size the allocation if userspace modifies the input concurrently. setup_tdparams_cpuids() later passes init_vm->cpuid.nent to kvm_find_cpuid_entry2(), which uses it as the array bound for the copied entries. Require the copied count to match the value used to size the allocation so that CPUID parsing cannot access beyond the entries actually copied. Fixes: 0bd0a4a1428b ("KVM: TDX: Replace kmalloc + copy_from_user with memdup_user in tdx_td_init()") Reported-by: Sashiko:gemini-3.1-pro-preview Cc: Signed-off-by: Binbin Wu Reviewed-by: Xiaoyao Li Reviewed-by: Thorsten Blum Link: https://patch.msgid.link/20260710035324.3170534-1-binbin.wu@linux.intel.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/vmx/tdx.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c index 989ab29b8c6f..545b03d9d10b 100644 --- a/arch/x86/kvm/vmx/tdx.c +++ b/arch/x86/kvm/vmx/tdx.c @@ -2797,7 +2797,11 @@ static int tdx_td_init(struct kvm *kvm, struct kvm_tdx_cmd *cmd) goto out; } - if (init_vm->cpuid.padding) { + /* + * Reject the request if userspace changes cpuid.nent between the + * initial read and the subsequent copy. + */ + if (init_vm->cpuid.padding || init_vm->cpuid.nent != nr_user_entries) { ret = -EINVAL; goto out; } From 22a78be4123dce81d72c963a98b774b7d5e1f8e5 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Wed, 8 Jul 2026 15:32:39 -0400 Subject: [PATCH 0937/1054] selftests/ftrace: Fix reading enabled_functions in add_remove_fprobe_module test The add_remove_fprobe_module test checks the number of functions added to the enabled_functions file to make sure that the functions added or removed is as expected. The issue is that it expects this file to be empty on start up. Now that systemd uses BPF that attaches to functions via ftrace, this file is not empty in several systems: # cat /sys/kernel/tracing/enabled_functions bpf_lsm_file_open (1) R D M tramp: ftrace_regs_caller+0x0/0x61 (call_direct_funcs+0x0/0x50) direct(jmp)-->bpf_trampoline_6442529439+0x0/0xe9 Change the test to read the number of lines in enabled_functions at the start of the test and subtract that from the value of the count for the checks within the test. Link: https://lore.kernel.org/r/20260708153239.055d56dd@gandalf.local.home Signed-off-by: Steven Rostedt Acked-by: Masami Hiramatsu (Google) Signed-off-by: Shuah Khan --- .../dynevent/add_remove_fprobe_module.tc | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_fprobe_module.tc b/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_fprobe_module.tc index 2915206777b6..89660a9adf44 100644 --- a/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_fprobe_module.tc +++ b/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_fprobe_module.tc @@ -16,23 +16,32 @@ echo > dynamic_events FUNC1='foo_bar*' FUNC2='vfs_read' +:;: "Save enabled functions count" ;: +ecount=`cat enabled_functions | wc -l` + +count_enabled_functions() { + count=`cat enabled_functions | wc -l` + count=$(($count-$ecount)) + echo $count +} + :;: "Add an event on the test module" ;: echo "f:test1 $FUNC1" >> dynamic_events echo 1 > events/fprobes/test1/enable :;: "Ensure it is enabled" ;: -funcs=`cat enabled_functions | wc -l` +funcs=`count_enabled_functions` test $funcs -ne 0 :;: "Check the enabled_functions is cleared on unloading" ;: rmmod trace-events-sample -funcs=`cat enabled_functions | wc -l` +funcs=`count_enabled_functions` test $funcs -eq 0 :;: "Check it is kept clean" ;: modprobe trace-events-sample echo 1 > events/fprobes/test1/enable || echo "OK" -funcs=`cat enabled_functions | wc -l` +funcs=`count_enabled_functions` test $funcs -eq 0 :;: "Add another event not on the test module" ;: @@ -40,19 +49,19 @@ echo "f:test2 $FUNC2" >> dynamic_events echo 1 > events/fprobes/test2/enable :;: "Ensure it is enabled" ;: -ofuncs=`cat enabled_functions | wc -l` +ofuncs=`count_enabled_functions` test $ofuncs -ne 0 :;: "Disable and remove the first event" echo 0 > events/fprobes/test1/enable echo "-:fprobes/test1" >> dynamic_events -funcs=`cat enabled_functions | wc -l` +funcs=`count_enabled_functions` test $ofuncs -eq $funcs :;: "Disable and remove other events" ;: echo 0 > events/fprobes/enable echo > dynamic_events -funcs=`cat enabled_functions | wc -l` +funcs=`count_enabled_functions` test $funcs -eq 0 rmmod trace-events-sample @@ -63,12 +72,12 @@ echo "f:test1 $FUNC1" >> dynamic_events echo 1 > events/fprobes/test1/enable echo "f:test2 $FUNC2" >> dynamic_events echo 1 > events/fprobes/test2/enable -ofuncs=`cat enabled_functions | wc -l` +ofuncs=`count_enabled_functions` test $ofuncs -ne 0 :;: "Unload module (ftrace entry should be removed)" ;: rmmod trace-events-sample -funcs=`cat enabled_functions | wc -l` +funcs=`count_enabled_functions` test $funcs -ne 0 test $ofuncs -ne $funcs @@ -77,7 +86,7 @@ echo 0 > events/fprobes/test2/enable echo "-:fprobes/test2" >> dynamic_events :;: "Ensure ftrace is disabled." ;: -funcs=`cat enabled_functions | wc -l` +funcs=`count_enabled_functions` test $funcs -eq 0 echo 0 > events/fprobes/enable From c5c413534d40eb4c982f1794e62813f926aba747 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Wed, 8 Jul 2026 16:34:36 -0400 Subject: [PATCH 0938/1054] selftests/tracing: Have trigger-hist-poll.tc use sched_process_exit Currently trigger-hist-poll.tc uses sched_process_free to test the polling of the histogram file. The way it does that is to run sleep, then execute the poll.c code that polls on the sched_process_free for up to 4 seconds to test that when sleep triggers the sched_process_free trace event, it will update the histogram and wake the poll.c code up. The issue is that sched_process_free trace event is called by delayed_put_task_struct() which is called after a RCU grace period has ended. If CONFIG_RCU_LAZY is enabled, RCU callbacks are batched together and do not execute right away. This causes the delayed_put_task_struct() to be called after the poll.c function finishes and it will report an error that it did not wake up on the event. That's because the event didn't trigger during its wait time. Use sched_process_exit instead, which is called when a process exits and doesn't depend on RCU callbacks that may be delayed. Link: https://lore.kernel.org/r/20260708163436.058cc3df@gandalf.local.home Signed-off-by: Steven Rostedt Acked-by: Masami Hiramatsu (Google) Signed-off-by: Shuah Khan --- .../selftests/ftrace/test.d/trigger/trigger-hist-poll.tc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/ftrace/test.d/trigger/trigger-hist-poll.tc b/tools/testing/selftests/ftrace/test.d/trigger/trigger-hist-poll.tc index 8d275e3238d9..04eb8546fc07 100644 --- a/tools/testing/selftests/ftrace/test.d/trigger/trigger-hist-poll.tc +++ b/tools/testing/selftests/ftrace/test.d/trigger/trigger-hist-poll.tc @@ -1,7 +1,7 @@ #!/bin/sh # SPDX-License-Identifier: GPL-2.0 # description: event trigger - test poll wait on histogram -# requires: set_event events/sched/sched_process_free/trigger events/sched/sched_process_free/hist +# requires: set_event events/sched/sched_process_exit/trigger events/sched/sched_process_exit/hist # flags: instance POLL=${FTRACETEST_ROOT}/poll @@ -11,7 +11,7 @@ if [ ! -x ${POLL} ]; then exit_unresolved fi -EVENT=events/sched/sched_process_free/ +EVENT=events/sched/sched_process_exit/ # Check poll ops is supported. Before implementing poll on hist file, it # returns soon with POLLIN | POLLOUT, but not POLLPRI. From 5142c56651578abc346d6c17f3fb919b9ffbb317 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 8 Jul 2026 11:54:58 +0200 Subject: [PATCH 0939/1054] bug: fix warning suppressions with kunit built as module CONFIG_KUNIT is a tristate symbol but the warning suppression code in lib/bug.c is only built if it's built-in due to it using a plain #ifdef, rendering warning suppressions broken for kunit build as loadable module. kunit_is_suppressed_warning() already has a stub for when kunit is disabled so drop that guard entirely. Link: https://lore.kernel.org/r/20260708095459.12111-1-bartosz.golaszewski@oss.qualcomm.com Suggested-by: Albert Esteve Cc: stable@vger.kernel.org Fixes: 85347718ab0d ("bug/kunit: Core support for suppressing warning backtraces") Signed-off-by: Bartosz Golaszewski Reviewed-by: Albert Esteve Reviewed-by: David Gow Signed-off-by: Shuah Khan --- lib/bug.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/bug.c b/lib/bug.c index 292420f45811..7c1c2c27f58e 100644 --- a/lib/bug.c +++ b/lib/bug.c @@ -219,14 +219,12 @@ static enum bug_trap_type __report_bug(struct bug_entry *bug, unsigned long buga no_cut = bug->flags & BUGFLAG_NO_CUT_HERE; has_args = bug->flags & BUGFLAG_ARGS; -#ifdef CONFIG_KUNIT /* * Before the once logic so suppressed warnings do not consume * the single-fire budget of WARN_ON_ONCE(). */ if (warning && kunit_is_suppressed_warning(true)) return BUG_TRAP_TYPE_WARN; -#endif disable_trace_on_warning(); From 422f1d4f141eaa3a6e4199ceec86cc6b9bf26570 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Fri, 10 Jul 2026 18:32:49 +0200 Subject: [PATCH 0940/1054] dm-bufio: fix wrong count calculation in dm_bufio_issue_discard block_to_sector converts a block number to a sector number and adds c->start to the result. It is inappropriate to use this function for converting the number of blocks to a number to sectors because c->start would be incorrectly added to the result. Luckily, the only target that uses dm_bufio_issue_discard is dm-ebs, which sets c->start to 0, so this bug is latent. Signed-off-by: Mikulas Patocka Assisted-by: Claude:claude-opus-4-6 Fixes: 6fbeb0048e6b ("dm bufio: implement discard") Cc: stable@vger.kernel.org --- drivers/md/dm-bufio.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/md/dm-bufio.c b/drivers/md/dm-bufio.c index 26fedf5883ef..a458b9fd2fcd 100644 --- a/drivers/md/dm-bufio.c +++ b/drivers/md/dm-bufio.c @@ -2238,7 +2238,9 @@ int dm_bufio_issue_discard(struct dm_bufio_client *c, sector_t block, sector_t c struct dm_io_region io_reg = { .bdev = c->bdev, .sector = block_to_sector(c, block), - .count = block_to_sector(c, count), + .count = likely(c->sectors_per_block_bits >= 0) ? + count << c->sectors_per_block_bits : + count * (c->block_size >> SECTOR_SHIFT), }; if (WARN_ON_ONCE(dm_bufio_in_request())) From 1917eb2db750ecbdf710f79a8042eaa545a063c7 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Fri, 10 Jul 2026 18:35:49 +0200 Subject: [PATCH 0941/1054] dm-stats: fix merge accounting There were wrong parentheses when setting stats_aux->merged, so that merging was never properly accounted. This commit fixes it. Signed-off-by: Mikulas Patocka Assisted-by: Claude:claude-opus-4-6 Fixes: fd2ed4d25270 ("dm: add statistics support") Cc: stable@vger.kernel.org --- drivers/md/dm-stats.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/md/dm-stats.c b/drivers/md/dm-stats.c index c53cf07ab7b0..e06374a3329e 100644 --- a/drivers/md/dm-stats.c +++ b/drivers/md/dm-stats.c @@ -692,10 +692,8 @@ void dm_stats_account_io(struct dm_stats *stats, unsigned long bi_rw, */ last = raw_cpu_ptr(stats->last); stats_aux->merged = - (bi_sector == (READ_ONCE(last->last_sector) && - ((bi_rw == WRITE) == - (READ_ONCE(last->last_rw) == WRITE)) - )); + bi_sector == READ_ONCE(last->last_sector) && + (bi_rw == WRITE) == (READ_ONCE(last->last_rw) == WRITE); WRITE_ONCE(last->last_sector, end_sector); WRITE_ONCE(last->last_rw, bi_rw); } else From 386df1a57b631c456d14f857cb0c0c2e11c16bef Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Fri, 10 Jul 2026 18:37:15 +0200 Subject: [PATCH 0942/1054] dm-stats: fix dm_jiffies_to_msec64 There were wrong calculations in dm_jiffies_to_msec64 that produced incorrect output when HZ was different from 1000. This commit fixes them. Signed-off-by: Mikulas Patocka Assisted-by: Claude:claude-opus-4-6 Fixes: fd2ed4d25270 ("dm: add statistics support") Cc: stable@vger.kernel.org --- drivers/md/dm-stats.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/md/dm-stats.c b/drivers/md/dm-stats.c index e06374a3329e..5df710061a11 100644 --- a/drivers/md/dm-stats.c +++ b/drivers/md/dm-stats.c @@ -840,10 +840,10 @@ static unsigned long long dm_jiffies_to_msec64(struct dm_stat *s, unsigned long result = jiffies_to_msecs(j & 0x3fffff); if (j >= 1 << 22) { mult = jiffies_to_msecs(1 << 22); - result += (unsigned long long)mult * (unsigned long long)jiffies_to_msecs((j >> 22) & 0x3fffff); + result += (unsigned long long)mult * ((j >> 22) & 0x3fffff); } if (j >= 1ULL << 44) - result += (unsigned long long)mult * (unsigned long long)mult * (unsigned long long)jiffies_to_msecs(j >> 44); + result += (unsigned long long)mult * (unsigned long long)(1 << 22) * (j >> 44); return result; } From 0e2f4ab68a89fad42e0f5a9ff4b740738e7aa1d6 Mon Sep 17 00:00:00 2001 From: Kuba Piecuch Date: Fri, 10 Jul 2026 14:43:41 +0000 Subject: [PATCH 0943/1054] sched_ext: Skip ops.set_weight() for disabled tasks When switching a task's sched_class away from sched_ext, we get the following sequence of events in __sched_setscheduler(): sched_change_begin() switched_from_scx() scx_disable_task(p) ops.disable(p) __setscheduler_params() set_load_weight() reweight_task_scx(p) ops.set_weight(p) p->sched_class = next_class; sched_change_end() ... Notably, ops.set_weight() is called _after_ ops.disable(). This violates the expected semantics of the callbacks, the expectation being that ops.disable() can only be followed by ops.exit_task() or ops.enable(). Skipping the weight adjustment for disabled tasks should be harmless since the weight will be recalculated in scx_enable_task() if the task ever rejoins SCX. Fixes: 637b0682821b ("sched: Fold sched_class::switch{ing,ed}_{to,from}() into the change pattern") Cc: stable@vger.kernel.org # v6.19+ Signed-off-by: Kuba Piecuch Signed-off-by: Tejun Heo --- kernel/sched/ext/ext.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/kernel/sched/ext/ext.c b/kernel/sched/ext/ext.c index aeee44e016ab..e3fa7b2fac9d 100644 --- a/kernel/sched/ext/ext.c +++ b/kernel/sched/ext/ext.c @@ -3967,6 +3967,17 @@ static void reweight_task_scx(struct rq *rq, struct task_struct *p, if (task_dead_and_done(p)) return; + /* + * When switching sched_class away from SCX, reweight_task_scx() + * is called _after_ scx_disable_task(). Skip calling ops.set_weight() + * since the BPF scheduler may have already forgotten the task in + * ops.disable(). + * p->scx.weight will be recalculated in scx_enable_task() if the task + * ever returns to SCX class. + */ + if (scx_get_task_state(p) != SCX_TASK_ENABLED) + return; + p->scx.weight = sched_weight_to_cgroup(scale_load_down(lw->weight)); if (SCX_HAS_OP(sch, set_weight)) SCX_CALL_OP_TASK(sch, set_weight, rq, p, p->scx.weight); From ec082d0b978b5fb4c11205ccce63587ac94c74e1 Mon Sep 17 00:00:00 2001 From: Vincent Donnefort Date: Thu, 9 Jul 2026 17:00:15 +0100 Subject: [PATCH 0944/1054] tracing/remotes: Fix leak in trace_remote_alloc_buffer() error path If page allocation fails in trace_remote_alloc_buffer(), desc->nr_cpus is not yet incremented for the current CPU. As a consequence, on error, half-allocated rb_desc will not be freed in trace_remote_free_buffer(). Increment desc->nr_cpus as soon as the first allocation for the current CPU has succeeded. Link: https://patch.msgid.link/20260709160017.1729517-2-vdonnefort@google.com Fixes: 96e43537af54 ("tracing: Introduce trace remotes") Reported-by: Sashiko Signed-off-by: Vincent Donnefort Signed-off-by: Steven Rostedt --- kernel/trace/trace_remote.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/trace/trace_remote.c b/kernel/trace/trace_remote.c index 2a6cc000ec98..d48042239d58 100644 --- a/kernel/trace/trace_remote.c +++ b/kernel/trace/trace_remote.c @@ -1006,6 +1006,8 @@ int trace_remote_alloc_buffer(struct trace_buffer_desc *desc, size_t desc_size, if (!rb_desc->meta_va) goto err; + desc->nr_cpus++; + for (id = 0; id < nr_pages; id++) { rb_desc->page_va[id] = (unsigned long)__get_free_page(GFP_KERNEL); if (!rb_desc->page_va[id]) @@ -1013,7 +1015,6 @@ int trace_remote_alloc_buffer(struct trace_buffer_desc *desc, size_t desc_size, rb_desc->nr_page_va++; } - desc->nr_cpus++; desc->struct_len += offsetof(struct ring_buffer_desc, page_va); desc->struct_len += struct_size(rb_desc, page_va, rb_desc->nr_page_va); rb_desc = __next_ring_buffer_desc(rb_desc); From d471d4f86e8689ad15309e49da8e5e4e9bb9ce87 Mon Sep 17 00:00:00 2001 From: Vincent Donnefort Date: Thu, 9 Jul 2026 17:00:16 +0100 Subject: [PATCH 0945/1054] tracing/remotes: Fix struct_len in trace_remote_alloc_buffer() Pre-calculate desc->struct_len up-front in trace_remote_alloc_buffer() with trace_buffer_desc_size() to fix double-counting. While at it, use the accessor __first_ring_buffer_desc(). Link: https://patch.msgid.link/20260709160017.1729517-3-vdonnefort@google.com Fixes: 96e43537af54 ("tracing: Introduce trace remotes") Reported-by: Sashiko Signed-off-by: Vincent Donnefort Signed-off-by: Steven Rostedt --- kernel/trace/trace_remote.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/kernel/trace/trace_remote.c b/kernel/trace/trace_remote.c index d48042239d58..0f6ef5c36d84 100644 --- a/kernel/trace/trace_remote.c +++ b/kernel/trace/trace_remote.c @@ -979,27 +979,22 @@ EXPORT_SYMBOL_GPL(trace_remote_free_buffer); int trace_remote_alloc_buffer(struct trace_buffer_desc *desc, size_t desc_size, size_t buffer_size, const struct cpumask *cpumask) { + size_t min_desc_size = trace_buffer_desc_size(buffer_size, cpumask_weight(cpumask)); unsigned int nr_pages = max(DIV_ROUND_UP(buffer_size, PAGE_SIZE), 2UL) + 1; - void *desc_end = desc + desc_size; struct ring_buffer_desc *rb_desc; int cpu, ret = -ENOMEM; - if (desc_size < struct_size(desc, __data, 0)) + if (desc_size < min_desc_size) return -EINVAL; desc->nr_cpus = 0; - desc->struct_len = struct_size(desc, __data, 0); + desc->struct_len = min_desc_size; - rb_desc = (struct ring_buffer_desc *)&desc->__data[0]; + rb_desc = __first_ring_buffer_desc(desc); for_each_cpu(cpu, cpumask) { unsigned int id; - if ((void *)rb_desc + struct_size(rb_desc, page_va, nr_pages) > desc_end) { - ret = -EINVAL; - goto err; - } - rb_desc->cpu = cpu; rb_desc->nr_page_va = 0; rb_desc->meta_va = (unsigned long)__get_free_page(GFP_KERNEL); @@ -1015,8 +1010,6 @@ int trace_remote_alloc_buffer(struct trace_buffer_desc *desc, size_t desc_size, rb_desc->nr_page_va++; } - desc->struct_len += offsetof(struct ring_buffer_desc, page_va); - desc->struct_len += struct_size(rb_desc, page_va, rb_desc->nr_page_va); rb_desc = __next_ring_buffer_desc(rb_desc); } From 601ddaceb861be7eb557278109966320a6f3478c Mon Sep 17 00:00:00 2001 From: Vincent Donnefort Date: Thu, 9 Jul 2026 17:00:17 +0100 Subject: [PATCH 0946/1054] ring-buffer: Allow sparse CPU masks in ring_buffer_desc() No user currently relies on sparse CPU masks, but the descriptor logic already supports them via linear fallback. Remove the arbitrary limitation. Link: https://patch.msgid.link/20260709160017.1729517-4-vdonnefort@google.com Fixes: 2e67fabd8b77 ("ring-buffer: Introduce ring-buffer remotes") Reported-by: Sashiko Signed-off-by: Vincent Donnefort Signed-off-by: Steven Rostedt --- kernel/trace/ring_buffer.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index 12a83d9ffd0a..804ccae694d2 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -2330,10 +2330,7 @@ static struct ring_buffer_desc *ring_buffer_desc(struct trace_buffer_desc *trace size_t len; int i; - if (!trace_desc) - return NULL; - - if (cpu >= trace_desc->nr_cpus) + if (!trace_desc || !trace_desc->nr_cpus) return NULL; end = (struct ring_buffer_desc *)((void *)trace_desc + trace_desc->struct_len); From adea84ee6cdea611146c4251d3c1616f5a09feca Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 3 Jul 2026 23:01:12 -0700 Subject: [PATCH 0947/1054] Input: mms114 - fix multi-touch slot corruption If the touchscreen controller reports a touch ID of 0, the driver calculates the slot ID as touch->id - 1, which underflows to UINT_MAX. This is passed to input_mt_slot() as -1. Since the input core ignores negative slot values, the active slot remains unchanged. The driver then reports the touch coordinates for the previously active slot, corrupting its state. Fix this by rejecting touch reports with ID 0. Fixes: 07b8481d4aff ("Input: add MELFAS mms114 touchscreen driver") Cc: stable@vger.kernel.org Reported-by: sashiko-bot@kernel.org Assisted-by: Antigravity:gemini-3.5-flash Link: https://patch.msgid.link/20260704060115.353049-1-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/mms114.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/touchscreen/mms114.c b/drivers/input/touchscreen/mms114.c index 53ad35d61d47..5cef97246a15 100644 --- a/drivers/input/touchscreen/mms114.c +++ b/drivers/input/touchscreen/mms114.c @@ -165,7 +165,7 @@ static void mms114_process_mt(struct mms114_data *data, struct mms114_touch *tou unsigned int x; unsigned int y; - if (touch->id > MMS114_MAX_TOUCH) { + if (touch->id == 0 || touch->id > MMS114_MAX_TOUCH) { dev_err(&client->dev, "Wrong touch id (%d)\n", touch->id); return; } From 47915e855fb38b42133e31ba917d99565f862154 Mon Sep 17 00:00:00 2001 From: Sandipan Das Date: Fri, 10 Jul 2026 22:04:49 +0530 Subject: [PATCH 0948/1054] perf/x86/amd/brs: Fix kernel address leakage A user-only branch stack can contain branches that originate from the kernel. As a result, kernel addresses are exposed to user space even when PERF_SAMPLE_BRANCH_USER is requested. On AMD processors supporting X86_FEATURE_BRS (Zen 3 only), perf can still report entries such as SYSRET/interrupt returns for which the branch-from addresses are in the kernel. E.g. $ perf record -j any,u -c 4000 -e branch-brs -o - -- \ perf bench syscall basic --loop 1000 | \ perf script -i - -F brstack|tr ' ' '\n'| \ grep -E '0x[89a-f][0-9a-f]{15}' ... 0xffffffff810001c4/0x72e2e32955eb/-/-/-/0//- 0xffffffff810001c4/0x72e2d94a9821/-/-/-/0//- 0xffffffff810001c4/0x72e2d94ffa1b/-/-/-/0//- ... BRS provides no hardware branch filtering, so privilege level filtering is performed entirely in software. However, amd_brs_match_plm() only validates the branch-to address against the requested privilege levels. For branches from the kernel to user space, the branch-from address is left unchecked and is leaked. Extend the software filter to also validate the branch-from address, so that any branch record whose branch-from address is in the kernel is dropped when PERF_SAMPLE_BRANCH_USER is requested. Fixes: 8910075d61a3 ("perf/x86/amd: Enable branch sampling priv level filtering") Reported-by: Sashiko Signed-off-by: Sandipan Das Signed-off-by: Ingo Molnar Cc: stable@vger.kernel.org Cc: Peter Zijlstra Cc: Stephane Eranian Link: https://patch.msgid.link/f05931c4f89a146c364bd5dc6b8170b1ac611c65.1783701239.git.sandipan.das@amd.com Closes: https://lore.kernel.org/all/20260710110235.F3FD81F000E9@smtp.kernel.org/ --- arch/x86/events/amd/brs.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/x86/events/amd/brs.c b/arch/x86/events/amd/brs.c index 06f35a6b58a5..dc564688f3d7 100644 --- a/arch/x86/events/amd/brs.c +++ b/arch/x86/events/amd/brs.c @@ -259,13 +259,13 @@ void amd_brs_disable_all(void) amd_brs_disable(); } -static bool amd_brs_match_plm(struct perf_event *event, u64 to) +static bool amd_brs_match_plm(struct perf_event *event, u64 from, u64 to) { int type = event->attr.branch_sample_type; int plm_k = PERF_SAMPLE_BRANCH_KERNEL | PERF_SAMPLE_BRANCH_HV; int plm_u = PERF_SAMPLE_BRANCH_USER; - if (!(type & plm_k) && kernel_ip(to)) + if (!(type & plm_k) && (kernel_ip(to) || kernel_ip(from))) return 0; if (!(type & plm_u) && !kernel_ip(to)) @@ -338,11 +338,11 @@ void amd_brs_drain(void) */ to = (u64)(((s64)to << shift) >> shift); - if (!amd_brs_match_plm(event, to)) - continue; - rdmsrq(brs_from(brs_idx), from); + if (!amd_brs_match_plm(event, from, to)) + continue; + perf_clear_branch_entry_bitfields(br+nr); br[nr].from = from; From 7410d11460eb90d6c9281162ccc6a128534d897d Mon Sep 17 00:00:00 2001 From: James Raphael Tiovalen Date: Sun, 5 Jul 2026 19:36:29 +0800 Subject: [PATCH 0949/1054] macsec: fix promiscuity refcount leak in macsec_dev_open() When a MACsec interface with IFF_PROMISC set is brought up on top of a device that has hardware offload enabled, macsec_dev_open() first calls dev_set_promiscuity(real_dev, 1) and then propagates the open to the offload device. If that propagation fails, the error path jumps to the clear_allmulti label, which only reverts allmulti and the unicast address. The promiscuity taken on the lower device is never dropped, so real_dev is left permanently stuck in promiscuous mode. Its promiscuity count can no longer be balanced from software. Add a clear_promisc label that drops the promiscuity reference and route the two offload failure paths to it. The dev_set_promiscuity() failure itself still jumps to clear_allmulti, since on that failure the count was not incremented. Fixes: 3cf3227a21d1 ("net: macsec: hardware offloading infrastructure") Cc: stable@vger.kernel.org Signed-off-by: James Raphael Tiovalen Reviewed-by: Sabrina Dubroca Link: https://patch.msgid.link/20260705113629.187490-1-jamestiotio@gmail.com Signed-off-by: Paolo Abeni --- drivers/net/macsec.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c index dd89282f0179..ee0e2eb7dbc6 100644 --- a/drivers/net/macsec.c +++ b/drivers/net/macsec.c @@ -3615,19 +3615,22 @@ static int macsec_dev_open(struct net_device *dev) ops = macsec_get_ops(netdev_priv(dev), &ctx); if (!ops) { err = -EOPNOTSUPP; - goto clear_allmulti; + goto clear_promisc; } ctx.secy = &macsec->secy; err = macsec_offload(ops->mdo_dev_open, &ctx); if (err) - goto clear_allmulti; + goto clear_promisc; } if (netif_carrier_ok(real_dev)) netif_carrier_on(dev); return 0; +clear_promisc: + if (dev->flags & IFF_PROMISC) + dev_set_promiscuity(real_dev, -1); clear_allmulti: if (dev->flags & IFF_ALLMULTI) dev_set_allmulti(real_dev, -1); From 3f1f755366687d051174739fb99f7d560202f60b Mon Sep 17 00:00:00 2001 From: Asim Viladi Oglu Manizada Date: Mon, 6 Jul 2026 09:44:10 +0000 Subject: [PATCH 0950/1054] net: openvswitch: reject oversized nested action attrs Open vSwitch stores generated flow actions as nlattrs, whose nla_len field is u16. Commit a1e64addf3ff ("net: openvswitch: remove misbehaving actions length check") allowed the total sw_flow_actions stream to grow beyond 64 KiB, which is valid, but also removed the last guard preventing a generated nested action attribute from exceeding U16_MAX. An oversized generated container can thus be closed with a truncated nla_len. A later dump or teardown then walks a structurally different stream than the one that was validated. In particular, an oversized nested CLONE/CT action may cause subsequent bytes in the generated stream to be interpreted as independent actions. Keep the larger total-action-stream behavior, but make nested action close reject generated containers that do not fit in nla_len, and return the error through all callers. For recursive SAMPLE, CLONE, DEC_TTL, and CHECK_PKT_LEN builders, trim resource-owning action-list tails in reverse construction order before discarding failed wrappers, so resources copied into the rejected tails are released before the wrappers are removed. Most failed outer wrappers are discarded by truncating actions_len after child resources have been released. CHECK_PKT_LEN also trims its parent after branch resources are gone. SET/TUNNEL close failures unwind their known tun_dst ownership directly, and SET_TO_MASKED has no external ownership and truncates on close failure. Fixes: a1e64addf3ff ("net: openvswitch: remove misbehaving actions length check") Cc: stable@vger.kernel.org Assisted-by: avom-custom-harness:gpt-5.5-qwen3.6-mod-mix Signed-off-by: Asim Viladi Oglu Manizada Reviewed-by: Eelco Chaudron Reviewed-by: Aaron Conole Reviewed-by: Ilya Maximets Link: https://patch.msgid.link/20260706094336.38639-1-manizada@pm.me Signed-off-by: Paolo Abeni --- net/openvswitch/flow_netlink.c | 201 +++++++++++++++++++++++++-------- 1 file changed, 157 insertions(+), 44 deletions(-) diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c index 13052408a132..d8079dee700e 100644 --- a/net/openvswitch/flow_netlink.c +++ b/net/openvswitch/flow_netlink.c @@ -2496,13 +2496,56 @@ static inline int add_nested_action_start(struct sw_flow_actions **sfa, return used; } -static inline void add_nested_action_end(struct sw_flow_actions *sfa, - int st_offset) +static inline int add_nested_action_end(struct sw_flow_actions *sfa, + int st_offset) { - struct nlattr *a = (struct nlattr *) ((unsigned char *)sfa->actions + - st_offset); + struct nlattr *a; + u32 attr_len; - a->nla_len = sfa->actions_len - st_offset; + if (WARN_ON_ONCE(st_offset < 0 || + (u32)st_offset > sfa->actions_len)) + return -EINVAL; + + attr_len = sfa->actions_len - (u32)st_offset; + if (WARN_ON_ONCE(attr_len < NLA_HDRLEN)) + return -EINVAL; + + if (attr_len > U16_MAX) + return -EMSGSIZE; + + a = (struct nlattr *)((u8 *)sfa->actions + st_offset); + a->nla_len = attr_len; + return 0; +} + +/* Free the generated action-list tail at @start and truncate it. + * If @nested, @start points to its containing nlattr header. + */ +static void ovs_nla_trim(struct sw_flow_actions *sfa, int start, bool nested) +{ + const struct nlattr *actions; + u32 len; + + if (start < 0) + return; + + if (WARN_ON_ONCE((u32)start > sfa->actions_len)) + return; + + actions = (const struct nlattr *)((u8 *)sfa->actions + start); + len = sfa->actions_len - (u32)start; + + if (nested) { + if (len < NLA_HDRLEN) + goto out; + + actions = (const struct nlattr *)((u8 *)actions + NLA_HDRLEN); + len -= NLA_HDRLEN; + } + + ovs_nla_free_nested_actions(actions, len); +out: + sfa->actions_len = start; } static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr, @@ -2522,6 +2565,7 @@ static int validate_and_copy_sample(struct net *net, const struct nlattr *attr, const struct nlattr *attrs[OVS_SAMPLE_ATTR_MAX + 1]; const struct nlattr *probability, *actions; const struct nlattr *a; + int actions_start; int rem, start, err; struct sample_arg arg; @@ -2565,18 +2609,27 @@ static int validate_and_copy_sample(struct net *net, const struct nlattr *attr, err = ovs_nla_add_action(sfa, OVS_SAMPLE_ATTR_ARG, &arg, sizeof(arg), log); if (err) - return err; + goto err; + actions_start = (*sfa)->actions_len; err = __ovs_nla_copy_actions(net, actions, key, sfa, eth_type, vlan_tci, mpls_label_count, log, depth + 1); if (err) - return err; + goto err_free; - add_nested_action_end(*sfa, start); + err = add_nested_action_end(*sfa, start); + if (err) + goto err_free; return 0; + +err_free: + ovs_nla_trim(*sfa, actions_start, false); +err: + (*sfa)->actions_len = start; + return err; } static int validate_and_copy_dec_ttl(struct net *net, @@ -2624,18 +2677,31 @@ static int validate_and_copy_dec_ttl(struct net *net, return start; action_start = add_nested_action_start(sfa, OVS_DEC_TTL_ATTR_ACTION, log); - if (action_start < 0) - return action_start; + if (action_start < 0) { + err = action_start; + goto err; + } err = __ovs_nla_copy_actions(net, actions, key, sfa, eth_type, vlan_tci, mpls_label_count, log, depth + 1); if (err) - return err; + goto err_free; - add_nested_action_end(*sfa, action_start); - add_nested_action_end(*sfa, start); + err = add_nested_action_end(*sfa, action_start); + if (err) + goto err_free; + + err = add_nested_action_end(*sfa, start); + if (err) + goto err_free; return 0; + +err_free: + ovs_nla_trim(*sfa, action_start, true); +err: + (*sfa)->actions_len = start; + return err; } static int validate_and_copy_clone(struct net *net, @@ -2646,6 +2712,7 @@ static int validate_and_copy_clone(struct net *net, u32 mpls_label_count, bool log, bool last, u32 depth) { + int actions_start; int start, err; u32 exec; @@ -2661,17 +2728,26 @@ static int validate_and_copy_clone(struct net *net, err = ovs_nla_add_action(sfa, OVS_CLONE_ATTR_EXEC, &exec, sizeof(exec), log); if (err) - return err; + goto err; + actions_start = (*sfa)->actions_len; err = __ovs_nla_copy_actions(net, attr, key, sfa, eth_type, vlan_tci, mpls_label_count, log, depth + 1); if (err) - return err; + goto err_free; - add_nested_action_end(*sfa, start); + err = add_nested_action_end(*sfa, start); + if (err) + goto err_free; return 0; + +err_free: + ovs_nla_trim(*sfa, actions_start, false); +err: + (*sfa)->actions_len = start; + return err; } void ovs_match_init(struct sw_flow_match *match, @@ -2763,20 +2839,20 @@ static int validate_and_copy_set_tun(const struct nlattr *attr, tun_dst = metadata_dst_alloc(key.tun_opts_len, METADATA_IP_TUNNEL, GFP_KERNEL); - if (!tun_dst) - return -ENOMEM; + if (!tun_dst) { + err = -ENOMEM; + goto err; + } err = dst_cache_init(&tun_dst->u.tun_info.dst_cache, GFP_KERNEL); - if (err) { - dst_release((struct dst_entry *)tun_dst); - return err; - } + if (err) + goto err_free_tun_dst; a = __add_action(sfa, OVS_KEY_ATTR_TUNNEL_INFO, NULL, sizeof(*ovs_tun), log); if (IS_ERR(a)) { - dst_release((struct dst_entry *)tun_dst); - return PTR_ERR(a); + err = PTR_ERR(a); + goto err_free_tun_dst; } ovs_tun = nla_data(a); @@ -2797,8 +2873,16 @@ static int validate_and_copy_set_tun(const struct nlattr *attr, ip_tunnel_info_opts_set(tun_info, TUN_METADATA_OPTS(&key, key.tun_opts_len), key.tun_opts_len, dst_opt_type); - add_nested_action_end(*sfa, start); + err = add_nested_action_end(*sfa, start); + if (WARN_ON_ONCE(err)) + goto err_free_tun_dst; + return 0; + +err_free_tun_dst: + dst_release((struct dst_entry *)tun_dst); +err: + (*sfa)->actions_len = start; return err; } @@ -2971,7 +3055,7 @@ static int validate_set(const struct nlattr *a, /* Convert non-masked non-tunnel set actions to masked set actions. */ if (!masked && key_type != OVS_KEY_ATTR_TUNNEL) { - int start, len = key_len * 2; + int err, start, len = key_len * 2; struct nlattr *at; *skip_copy = true; @@ -2983,8 +3067,11 @@ static int validate_set(const struct nlattr *a, return start; at = __add_action(sfa, key_type, NULL, len, log); - if (IS_ERR(at)) - return PTR_ERR(at); + if (IS_ERR(at)) { + err = PTR_ERR(at); + (*sfa)->actions_len = start; + return err; + } memcpy(nla_data(at), nla_data(ovs_key), key_len); /* Key. */ memset(nla_data(at) + key_len, 0xff, key_len); /* Mask. */ @@ -2994,7 +3081,11 @@ static int validate_set(const struct nlattr *a, mask->ipv6_label &= htonl(0x000FFFFF); } - add_nested_action_end(*sfa, start); + err = add_nested_action_end(*sfa, start); + if (WARN_ON_ONCE(err)) { + (*sfa)->actions_len = start; + return err; + } } return 0; @@ -3040,7 +3131,8 @@ static int validate_and_copy_check_pkt_len(struct net *net, const struct nlattr *acts_if_greater, *acts_if_lesser_eq; struct nlattr *a[OVS_CHECK_PKT_LEN_ATTR_MAX + 1]; struct check_pkt_len_arg arg; - int nested_acts_start; + int greater_acts_start = -1; + int lesser_acts_start = -1; int start, err; err = nla_parse_deprecated_strict(a, OVS_CHECK_PKT_LEN_ATTR_MAX, @@ -3075,37 +3167,58 @@ static int validate_and_copy_check_pkt_len(struct net *net, err = ovs_nla_add_action(sfa, OVS_CHECK_PKT_LEN_ATTR_ARG, &arg, sizeof(arg), log); if (err) - return err; + goto err_free; - nested_acts_start = add_nested_action_start(sfa, - OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL, log); - if (nested_acts_start < 0) - return nested_acts_start; + lesser_acts_start = + add_nested_action_start(sfa, + OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL, + log); + if (lesser_acts_start < 0) { + err = lesser_acts_start; + goto err_free; + } err = __ovs_nla_copy_actions(net, acts_if_lesser_eq, key, sfa, eth_type, vlan_tci, mpls_label_count, log, depth + 1); if (err) - return err; + goto err_free; - add_nested_action_end(*sfa, nested_acts_start); + err = add_nested_action_end(*sfa, lesser_acts_start); + if (err) + goto err_free; - nested_acts_start = add_nested_action_start(sfa, - OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER, log); - if (nested_acts_start < 0) - return nested_acts_start; + greater_acts_start = + add_nested_action_start(sfa, + OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER, + log); + if (greater_acts_start < 0) { + err = greater_acts_start; + goto err_free; + } err = __ovs_nla_copy_actions(net, acts_if_greater, key, sfa, eth_type, vlan_tci, mpls_label_count, log, depth + 1); if (err) - return err; + goto err_free; - add_nested_action_end(*sfa, nested_acts_start); - add_nested_action_end(*sfa, start); + err = add_nested_action_end(*sfa, greater_acts_start); + if (err) + goto err_free; + + err = add_nested_action_end(*sfa, start); + if (err) + goto err_free; return 0; + +err_free: + ovs_nla_trim(*sfa, greater_acts_start, true); + ovs_nla_trim(*sfa, lesser_acts_start, true); + ovs_nla_trim(*sfa, start, false); + return err; } static int validate_psample(const struct nlattr *attr) From 4b22d0801fadfcae2e106e6ba32e49439c7c7ebf Mon Sep 17 00:00:00 2001 From: Genjian Zhang Date: Sat, 11 Jul 2026 18:05:26 +0800 Subject: [PATCH 0951/1054] dm thin metadata: fix superblock refcount leak on snapshot shadow failure __reserve_metadata_snap() increments THIN_SUPERBLOCK_LOCATION in the metadata space map before shadowing it. When dm_tm_shadow_block() fails, a reference is leaked in the metadata space map. Fix by adding the missing dm_sm_dec_block(). Signed-off-by: Genjian Zhang Signed-off-by: Mikulas Patocka Fixes: cc8394d86f04 ("dm thin: provide userspace access to pool metadata") Cc: stable@vger.kernel.org --- drivers/md/dm-thin-metadata.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/md/dm-thin-metadata.c b/drivers/md/dm-thin-metadata.c index a010ff1e5c92..e60e1326376a 100644 --- a/drivers/md/dm-thin-metadata.c +++ b/drivers/md/dm-thin-metadata.c @@ -1362,8 +1362,10 @@ static int __reserve_metadata_snap(struct dm_pool_metadata *pmd) dm_sm_inc_block(pmd->metadata_sm, THIN_SUPERBLOCK_LOCATION); r = dm_tm_shadow_block(pmd->tm, THIN_SUPERBLOCK_LOCATION, &sb_validator, ©, &inc); - if (r) + if (r) { + dm_sm_dec_block(pmd->metadata_sm, THIN_SUPERBLOCK_LOCATION); return r; + } BUG_ON(!inc); From 9202ee546b0cd71004eed7598546efe4660097da Mon Sep 17 00:00:00 2001 From: Andrey Albershteyn Date: Thu, 9 Jul 2026 17:24:07 +0200 Subject: [PATCH 0952/1054] xfs: fix null pointer dereference in tracepoint If dfp is not NULL we exit early here, when dfp is NULL it's allocated in xfs_defer_alloc() but not assigned. The tracepoint tries to dereference members of dfp struct. Signed-off-by: Andrey Albershteyn Cc: stable@vger.kernel.org # v6.8 Fixes: 3f3cec031099c3 ("xfs: force small EFIs for reaping btree extents") Reviewed-by: "Darrick J. Wong" Signed-off-by: Carlos Maiolino --- fs/xfs/libxfs/xfs_defer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/xfs/libxfs/xfs_defer.c b/fs/xfs/libxfs/xfs_defer.c index c6909716b041..89501e8bd2f8 100644 --- a/fs/xfs/libxfs/xfs_defer.c +++ b/fs/xfs/libxfs/xfs_defer.c @@ -878,7 +878,7 @@ xfs_defer_add_barrier( if (dfp) return; - xfs_defer_alloc(&tp->t_dfops, &xfs_barrier_defer_type); + dfp = xfs_defer_alloc(&tp->t_dfops, &xfs_barrier_defer_type); trace_xfs_defer_add_item(tp->t_mountp, dfp, NULL); } From f20d61c22bcaf172d6790b6500e3838e532e71c8 Mon Sep 17 00:00:00 2001 From: Jaewon Yang Date: Mon, 13 Jul 2026 02:11:47 +0900 Subject: [PATCH 0953/1054] tpm: Make the TPM character devices non-seekable The TPM character devices expose a sequential command/response interface, but their open handlers leave FMODE_PREAD and FMODE_PWRITE enabled. After a command leaves a response pending, pread(fd, buf, 16, 0x1400) passes 0x1400 as *off to tpm_common_read(). The transfer length is bounded by response_length, but the offset is used unchecked when forming data_buffer + *off. A sufficiently large offset therefore causes an out-of-bounds heap read through copy_to_user() and, if the copy succeeds, an out-of-bounds zero-write through the following memset(). Positional I/O does not provide coherent semantics for this interface. An arbitrary pread offset cannot represent how much of a response has been consumed sequentially. The write callback always stores a command at the start of data_buffer, while pwrite() does not update file->f_pos and can leave the sequential read cursor stale. Call nonseekable_open() from both open handlers. This removes FMODE_PREAD and FMODE_PWRITE, causing positional reads and writes to fail with -ESPIPE before reaching the TPM callbacks, and explicitly marks the files non-seekable. Normal read() and write() continue to use the existing sequential f_pos cursor, leaving the response state machine unchanged. Tested on Linux 6.12 with KASAN and a swtpm TPM2 device: - sequential partial reads returned the complete response - pread() and preadv() with offset 0x1400 returned -ESPIPE - pwrite() and pwritev() with offset zero returned -ESPIPE - the pending response remained intact after the rejected operations - a subsequent normal command/response cycle completed normally - no KASAN report was produced. Fixes: 9488585b21be ("tpm: add support for partial reads") Link: https://lore.kernel.org/all/20260710090217.191289-1-yong010301@gmail.com/ Cc: stable@vger.kernel.org Signed-off-by: Jaewon Yang Signed-off-by: Linus Torvalds --- drivers/char/tpm/tpm-dev.c | 2 +- drivers/char/tpm/tpmrm-dev.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/char/tpm/tpm-dev.c b/drivers/char/tpm/tpm-dev.c index 2779a8738c59..74488f0a7b78 100644 --- a/drivers/char/tpm/tpm-dev.c +++ b/drivers/char/tpm/tpm-dev.c @@ -36,7 +36,7 @@ static int tpm_open(struct inode *inode, struct file *file) tpm_common_open(file, chip, priv, NULL); - return 0; + return nonseekable_open(inode, file); out: clear_bit(0, &chip->is_open); diff --git a/drivers/char/tpm/tpmrm-dev.c b/drivers/char/tpm/tpmrm-dev.c index f48d4d9e179c..19e8f2779265 100644 --- a/drivers/char/tpm/tpmrm-dev.c +++ b/drivers/char/tpm/tpmrm-dev.c @@ -29,7 +29,7 @@ static int tpmrm_open(struct inode *inode, struct file *file) tpm_common_open(file, chip, &priv->priv, &priv->space); - return 0; + return nonseekable_open(inode, file); } static int tpmrm_release(struct inode *inode, struct file *file) From a13c140cc289c0b7b3770bce5b3ad42ab35074aa Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 12 Jul 2026 14:16:39 -0700 Subject: [PATCH 0954/1054] Linux 7.2-rc3 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index b4035d3cef26..bfb47ad8cb9a 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ VERSION = 7 PATCHLEVEL = 2 SUBLEVEL = 0 -EXTRAVERSION = -rc2 +EXTRAVERSION = -rc3 NAME = Baby Opossum Posse # *DOCUMENTATION* From 1572282de6d3377ca8605d48c50df3a8c08468e9 Mon Sep 17 00:00:00 2001 From: Gao Xiang Date: Mon, 13 Jul 2026 16:35:29 +0800 Subject: [PATCH 0955/1054] erofs: hide "cache_strategy=" for plain filesystems "cache_strategy=" is meaningless and confusing on unencoded EROFS filesystems; gate it on compressed images only since it's now possible after commit 7cef3c834194 ("erofs: separate plain and compressed filesystems formally"). Signed-off-by: Gao Xiang --- fs/erofs/super.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/erofs/super.c b/fs/erofs/super.c index c5881bb8d52b..9d8f862f309f 100644 --- a/fs/erofs/super.c +++ b/fs/erofs/super.c @@ -1029,7 +1029,7 @@ static int erofs_show_options(struct seq_file *seq, struct dentry *root) ",user_xattr" : ",nouser_xattr"); if (IS_ENABLED(CONFIG_EROFS_FS_POSIX_ACL)) seq_puts(seq, test_opt(opt, POSIX_ACL) ? ",acl" : ",noacl"); - if (IS_ENABLED(CONFIG_EROFS_FS_ZIP)) + if (IS_ENABLED(CONFIG_EROFS_FS_ZIP) && sbi->available_compr_algs) seq_printf(seq, ",cache_strategy=%s", erofs_param_cache_strategy[opt->cache_strategy].name); if (test_opt(opt, DAX_ALWAYS)) From 47b87f469a35b5ffc81c16eee6b13a9b6c8d55c6 Mon Sep 17 00:00:00 2001 From: Junrui Luo Date: Mon, 1 Jun 2026 15:50:00 +0800 Subject: [PATCH 0956/1054] powerpc/spufs: fix out-of-bounds access in spufs_mem_mmap_access() spufs_mem_mmap_access() computes the local store offset as address - vma->vm_start, but bounds-checks it against vma->vm_end instead of the local store size. On 64-bit, offset is always well below vma->vm_end, so the clamp never fires and len stays unbounded against the LS_SIZE buffer returned by ctx->ops->get_ls(). Reject offsets at or beyond LS_SIZE and clamp len to the remaining space, mirroring the guard already used by spufs_mem_mmap_fault() and spufs_ps_fault(). Fixes: a352894d0705 ("spufs: use new vm_ops->access to allow local state access from gdb") Reported-by: Yuhao Jiang Cc: stable@vger.kernel.org Signed-off-by: Junrui Luo Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/SYBPR01MB7881EE775E8B51C09F5A29E7AF152@SYBPR01MB7881.ausprd01.prod.outlook.com --- arch/powerpc/platforms/cell/spufs/file.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/platforms/cell/spufs/file.c b/arch/powerpc/platforms/cell/spufs/file.c index f6de8c1169d5..de7494748fec 100644 --- a/arch/powerpc/platforms/cell/spufs/file.c +++ b/arch/powerpc/platforms/cell/spufs/file.c @@ -268,10 +268,12 @@ static int spufs_mem_mmap_access(struct vm_area_struct *vma, if (write && !(vma->vm_flags & VM_WRITE)) return -EACCES; + if (offset >= LS_SIZE) + return -EFAULT; if (spu_acquire(ctx)) return -EINTR; - if ((offset + len) > vma->vm_end) - len = vma->vm_end - offset; + if ((offset + len) > LS_SIZE) + len = LS_SIZE - offset; local_store = ctx->ops->get_ls(ctx); if (write) memcpy_toio(local_store + offset, buf, len); From 93b47e66cc6d6c6382d44b44f5e7f6fc3a7b38c3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 9 Jul 2026 11:56:06 +0200 Subject: [PATCH 0957/1054] ALSA: usb-audio: Skip DSD quirk for Musical Fidelity M6s DAC Salvador reported that the recent fix for applying the DSD quirk to Musical Fidelity devices broke for his M6s DAC model (2772:0502). Although this is basically a firmware bug, the model in question is fairly old, and no further firmware update can be expected, so it'd be better to address in the driver side. As an ad hoc workaround, skip the DSD quirk for this device by adding an empty quirk entry of 2772:0502; this essentially skips the later DSD quirk entry by the match with the vendor 2772. Fixes: da3a7efff64e ("ALSA: usb-audio: Update for native DSD support quirks") Reported-by: Salvador Blaya Closes: https://lore.kernel.org/CAOdyq+qFaqCh=tK_wNnA64hv5pQuA1Y09ANxQ=xK8yR-t4mf9Q@mail.gmail.com Tested-by: Salvador Blaya Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260709095614.1418838-1-tiwai@suse.de --- sound/usb/quirks.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 2949a0d2d961..d98825f9697c 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -2463,6 +2463,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_DSD_RAW), DEVICE_FLG(0x2708, 0x0002, /* Audient iD14 */ QUIRK_FLAG_IGNORE_CTL_ERROR), + DEVICE_FLG(0x2772, 0x0502, /* Musical Fidelity M6s DAC */ + 0), /* for avoiding QUIRK_FLAG_DSD_RAW with vendor match */ DEVICE_FLG(0x2912, 0x30c8, /* Audioengine D1 */ QUIRK_FLAG_GET_SAMPLE_RATE), DEVICE_FLG(0x2a70, 0x1881, /* OnePlus Technology (Shenzhen) Co., Ltd. BE02T */ From 07ae060d35900977a93d4a05ec995b06c85c2552 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Thu, 9 Jul 2026 17:12:06 +0100 Subject: [PATCH 0958/1054] ALSA: hda: cs35l56: Fail if wmfw file is missing Fail cs35l56_hda_fw_load() and log an error if it didn't find a wmfw file and the BIOS didn't patch the firmware already. Also remove a section of code from cs35l56_hda_request_firmware_files() that attempted to find a .bin if a .wmfw was not found. The CS35L56 ROM can only provide default audio in SoundWire mode. A wmfw is needed to enable I2S audio. Also none of the customer-specific .bin files are compatible with the ROM firmware. So a .wmfw file is always required. Signed-off-by: Richard Fitzgerald Link: https://patch.msgid.link/20260709161211.686498-1-rf@opensource.cirrus.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/side-codecs/cs35l56_hda.c | 30 +++++++--------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/sound/hda/codecs/side-codecs/cs35l56_hda.c b/sound/hda/codecs/side-codecs/cs35l56_hda.c index a0ea08eb96a9..78c2cf387a00 100644 --- a/sound/hda/codecs/side-codecs/cs35l56_hda.c +++ b/sound/hda/codecs/side-codecs/cs35l56_hda.c @@ -512,20 +512,6 @@ static void cs35l56_hda_request_firmware_files(struct cs35l56_hda *cs35l56, NULL, "bin"); return; } - - /* - * Check for system-specific bin files without wmfw before - * falling back to generic firmware - */ - if (amp_name) - cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename, - base_name, system_name, amp_name, "bin"); - if (!*coeff_firmware) - cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename, - base_name, system_name, NULL, "bin"); - - if (*coeff_firmware) - return; } ret = cs35l56_hda_request_firmware_file(cs35l56, wmfw_firmware, wmfw_filename, @@ -616,13 +602,15 @@ static void cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56) &wmfw_firmware, &wmfw_filename, &coeff_firmware, &coeff_filename); - /* - * If the BIOS didn't patch the firmware a bin file is mandatory to - * enable the ASP· - */ - if (!coeff_firmware && firmware_missing) { - dev_err(cs35l56->base.dev, ".bin file required but not found\n"); - goto err_fw_release; + /* If the BIOS didn't patch the firmware a wmfw and bin file are mandatory */ + if (firmware_missing) { + if (!wmfw_firmware) { + dev_err(cs35l56->base.dev, ".%s file required but not found\n", "wmfw"); + goto err_fw_release; + } else if (!coeff_firmware) { + dev_err(cs35l56->base.dev, ".%s file required but not found\n", "bin"); + goto err_fw_release; + } } mutex_lock(&cs35l56->base.irq_lock); From 9064637fb2a80b43105900a47d414997630e5b6b Mon Sep 17 00:00:00 2001 From: Chen Bowen Date: Fri, 10 Jul 2026 21:34:09 +0800 Subject: [PATCH 0959/1054] ALSA: hda/realtek: Fix speakers on MECHREVO WUJIE Series The internal speakers on the MECHREVO WUJIE Series are silent, while the headphone output works correctly. The BIOS reports NID 0x1b on the Realtek ALC233 codec as unconnected with pin configuration 0x411111f0. However, the pin is connected to an internal speaker. Overriding NID 0x1b with 0x90170150 makes the HDA generic parser detect both 0x14 and 0x1b as speaker outputs and restores internal speaker playback. Add a pin configuration fixup for the affected PCI SSID c011:1d05. Tested on a MECHREVO WUJIE Series laptop with board WUJIE Series-Lark4-LNL and firmware EM_LNL326_V1.0.23. Signed-off-by: Chen Bowen Link: https://patch.msgid.link/20260710133409.16641-1-hicbowen@gmail.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index b47177d734c7..55d699d5afca 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -3942,6 +3942,7 @@ enum { ALC275_FIXUP_DELL_XPS, ALC293_FIXUP_LENOVO_SPK_NOISE, ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY, + ALC233_FIXUP_WUJIE_SPEAKERS, ALC233_FIXUP_LENOVO_L2MH_LOW_ENLED, ALC255_FIXUP_DELL_SPK_NOISE, ALC225_FIXUP_DISABLE_MIC_VREF, @@ -4210,6 +4211,13 @@ static void alc287_fixup_lenovo_yoga_book_9i(struct hda_codec *codec, } static const struct hda_fixup alc269_fixups[] = { + [ALC233_FIXUP_WUJIE_SPEAKERS] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1b, 0x90170150 }, /* internal speaker */ + { } + }, + }, [ALC269_FIXUP_GPIO2] = { .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_gpio2, @@ -7949,6 +7957,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x8086, 0x2080, "Intel NUC 8 Rugged", ALC256_FIXUP_INTEL_NUC8_RUGGED), SND_PCI_QUIRK(0x8086, 0x2081, "Intel NUC 10", ALC256_FIXUP_INTEL_NUC10), SND_PCI_QUIRK(0x8086, 0x3038, "Intel NUC 13", ALC295_FIXUP_CHROME_BOOK), + SND_PCI_QUIRK(0xc011, 0x1d05, "MECHREVO WUJIE Series", ALC233_FIXUP_WUJIE_SPEAKERS), SND_PCI_QUIRK(0xf111, 0x0001, "Framework Laptop", ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE), SND_PCI_QUIRK(0xf111, 0x0006, "Framework Laptop", ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE), SND_PCI_QUIRK(0xf111, 0x0009, "Framework Laptop", ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE), From d35dfb6329accfe1cfa0b57e35214b5cbbe0f9ae Mon Sep 17 00:00:00 2001 From: Damien Laine Date: Sun, 12 Jul 2026 23:37:08 +0200 Subject: [PATCH 0960/1054] ALSA: hda/realtek: Fix speakers on Legion Pro 7 16ARX8H with codec SSID 17aa:38a7 Some units of the Lenovo Legion Pro 7 16ARX8H (82WS) report codec subsystem ID 17aa:38a7 instead of 17aa:38a8. Since only 38a8 has a codec SSID quirk, these machines fall through to the PCI SSID match 17aa:386f (Legion Pro 7i 16IAX7) and get ALC287_FIXUP_CS35L41_I2C_2, which probes the Cirrus amplifiers of the Intel variant. The TI TAS2781 amplifier (ACPI TIAS2781:00) present on this AMD variant is never bound and the internal speakers remain silent. Add a codec SSID quirk for 17aa:38a7 pointing to ALC287_FIXUP_TAS2781_I2C, mirroring the existing 38a8 entry. Tested on a Legion Pro 7 16ARX8H (82WS, BIOS LPCN62WW): with the codec SSID overridden to 17aa:38a8 via the HDA patch loader, the TAS2781 amplifier binds and the internal speakers work. Cc: Signed-off-by: Damien Laine Link: https://patch.msgid.link/20260712213708.1835469-1-damien.laine@gmail.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 55d699d5afca..443bc92c5e4b 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -7807,6 +7807,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { HDA_CODEC_QUIRK(0x17aa, 0x386e, "Legion Y9000X 2022 IAH7", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x17aa, 0x386e, "Yoga Pro 7 14ARP8", ALC285_FIXUP_SPEAKER2_TO_DAC1), HDA_CODEC_QUIRK(0x17aa, 0x38a8, "Legion Pro 7 16ARX8H", ALC287_FIXUP_TAS2781_I2C), /* this must match before PCI SSID 17aa:386f below */ + HDA_CODEC_QUIRK(0x17aa, 0x38a7, "Legion Pro 7 16ARX8H", ALC287_FIXUP_TAS2781_I2C), /* this must match before PCI SSID 17aa:386f below */ SND_PCI_QUIRK(0x17aa, 0x386f, "Legion Pro 7i 16IAX7", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x17aa, 0x3870, "Lenovo Yoga 7 14ARB7", ALC287_FIXUP_YOGA7_14ARB7_I2C), SND_PCI_QUIRK(0x17aa, 0x3874, "Legion 7i 16IAX7", ALC287_FIXUP_CS35L41_I2C_2), From a4876f11aa1d076802676e23f8af500706e780e3 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Thu, 9 Jul 2026 10:46:32 +0200 Subject: [PATCH 0961/1054] pmdomain: mediatek: Fix possible nullptr KP in HWV cleanup/on-check Should probe fail for HW_VOTER type power domains, this driver was unconditionally trying to perform cleanup for DIRECT_CTL domains, but only after checking if the target domain is powered on... with the DIRECT_CTL scpsys_domain_is_on() code again. And there's more: the scpsys_domain_is_on() function is also being unconditionally used in the probe path, for any power domain that has flag MTK_SCPD_KEEP_DEFAULT_OFF! This bug was never experienced by anyone because the HWV domains never failed probe, and because none of those is declared with the aforementioned flag - but it's still something critical. In order to fix this, add a check for MTCMOS Type and, based on that, call the correct functions for an "is on" check, and also do the same for the cleanup path, calling the correct functions for the "power off" action. For the latter, since there's a call to pm_genpd_remove() right before calling power_off, be cautious and add a variation of the power off functions (with a _internal suffix) for those to get a pointer to scpsys_domain instead of one to generic_pm_domain as, even if that's still working, this is way too much fragile and would break at some point. Fixes: 88914db077b6 ("pmdomain: mediatek: Add support for Hardware Voter power domains") Signed-off-by: AngeloGioacchino Del Regno Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson --- drivers/pmdomain/mediatek/mtk-pm-domains.c | 40 ++++++++++++++++++---- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/drivers/pmdomain/mediatek/mtk-pm-domains.c b/drivers/pmdomain/mediatek/mtk-pm-domains.c index e1cfd4223473..f0a6339affd7 100644 --- a/drivers/pmdomain/mediatek/mtk-pm-domains.c +++ b/drivers/pmdomain/mediatek/mtk-pm-domains.c @@ -393,9 +393,8 @@ static int scpsys_hwv_power_on(struct generic_pm_domain *genpd) return ret; }; -static int scpsys_hwv_power_off(struct generic_pm_domain *genpd) +static int scpsys_hwv_power_off_internal(struct scpsys_domain *pd) { - struct scpsys_domain *pd = container_of(genpd, struct scpsys_domain, genpd); const struct scpsys_hwv_domain_data *hwv = pd->hwv_data; struct scpsys *scpsys = pd->scpsys; u32 val; @@ -464,6 +463,13 @@ static int scpsys_hwv_power_off(struct generic_pm_domain *genpd) return ret; }; +static int scpsys_hwv_power_off(struct generic_pm_domain *genpd) +{ + struct scpsys_domain *pd = container_of(genpd, struct scpsys_domain, genpd); + + return scpsys_hwv_power_off_internal(pd); +} + static int scpsys_ctl_pwrseq_on(struct scpsys_domain *pd) { struct scpsys *scpsys = pd->scpsys; @@ -694,9 +700,8 @@ static int scpsys_power_on(struct generic_pm_domain *genpd) return ret; } -static int scpsys_power_off(struct generic_pm_domain *genpd) +static int scpsys_power_off_internal(struct scpsys_domain *pd) { - struct scpsys_domain *pd = container_of(genpd, struct scpsys_domain, genpd); struct scpsys *scpsys = pd->scpsys; bool tmp; int ret; @@ -737,6 +742,13 @@ static int scpsys_power_off(struct generic_pm_domain *genpd) return 0; } +static int scpsys_power_off(struct generic_pm_domain *genpd) +{ + struct scpsys_domain *pd = container_of(genpd, struct scpsys_domain, genpd); + + return scpsys_power_off_internal(pd); +} + static struct generic_pm_domain *scpsys_add_one_domain(struct scpsys *scpsys, struct device_node *node) { @@ -884,7 +896,14 @@ generic_pm_domain *scpsys_add_one_domain(struct scpsys *scpsys, struct device_no * late_init time. */ if (MTK_SCPD_CAPS(pd, MTK_SCPD_KEEP_DEFAULT_OFF)) { - if (scpsys_domain_is_on(pd)) + bool domain_is_on; + + if (scpsys->soc_data->type == SCPSYS_MTCMOS_TYPE_HW_VOTER) + domain_is_on = scpsys_hwv_domain_is_enable_done(pd); + else + domain_is_on = scpsys_domain_is_on(pd); + + if (domain_is_on) dev_warn(scpsys->dev, "%pOF: A default off power domain has been ON\n", node); } else { @@ -973,6 +992,7 @@ static int scpsys_add_subdomain(struct scpsys *scpsys, struct device_node *paren static void scpsys_remove_one_domain(struct scpsys_domain *pd) { + struct scpsys *scpsys = pd->scpsys; int ret; /* @@ -984,8 +1004,14 @@ static void scpsys_remove_one_domain(struct scpsys_domain *pd) dev_err(pd->scpsys->dev, "failed to remove domain '%s' : %d - state may be inconsistent\n", pd->genpd.name, ret); - if (scpsys_domain_is_on(pd)) - scpsys_power_off(&pd->genpd); + + if (scpsys->soc_data->type == SCPSYS_MTCMOS_TYPE_HW_VOTER) { + if (scpsys_hwv_domain_is_enable_done(pd)) + scpsys_hwv_power_off_internal(pd); + } else { + if (scpsys_domain_is_on(pd)) + scpsys_power_off_internal(pd); + } clk_bulk_put(pd->num_clks, pd->clks); clk_bulk_put(pd->num_subsys_clks, pd->subsys_clks); From 9c66085e6dcfb44b70970aa4e323003fc7f2b738 Mon Sep 17 00:00:00 2001 From: Daniel Paziyski Date: Fri, 10 Jul 2026 15:40:54 +0200 Subject: [PATCH 0962/1054] KVM: x86: Fix null pointer deref due to dummy array in trace_kvm_inj_exception() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The trace_kvm_inj_exception tracepoint takes as arguments the exception vector, whether the exception has an error code (and subsequently, the error code), and whether it is being reinjected. Because '0' is a valid error code, KVM uses __print_symbolic() to format the error code as a string to avoid printing the error code entirely if the exception doesn't have an error code (see commit 21d4c575eb4a ("KVM: x86: Print error code in exception injection tracepoint iff valid"). KVM's abuse of __print_symbolic() was all fine and dandy, until commit 754e38d2d1ae ("tracing: Use explicit array size instead of sentinel elements in symbol printing") reworked the printing to avoid terminating the arrays with NULL/0 values, and missed KVM's clever use of not-quite empty array of symbols. BUG: kernel NULL pointer dereference, address: 0000000000000000 #PF: supervisor read access in kernel mode #PF: error_code(0x0000) - not-present page PGD 0 P4D 0 Oops: Oops: 0000 [#1] SMP CPU: 20 UID: 0 PID: 791 Comm: less Not tainted 7.2.0-rc2 #401 PREEMPT Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 0.0.0 02/06/2015 RIP: 0010:strlen+0x0/0x20 Call Trace: trace_seq_puts+0x18/0x80 trace_print_symbols_seq+0x68/0xa0 trace_raw_output_kvm_inj_exception+0x64/0xf0 [kvm] s_show+0x47/0x110 seq_read_iter+0x2a5/0x4c0 seq_read+0xfd/0x130 vfs_read+0xb6/0x330 ? vfs_write+0x2f2/0x3f0 ksys_read+0x61/0xd0 do_syscall_64+0xb7/0x570 entry_SYSCALL_64_after_hwframe+0x4b/0x53 RIP: 0033:0x7ff283714862 Simply drop the dummy array entirely, so that __print_symbolic() generates a truly empty array. Signed-off-by: Daniel Paziyski Fixes: 754e38d2d1ae ("tracing: Use explicit array size instead of sentinel elements in symbol printing") Reviewed-by: Thomas Weißschuh Link: https://patch.msgid.link/20260710134055.16432-1-danielpaziyski@gmail.com [sean: massage changelog, add splat, add Fixes, cc stable] Signed-off-by: Sean Christopherson --- arch/x86/kvm/trace.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/kvm/trace.h b/arch/x86/kvm/trace.h index 0db25bba17f6..93de876c318c 100644 --- a/arch/x86/kvm/trace.h +++ b/arch/x86/kvm/trace.h @@ -490,7 +490,7 @@ TRACE_EVENT(kvm_inj_exception, TP_printk("%s%s%s%s%s", __print_symbolic(__entry->exception, kvm_trace_sym_exc), !__entry->has_error ? "" : " (", - !__entry->has_error ? "" : __print_symbolic(__entry->error_code, { }), + !__entry->has_error ? "" : __print_symbolic(__entry->error_code), !__entry->has_error ? "" : ")", __entry->reinjected ? " [reinjected]" : "") ); From c90164ca0f7036942ba088eb7ea8d3f6c2352020 Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Sat, 4 Jul 2026 16:10:30 -0700 Subject: [PATCH 0963/1054] Bluetooth: qca: fix NVM tag length underflow in TLV parser In the TLV_TYPE_NVM branch of qca_tlv_check_data() the tag loop bound is "while (idx < length - sizeof(struct tlv_type_nvm))". "length" is a signed int from the firmware TLV header and sizeof(struct tlv_type_nvm) is a size_t (12), so "length" is converted to size_t and any firmware-supplied "length" < 12 makes the subtraction wrap to a huge value. The loop body then reads a 12-byte struct tlv_type_nvm past the end of the short vmalloc'd firmware buffer (and the EDL_TAG_ID_* handlers can write past it). Rewrite the bound as "idx + sizeof(struct tlv_type_nvm) <= length"; both operands are non-negative, so it no longer underflows and a "length" too small for one record correctly skips the loop. BUG: KASAN: vmalloc-out-of-bounds in qca_download_firmware.isra.0 (drivers/bluetooth/btqca.c:421) Read of size 2 at addr ffffc900000e5004 by task kworker/u9:0/52 Workqueue: hci0 hci_power_on Call Trace: ... kasan_report (mm/kasan/report.c:595) qca_download_firmware.isra.0 (drivers/bluetooth/btqca.c:421 drivers/bluetooth/btqca.c:617) qca_uart_setup (drivers/bluetooth/btqca.c:948) qca_setup (drivers/bluetooth/hci_qca.c:2029) hci_uart_setup (drivers/bluetooth/hci_ldisc.c:438) hci_dev_open_sync (net/bluetooth/hci_sync.c:5227) hci_power_on (net/bluetooth/hci_core.c:920) process_one_work (kernel/workqueue.c:3322) worker_thread (kernel/workqueue.c:3486) kthread (kernel/kthread.c:436) ret_from_fork (arch/x86/kernel/process.c:158) ret_from_fork_asm (arch/x86/entry/entry_64.S:245) Fixes: 2e4edfa1e2bd ("Bluetooth: qca: add missing firmware sanity checks") Reported-by: Weiming Shi Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Xiang Mei Reported-by: Weiming Shi Reviewed-by: Johan Hovold Acked-by: Bartosz Golaszewski Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btqca.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c index 04ebe290bc78..10c496eaea2c 100644 --- a/drivers/bluetooth/btqca.c +++ b/drivers/bluetooth/btqca.c @@ -415,7 +415,7 @@ static int qca_tlv_check_data(struct hci_dev *hdev, idx = 0; data = tlv->data; - while (idx < length - sizeof(struct tlv_type_nvm)) { + while (idx + sizeof(struct tlv_type_nvm) <= length) { tlv_nvm = (struct tlv_type_nvm *)(data + idx); tag_id = le16_to_cpu(tlv_nvm->tag_id); From 2bf282f8f715f5d05d6f4c49ffb3bd241c5e667e Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Tue, 7 Jul 2026 12:15:18 +0800 Subject: [PATCH 0964/1054] Bluetooth: MGMT: revalidate LOAD_CONN_PARAM queued update MGMT_OP_LOAD_CONN_PARAM queues conn_update_sync() when a single parameter update changes an existing LE central connection. The queued work currently stores a borrowed hci_conn_params entry from hdev->le_conn_params. A later LOAD_CONN_PARAM request can clear disabled parameters and free that entry before hci_cmd_sync_work() runs the queued callback. Do not keep the borrowed hci_conn_params pointer in queued work. Queue the hci_conn instead and hold a reference until the queued callback completes. When the work runs, revalidate that the connection is still present, look up the current hci_conn_params entry, and cancel the update if userspace removed that entry while the work was pending. Copy the interval values from the current params entry under hdev->lock, then drop the lock and keep using hci_le_conn_update_sync() to issue the update. Validation reproduced this kernel report: BUG: KASAN: slab-use-after-free in conn_update_sync+0x2a/0xf0 [bluetooth] Read of size 1 at addr ffff88810c697126 by task kworker/u17:0/377 Workqueue: hci0 hci_cmd_sync_work [bluetooth] Call Trace: dump_stack_lvl+0x66/0xa0 print_report+0xce/0x5f0 kasan_report+0xe0/0x110 conn_update_sync+0x2a/0xf0 [bluetooth] hci_cmd_sync_work+0x187/0x210 [bluetooth] process_one_work+0x4fd/0xbc0 worker_thread+0x2d8/0x570 kthread+0x1ad/0x1f0 ret_from_fork+0x3c9/0x540 ret_from_fork_asm+0x1a/0x30 Allocated by task 466: hci_conn_params_add+0xa6/0x240 [bluetooth] load_conn_param+0x4e1/0x850 [bluetooth] hci_sock_sendmsg+0x96b/0xf80 [bluetooth] Freed by task 474: kfree+0x313/0x590 hci_conn_params_clear_disabled+0x9b/0xc0 [bluetooth] load_conn_param+0x4bf/0x850 [bluetooth] hci_sock_sendmsg+0x96b/0xf80 [bluetooth] Fixes: 0ece498c27d8c ("Bluetooth: MGMT: Make MGMT_OP_LOAD_CONN_PARAM update existing connection") Suggested-by: Luiz Augusto von Dentz Assisted-by: Codex:gpt-5.5 Signed-off-by: Cen Zhang Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/mgmt.c | 44 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 733a4b70e10c..a0a491e5311e 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -7937,14 +7937,36 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev, static int conn_update_sync(struct hci_dev *hdev, void *data) { - struct hci_conn_params *params = data; - struct hci_conn *conn; + struct hci_conn *conn = data; + struct hci_conn_params *params; + struct hci_conn_params local = {}; - conn = hci_conn_hash_lookup_le(hdev, ¶ms->addr, params->addr_type); - if (!conn) - return -ECANCELED; + hci_dev_lock(hdev); - return hci_le_conn_update_sync(hdev, conn, params); + if (!hci_conn_valid(hdev, conn) || conn->role != HCI_ROLE_MASTER) + goto cancel; + + params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type); + if (!params) + goto cancel; + + local.conn_min_interval = params->conn_min_interval; + local.conn_max_interval = params->conn_max_interval; + local.conn_latency = params->conn_latency; + local.supervision_timeout = params->supervision_timeout; + + hci_dev_unlock(hdev); + + return hci_le_conn_update_sync(hdev, conn, &local); + +cancel: + hci_dev_unlock(hdev); + return -ECANCELED; +} + +static void conn_update_sync_destroy(struct hci_dev *hdev, void *data, int err) +{ + hci_conn_put(data); } static int load_conn_param(struct sock *sk, struct hci_dev *hdev, void *data, @@ -8054,9 +8076,13 @@ static int load_conn_param(struct sock *sk, struct hci_dev *hdev, void *data, (conn->le_conn_min_interval != min || conn->le_conn_max_interval != max || conn->le_conn_latency != latency || - conn->le_supv_timeout != timeout)) - hci_cmd_sync_queue(hdev, conn_update_sync, - hci_param, NULL); + conn->le_supv_timeout != timeout)) { + hci_conn_get(conn); + if (hci_cmd_sync_queue(hdev, conn_update_sync, + conn, + conn_update_sync_destroy) < 0) + hci_conn_put(conn); + } } } From 609c5b04a28dc1b0f3af6a7bc93055135b2d2059 Mon Sep 17 00:00:00 2001 From: Laxman Acharya Padhya Date: Fri, 10 Jul 2026 23:10:03 +0545 Subject: [PATCH 0965/1054] Bluetooth: btrtl: validate firmware patch bounds rtlbt_parse_firmware() copies patch_length - 4 bytes before appending the firmware version. A malformed firmware patch shorter than the version field can make this subtraction underflow and turn the copy into an oversized read and write during Bluetooth setup. The existing patch_offset + patch_length check can also wrap on 32-bit architectures. Validate the patch length and range without arithmetic overflow before allocating or copying the patch. Fixes: db33c77dddc2 ("Bluetooth: btrtl: Create separate module for Realtek BT driver") Cc: stable@vger.kernel.org Signed-off-by: Laxman Acharya Padhya Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btrtl.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/bluetooth/btrtl.c b/drivers/bluetooth/btrtl.c index 49ecb18fea45..7f54d2d2d13a 100644 --- a/drivers/bluetooth/btrtl.c +++ b/drivers/bluetooth/btrtl.c @@ -797,8 +797,9 @@ static int rtlbt_parse_firmware(struct hci_dev *hdev, } BT_DBG("length=%x offset=%x index %d", patch_length, patch_offset, i); - min_size = patch_offset + patch_length; - if (btrtl_dev->fw_len < min_size) + if (patch_length < sizeof(epatch_info->fw_version) || + patch_offset > btrtl_dev->fw_len || + patch_length > btrtl_dev->fw_len - patch_offset) return -EINVAL; /* Copy the firmware into a new buffer and write the version at From d5efd6e4b8b0634af6843178fe1a7dd2b2178a3d Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Fri, 10 Jul 2026 11:23:40 +0300 Subject: [PATCH 0966/1054] Bluetooth: hci_sync: extend conn_hash lookup critical sections Using RCU-protected pointers outside the critical sections without refcount is incorrect and may result to UAF. Extend critical section to cover both hci_conn_hash lookup and use of the returned conn. Add surrounding rcu_read_lock() also when return value is not used, in preparation for RCU lockdep requirement to hci_lookup_le_connect(). This avoids concurrent deletion of the conn before we are done dereferencing it. Also, make sure to hold hdev->lock when accessing hdev->accept_list. Fixes: 6d0417e4e1cf ("Bluetooth: hci_conn: Fix not setting conn_timeout for Broadcast Receiver") Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_sync.c | 44 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index c896d4edd013..7a60e34c91b6 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -1054,14 +1054,19 @@ static int hci_set_random_addr_sync(struct hci_dev *hdev, bdaddr_t *rpa) * In this kind of scenario skip the update and let the random * address be updated at the next cycle. */ + rcu_read_lock(); + if (bacmp(&hdev->random_addr, BDADDR_ANY) && (hci_dev_test_flag(hdev, HCI_LE_ADV) || hci_lookup_le_connect(hdev))) { bt_dev_dbg(hdev, "Deferring random address update"); hci_dev_set_flag(hdev, HCI_RPA_EXPIRED); + rcu_read_unlock(); return 0; } + rcu_read_unlock(); + return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_RANDOM_ADDR, 6, rpa, HCI_CMD_TIMEOUT); } @@ -2647,12 +2652,17 @@ static int hci_pause_addr_resolution(struct hci_dev *hdev) /* Cannot disable addr resolution if scanning is enabled or * when initiating an LE connection. */ + rcu_read_lock(); + if (hci_dev_test_flag(hdev, HCI_LE_SCAN) || hci_lookup_le_connect(hdev)) { + rcu_read_unlock(); bt_dev_err(hdev, "Command not allowed when scan/LE connect"); return -EPERM; } + rcu_read_unlock(); + /* Cannot disable addr resolution if advertising is enabled. */ err = hci_pause_advertising_sync(hdev); if (err) { @@ -2790,6 +2800,8 @@ static u8 hci_update_accept_list_sync(struct hci_dev *hdev) if (hci_dev_test_flag(hdev, HCI_PA_SYNC)) { struct hci_conn *conn; + rcu_read_lock(); + conn = hci_conn_hash_lookup_create_pa_sync(hdev); if (conn) { struct conn_params pa; @@ -2799,6 +2811,8 @@ static u8 hci_update_accept_list_sync(struct hci_dev *hdev) bacpy(&pa.addr, &conn->dst); pa.addr_type = conn->dst_type; + rcu_read_unlock(); + /* Clear first since there could be addresses left * behind. */ @@ -2808,6 +2822,8 @@ static u8 hci_update_accept_list_sync(struct hci_dev *hdev) err = hci_le_add_accept_list_sync(hdev, &pa, &num_entries); goto done; + } else { + rcu_read_unlock(); } } @@ -2818,10 +2834,13 @@ static u8 hci_update_accept_list_sync(struct hci_dev *hdev) * the controller. */ list_for_each_entry_safe(b, t, &hdev->le_accept_list, list) { - if (hci_conn_hash_lookup_le(hdev, &b->bdaddr, b->bdaddr_type)) - continue; + rcu_read_lock(); + + if (hci_conn_hash_lookup_le(hdev, &b->bdaddr, b->bdaddr_type)) { + rcu_read_unlock(); + continue; + } - /* Pointers not dereferenced, no locks needed */ pend_conn = hci_pend_le_action_lookup(&hdev->pend_le_conns, &b->bdaddr, b->bdaddr_type); @@ -2829,6 +2848,8 @@ static u8 hci_update_accept_list_sync(struct hci_dev *hdev) &b->bdaddr, b->bdaddr_type); + rcu_read_unlock(); + /* If the device is not likely to connect or report, * remove it from the acceptlist. */ @@ -2955,6 +2976,8 @@ static int hci_le_set_ext_scan_param_sync(struct hci_dev *hdev, u8 type, if (sent) { struct hci_conn *conn; + rcu_read_lock(); + conn = hci_conn_hash_lookup_ba(hdev, PA_LINK, &sent->bdaddr); if (conn) { @@ -2979,8 +3002,12 @@ static int hci_le_set_ext_scan_param_sync(struct hci_dev *hdev, u8 type, phy++; } + rcu_read_unlock(); + if (num_phy) goto done; + } else { + rcu_read_unlock(); } } } @@ -3231,12 +3258,16 @@ int hci_update_passive_scan_sync(struct hci_dev *hdev) /* If there is at least one pending LE connection, we should * keep the background scan running. */ + bool exists; /* If controller is connecting, we should not start scanning * since some controllers are not able to scan and connect at * the same time. */ - if (hci_lookup_le_connect(hdev)) + rcu_read_lock(); + exists = hci_lookup_le_connect(hdev); + rcu_read_unlock(); + if (exists) return 0; bt_dev_dbg(hdev, "start background scanning"); @@ -3454,6 +3485,7 @@ int hci_write_fast_connectable_sync(struct hci_dev *hdev, bool enable) } static bool disconnected_accept_list_entries(struct hci_dev *hdev) + __must_hold(&hdev->lock) { struct bdaddr_list *b; @@ -3494,12 +3526,16 @@ int hci_update_scan_sync(struct hci_dev *hdev) if (hdev->scanning_paused) return 0; + hci_dev_lock(hdev); + if (hci_dev_test_flag(hdev, HCI_CONNECTABLE) || disconnected_accept_list_entries(hdev)) scan = SCAN_PAGE; else scan = SCAN_DISABLED; + hci_dev_unlock(hdev); + if (hci_dev_test_flag(hdev, HCI_DISCOVERABLE)) scan |= SCAN_INQUIRY; From 16cd66443957e4ad42155c6fec401012f600c6f8 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Fri, 10 Jul 2026 11:23:41 +0300 Subject: [PATCH 0967/1054] Bluetooth: mgmt: fix locking in unpair_device/disconnect_sync Dereferencing RCU-protected pointers outside critical sections is invalid and may lead to UAF. Take hdev->lock for hci_conn lookup and hci_abort_conn(). Don't use RCU to ensure the conn is fully initialized at this point. Fixes: 227a0cdf4a028 ("Bluetooth: MGMT: Fix not generating command complete for MGMT_OP_DISCONNECT") Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/mgmt.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index a0a491e5311e..f3437a3fe4a1 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3091,6 +3091,8 @@ static int unpair_device_sync(struct hci_dev *hdev, void *data) struct mgmt_cp_unpair_device *cp = cmd->param; struct hci_conn *conn; + hci_dev_lock(hdev); + if (cp->addr.type == BDADDR_BREDR) conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->addr.bdaddr); @@ -3098,6 +3100,11 @@ static int unpair_device_sync(struct hci_dev *hdev, void *data) conn = hci_conn_hash_lookup_le(hdev, &cp->addr.bdaddr, le_addr_type(cp->addr.type)); + if (conn) + hci_conn_get(conn); + + hci_dev_unlock(hdev); + if (!conn) return 0; @@ -3105,6 +3112,7 @@ static int unpair_device_sync(struct hci_dev *hdev, void *data) * will clean up the connection no matter the error. */ hci_abort_conn(conn, HCI_ERROR_REMOTE_USER_TERM); + hci_conn_put(conn); return 0; } @@ -3252,6 +3260,8 @@ static int disconnect_sync(struct hci_dev *hdev, void *data) struct mgmt_cp_disconnect *cp = cmd->param; struct hci_conn *conn; + hci_dev_lock(hdev); + if (cp->addr.type == BDADDR_BREDR) conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->addr.bdaddr); @@ -3259,6 +3269,11 @@ static int disconnect_sync(struct hci_dev *hdev, void *data) conn = hci_conn_hash_lookup_le(hdev, &cp->addr.bdaddr, le_addr_type(cp->addr.type)); + if (conn) + hci_conn_get(conn); + + hci_dev_unlock(hdev); + if (!conn) return -ENOTCONN; @@ -3266,6 +3281,7 @@ static int disconnect_sync(struct hci_dev *hdev, void *data) * will clean up the connection no matter the error. */ hci_abort_conn(conn, HCI_ERROR_REMOTE_USER_TERM); + hci_conn_put(conn); return 0; } From da55f570191d5d72f10c607a7043b947eb05ea46 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Fri, 10 Jul 2026 11:23:42 +0300 Subject: [PATCH 0968/1054] Bluetooth: mgmt: hold reference for hci_conn in mgmt_pending_cmds Dereferencing RCU-protected pointers outside critical sections is invalid and may lead to UAF. Use of hci_conn in hci_sync callbacks also needs to hold refcount to avoid UAF. Take appropriate locks for hci_conn lookups, and take refcount for hci_conn pointers stored in mgmt_pending_cmd so that the pointer stays valid. When accessing conn->state, ensure hdev->lock is held to avoid data race. Fixes: 7b445e220db9 ("Bluetooth: MGMT: Fix holding hci_conn reference while command is queued") Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/mgmt.c | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index f3437a3fe4a1..23a1aded0ca8 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -7404,6 +7404,9 @@ static void get_conn_info_complete(struct hci_dev *hdev, void *data, int err) rp.max_tx_power = HCI_TX_POWER_INVALID; } + if (conn) + hci_conn_put(conn); + mgmt_cmd_complete(cmd->sk, cmd->hdev->id, MGMT_OP_GET_CONN_INFO, status, &rp, sizeof(rp)); @@ -7418,6 +7421,8 @@ static int get_conn_info_sync(struct hci_dev *hdev, void *data) int err; __le16 handle; + hci_dev_lock(hdev); + /* Make sure we are still connected */ if (cp->addr.type == BDADDR_BREDR) conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, @@ -7425,12 +7430,16 @@ static int get_conn_info_sync(struct hci_dev *hdev, void *data) else conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->addr.bdaddr); - if (!conn || conn->state != BT_CONNECTED) + if (!conn || conn->state != BT_CONNECTED) { + hci_dev_unlock(hdev); return MGMT_STATUS_NOT_CONNECTED; + } - cmd->user_data = conn; + cmd->user_data = hci_conn_get(conn); handle = cpu_to_le16(conn->handle); + hci_dev_unlock(hdev); + /* Refresh RSSI each time */ err = hci_read_rssi_sync(hdev, handle); @@ -7564,6 +7573,9 @@ static void get_clock_info_complete(struct hci_dev *hdev, void *data, int err) } complete: + if (conn) + hci_conn_put(conn); + mgmt_cmd_complete(cmd->sk, cmd->hdev->id, cmd->opcode, status, &rp, sizeof(rp)); @@ -7580,15 +7592,21 @@ static int get_clock_info_sync(struct hci_dev *hdev, void *data) memset(&hci_cp, 0, sizeof(hci_cp)); hci_read_clock_sync(hdev, &hci_cp); + hci_dev_lock(hdev); + /* Make sure connection still exists */ conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->addr.bdaddr); - if (!conn || conn->state != BT_CONNECTED) + if (!conn || conn->state != BT_CONNECTED) { + hci_dev_unlock(hdev); return MGMT_STATUS_NOT_CONNECTED; + } - cmd->user_data = conn; + cmd->user_data = hci_conn_get(conn); hci_cp.handle = cpu_to_le16(conn->handle); hci_cp.which = 0x01; /* Piconet clock */ + hci_dev_unlock(hdev); + return hci_read_clock_sync(hdev, &hci_cp); } From c363202ec841df36421ec280eea3d5f94f556143 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Fri, 10 Jul 2026 11:23:43 +0300 Subject: [PATCH 0969/1054] Bluetooth: hci_sync: hold hdev->lock for hci_conn_params lookups hci_conn_params_lookup requires hdev->lock be held, otherwise the list iteration or param access is not safe. Hold hdev->lock for params lookups in hci_sync. Fixes: c530569adc19 ("Bluetooth: hci_core: Introduce HCI_CONN_FLAG_PAST") Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_sync.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index 7a60e34c91b6..532534bc601c 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -6701,6 +6701,8 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data) if (!hci_dev_test_flag(hdev, HCI_LE_SIMULTANEOUS_ROLES)) hci_pause_advertising_sync(hdev); + hci_dev_lock(hdev); + params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type); if (params) { conn->le_conn_min_interval = params->conn_min_interval; @@ -6714,6 +6716,8 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data) conn->le_supv_timeout = hdev->le_supv_timeout; } + hci_dev_unlock(hdev); + /* If controller is scanning, we stop it since some controllers are * not able to scan and connect at the same time. Also set the * HCI_LE_SCAN_INTERRUPTED flag so that the command complete @@ -7271,13 +7275,13 @@ static void create_pa_complete(struct hci_dev *hdev, void *data, int err) } static int hci_le_past_params_sync(struct hci_dev *hdev, struct hci_conn *conn, - struct hci_conn *acl, struct bt_iso_qos *qos) + u16 acl_handle, struct bt_iso_qos *qos) { struct hci_cp_le_past_params cp; int err; memset(&cp, 0, sizeof(cp)); - cp.handle = cpu_to_le16(acl->handle); + cp.handle = cpu_to_le16(acl_handle); /* An HCI_LE_Periodic_Advertising_Sync_Transfer_Received event is sent * to the Host. HCI_LE_Periodic_Advertising_Report events will be * enabled with duplicate filtering enabled. @@ -7342,16 +7346,28 @@ static int hci_le_pa_create_sync(struct hci_dev *hdev, void *data) * 2. Check if that HCI_CONN_FLAG_PAST has been set which indicates that * user really intended to use PAST. */ + hci_dev_lock(hdev); + le = hci_conn_hash_lookup_le(hdev, &conn->dst, conn->dst_type); if (le) { struct hci_conn_params *params; + hci_conn_flags_t flags = 0; + u16 le_handle = le->handle; params = hci_conn_params_lookup(hdev, &le->dst, le->dst_type); - if (params && params->flags & HCI_CONN_FLAG_PAST) { - err = hci_le_past_params_sync(hdev, conn, le, qos); + if (params) + flags = params->flags; + + hci_dev_unlock(hdev); + + if (flags & HCI_CONN_FLAG_PAST) { + err = hci_le_past_params_sync(hdev, conn, le_handle, + qos); if (!err) goto done; } + } else { + hci_dev_unlock(hdev); } /* SID has not been set listen for HCI_EV_LE_EXT_ADV_REPORT to update From bf587a10c33e5571a299742e45bc18960b9912e7 Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Thu, 9 Jul 2026 14:22:50 +0800 Subject: [PATCH 0970/1054] Bluetooth: hci_qca: Clear memdump state on invalid dump size qca_controller_memdump() allocates qca->qca_memdump before processing the first dump packet. For a sequence-zero packet it then disables IBS, marks memdump collection active, and reads the advertised dump size. If the controller reports a zero dump size, the error path frees the local qca_memdump object and returns without clearing qca->qca_memdump or undoing the collection state. A later memdump work item initializes its local pointer from qca->qca_memdump and skips allocation when that pointer is non-NULL, so it can operate on freed memory. The stale collection and IBS-disabled flags can also leave waiters or later transmit handling blocked behind an aborted dump. Clear the saved pointer and memdump state before returning from the invalid-size path, matching the cleanup used when hci_devcd_init() fails. A static analysis checker reported the stale memdump state, and manual source review confirmed the invalid-size failure path. Fixes: 06d3fdfcdf5c ("Bluetooth: hci_qca: Add qcom devcoredump support") Signed-off-by: Ruoyu Wang Reviewed-by: Paul Menzel Reviewed-by: Zijun Hu Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/hci_qca.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c index b2d1ee3a3d11..1222f97800f4 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -1087,6 +1087,10 @@ static void qca_controller_memdump(struct work_struct *work) if (!(qca_memdump->ram_dump_size)) { bt_dev_err(hu->hdev, "Rx invalid memdump size"); kfree(qca_memdump); + qca->qca_memdump = NULL; + qca->memdump_state = QCA_MEMDUMP_COLLECTED; + clear_and_wake_up_bit(QCA_MEMDUMP_COLLECTION, &qca->flags); + clear_bit(QCA_IBS_DISABLED, &qca->flags); kfree_skb(skb); mutex_unlock(&qca->hci_memdump_lock); return; From c1cec2bbbeb5922d42d28c6af1707c4f3f8647e3 Mon Sep 17 00:00:00 2001 From: Mikhail Gavrilov Date: Fri, 10 Jul 2026 14:47:31 +0500 Subject: [PATCH 0971/1054] Bluetooth: mgmt: Translate HCI reason in Device Disconnected event MGMT_EV_DEVICE_DISCONNECTED carries a reason field which is defined to be one of MGMT_DEV_DISCONN_* (0x00..0x05). hci_disconn_complete_evt() converts the HCI error with hci_to_mgmt_reason(), but two other paths pass the raw HCI error straight through: hci_cs_disconnect() -> cp->reason mgmt_connect_failed() -> status The latter is reached whenever the adapter is powered off or suspended: hci_disconnect_all_sync() aborts every link with HCI_ERROR_REMOTE_POWER_OFF, hci_disconnect_sync() deliberately does not wait for HCI_EV_DISCONN_COMPLETE for that reason, so that hci_abort_conn_sync() finishes the connection off through hci_conn_failed() instead. As a result userspace sees an out of range reason: @ MGMT Event: Device Disconnected (0x000c) plen 8 BR/EDR Address: 8C:A9:6F:2C:51:46 Reason: Reserved (0x15) bluetoothd: btd_bearer_disconnected() Unknown disconnection value: 21 bluetoothd: device_disconnected() Unknown disconnection value: 21 Export hci_to_mgmt_reason() and use it in both places, so that a power off is reported as MGMT_DEV_DISCONN_REMOTE rather than as the raw HCI_ERROR_REMOTE_POWER_OFF (0x15). Fixes: d47da6bd4cfa ("Bluetooth: hci_core: Fix sending MGMT_EV_CONNECT_FAILED") Fixes: 182ee45da083 ("Bluetooth: hci_sync: Rework hci_suspend_notifier") Signed-off-by: Mikhail Gavrilov Signed-off-by: Luiz Augusto von Dentz --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_event.c | 18 +----------------- net/bluetooth/mgmt.c | 19 ++++++++++++++++++- 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 4ca09298e11a..e7133ff87fbf 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -2430,6 +2430,7 @@ void mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key, bool persistent); void mgmt_device_connected(struct hci_dev *hdev, struct hci_conn *conn, u8 *name, u8 name_len); +u8 hci_to_mgmt_reason(u8 err); void mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u8 reason, bool mgmt_connected); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index b6d963ce26d0..741d658e9630 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -2763,7 +2763,7 @@ static void hci_cs_disconnect(struct hci_dev *hdev, u8 status) } mgmt_device_disconnected(hdev, &conn->dst, conn->type, conn->dst_type, - cp->reason, mgmt_conn); + hci_to_mgmt_reason(cp->reason), mgmt_conn); hci_disconn_cfm(conn, cp->reason); @@ -3381,22 +3381,6 @@ static void hci_conn_request_evt(struct hci_dev *hdev, void *data, hci_dev_unlock(hdev); } -static u8 hci_to_mgmt_reason(u8 err) -{ - switch (err) { - case HCI_ERROR_CONNECTION_TIMEOUT: - return MGMT_DEV_DISCONN_TIMEOUT; - case HCI_ERROR_REMOTE_USER_TERM: - case HCI_ERROR_REMOTE_LOW_RESOURCES: - case HCI_ERROR_REMOTE_POWER_OFF: - return MGMT_DEV_DISCONN_REMOTE; - case HCI_ERROR_LOCAL_HOST_TERM: - return MGMT_DEV_DISCONN_LOCAL_HOST; - default: - return MGMT_DEV_DISCONN_UNKNOWN; - } -} - static void hci_disconn_complete_evt(struct hci_dev *hdev, void *data, struct sk_buff *skb) { diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 23a1aded0ca8..1db10e0f617f 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -9908,6 +9908,22 @@ bool mgmt_powering_down(struct hci_dev *hdev) return false; } +u8 hci_to_mgmt_reason(u8 err) +{ + switch (err) { + case HCI_ERROR_CONNECTION_TIMEOUT: + return MGMT_DEV_DISCONN_TIMEOUT; + case HCI_ERROR_REMOTE_USER_TERM: + case HCI_ERROR_REMOTE_LOW_RESOURCES: + case HCI_ERROR_REMOTE_POWER_OFF: + return MGMT_DEV_DISCONN_REMOTE; + case HCI_ERROR_LOCAL_HOST_TERM: + return MGMT_DEV_DISCONN_LOCAL_HOST; + default: + return MGMT_DEV_DISCONN_UNKNOWN; + } +} + void mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u8 reason, bool mgmt_connected) @@ -9969,7 +9985,8 @@ void mgmt_connect_failed(struct hci_dev *hdev, struct hci_conn *conn, u8 status) if (test_and_clear_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) { mgmt_device_disconnected(hdev, &conn->dst, conn->type, - conn->dst_type, status, true); + conn->dst_type, + hci_to_mgmt_reason(status), true); return; } From f52524da7084c1a54683ae9fbc73e93fff19dd64 Mon Sep 17 00:00:00 2001 From: Zhang Heng Date: Mon, 13 Jul 2026 18:03:29 +0800 Subject: [PATCH 0972/1054] ALSA: hda: conexant: Remove mic bias threshold override Remove the mic bias current comparator threshold override (NID 0x1c, verb 0x320, value 0x010) from Conexant codec driver. This override was originally intended to support volume up/down controls on headsets with inline remote controls, but it causes microphone detection failures on some headsets with impedance less than 1k ohm. After consulting with the vendor's engineers, it was confirmed that this setting is board-specific and should be handled by BIOS/firmware rather than the generic codec driver, especially since inline remote support is not currently implemented. Fixes: 7aeb25908648 ("ALSA: hda/conexant: Fix headset auto detect fail in cx8070 and SN6140") Cc: stable@vger.kernel.org Signed-off-by: Zhang Heng Link: https://patch.msgid.link/20260713100329.306892-1-zhangheng@kylinos.cn Signed-off-by: Takashi Iwai --- sound/hda/codecs/conexant.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/sound/hda/codecs/conexant.c b/sound/hda/codecs/conexant.c index 3d92262763f6..40da2832ba66 100644 --- a/sound/hda/codecs/conexant.c +++ b/sound/hda/codecs/conexant.c @@ -162,9 +162,6 @@ static void cx_fixup_headset_recog(struct hda_codec *codec) { unsigned int mic_present; - /* fix some headset type recognize fail issue, such as EDIFIER headset */ - /* set micbias output current comparator threshold from 66% to 55%. */ - snd_hda_codec_write(codec, 0x1c, 0, 0x320, 0x010); /* set OFF voltage for DFET from -1.2V to -0.8V, set headset micbias register * value adjustment trim from 2.2K ohms to 2.0K ohms. */ From 5caf27a2bf7f86f92f03e851d252901b64ed498c Mon Sep 17 00:00:00 2001 From: Lianqin Hu Date: Mon, 13 Jul 2026 12:35:50 +0000 Subject: [PATCH 0973/1054] ALSA: usb-audio: Add delay quirk for iBasso DC-Elite Audio control requests that sets sampling frequency sometimes fail on this card. Adding delay between control messages eliminates that problem. usb 1-1: New USB device found, idVendor=2fc6, idProduct=f0b5 usb 1-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3 usb 1-1: Product: iBasso DC-Elite usb 1-1: Manufacturer: iBasso usb 1-1: SerialNumber: CTUA171130B Signed-off-by: Lianqin Hu Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/TYUPR06MB6217D8FF419F24378196FCEFD2FA2@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 d98825f9697c..41149561aa06 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_GET_CUR_BROKEN), DEVICE_FLG(0x2fc6, 0xf06b, /* MOONDROP Moonriver2 Ti */ QUIRK_FLAG_CTL_MSG_DELAY), + DEVICE_FLG(0x2fc6, 0xf0b5, /* iBasso DC-Elite */ + QUIRK_FLAG_CTL_MSG_DELAY_1M), DEVICE_FLG(0x2fc6, 0xf0b7, /* iBasso DC07 Pro */ QUIRK_FLAG_CTL_MSG_DELAY_1M), DEVICE_FLG(0x30be, 0x0101, /* Schiit Hel */ From 806c00c23e3ce8eae397a40ced536ef88ae4e012 Mon Sep 17 00:00:00 2001 From: Fredric Cover Date: Sat, 11 Jul 2026 19:54:02 -0700 Subject: [PATCH 0974/1054] smb: client: use kvzalloc() for megabyte buffer in simple fallocate Currently in smb3_simple_fallocate_range(), a 1 MB buffer is allocated using kzalloc(). Under heavy memory fragmentation, a contiguous 1 MB block of physical memory (an order-8 allocation) may not be available, causing the allocation to fail. This failure was observed during xfstests generic/013 on a 4GB RAM test machine running fsstress: fsstress: page allocation failure: order:8, mode:0x40dc0(GFP_KERNEL|__GFP_ZERO|__GFP_COMP), nodemask=(null),cpuset=/,mems_allowed=0 Call Trace: dump_stack_lvl+0x5d/0x80 warn_alloc+0x163/0x190 __alloc_pages_slowpath.constprop.0+0x71b/0x12f0 __alloc_frozen_pages_noprof+0x2f6/0x340 alloc_pages_mpol+0xb6/0x170 ___kmalloc_large_node+0xb3/0xd0 __kmalloc_large_noprof+0x1e/0xc0 smb3_simple_falloc.isra.0+0x62b/0x960 cifs_fallocate+0xed/0x180 vfs_fallocate+0x165/0x3c0 __x64_sys_fallocate+0x48/0xa0 do_syscall_64+0xe1/0x640 entry_SYSCALL_64_after_hwframe+0x76/0x7e Node 0 Normal: 3375*4kB ... 7*128kB 0*256kB 0*512kB 0*1024kB 0*2048kB 0*4096kB Since this scratch buffer does not require physically contiguous memory, switch the allocation to kvzalloc(). This retains the performance benefits of kmalloc() under normal conditions, while gracefully falling back to virtually contiguous memory when physical allocation fails. Fixes: 966a3cb7c7db ("cifs: improve fallocate emulation") Cc: stable@vger.kernel.org Signed-off-by: Fredric Cover Tested-by: Fredric Cover Signed-off-by: Steve French --- fs/smb/client/smb2ops.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c index 6bce44b171e4..5f0e268aa644 100644 --- a/fs/smb/client/smb2ops.c +++ b/fs/smb/client/smb2ops.c @@ -3595,7 +3595,7 @@ static int smb3_simple_fallocate_range(unsigned int xid, if (rc) goto out; - buf = kzalloc(1024 * 1024, GFP_KERNEL); + buf = kvzalloc(1024 * 1024, GFP_KERNEL); if (buf == NULL) { rc = -ENOMEM; goto out; @@ -3652,7 +3652,7 @@ static int smb3_simple_fallocate_range(unsigned int xid, out: kfree(out_data); - kfree(buf); + kvfree(buf); return rc; } From 5906d0e82e8e07d756f3ed1abfac8f8cea2c20dc Mon Sep 17 00:00:00 2001 From: Huiwen He Date: Fri, 3 Jul 2026 13:32:55 +0800 Subject: [PATCH 0975/1054] smb/client: refresh allocation size after duplicate extents FSCTL_DUPLICATE_EXTENTS_TO_FILE changes the target file extents on the server, but the client does not refresh the target AllocationSize/i_blocks. Callers can observe or use the wrong st_blocks value immediately after the clone, before a later attribute revalidation corrects it. For example, create a reflinked file with a leading hole: xfs_io -f -c "pwrite -S 0x61 0 64k" src touch dst chmod 600 dst xfs_io -c "reflink src 0 1m 64k" dst mkswap dst swapon dst The file still has a hole after mkswap: /mnt/scratch/dst: [0..7]: allocated [8..2047]: hole [2048..2175]: allocated The server also reports only the allocated ranges: server dst size=1114112 blocks=144 but the client reported EOF-derived blocks: client dst size=1114112 blocks=2176 and swapon succeeded: swapon_result=success /mnt/scratch/dst 1.1M 0B -1 So EOF-derived i_blocks can let a sparse reflinked file pass the CIFS swapfile hole check. Fix this by querying FILE_ALL_INFORMATION on the target handle after a successful duplicate extents request. Update i_blocks from AllocationSize and keep the refreshed target inode attributes valid so a following stat does not immediately revalidate again. If the query fails, mark the cached inode attributes stale so a later getattr can refresh them. This also fixes the xfstests generic/370 regression introduced by the i_blocks accounting change, as tested on a Samba "vfs objects = btrfs" share. Fixes: 99cd0a6eeb6c ("smb/client: do not account EOF extension as allocation") Signed-off-by: Huiwen He Reviewed-by: ChenXiaoSong Signed-off-by: Steve French --- fs/smb/client/cifsfs.c | 10 +++++++--- fs/smb/client/smb2ops.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c index 4df6ca03a8de..66b9104e7ca2 100644 --- a/fs/smb/client/cifsfs.c +++ b/fs/smb/client/cifsfs.c @@ -1490,9 +1490,13 @@ static loff_t cifs_remap_file_range(struct file *src_file, loff_t off, } } - /* force revalidate of size and timestamps of target file now - that target is updated on the server */ - CIFS_I(target_inode)->time = 0; + /* + * On success, duplicate_extents already updated the target inode attrs + * or marked them stale if the refresh failed. On failure, mark attrs + * stale because EOF may have changed before the clone failed. + */ + if (rc) + CIFS_I(target_inode)->time = 0; unlock: /* although unlocking in the reverse order from locking is not strictly necessary here it is a little cleaner to be consistent */ diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c index 5f0e268aa644..9bf57a014744 100644 --- a/fs/smb/client/smb2ops.c +++ b/fs/smb/client/smb2ops.c @@ -2193,10 +2193,14 @@ smb2_duplicate_extents(const unsigned int xid, u64 len, u64 dest_off) { int rc; + int qrc; unsigned int ret_data_len; struct inode *inode; + struct smb2_file_all_info file_inf; struct duplicate_extents_to_file dup_ext_buf; + struct timespec64 ts; struct cifs_tcon *tcon = tlink_tcon(trgtfile->tlink); + u64 asize; /* server fileays advertise duplicate extent support with this flag */ if ((le32_to_cpu(tcon->fsAttrInfo.Attributes) & @@ -2232,6 +2236,32 @@ smb2_duplicate_extents(const unsigned int xid, if (ret_data_len > 0) cifs_dbg(FYI, "Non-zero response length in duplicate extents\n"); + if (rc == 0) { + qrc = SMB2_query_info(xid, tcon, trgtfile->fid.persistent_fid, + trgtfile->fid.volatile_fid, &file_inf); + spin_lock(&inode->i_lock); + if (qrc == 0) { + asize = le64_to_cpu(file_inf.AllocationSize); + CIFS_I(inode)->time = jiffies; + if (file_inf.LastWriteTime) { + ts = cifs_NTtimeToUnix(file_inf.LastWriteTime); + inode_set_mtime_to_ts(inode, ts); + } + if (file_inf.ChangeTime) { + ts = cifs_NTtimeToUnix(file_inf.ChangeTime); + inode_set_ctime_to_ts(inode, ts); + } + if (file_inf.LastAccessTime) { + ts = cifs_NTtimeToUnix(file_inf.LastAccessTime); + inode_set_atime_to_ts(inode, ts); + } + inode->i_blocks = CIFS_INO_BLOCKS(asize); + } else { + CIFS_I(inode)->time = 0; /* force reval */ + } + spin_unlock(&inode->i_lock); + } + duplicate_extents_out: if (rc) trace_smb3_clone_err(xid, srcfile->fid.volatile_fid, From b09ae45d85dc816987a71db9eebc54b0ae288e94 Mon Sep 17 00:00:00 2001 From: Huiwen He Date: Fri, 3 Jul 2026 13:32:56 +0800 Subject: [PATCH 0976/1054] smb/client: handle overlapping allocated ranges in fallocate smb3_simple_fallocate_range() can skip holes when an allocated range returned by the server starts before the current fallocate offset. The skipped hole is not zero-filled, but fallocate still returns success. A later write to that hole may therefore fail with ENOSPC. The function queries allocated ranges so that it can preserve existing contents and write zeroes only into holes. However, the server may return a range that starts before the current fallocate offset. For example, assume the fallocate request is [100, 400) and the only allocated range returned by the server is [0, 200): Request: [100, 400) Server range: [ 0, 200) allocated Correct: [100, 200) allocated data, skip [200, 400) hole, zero-fill Current: [100, 300) skipped [300, 400) zero-filled afterwards The current code adds the full server range length, 200, to the current offset 100 and moves to 300. As a result, the hole in [200, 300) is skipped without being zero-filled. Fix this by advancing only over the part of the allocated range that overlaps the current fallocate offset. Ignore ranges that end before the current offset and reject ranges whose end offset overflows. This also prevents a malformed range length from causing an out-of-bounds zero-buffer read. Fixes: 966a3cb7c7db ("cifs: improve fallocate emulation") Signed-off-by: Huiwen He Reviewed-by: ChenXiaoSong Signed-off-by: Steve French --- fs/smb/client/smb2ops.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c index 9bf57a014744..ddb28464ff81 100644 --- a/fs/smb/client/smb2ops.c +++ b/fs/smb/client/smb2ops.c @@ -3611,6 +3611,7 @@ static int smb3_simple_fallocate_range(unsigned int xid, struct file_allocated_range_buffer in_data, *out_data = NULL, *tmp_data; u32 out_data_len; char *buf = NULL; + u64 range_start, range_len, range_end; loff_t l; int rc; @@ -3647,13 +3648,21 @@ static int smb3_simple_fallocate_range(unsigned int xid, goto out; } - if (off < le64_to_cpu(tmp_data->file_offset)) { + range_start = le64_to_cpu(tmp_data->file_offset); + range_len = le64_to_cpu(tmp_data->length); + if (check_add_overflow(range_start, range_len, &range_end) || + range_end > S64_MAX) { + rc = -EINVAL; + goto out; + } + + if (off < range_start) { /* * We are at a hole. Write until the end of the region * or until the next allocated data, * whichever comes next. */ - l = le64_to_cpu(tmp_data->file_offset) - off; + l = range_start - off; if (len < l) l = len; rc = smb3_simple_fallocate_write_range(xid, tcon, @@ -3670,11 +3679,13 @@ static int smb3_simple_fallocate_range(unsigned int xid, * until the end of the data or the end of the region * we are supposed to fallocate, whichever comes first. */ - l = le64_to_cpu(tmp_data->length); - if (len < l) - l = len; - off += l; - len -= l; + if (off < range_end) { + l = range_end - off; + if (len < l) + l = len; + off += l; + len -= l; + } tmp_data = &tmp_data[1]; out_data_len -= sizeof(struct file_allocated_range_buffer); From 9e4ec3be67af41ab859302d7109b34976efd9258 Mon Sep 17 00:00:00 2001 From: Huiwen He Date: Fri, 3 Jul 2026 13:32:57 +0800 Subject: [PATCH 0977/1054] smb/client: reduce fallocate zero buffer allocation The fallocate emulation allocates a 1 MiB zero-filled buffer even though each SMB2_write request is limited to SMB2_MAX_BUFFER_SIZE, which is 64 KiB. A high-order 1 MiB allocation is more likely to fail on a fragmented system. Allocate only the smaller of the requested range and SMB2_MAX_BUFFER_SIZE, and reuse that zero-filled buffer for every write request. Also reject a successful write that makes no progress to avoid looping indefinitely. This reduces the contiguous allocation required by fallocate emulation without changing the written data or range semantics. Signed-off-by: Huiwen He Reviewed-by: ChenXiaoSong Signed-off-by: Steve French --- fs/smb/client/smb2ops.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c index ddb28464ff81..ce607915a483 100644 --- a/fs/smb/client/smb2ops.c +++ b/fs/smb/client/smb2ops.c @@ -3573,7 +3573,7 @@ static int smb3_simple_fallocate_write_range(unsigned int xid, char *buf) { struct cifs_io_parms io_parms = {0}; - int nbytes; + unsigned int nbytes; int rc = 0; struct kvec iov[2]; @@ -3594,9 +3594,10 @@ static int smb3_simple_fallocate_write_range(unsigned int xid, rc = SMB2_write(xid, &io_parms, &nbytes, iov, 1); if (rc) break; + if (!nbytes) + return -EIO; if (nbytes > len) return -EINVAL; - buf += nbytes; off += nbytes; len -= nbytes; } @@ -3626,7 +3627,7 @@ static int smb3_simple_fallocate_range(unsigned int xid, if (rc) goto out; - buf = kvzalloc(1024 * 1024, GFP_KERNEL); + buf = kvzalloc(min_t(loff_t, len, SMB2_MAX_BUFFER_SIZE), GFP_KERNEL); if (buf == NULL) { rc = -ENOMEM; goto out; From 7a06d3b816d73448b4e38b83d65049f090b7b201 Mon Sep 17 00:00:00 2001 From: Huiwen He Date: Fri, 3 Jul 2026 13:32:58 +0800 Subject: [PATCH 0978/1054] smb/client: emulate small EOF-extending mode 0 fallocate ranges When a mode 0 fallocate extends EOF from 1G to 2G + 1M, the client currently sends SetEOF for 2G + 1M. This can make fallocate return success without allocating the requested range, or allocate extra space before that range. For example, on a fresh file: xfs_io -f \ -c "falloc 0 1G" \ -c "falloc 2G 1M" \ -c "truncate 3G" test The second fallocate should allocate [2G, 2G + 1M), leaving [1G, 2G) as a hole. Before this change, the result depended on the server allocation policy. With Samba "strict allocate = no", SetEOF could return success without allocating [2G, 2G + 1M). With "strict allocate = yes": # filefrag -v test [0, 1G) allocated [1G, 2G) allocated unexpectedly [2G, 2G + 1M) allocated SMB cannot allocate that arbitrary range, so write zeroes to small EOF-extending ranges instead. Limit this to 1 MiB to bound the client-side I/O cost. With "strict allocate = no", the requested range [2G, 2G + 1M) is allocated by the writes. With "strict allocate = yes": # filefrag -v test [0, 1G) allocated [1G, 2G) hole [2G, 2G + 1M) allocated This fixes the small EOF-extending range case exercised by generic/213. Signed-off-by: Huiwen He Reviewed-by: ChenXiaoSong Signed-off-by: Steve French --- fs/smb/client/smb2ops.c | 69 +++++++++++++++++++++++++++++++++++------ 1 file changed, 60 insertions(+), 9 deletions(-) diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c index ce607915a483..3e32ad18e6c2 100644 --- a/fs/smb/client/smb2ops.c +++ b/fs/smb/client/smb2ops.c @@ -3610,12 +3610,25 @@ static int smb3_simple_fallocate_range(unsigned int xid, loff_t off, loff_t len) { struct file_allocated_range_buffer in_data, *out_data = NULL, *tmp_data; + struct inode *inode = d_inode(cfile->dentry); u32 out_data_len; char *buf = NULL; u64 range_start, range_len, range_end; loff_t l; int rc; + buf = kvzalloc(min_t(loff_t, len, SMB2_MAX_BUFFER_SIZE), GFP_KERNEL); + if (!buf) { + rc = -ENOMEM; + goto out; + } + + if (off >= i_size_read(inode)) { + rc = smb3_simple_fallocate_write_range(xid, tcon, cfile, + off, len, buf); + goto out; + } + in_data.file_offset = cpu_to_le64(off); in_data.length = cpu_to_le64(len); rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, @@ -3627,12 +3640,6 @@ static int smb3_simple_fallocate_range(unsigned int xid, if (rc) goto out; - buf = kvzalloc(min_t(loff_t, len, SMB2_MAX_BUFFER_SIZE), GFP_KERNEL); - if (buf == NULL) { - rc = -ENOMEM; - goto out; - } - tmp_data = out_data; while (len) { /* @@ -3707,18 +3714,22 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon, struct cifsFileInfo *cfile = file->private_data; long rc = -EOPNOTSUPP; unsigned int xid; - loff_t new_eof; + loff_t old_eof, new_eof; + struct smb2_file_all_info file_inf; + u64 asize; + int qrc; xid = get_xid(); inode = d_inode(cfile->dentry); cifsi = CIFS_I(inode); + old_eof = i_size_read(inode); trace_smb3_falloc_enter(xid, cfile->fid.persistent_fid, tcon->tid, tcon->ses->Suid, off, len); /* if file not oplocked can't be sure whether asking to extend size */ if (!CIFS_CACHE_READ(cifsi)) - if (keep_size == false) { + if (!keep_size) { trace_smb3_falloc_err(xid, cfile->fid.persistent_fid, tcon->tid, tcon->ses->Suid, off, len, rc); free_xid(xid); @@ -3728,11 +3739,51 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon, /* * Extending the file */ - if ((keep_size == false) && i_size_read(inode) < off + len) { + if (!keep_size && old_eof < off + len) { rc = inode_newsize_ok(inode, off + len); if (rc) goto out; + /* + * A small range at or beyond EOF can be allocated by writing + * zeroes. For off > old_eof, this preserves the intervening + * hole instead of allocating from offset 0. + */ + if (off > old_eof || + (off == old_eof && old_eof != 0 && + (cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE))) { + if (len > 1024 * 1024) { + rc = -EOPNOTSUPP; + goto out; + } + + rc = smb3_simple_fallocate_range(xid, tcon, cfile, + off, len); + if (rc) { + spin_lock(&inode->i_lock); + cifsi->time = 0; + spin_unlock(&inode->i_lock); + goto out; + } + + new_eof = off + len; + netfs_resize_file(&cifsi->netfs, new_eof, true); + cifs_setsize(inode, new_eof); + + qrc = SMB2_query_info(xid, tcon, + cfile->fid.persistent_fid, + cfile->fid.volatile_fid, &file_inf); + spin_lock(&inode->i_lock); + if (qrc == 0) { + asize = le64_to_cpu(file_inf.AllocationSize); + inode->i_blocks = CIFS_INO_BLOCKS(asize); + } else { + cifsi->time = 0; + } + spin_unlock(&inode->i_lock); + goto out; + } + if (cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE) smb2_set_sparse(xid, tcon, cfile, inode, false); From 5bd1d3dcc25a51663b3878cbc506e16be15af354 Mon Sep 17 00:00:00 2001 From: Huiwen He Date: Fri, 3 Jul 2026 13:32:59 +0800 Subject: [PATCH 0979/1054] smb/client: refresh allocation after EOF-extending fallocate Before this change, xfstests generic/496 was not supported on ksmbd: generic/496 ... [not run] fallocated swap not supported here ksmbd handles SetEOF as truncate, so EOF extension alone does not allocate backing blocks. A fallocated swapfile can therefore still look sparse to swapon. Request allocation for EOF-extending fallocate ranges that can be represented by FILE_ALLOCATION_INFORMATION, and refresh the allocation state afterwards. With this change, xfstests generic/496 and generic/701 pass on ksmbd. However, Samba "strict allocate = no" now exposes the real generic/701 failure: the old pass came from inflated local i_blocks, not from server allocation. generic/213 also fails in that case because an oversized allocation request may not return ENOSPC. Signed-off-by: Huiwen He Reviewed-by: ChenXiaoSong Signed-off-by: Steve French --- fs/smb/client/smb2ops.c | 43 ++++++++++++++++++++++++++++++++++++--- fs/smb/client/smb2pdu.c | 19 +++++++++++++++++ fs/smb/client/smb2proto.h | 3 +++ fs/smb/common/fscc.h | 5 +++++ fs/smb/server/smb2pdu.h | 4 ---- 5 files changed, 67 insertions(+), 7 deletions(-) diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c index 3e32ad18e6c2..05f2ab6d345a 100644 --- a/fs/smb/client/smb2ops.c +++ b/fs/smb/client/smb2ops.c @@ -3788,12 +3788,49 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon, smb2_set_sparse(xid, tcon, cfile, inode, false); new_eof = off + len; + + qrc = SMB2_query_info(xid, tcon, + cfile->fid.persistent_fid, + cfile->fid.volatile_fid, &file_inf); + if (qrc == 0) + asize = le64_to_cpu(file_inf.AllocationSize); + + /* + * FILE_ALLOCATION_INFORMATION can only describe allocation up to + * new_eof. Some servers may accept it without allocating blocks, + * so refresh AllocationSize before updating i_blocks. + */ + if (off == 0 || off == old_eof) { + if (qrc || asize < new_eof) { + rc = SMB2_set_allocation(xid, tcon, + cfile->fid.persistent_fid, + cfile->fid.volatile_fid, + cfile->pid, new_eof); + if (rc) + goto out; + } + } + rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid, cfile->fid.volatile_fid, cfile->pid, new_eof); - if (rc == 0) { - netfs_resize_file(&cifsi->netfs, new_eof, true); - cifs_setsize(inode, new_eof); + if (rc) + goto out; + + netfs_resize_file(&cifsi->netfs, new_eof, true); + cifs_setsize(inode, new_eof); + + qrc = SMB2_query_info(xid, tcon, + cfile->fid.persistent_fid, + cfile->fid.volatile_fid, &file_inf); + spin_lock(&inode->i_lock); + if (qrc == 0) { + asize = le64_to_cpu(file_inf.AllocationSize); + if (asize >= new_eof) + inode->i_blocks = CIFS_INO_BLOCKS(asize); + } else { + cifsi->time = 0; } + spin_unlock(&inode->i_lock); goto out; } diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index 8f83ab377db1..4ce165e40657 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -5949,6 +5949,25 @@ SMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, 0, 1, &data, &size); } +int +SMB2_set_allocation(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, u32 pid, + loff_t allocation_size) +{ + struct smb2_file_alloc_info info; + void *data; + unsigned int size; + + info.AllocationSize = cpu_to_le64(allocation_size); + + data = &info; + size = sizeof(struct smb2_file_alloc_info); + + return send_set_info(xid, tcon, persistent_fid, volatile_fid, + pid, FILE_ALLOCATION_INFORMATION, SMB2_O_INFO_FILE, + 0, 1, &data, &size); +} + int SMB2_set_acl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid, diff --git a/fs/smb/client/smb2proto.h b/fs/smb/client/smb2proto.h index e01effe45ae2..2e9f70096825 100644 --- a/fs/smb/client/smb2proto.h +++ b/fs/smb/client/smb2proto.h @@ -204,6 +204,9 @@ void SMB2_query_directory_free(struct smb_rqst *rqst); int SMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid, u32 pid, loff_t new_eof); +int SMB2_set_allocation(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, u32 pid, + loff_t allocation_size); int SMB2_set_info_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server, struct smb_rqst *rqst, u64 persistent_fid, u64 volatile_fid, u32 pid, u8 info_class, u8 info_type, diff --git a/fs/smb/common/fscc.h b/fs/smb/common/fscc.h index 859849a42fec..941db5a95564 100644 --- a/fs/smb/common/fscc.h +++ b/fs/smb/common/fscc.h @@ -283,6 +283,11 @@ struct smb2_file_eof_info { /* encoding of request for level 10 */ __le64 EndOfFile; /* new end of file value */ } __packed; /* level 20 Set */ +/* See MS-FSCC 2.4.4 */ +struct smb2_file_alloc_info { /* encoding of request for level 19 */ + __le64 AllocationSize; +} __packed; + /* See MS-FSCC 2.4.15 */ typedef struct { __le32 NextEntryOffset; diff --git a/fs/smb/server/smb2pdu.h b/fs/smb/server/smb2pdu.h index c2512dbcdec8..aa06c8c905f1 100644 --- a/fs/smb/server/smb2pdu.h +++ b/fs/smb/server/smb2pdu.h @@ -212,10 +212,6 @@ struct smb2_file_ea_info { __le32 EASize; } __packed; -struct smb2_file_alloc_info { - __le64 AllocationSize; -} __packed; - struct smb2_file_disposition_info { __u8 DeletePending; } __packed; From 8986c932905ea508d66da421eb2eb6e676ace1fe Mon Sep 17 00:00:00 2001 From: Shoichiro Miyamoto Date: Sat, 11 Jul 2026 22:33:26 +0900 Subject: [PATCH 0980/1054] smb: client: reject overlapping data areas in SMB2 responses Commit 53b7c271f06b ("smb: client: restrict implied bcc[0] exemption to responses without data area") restricted the implied bcc[0] length exception to responses without a data area. However, the overlap handling in __smb2_calc_size() clears data_length, which can make an invalid response appear to have no data area and so qualify for the exception. Track data area overlap separately and reject such responses before applying the length compatibility exceptions. Fixes: 53b7c271f06b ("smb: client: restrict implied bcc[0] exemption to responses without data area") Cc: stable@vger.kernel.org Signed-off-by: Shoichiro Miyamoto Signed-off-by: Steve French --- fs/smb/client/smb2misc.c | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/fs/smb/client/smb2misc.c b/fs/smb/client/smb2misc.c index 6270b33147d2..9068175e57cd 100644 --- a/fs/smb/client/smb2misc.c +++ b/fs/smb/client/smb2misc.c @@ -19,7 +19,8 @@ #include "nterr.h" #include "cached_dir.h" -static unsigned int __smb2_calc_size(void *buf, bool *have_data); +static unsigned int __smb2_calc_size(void *buf, bool *have_data, + bool *data_area_overlap); static int check_smb2_hdr(struct smb2_hdr *shdr, __u64 mid) @@ -148,6 +149,7 @@ smb2_check_message(char *buf, unsigned int pdu_len, unsigned int len, __u32 calc_len; /* calculated length */ __u64 mid; bool have_data; + bool data_area_overlap; /* If server is a channel, select the primary channel */ pserver = SERVER_IS_CHAN(server) ? server->primary_server : server; @@ -232,7 +234,12 @@ smb2_check_message(char *buf, unsigned int pdu_len, unsigned int len, } have_data = false; - calc_len = __smb2_calc_size(buf, &have_data); + data_area_overlap = false; + calc_len = __smb2_calc_size(buf, &have_data, &data_area_overlap); + + /* Reject responses whose data area overlaps the fixed area. */ + if (data_area_overlap) + return 1; /* For SMB2_IOCTL, OutputOffset and OutputLength are optional, so might * be 0, and not a real miscalculation */ @@ -416,14 +423,15 @@ smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *shdr) } /* - * Calculate the size of the SMB message based on the fixed header - * portion, the number of word parameters and the data portion of the message. - * If have_data is non-NULL, it is set to true when a non-empty data area was - * found (data_length > 0), allowing callers to distinguish the implied bcc[0] - * case (no data area) from an overreported data length. + * Calculate the size of the SMB message based on the fixed header, fixed + * parameter area, and variable data area. + * + * If have_data is not NULL, it is set when a non-empty data area is found. + * If data_area_overlap is not NULL, it is set when the data area overlaps + * the fixed area. */ static unsigned int -__smb2_calc_size(void *buf, bool *have_data) +__smb2_calc_size(void *buf, bool *have_data, bool *data_area_overlap) { struct smb2_pdu *pdu = buf; struct smb2_hdr *shdr = &pdu->hdr; @@ -432,6 +440,11 @@ __smb2_calc_size(void *buf, bool *have_data) /* Structure Size has already been checked to make sure it is 64 */ int len = le16_to_cpu(shdr->StructureSize); + if (have_data) + *have_data = false; + if (data_area_overlap) + *data_area_overlap = false; + /* * StructureSize2, ie length of fixed parameter area has already * been checked to make sure it is the correct length. @@ -454,7 +467,10 @@ __smb2_calc_size(void *buf, bool *have_data) if (offset + 1 < len) { cifs_dbg(VFS, "data area offset %d overlaps SMB2 header %d\n", offset + 1, len); + if (data_area_overlap) + *data_area_overlap = true; data_length = 0; + goto calc_size_exit; } else { len = offset + data_length; } @@ -469,7 +485,7 @@ __smb2_calc_size(void *buf, bool *have_data) unsigned int smb2_calc_size(void *buf) { - return __smb2_calc_size(buf, NULL); + return __smb2_calc_size(buf, NULL, NULL); } /* Note: caller must free return buffer */ From 6fa6ee724d8dadf392139e242ac936b5da730c4b Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Fri, 10 Jul 2026 18:04:22 +0200 Subject: [PATCH 0981/1054] arm64: dts: renesas: ironhide: Describe inline ECC carveouts The DBSC5 DRAM controller protects DRAM content using inline ECC. The inline ECC utilizes areas of DRAM for its operation, which are in the DRAM address range, but must not be accessed or modified. Describe the inline ECC carveout areas used by the DBSC5 controller on this hardware as reserved-memory, which must not be accessed. Include DRAM areas which are unprotected by ECC as well, those are parts of the DRAM which directly precede the ECC carveout. In case of high DRAM utilization, unless the inline ECC carveouts are properly reserved, Linux may use and corrupt the memory used by the DBSC5 DRAM controller for inline ECC, which would lead to the system becoming unstable. Fixes: ad142a4ef710 ("arm64: dts: renesas: r8a78000: Add initial Ironhide board support") Cc: stable@vger.kernel.org Signed-off-by: Marek Vasut Tested-by: Geert Uytterhoeven Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260710160450.64967-1-marek.vasut+renesas@mailbox.org Signed-off-by: Geert Uytterhoeven --- .../boot/dts/renesas/r8a78000-ironhide.dts | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/arch/arm64/boot/dts/renesas/r8a78000-ironhide.dts b/arch/arm64/boot/dts/renesas/r8a78000-ironhide.dts index d2b3fc08954a..0ab303863155 100644 --- a/arch/arm64/boot/dts/renesas/r8a78000-ironhide.dts +++ b/arch/arm64/boot/dts/renesas/r8a78000-ironhide.dts @@ -107,6 +107,47 @@ tee@8c400000 { reg = <0x0 0x8c400000 0x0 0x02000000>; no-map; }; + + /* DRAM controller inline ECC areas */ + ecc@10cccc0000 { + reg = <0x10 0xcccc0000 0x0 0x33340000>; + no-map; + }; + + ecc@12cccc0000 { + reg = <0x12 0xcccc0000 0x0 0x33340000>; + no-map; + }; + + ecc@14cccc0000 { + reg = <0x14 0xcccc0000 0x0 0x33340000>; + no-map; + }; + + ecc@16cccc0000 { + reg = <0x16 0xcccc0000 0x0 0x33340000>; + no-map; + }; + + ecc@18cccc0000 { + reg = <0x18 0xcccc0000 0x0 0x33340000>; + no-map; + }; + + ecc@1a66660000 { + reg = <0x1a 0x66660000 0x0 0x999a0000>; + no-map; + }; + + ecc@1c66660000 { + reg = <0x1c 0x66660000 0x0 0x999a0000>; + no-map; + }; + + ecc@1e66660000 { + reg = <0x1e 0x66660000 0x0 0x999a0000>; + no-map; + }; }; }; From 79adf48fb091e32ed94113d919908be20e09f5eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20H=C3=B6lzl?= Date: Fri, 19 Jun 2026 11:00:35 +0200 Subject: [PATCH 0982/1054] can: vxcan: Kconfig: fix description stating no local echo provided MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Kconfig description of the vxcan kernel module erroneously states the the vxcan interface does not provide a local echo of sent can frames. However this behavior changed in commit 259bdba27e32 ("vxcan: enable local echo for sent CAN frames") and vxcan interfaces now provide a local echo. Change the description of the vxcan module in the Kconfig to reflect this change. Signed-off-by: Alexander Hölzl Acked-by: Oliver Hartkopp Link: https://patch.msgid.link/20260619090035.17769-1-alexander.hoelzl@gmx.net [mkl: rephrase patch description] Signed-off-by: Marc Kleine-Budde --- drivers/net/can/Kconfig | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig index e4058708ae68..a8fad6fe5302 100644 --- a/drivers/net/can/Kconfig +++ b/drivers/net/can/Kconfig @@ -40,11 +40,8 @@ config CAN_VXCAN When one end receives the packet it appears on its pair and vice versa. The vxcan can be used for cross namespace communication. - In opposite to vcan loopback devices the vxcan only forwards CAN - frames to its pair and does *not* provide a local echo of sent - CAN frames. To disable a potential echo in af_can.c the vxcan driver - announces IFF_ECHO in the interface flags. To have a clean start - in each namespace the CAN GW hop counter is set to zero. + To have a clean start in each namespace the CAN GW hop counter is + set to zero. This driver can also be built as a module. If so, the module will be called vxcan. From c43122fef328a70045fe7621c06de6b2b8e19264 Mon Sep 17 00:00:00 2001 From: Fan Wu Date: Thu, 9 Jul 2026 16:41:59 +0000 Subject: [PATCH 0983/1054] can: esd_usb: kill anchored URBs before freeing netdevs esd_usb_disconnect() frees each CAN netdev with free_candev() inside its per-netdev loop and only calls unlink_all_urbs(dev) afterwards. The per-netdev private data (struct esd_usb_net_priv) is embedded in the net_device allocation returned by alloc_candev(), so once free_candev() has run, dev->nets[i] points to freed memory. unlink_all_urbs() then dereferences the freed dev->nets[i] to kill the per-netdev TX anchor (usb_kill_anchored_urbs(&priv->tx_submitted)), clear active_tx_jobs, and reset priv->tx_contexts[]. Reorder the teardown so the anchored URBs are killed before the netdevs are freed, matching other CAN/USB drivers in the same directory such as ems_usb, usb_8dev and mcba_usb, which unregister, then unlink, then free: unregister the netdevs first (which stops their TX queues), call unlink_all_urbs(dev) once, then free the netdevs. This issue was found by an in-house static analysis tool. Fixes: 96d8e90382dc ("can: Add driver for esd CAN-USB/2 device") Cc: stable@vger.kernel.org Assisted-by: Codex:gpt-5.5 Signed-off-by: Fan Wu Link: https://patch.msgid.link/20260709164159.497640-1-fanwu01@zju.edu.cn Signed-off-by: Marc Kleine-Budde --- drivers/net/can/usb/esd_usb.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/can/usb/esd_usb.c b/drivers/net/can/usb/esd_usb.c index d257440fa01f..f41d4a0d140f 100644 --- a/drivers/net/can/usb/esd_usb.c +++ b/drivers/net/can/usb/esd_usb.c @@ -1390,10 +1390,13 @@ static void esd_usb_disconnect(struct usb_interface *intf) netdev = dev->nets[i]->netdev; netdev_info(netdev, "unregister\n"); unregister_netdev(netdev); - free_candev(netdev); } } unlink_all_urbs(dev); + for (i = 0; i < dev->net_count; i++) { + if (dev->nets[i]) + free_candev(dev->nets[i]->netdev); + } kfree(dev); } } From 2f4acd0fcd862e22eab45690ec2c08c80b6ef2e7 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 13 Jul 2026 23:03:44 -0700 Subject: [PATCH 0984/1054] xfs: resample the data fork mapping after cycling ILOCK xfs_reflink_fill_{cow_hole,delalloc} are both presented with an inode, a data fork mapping, and a cow fork mapping. Unfortunately, these two helpers cycle the ILOCK to grab a transaction, which means that the mappings are stale as soon as we reacquire the ILOCK. Currently we refresh the cow fork mapping by re-calling xfs_find_trim_cow_extent, but we don't refresh the data fork mapping beforehand, which means that the xfs_bmap_trim_cow in that function queries the refcount btree about the wrong physical blocks and returns an inaccurate value in *shared. If *shared is now false, the directio write proceeds with a stale data fork mapping. Fix this by querying the data fork mapping if the sequence counter changes across the ILOCK cycle. Cc: hch@lst.de Cc: stable@vger.kernel.org # v4.11 Fixes: 3c68d44a2b49a0 ("xfs: allocate direct I/O COW blocks in iomap_begin") Signed-off-by: "Darrick J. Wong" Reviewed-by: Christoph Hellwig Reviewed-by: Carlos Maiolino Signed-off-by: Carlos Maiolino --- fs/xfs/xfs_reflink.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c index a5c188b78138..480136136635 100644 --- a/fs/xfs/xfs_reflink.c +++ b/fs/xfs/xfs_reflink.c @@ -440,6 +440,7 @@ xfs_reflink_fill_cow_hole( struct xfs_mount *mp = ip->i_mount; struct xfs_trans *tp; xfs_filblks_t resaligned; + unsigned int seq_before = READ_ONCE(ip->i_df.if_seq); unsigned int dblocks = 0, rblocks = 0; int nimaps; int error; @@ -465,6 +466,22 @@ xfs_reflink_fill_cow_hole( *lockmode = XFS_ILOCK_EXCL; + /* + * The data fork mapping may have changed while we dropped the ILOCK + * (a racing O_DIRECT writer under IOLOCK_SHARED can complete a full + * CoW cycle including xfs_reflink_end_cow(), which remaps this offset + * and drops the refcount of the old shared block). Re-read it so the + * shared-status recheck below and the caller's in-place iomap both + * operate on the current mapping rather than a stale physical block. + */ + if (seq_before != READ_ONCE(ip->i_df.if_seq)) { + nimaps = 1; + error = xfs_bmapi_read(ip, imap->br_startoff, + imap->br_blockcount, imap, &nimaps, 0); + if (error) + goto out_trans_cancel; + } + error = xfs_find_trim_cow_extent(ip, imap, cmap, shared, &found); if (error || !*shared) goto out_trans_cancel; @@ -511,6 +528,8 @@ xfs_reflink_fill_delalloc( bool found; do { + unsigned int seq_before = READ_ONCE(ip->i_df.if_seq); + xfs_iunlock(ip, *lockmode); *lockmode = 0; @@ -521,6 +540,23 @@ xfs_reflink_fill_delalloc( *lockmode = XFS_ILOCK_EXCL; + /* + * The data fork mapping may have changed while we dropped the + * ILOCK (a racing O_DIRECT writer under IOLOCK_SHARED can + * complete a full CoW cycle including xfs_reflink_end_cow(), + * which remaps this offset and drops the refcount of the old + * shared block). Re-read it so the shared-status recheck + * below and the caller's in-place iomap both operate on the + * current mapping rather than a stale physical block. + */ + if (seq_before != READ_ONCE(ip->i_df.if_seq)) { + nimaps = 1; + error = xfs_bmapi_read(ip, imap->br_startoff, + imap->br_blockcount, imap, &nimaps, 0); + if (error) + goto out_trans_cancel; + } + error = xfs_find_trim_cow_extent(ip, imap, cmap, shared, &found); if (error || !*shared) From a1caeeadbf57ff86dfc3454398c46de86056a74e Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 13 Jul 2026 23:04:08 -0700 Subject: [PATCH 0985/1054] xfs: don't replace the wrong part of the cow fork LOLLM points out that xfs_iext_lookup_extent can return a @got where got->br_startoff < startoff. In this case, xrep_cow_replace_range replaces the entire mapping instead of just the part that had been marked bad in the bitmap, but advances the bitmap cursor in xrep_cow_replace by the amount replaced. As a result, we fail to replace the end of the bad range, and replace part of the good range. Fix this by rewriting the replace method to handle replacing the middle of a cow fork mapping. This we do by returning both the current mapping as @got, and the subset of the mapping that we want to replace as @rep, using @rep to store the results of the new allocation, and comparing @rep to @got to figure out the exact transformations needed. Cc: stable@vger.kernel.org # v6.8 Fixes: dbbdbd0086320a ("xfs: repair problems in CoW forks") Signed-off-by: "Darrick J. Wong" Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/cow_repair.c | 203 ++++++++++++++++++++++++-------------- fs/xfs/scrub/trace.h | 28 +++--- 2 files changed, 148 insertions(+), 83 deletions(-) diff --git a/fs/xfs/scrub/cow_repair.c b/fs/xfs/scrub/cow_repair.c index 0075b6d5a1b5..ca3405a26b64 100644 --- a/fs/xfs/scrub/cow_repair.c +++ b/fs/xfs/scrub/cow_repair.c @@ -80,12 +80,6 @@ struct xrep_cow { unsigned int next_bno; }; -/* CoW staging extent. */ -struct xrep_cow_extent { - xfs_fsblock_t fsbno; - xfs_extlen_t len; -}; - /* * Mark the part of the file range that corresponds to the given physical * space. Caller must ensure that the physical range is within xc->irec. @@ -401,22 +395,21 @@ xrep_cow_find_bad_rt( STATIC int xrep_cow_alloc( struct xfs_scrub *sc, - xfs_extlen_t maxlen, - struct xrep_cow_extent *repl) + struct xfs_bmbt_irec *del) { struct xfs_alloc_arg args = { .tp = sc->tp, .mp = sc->mp, .oinfo = XFS_RMAP_OINFO_SKIP_UPDATE, .minlen = 1, - .maxlen = maxlen, + .maxlen = del->br_blockcount, .prod = 1, .resv = XFS_AG_RESV_NONE, .datatype = XFS_ALLOC_USERDATA, }; int error; - error = xfs_trans_reserve_more(sc->tp, maxlen, 0); + error = xfs_trans_reserve_more(sc->tp, del->br_blockcount, 0); if (error) return error; @@ -428,8 +421,8 @@ xrep_cow_alloc( xfs_refcount_alloc_cow_extent(sc->tp, false, args.fsbno, args.len); - repl->fsbno = args.fsbno; - repl->len = args.len; + del->br_startblock = args.fsbno; + del->br_blockcount = args.len; return 0; } @@ -440,10 +433,12 @@ xrep_cow_alloc( STATIC int xrep_cow_alloc_rt( struct xfs_scrub *sc, - xfs_extlen_t maxlen, - struct xrep_cow_extent *repl) + struct xfs_bmbt_irec *del) { - xfs_rtxlen_t maxrtx = xfs_rtb_to_rtx(sc->mp, maxlen); + xfs_fsblock_t fsbno; + xfs_rtxlen_t maxrtx = + min(U32_MAX, xfs_blen_to_rtbxlen(sc->mp, del->br_blockcount)); + xfs_extlen_t len; int error; error = xfs_trans_reserve_more(sc->tp, 0, maxrtx); @@ -451,11 +446,14 @@ xrep_cow_alloc_rt( return error; error = xfs_rtallocate_rtgs(sc->tp, NULLRTBLOCK, 1, maxrtx, 1, false, - false, &repl->fsbno, &repl->len); + false, &fsbno, &len); if (error) return error; - xfs_refcount_alloc_cow_extent(sc->tp, true, repl->fsbno, repl->len); + xfs_refcount_alloc_cow_extent(sc->tp, true, fsbno, len); + + del->br_startblock = fsbno; + del->br_blockcount = len; return 0; } @@ -469,19 +467,19 @@ static inline int xrep_cow_find_mapping( struct xrep_cow *xc, struct xfs_iext_cursor *icur, - xfs_fileoff_t startoff, - struct xfs_bmbt_irec *got) + xfs_fileoff_t badoff, + xfs_extlen_t badlen, + struct xfs_bmbt_irec *got, + struct xfs_bmbt_irec *rep) { struct xfs_inode *ip = xc->sc->ip; struct xfs_ifork *ifp = xfs_ifork_ptr(ip, XFS_COW_FORK); - if (!xfs_iext_lookup_extent(ip, ifp, startoff, icur, got)) + if (!xfs_iext_lookup_extent(ip, ifp, badoff, icur, got)) goto bad; + memcpy(rep, got, sizeof(*rep)); - if (got->br_startoff > startoff) - goto bad; - - if (got->br_blockcount == 0) + if (got->br_startoff > badoff) goto bad; if (isnullstartblock(got->br_startblock)) @@ -490,6 +488,24 @@ xrep_cow_find_mapping( if (xfs_bmap_is_written_extent(got)) goto bad; + if (got->br_startoff < badoff) { + const int64_t delta = badoff - got->br_startoff; + + rep->br_blockcount -= delta; + rep->br_startoff += delta; + rep->br_startblock += delta; + } + + if (got->br_startoff + got->br_blockcount > badoff + badlen) { + const int64_t delta = (got->br_startoff + got->br_blockcount) - + (badoff + badlen); + + rep->br_blockcount -= delta; + } + + if (got->br_blockcount == 0) + goto bad; + return 0; bad: ASSERT(0); @@ -500,46 +516,92 @@ xrep_cow_find_mapping( #define REPLACE_RIGHT_SIDE (1U << 1) /* - * Given a CoW fork mapping @got and a replacement mapping @repl, remap the - * beginning of @got with the space described by @rep. + * Given a CoW fork mapping @got and a replacement mapping @rep, map the space + * described by @rep into the cow fork, pushing aside @got as necessary. @icur + * must point to iext tree leaf containing @got. */ static inline void xrep_cow_replace_mapping( - struct xfs_inode *ip, - struct xfs_iext_cursor *icur, - const struct xfs_bmbt_irec *got, - const struct xrep_cow_extent *repl) + struct xfs_inode *ip, + struct xfs_iext_cursor *icur, + struct xfs_bmbt_irec *got, + struct xfs_bmbt_irec *rep) { - struct xfs_bmbt_irec new = *got; /* struct copy */ + struct xfs_ifork *ifp = xfs_ifork_ptr(ip, XFS_COW_FORK); + xfs_fileoff_t rep_endoff = + rep->br_startoff + rep->br_blockcount; + xfs_fileoff_t got_endoff = + got->br_startoff + got->br_blockcount; + uint32_t state = BMAP_COWFORK; - ASSERT(repl->len > 0); + ASSERT(rep->br_blockcount > 0); ASSERT(!isnullstartblock(got->br_startblock)); + ASSERT(got->br_startoff <= rep->br_startoff); + ASSERT(got_endoff >= rep_endoff); - trace_xrep_cow_replace_mapping(ip, got, repl->fsbno, repl->len); + trace_xrep_cow_replace_mapping(ip, got, rep); - if (got->br_blockcount == repl->len) { + if (got->br_startoff == rep->br_startoff) + state |= BMAP_LEFT_FILLING; + if (got_endoff == rep_endoff) + state |= BMAP_RIGHT_FILLING; + + switch (state & (BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING)) { + case BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING: /* - * The new extent is a complete replacement for the existing - * extent. Update the COW fork record. + * Replacement matches the whole mapping, update the record. */ - new.br_startblock = repl->fsbno; - xfs_iext_update_extent(ip, BMAP_COWFORK, icur, &new); - return; + xfs_iext_update_extent(ip, state, icur, rep); + break; + case BMAP_LEFT_FILLING: + /* + * Replace the first part of the mapping: Update the cursor + * position with the new mapping, then add a record with the + * tail of the old mapping. + */ + got->br_startoff = rep_endoff; + got->br_blockcount -= rep->br_blockcount; + got->br_startblock += rep->br_blockcount; + + xfs_iext_update_extent(ip, state, icur, rep); + xfs_iext_next(ifp, icur); + xfs_iext_insert(ip, icur, got, state); + break; + case BMAP_RIGHT_FILLING: + /* + * Replacing the last part of the mapping. Shorten the current + * mapping then add a record with the new mapping. + */ + got->br_blockcount -= rep->br_blockcount; + + xfs_iext_update_extent(ip, state, icur, got); + xfs_iext_next(ifp, icur); + xfs_iext_insert(ip, icur, rep, state); + break; + case 0: + /* + * Replacing the middle of the extent. Shorten the current + * mapping, add a new record with the new mapping, and add a + * second new record with the tail of the old mapping. + */ + got->br_blockcount = rep->br_startoff - got->br_startoff; + + struct xfs_bmbt_irec new = { + .br_startoff = rep_endoff, + .br_blockcount = got_endoff - rep_endoff, + .br_state = got->br_state, + .br_startblock = got->br_startblock + + rep->br_blockcount + + got->br_blockcount, + }; + + xfs_iext_update_extent(ip, state, icur, got); + xfs_iext_next(ifp, icur); + xfs_iext_insert(ip, icur, rep, state); + xfs_iext_next(ifp, icur); + xfs_iext_insert(ip, icur, &new, state); + break; } - - /* - * The new extent can replace the beginning of the COW fork record. - * Move the left side of @got upwards, then insert the new record. - */ - new.br_startoff += repl->len; - new.br_startblock += repl->len; - new.br_blockcount -= repl->len; - xfs_iext_update_extent(ip, BMAP_COWFORK, icur, &new); - - new.br_startoff = got->br_startoff; - new.br_startblock = repl->fsbno; - new.br_blockcount = repl->len; - xfs_iext_insert(ip, icur, &new, BMAP_COWFORK); } /* @@ -553,33 +615,30 @@ xrep_cow_replace_range( xfs_extlen_t *blockcount) { struct xfs_iext_cursor icur; - struct xrep_cow_extent repl; - struct xfs_bmbt_irec got; + struct xfs_bmbt_irec got, rep; struct xfs_scrub *sc = xc->sc; - xfs_fileoff_t nextoff; - xfs_extlen_t alloc_len; + xfs_fsblock_t old_fsbno; int error; /* - * Put the existing CoW fork mapping in @got. If @got ends before - * @rep, truncate @rep so we only replace one extent mapping at a time. + * Put the existing CoW fork mapping in @got, and put in @rep the + * contents of @got trimmed to @startoff/@blockcount. We only want + * to replace the bad region, and only one mapping at a time. */ - error = xrep_cow_find_mapping(xc, &icur, startoff, &got); + error = xrep_cow_find_mapping(xc, &icur, startoff, *blockcount, &got, + &rep); if (error) return error; - nextoff = min(startoff + *blockcount, - got.br_startoff + got.br_blockcount); + old_fsbno = rep.br_startblock; /* * Allocate a replacement extent. If we don't fill all the blocks, * shorten the quantity that will be deleted in this step. */ - alloc_len = min_t(xfs_fileoff_t, XFS_MAX_BMBT_EXTLEN, - nextoff - startoff); if (XFS_IS_REALTIME_INODE(sc->ip)) - error = xrep_cow_alloc_rt(sc, alloc_len, &repl); + error = xrep_cow_alloc_rt(sc, &rep); else - error = xrep_cow_alloc(sc, alloc_len, &repl); + error = xrep_cow_alloc(sc, &rep); if (error) return error; @@ -587,7 +646,7 @@ xrep_cow_replace_range( * Replace the old mapping with the new one, and commit the metadata * changes made so far. */ - xrep_cow_replace_mapping(sc->ip, &icur, &got, &repl); + xrep_cow_replace_mapping(sc->ip, &icur, &got, &rep); xfs_inode_set_cowblocks_tag(sc->ip); error = xfs_defer_finish(&sc->tp); @@ -596,15 +655,15 @@ xrep_cow_replace_range( /* Note the old CoW staging extents; we'll reap them all later. */ if (XFS_IS_REALTIME_INODE(sc->ip)) - error = xrtb_bitmap_set(&xc->old_cowfork_rtblocks, - got.br_startblock, repl.len); + error = xrtb_bitmap_set(&xc->old_cowfork_rtblocks, old_fsbno, + rep.br_blockcount); else - error = xfsb_bitmap_set(&xc->old_cowfork_fsblocks, - got.br_startblock, repl.len); + error = xfsb_bitmap_set(&xc->old_cowfork_fsblocks, old_fsbno, + rep.br_blockcount); if (error) return error; - *blockcount = repl.len; + *blockcount = rep.br_blockcount; return 0; } diff --git a/fs/xfs/scrub/trace.h b/fs/xfs/scrub/trace.h index 1b7d9e07a27d..d48a6db5b5f3 100644 --- a/fs/xfs/scrub/trace.h +++ b/fs/xfs/scrub/trace.h @@ -2672,9 +2672,9 @@ TRACE_EVENT(xrep_cow_mark_file_range, ); TRACE_EVENT(xrep_cow_replace_mapping, - TP_PROTO(struct xfs_inode *ip, const struct xfs_bmbt_irec *irec, - xfs_fsblock_t new_startblock, xfs_extlen_t new_blockcount), - TP_ARGS(ip, irec, new_startblock, new_blockcount), + TP_PROTO(struct xfs_inode *ip, const struct xfs_bmbt_irec *got, + const struct xfs_bmbt_irec *rep), + TP_ARGS(ip, got, rep), TP_STRUCT__entry( __field(dev_t, dev) __field(xfs_ino_t, ino) @@ -2682,28 +2682,34 @@ TRACE_EVENT(xrep_cow_replace_mapping, __field(xfs_fileoff_t, startoff) __field(xfs_filblks_t, blockcount) __field(xfs_exntst_t, state) + __field(xfs_fileoff_t, new_startoff) __field(xfs_fsblock_t, new_startblock) __field(xfs_extlen_t, new_blockcount) + __field(xfs_exntst_t, new_state) ), TP_fast_assign( __entry->dev = ip->i_mount->m_super->s_dev; __entry->ino = I_INO(ip); - __entry->startoff = irec->br_startoff; - __entry->startblock = irec->br_startblock; - __entry->blockcount = irec->br_blockcount; - __entry->state = irec->br_state; - __entry->new_startblock = new_startblock; - __entry->new_blockcount = new_blockcount; + __entry->startoff = got->br_startoff; + __entry->startblock = got->br_startblock; + __entry->blockcount = got->br_blockcount; + __entry->state = got->br_state; + __entry->new_startoff = rep->br_startoff; + __entry->new_startblock = rep->br_startblock; + __entry->new_blockcount = rep->br_blockcount; + __entry->new_state = rep->br_state; ), - TP_printk("dev %d:%d ino 0x%llx startoff 0x%llx startblock 0x%llx fsbcount 0x%llx state 0x%x new_startblock 0x%llx new_fsbcount 0x%x", + TP_printk("dev %d:%d ino 0x%llx startoff 0x%llx startblock 0x%llx fsbcount 0x%llx state 0x%x new_startoff 0x%llx new_startblock 0x%llx new_fsbcount 0x%x new_state 0x%x", MAJOR(__entry->dev), MINOR(__entry->dev), __entry->ino, __entry->startoff, __entry->startblock, __entry->blockcount, __entry->state, + __entry->new_startoff, __entry->new_startblock, - __entry->new_blockcount) + __entry->new_blockcount, + __entry->new_state) ); TRACE_EVENT(xrep_cow_free_staging, From bcb0621204e6999bb8f3a6c7deb5c6abfe0f197b Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 13 Jul 2026 23:04:24 -0700 Subject: [PATCH 0986/1054] xfs: make cow repair somewhat flaky when debugging knob enabled Introduce a new behavior for the cow fork repair code: if the debugging knob is enabled, we'll pick a random subrange of each cow fork mapping to mark as bad. This will exercise the xrep_cow_replace_mapping more thoroughly. Signed-off-by: "Darrick J. Wong" Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/cow_repair.c | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/fs/xfs/scrub/cow_repair.c b/fs/xfs/scrub/cow_repair.c index ca3405a26b64..511a761dca9f 100644 --- a/fs/xfs/scrub/cow_repair.c +++ b/fs/xfs/scrub/cow_repair.c @@ -224,6 +224,29 @@ xrep_cow_mark_missing_staging_rmap( xfs_gbno_to_fsb(cur->bc_group, rec_bno), rec_len); } +/* + * Trim the start and end of the current mapping by up to 1/4 of the length + * and mark that as "bad" to test the cow fork repair mechanism. + */ +static inline int +xrep_cow_debug_replacement( + struct xrep_cow *xc) +{ + xfs_fsblock_t fsbno = xc->irec.br_startblock; + xfs_extlen_t len = xc->irec.br_blockcount; + uint32_t trim; + + /* get_random_u32_below requires a nonzero argument */ + trim = len > 4 ? get_random_u32_below(len / 4) : 0; + len -= trim; + + trim = len > 4 ? get_random_u32_below(len / 4) : 0; + fsbno += trim; + len -= trim; + + return xrep_cow_mark_file_range(xc, fsbno, len); +} + /* * Find any part of the CoW fork mapping that isn't a single-owner CoW staging * extent and mark the corresponding part of the file range in the bitmap. @@ -293,8 +316,9 @@ xrep_cow_find_bad( * If userspace is forcing us to rebuild the CoW fork or someone turned * 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)) + if (XFS_TEST_ERROR(sc->mp, XFS_ERRTAG_FORCE_SCRUB_REPAIR)) + error = xrep_cow_debug_replacement(xc); + else if (sc->sm->sm_flags & XFS_SCRUB_IFLAG_FORCE_REBUILD) error = xrep_cow_mark_file_range(xc, xc->irec.br_startblock, xc->irec.br_blockcount); @@ -375,8 +399,9 @@ xrep_cow_find_bad_rt( * turned on the debugging knob, replace everything in the * 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)) + if (XFS_TEST_ERROR(sc->mp, XFS_ERRTAG_FORCE_SCRUB_REPAIR)) + error = xrep_cow_debug_replacement(xc); + else if (sc->sm->sm_flags & XFS_SCRUB_IFLAG_FORCE_REBUILD) error = xrep_cow_mark_file_range(xc, xc->irec.br_startblock, xc->irec.br_blockcount); From 60a1dde9d28948a88ed5a06cf0b6179b1d487f85 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 13 Jul 2026 23:04:39 -0700 Subject: [PATCH 0987/1054] xfs: move cow_replace_mapping to xfs_bmap_util.c Move the actual details of (partially) replacing a COW fork mapping to xfs_bmap_util.c so that all the code doing hairy operations on subsets of bmbt_irecs are kept together. Signed-off-by: "Darrick J. Wong" Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/cow_repair.c | 95 +-------------------------------------- fs/xfs/scrub/trace.h | 41 ----------------- fs/xfs/xfs_bmap_util.c | 89 ++++++++++++++++++++++++++++++++++++ fs/xfs/xfs_bmap_util.h | 4 ++ fs/xfs/xfs_trace.h | 41 +++++++++++++++++ 5 files changed, 136 insertions(+), 134 deletions(-) diff --git a/fs/xfs/scrub/cow_repair.c b/fs/xfs/scrub/cow_repair.c index 511a761dca9f..8dd9c0266e21 100644 --- a/fs/xfs/scrub/cow_repair.c +++ b/fs/xfs/scrub/cow_repair.c @@ -29,6 +29,7 @@ #include "xfs_rtalloc.h" #include "xfs_rtbitmap.h" #include "xfs_rtgroup.h" +#include "xfs_bmap_util.h" #include "scrub/xfs_scrub.h" #include "scrub/scrub.h" #include "scrub/common.h" @@ -537,98 +538,6 @@ xrep_cow_find_mapping( return -EFSCORRUPTED; } -#define REPLACE_LEFT_SIDE (1U << 0) -#define REPLACE_RIGHT_SIDE (1U << 1) - -/* - * Given a CoW fork mapping @got and a replacement mapping @rep, map the space - * described by @rep into the cow fork, pushing aside @got as necessary. @icur - * must point to iext tree leaf containing @got. - */ -static inline void -xrep_cow_replace_mapping( - struct xfs_inode *ip, - struct xfs_iext_cursor *icur, - struct xfs_bmbt_irec *got, - struct xfs_bmbt_irec *rep) -{ - struct xfs_ifork *ifp = xfs_ifork_ptr(ip, XFS_COW_FORK); - xfs_fileoff_t rep_endoff = - rep->br_startoff + rep->br_blockcount; - xfs_fileoff_t got_endoff = - got->br_startoff + got->br_blockcount; - uint32_t state = BMAP_COWFORK; - - ASSERT(rep->br_blockcount > 0); - ASSERT(!isnullstartblock(got->br_startblock)); - ASSERT(got->br_startoff <= rep->br_startoff); - ASSERT(got_endoff >= rep_endoff); - - trace_xrep_cow_replace_mapping(ip, got, rep); - - if (got->br_startoff == rep->br_startoff) - state |= BMAP_LEFT_FILLING; - if (got_endoff == rep_endoff) - state |= BMAP_RIGHT_FILLING; - - switch (state & (BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING)) { - case BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING: - /* - * Replacement matches the whole mapping, update the record. - */ - xfs_iext_update_extent(ip, state, icur, rep); - break; - case BMAP_LEFT_FILLING: - /* - * Replace the first part of the mapping: Update the cursor - * position with the new mapping, then add a record with the - * tail of the old mapping. - */ - got->br_startoff = rep_endoff; - got->br_blockcount -= rep->br_blockcount; - got->br_startblock += rep->br_blockcount; - - xfs_iext_update_extent(ip, state, icur, rep); - xfs_iext_next(ifp, icur); - xfs_iext_insert(ip, icur, got, state); - break; - case BMAP_RIGHT_FILLING: - /* - * Replacing the last part of the mapping. Shorten the current - * mapping then add a record with the new mapping. - */ - got->br_blockcount -= rep->br_blockcount; - - xfs_iext_update_extent(ip, state, icur, got); - xfs_iext_next(ifp, icur); - xfs_iext_insert(ip, icur, rep, state); - break; - case 0: - /* - * Replacing the middle of the extent. Shorten the current - * mapping, add a new record with the new mapping, and add a - * second new record with the tail of the old mapping. - */ - got->br_blockcount = rep->br_startoff - got->br_startoff; - - struct xfs_bmbt_irec new = { - .br_startoff = rep_endoff, - .br_blockcount = got_endoff - rep_endoff, - .br_state = got->br_state, - .br_startblock = got->br_startblock + - rep->br_blockcount + - got->br_blockcount, - }; - - xfs_iext_update_extent(ip, state, icur, got); - xfs_iext_next(ifp, icur); - xfs_iext_insert(ip, icur, rep, state); - xfs_iext_next(ifp, icur); - xfs_iext_insert(ip, icur, &new, state); - break; - } -} - /* * Replace the unwritten CoW staging extent backing the given file range with a * new space extent that isn't as problematic. @@ -671,7 +580,7 @@ xrep_cow_replace_range( * Replace the old mapping with the new one, and commit the metadata * changes made so far. */ - xrep_cow_replace_mapping(sc->ip, &icur, &got, &rep); + xfs_bmap_replace_cow_mapping(sc->ip, &icur, &got, &rep); xfs_inode_set_cowblocks_tag(sc->ip); error = xfs_defer_finish(&sc->tp); diff --git a/fs/xfs/scrub/trace.h b/fs/xfs/scrub/trace.h index d48a6db5b5f3..d5d39d82749e 100644 --- a/fs/xfs/scrub/trace.h +++ b/fs/xfs/scrub/trace.h @@ -2671,47 +2671,6 @@ TRACE_EVENT(xrep_cow_mark_file_range, __entry->blockcount) ); -TRACE_EVENT(xrep_cow_replace_mapping, - TP_PROTO(struct xfs_inode *ip, const struct xfs_bmbt_irec *got, - const struct xfs_bmbt_irec *rep), - TP_ARGS(ip, got, rep), - TP_STRUCT__entry( - __field(dev_t, dev) - __field(xfs_ino_t, ino) - __field(xfs_fsblock_t, startblock) - __field(xfs_fileoff_t, startoff) - __field(xfs_filblks_t, blockcount) - __field(xfs_exntst_t, state) - __field(xfs_fileoff_t, new_startoff) - __field(xfs_fsblock_t, new_startblock) - __field(xfs_extlen_t, new_blockcount) - __field(xfs_exntst_t, new_state) - ), - TP_fast_assign( - __entry->dev = ip->i_mount->m_super->s_dev; - __entry->ino = I_INO(ip); - __entry->startoff = got->br_startoff; - __entry->startblock = got->br_startblock; - __entry->blockcount = got->br_blockcount; - __entry->state = got->br_state; - __entry->new_startoff = rep->br_startoff; - __entry->new_startblock = rep->br_startblock; - __entry->new_blockcount = rep->br_blockcount; - __entry->new_state = rep->br_state; - ), - TP_printk("dev %d:%d ino 0x%llx startoff 0x%llx startblock 0x%llx fsbcount 0x%llx state 0x%x new_startoff 0x%llx new_startblock 0x%llx new_fsbcount 0x%x new_state 0x%x", - MAJOR(__entry->dev), MINOR(__entry->dev), - __entry->ino, - __entry->startoff, - __entry->startblock, - __entry->blockcount, - __entry->state, - __entry->new_startoff, - __entry->new_startblock, - __entry->new_blockcount, - __entry->new_state) -); - TRACE_EVENT(xrep_cow_free_staging, TP_PROTO(const struct xfs_perag *pag, xfs_agblock_t agbno, xfs_extlen_t blockcount), diff --git a/fs/xfs/xfs_bmap_util.c b/fs/xfs/xfs_bmap_util.c index 3b9f262f8e91..c88b9ade7389 100644 --- a/fs/xfs/xfs_bmap_util.c +++ b/fs/xfs/xfs_bmap_util.c @@ -1744,3 +1744,92 @@ xfs_swap_extents( xfs_trans_cancel(tp); goto out_unlock_ilock; } + +/* + * Given a CoW fork mapping @got and a replacement mapping @rep, map the space + * described by @rep into the cow fork, pushing aside @got as necessary. @icur + * must point to iext tree leaf containing @got. + */ +void +xfs_bmap_replace_cow_mapping( + struct xfs_inode *ip, + struct xfs_iext_cursor *icur, + struct xfs_bmbt_irec *got, + struct xfs_bmbt_irec *rep) +{ + struct xfs_ifork *ifp = xfs_ifork_ptr(ip, XFS_COW_FORK); + xfs_fileoff_t rep_endoff = + rep->br_startoff + rep->br_blockcount; + xfs_fileoff_t got_endoff = + got->br_startoff + got->br_blockcount; + uint32_t state = BMAP_COWFORK; + + ASSERT(rep->br_blockcount > 0); + ASSERT(!isnullstartblock(got->br_startblock)); + ASSERT(got->br_startoff <= rep->br_startoff); + ASSERT(got_endoff >= rep_endoff); + + trace_xfs_bmap_replace_cow_mapping(ip, got, rep); + + if (got->br_startoff == rep->br_startoff) + state |= BMAP_LEFT_FILLING; + if (got_endoff == rep_endoff) + state |= BMAP_RIGHT_FILLING; + + switch (state & (BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING)) { + case BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING: + /* + * Replacement matches the whole mapping, update the record. + */ + xfs_iext_update_extent(ip, state, icur, rep); + break; + case BMAP_LEFT_FILLING: + /* + * Replace the first part of the mapping: Update the cursor + * position with the new mapping, then add a record with the + * tail of the old mapping. + */ + got->br_startoff = rep_endoff; + got->br_blockcount -= rep->br_blockcount; + got->br_startblock += rep->br_blockcount; + + xfs_iext_update_extent(ip, state, icur, rep); + xfs_iext_next(ifp, icur); + xfs_iext_insert(ip, icur, got, state); + break; + case BMAP_RIGHT_FILLING: + /* + * Replacing the last part of the mapping. Shorten the current + * mapping then add a record with the new mapping. + */ + got->br_blockcount -= rep->br_blockcount; + + xfs_iext_update_extent(ip, state, icur, got); + xfs_iext_next(ifp, icur); + xfs_iext_insert(ip, icur, rep, state); + break; + case 0: + /* + * Replacing the middle of the extent. Shorten the current + * mapping, add a new record with the new mapping, and add a + * second new record with the tail of the old mapping. + */ + got->br_blockcount = rep->br_startoff - got->br_startoff; + + struct xfs_bmbt_irec new = { + .br_startoff = rep_endoff, + .br_blockcount = got_endoff - rep_endoff, + .br_state = got->br_state, + .br_startblock = got->br_startblock + + rep->br_blockcount + + got->br_blockcount, + }; + + xfs_iext_update_extent(ip, state, icur, got); + xfs_iext_next(ifp, icur); + xfs_iext_insert(ip, icur, rep, state); + xfs_iext_next(ifp, icur); + xfs_iext_insert(ip, icur, &new, state); + break; + } +} diff --git a/fs/xfs/xfs_bmap_util.h b/fs/xfs/xfs_bmap_util.h index c477b3361630..eaaf094154b9 100644 --- a/fs/xfs/xfs_bmap_util.h +++ b/fs/xfs/xfs_bmap_util.h @@ -81,4 +81,8 @@ int xfs_bmap_count_blocks(struct xfs_trans *tp, struct xfs_inode *ip, int xfs_flush_unmap_range(struct xfs_inode *ip, xfs_off_t offset, xfs_off_t len); +void xfs_bmap_replace_cow_mapping(struct xfs_inode *ip, + struct xfs_iext_cursor *icur, struct xfs_bmbt_irec *got, + struct xfs_bmbt_irec *rep); + #endif /* __XFS_BMAP_UTIL_H__ */ diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h index d478693674f9..aeb89ac53bf1 100644 --- a/fs/xfs/xfs_trace.h +++ b/fs/xfs/xfs_trace.h @@ -6439,6 +6439,47 @@ TRACE_EVENT(xfs_verify_media_error, __entry->error) ); +TRACE_EVENT(xfs_bmap_replace_cow_mapping, + TP_PROTO(struct xfs_inode *ip, const struct xfs_bmbt_irec *got, + const struct xfs_bmbt_irec *rep), + TP_ARGS(ip, got, rep), + TP_STRUCT__entry( + __field(dev_t, dev) + __field(xfs_ino_t, ino) + __field(xfs_fsblock_t, startblock) + __field(xfs_fileoff_t, startoff) + __field(xfs_filblks_t, blockcount) + __field(xfs_exntst_t, state) + __field(xfs_fileoff_t, new_startoff) + __field(xfs_fsblock_t, new_startblock) + __field(xfs_extlen_t, new_blockcount) + __field(xfs_exntst_t, new_state) + ), + TP_fast_assign( + __entry->dev = ip->i_mount->m_super->s_dev; + __entry->ino = I_INO(ip); + __entry->startoff = got->br_startoff; + __entry->startblock = got->br_startblock; + __entry->blockcount = got->br_blockcount; + __entry->state = got->br_state; + __entry->new_startoff = rep->br_startoff; + __entry->new_startblock = rep->br_startblock; + __entry->new_blockcount = rep->br_blockcount; + __entry->new_state = rep->br_state; + ), + TP_printk("dev %d:%d ino 0x%llx startoff 0x%llx startblock 0x%llx fsbcount 0x%llx state 0x%x new_startoff 0x%llx new_startblock 0x%llx new_fsbcount 0x%x new_state 0x%x", + MAJOR(__entry->dev), MINOR(__entry->dev), + __entry->ino, + __entry->startoff, + __entry->startblock, + __entry->blockcount, + __entry->state, + __entry->new_startoff, + __entry->new_startblock, + __entry->new_blockcount, + __entry->new_state) +); + #endif /* _TRACE_XFS_H */ #undef TRACE_INCLUDE_PATH From d766e4e5e85d829629c3ba503802fe1303d7b591 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 13 Jul 2026 23:04:55 -0700 Subject: [PATCH 0988/1054] xfs: don't wrap around quota ids in dqiterate LOLLM noticed that q_id is an unsigned 32-bit variable. If it happens to be set to XFS_DQ_ID_MAX due to a filesystem that actually has a dquot for ID_MAX, then this addition will truncate to zero and the iteration starts over. Fix this by casting to u64. Cc: stable@vger.kernel.org # v6.8 Fixes: 21d7500929c8a0 ("xfs: improve dquot iteration for scrub") Signed-off-by: "Darrick J. Wong" Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/dqiterate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/xfs/scrub/dqiterate.c b/fs/xfs/scrub/dqiterate.c index 10950e4bd4c3..079dc4e691a0 100644 --- a/fs/xfs/scrub/dqiterate.c +++ b/fs/xfs/scrub/dqiterate.c @@ -205,7 +205,7 @@ xchk_dquot_iter( if (error) return error; - cursor->id = dq->q_id + 1; + cursor->id = (uint64_t)dq->q_id + 1; *dqpp = dq; return 1; } From ee248157da501f0c02688fb64e5359f2832b0b01 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 13 Jul 2026 23:05:10 -0700 Subject: [PATCH 0989/1054] xfs: use rtrefcount btree cursor in xchk_xref_is_rt_cow_staging LOLLM points out that we pass the wrong btree cursor here. We want the rtrefcount btree cursor, not the non-rt one. This is fairly benign since it only affects tracing data. Cc: stable@vger.kernel.org # v6.14 Fixes: 91683bb3f264c0 ("xfs: cross-reference checks with the rt refcount btree") Signed-off-by: "Darrick J. Wong" Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/rtrefcount.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/xfs/scrub/rtrefcount.c b/fs/xfs/scrub/rtrefcount.c index 0d10ce2910c2..4e7c540c8d23 100644 --- a/fs/xfs/scrub/rtrefcount.c +++ b/fs/xfs/scrub/rtrefcount.c @@ -607,7 +607,7 @@ xchk_xref_is_rt_cow_staging( /* CoW lookup returned a shared extent record? */ if (rc.rc_domain != XFS_REFC_DOMAIN_COW) - xchk_btree_xref_set_corrupt(sc, sc->sa.refc_cur, 0); + xchk_btree_xref_set_corrupt(sc, sc->sr.refc_cur, 0); /* Must be at least as long as what was passed in */ if (rc.rc_blockcount < len) From 881f2eb0fcdea2f46a50fa6a892fe9e3ccf19e01 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 13 Jul 2026 23:05:26 -0700 Subject: [PATCH 0990/1054] xfs: use the rt version of the cow staging checker LOLLM also noticed that xchk_rtrmapbt_xref ought to be using the rtdev version of the "is this a cow extent?" helper function, not the datadev one. Cc: stable@vger.kernel.org # v6.14 Fixes: 91683bb3f264c0 ("xfs: cross-reference checks with the rt refcount btree") Signed-off-by: "Darrick J. Wong" Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/rtrmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/xfs/scrub/rtrmap.c b/fs/xfs/scrub/rtrmap.c index 043be93c7148..b3b2cf17ba2c 100644 --- a/fs/xfs/scrub/rtrmap.c +++ b/fs/xfs/scrub/rtrmap.c @@ -209,7 +209,7 @@ xchk_rtrmapbt_xref( xfs_rgbno_to_rtb(sc->sr.rtg, irec->rm_startblock), irec->rm_blockcount); if (irec->rm_owner == XFS_RMAP_OWN_COW) - xchk_xref_is_cow_staging(sc, irec->rm_startblock, + xchk_xref_is_rt_cow_staging(sc, irec->rm_startblock, irec->rm_blockcount); else xchk_rtrmapbt_xref_rtrefc(sc, irec); From 9af789fa274bc14b907f811f79fa988a6fc6d4e7 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 13 Jul 2026 23:05:41 -0700 Subject: [PATCH 0991/1054] xfs: write the rg superblock when fixing it The rtgroup superblock fixer should write the rtgroup superblock. LOLLM noticed this, oops. :/ Cc: stable@vger.kernel.org # v6.13 Fixes: 1433f8f9cead37 ("xfs: repair realtime group superblock") Signed-off-by: "Darrick J. Wong" Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/rgsuper.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/xfs/scrub/rgsuper.c b/fs/xfs/scrub/rgsuper.c index 482f899a518a..3dad6e5da74e 100644 --- a/fs/xfs/scrub/rgsuper.c +++ b/fs/xfs/scrub/rgsuper.c @@ -80,9 +80,13 @@ int xrep_rgsuperblock( struct xfs_scrub *sc) { + struct xfs_buf *sb_bp; + ASSERT(rtg_rgno(sc->sr.rtg) == 0); + sb_bp = xfs_trans_getsb(sc->tp); xfs_log_sb(sc->tp); + xfs_log_rtsb(sc->tp, sb_bp); return 0; } #endif /* CONFIG_XFS_ONLINE_REPAIR */ From ea6e2d9de25d2095845e0cdf2274e581fd40d636 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 13 Jul 2026 23:05:57 -0700 Subject: [PATCH 0992/1054] xfs: grab rtrmap btree when checking rgsuper LOLLM noticed that we aren't grabbing the rtrmap btree when we check the realtime group superblock. As a result, none of the cross-referencing checks have ever run. Fix this. Cc: stable@vger.kernel.org # v6.14 Fixes: 428e4884656db9 ("xfs: allow queued realtime intents to drain before scrubbing") Signed-off-by: "Darrick J. Wong" Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/rgsuper.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/fs/xfs/scrub/rgsuper.c b/fs/xfs/scrub/rgsuper.c index 3dad6e5da74e..2bd2c0351b35 100644 --- a/fs/xfs/scrub/rgsuper.c +++ b/fs/xfs/scrub/rgsuper.c @@ -23,6 +23,8 @@ int xchk_setup_rgsuperblock( struct xfs_scrub *sc) { + if (xchk_need_intent_drain(sc)) + xchk_fsgates_enable(sc, XCHK_FSGATES_DRAIN); return xchk_trans_alloc(sc, 0); } @@ -43,6 +45,7 @@ xchk_rgsuperblock( struct xfs_scrub *sc) { xfs_rgnumber_t rgno = sc->sm->sm_agno; + unsigned int flags; int error; /* @@ -63,7 +66,12 @@ xchk_rgsuperblock( if (!xchk_xref_process_error(sc, 0, 0, &error)) return error; - error = xchk_rtgroup_lock(sc, &sc->sr, XFS_RTGLOCK_BITMAP_SHARED); + if (xfs_has_rtrmapbt(sc->mp)) + flags = XFS_RTGLOCK_BITMAP | XFS_RTGLOCK_RMAP; + else + flags = XFS_RTGLOCK_BITMAP_SHARED; + + error = xchk_rtgroup_lock(sc, &sc->sr, flags); if (error) return error; From 540ddc626245f12f56326ee0c1601f71ebb41d64 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 13 Jul 2026 23:06:12 -0700 Subject: [PATCH 0993/1054] xfs: set xfarray killable sort correctly LOLLM noticed that we *disable* interruptible sorts when the KILLABLE flag is set. This is backwards. Fix the incorrect logic, and rename the variable to make the connection more obvious. Cc: stable@vger.kernel.org # v6.10 Fixes: 271557de7cbfde ("xfs: reduce the rate of cond_resched calls inside scrub") Signed-off-by: "Darrick J. Wong" Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/scrub.h | 6 +++--- fs/xfs/scrub/xfarray.c | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/fs/xfs/scrub/scrub.h b/fs/xfs/scrub/scrub.h index a3f1abc91390..6d7d3523b71f 100644 --- a/fs/xfs/scrub/scrub.h +++ b/fs/xfs/scrub/scrub.h @@ -11,7 +11,7 @@ struct xfs_scrub; struct xchk_relax { unsigned long next_resched; unsigned int resched_nr; - bool interruptible; + bool killable; }; /* Yield to the scheduler at most 10x per second. */ @@ -21,7 +21,7 @@ struct xchk_relax { (struct xchk_relax){ \ .next_resched = XCHK_RELAX_NEXT, \ .resched_nr = 0, \ - .interruptible = true, \ + .killable = true, \ } /* @@ -45,7 +45,7 @@ static inline int xchk_maybe_relax(struct xchk_relax *widget) widget->next_resched = XCHK_RELAX_NEXT; } - if (widget->interruptible && fatal_signal_pending(current)) + if (widget->killable && fatal_signal_pending(current)) return -EINTR; return 0; diff --git a/fs/xfs/scrub/xfarray.c b/fs/xfs/scrub/xfarray.c index c7c4a71b6fa7..2ce24bfe4c0f 100644 --- a/fs/xfs/scrub/xfarray.c +++ b/fs/xfs/scrub/xfarray.c @@ -487,8 +487,7 @@ xfarray_sortinfo_alloc( xfarray_sortinfo_lo(si)[0] = 0; xfarray_sortinfo_hi(si)[0] = array->nr - 1; si->relax = INIT_XCHK_RELAX; - if (flags & XFARRAY_SORT_KILLABLE) - si->relax.interruptible = false; + si->relax.killable = !!(flags & XFARRAY_SORT_KILLABLE); trace_xfarray_sort(si, nr_bytes); *infop = si; From 5d72a68f2007ba2a968d6bf47dba3f4620bd182e Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 13 Jul 2026 23:06:28 -0700 Subject: [PATCH 0994/1054] xfs: fix off-by-one error when calling xchk_xref_has_rt_owner LOLLM noticed an off-by-one error when computing the length of the rtrmap to cross-check. Cc: stable@vger.kernel.org # v6.14 Fixes: 037a44d8277adf ("xfs: cross-reference the realtime rmapbt") Signed-off-by: "Darrick J. Wong" Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/rtbitmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/xfs/scrub/rtbitmap.c b/fs/xfs/scrub/rtbitmap.c index de3f22f310f7..52c24d3d4be6 100644 --- a/fs/xfs/scrub/rtbitmap.c +++ b/fs/xfs/scrub/rtbitmap.c @@ -258,7 +258,7 @@ xchk_rtbitmap( * the last free extent we saw and the last possible extent in the rt * group. */ - last_rgbno = rtg->rtg_extents * mp->m_sb.sb_rextsize - 1; + last_rgbno = rtg->rtg_extents * mp->m_sb.sb_rextsize; if (rtb->next_free_rgbno < last_rgbno) xchk_xref_has_rt_owner(sc, rtb->next_free_rgbno, last_rgbno - rtb->next_free_rgbno); From 353a5900bc85234e7df05caac37698042dc26348 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 13 Jul 2026 23:06:43 -0700 Subject: [PATCH 0995/1054] xfs: handle non-inode owners for rtrmap record checking LOLLM noticed that two helper functions in the rtrmapbt scrub code don't actually handle non-inode owners correctly -- CoW staging extents and rgsuperblock extents are not shareable, but they are mergeable. Fix these two helpers. Cc: stable@vger.kernel.org # v6.14 Fixes: 2d9a3e98053e8c ("xfs: allow overlapping rtrmapbt records for shared data extents") Signed-off-by: "Darrick J. Wong" Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/rtrmap.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fs/xfs/scrub/rtrmap.c b/fs/xfs/scrub/rtrmap.c index b3b2cf17ba2c..564d19a97a2f 100644 --- a/fs/xfs/scrub/rtrmap.c +++ b/fs/xfs/scrub/rtrmap.c @@ -87,6 +87,9 @@ xchk_rtrmapbt_is_shareable( return false; if (irec->rm_flags & XFS_RMAP_UNWRITTEN) return false; + if (irec->rm_owner == XFS_RMAP_OWN_COW || + irec->rm_owner == XFS_RMAP_OWN_FS) + return false; return true; } @@ -146,6 +149,9 @@ xchk_rtrmap_mergeable( return false; if (r1->rm_flags != r2->rm_flags) return false; + if (r1->rm_owner == XFS_RMAP_OWN_COW || + r1->rm_owner == XFS_RMAP_OWN_FS) + return true; return r1->rm_offset + r1->rm_blockcount == r2->rm_offset; } From ba150ce63453ccd74bae1404c1dfedbd01ecfd55 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 13 Jul 2026 23:06:59 -0700 Subject: [PATCH 0996/1054] xfs: fully check the parent handle when it points to the rootdir LOLLM noticed that the directory tree path checking declares the path to be ok if the inumber in the parent pointer reaches the root directory. Unfortunately, it neglects to check that the generation is correct. Fix that by moving the generation check up. Cc: stable@vger.kernel.org # v6.10 Fixes: 928b721a11789a ("xfs: teach online scrub to find directory tree structure problems") Signed-off-by: "Darrick J. Wong" Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/dirtree.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/fs/xfs/scrub/dirtree.c b/fs/xfs/scrub/dirtree.c index c6a210f7508f..b2cf6e5439d9 100644 --- a/fs/xfs/scrub/dirtree.c +++ b/fs/xfs/scrub/dirtree.c @@ -383,6 +383,14 @@ xchk_dirpath_step_up( goto out_scanlock; } + /* The handle encoded in the parent pointer must match. */ + if (VFS_I(dp)->i_generation != be32_to_cpu(dl->pptr_rec.p_gen)) { + trace_xchk_dirpath_badgen(dl->sc, dp, path->path_nr, + path->nr_steps, &dl->xname, &dl->pptr_rec); + error = -EFSCORRUPTED; + goto out_scanlock; + } + /* We've reached the root directory; the path is ok. */ if (parent_ino == dl->root_ino) { xchk_dirpath_set_outcome(dl, path, XCHK_DIRPATH_OK); @@ -411,14 +419,6 @@ xchk_dirpath_step_up( goto out_scanlock; } - /* The handle encoded in the parent pointer must match. */ - if (VFS_I(dp)->i_generation != be32_to_cpu(dl->pptr_rec.p_gen)) { - trace_xchk_dirpath_badgen(dl->sc, dp, path->path_nr, - path->nr_steps, &dl->xname, &dl->pptr_rec); - error = -EFSCORRUPTED; - goto out_scanlock; - } - /* Parent pointer must point up to a directory. */ if (!S_ISDIR(VFS_I(dp)->i_mode)) { trace_xchk_dirpath_nondir_parent(dl->sc, dp, path->path_nr, From 15e38a9366b31d3d61081ead115f1dff59379e24 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 13 Jul 2026 23:07:15 -0700 Subject: [PATCH 0997/1054] xfs: clamp timestamp nanoseconds correctly LOLLM noticed an off-by-one error in the nsec clamping; fix that so that we never have tv_nsec == 1e9. Cc: stable@vger.kernel.org # v6.8 Fixes: 2d295fe65776d1 ("xfs: repair inode records") Signed-off-by: "Darrick J. Wong" Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/inode_repair.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/xfs/scrub/inode_repair.c b/fs/xfs/scrub/inode_repair.c index 493dcf5cc6c1..5565e80691a6 100644 --- a/fs/xfs/scrub/inode_repair.c +++ b/fs/xfs/scrub/inode_repair.c @@ -1757,7 +1757,7 @@ xrep_clamp_timestamp( struct xfs_inode *ip, struct timespec64 *ts) { - ts->tv_nsec = clamp_t(long, ts->tv_nsec, 0, NSEC_PER_SEC); + ts->tv_nsec = clamp_t(long, ts->tv_nsec, 0, NSEC_PER_SEC - 1); *ts = timestamp_truncate(*ts, VFS_I(ip)); } From 59c462b0f5cfa107794228051724b34ae9334168 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 13 Jul 2026 23:07:30 -0700 Subject: [PATCH 0998/1054] xfs: don't zap bmbt forks if they are MAXLEVELS tall LOLLM noticed a discrepancy between the bmbt level checks in the libxfs bmbt code vs. the inode repair code. We do actually allow a bmbt root that proclaims to have a height of XFS_BM_MAXLEVELS. Cc: stable@vger.kernel.org # v6.8 Fixes: e744cef2060559 ("xfs: zap broken inode forks") Signed-off-by: "Darrick J. Wong" Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/inode_repair.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/xfs/scrub/inode_repair.c b/fs/xfs/scrub/inode_repair.c index 5565e80691a6..3ec41c198351 100644 --- a/fs/xfs/scrub/inode_repair.c +++ b/fs/xfs/scrub/inode_repair.c @@ -921,7 +921,7 @@ xrep_dinode_bad_bmbt_fork( if (nrecs == 0 || xfs_bmdr_space_calc(nrecs) > dfork_size) return true; - if (level == 0 || level >= XFS_BM_MAXLEVELS(sc->mp, whichfork)) + if (level == 0 || level > XFS_BM_MAXLEVELS(sc->mp, whichfork)) return true; dmxr = xfs_bmdr_maxrecs(dfork_size, 0); From 1e5185c090589f4146d728ab36417d8a5419f127 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Thu, 7 May 2026 10:22:23 +0200 Subject: [PATCH 0999/1054] can: raw: add locking for raw flags bitfield With commit 890e5198a6e5 ("can: raw: use bitfields to store flags in struct raw_sock") the formerly separate integer values have been integrated into a single bitfield. This led to a read-modify-write operation when changing a flag in raw_setsockopt() which now needs a locking to prevent concurrent access. Instead of adding a lock/unlock hell in each of the flag manipulations this patch introduces a wrapper for a new raw_setsockopt_locked() function analogue to the isotp_setsockopt[_locked]() approach in net/can/isotp.c Fixes: 890e5198a6e5 ("can: raw: use bitfields to store flags in struct raw_sock") Reported-by: Eulgyu Kim Closes: https://lore.kernel.org/linux-can/20260503112200.22727-1-eulgyukim@snu.ac.kr/ Tested-by: Eulgyu Kim Signed-off-by: Oliver Hartkopp Reviewed-by: Vincent Mailhol Tested-by: Vincent Mailhol Link: https://patch.msgid.link/20260504111928.41856-1-socketcan@hartkopp.net [mkl: use Closes tag instead of Link] Signed-off-by: Marc Kleine-Budde --- net/can/raw.c | 66 +++++++++++++++++++++++---------------------------- 1 file changed, 30 insertions(+), 36 deletions(-) diff --git a/net/can/raw.c b/net/can/raw.c index a26942e78e68..82d9c0499c95 100644 --- a/net/can/raw.c +++ b/net/can/raw.c @@ -562,8 +562,8 @@ static int raw_getname(struct socket *sock, struct sockaddr *uaddr, return RAW_MIN_NAMELEN; } -static int raw_setsockopt(struct socket *sock, int level, int optname, - sockptr_t optval, unsigned int optlen) +static int raw_setsockopt_locked(struct socket *sock, int optname, + sockptr_t optval, unsigned int optlen) { struct sock *sk = sock->sk; struct raw_sock *ro = raw_sk(sk); @@ -575,9 +575,6 @@ static int raw_setsockopt(struct socket *sock, int level, int optname, int flag; int err = 0; - if (level != SOL_CAN_RAW) - return -EINVAL; - switch (optname) { case CAN_RAW_FILTER: if (optlen % sizeof(struct can_filter) != 0) @@ -598,17 +595,11 @@ static int raw_setsockopt(struct socket *sock, int level, int optname, return -EFAULT; } - rtnl_lock(); - lock_sock(sk); - dev = ro->dev; - if (ro->bound && dev) { - if (dev->reg_state != NETREG_REGISTERED) { - if (count > 1) - kfree(filter); - err = -ENODEV; - goto out_fil; - } + if (ro->bound && dev && dev->reg_state != NETREG_REGISTERED) { + if (count > 1) + kfree(filter); + return -ENODEV; } if (ro->bound) { @@ -622,7 +613,7 @@ static int raw_setsockopt(struct socket *sock, int level, int optname, if (err) { if (count > 1) kfree(filter); - goto out_fil; + return err; } /* remove old filter registrations */ @@ -642,11 +633,6 @@ static int raw_setsockopt(struct socket *sock, int level, int optname, } ro->filter = filter; ro->count = count; - - out_fil: - release_sock(sk); - rtnl_unlock(); - break; case CAN_RAW_ERR_FILTER: @@ -658,16 +644,9 @@ static int raw_setsockopt(struct socket *sock, int level, int optname, err_mask &= CAN_ERR_MASK; - rtnl_lock(); - lock_sock(sk); - dev = ro->dev; - if (ro->bound && dev) { - if (dev->reg_state != NETREG_REGISTERED) { - err = -ENODEV; - goto out_err; - } - } + if (ro->bound && dev && dev->reg_state != NETREG_REGISTERED) + return -ENODEV; /* remove current error mask */ if (ro->bound) { @@ -676,7 +655,7 @@ static int raw_setsockopt(struct socket *sock, int level, int optname, err_mask); if (err) - goto out_err; + return err; /* remove old err_mask registration */ raw_disable_errfilter(sock_net(sk), dev, sk, @@ -685,11 +664,6 @@ static int raw_setsockopt(struct socket *sock, int level, int optname, /* link new err_mask to the socket */ ro->err_mask = err_mask; - - out_err: - release_sock(sk); - rtnl_unlock(); - break; case CAN_RAW_LOOPBACK: @@ -769,6 +743,26 @@ static int raw_setsockopt(struct socket *sock, int level, int optname, return err; } +static int raw_setsockopt(struct socket *sock, int level, int optname, + sockptr_t optval, unsigned int optlen) +{ + struct sock *sk = sock->sk; + int err; + + if (level != SOL_CAN_RAW) + return -EINVAL; + + rtnl_lock(); + lock_sock(sk); + + err = raw_setsockopt_locked(sock, optname, optval, optlen); + + release_sock(sk); + rtnl_unlock(); + + return err; +} + static int raw_getsockopt(struct socket *sock, int level, int optname, sockopt_t *opt) { From 9fe595fad54d4ac6a402edb3f60bec859d52cea6 Mon Sep 17 00:00:00 2001 From: Ondrej Mosnacek Date: Tue, 14 Jul 2026 14:57:59 +0200 Subject: [PATCH 1000/1054] selinux: fix incorrect execmem checks on overlayfs The commit fixing the overlayfs mmap() and mprotect() access checks failed to skip the execmem check in __file_map_prot_check() for the case where the "mounter check" is being performed. This check should be performed only against the credentials of the task that is calling mmap()/mprotect(), since it doesn't pertain to the file itself, but rather just gates the ability of the calling task to get an executable memory mapping in general. The purpose of the "mounter check" is to guard against using an overlayfs mount to gain file access that would otherwise be denied to the mounter. For execmem this is not relevant, as there is no further file access granted based on it (notice that the file's context is not used as the target in the check), so checking it also against the mounter credentials would be incorrect. Fix this by passing a boolean to [__]file_map_prot_check() and selinux_mmap_file_common() that indicates if we are doing the "mounter check" and skiping the execmem check in that case. Since this boolean also indicates if we use current_cred() or the mounter cred as the subject, also remove the "cred" argument from these functions and determine it based on the boolean and the file struct. Cc: stable@vger.kernel.org Fixes: 82544d36b172 ("selinux: fix overlayfs mmap() and mprotect() access checks") Signed-off-by: Ondrej Mosnacek Reviewed-by: Stephen Smalley Signed-off-by: Paul Moore --- security/selinux/hooks.c | 42 +++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 70a3388c047d..8d6945edae7a 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3969,9 +3969,9 @@ static int selinux_file_ioctl_compat(struct file *file, unsigned int cmd, static int default_noexec __ro_after_init; -static int __file_map_prot_check(const struct cred *cred, - const struct file *file, unsigned long prot, - bool shared, bool bf_user_file) +static int __file_map_prot_check(const struct file *file, unsigned long prot, + bool shared, bool mounter_check, + bool bf_user_file) { struct inode *inode = NULL; bool prot_exec = prot & PROT_EXEC; @@ -3984,10 +3984,10 @@ static int __file_map_prot_check(const struct cred *cred, inode = file_inode(file); } - if (default_noexec && prot_exec && + if (!mounter_check && default_noexec && prot_exec && (!file || IS_PRIVATE(inode) || (!shared && prot_write))) { int rc; - u32 sid = cred_sid(cred); + u32 sid = current_sid(); /* * We are making executable an anonymous mapping or a private @@ -4000,6 +4000,8 @@ static int __file_map_prot_check(const struct cred *cred, } if (file) { + const struct cred *cred = mounter_check ? + file->f_cred : current_cred(); /* "read" always possible, "write" only if shared */ u32 av = FILE__READ; if (shared && prot_write) @@ -4013,11 +4015,11 @@ static int __file_map_prot_check(const struct cred *cred, return 0; } -static inline int file_map_prot_check(const struct cred *cred, - const struct file *file, - unsigned long prot, bool shared) +static inline int file_map_prot_check(const struct file *file, + unsigned long prot, bool shared, + bool mounter_check) { - return __file_map_prot_check(cred, file, prot, shared, false); + return __file_map_prot_check(file, prot, shared, mounter_check, false); } static int selinux_mmap_addr(unsigned long addr) @@ -4033,12 +4035,14 @@ static int selinux_mmap_addr(unsigned long addr) return rc; } -static int selinux_mmap_file_common(const struct cred *cred, struct file *file, - unsigned long prot, bool shared) +static int selinux_mmap_file_common(struct file *file, unsigned long prot, + bool shared, bool mounter_check) { if (file) { int rc; struct common_audit_data ad; + const struct cred *cred = mounter_check ? + file->f_cred : current_cred(); ad.type = LSM_AUDIT_DATA_FILE; ad.u.file = file; @@ -4047,15 +4051,16 @@ static int selinux_mmap_file_common(const struct cred *cred, struct file *file, return rc; } - return file_map_prot_check(cred, file, prot, shared); + return file_map_prot_check(file, prot, shared, mounter_check); } static int selinux_mmap_file(struct file *file, unsigned long reqprot __always_unused, unsigned long prot, unsigned long flags) { - return selinux_mmap_file_common(current_cred(), file, prot, - (flags & MAP_TYPE) == MAP_SHARED); + return selinux_mmap_file_common(file, prot, + (flags & MAP_TYPE) == MAP_SHARED, + false); } /** @@ -4087,8 +4092,9 @@ static int selinux_mmap_backing_file(struct vm_area_struct *vma, if (vma->vm_flags & VM_EXEC) prot |= PROT_EXEC; - return selinux_mmap_file_common(backing_file->f_cred, backing_file, - prot, vma->vm_flags & VM_SHARED); + return selinux_mmap_file_common(backing_file, prot, + vma->vm_flags & VM_SHARED, + true); } static int selinux_file_mprotect(struct vm_area_struct *vma, @@ -4149,11 +4155,11 @@ static int selinux_file_mprotect(struct vm_area_struct *vma, } } - rc = __file_map_prot_check(cred, file, prot, shared, backing_file); + rc = __file_map_prot_check(file, prot, shared, false, backing_file); if (rc) return rc; if (backing_file) { - rc = file_map_prot_check(file->f_cred, file, prot, shared); + rc = file_map_prot_check(file, prot, shared, true); if (rc) return rc; } From 25957f7c3dac3265332d766b71233e3622f17e14 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Wed, 3 Jun 2026 21:33:09 -0700 Subject: [PATCH 1001/1054] powerpc/85xx: Add fsl,ifc to common device ids Add fsl,ifc to mpc85xx_common_ids so that of_platform_bus_probe creates a platform device for the IFC node even without 'simple-bus' in its compatible property. On P1010 and similar platforms the IFC node is a direct child of the root, so it must be explicitly matched to be populated. Fixes: 0bf51cc9e9e5 ("powerpc: dts: mpc85xx: remove "simple-bus" compatible from ifc node") Assisted-by: opencode:big-pickle Signed-off-by: Rosen Penev Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260604043309.91280-1-rosenp@gmail.com --- arch/powerpc/platforms/85xx/common.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/powerpc/platforms/85xx/common.c b/arch/powerpc/platforms/85xx/common.c index 757811155587..c11deb2f50ed 100644 --- a/arch/powerpc/platforms/85xx/common.c +++ b/arch/powerpc/platforms/85xx/common.c @@ -42,6 +42,8 @@ static const struct of_device_id mpc85xx_common_ids[] __initconst = { { .compatible = "fsl,qoriq-pcie-v2.3", }, { .compatible = "fsl,qoriq-pcie-v2.2", }, { .compatible = "fsl,fman", }, + /* IFC NAND and NOR controllers */ + { .compatible = "fsl,ifc", }, {}, }; From c1c1ffa490fc33591e90852ed0d38804dd20bc36 Mon Sep 17 00:00:00 2001 From: Shrikanth Hegde Date: Fri, 5 Jun 2026 18:13:29 +0530 Subject: [PATCH 1002/1054] powerpc/vtime: Initialize starttime at boot for native accounting It was observed that /proc/stat had very large value for one ore more CPUs. It was more visible after recent code simplifications around cpustats. System has 240 CPUs. cat /proc/uptime; 194.18 46500.55 cat /proc/stat cpu 5966 39 837032887 4650070 164 185 100 0 0 0 cpu0 108 0 837030890 19109 24 4 23 0 0 0 Since uptime is 194s, system time of each CPU can't be more than 19400. Sum of system time of all CPUs can't be more than 19400*240 4656000. In fact huge value is close to mftb(). Note mftb doesn't reset on powerVM when the LPAR restart. It only resets when whole system resets. The same issue exists for kexec too. This happens since starttime is not setup at init time. Once it is set then subsequent vtime_delta will return the right delta. Fix it by initializing the starttime during CPU initialization. This fixes the large times seen. cat /proc/uptime; cat /proc/stat 15.78 3694.63 cpu 6035 35 1347 369479 23 144 49 0 0 0 cpu0 19 0 38 1508 0 1 14 0 0 0 Now, system time is reported as expected. Fixes: cf9efce0ce31 ("powerpc: Account time using timebase rather than PURR") Reviewed-by: Christophe Leroy (CS GROUP) Suggested-by: Christophe Leroy (CS GROUP) Signed-off-by: Shrikanth Hegde Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260605124329.377533-1-sshegde@linux.ibm.com --- arch/powerpc/kernel/time.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c index 3460d1a5a97c..11145c40183d 100644 --- a/arch/powerpc/kernel/time.c +++ b/arch/powerpc/kernel/time.c @@ -377,7 +377,6 @@ void vtime_task_switch(struct task_struct *prev) } } -#ifdef CONFIG_NO_HZ_COMMON /** * vtime_reset - Fast forward vtime entry clocks * @@ -394,6 +393,7 @@ void vtime_reset(void) #endif } +#ifdef CONFIG_NO_HZ_COMMON /** * vtime_dyntick_start - Inform vtime about entry to idle-dynticks * @@ -933,6 +933,7 @@ static void __init set_decrementer_max(void) static void __init init_decrementer_clockevent(void) { register_decrementer_clockevent(smp_processor_id()); + vtime_reset(); } void secondary_cpu_time_init(void) @@ -948,6 +949,7 @@ void secondary_cpu_time_init(void) /* FIME: Should make unrelated change to move snapshot_timebase * call here ! */ register_decrementer_clockevent(smp_processor_id()); + vtime_reset(); } /* From d610d3ab18197d87618da11ec5fe8b3cebf32208 Mon Sep 17 00:00:00 2001 From: Ethan Nelson-Moore Date: Mon, 15 Jun 2026 16:37:26 -0700 Subject: [PATCH 1003/1054] powerpc/uaccess: correct check for CONFIG_PPC_E500 in mask_user_address() mask_user_address() incorrectly checks for CONFIG_E500 instead of CONFIG_PPC_E500, causing mask_user_address_isel() to not be used on E500 hardware. Fix the check to use the correct name. Fixes: 861574d51bbd ("powerpc/uaccess: Implement masked user access") Cc: stable@vger.kernel.org # 7.0+ Signed-off-by: Ethan Nelson-Moore Fixes: 861574d51bbd ("powerpc/uaccess: Implement masked user access") Reviewed-by: Christophe Leroy (CS GROUP) Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260615233729.29386-1-enelsonmoore@gmail.com --- arch/powerpc/include/asm/uaccess.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/include/asm/uaccess.h b/arch/powerpc/include/asm/uaccess.h index 7b8c56962c31..49039074b33f 100644 --- a/arch/powerpc/include/asm/uaccess.h +++ b/arch/powerpc/include/asm/uaccess.h @@ -537,7 +537,7 @@ static inline void __user *mask_user_address(const void __user *ptr) if (IS_ENABLED(CONFIG_PPC64)) return mask_user_address_simple(ptr); - if (IS_ENABLED(CONFIG_E500)) + if (IS_ENABLED(CONFIG_PPC_E500)) return mask_user_address_isel(ptr); if (TASK_SIZE <= UL(SZ_2G) && border >= UL(SZ_2G)) return mask_user_address_simple(ptr); From bd83c98b988d2c560531084e296dbfb530aff829 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Sun, 14 Jun 2026 16:23:56 +0200 Subject: [PATCH 1004/1054] powerpc/pseries: fix memory leak on krealloc failure in papr_init When krealloc() fails, free the original esi_buf before returning to avoid a memory leak. Fixes: 3c14b73454cf ("powerpc/pseries: Interface to represent PAPR firmware attributes") Cc: stable@vger.kernel.org Signed-off-by: Thorsten Blum Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260614142356.658212-2-thorsten.blum@linux.dev --- arch/powerpc/platforms/pseries/papr_platform_attributes.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/arch/powerpc/platforms/pseries/papr_platform_attributes.c b/arch/powerpc/platforms/pseries/papr_platform_attributes.c index c6159870de0e..9c3758aa54c6 100644 --- a/arch/powerpc/platforms/pseries/papr_platform_attributes.c +++ b/arch/powerpc/platforms/pseries/papr_platform_attributes.c @@ -271,11 +271,9 @@ static int __init papr_init(void) esi_buf_size = ESI_HDR_SIZE + (CURR_MAX_ESI_ATTRS * max_esi_attrs); temp_esi_buf = krealloc(esi_buf, esi_buf_size, GFP_KERNEL); - if (temp_esi_buf) - esi_buf = temp_esi_buf; - else - return -ENOMEM; - + if (!temp_esi_buf) + goto out_free_esi_buf; + esi_buf = temp_esi_buf; goto retry; } From e4de1b9cb3b5c981e4fe9bca253a7fb9161f5acd Mon Sep 17 00:00:00 2001 From: Amit Machhiwal Date: Sun, 14 Jun 2026 23:04:37 +0530 Subject: [PATCH 1005/1054] powerpc/dt_cpu_ftrs: Set CPU_FTR_P11_PVR for Power11 and later processors When using device tree CPU features (dt-cpu-ftrs), the kernel bypasses the traditional cputable-based CPU identification and instead derives CPU features from the device tree's "ibm,powerpc-cpu-features" node provided by firmware. However, CPU_FTR_P11_PVR is a kernel-internal feature flag used to identify Power11 and later processors, and is not represented in the device tree's ISA feature set. While ISA v3.1 support (indicated by CPU_FTR_ARCH_31) is present on both Power10 and Power11, the CPU_FTR_P11_PVR flag is specifically needed by code that must distinguish between Power10 and Power11 processors. Without this flag set, code that checks for Power11 using cpu_has_feature(CPU_FTR_P11_PVR) will incorrectly return false on Power11+ systems using dt-cpu-ftrs, leading to incorrect behavior. This issue manifests specifically in powernv environments (bare-metal or QEMU TCG with powernv machine type), where skiboot/OPAL firmware provides the "ibm,powerpc-cpu-features" node, causing the kernel to use dt-cpu-ftrs. The issue does not affect pseries guests, where SLOF firmware does not provide this node, causing the kernel to fall back to the traditional cputable path (identify_cpu) which correctly sets CPU_FTR_P11_PVR during PVR-based CPU identification. In powernv TCG guests, the missing flag causes KVM code to trigger warnings when attempting to create KVM guests, as cpu_features shows 0x000c00eb8f4fb187 (missing bit 53) instead of the correct 0x002c00eb8f4fb187 (with bit 53 set). Fix this by setting CPU_FTR_P11_PVR for all processors with PVR >= PVR_POWER11 when ISA v3.1 support is detected in cpufeatures_setup_start(). This approach ensures forward compatibility with future processor generations. Fixes: 96e266e3bcd6 ("KVM: PPC: Book3S HV: Add Power11 capability support for Nested PAPR guests") Cc: stable@vger.kernel.org # v6.13+ Signed-off-by: Amit Machhiwal Reviewed-by: Mukesh Kumar Chaurasiya (IBM) Reviewed-by: Christophe Leroy (CS GROUP) Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260614173437.26352-1-amachhiw@linux.ibm.com --- arch/powerpc/kernel/dt_cpu_ftrs.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/arch/powerpc/kernel/dt_cpu_ftrs.c b/arch/powerpc/kernel/dt_cpu_ftrs.c index 3af6c06af02f..e5853daa6a48 100644 --- a/arch/powerpc/kernel/dt_cpu_ftrs.c +++ b/arch/powerpc/kernel/dt_cpu_ftrs.c @@ -704,6 +704,15 @@ static void __init cpufeatures_setup_start(u32 isa) if (isa >= ISA_V3_1) { cur_cpu_spec->cpu_features |= CPU_FTR_ARCH_31; cur_cpu_spec->cpu_user_features2 |= PPC_FEATURE2_ARCH_3_1; + + /* + * CPU_FTR_P11_PVR is a kernel-internal flag to identify + * Power11 and later processors. While ISA v3.1 is supported + * by Power10+, this flag specifically indicates Power11+ + * for code that needs to distinguish between P10 and P11. + */ + if (PVR_VER(mfspr(SPRN_PVR)) >= PVR_POWER11) + cur_cpu_spec->cpu_features |= CPU_FTR_P11_PVR; } } From a2c02aa0c6ca3ec9fab6f1c99912a440c7b8bfdb Mon Sep 17 00:00:00 2001 From: "Christophe Leroy (CS GROUP)" Date: Fri, 19 Jun 2026 14:08:28 +0200 Subject: [PATCH 1006/1054] powerpc: Remove dead non-preemption code Since commit 7dadeaa6e851 ("sched: Further restrict the preemption modes"), powerpc always has CONFIG_PREEMPTION because only CONFIG_PREEMPT and CONFIG_PREEMPT_LAZY are possible, even in dynamic preemption mode (see sched_dynamic_mode). As a consequence, need_irq_preemption() is always true and can be removed. And because commit bee25f97ad24 ("powerpc: Enable GENERIC_ENTRY feature") includes linux/irq-entry-common.h which already declares sk_dynamic_irqentry_exit_cond_resched static key, asm/preempt.h becauses useless and can be removed. Signed-off-by: Christophe Leroy (CS GROUP) Reviewed-by: Shrikanth Hegde Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/2bf10a0afffefb6aca44bf2f864cc17471a80e31.1781870889.git.chleroy@kernel.org --- arch/powerpc/include/asm/preempt.h | 16 ---------------- arch/powerpc/lib/vmx-helper.c | 2 +- 2 files changed, 1 insertion(+), 17 deletions(-) delete mode 100644 arch/powerpc/include/asm/preempt.h diff --git a/arch/powerpc/include/asm/preempt.h b/arch/powerpc/include/asm/preempt.h deleted file mode 100644 index 000e2b9681f3..000000000000 --- a/arch/powerpc/include/asm/preempt.h +++ /dev/null @@ -1,16 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __ASM_POWERPC_PREEMPT_H -#define __ASM_POWERPC_PREEMPT_H - -#include - -#if defined(CONFIG_PREEMPT_DYNAMIC) -#include -DECLARE_STATIC_KEY_TRUE(sk_dynamic_irqentry_exit_cond_resched); -#define need_irq_preemption() \ - (static_branch_unlikely(&sk_dynamic_irqentry_exit_cond_resched)) -#else -#define need_irq_preemption() (IS_ENABLED(CONFIG_PREEMPTION)) -#endif - -#endif /* __ASM_POWERPC_PREEMPT_H */ diff --git a/arch/powerpc/lib/vmx-helper.c b/arch/powerpc/lib/vmx-helper.c index 57e897b60db8..cc9fb72cb4eb 100644 --- a/arch/powerpc/lib/vmx-helper.c +++ b/arch/powerpc/lib/vmx-helper.c @@ -46,7 +46,7 @@ int exit_vmx_usercopy(void) * set and we are preemptible. The hack here is to schedule a * decrementer to fire here and reschedule for us if necessary. */ - if (need_irq_preemption() && need_resched()) + if (need_resched()) set_dec(1); return 0; } From b8de511f0e80a2ab9d23df73d147423968ebf102 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 14 Jul 2026 14:21:20 +0200 Subject: [PATCH 1007/1054] smb/client: Use EXPORT_SYMBOL_IF_KUNIT() to export symbols Replace EXPORT_SYMBOL_FOR_MODULES() with EXPORT_SYMBOL_IF_KUNIT() to mark the symbols as visible only if CONFIG_KUNIT is enabled. Kunit test should import the namespace EXPORTED_FOR_KUNIT_TESTING to use these marked symbols. This is the standard way for all KUnit tests. Signed-off-by: Andy Shevchenko Reviewed-by: ChenXiaoSong Signed-off-by: Steve French --- fs/smb/client/smb1maperror.c | 24 ++++++++++++------------ fs/smb/client/smb1maperror_test.c | 1 + 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/fs/smb/client/smb1maperror.c b/fs/smb/client/smb1maperror.c index 74530088d17d..ab3d09613c91 100644 --- a/fs/smb/client/smb1maperror.c +++ b/fs/smb/client/smb1maperror.c @@ -10,6 +10,9 @@ */ #include + +#include + #include "cifsproto.h" #include "smb1proto.h" #include "smberr.h" @@ -239,48 +242,45 @@ int __init smb1_init_maperror(void) } #if IS_ENABLED(CONFIG_SMB1_KUNIT_TESTS) -#define EXPORT_SYMBOL_FOR_SMB_TEST(sym) \ - EXPORT_SYMBOL_FOR_MODULES(sym, "smb1maperror_test") - const struct ntstatus_to_dos_err * search_ntstatus_to_dos_map_test(__u32 ntstatus) { return search_ntstatus_to_dos_map(ntstatus); } -EXPORT_SYMBOL_FOR_SMB_TEST(search_ntstatus_to_dos_map_test); +EXPORT_SYMBOL_IF_KUNIT(search_ntstatus_to_dos_map_test); const struct ntstatus_to_dos_err * ntstatus_to_dos_map_test = ntstatus_to_dos_map; -EXPORT_SYMBOL_FOR_SMB_TEST(ntstatus_to_dos_map_test); +EXPORT_SYMBOL_IF_KUNIT(ntstatus_to_dos_map_test); unsigned int ntstatus_to_dos_num = ARRAY_SIZE(ntstatus_to_dos_map); -EXPORT_SYMBOL_FOR_SMB_TEST(ntstatus_to_dos_num); +EXPORT_SYMBOL_IF_KUNIT(ntstatus_to_dos_num); const struct smb_to_posix_error * search_mapping_table_ERRDOS_test(__u16 smb_err) { return search_mapping_table_ERRDOS(smb_err); } -EXPORT_SYMBOL_FOR_SMB_TEST(search_mapping_table_ERRDOS_test); +EXPORT_SYMBOL_IF_KUNIT(search_mapping_table_ERRDOS_test); const struct smb_to_posix_error * mapping_table_ERRDOS_test = mapping_table_ERRDOS; -EXPORT_SYMBOL_FOR_SMB_TEST(mapping_table_ERRDOS_test); +EXPORT_SYMBOL_IF_KUNIT(mapping_table_ERRDOS_test); unsigned int mapping_table_ERRDOS_num = ARRAY_SIZE(mapping_table_ERRDOS); -EXPORT_SYMBOL_FOR_SMB_TEST(mapping_table_ERRDOS_num); +EXPORT_SYMBOL_IF_KUNIT(mapping_table_ERRDOS_num); const struct smb_to_posix_error * search_mapping_table_ERRSRV_test(__u16 smb_err) { return search_mapping_table_ERRSRV(smb_err); } -EXPORT_SYMBOL_FOR_SMB_TEST(search_mapping_table_ERRSRV_test); +EXPORT_SYMBOL_IF_KUNIT(search_mapping_table_ERRSRV_test); const struct smb_to_posix_error * mapping_table_ERRSRV_test = mapping_table_ERRSRV; -EXPORT_SYMBOL_FOR_SMB_TEST(mapping_table_ERRSRV_test); +EXPORT_SYMBOL_IF_KUNIT(mapping_table_ERRSRV_test); unsigned int mapping_table_ERRSRV_num = ARRAY_SIZE(mapping_table_ERRSRV); -EXPORT_SYMBOL_FOR_SMB_TEST(mapping_table_ERRSRV_num); +EXPORT_SYMBOL_IF_KUNIT(mapping_table_ERRSRV_num); #endif diff --git a/fs/smb/client/smb1maperror_test.c b/fs/smb/client/smb1maperror_test.c index 2caaf11228ef..903c46f71291 100644 --- a/fs/smb/client/smb1maperror_test.c +++ b/fs/smb/client/smb1maperror_test.c @@ -75,3 +75,4 @@ kunit_test_suite(maperror_suite); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("KUnit tests of SMB1 maperror"); +MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); From e2e08effef2cd5d5d27b44d6239718ada614eb62 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 14 Jul 2026 15:53:33 +0200 Subject: [PATCH 1008/1054] smb/client: Use EXPORT_SYMBOL_IF_KUNIT() to export symbols in SMB2 Replace EXPORT_SYMBOL_FOR_MODULES() with EXPORT_SYMBOL_IF_KUNIT() to mark the symbols as visible only if CONFIG_KUNIT is enabled. Kunit test should import the namespace EXPORTED_FOR_KUNIT_TESTING to use these marked symbols. This is the standard way for all KUnit tests. Signed-off-by: Andy Shevchenko Reviewed-by: ChenXiaoSong Signed-off-by: Steve French --- fs/smb/client/smb2maperror.c | 12 ++++++------ fs/smb/client/smb2maperror_test.c | 1 + 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/fs/smb/client/smb2maperror.c b/fs/smb/client/smb2maperror.c index 9ed21f7b618c..d86f2460d0e5 100644 --- a/fs/smb/client/smb2maperror.c +++ b/fs/smb/client/smb2maperror.c @@ -8,6 +8,9 @@ * */ #include + +#include + #include "cifsproto.h" #include "cifs_debug.h" #include "smb2proto.h" @@ -109,18 +112,15 @@ int __init smb2_init_maperror(void) } #if IS_ENABLED(CONFIG_SMB_KUNIT_TESTS) -#define EXPORT_SYMBOL_FOR_SMB_TEST(sym) \ - EXPORT_SYMBOL_FOR_MODULES(sym, "smb2maperror_test") - const struct status_to_posix_error *smb2_get_err_map_test(__u32 smb2_status) { return smb2_get_err_map(smb2_status); } -EXPORT_SYMBOL_FOR_SMB_TEST(smb2_get_err_map_test); +EXPORT_SYMBOL_IF_KUNIT(smb2_get_err_map_test); const struct status_to_posix_error *smb2_error_map_table_test = smb2_error_map_table; -EXPORT_SYMBOL_FOR_SMB_TEST(smb2_error_map_table_test); +EXPORT_SYMBOL_IF_KUNIT(smb2_error_map_table_test); unsigned int smb2_error_map_num = ARRAY_SIZE(smb2_error_map_table); -EXPORT_SYMBOL_FOR_SMB_TEST(smb2_error_map_num); +EXPORT_SYMBOL_IF_KUNIT(smb2_error_map_num); #endif diff --git a/fs/smb/client/smb2maperror_test.c b/fs/smb/client/smb2maperror_test.c index 0f8a44a5ed3c..44dc5e899cad 100644 --- a/fs/smb/client/smb2maperror_test.c +++ b/fs/smb/client/smb2maperror_test.c @@ -47,3 +47,4 @@ kunit_test_suite(maperror_suite); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("KUnit tests of SMB2 maperror"); +MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); From d7d2adcd022baade5cab65ca492ce63421ce3a6e Mon Sep 17 00:00:00 2001 From: Huiwen He Date: Tue, 14 Jul 2026 17:38:34 -0500 Subject: [PATCH 1009/1054] smb/client: flush dirty data before punching a hole Punching a hole after a large buffered write may leave the range reported as data. Reproduce it with: xfs_io -f \ -c "pwrite -b 3m -S 0x61 0 3m" \ -c "fpunch 1m 1m" \ -c "seek -h 0" \ -c "seek -d 1m" \ /mnt/test/repro Punching 1 MiB at offset 1 MiB should produce: 0 1 MiB 2 MiB 3 MiB | DATA | HOLE | DATA | EOF Instead, the entire file is reported as data. SEEK_HOLE(0) returns EOF, and SEEK_DATA(1M) returns 1M. This happens because a dirty folio spanning the punched range can be written back after the punch and refill the hole. Fix this by flushing and waiting for dirty data in the punched range before invalidating the page cache and issuing FSCTL_SET_ZERO_DATA. The xfstests generic/539 pass against Samba/ksmbd with this change. Signed-off-by: Huiwen He Reviewed-by: ChenXiaoSong Signed-off-by: Steve French --- fs/smb/client/smb2ops.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c index 05f2ab6d345a..cbd51a08e97e 100644 --- a/fs/smb/client/smb2ops.c +++ b/fs/smb/client/smb2ops.c @@ -3519,6 +3519,15 @@ static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon, goto out; filemap_invalidate_lock(inode->i_mapping); + /* + * Flush dirty data first, otherwise a dirty folio spanning the punched + * range may be written back after the ioctl and refill the hole. + */ + rc = filemap_write_and_wait_range(inode->i_mapping, offset, + offset + len - 1); + if (rc < 0) + goto unlock; + /* * We implement the punch hole through ioctl, so we need remove the page * caches first, otherwise the data may be inconsistent with the server. From fe3ff273767ef22fe8a7cb3816f264927c190e50 Mon Sep 17 00:00:00 2001 From: Michal Luczaj Date: Tue, 7 Jul 2026 06:23:56 +0200 Subject: [PATCH 1010/1054] selftests/bpf: Ensure UDP sockets are bound Update sockmap_basic tests to bind sockets before they are used. This accommodates the recent change in sockmap that rejects unbound UDP sockets. Signed-off-by: Michal Luczaj Reviewed-by: Kuniyuki Iwashima Reviewed-by: Jakub Sitnicki Link: https://lore.kernel.org/bpf/20260707-sockmap-lookup-udp-leak-v4-1-f878346f27ab@rbox.co Signed-off-by: Kumar Kartikeya Dwivedi --- tools/testing/selftests/bpf/prog_tests/sockmap_basic.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c b/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c index cb3229711f93..2d22a9058a8e 100644 --- a/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c +++ b/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c @@ -853,7 +853,7 @@ static void test_sockmap_many_socket(void) return; } - udp = xsocket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0); + udp = socket_loopback(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK); if (udp < 0) { close(dgram); close(tcp); @@ -922,7 +922,7 @@ static void test_sockmap_many_maps(void) return; } - udp = xsocket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0); + udp = socket_loopback(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK); if (udp < 0) { close(dgram); close(tcp); @@ -993,7 +993,7 @@ static void test_sockmap_same_sock(void) return; } - udp = xsocket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0); + udp = socket_loopback(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK); if (udp < 0) { close(dgram); close(tcp); From 66efd3368ae10d05e08fbe6425b50fdec7186ac7 Mon Sep 17 00:00:00 2001 From: Michal Luczaj Date: Tue, 7 Jul 2026 06:23:57 +0200 Subject: [PATCH 1011/1054] bpf, sockmap: Reject unhashed UDP sockets on sockmap update UDP sockets get SOCK_RCU_FREE set when (auto-)bound. This means sk_is_refcounted(unbound) = true, while sk_is_refcounted(bound) = false. Because sockmap accepts unbound UDP sockets, a BPF program can increment a socket's refcount via lookup. If the socket is subsequently bound, the transition from unbound to bound causes bpf_sk_release() to skip the decrement of the refcount, causing a memory leak. unreferenced object 0xffff88810bc2eb40 (size 1984): comm "test_progs", pid 2451, jiffies 4295320596 hex dump (first 32 bytes): 7f 00 00 01 7f 00 00 01 d2 04 1b b7 04 d2 00 00 ................ 02 00 01 40 00 00 00 00 00 00 00 00 00 00 00 00 ...@............ backtrace (crc bdee079d): kmem_cache_alloc_noprof+0x557/0x660 sk_prot_alloc+0x69/0x240 sk_alloc+0x30/0x460 inet_create+0x2ce/0xf80 __sock_create+0x25b/0x5c0 __sys_socket+0x119/0x1d0 __x64_sys_socket+0x72/0xd0 do_syscall_64+0xa1/0x5f0 entry_SYSCALL_64_after_hwframe+0x76/0x7e Instead of special-casing for refcounted sockets, reject unhashed UDP sockets during sockmap updates, as there is no benefit to supporting those. This effectively reverts the commit under Fixes, with two exceptions: 1. sock_map_sk_state_allowed() maintains a fall-through `return true`. 2. In the spirit of commit b8b8315e39ff ("bpf, sockmap: Remove unhash handler for BPF sockmap usage"), the proto::unhash BPF handler is not reintroduced. Historical note: this issue is related to commit 67312adc96b5 ("bpf: reject unhashed sockets in bpf_sk_assign"). Fixes: 0c48eefae712 ("sock_map: Lift socket state restriction for datagram sockets") Suggested-by: Kuniyuki Iwashima Signed-off-by: Michal Luczaj Reviewed-by: Kuniyuki Iwashima Reviewed-by: Jakub Sitnicki Reviewed-by: John Fastabend Link: https://lore.kernel.org/bpf/20260707-sockmap-lookup-udp-leak-v4-2-f878346f27ab@rbox.co Signed-off-by: Kumar Kartikeya Dwivedi --- net/core/sock_map.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/core/sock_map.c b/net/core/sock_map.c index c60ba6d292f9..9efbd8ca7db8 100644 --- a/net/core/sock_map.c +++ b/net/core/sock_map.c @@ -542,6 +542,8 @@ static bool sock_map_sk_state_allowed(const struct sock *sk) { if (sk_is_tcp(sk)) return (1 << sk->sk_state) & (TCPF_ESTABLISHED | TCPF_LISTEN); + if (sk_is_udp(sk)) + return sk_hashed(sk); if (sk_is_stream_unix(sk)) return (1 << READ_ONCE(sk->sk_state)) & TCPF_ESTABLISHED; if (sk_is_vsock(sk) && From 30581eda4a07ff15db623612cac578e81869e96f Mon Sep 17 00:00:00 2001 From: Michal Luczaj Date: Tue, 7 Jul 2026 06:23:58 +0200 Subject: [PATCH 1012/1054] selftests/bpf: Adapt sockmap update error handling Update sockmap_listen to accommodate the recent change in sockmap that rejects unbound UDP sockets. TCP: Reject unbound and bound (unless established or listening). UDP: Accept only bound sockets. While at it, migrate to ASSERT_* and enforce reverse xmas tree. Signed-off-by: Michal Luczaj Reviewed-by: Kuniyuki Iwashima Reviewed-by: Jakub Sitnicki Link: https://lore.kernel.org/bpf/20260707-sockmap-lookup-udp-leak-v4-3-f878346f27ab@rbox.co Signed-off-by: Kumar Kartikeya Dwivedi --- .../selftests/bpf/prog_tests/sockmap_listen.c | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c b/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c index cc0c68bab907..1c96a3cf4b97 100644 --- a/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c +++ b/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c @@ -53,8 +53,8 @@ static void test_insert_opened(struct test_sockmap_listen *skel __always_unused, int family, int sotype, int mapfd) { u32 key = 0; - u64 value; int err, s; + u64 value; s = xsocket(family, sotype, 0); if (s == -1) @@ -63,11 +63,8 @@ static void test_insert_opened(struct test_sockmap_listen *skel __always_unused, errno = 0; value = s; err = bpf_map_update_elem(mapfd, &key, &value, BPF_NOEXIST); - if (sotype == SOCK_STREAM) { - if (!err || errno != EOPNOTSUPP) - FAIL_ERRNO("map_update: expected EOPNOTSUPP"); - } else if (err) - FAIL_ERRNO("map_update: expected success"); + ASSERT_ERR(err, "map_update"); + ASSERT_EQ(errno, EOPNOTSUPP, "errno"); xclose(s); } @@ -77,8 +74,8 @@ static void test_insert_bound(struct test_sockmap_listen *skel __always_unused, struct sockaddr_storage addr; socklen_t len = 0; u32 key = 0; - u64 value; int err, s; + u64 value; init_addr_loopback(family, &addr, &len); @@ -93,8 +90,12 @@ static void test_insert_bound(struct test_sockmap_listen *skel __always_unused, errno = 0; value = s; err = bpf_map_update_elem(mapfd, &key, &value, BPF_NOEXIST); - if (!err || errno != EOPNOTSUPP) - FAIL_ERRNO("map_update: expected EOPNOTSUPP"); + if (sotype == SOCK_STREAM) { + ASSERT_ERR(err, "map_update"); + ASSERT_EQ(errno, EOPNOTSUPP, "errno"); + } else { + ASSERT_OK(err, "map_update"); + } close: xclose(s); } @@ -1289,7 +1290,7 @@ static void test_ops(struct test_sockmap_listen *skel, struct bpf_map *map, /* insert */ TEST(test_insert_invalid), TEST(test_insert_opened), - TEST(test_insert_bound, SOCK_STREAM), + TEST(test_insert_bound), TEST(test_insert), /* delete */ TEST(test_delete_after_insert), From 203b06932777b9ad5085319389dea566f5c2ca63 Mon Sep 17 00:00:00 2001 From: Michal Luczaj Date: Tue, 7 Jul 2026 06:23:59 +0200 Subject: [PATCH 1013/1054] selftests/bpf: Fail unbound UDP on sockmap update sockmap now rejects unbound UDP sockets. Adjust test_maps. While at it, check socket()'s return value. This effectively reverts commit c39aa2159974 ("bpf, selftests: Fix test_maps now that sockmap supports UDP"). Signed-off-by: Michal Luczaj Reviewed-by: Kuniyuki Iwashima Reviewed-by: Jakub Sitnicki Link: https://lore.kernel.org/bpf/20260707-sockmap-lookup-udp-leak-v4-4-f878346f27ab@rbox.co Signed-off-by: Kumar Kartikeya Dwivedi --- tools/testing/selftests/bpf/test_maps.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/tools/testing/selftests/bpf/test_maps.c b/tools/testing/selftests/bpf/test_maps.c index c32da7bd8be2..6a2641ee7897 100644 --- a/tools/testing/selftests/bpf/test_maps.c +++ b/tools/testing/selftests/bpf/test_maps.c @@ -759,16 +759,15 @@ static void test_sockmap(unsigned int tasks, void *data) goto out_sockmap; } - /* Test update with unsupported UDP socket */ + /* Test update with unsupported unbound UDP socket */ udp = socket(AF_INET, SOCK_DGRAM, 0); - i = 0; - err = bpf_map_update_elem(fd, &i, &udp, BPF_ANY); - if (err) { - printf("Failed socket update SOCK_DGRAM '%i:%i'\n", - i, udp); + CHECK(udp < 0, "socket(AF_INET, SOCK_DGRAM)", "errno:%d\n", errno); + err = bpf_map_update_elem(fd, &(int){0}, &udp, BPF_ANY); + close(udp); + if (!err) { + printf("Unexpectedly succeeded unbound UDP update '0:%i'\n", udp); goto out_sockmap; } - close(udp); /* Test update without programs */ for (i = 0; i < 6; i++) { From 899160e2774d9952e9f2770b38f701ff1906c0b2 Mon Sep 17 00:00:00 2001 From: Luke Wang Date: Wed, 15 Jul 2026 15:18:11 +0800 Subject: [PATCH 1014/1054] mmc: sdhci-esdhc-imx: remove unnecessary mmc_card_wake_sdio_irq check for tuning save/restore The tuning save/restore during system PM is conditioned on mmc_card_wake_sdio_irq(), but this check is unrelated to whether tuning values need to be preserved. The actual requirement is that the card keeps power during suspend and the controller is a uSDHC. SDIO devices using out-of-band GPIO wakeup maintain power during suspend but do not set the SDIO IRQ wake flag. In this case the tuning delay values are not saved/restored. Remove the unnecessary mmc_card_wake_sdio_irq() condition from both the suspend save and resume restore paths. Fixes: c63d25cdc59a ("mmc: sdhci-esdhc-imx: Save tuning value when card stays powered in suspend") Acked-by: Adrian Hunter Reviewed-by: Frank Li Reviewed-by: Haibo Chen Signed-off-by: Luke Wang Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-esdhc-imx.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 18ecddd6df6f..6526d65538de 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -2064,8 +2064,7 @@ static int sdhci_esdhc_suspend(struct device *dev) * to save the tuning delay value just in case the usdhc * lost power during system PM. */ - if (mmc_card_keep_power(host->mmc) && mmc_card_wake_sdio_irq(host->mmc) && - esdhc_is_usdhc(imx_data)) + if (mmc_card_keep_power(host->mmc) && esdhc_is_usdhc(imx_data)) sdhc_esdhc_tuning_save(host); if (device_may_wakeup(dev)) { @@ -2124,8 +2123,7 @@ static int sdhci_esdhc_resume(struct device *dev) * restore the saved tuning delay value for the device which keep * power during system PM. */ - if (mmc_card_keep_power(host->mmc) && mmc_card_wake_sdio_irq(host->mmc) && - esdhc_is_usdhc(imx_data)) + if (mmc_card_keep_power(host->mmc) && esdhc_is_usdhc(imx_data)) sdhc_esdhc_tuning_restore(host); pm_runtime_put_autosuspend(dev); From 2439becd91bad6883b135044f85f83a0538b96a6 Mon Sep 17 00:00:00 2001 From: Luke Wang Date: Wed, 15 Jul 2026 15:18:12 +0800 Subject: [PATCH 1015/1054] mmc: sdhci-esdhc-imx: restore DLL override for DDR modes on resume sdhci_esdhc_imx_hwinit() unconditionally clears ESDHC_DLL_CTRL by writing zero. For SDIO devices that keep power during system suspend and operate in DDR mode, the card remains in DDR timing while the host DLL override configuration is lost. Extract the DLL override setup from esdhc_set_uhs_signaling() into a helper esdhc_set_dll_override(), and call it on the resume path when the card kept power and is using a DDR timing mode. Fixes: 676a83855614 ("mmc: host: sdhci-esdhc-imx: refactor the system PM logic") Acked-by: Adrian Hunter Reviewed-by: Frank Li Reviewed-by: Haibo Chen Signed-off-by: Luke Wang Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-esdhc-imx.c | 39 +++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 6526d65538de..7230d70e02ae 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -1349,6 +1349,23 @@ static int esdhc_change_pinstate(struct sdhci_host *host, return pinctrl_select_state(imx_data->pinctrl, pinctrl); } +static void esdhc_set_dll_override(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host); + struct esdhc_platform_data *boarddata = &imx_data->boarddata; + u32 v; + + if (!boarddata->delay_line) + return; + + v = boarddata->delay_line << ESDHC_DLL_OVERRIDE_VAL_SHIFT | + (1 << ESDHC_DLL_OVERRIDE_EN_SHIFT); + if (is_imx53_esdhc(imx_data)) + v <<= 1; + writel(v, host->ioaddr + ESDHC_DLL_CTRL); +} + /* * For HS400 eMMC, there is a data_strobe line. This signal is generated * by the device and used for data output and CRC status response output @@ -1404,7 +1421,6 @@ static void esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned timing) u32 m; struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host); - struct esdhc_platform_data *boarddata = &imx_data->boarddata; /* disable ddr mode and disable HS400 mode */ m = readl(host->ioaddr + ESDHC_MIX_CTRL); @@ -1425,15 +1441,7 @@ static void esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned timing) m |= ESDHC_MIX_CTRL_DDREN; writel(m, host->ioaddr + ESDHC_MIX_CTRL); imx_data->is_ddr = 1; - if (boarddata->delay_line) { - u32 v; - v = boarddata->delay_line << - ESDHC_DLL_OVERRIDE_VAL_SHIFT | - (1 << ESDHC_DLL_OVERRIDE_EN_SHIFT); - if (is_imx53_esdhc(imx_data)) - v <<= 1; - writel(v, host->ioaddr + ESDHC_DLL_CTRL); - } + esdhc_set_dll_override(host); break; case MMC_TIMING_MMC_HS400: m |= ESDHC_MIX_CTRL_DDREN | ESDHC_MIX_CTRL_HS400_EN; @@ -2123,9 +2131,18 @@ static int sdhci_esdhc_resume(struct device *dev) * restore the saved tuning delay value for the device which keep * power during system PM. */ - if (mmc_card_keep_power(host->mmc) && esdhc_is_usdhc(imx_data)) + if (mmc_card_keep_power(host->mmc) && esdhc_is_usdhc(imx_data)) { sdhc_esdhc_tuning_restore(host); + /* + * Restore DLL override for DDR modes. hwinit unconditionally + * clears ESDHC_DLL_CTRL, but the card is still in DDR mode. + */ + if (host->timing == MMC_TIMING_UHS_DDR50 || + host->timing == MMC_TIMING_MMC_DDR52) + esdhc_set_dll_override(host); + } + pm_runtime_put_autosuspend(dev); return ret; From 5adc14cd4b905629d5b9163b3a416dcab24c7ce2 Mon Sep 17 00:00:00 2001 From: Luke Wang Date: Wed, 15 Jul 2026 15:18:13 +0800 Subject: [PATCH 1016/1054] mmc: sdhci-esdhc-imx: fix esdhc_change_pinstate() to allow default state restore esdhc_change_pinstate() checks for pins_100mhz and pins_200mhz at the top of the function and returns -EINVAL if either is not defined. This prevents the default case from ever being reached, which means devices with a sleep pinctrl state but without high-speed pin states (100mhz/ 200mhz) can never restore their default pin configuration. Move the IS_ERR checks for pins_100mhz and pins_200mhz into their respective switch cases. Fixes: 676a83855614 ("mmc: host: sdhci-esdhc-imx: refactor the system PM logic") Acked-by: Adrian Hunter Reviewed-by: Frank Li Signed-off-by: Luke Wang Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-esdhc-imx.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 7230d70e02ae..ead4685d621a 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -1326,19 +1326,21 @@ static int esdhc_change_pinstate(struct sdhci_host *host, dev_dbg(mmc_dev(host->mmc), "change pinctrl state for uhs %d\n", uhs); - if (IS_ERR(imx_data->pinctrl) || - IS_ERR(imx_data->pins_100mhz) || - IS_ERR(imx_data->pins_200mhz)) + if (IS_ERR(imx_data->pinctrl)) return -EINVAL; switch (uhs) { case MMC_TIMING_UHS_SDR50: case MMC_TIMING_UHS_DDR50: + if (IS_ERR(imx_data->pins_100mhz)) + return -EINVAL; pinctrl = imx_data->pins_100mhz; break; case MMC_TIMING_UHS_SDR104: case MMC_TIMING_MMC_HS200: case MMC_TIMING_MMC_HS400: + if (IS_ERR(imx_data->pins_200mhz)) + return -EINVAL; pinctrl = imx_data->pins_200mhz; break; default: From 1db87818bde3d2295613660879378b43a70d31f8 Mon Sep 17 00:00:00 2001 From: Luke Wang Date: Wed, 15 Jul 2026 15:18:14 +0800 Subject: [PATCH 1017/1054] mmc: sdhci-esdhc-imx: restore pinctrl before restoring ios timing on resume SDIO devices such as WiFi may keep power during suspend, so the MMC core skips full card re-initialization on resume and directly restores the host controller's ios timing to match the card. For DDR mode, pm_runtime_force_resume() sets DDR_EN before the pin configuration is restored from sleep state. This is related to the SoC IP integration: switching pinctrl setting (changing alt from GPIO to USDHC) impacts the internal loopback path. If pinctrl configures the pad to GPIO function, once DDR_EN is set, the DLL delay will be fixed based on the GPIO function loopback path. When the pinctrl is later changed to USDHC function, the internal loopback path changes, making the original fixed sample point no longer suitable for the current loopback path. This causes persistent read CRC errors on subsequent data transfers. SD/eMMC running in DDR mode are unaffected as they are fully re-initialized from legacy timing after resume. Fix this by restoring the pinctrl state based on current timing mode using esdhc_change_pinstate() before pm_runtime_force_resume(). This ensures the correct pin configuration (e.g., 100/200MHz for UHS modes) is applied before DDR_EN is set. Only restore for non-wakeup devices since wakeup devices kept their active pin state during suspend. Fixes: 676a83855614 ("mmc: host: sdhci-esdhc-imx: refactor the system PM logic") Acked-by: Adrian Hunter Reviewed-by: Haibo Chen Signed-off-by: Luke Wang Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-esdhc-imx.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index ead4685d621a..a54da5757acc 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -2115,6 +2115,12 @@ static int sdhci_esdhc_resume(struct device *dev) struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host); int ret; + if (!device_may_wakeup(dev)) { + ret = esdhc_change_pinstate(host, host->timing); + if (ret) + dev_warn(dev, "Failed to restore pinctrl state\n"); + } + pm_runtime_force_resume(dev); ret = mmc_gpio_set_cd_wake(host->mmc, false); From 9d87eaf985cef9581b6ed99b461b38e8cd666480 Mon Sep 17 00:00:00 2001 From: Luke Wang Date: Wed, 15 Jul 2026 15:18:15 +0800 Subject: [PATCH 1018/1054] mmc: sdhci-esdhc-imx: disable irq during suspend to fix unhandled interrupt When using WIFI out-of-band wakeup, an "irq xxx: nobody cared" warning occurs. This happens because the usdhc interrupt is not disabled during system suspend when device_may_wakeup() returns false. The sequence of events leading to this issue: 1. System enters suspend without disabling usdhc interrupt (because device_may_wakeup() returns false for usdhc device) 2. WIFI out-of-band wakeup triggers system resume via GPIO interrupt 3. WIFI sends a Card interrupt before usdhc has fully resumed 4. usdhc is still in runtime suspend state and cannot handle the interrupt properly 5. The unhandled interrupt triggers "nobody cared" warning Fix this by unconditionally disabling the usdhc interrupt during suspend and re-enabling it during resume, regardless of the wakeup capability. This ensures no interrupts are processed during the suspend/resume transition. Fixes: 676a83855614 ("mmc: host: sdhci-esdhc-imx: refactor the system PM logic") Acked-by: Adrian Hunter Reviewed-by: Haibo Chen Signed-off-by: Luke Wang Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-esdhc-imx.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index a54da5757acc..d980dea9cad1 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -2077,9 +2077,10 @@ static int sdhci_esdhc_suspend(struct device *dev) if (mmc_card_keep_power(host->mmc) && esdhc_is_usdhc(imx_data)) sdhc_esdhc_tuning_save(host); + /* The irqs of imx are not shared. It is safe to disable */ + disable_irq(host->irq); + if (device_may_wakeup(dev)) { - /* The irqs of imx are not shared. It is safe to disable */ - disable_irq(host->irq); ret = sdhci_enable_irq_wakeups(host); if (!ret) dev_warn(dev, "Failed to enable irq wakeup\n"); @@ -2130,10 +2131,10 @@ static int sdhci_esdhc_resume(struct device *dev) /* re-initialize hw state in case it's lost in low power mode */ sdhci_esdhc_imx_hwinit(host); - if (host->irq_wake_enabled) { + if (host->irq_wake_enabled) sdhci_disable_irq_wakeups(host); - enable_irq(host->irq); - } + + enable_irq(host->irq); /* * restore the saved tuning delay value for the device which keep From 8da5930144712412d85e7f868693d96ec5c2018c Mon Sep 17 00:00:00 2001 From: Luke Wang Date: Wed, 15 Jul 2026 15:18:16 +0800 Subject: [PATCH 1019/1054] mmc: sdhci-esdhc-imx: use pm_runtime_resume_and_get() in suspend Replace pm_runtime_get_sync() with pm_runtime_resume_and_get() to simplify error handling. pm_runtime_resume_and_get() automatically drops the usage counter on failure, avoiding the need for a separate pm_runtime_put_noidle() call. If it fails, the device is unclocked and accessing hardware registers would cause a kernel panic, so return the error immediately. Fixes: 676a83855614 ("mmc: host: sdhci-esdhc-imx: refactor the system PM logic") Acked-by: Adrian Hunter Signed-off-by: Luke Wang Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-esdhc-imx.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index d980dea9cad1..7818f351a58a 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -2061,7 +2061,9 @@ static int sdhci_esdhc_suspend(struct device *dev) * 2, make sure the pm_runtime_force_resume() in sdhci_esdhc_resume() really * invoke its ->runtime_resume callback (needs_force_resume = 1). */ - pm_runtime_get_sync(dev); + ret = pm_runtime_resume_and_get(dev); + if (ret) + return ret; if ((imx_data->socdata->flags & ESDHC_FLAG_STATE_LOST_IN_LPMODE) && (host->tuning_mode != SDHCI_TUNING_MODE_1)) { From 6aa00a43bbd3d994558a55586351757cebbff236 Mon Sep 17 00:00:00 2001 From: Luke Wang Date: Wed, 15 Jul 2026 15:18:17 +0800 Subject: [PATCH 1020/1054] mmc: sdhci-esdhc-imx: make non-fatal errors non-blocking in suspend Make pinctrl_pm_select_sleep_state() and mmc_gpio_set_cd_wake() failures non-fatal in the suspend path. These failures only mean slightly higher power consumption or missing CD wakeup capability, but should not block system suspend. Also change the function to always return 0 on the success path instead of propagating non-fatal warning return values. Fixes: 676a83855614 ("mmc: host: sdhci-esdhc-imx: refactor the system PM logic") Acked-by: Adrian Hunter Signed-off-by: Luke Wang Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-esdhc-imx.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 7818f351a58a..290a3172931b 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -2083,8 +2083,7 @@ static int sdhci_esdhc_suspend(struct device *dev) disable_irq(host->irq); if (device_may_wakeup(dev)) { - ret = sdhci_enable_irq_wakeups(host); - if (!ret) + if (!sdhci_enable_irq_wakeups(host)) dev_warn(dev, "Failed to enable irq wakeup\n"); } else { /* @@ -2095,12 +2094,12 @@ static int sdhci_esdhc_suspend(struct device *dev) * other function like GPIO function to save power in PM, * which finally block the SDIO wakeup function. */ - ret = pinctrl_pm_select_sleep_state(dev); - if (ret) - return ret; + if (pinctrl_pm_select_sleep_state(dev)) + dev_warn(dev, "Failed to select sleep pinctrl state\n"); } - ret = mmc_gpio_set_cd_wake(host->mmc, true); + if (mmc_gpio_set_cd_wake(host->mmc, true)) + dev_warn(dev, "Failed to enable cd wake\n"); /* * Make sure invoke runtime_suspend to gate off clock. @@ -2108,7 +2107,7 @@ static int sdhci_esdhc_suspend(struct device *dev) */ pm_runtime_force_suspend(dev); - return ret; + return 0; } static int sdhci_esdhc_resume(struct device *dev) From e27c946b589c53520409a0956b33d52ef7a0898f Mon Sep 17 00:00:00 2001 From: Luke Wang Date: Wed, 15 Jul 2026 15:18:18 +0800 Subject: [PATCH 1021/1054] mmc: sdhci-esdhc-imx: fix resume error handling Check pm_runtime_force_resume() return value in resume. If it fails (clock enable failure), return immediately since accessing hardware registers on an unclocked device would cause a kernel panic. The early return intentionally skips enable_irq() and sdhci_disable_irq_wakeups() because the IRQ handler reads SDHCI_INT_STATUS, which would also fault without clocks. The PM runtime usage counter leak only affects this already-broken device instance and is an acceptable tradeoff to preserve system stability. Remove the return value check for mmc_gpio_set_cd_wake(host->mmc, false) since disable_irq_wake() called internally always returns 0. Also return 0 explicitly on the success path instead of propagating stale return values. Fixes: 676a83855614 ("mmc: host: sdhci-esdhc-imx: refactor the system PM logic") Acked-by: Adrian Hunter Reviewed-by: Frank Li Signed-off-by: Luke Wang Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-esdhc-imx.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 290a3172931b..18f4905c15b9 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -2123,12 +2123,12 @@ static int sdhci_esdhc_resume(struct device *dev) dev_warn(dev, "Failed to restore pinctrl state\n"); } - pm_runtime_force_resume(dev); - - ret = mmc_gpio_set_cd_wake(host->mmc, false); + ret = pm_runtime_force_resume(dev); if (ret) return ret; + mmc_gpio_set_cd_wake(host->mmc, false); + /* re-initialize hw state in case it's lost in low power mode */ sdhci_esdhc_imx_hwinit(host); @@ -2155,7 +2155,7 @@ static int sdhci_esdhc_resume(struct device *dev) pm_runtime_put_autosuspend(dev); - return ret; + return 0; } static int sdhci_esdhc_runtime_suspend(struct device *dev) From 04af4efde58a4a4ef4feab7360c46af9ec0b83a0 Mon Sep 17 00:00:00 2001 From: Mattia Meleleo Date: Wed, 8 Jul 2026 18:55:00 +0200 Subject: [PATCH 1022/1054] bpf, sockmap: Account for receive queue in FIONREAD without a verdict program tcp_bpf_ioctl() answers SIOCINQ from psock->msg_tot_len, which only counts bytes in ingress_msg. Without a stream/skb verdict program nothing is diverted there: data stays in sk_receive_queue, so FIONREAD returns 0 even though read() returns data. Add tcp_inq() to the reported value when the psock has no verdict program. The two queues are disjoint, so bytes redirected into ingress_msg from other sockets stay correctly accounted through msg_tot_len. Remove unused sk_psock_msg_inq(). Fixes: 929e30f93125 ("bpf, sockmap: Fix FIONREAD for sockmap") Signed-off-by: Mattia Meleleo Reviewed-by: Jiayuan Chen Reviewed-by: Emil Tsalapatis Reviewed-by: John Fastabend Link: https://lore.kernel.org/bpf/20260708-fionread-no-verdict-v3-1-b4ee31b3af53@coralogix.com Signed-off-by: Kumar Kartikeya Dwivedi --- include/linux/skmsg.h | 14 -------------- net/ipv4/tcp_bpf.c | 17 ++++++++++++++++- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/include/linux/skmsg.h b/include/linux/skmsg.h index a8553401b1c9..d5e35f24738d 100644 --- a/include/linux/skmsg.h +++ b/include/linux/skmsg.h @@ -551,20 +551,6 @@ static inline void psock_progs_drop(struct sk_psock_progs *progs) psock_set_prog(&progs->skb_verdict, NULL); } -/* for tcp only, sk is locked */ -static inline ssize_t sk_psock_msg_inq(struct sock *sk) -{ - struct sk_psock *psock; - ssize_t inq = 0; - - psock = sk_psock_get(sk); - if (likely(psock)) { - inq = sk_psock_get_msg_len_nolock(psock); - sk_psock_put(sk, psock); - } - return inq; -} - /* for udp only, sk is not locked */ static inline ssize_t sk_msg_first_len(struct sock *sk) { diff --git a/net/ipv4/tcp_bpf.c b/net/ipv4/tcp_bpf.c index cc0bd73f36b6..8e905b50dead 100644 --- a/net/ipv4/tcp_bpf.c +++ b/net/ipv4/tcp_bpf.c @@ -334,6 +334,7 @@ static int tcp_bpf_recvmsg_parser(struct sock *sk, static int tcp_bpf_ioctl(struct sock *sk, int cmd, int *karg) { + struct sk_psock *psock; bool slow; if (cmd != SIOCINQ) @@ -344,7 +345,21 @@ static int tcp_bpf_ioctl(struct sock *sk, int cmd, int *karg) return -EINVAL; slow = lock_sock_fast(sk); - *karg = sk_psock_msg_inq(sk); + psock = sk_psock_get(sk); + if (unlikely(!psock)) { + unlock_sock_fast(sk, slow); + return tcp_ioctl(sk, cmd, karg); + } + *karg = sk_psock_get_msg_len_nolock(psock); + /* Without a verdict program, ingress data is never diverted to + * ingress_msg: it stays in sk_receive_queue and is read through + * the fallback to tcp_recvmsg(), so account for it like + * tcp_ioctl() does. + */ + if (!READ_ONCE(psock->progs.stream_verdict) && + !READ_ONCE(psock->progs.skb_verdict)) + *karg += tcp_inq(sk); + sk_psock_put(sk, psock); unlock_sock_fast(sk, slow); return 0; From a42f05cc4c1d49e27bbc6f65415aa76274ef7736 Mon Sep 17 00:00:00 2001 From: Mattia Meleleo Date: Wed, 8 Jul 2026 18:55:01 +0200 Subject: [PATCH 1023/1054] selftests/bpf: Test FIONREAD on a sockmap socket without a verdict program Add a test validating that FIONREAD on a TCP socket in a sockmap without a verdict program reports data pending in sk_receive_queue. Signed-off-by: Mattia Meleleo Reviewed-by: Jiayuan Chen Reviewed-by: Emil Tsalapatis Reviewed-by: John Fastabend Link: https://lore.kernel.org/bpf/20260708-fionread-no-verdict-v3-2-b4ee31b3af53@coralogix.com Signed-off-by: Kumar Kartikeya Dwivedi --- .../selftests/bpf/prog_tests/sockmap_basic.c | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c b/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c index 2d22a9058a8e..e5fc038d747b 100644 --- a/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c +++ b/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c @@ -1373,6 +1373,43 @@ static void test_sockmap_multi_channels(int sotype) test_sockmap_pass_prog__destroy(skel); } +/* A socket in a sockmap without a verdict program keeps its ingress data + * in sk_receive_queue: FIONREAD must account for it. + */ +static void test_sockmap_no_verdict_fionread(void) +{ + int err, map, zero = 0, sent, avail; + int c0 = -1, c1 = -1, p0 = -1, p1 = -1; + struct test_sockmap_pass_prog *skel; + char buf[256] = "0123456789"; + + skel = test_sockmap_pass_prog__open_and_load(); + if (!ASSERT_OK_PTR(skel, "open_and_load")) + return; + map = bpf_map__fd(skel->maps.sock_map_rx); + + err = create_socket_pairs(AF_INET, SOCK_STREAM, &c0, &c1, &p0, &p1); + if (!ASSERT_OK(err, "create_socket_pairs()")) + goto out; + + err = bpf_map_update_elem(map, &zero, &c1, BPF_NOEXIST); + if (!ASSERT_OK(err, "bpf_map_update_elem(c1)")) + goto out_close; + + sent = xsend(p1, &buf, sizeof(buf), 0); + ASSERT_EQ(sent, sizeof(buf), "xsend(p1)"); + avail = wait_for_fionread(c1, sizeof(buf), IO_TIMEOUT_SEC); + ASSERT_EQ(avail, sizeof(buf), "ioctl(FIONREAD)"); + +out_close: + close(c0); + close(p0); + close(c1); + close(p1); +out: + test_sockmap_pass_prog__destroy(skel); +} + void test_sockmap_basic(void) { if (test__start_subtest("sockmap create_update_free")) @@ -1415,6 +1452,8 @@ void test_sockmap_basic(void) test_sockmap_skb_verdict_shutdown(); if (test__start_subtest("sockmap skb_verdict fionread")) test_sockmap_skb_verdict_fionread(true); + if (test__start_subtest("sockmap no_verdict fionread")) + test_sockmap_no_verdict_fionread(); if (test__start_subtest("sockmap skb_verdict fionread on drop")) test_sockmap_skb_verdict_fionread(false); if (test__start_subtest("sockmap skb_verdict change tail")) From fd4cfa8c8f9a17cdec0539334d28754bc1d8a5d9 Mon Sep 17 00:00:00 2001 From: Sun Jian Date: Tue, 14 Jul 2026 02:38:45 -0700 Subject: [PATCH 1024/1054] bpf: Reject negative const offsets for buffer pointers The verifier rejects variable offsets for PTR_TO_TP_BUFFER and PTR_TO_BUF accesses, but it currently accepts a constant negative offset produced by pointer arithmetic. Commit 022ac0750883 ("bpf: use reg->var_off instead of reg->off for pointers") moved constant pointer offsets from reg->off to reg->var_off. However, __check_buffer_access() continued to check only the instruction offset. An access with reg->var_off equal to -8 and an instruction offset of zero therefore passes verification. For writable raw tracepoints, the access end is also calculated from the unsigned reg->var_off.value. An eight-byte access starting at -8 wraps the calculated end to zero, allowing the program to load and attach without increasing max_tp_access. After ensuring that reg->var_off is constant, calculate the effective access start using signed arithmetic and reject it when it is negative. Use the validated start to calculate the access end for both PTR_TO_TP_BUFFER and PTR_TO_BUF. Fixes: 022ac0750883 ("bpf: use reg->var_off instead of reg->off for pointers") Signed-off-by: Sun Jian Acked-by: Shung-Hsi Yu Cc: stable@vger.kernel.org # 5.2.0 Link: https://patch.msgid.link/20260714093846.18159-2-sun.jian.kdev@gmail.com Signed-off-by: Eduard Zingerman --- kernel/bpf/verifier.c | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 6515d4d3c003..9f1333676365 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -5326,14 +5326,11 @@ static int check_max_stack_depth(struct bpf_verifier_env *env) static int __check_buffer_access(struct bpf_verifier_env *env, const char *buf_info, const struct bpf_reg_state *reg, - argno_t argno, int off, int size) + argno_t argno, int off, int size, + u32 *access_end) { - if (off < 0) { - verbose(env, - "%s invalid %s buffer access: off=%d, size=%d\n", - reg_arg_name(env, argno), buf_info, off, size); - return -EACCES; - } + s64 start; + if (!tnum_is_const(reg->var_off)) { char tn_buf[48]; @@ -5344,6 +5341,15 @@ static int __check_buffer_access(struct bpf_verifier_env *env, return -EACCES; } + start = (s64)reg->var_off.value + off; + if (start < 0) { + verbose(env, + "%s invalid negative %s buffer offset: off=%d, var_off=%lld\n", + reg_arg_name(env, argno), buf_info, off, (s64)reg->var_off.value); + return -EACCES; + } + + *access_end = start + size; return 0; } @@ -5351,14 +5357,14 @@ static int check_tp_buffer_access(struct bpf_verifier_env *env, const struct bpf_reg_state *reg, argno_t argno, int off, int size) { + u32 access_end; int err; - err = __check_buffer_access(env, "tracepoint", reg, argno, off, size); + err = __check_buffer_access(env, "tracepoint", reg, argno, off, size, &access_end); if (err) return err; - env->prog->aux->max_tp_access = max(reg->var_off.value + off + size, - env->prog->aux->max_tp_access); + env->prog->aux->max_tp_access = max(access_end, env->prog->aux->max_tp_access); return 0; } @@ -5370,13 +5376,14 @@ static int check_buffer_access(struct bpf_verifier_env *env, u32 *max_access) { const char *buf_info = type_is_rdonly_mem(reg->type) ? "rdonly" : "rdwr"; + u32 access_end; int err; - err = __check_buffer_access(env, buf_info, reg, argno, off, size); + err = __check_buffer_access(env, buf_info, reg, argno, off, size, &access_end); if (err) return err; - *max_access = max(reg->var_off.value + off + size, *max_access); + *max_access = max(access_end, *max_access); return 0; } From 6f59deb32efa4673fc3b1fef9f3a0da48e9d8494 Mon Sep 17 00:00:00 2001 From: Sun Jian Date: Tue, 14 Jul 2026 02:38:46 -0700 Subject: [PATCH 1025/1054] selftests/bpf: Cover negative buffer pointer offsets Add verifier coverage for constant negative offsets on PTR_TO_TP_BUFFER and PTR_TO_BUF pointers. Both programs adjust the buffer pointer by -8 and access it at offset zero, so the negative effective start must be rejected at load time. Switch the raw tracepoint writable attach checks from nbd_send_request to bpf_testmod_test_writable_bare_tp, avoiding a dependency on the NBD tracepoint. Keep the existing past-end case and add a case with a negative var_off compensated by a positive instruction offset. The effective start remains non-negative, so the program loads, but its access end exceeds the writable context size and bpf_raw_tracepoint_open() must return -EINVAL. Cc: stable@vger.kernel.org # 5.2.0 Signed-off-by: Sun Jian Acked-by: Shung-Hsi Yu Link: https://patch.msgid.link/20260714093846.18159-3-sun.jian.kdev@gmail.com Signed-off-by: Eduard Zingerman --- .../raw_tp_writable_reject_bad_access.c | 57 +++++++++++++++++++ .../raw_tp_writable_reject_nbd_invalid.c | 43 -------------- .../selftests/bpf/prog_tests/verifier.c | 2 + .../selftests/bpf/progs/verifier_ptr_to_buf.c | 27 +++++++++ .../bpf/progs/verifier_raw_tp_writable.c | 16 ++++++ 5 files changed, 102 insertions(+), 43 deletions(-) create mode 100644 tools/testing/selftests/bpf/prog_tests/raw_tp_writable_reject_bad_access.c delete mode 100644 tools/testing/selftests/bpf/prog_tests/raw_tp_writable_reject_nbd_invalid.c create mode 100644 tools/testing/selftests/bpf/progs/verifier_ptr_to_buf.c diff --git a/tools/testing/selftests/bpf/prog_tests/raw_tp_writable_reject_bad_access.c b/tools/testing/selftests/bpf/prog_tests/raw_tp_writable_reject_bad_access.c new file mode 100644 index 000000000000..b8538fc4fc3f --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/raw_tp_writable_reject_bad_access.c @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include "test_kmods/bpf_testmod.h" +#include "bpf_util.h" + +static void check_attach_reject(const struct bpf_insn *program, size_t prog_len) +{ + LIBBPF_OPTS(bpf_prog_load_opts, opts); + char error[4096]; + int bpf_fd, tp_fd; + + opts.log_level = 2; + opts.log_buf = error; + opts.log_size = sizeof(error); + + bpf_fd = bpf_prog_load(BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE, NULL, "GPL v2", + program, prog_len, &opts); + if (!ASSERT_GE(bpf_fd, 0, "prog_load")) + return; + + tp_fd = bpf_raw_tracepoint_open("bpf_testmod_test_writable_bare_tp", bpf_fd); + ASSERT_EQ(tp_fd, -EINVAL, "bpf_raw_tracepoint_open"); + if (tp_fd >= 0) + close(tp_fd); + + close(bpf_fd); +} + +void test_raw_tp_writable_reject_bad_access(void) +{ + const struct bpf_insn program[] = { + /* r6 is our tp buffer */ + BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 0), + /* one byte beyond the end of the writable context */ + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_6, + sizeof(struct bpf_testmod_test_writable_ctx)), + BPF_EXIT_INSN(), + }; + + const struct bpf_insn negative_var_off_program[] = { + BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 0), + /* make var_off negative, but keep the effective access offset non-negative */ + BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -8), + /* one byte beyond the end of the writable context */ + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_6, + sizeof(struct bpf_testmod_test_writable_ctx) + 8), + BPF_EXIT_INSN(), + }; + + if (test__start_subtest("past_end")) + check_attach_reject(program, ARRAY_SIZE(program)); + + if (test__start_subtest("negative_var_off_past_end")) + check_attach_reject(negative_var_off_program, + ARRAY_SIZE(negative_var_off_program)); +} diff --git a/tools/testing/selftests/bpf/prog_tests/raw_tp_writable_reject_nbd_invalid.c b/tools/testing/selftests/bpf/prog_tests/raw_tp_writable_reject_nbd_invalid.c deleted file mode 100644 index 216b0dfac0fe..000000000000 --- a/tools/testing/selftests/bpf/prog_tests/raw_tp_writable_reject_nbd_invalid.c +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -#include -#include -#include "bpf_util.h" - -void test_raw_tp_writable_reject_nbd_invalid(void) -{ - __u32 duration = 0; - char error[4096]; - int bpf_fd = -1, tp_fd = -1; - - const struct bpf_insn program[] = { - /* r6 is our tp buffer */ - BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 0), - /* one byte beyond the end of the nbd_request struct */ - BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_6, - sizeof(struct nbd_request)), - BPF_EXIT_INSN(), - }; - - LIBBPF_OPTS(bpf_prog_load_opts, opts, - .log_level = 2, - .log_buf = error, - .log_size = sizeof(error), - ); - - bpf_fd = bpf_prog_load(BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE, NULL, "GPL v2", - program, ARRAY_SIZE(program), - &opts); - if (CHECK(bpf_fd < 0, "bpf_raw_tracepoint_writable load", - "failed: %d errno %d\n", bpf_fd, errno)) - return; - - tp_fd = bpf_raw_tracepoint_open("nbd_send_request", bpf_fd); - if (CHECK(tp_fd >= 0, "bpf_raw_tracepoint_writable open", - "erroneously succeeded\n")) - goto out_bpffd; - - close(tp_fd); -out_bpffd: - close(bpf_fd); -} diff --git a/tools/testing/selftests/bpf/prog_tests/verifier.c b/tools/testing/selftests/bpf/prog_tests/verifier.c index 8a3d69e2453c..be97f6887f0e 100644 --- a/tools/testing/selftests/bpf/prog_tests/verifier.c +++ b/tools/testing/selftests/bpf/prog_tests/verifier.c @@ -78,6 +78,7 @@ #include "verifier_precision.skel.h" #include "verifier_prevent_map_lookup.skel.h" #include "verifier_private_stack.skel.h" +#include "verifier_ptr_to_buf.skel.h" #include "verifier_raw_stack.skel.h" #include "verifier_raw_tp_writable.skel.h" #include "verifier_reg_equal.skel.h" @@ -230,6 +231,7 @@ void test_verifier_or_jmp32_k(void) { RUN(verifier_or_jmp32_k); } void test_verifier_precision(void) { RUN(verifier_precision); } void test_verifier_prevent_map_lookup(void) { RUN(verifier_prevent_map_lookup); } void test_verifier_private_stack(void) { RUN(verifier_private_stack); } +void test_verifier_ptr_to_buf(void) { RUN(verifier_ptr_to_buf); } void test_verifier_raw_stack(void) { RUN(verifier_raw_stack); } void test_verifier_raw_tp_writable(void) { RUN(verifier_raw_tp_writable); } void test_verifier_reg_equal(void) { RUN(verifier_reg_equal); } diff --git a/tools/testing/selftests/bpf/progs/verifier_ptr_to_buf.c b/tools/testing/selftests/bpf/progs/verifier_ptr_to_buf.c new file mode 100644 index 000000000000..12cf24db46a0 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/verifier_ptr_to_buf.c @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include "bpf_misc.h" + +SEC("iter/bpf_map_elem") +__description("PTR_TO_BUF: reject negative const offset") +__failure +__msg("invalid negative rdwr buffer offset") +__naked void ptr_to_buf_reject_negative_const_offset(void) +{ + asm volatile ("r0 = 0; \ + r2 = *(u64 *)(r1 + %[value_off]); \ + if r2 == 0 goto l0_%=; \ + r2 += -8; \ + r0 = *(u64 *)(r2 + 0); \ +l0_%=: \ + exit; \ + " + : + : __imm_const(value_off, + offsetof(struct bpf_iter__bpf_map_elem, value)) + : __clobber_all); +} + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/verifier_raw_tp_writable.c b/tools/testing/selftests/bpf/progs/verifier_raw_tp_writable.c index 14a0172e2141..4055a6443bc2 100644 --- a/tools/testing/selftests/bpf/progs/verifier_raw_tp_writable.c +++ b/tools/testing/selftests/bpf/progs/verifier_raw_tp_writable.c @@ -47,4 +47,20 @@ l0_%=: /* shift the buffer pointer to a variable location */\ : __clobber_all); } +SEC("raw_tracepoint.w") +__description("raw_tracepoint_writable: reject negative const offset") +__failure +__msg("invalid negative tracepoint buffer offset") +__naked void tracepoint_writable_reject_negative_const_offset(void) +{ + asm volatile (" \ + r6 = *(u64 *)(r1 + 0); \ + r6 += -8; \ + r0 = *(u64 *)(r6 + 0); \ + exit; \ +" : + : + : __clobber_all); +} + char _license[] SEC("license") = "GPL"; From 2f2312c422fd2695da772cecb30c69994b795964 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Tue, 14 Jul 2026 09:03:06 -0700 Subject: [PATCH 1026/1054] KVM: nVMX: Put vmcs12 pages if nested VM-Enter fails due to invalid guest state Put all vmcs12 pages if KVM synthesizes a nested VM-Exit due to invalid guest while emulating VMLAUNCH or VMRESUME. The invalid guest state path doesn't use nested_vmx_vmexit() as that API is intended to be used if and only if L2 is active, and the open coded equivalent neglects to put the vmcs12 pages. Failure to put the vmcs12 pages leaks any pinned pages (and/or mappings) if L1 retries VMLAUNCH/VMRESUME. Note, the !from_vmenter scenario doesn't suffer the same problem, as vmx_get_nested_state_pages() only gets/pins/maps the vmcs12 pages if L2 is active, i.e. if a "full" VM-Exit is guaranteed before KVM will retry getting vmcs12 pages. Fixes: 96c66e87deee ("KVM/nVMX: Use kvm_vcpu_map when mapping the virtual APIC page") Fixes: 3278e0492554 ("KVM/nVMX: Use kvm_vcpu_map when mapping the posted interrupt descriptor table") Fixes: fe1911aa443e ("KVM: nVMX: Use kvm_vcpu_map() to get/pin vmcs12's APIC-access page") Reported-by: Minh Nguyen Cc: stable@vger.kernel.org Signed-off-by: Sean Christopherson Signed-off-by: Paolo Bonzini --- arch/x86/kvm/vmx/nested.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c index bb0eb40b4448..220d42ebc82e 100644 --- a/arch/x86/kvm/vmx/nested.c +++ b/arch/x86/kvm/vmx/nested.c @@ -3761,6 +3761,8 @@ enum nvmx_vmentry_status nested_vmx_enter_non_root_mode(struct kvm_vcpu *vcpu, if (!from_vmentry) return NVMX_VMENTRY_VMEXIT; + nested_put_vmcs12_pages(vcpu); + load_vmcs12_host_state(vcpu, vmcs12); vmcs12->vm_exit_reason = exit_reason.full; if (enable_shadow_vmcs || nested_vmx_is_evmptr12_valid(vmx)) From 25f744ffa0c8e799e06250ce2e618367b166b0d4 Mon Sep 17 00:00:00 2001 From: Nikunj A Dadhania Date: Wed, 15 Jul 2026 06:35:06 +0000 Subject: [PATCH 1027/1054] KVM: SVM: Bump asid_generation on CPU online to avoid ASID collision after hotplug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If a vCPU stays scheduled out (or blocked) while the last pCPU it ran on goes through a hotplug cycle (online->offline->online), and the vCPU then resumes execution on the same pCPU, then it is possible for it to run with an ASID that has now been assigned to a different vCPU, resulting in stale TLB translations being used. svm_enable_virtualization_cpu() resets asid_generation to 1 and sets next_asid to max_asid + 1 on every CPU online event, including hotplug cycles. Because next_asid starts beyond the pool boundary, the first call to new_asid() after an online event always wraps the pool, incrementing asid_generation to 2 and assigning ASIDs starting from min_asid. Consider two vCPUs from different VMs, vCPU-A pinned to CPU-X holding asid_generation=2 and ASID=N from before the hotplug event: 1. CPU-X goes offline and back online: asid_generation resets to 1, next_asid = max_asid + 1. 2. One or more vCPUs migrate to CPU-X and call new_asid(), wrapping the pool and consuming ASIDs starting from min_asid. Eventually vCPU-B from a different VM is assigned asid_generation=2, ASID=N — the same ASID that vCPU-A held before the hotplug. 3. vCPU-A enters pre_svm_run() on CPU-X: current_vmcb->cpu is unchanged so the migration branch is skipped. Its saved asid_generation=2 matches sd->asid_generation=2, so the generation check silently passes and vCPU-A continues running with ASID=N — the same ASID just freshly assigned to vCPU-B. Both vCPUs from different VMs now run on CPU-X with the same ASID, causing them to share NPT TLB entries and producing stale translations. The collision manifests as a KVM internal error (Suberror: 1, emulation failure). The NPT page fault reports a faulting GPA far outside the VM's physical memory range — a sign of stale TLB translations being used. KVM falls back to instruction emulation, which fails on FPU/XSave instructions (XRSTOR, STMXCSR) that the emulator does not implement. Fix this by incrementing asid_generation instead of resetting it to 1 in svm_enable_virtualization_cpu(). On module load, asid_generation starts at 0 (memset) and the increment produces 1, identical to the old behaviour. On subsequent hotplug cycles the generation advances beyond any value a vCPU previously observed on this CPU, so the generation check in pre_svm_run() reliably forces new_asid() on every vCPU after every hotplug cycle. Fixes: 774c47f1d78e ("[PATCH] KVM: cpu hotplug support") Reported-by: Chandrakanth Silveru Tested-by: Srikanth Aithal Reviewed-by: K Prateek Nayak Reviewed-by: Tom Lendacky Signed-off-by: Nikunj A Dadhania Message-ID: <20260715063506.672432-1-nikunj@amd.com> Signed-off-by: Paolo Bonzini --- arch/x86/kvm/svm/svm.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index 4d2bacd00ec4..d0971685034b 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -571,7 +571,12 @@ static int svm_enable_virtualization_cpu(void) return r; sd = per_cpu_ptr(&svm_data, me); - sd->asid_generation = 1; + /* + * Bump the current asid_generation value to ensure any vCPU that + * previously ran on this CPU sees a stale generation and is forced + * to acquire a new ASID, preventing a latent ASID collision. + */ + sd->asid_generation++; sd->max_asid = cpuid_ebx(SVM_CPUID_FUNC) - 1; sd->next_asid = sd->max_asid + 1; sd->min_asid = max_sev_asid + 1; From e4e8af62adab2fdcca230006f829407a953070cd Mon Sep 17 00:00:00 2001 From: Shuhao Fu Date: Thu, 7 May 2026 10:22:26 +0200 Subject: [PATCH 1028/1054] can: j1939: fix lockless local-destination check j1939_priv.ents[].nusers is documented as protected by priv->lock, and its updates already happen under that lock. j1939_can_recv() also reads it under read_lock_bh(). However, j1939_session_skb_queue() and j1939_tp_send() still read priv->ents[da].nusers without taking the lock. Those transport-side checks decide whether to set J1939_ECU_LOCAL_DST, so they can race with j1939_local_ecu_get() and j1939_local_ecu_put() while userspace is binding or releasing sockets concurrently with TP traffic. This can misclassify TP/ETP sessions as local or remote and take the wrong transport path. Fix both transport paths by routing the destination-locality check through a helper that reads ents[].nusers under read_lock_bh(&priv->lock). Fixes: 9d71dd0c7009 ("can: add support of SAE J1939 protocol") Signed-off-by: Shuhao Fu Tested-by: Oleksij Rempel Acked-by: Oleksij Rempel Link: https://patch.msgid.link/20260419140614.GA4041240@chcpu16 Signed-off-by: Marc Kleine-Budde --- net/can/j1939/transport.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/net/can/j1939/transport.c b/net/can/j1939/transport.c index df93d57907da..8a31cb23bc76 100644 --- a/net/can/j1939/transport.c +++ b/net/can/j1939/transport.c @@ -351,6 +351,18 @@ static void j1939_session_skb_drop_old(struct j1939_session *session) } } +static bool j1939_address_is_local(struct j1939_priv *priv, u8 addr) +{ + bool local = false; + + read_lock_bh(&priv->lock); + if (j1939_address_is_unicast(addr) && priv->ents[addr].nusers) + local = true; + read_unlock_bh(&priv->lock); + + return local; +} + void j1939_session_skb_queue(struct j1939_session *session, struct sk_buff *skb) { @@ -359,8 +371,7 @@ void j1939_session_skb_queue(struct j1939_session *session, j1939_ac_fixup(priv, skb); - if (j1939_address_is_unicast(skcb->addr.da) && - priv->ents[skcb->addr.da].nusers) + if (j1939_address_is_local(priv, skcb->addr.da)) skcb->flags |= J1939_ECU_LOCAL_DST; skcb->flags |= J1939_ECU_LOCAL_SRC; @@ -2038,8 +2049,7 @@ struct j1939_session *j1939_tp_send(struct j1939_priv *priv, return ERR_PTR(ret); /* fix DST flags, it may be used there soon */ - if (j1939_address_is_unicast(skcb->addr.da) && - priv->ents[skcb->addr.da].nusers) + if (j1939_address_is_local(priv, skcb->addr.da)) skcb->flags |= J1939_ECU_LOCAL_DST; /* src is always local, I'm sending ... */ From d83762005c13269fffa51620399fc8d09095c4f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Grosjean?= Date: Thu, 7 May 2026 10:22:27 +0200 Subject: [PATCH 1029/1054] can: peak: Modification of references to email accounts being deleted MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Following the sale of PEAK-System France by HMS-Networks, this update is intended to change all my @hms-networks.com email addresses to my new @peak-system.fr address. Signed-off-by: Stéphane Grosjean Link: https://patch.msgid.link/20260410124251.40506-1-stephane.grosjean@free.fr Signed-off-by: Marc Kleine-Budde --- .mailmap | 4 ++-- drivers/net/can/peak_canfd/peak_canfd.c | 2 +- drivers/net/can/peak_canfd/peak_canfd_user.h | 2 +- drivers/net/can/peak_canfd/peak_pciefd_main.c | 4 ++-- drivers/net/can/sja1000/peak_pci.c | 4 ++-- drivers/net/can/sja1000/peak_pcmcia.c | 4 ++-- drivers/net/can/usb/peak_usb/pcan_usb.c | 2 +- drivers/net/can/usb/peak_usb/pcan_usb_core.c | 4 ++-- drivers/net/can/usb/peak_usb/pcan_usb_core.h | 2 +- drivers/net/can/usb/peak_usb/pcan_usb_fd.c | 2 +- drivers/net/can/usb/peak_usb/pcan_usb_pro.c | 2 +- drivers/net/can/usb/peak_usb/pcan_usb_pro.h | 2 +- include/linux/can/dev/peak_canfd.h | 2 +- 13 files changed, 18 insertions(+), 18 deletions(-) diff --git a/.mailmap b/.mailmap index 12f3acdebd72..918fcd6575b1 100644 --- a/.mailmap +++ b/.mailmap @@ -825,8 +825,8 @@ Sriram Yagnaraman Stanislav Fomichev Stanislav Fomichev Stefan Wahren -Stéphane Grosjean -Stéphane Grosjean +Stéphane Grosjean +Stéphane Grosjean Stéphane Witzmann Stephen Hemminger Stephen Hemminger diff --git a/drivers/net/can/peak_canfd/peak_canfd.c b/drivers/net/can/peak_canfd/peak_canfd.c index 06cb2629f66a..4fd1aefb780f 100644 --- a/drivers/net/can/peak_canfd/peak_canfd.c +++ b/drivers/net/can/peak_canfd/peak_canfd.c @@ -2,7 +2,7 @@ /* Copyright (C) 2007, 2011 Wolfgang Grandegger * * Copyright (C) 2016-2025 PEAK System-Technik GmbH - * Author: Stéphane Grosjean + * Author: Stéphane Grosjean */ #include diff --git a/drivers/net/can/peak_canfd/peak_canfd_user.h b/drivers/net/can/peak_canfd/peak_canfd_user.h index 60c6542028cf..dc0ecb566a85 100644 --- a/drivers/net/can/peak_canfd/peak_canfd_user.h +++ b/drivers/net/can/peak_canfd/peak_canfd_user.h @@ -2,7 +2,7 @@ /* CAN driver for PEAK System micro-CAN based adapters * * Copyright (C) 2003-2025 PEAK System-Technik GmbH - * Author: Stéphane Grosjean + * Author: Stéphane Grosjean */ #ifndef PEAK_CANFD_USER_H #define PEAK_CANFD_USER_H diff --git a/drivers/net/can/peak_canfd/peak_pciefd_main.c b/drivers/net/can/peak_canfd/peak_pciefd_main.c index 93558e33bc02..7c749301ea84 100644 --- a/drivers/net/can/peak_canfd/peak_pciefd_main.c +++ b/drivers/net/can/peak_canfd/peak_pciefd_main.c @@ -4,7 +4,7 @@ * Derived from the PCAN project file driver/src/pcan_pci.c: * * Copyright (C) 2001-2025 PEAK System-Technik GmbH - * Author: Stéphane Grosjean + * Author: Stéphane Grosjean */ #include @@ -19,7 +19,7 @@ #include "peak_canfd_user.h" -MODULE_AUTHOR("Stéphane Grosjean "); +MODULE_AUTHOR("Stéphane Grosjean "); MODULE_DESCRIPTION("Socket-CAN driver for PEAK PCAN PCIe/M.2 FD family cards"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/can/sja1000/peak_pci.c b/drivers/net/can/sja1000/peak_pci.c index 4cc4a1581dd1..69c61ccf621d 100644 --- a/drivers/net/can/sja1000/peak_pci.c +++ b/drivers/net/can/sja1000/peak_pci.c @@ -5,7 +5,7 @@ * Derived from the PCAN project file driver/src/pcan_pci.c: * * Copyright (C) 2001-2025 PEAK System-Technik GmbH - * Author: Stéphane Grosjean + * Author: Stéphane Grosjean */ #include @@ -22,7 +22,7 @@ #include "sja1000.h" -MODULE_AUTHOR("Stéphane Grosjean "); +MODULE_AUTHOR("Stéphane Grosjean "); MODULE_DESCRIPTION("Socket-CAN driver for PEAK PCAN PCI family cards"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/can/sja1000/peak_pcmcia.c b/drivers/net/can/sja1000/peak_pcmcia.c index 42a77d435b39..c3c2aa21da47 100644 --- a/drivers/net/can/sja1000/peak_pcmcia.c +++ b/drivers/net/can/sja1000/peak_pcmcia.c @@ -4,7 +4,7 @@ * Derived from the PCAN project file driver/src/pcan_pccard.c * * Copyright (C) 2006-2025 PEAK System-Technik GmbH - * Author: Stéphane Grosjean + * Author: Stéphane Grosjean */ #include #include @@ -19,7 +19,7 @@ #include #include "sja1000.h" -MODULE_AUTHOR("Stéphane Grosjean "); +MODULE_AUTHOR("Stéphane Grosjean "); MODULE_DESCRIPTION("CAN driver for PEAK-System PCAN-PC Cards"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/can/usb/peak_usb/pcan_usb.c b/drivers/net/can/usb/peak_usb/pcan_usb.c index 9278a1522aae..8fd058c32856 100644 --- a/drivers/net/can/usb/peak_usb/pcan_usb.c +++ b/drivers/net/can/usb/peak_usb/pcan_usb.c @@ -4,7 +4,7 @@ * Derived from the PCAN project file driver/src/pcan_usb.c * * Copyright (C) 2003-2025 PEAK System-Technik GmbH - * Author: Stéphane Grosjean + * Author: Stéphane Grosjean * * Many thanks to Klaus Hitschler */ diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_core.c b/drivers/net/can/usb/peak_usb/pcan_usb_core.c index cf48bb26d46d..c7933d1acc99 100644 --- a/drivers/net/can/usb/peak_usb/pcan_usb_core.c +++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.c @@ -4,7 +4,7 @@ * Derived from the PCAN project file driver/src/pcan_usb_core.c * * Copyright (C) 2003-2025 PEAK System-Technik GmbH - * Author: Stéphane Grosjean + * Author: Stéphane Grosjean * * Many thanks to Klaus Hitschler */ @@ -24,7 +24,7 @@ #include "pcan_usb_core.h" -MODULE_AUTHOR("Stéphane Grosjean "); +MODULE_AUTHOR("Stéphane Grosjean "); MODULE_DESCRIPTION("CAN driver for PEAK-System USB adapters"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_core.h b/drivers/net/can/usb/peak_usb/pcan_usb_core.h index d1c1897d47b9..65999f04f4b7 100644 --- a/drivers/net/can/usb/peak_usb/pcan_usb_core.h +++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.h @@ -4,7 +4,7 @@ * Derived from the PCAN project file driver/src/pcan_usb_core.c * * Copyright (C) 2003-2025 PEAK System-Technik GmbH - * Author: Stéphane Grosjean + * Author: Stéphane Grosjean * * Many thanks to Klaus Hitschler */ diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_fd.c b/drivers/net/can/usb/peak_usb/pcan_usb_fd.c index eb4f5884ad73..ef9fd693e9bd 100644 --- a/drivers/net/can/usb/peak_usb/pcan_usb_fd.c +++ b/drivers/net/can/usb/peak_usb/pcan_usb_fd.c @@ -3,7 +3,7 @@ * CAN driver for PEAK System PCAN-USB FD / PCAN-USB Pro FD adapter * * Copyright (C) 2013-2025 PEAK System-Technik GmbH - * Author: Stéphane Grosjean + * Author: Stéphane Grosjean */ #include #include diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_pro.c b/drivers/net/can/usb/peak_usb/pcan_usb_pro.c index 4bfa8d0fbb32..aefcded8e12a 100644 --- a/drivers/net/can/usb/peak_usb/pcan_usb_pro.c +++ b/drivers/net/can/usb/peak_usb/pcan_usb_pro.c @@ -4,7 +4,7 @@ * Derived from the PCAN project file driver/src/pcan_usbpro.c * * Copyright (C) 2003-2025 PEAK System-Technik GmbH - * Author: Stéphane Grosjean + * Author: Stéphane Grosjean */ #include #include diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_pro.h b/drivers/net/can/usb/peak_usb/pcan_usb_pro.h index 162c7546d3a8..d669c9e610c7 100644 --- a/drivers/net/can/usb/peak_usb/pcan_usb_pro.h +++ b/drivers/net/can/usb/peak_usb/pcan_usb_pro.h @@ -4,7 +4,7 @@ * Derived from the PCAN project file driver/src/pcan_usbpro_fw.h * * Copyright (C) 2003-2025 PEAK System-Technik GmbH - * Author: Stéphane Grosjean + * Author: Stéphane Grosjean */ #ifndef PCAN_USB_PRO_H #define PCAN_USB_PRO_H diff --git a/include/linux/can/dev/peak_canfd.h b/include/linux/can/dev/peak_canfd.h index d3788a3d0942..056e0efa649f 100644 --- a/include/linux/can/dev/peak_canfd.h +++ b/include/linux/can/dev/peak_canfd.h @@ -3,7 +3,7 @@ * CAN driver for PEAK System micro-CAN based adapters * * Copyright (C) 2003-2025 PEAK System-Technik GmbH - * Author: Stéphane Grosjean + * Author: Stéphane Grosjean */ #ifndef PUCAN_H #define PUCAN_H From 68973f9db76144825e4f35dfdc80fb8279eb2d57 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 14 Jul 2026 18:55:23 +0200 Subject: [PATCH 1030/1054] can: bcm: defer rx_op deallocation to workqueue to fix thrtimer UAF Commit f1b4e32aca08 ("can: bcm: use call_rcu() instead of costly synchronize_rcu()") replaced synchronize_rcu() in bcm_delete_rx_op() with call_rcu() and introduced the RX_NO_AUTOTIMER flag. However, this flag check was omitted for thrtimer in the packet rx fast-path. During BCM RX operation teardown, a concurrent RCU reader (bcm_rx_handler) can race and re-arm thrtimer via bcm_rx_update_and_send() after call_rcu() has been scheduled. Once the RCU grace period elapses, bcm_op is freed. The subsequently firing thrtimer then dereferences the deallocated op, causing a UAF. Adding flag checks to the rx fast-path (bcm_rx_update_and_send) does not fully close the TOCTOU race and introduces latency for every CAN frame. Conversely, calling hrtimer_cancel() directly inside the RCU callback (softirq context) is fatal as hrtimer_cancel() can sleep, triggering a "scheduling while atomic" panic. Resolve this by deferring the timer cancellation and memory free to a dedicated unbound workqueue (bcm_wq). The RCU callback now queues a work item to bcm_wq, which safely cancels both timers and deallocates memory in sleepable process context. A dedicated workqueue is used to prevent system-wide WQ saturation and is cleanly flushed/destroyed on module unload to avoid rmmod page faults. Since the deferred work can now outlive the calling context by an unbounded amount, also take a reference on op->sk when it is assigned and drop it only once the deferred work has cancelled both timers, so a socket can no longer be freed out from under a still-armed timer whose callback (bcm_send_to_user()) dereferences op->sk. Fixes: f1b4e32aca08 ("can: bcm: use call_rcu() instead of costly synchronize_rcu()") Tested-by: Feng Xue Tested-by: Oliver Hartkopp Signed-off-by: Lee Jones Signed-off-by: Oliver Hartkopp Link: https://patch.msgid.link/20260714-bcm_fixes-v15-1-562f7e3e42da@hartkopp.net Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde --- net/can/bcm.c | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/net/can/bcm.c b/net/can/bcm.c index a4bef2c48a55..bdf53241bd7b 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -58,6 +58,7 @@ #include #include #include +#include #include #include #include @@ -92,6 +93,8 @@ MODULE_ALIAS("can-proto-2"); #define BCM_MIN_NAMELEN CAN_REQUIRED_SIZE(struct sockaddr_can, can_ifindex) +static struct workqueue_struct *bcm_wq; + /* * easy access to the first 64 bit of can(fd)_frame payload. cp->data is * 64 bit aligned so the offset has to be multiples of 8 which is ensured @@ -105,6 +108,7 @@ static inline u64 get_u64(const struct canfd_frame *cp, int offset) struct bcm_op { struct list_head list; struct rcu_head rcu; + struct work_struct work; int ifindex; canid_t can_id; u32 flags; @@ -793,9 +797,12 @@ static struct bcm_op *bcm_find_op(struct list_head *ops, return NULL; } -static void bcm_free_op_rcu(struct rcu_head *rcu_head) +static void bcm_free_op_work(struct work_struct *work) { - struct bcm_op *op = container_of(rcu_head, struct bcm_op, rcu); + struct bcm_op *op = container_of(work, struct bcm_op, work); + + hrtimer_cancel(&op->timer); + hrtimer_cancel(&op->thrtimer); if ((op->frames) && (op->frames != &op->sframe)) kfree(op->frames); @@ -803,9 +810,23 @@ static void bcm_free_op_rcu(struct rcu_head *rcu_head) if ((op->last_frames) && (op->last_frames != &op->last_sframe)) kfree(op->last_frames); + /* the last possible access to op->timer/op->thrtimer has now + * happened above via hrtimer_cancel() - op->sk is no longer + * needed by any pending timer callback, so drop our reference + */ + sock_put(op->sk); + kfree(op); } +static void bcm_free_op_rcu(struct rcu_head *rcu_head) +{ + struct bcm_op *op = container_of(rcu_head, struct bcm_op, rcu); + + INIT_WORK(&op->work, bcm_free_op_work); + queue_work(bcm_wq, &op->work); +} + static void bcm_remove_op(struct bcm_op *op) { hrtimer_cancel(&op->timer); @@ -1060,6 +1081,7 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, /* bcm_can_tx / bcm_tx_timeout_handler needs this */ op->sk = sk; + sock_hold(sk); op->ifindex = ifindex; /* initialize uninitialized (kzalloc) structure */ @@ -1221,6 +1243,7 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, /* bcm_can_tx / bcm_tx_timeout_handler needs this */ op->sk = sk; + sock_hold(sk); op->ifindex = ifindex; /* ifindex for timeout events w/o previous frame reception */ @@ -1839,11 +1862,15 @@ static int __init bcm_module_init(void) { int err; + bcm_wq = alloc_workqueue("can-bcm-wq", WQ_UNBOUND, 0); + if (!bcm_wq) + return -ENOMEM; + pr_info("can: broadcast manager protocol\n"); err = register_pernet_subsys(&canbcm_pernet_ops); if (err) - return err; + goto register_pernet_failed; err = register_netdevice_notifier(&canbcm_notifier); if (err) @@ -1861,6 +1888,8 @@ static int __init bcm_module_init(void) unregister_netdevice_notifier(&canbcm_notifier); register_notifier_failed: unregister_pernet_subsys(&canbcm_pernet_ops); +register_pernet_failed: + destroy_workqueue(bcm_wq); return err; } @@ -1869,6 +1898,8 @@ static void __exit bcm_module_exit(void) can_proto_unregister(&bcm_can_proto); unregister_netdevice_notifier(&canbcm_notifier); unregister_pernet_subsys(&canbcm_pernet_ops); + rcu_barrier(); + destroy_workqueue(bcm_wq); } module_init(bcm_module_init); From d9b091d9d22fee81ec53fb55d2032951993ceadb Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Tue, 14 Jul 2026 18:55:24 +0200 Subject: [PATCH 1031/1054] can: bcm: fix lockless bound/ifindex race and silent RX_SETUP failure bcm_sendmsg() reads bo->ifindex and checks bo->bound before taking lock_sock(), while bcm_notify(), bcm_connect() and bcm_release() all mutate both fields under that same lock. Because the lockless reads and the locked writes are unordered with respect to each other, a racing bcm_notify() (device unregister) or bcm_connect() (concurrent bind on another thread sharing the socket) can make bcm_sendmsg() observe an inconsistent combination, e.g. a stale bound=1 together with the now-cleared ifindex=0, silently turning a socket bound to a specific CAN interface into one that also matches "any" interface. Keep the lockless bo->bound check purely as a fast-path reject, and move the ifindex read (and a bo->bound re-check) into the locked section, where every writer already serializes. This removes the possibility of observing the two fields torn against each other, rather than trying to fix it with more READ_ONCE()/WRITE_ONCE() pairs on two independently updated fields. Annotate the now-purely-lockless bo->bound accesses consistently across all its write sites. Also fix bcm_rx_setup() silently returning success when the target device disappears concurrently instead of reporting -ENODEV, so a broken RX op is no longer left registered as if it had succeeded. Fixes: ffd980f976e7 ("[CAN]: Add broadcast manager (bcm) protocol") Reported-by: Ginger Closes: https://lore.kernel.org/linux-can/CAGp+u1aBK8QVjsvAxM2Ldzep4rEbsP9x_pV3At4g=h1kVEtyhA@mail.gmail.com/ Signed-off-by: Oliver Hartkopp Link: https://patch.msgid.link/20260714-bcm_fixes-v15-2-562f7e3e42da@hartkopp.net Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde --- net/can/bcm.c | 65 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 51 insertions(+), 14 deletions(-) diff --git a/net/can/bcm.c b/net/can/bcm.c index bdf53241bd7b..b612135b017d 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -1323,6 +1323,11 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, op->rx_reg_dev = dev; dev_put(dev); + } else { + /* the requested device is gone - do not + * silently succeed without registering + */ + err = -ENODEV; } } else @@ -1396,12 +1401,13 @@ static int bcm_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) { struct sock *sk = sock->sk; struct bcm_sock *bo = bcm_sk(sk); - int ifindex = bo->ifindex; /* default ifindex for this bcm_op */ + int ifindex; struct bcm_msg_head msg_head; int cfsiz; int ret; /* read bytes or error codes as return value */ - if (!bo->bound) + /* Lockless fast-path check for bound socket */ + if (!READ_ONCE(bo->bound)) return -ENOTCONN; /* check for valid message length from userspace */ @@ -1417,17 +1423,38 @@ static int bcm_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) if ((size - MHSIZ) % cfsiz) return -EINVAL; + lock_sock(sk); + + /* Re-validate under the socket lock: a concurrent bcm_notify() + * may have unbound this socket (device removal) after the + * lockless fast-path check above. bo->ifindex is only ever + * mutated under lock_sock(), so reading it here - instead of + * before taking the lock - guarantees it can't be observed + * torn against bo->bound. + */ + if (!bo->bound) { + ret = -ENOTCONN; + goto out_release; + } + + /* default ifindex for this bcm_op */ + ifindex = bo->ifindex; + /* check for alternative ifindex for this bcm_op */ if (!ifindex && msg->msg_name) { /* no bound device as default => check msg_name */ DECLARE_SOCKADDR(struct sockaddr_can *, addr, msg->msg_name); - if (msg->msg_namelen < BCM_MIN_NAMELEN) - return -EINVAL; + if (msg->msg_namelen < BCM_MIN_NAMELEN) { + ret = -EINVAL; + goto out_release; + } - if (addr->can_family != AF_CAN) - return -EINVAL; + if (addr->can_family != AF_CAN) { + ret = -EINVAL; + goto out_release; + } /* ifindex from sendto() */ ifindex = addr->can_ifindex; @@ -1436,20 +1463,21 @@ static int bcm_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) struct net_device *dev; dev = dev_get_by_index(sock_net(sk), ifindex); - if (!dev) - return -ENODEV; + if (!dev) { + ret = -ENODEV; + goto out_release; + } if (dev->type != ARPHRD_CAN) { dev_put(dev); - return -ENODEV; + ret = -ENODEV; + goto out_release; } dev_put(dev); } } - lock_sock(sk); - switch (msg_head.opcode) { case TX_SETUP: @@ -1499,6 +1527,7 @@ static int bcm_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) break; } +out_release: release_sock(sk); return ret; @@ -1535,7 +1564,12 @@ static void bcm_notify(struct bcm_sock *bo, unsigned long msg, bo->bcm_proc_read = NULL; } #endif - bo->bound = 0; + /* Paired with the lockless fast-path check in + * bcm_sendmsg(); bo->ifindex itself is only ever + * accessed under lock_sock() so it needs no + * annotation. + */ + WRITE_ONCE(bo->bound, 0); bo->ifindex = 0; notify_enodev = 1; } @@ -1676,7 +1710,7 @@ static int bcm_release(struct socket *sock) /* remove device reference */ if (bo->bound) { - bo->bound = 0; + WRITE_ONCE(bo->bound, 0); bo->ifindex = 0; } @@ -1746,7 +1780,10 @@ static int bcm_connect(struct socket *sock, struct sockaddr_unsized *uaddr, int } #endif /* CONFIG_PROC_FS */ - bo->bound = 1; + /* bo->ifindex above is fully assigned before this point; pairs + * with the lockless fast-path check in bcm_sendmsg() + */ + WRITE_ONCE(bo->bound, 1); fail: release_sock(sk); From 749179c2e25b95d22499ed29096b3e02d6dfd2b4 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Tue, 14 Jul 2026 18:55:25 +0200 Subject: [PATCH 1032/1054] can: bcm: add locking when updating filter and timer values KCSAN detected a simultaneous access to timer values that can be overwritten in bcm_rx_setup() when updating timer and filter content while bcm_rx_handler(), bcm_rx_timeout_handler() or bcm_rx_thr_handler() run concurrently on incoming CAN traffic. Protect the timer (ival1/ival2/kt_ival1/kt_ival2/kt_lastmsg) and filter (nframes/flags/frames/last_frames) updates in bcm_rx_setup() with a new per-op bcm_rx_update_lock, taken with the matching scope in the RX handlers. memcpy_from_msg() is staged into a temporary buffer before the lock is taken, since it can sleep and must not run under a spinlock. hrtimer_cancel() is always called without bcm_rx_update_lock held, since bcm_rx_timeout_handler()/bcm_rx_thr_handler() take the same lock and a running callback would otherwise deadlock against the canceller. Also close a related race: bcm_rx_setup() cleared the RTR flag in the stored reply frame's can_id as a separate, unprotected step after the frame content was already installed, so a concurrent bcm_rx_handler() could transmit a stale reply with CAN_RTR_FLAG still set. Fold that normalization into the initial frame preparation instead (on the staged buffer for updates, directly on op->frames pre-registration for new ops), so the installed frame is always atomically self-consistent. bcm_rx_handler()'s RX_RTR_FRAME check now takes a lock-protected snapshot of op->flags before deciding whether to call bcm_can_tx(), but does not hold the lock across that call. Also take a lock-protected snapshot of the currframe in bcm_can_tx() to avoid partly overwrites by content updates in bcm_tx_setup(). Finally check if a TX_RESET_MULTI_IDX/SETTIMER might have reset op->currframe between the two locked sections in bcm_can_tx(). Omit calling hrtimer_forward() with zero interval in bcm_rx_thr_handler(). kt_ival2 may have been concurrently cleared by bcm_rx_setup() before it cancels this timer, so check kt_ival2 inside the bcm_rx_update_lock. Fixes: c2aba69d0c36 ("can: bcm: add locking for bcm_op runtime updates") Reported-by: syzbot+75e5e4ae00c3b4bb544e@syzkaller.appspotmail.com Closes: https://lore.kernel.org/linux-can/6975d5cf.a00a0220.33ccc7.0022.GAE@google.com/ Signed-off-by: Oliver Hartkopp Link: https://patch.msgid.link/20260714-bcm_fixes-v15-3-562f7e3e42da@hartkopp.net Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde --- net/can/bcm.c | 180 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 135 insertions(+), 45 deletions(-) diff --git a/net/can/bcm.c b/net/can/bcm.c index b612135b017d..1e5f8d65d351 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -129,6 +129,7 @@ struct bcm_op { struct sock *sk; struct net_device *rx_reg_dev; spinlock_t bcm_tx_lock; /* protect currframe/count in runtime updates */ + spinlock_t bcm_rx_update_lock; /* protect filter/timer data updates */ }; struct bcm_sock { @@ -293,22 +294,28 @@ static int bcm_proc_show(struct seq_file *m, void *v) * bcm_can_tx - send the (next) CAN frame to the appropriate CAN interface * of the given bcm tx op */ -static void bcm_can_tx(struct bcm_op *op) +static void bcm_can_tx(struct bcm_op *op, struct canfd_frame *cf) { struct sk_buff *skb; struct can_skb_ext *csx; struct net_device *dev; - struct canfd_frame *cf; + struct canfd_frame cframe; + bool cyclic = !cf; + unsigned int idx = 0; int err; /* no target device? => exit */ if (!op->ifindex) return; - /* read currframe under lock protection */ - spin_lock_bh(&op->bcm_tx_lock); - cf = op->frames + op->cfsiz * op->currframe; - spin_unlock_bh(&op->bcm_tx_lock); + if (cyclic) { + /* read currframe under lock protection */ + spin_lock_bh(&op->bcm_tx_lock); + idx = op->currframe; + memcpy(&cframe, op->frames + op->cfsiz * idx, op->cfsiz); + cf = &cframe; + spin_unlock_bh(&op->bcm_tx_lock); + } dev = dev_get_by_index(sock_net(op->sk), op->ifindex); if (!dev) { @@ -341,14 +348,20 @@ static void bcm_can_tx(struct bcm_op *op) if (!err) op->frames_abs++; - op->currframe++; + /* only advance the cyclic sequence if nothing reset currframe while + * we were sending - a concurrent TX_RESET_MULTI_IDX means this + * frame's bookkeeping belongs to a sequence that no longer exists + */ + if (!cyclic || op->currframe == idx) { + op->currframe++; - /* reached last frame? */ - if (op->currframe >= op->nframes) - op->currframe = 0; + /* reached last frame? */ + if (op->currframe >= op->nframes) + op->currframe = 0; - if (op->count > 0) - op->count--; + if (op->count > 0) + op->count--; + } spin_unlock_bh(&op->bcm_tx_lock); out: @@ -461,7 +474,7 @@ static enum hrtimer_restart bcm_tx_timeout_handler(struct hrtimer *hrtimer) struct bcm_msg_head msg_head; if (op->kt_ival1 && (op->count > 0)) { - bcm_can_tx(op); + bcm_can_tx(op, NULL); if (!op->count && (op->flags & TX_COUNTEVT)) { /* create notification to user */ @@ -478,7 +491,7 @@ static enum hrtimer_restart bcm_tx_timeout_handler(struct hrtimer *hrtimer) } } else if (op->kt_ival2) { - bcm_can_tx(op); + bcm_can_tx(op, NULL); } return bcm_tx_set_expiry(op, &op->timer) ? @@ -622,6 +635,8 @@ static enum hrtimer_restart bcm_rx_timeout_handler(struct hrtimer *hrtimer) struct bcm_op *op = container_of(hrtimer, struct bcm_op, timer); struct bcm_msg_head msg_head; + spin_lock_bh(&op->bcm_rx_update_lock); + /* if user wants to be informed, when cyclic CAN-Messages come back */ if ((op->flags & RX_ANNOUNCE_RESUME) && op->last_frames) { /* clear received CAN frames to indicate 'nothing received' */ @@ -638,6 +653,8 @@ static enum hrtimer_restart bcm_rx_timeout_handler(struct hrtimer *hrtimer) msg_head.can_id = op->can_id; msg_head.nframes = 0; + spin_unlock_bh(&op->bcm_rx_update_lock); + bcm_send_to_user(op, &msg_head, NULL, 0); return HRTIMER_NORESTART; @@ -686,15 +703,26 @@ static int bcm_rx_thr_flush(struct bcm_op *op) static enum hrtimer_restart bcm_rx_thr_handler(struct hrtimer *hrtimer) { struct bcm_op *op = container_of(hrtimer, struct bcm_op, thrtimer); + enum hrtimer_restart ret; - if (bcm_rx_thr_flush(op)) { + spin_lock_bh(&op->bcm_rx_update_lock); + + /* kt_ival2 may have been concurrently cleared by bcm_rx_setup() + * before it cancels this timer - never forward with a zero + * interval in that case. + */ + if (bcm_rx_thr_flush(op) && op->kt_ival2) { hrtimer_forward_now(hrtimer, op->kt_ival2); - return HRTIMER_RESTART; + ret = HRTIMER_RESTART; } else { /* rearm throttle handling */ op->kt_lastmsg = 0; - return HRTIMER_NORESTART; + ret = HRTIMER_NORESTART; } + + spin_unlock_bh(&op->bcm_rx_update_lock); + + return ret; } /* @@ -704,8 +732,10 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data) { struct bcm_op *op = (struct bcm_op *)data; const struct canfd_frame *rxframe = (struct canfd_frame *)skb->data; + struct canfd_frame rtrframe; unsigned int i; unsigned char traffic_flags; + bool rtr_frame; if (op->can_id != rxframe->can_id) return; @@ -729,9 +759,18 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data) /* update statistics */ op->frames_abs++; - if (op->flags & RX_RTR_FRAME) { + /* snapshot the flag under lock: op->flags/op->frames may be updated + * concurrently by bcm_rx_setup(). + */ + spin_lock_bh(&op->bcm_rx_update_lock); + rtr_frame = op->flags & RX_RTR_FRAME; + if (rtr_frame) + memcpy(&rtrframe, op->frames, op->cfsiz); + spin_unlock_bh(&op->bcm_rx_update_lock); + + if (rtr_frame) { /* send reply for RTR-request (placed in op->frames[0]) */ - bcm_can_tx(op); + bcm_can_tx(op, &rtrframe); return; } @@ -743,6 +782,8 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data) traffic_flags |= RX_OWN; } + spin_lock_bh(&op->bcm_rx_update_lock); + if (op->flags & RX_FILTER_ID) { /* the easiest case */ bcm_rx_update_and_send(op, op->last_frames, rxframe, @@ -778,6 +819,8 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data) rx_starttimer: bcm_rx_starttimer(op); + + spin_unlock_bh(&op->bcm_rx_update_lock); } /* @@ -1116,7 +1159,7 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, } if (op->flags & TX_ANNOUNCE) - bcm_can_tx(op); + bcm_can_tx(op, NULL); if (op->flags & STARTTIMER) bcm_tx_start_timer(op); @@ -1130,6 +1173,24 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, return err; } +static void bcm_rx_setup_rtr_check(struct bcm_msg_head *msg_head, + struct bcm_op *op, void *new_frames) +{ + /* funny feature in RX(!)_SETUP only for RTR-mode: + * copy can_id into frame BUT without RTR-flag to + * prevent a full-load-loopback-test ... ;-] + * normalize this on the staged buffer, before it is + * ever installed into op->frames. + */ + if (msg_head->flags & RX_RTR_FRAME) { + struct canfd_frame *frame0 = new_frames; + + if ((msg_head->flags & TX_CP_CAN_ID) || + frame0->can_id == op->can_id) + frame0->can_id = op->can_id & ~CAN_RTR_FLAG; + } +} + /* * bcm_rx_setup - create or update a bcm rx op (for bcm_sendmsg) */ @@ -1164,6 +1225,8 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, /* check the given can_id */ op = bcm_find_op(&bo->rx_ops, msg_head, ifindex); if (op) { + void *new_frames = NULL; + /* update existing BCM operation */ /* @@ -1175,19 +1238,48 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, return -E2BIG; if (msg_head->nframes) { - /* update CAN frames content */ - err = memcpy_from_msg(op->frames, msg, - msg_head->nframes * op->cfsiz); - if (err < 0) - return err; + /* get new CAN frames content before locking */ + new_frames = kmalloc(msg_head->nframes * op->cfsiz, + GFP_KERNEL); + if (!new_frames) + return -ENOMEM; - /* clear last_frames to indicate 'nothing received' */ - memset(op->last_frames, 0, msg_head->nframes * op->cfsiz); + err = memcpy_from_msg(new_frames, msg, + msg_head->nframes * op->cfsiz); + if (err < 0) { + kfree(new_frames); + return err; + } + + bcm_rx_setup_rtr_check(msg_head, op, new_frames); } + spin_lock_bh(&op->bcm_rx_update_lock); op->nframes = msg_head->nframes; op->flags = msg_head->flags; + if (msg_head->nframes) { + /* update CAN frames content */ + memcpy(op->frames, new_frames, + msg_head->nframes * op->cfsiz); + + /* clear last_frames to indicate 'nothing received' */ + memset(op->last_frames, 0, + msg_head->nframes * op->cfsiz); + } + + if (msg_head->flags & SETTIMER) { + op->ival1 = msg_head->ival1; + op->ival2 = msg_head->ival2; + op->kt_ival1 = bcm_timeval_to_ktime(msg_head->ival1); + op->kt_ival2 = bcm_timeval_to_ktime(msg_head->ival2); + op->kt_lastmsg = 0; + } + spin_unlock_bh(&op->bcm_rx_update_lock); + + /* free temporary frames / kfree(NULL) is safe */ + kfree(new_frames); + /* Only an update -> do not call can_rx_register() */ do_rx_register = 0; @@ -1198,6 +1290,7 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, return -ENOMEM; spin_lock_init(&op->bcm_tx_lock); + spin_lock_init(&op->bcm_rx_update_lock); op->can_id = msg_head->can_id; op->nframes = msg_head->nframes; op->cfsiz = CFSIZ(msg_head->flags); @@ -1239,6 +1332,8 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, kfree(op); return err; } + + bcm_rx_setup_rtr_check(msg_head, op, op->frames); } /* bcm_can_tx / bcm_tx_timeout_handler needs this */ @@ -1266,29 +1361,22 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, /* check flags */ if (op->flags & RX_RTR_FRAME) { - struct canfd_frame *frame0 = op->frames; - /* no timers in RTR-mode */ hrtimer_cancel(&op->thrtimer); hrtimer_cancel(&op->timer); - - /* - * funny feature in RX(!)_SETUP only for RTR-mode: - * copy can_id into frame BUT without RTR-flag to - * prevent a full-load-loopback-test ... ;-] - */ - if ((op->flags & TX_CP_CAN_ID) || - (frame0->can_id == op->can_id)) - frame0->can_id = op->can_id & ~CAN_RTR_FLAG; - } else { if (op->flags & SETTIMER) { - /* set timer value */ - op->ival1 = msg_head->ival1; - op->ival2 = msg_head->ival2; - op->kt_ival1 = bcm_timeval_to_ktime(msg_head->ival1); - op->kt_ival2 = bcm_timeval_to_ktime(msg_head->ival2); + /* set timers (locked) for newly created op */ + if (do_rx_register) { + spin_lock_bh(&op->bcm_rx_update_lock); + op->ival1 = msg_head->ival1; + op->ival2 = msg_head->ival2; + op->kt_ival1 = bcm_timeval_to_ktime(msg_head->ival1); + op->kt_ival2 = bcm_timeval_to_ktime(msg_head->ival2); + op->kt_lastmsg = 0; + spin_unlock_bh(&op->bcm_rx_update_lock); + } /* disable an active timer due to zero value? */ if (!op->kt_ival1) @@ -1298,9 +1386,11 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, * In any case cancel the throttle timer, flush * potentially blocked msgs and reset throttle handling */ - op->kt_lastmsg = 0; hrtimer_cancel(&op->thrtimer); + + spin_lock_bh(&op->bcm_rx_update_lock); bcm_rx_thr_flush(op); + spin_unlock_bh(&op->bcm_rx_update_lock); } if ((op->flags & STARTTIMER) && op->kt_ival1) From e6c24ba95fc3f1b5e1dcd28b1c6e59ef61a9daa5 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Tue, 14 Jul 2026 18:55:26 +0200 Subject: [PATCH 1033/1054] can: bcm: fix CAN frame rx/tx statistics KCSAN detected a data race within the bcm_rx_handler() when two CAN frames have been simultaneously received and processed in a single rx op by two different CPUs. Use atomic operations with (signed) long data types to access the statistics in the hot path to fix the KCSAN complaint. Additionally simplify the update and check of statistics overflow by using the atomic operations in separate bcm_update_[rx|tx]_stats() functions. The rx variant runs under bcm_rx_update_lock to prevent races when resetting the two rx counters; the tx variant runs under bcm_tx_lock and only needs to guard its own counter's overflow. As the rx path resets its values already at LONG_MAX / 100, there is no conflict between the two locking domains (bcm_rx_update_lock vs. bcm_tx_lock) even for ops that use both paths. The rx statistics update and the frames_filtered update in bcm_rx_changed() were previously performed in two separate bcm_rx_update_lock sections. For an rx op subscribed on all interfaces (ifindex == 0), bcm_rx_handler() can run concurrently on different CPUs, so a counter reset by one CPU between these two sections could leave frames_filtered larger than frames_abs on another CPU, producing a bogus (even negative) reduction percentage in procfs. Update the statistics in the same critical section as bcm_rx_changed() to close this gap, which also removes the now unneeded extra lock/unlock pair around the traffic_flags calculation. Fixes: ffd980f976e7 ("[CAN]: Add broadcast manager (bcm) protocol") Signed-off-by: Oliver Hartkopp Link: https://patch.msgid.link/20260714-bcm_fixes-v15-4-562f7e3e42da@hartkopp.net Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde --- net/can/bcm.c | 71 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 47 insertions(+), 24 deletions(-) diff --git a/net/can/bcm.c b/net/can/bcm.c index 1e5f8d65d351..03c98e4cc677 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -112,7 +112,7 @@ struct bcm_op { int ifindex; canid_t can_id; u32 flags; - unsigned long frames_abs, frames_filtered; + atomic_long_t frames_abs, frames_filtered; struct bcm_timeval ival1, ival2; struct hrtimer timer, thrtimer; ktime_t rx_stamp, kt_ival1, kt_ival2, kt_lastmsg; @@ -229,10 +229,13 @@ static int bcm_proc_show(struct seq_file *m, void *v) list_for_each_entry_rcu(op, &bo->rx_ops, list) { - unsigned long reduction; + long reduction, frames_filtered, frames_abs; + + frames_filtered = atomic_long_read(&op->frames_filtered); + frames_abs = atomic_long_read(&op->frames_abs); /* print only active entries & prevent division by zero */ - if (!op->frames_abs) + if (!frames_abs) continue; seq_printf(m, "rx_op: %03X %-5s ", op->can_id, @@ -254,9 +257,9 @@ static int bcm_proc_show(struct seq_file *m, void *v) (long long)ktime_to_us(op->kt_ival2)); seq_printf(m, "# recv %ld (%ld) => reduction: ", - op->frames_filtered, op->frames_abs); + frames_filtered, frames_abs); - reduction = 100 - (op->frames_filtered * 100) / op->frames_abs; + reduction = 100 - (frames_filtered * 100) / frames_abs; seq_printf(m, "%s%ld%%\n", (reduction == 100) ? "near " : "", reduction); @@ -280,7 +283,8 @@ static int bcm_proc_show(struct seq_file *m, void *v) seq_printf(m, "t2=%lld ", (long long)ktime_to_us(op->kt_ival2)); - seq_printf(m, "# sent %ld\n", op->frames_abs); + seq_printf(m, "# sent %ld\n", + atomic_long_read(&op->frames_abs)); } seq_putc(m, '\n'); @@ -290,6 +294,24 @@ static int bcm_proc_show(struct seq_file *m, void *v) } #endif /* CONFIG_PROC_FS */ +static void bcm_update_rx_stats(struct bcm_op *op) +{ + /* prevent overflow of the reduction% calculation in bcm_proc_show() */ + if (atomic_long_inc_return(&op->frames_abs) > LONG_MAX / 100) { + atomic_long_set(&op->frames_filtered, 0); + atomic_long_set(&op->frames_abs, 0); + } +} + +static void bcm_update_tx_stats(struct bcm_op *op) +{ + /* tx_op has no reduction% calculation - use the full range and + * just keep the displayed counter non-negative on overflow + */ + if (atomic_long_inc_return(&op->frames_abs) == LONG_MAX) + atomic_long_set(&op->frames_abs, 0); +} + /* * bcm_can_tx - send the (next) CAN frame to the appropriate CAN interface * of the given bcm tx op @@ -346,7 +368,7 @@ static void bcm_can_tx(struct bcm_op *op, struct canfd_frame *cf) spin_lock_bh(&op->bcm_tx_lock); if (!err) - op->frames_abs++; + bcm_update_tx_stats(op); /* only advance the cyclic sequence if nothing reset currframe while * we were sending - a concurrent TX_RESET_MULTI_IDX means this @@ -505,12 +527,9 @@ static void bcm_rx_changed(struct bcm_op *op, struct canfd_frame *data) { struct bcm_msg_head head; - /* update statistics */ - op->frames_filtered++; - - /* prevent statistics overflow */ - if (op->frames_filtered > ULONG_MAX/100) - op->frames_filtered = op->frames_abs = 0; + /* update statistics (frames_filtered <= frames_abs) */ + if (atomic_long_read(&op->frames_abs)) + atomic_long_inc(&op->frames_filtered); /* this element is not throttled anymore */ data->flags &= ~RX_THR; @@ -756,24 +775,30 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data) op->rx_stamp = skb->tstamp; /* save originator for recvfrom() */ op->rx_ifindex = skb->dev->ifindex; - /* update statistics */ - op->frames_abs++; - /* snapshot the flag under lock: op->flags/op->frames may be updated - * concurrently by bcm_rx_setup(). - */ + /* op->flags/op->frames may be updated concurrently by bcm_rx_setup() */ spin_lock_bh(&op->bcm_rx_update_lock); - rtr_frame = op->flags & RX_RTR_FRAME; - if (rtr_frame) - memcpy(&rtrframe, op->frames, op->cfsiz); - spin_unlock_bh(&op->bcm_rx_update_lock); + rtr_frame = op->flags & RX_RTR_FRAME; if (rtr_frame) { + bcm_update_rx_stats(op); + /* snapshot RTR content under lock */ + memcpy(&rtrframe, op->frames, op->cfsiz); + spin_unlock_bh(&op->bcm_rx_update_lock); + /* send reply for RTR-request (placed in op->frames[0]) */ bcm_can_tx(op, &rtrframe); return; } + /* update statistics in the same critical section as bcm_rx_changed() + * below: frames_filtered must never be checked/incremented against a + * frames_abs snapshot from a concurrent bcm_rx_handler() call on + * another CPU for the same (wildcard) op, or frames_filtered can end + * up larger than frames_abs. + */ + bcm_update_rx_stats(op); + /* compute flags to distinguish between own/local/remote CAN traffic */ traffic_flags = 0; if (skb->sk) { @@ -782,8 +807,6 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data) traffic_flags |= RX_OWN; } - spin_lock_bh(&op->bcm_rx_update_lock); - if (op->flags & RX_FILTER_ID) { /* the easiest case */ bcm_rx_update_and_send(op, op->last_frames, rxframe, From 7b2c3eabc4dafc062a25e10711154f2107526a78 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Tue, 14 Jul 2026 18:55:27 +0200 Subject: [PATCH 1034/1054] can: bcm: add missing rcu list annotations and operations sashiko-bot remarked the missing use of list_add_rcu() in bcm_[rx|tx]_setup() to have a proper initialized bcm_op structure when bcm_proc_show() traverses the bcm_op's under rcu_read_lock(). To cover all initial settings of the bcm_op's the list_add_rcu() calls are moved to the end of the setup code. While at it, also fix the mirroring removal side: bcm_release() called bcm_remove_op() - which frees the op via call_rcu() - on ops that were still linked in bo->tx_ops/bo->rx_ops, without list_del_rcu() first. Unlink each op with list_del_rcu() before handing it to bcm_remove_op(), matching the existing pattern in bcm_delete_tx_op()/bcm_delete_rx_op(). Reported-by: sashiko-reviews@lists.linux.dev Closes: https://lore.kernel.org/linux-can/20260610094654.A1FFE1F00893@smtp.kernel.org/ Fixes: dac5e6249159 ("can: bcm: add missing rcu read protection for procfs content") Signed-off-by: Oliver Hartkopp Link: https://patch.msgid.link/20260714-bcm_fixes-v15-5-562f7e3e42da@hartkopp.net Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde --- net/can/bcm.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/net/can/bcm.c b/net/can/bcm.c index 03c98e4cc677..5c1e83eeb4ff 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -265,7 +265,7 @@ static int bcm_proc_show(struct seq_file *m, void *v) (reduction == 100) ? "near " : "", reduction); } - list_for_each_entry(op, &bo->tx_ops, list) { + list_for_each_entry_rcu(op, &bo->tx_ops, list) { seq_printf(m, "tx_op: %03X %s ", op->can_id, bcm_proc_getifname(net, ifname, op->ifindex)); @@ -1017,6 +1017,7 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, struct bcm_sock *bo = bcm_sk(sk); struct bcm_op *op; struct canfd_frame *cf; + bool add_op_to_list = false; unsigned int i; int err; @@ -1158,8 +1159,7 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, hrtimer_setup(&op->thrtimer, hrtimer_dummy_timeout, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT); - /* add this bcm_op to the list of the tx_ops */ - list_add(&op->list, &bo->tx_ops); + add_op_to_list = true; } /* if ((op = bcm_find_op(&bo->tx_ops, msg_head->can_id, ifindex))) */ @@ -1181,6 +1181,10 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, op->flags |= TX_ANNOUNCE; } + /* add this bcm_op to the list of the tx_ops? */ + if (add_op_to_list) + list_add_rcu(&op->list, &bo->tx_ops); + if (op->flags & TX_ANNOUNCE) bcm_can_tx(op, NULL); @@ -1373,9 +1377,6 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, hrtimer_setup(&op->thrtimer, bcm_rx_thr_handler, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT); - /* add this bcm_op to the list of the rx_ops */ - list_add(&op->list, &bo->rx_ops); - /* call can_rx_register() */ do_rx_register = 1; @@ -1449,10 +1450,12 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, bcm_rx_handler, op, "bcm", sk); if (err) { /* this bcm rx op is broken -> remove it */ - list_del_rcu(&op->list); bcm_remove_op(op); return err; } + + /* add this bcm_op to the list of the rx_ops */ + list_add_rcu(&op->list, &bo->rx_ops); } return msg_head->nframes * op->cfsiz + MHSIZ; @@ -1786,8 +1789,10 @@ static int bcm_release(struct socket *sock) remove_proc_entry(bo->procname, net->can.bcmproc_dir); #endif /* CONFIG_PROC_FS */ - list_for_each_entry_safe(op, next, &bo->tx_ops, list) + list_for_each_entry_safe(op, next, &bo->tx_ops, list) { + list_del_rcu(&op->list); bcm_remove_op(op); + } list_for_each_entry_safe(op, next, &bo->rx_ops, list) { /* @@ -1818,8 +1823,10 @@ static int bcm_release(struct socket *sock) synchronize_rcu(); - list_for_each_entry_safe(op, next, &bo->rx_ops, list) + list_for_each_entry_safe(op, next, &bo->rx_ops, list) { + list_del_rcu(&op->list); bcm_remove_op(op); + } /* remove device reference */ if (bo->bound) { From 12ce799f7ab1e05bd8fbf79e46f403bfe5597ebc Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Tue, 14 Jul 2026 18:55:28 +0200 Subject: [PATCH 1035/1054] can: bcm: extend bcm_tx_lock usage for data and timer updates Stage new CAN frame content for an existing tx op into a kmalloc()'d buffer and validate it there, mirroring the approach already used in bcm_rx_setup(). Only copy the validated data into op->frames while holding op->bcm_tx_lock, so bcm_can_tx() and bcm_tx_timeout_handler() can no longer observe a partially updated or unvalidated frame. Add a missing error path for memcpy_from_msg() when copying CAN frame data from userspace. Also move the kt_ival1/kt_ival2/ival1/ival2 updates in bcm_tx_setup() under op->bcm_tx_lock, and read kt_ival1/kt_ival2/count under the same lock in bcm_tx_set_expiry() and bcm_tx_timeout_handler(), closing the torn 64-bit ktime_t read on 32-bit platforms. Fixes: c2aba69d0c36 ("can: bcm: add locking for bcm_op runtime updates") Signed-off-by: Oliver Hartkopp Link: https://patch.msgid.link/20260714-bcm_fixes-v15-6-562f7e3e42da@hartkopp.net Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde --- net/can/bcm.c | 108 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 77 insertions(+), 31 deletions(-) diff --git a/net/can/bcm.c b/net/can/bcm.c index 5c1e83eeb4ff..68a62f605432 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -128,7 +128,7 @@ struct bcm_op { struct canfd_frame last_sframe; struct sock *sk; struct net_device *rx_reg_dev; - spinlock_t bcm_tx_lock; /* protect currframe/count in runtime updates */ + spinlock_t bcm_tx_lock; /* protect tx data and timer updates */ spinlock_t bcm_rx_update_lock; /* protect filter/timer data updates */ }; @@ -472,12 +472,18 @@ static bool bcm_tx_set_expiry(struct bcm_op *op, struct hrtimer *hrt) { ktime_t ival; + spin_lock_bh(&op->bcm_tx_lock); + if (op->kt_ival1 && op->count) ival = op->kt_ival1; - else if (op->kt_ival2) + else if (op->kt_ival2) { ival = op->kt_ival2; - else + } else { + spin_unlock_bh(&op->bcm_tx_lock); return false; + } + + spin_unlock_bh(&op->bcm_tx_lock); hrtimer_set_expires(hrt, ktime_add(ktime_get(), ival)); return true; @@ -494,25 +500,47 @@ static enum hrtimer_restart bcm_tx_timeout_handler(struct hrtimer *hrtimer) { struct bcm_op *op = container_of(hrtimer, struct bcm_op, timer); struct bcm_msg_head msg_head; + bool tx_ival1, tx_ival2; + + /* snapshot kt_ival1/kt_ival2/count under lock to avoid torn + * ktime_t reads racing with concurrent bcm_tx_setup() updates + */ + spin_lock_bh(&op->bcm_tx_lock); + tx_ival1 = op->kt_ival1 && (op->count > 0); + tx_ival2 = !!op->kt_ival2; + spin_unlock_bh(&op->bcm_tx_lock); + + if (tx_ival1) { + u32 flags, count; + struct bcm_timeval ival1, ival2; - if (op->kt_ival1 && (op->count > 0)) { bcm_can_tx(op, NULL); - if (!op->count && (op->flags & TX_COUNTEVT)) { + /* snapshot variables under lock to avoid torn reads racing + * with concurrent bcm_tx_setup() updates + */ + spin_lock_bh(&op->bcm_tx_lock); + flags = op->flags; + count = op->count; + ival1 = op->ival1; + ival2 = op->ival2; + spin_unlock_bh(&op->bcm_tx_lock); + + if (!count && (flags & TX_COUNTEVT)) { /* create notification to user */ memset(&msg_head, 0, sizeof(msg_head)); msg_head.opcode = TX_EXPIRED; - msg_head.flags = op->flags; - msg_head.count = op->count; - msg_head.ival1 = op->ival1; - msg_head.ival2 = op->ival2; + msg_head.flags = flags; + msg_head.count = count; + msg_head.ival1 = ival1; + msg_head.ival2 = ival2; msg_head.can_id = op->can_id; msg_head.nframes = 0; bcm_send_to_user(op, &msg_head, NULL, 0); } - } else if (op->kt_ival2) { + } else if (tx_ival2) { bcm_can_tx(op, NULL); } @@ -1036,6 +1064,8 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, /* check the given can_id */ op = bcm_find_op(&bo->tx_ops, msg_head, ifindex); if (op) { + void *new_frames; + /* update existing BCM operation */ /* @@ -1046,11 +1076,23 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, if (msg_head->nframes > op->nframes) return -E2BIG; - /* update CAN frames content */ + /* get new CAN frames content into a staging buffer before + * locking: validate and normalize the frames there so that + * bcm_can_tx() / bcm_tx_timeout_handler() never observe a + * partially updated or unvalidated frame in op->frames + */ + new_frames = kmalloc(msg_head->nframes * op->cfsiz, GFP_KERNEL); + if (!new_frames) + return -ENOMEM; + for (i = 0; i < msg_head->nframes; i++) { - cf = op->frames + op->cfsiz * i; + cf = new_frames + op->cfsiz * i; err = memcpy_from_msg((u8 *)cf, msg, op->cfsiz); + if (err < 0) { + kfree(new_frames); + return err; + } if (op->flags & CAN_FD_FRAME) { if (cf->len > 64) @@ -1060,37 +1102,39 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, err = -EINVAL; } - if (err < 0) + if (err < 0) { + kfree(new_frames); return err; + } if (msg_head->flags & TX_CP_CAN_ID) { /* copy can_id into frame */ cf->can_id = msg_head->can_id; } } + + spin_lock_bh(&op->bcm_tx_lock); + + /* update CAN frames content */ + memcpy(op->frames, new_frames, msg_head->nframes * op->cfsiz); + op->flags = msg_head->flags; - /* only lock for unlikely count/nframes/currframe changes */ if (op->nframes != msg_head->nframes || - op->flags & TX_RESET_MULTI_IDX || - op->flags & SETTIMER) { - - spin_lock_bh(&op->bcm_tx_lock); - - if (op->nframes != msg_head->nframes || - op->flags & TX_RESET_MULTI_IDX) { - /* potentially update changed nframes */ - op->nframes = msg_head->nframes; - /* restart multiple frame transmission */ - op->currframe = 0; - } - - if (op->flags & SETTIMER) - op->count = msg_head->count; - - spin_unlock_bh(&op->bcm_tx_lock); + op->flags & TX_RESET_MULTI_IDX) { + /* potentially update changed nframes */ + op->nframes = msg_head->nframes; + /* restart multiple frame transmission */ + op->currframe = 0; } + if (op->flags & SETTIMER) + op->count = msg_head->count; + + spin_unlock_bh(&op->bcm_tx_lock); + + kfree(new_frames); + } else { /* insert new BCM operation for the given can_id */ @@ -1165,10 +1209,12 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, if (op->flags & SETTIMER) { /* set timer values */ + spin_lock_bh(&op->bcm_tx_lock); op->ival1 = msg_head->ival1; op->ival2 = msg_head->ival2; op->kt_ival1 = bcm_timeval_to_ktime(msg_head->ival1); op->kt_ival2 = bcm_timeval_to_ktime(msg_head->ival2); + spin_unlock_bh(&op->bcm_tx_lock); /* disable an active timer due to zero values? */ if (!op->kt_ival1 && !op->kt_ival2) From 62ec41f364648be79d54d94d0d240ee326948afd Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Tue, 14 Jul 2026 18:55:29 +0200 Subject: [PATCH 1036/1054] can: bcm: validate frame length in bcm_rx_setup() for RTR replies bcm_tx_setup() validates cf->len against the CAN/CAN FD DLC limits before installing frames for TX_SETUP, but bcm_rx_setup() never did the same for the RTR-reply frame configured via RX_SETUP with RX_RTR_FRAME. Fixes: ffd980f976e7 ("[CAN]: Add broadcast manager (bcm) protocol") Signed-off-by: Oliver Hartkopp Link: https://patch.msgid.link/20260714-bcm_fixes-v15-7-562f7e3e42da@hartkopp.net Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde --- net/can/bcm.c | 59 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 41 insertions(+), 18 deletions(-) diff --git a/net/can/bcm.c b/net/can/bcm.c index 68a62f605432..2d9c9cd74536 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -1246,22 +1246,37 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, return err; } -static void bcm_rx_setup_rtr_check(struct bcm_msg_head *msg_head, - struct bcm_op *op, void *new_frames) +static int bcm_rx_setup_rtr_check(struct bcm_msg_head *msg_head, + struct bcm_op *op, void *new_frames) { + struct canfd_frame *frame0 = new_frames; + + if (!(msg_head->flags & RX_RTR_FRAME)) + return 0; + + /* this frame is sent out as-is by bcm_can_tx() whenever a matching + * remote request is received, so validate its length the same way + * bcm_tx_setup() validates TX_SETUP frames before installing it + */ + if (msg_head->flags & CAN_FD_FRAME) { + if (frame0->len > 64) + return -EINVAL; + } else { + if (frame0->len > 8) + return -EINVAL; + } + /* funny feature in RX(!)_SETUP only for RTR-mode: * copy can_id into frame BUT without RTR-flag to * prevent a full-load-loopback-test ... ;-] * normalize this on the staged buffer, before it is * ever installed into op->frames. */ - if (msg_head->flags & RX_RTR_FRAME) { - struct canfd_frame *frame0 = new_frames; + if ((msg_head->flags & TX_CP_CAN_ID) || + frame0->can_id == op->can_id) + frame0->can_id = op->can_id & ~CAN_RTR_FLAG; - if ((msg_head->flags & TX_CP_CAN_ID) || - frame0->can_id == op->can_id) - frame0->can_id = op->can_id & ~CAN_RTR_FLAG; - } + return 0; } /* @@ -1324,7 +1339,11 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, return err; } - bcm_rx_setup_rtr_check(msg_head, op, new_frames); + err = bcm_rx_setup_rtr_check(msg_head, op, new_frames); + if (err < 0) { + kfree(new_frames); + return err; + } } spin_lock_bh(&op->bcm_rx_update_lock); @@ -1397,16 +1416,12 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, if (msg_head->nframes) { err = memcpy_from_msg(op->frames, msg, msg_head->nframes * op->cfsiz); - if (err < 0) { - if (op->frames != &op->sframe) - kfree(op->frames); - if (op->last_frames != &op->last_sframe) - kfree(op->last_frames); - kfree(op); - return err; - } + if (err < 0) + goto free_op; - bcm_rx_setup_rtr_check(msg_head, op, op->frames); + err = bcm_rx_setup_rtr_check(msg_head, op, op->frames); + if (err < 0) + goto free_op; } /* bcm_can_tx / bcm_tx_timeout_handler needs this */ @@ -1505,6 +1520,14 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, } return msg_head->nframes * op->cfsiz + MHSIZ; + +free_op: + if (op->frames != &op->sframe) + kfree(op->frames); + if (op->last_frames != &op->last_sframe) + kfree(op->last_frames); + kfree(op); + return err; } /* From d59948293ea34b6337ce2b5febab8510de70048c Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Tue, 14 Jul 2026 18:55:30 +0200 Subject: [PATCH 1037/1054] can: bcm: add missing device refcount for CAN filter removal sashiko-bot remarked a problem with a concurrent device unregistration in isotp.c which also is present in the bcm.c code. A former fix for raw.c commit c275a176e4b6 ("can: raw: add missing refcount for memory leak fix") introduced a netdevice_tracker which solves the issue for bcm.c too. bcm_release(), bcm_delete_rx_op() and bcm_notifier() relied on dev_get_by_index(ifindex) to re-find the device for an rx_op before unregistering its filter. If a concurrent NETDEV_UNREGISTER has already unlisted the device from the ifindex table, that lookup fails and can_rx_unregister() is silently skipped, leaving a stale CAN filter pointing at the soon-to-be-freed bcm_op/socket. Hold a netdev_hold()/netdev_put() tracked reference on op->rx_reg_dev from the moment the rx filter is registered in bcm_rx_setup() until it is unregistered in bcm_rx_unreg(), and use that reference directly in bcm_release() and bcm_delete_rx_op() instead of re-looking the device up by ifindex. Reported-by: sashiko-bot@kernel.org Closes: https://sashiko.dev/#/patchset/20260707094716.63578-1-socketcan@hartkopp.net Fixes: ffd980f976e7 ("[CAN]: Add broadcast manager (bcm) protocol") Signed-off-by: Oliver Hartkopp Link: https://patch.msgid.link/20260714-bcm_fixes-v15-8-562f7e3e42da@hartkopp.net Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde --- net/can/bcm.c | 47 +++++++++++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/net/can/bcm.c b/net/can/bcm.c index 2d9c9cd74536..25842061800b 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -128,6 +128,7 @@ struct bcm_op { struct canfd_frame last_sframe; struct sock *sk; struct net_device *rx_reg_dev; + netdevice_tracker rx_reg_dev_tracker; spinlock_t bcm_tx_lock; /* protect tx data and timer updates */ spinlock_t bcm_rx_update_lock; /* protect filter/timer data updates */ }; @@ -937,6 +938,7 @@ static void bcm_rx_unreg(struct net_device *dev, struct bcm_op *op) /* mark as removed subscription */ op->rx_reg_dev = NULL; + netdev_put(dev, &op->rx_reg_dev_tracker); } else printk(KERN_ERR "can-bcm: bcm_rx_unreg: registered device " "mismatch %p %p\n", op->rx_reg_dev, dev); @@ -967,17 +969,14 @@ static int bcm_delete_rx_op(struct list_head *ops, struct bcm_msg_head *mh, * Only remove subscriptions that had not * been removed due to NETDEV_UNREGISTER * in bcm_notifier() + * + * op->rx_reg_dev is a tracked reference taken + * when the subscription was registered, so it + * stays valid here even if a concurrent + * NETDEV_UNREGISTER already unlisted the dev. */ - if (op->rx_reg_dev) { - struct net_device *dev; - - dev = dev_get_by_index(sock_net(op->sk), - op->ifindex); - if (dev) { - bcm_rx_unreg(dev, op); - dev_put(dev); - } - } + if (op->rx_reg_dev) + bcm_rx_unreg(op->rx_reg_dev, op); } else can_rx_unregister(sock_net(op->sk), NULL, op->can_id, @@ -1496,7 +1495,17 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, bcm_rx_handler, op, "bcm", sk); - op->rx_reg_dev = dev; + /* keep a tracked reference so that a later + * unregister can safely reach the device even + * if a concurrent NETDEV_UNREGISTER has + * already unlisted it by ifindex + */ + if (!err) { + op->rx_reg_dev = dev; + netdev_hold(dev, + &op->rx_reg_dev_tracker, + GFP_KERNEL); + } dev_put(dev); } else { /* the requested device is gone - do not @@ -1873,16 +1882,14 @@ static int bcm_release(struct socket *sock) * Only remove subscriptions that had not * been removed due to NETDEV_UNREGISTER * in bcm_notifier() + * + * op->rx_reg_dev is a tracked reference taken + * when the subscription was registered, so it + * stays valid here even if a concurrent + * NETDEV_UNREGISTER already unlisted the device. */ - if (op->rx_reg_dev) { - struct net_device *dev; - - dev = dev_get_by_index(net, op->ifindex); - if (dev) { - bcm_rx_unreg(dev, op); - dev_put(dev); - } - } + if (op->rx_reg_dev) + bcm_rx_unreg(op->rx_reg_dev, op); } else can_rx_unregister(net, NULL, op->can_id, REGMASK(op->can_id), From 3b762c0d950383ab7a002686c9136b9aa55d2d70 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Tue, 14 Jul 2026 18:55:31 +0200 Subject: [PATCH 1038/1054] can: bcm: fix stale rx/tx ops after device removal RX: an RX_SETUP update(!) for an existing op skipped can_rx_register() unconditionally, even when a concurrent NETDEV_UNREGISTER had already torn down its registration (op->rx_reg_dev == NULL). This silently did not re-enable frame delivery for that updated filter. bcm_rx_setup() now re-registers in that case, while leaving rx_ops with ifindex = 0 (all CAN devices) which never carry a tracked rx_reg_dev registered as-is. TX: bcm_notify() only handled bo->rx_ops on NETDEV_UNREGISTER, leaving tx_ops with an active cyclic transmission re-arming its hrtimer indefinitely to execute bcm_tx_timeout_handler(). Cancelling the hrtimer prevents the runaway timer and any injection into a later reused ifindex, since nothing else calls bcm_can_tx() for the op until an explicit TX_SETUP update re-arms it. Unlike bcm_rx_unreg(), which clears the tracked rx_reg_dev for rx_ops, the ifindex is intentionally left unchanged for tx_ops. bcm_tx_setup() always rejects ifindex 0, so clearing it would strand the op: neither a later TX_SETUP (bcm_find_op()) nor TX_DELETE (bcm_delete_tx_op()) could ever find it again, since both require an exact ifindex match. Reported-by: sashiko-bot@kernel.org Closes: https://lore.kernel.org/linux-can/20260708094536.DDF821F00A3A@smtp.kernel.org/ Closes: https://lore.kernel.org/linux-can/20260708154039.347ED1F000E9@smtp.kernel.org/ Fixes: ffd980f976e7 ("[CAN]: Add broadcast manager (bcm) protocol") Signed-off-by: Oliver Hartkopp Link: https://patch.msgid.link/20260714-bcm_fixes-v15-9-562f7e3e42da@hartkopp.net Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde --- net/can/bcm.c | 54 +++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 44 insertions(+), 10 deletions(-) diff --git a/net/can/bcm.c b/net/can/bcm.c index 25842061800b..a53dba6ab8b8 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -1287,6 +1287,7 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, struct bcm_sock *bo = bcm_sk(sk); struct bcm_op *op; int do_rx_register; + int new_op = 0; int err = 0; if ((msg_head->flags & RX_FILTER_ID) || (!(msg_head->nframes))) { @@ -1371,8 +1372,15 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, /* free temporary frames / kfree(NULL) is safe */ kfree(new_frames); - /* Only an update -> do not call can_rx_register() */ - do_rx_register = 0; + /* Don't register a new CAN filter for the rx_op update unless + * a concurrent NETDEV_UNREGISTER notifier already tore down + * the previous registration. In this case the receiver needs + * to be re-registered here so that this update doesn't + * silently stop delivering frames for the given ifindex. + * Ops with ifindex = 0 (all CAN interfaces) never carry a + * tracked rx_reg_dev and stay registered as-is. + */ + do_rx_register = (ifindex && !op->rx_reg_dev) ? 1 : 0; } else { /* insert new BCM operation for the given can_id */ @@ -1439,6 +1447,7 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, /* call can_rx_register() */ do_rx_register = 1; + new_op = 1; } /* if ((op = bcm_find_op(&bo->rx_ops, msg_head->can_id, ifindex))) */ @@ -1452,7 +1461,7 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, if (op->flags & SETTIMER) { /* set timers (locked) for newly created op */ - if (do_rx_register) { + if (new_op) { spin_lock_bh(&op->bcm_rx_update_lock); op->ival1 = msg_head->ival1; op->ival2 = msg_head->ival2; @@ -1482,7 +1491,10 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, HRTIMER_MODE_REL_SOFT); } - /* now we can register for can_ids, if we added a new bcm_op */ + /* now we can register for can_ids, if we added a new bcm_op + * or need to re-register after a NETDEV_UNREGISTER tore down + * the previous registration of an existing op + */ if (do_rx_register) { if (ifindex) { struct net_device *dev; @@ -1514,18 +1526,32 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, err = -ENODEV; } - } else + } else { err = can_rx_register(sock_net(sk), NULL, op->can_id, REGMASK(op->can_id), bcm_rx_handler, op, "bcm", sk); + } + if (err) { - /* this bcm rx op is broken -> remove it */ - bcm_remove_op(op); + /* newly created bcm rx op is broken -> remove it */ + if (new_op) { + bcm_remove_op(op); + return err; + } + + /* an existing op just stays unregistered. + * Cancel op->timer and (defensively) op->thrtimer. + * Other settings can't be reached until the next + * successful RX_SETUP. + */ + hrtimer_cancel(&op->timer); + hrtimer_cancel(&op->thrtimer); return err; } - /* add this bcm_op to the list of the rx_ops */ - list_add_rcu(&op->list, &bo->rx_ops); + /* add a new bcm_op to the list of the rx_ops */ + if (new_op) + list_add_rcu(&op->list, &bo->rx_ops); } return msg_head->nframes * op->cfsiz + MHSIZ; @@ -1745,11 +1771,19 @@ static void bcm_notify(struct bcm_sock *bo, unsigned long msg, case NETDEV_UNREGISTER: lock_sock(sk); - /* remove device specific receive entries */ + /* rx_ops: remove device specific receive entries */ list_for_each_entry(op, &bo->rx_ops, list) if (op->rx_reg_dev == dev) bcm_rx_unreg(dev, op); + /* tx_ops: stop device specific cyclic transmissions on the + * vanishing ifindex. Cancelling the timer is enough to stop + * cyclic bcm_can_tx() calls as there is no re-arming. + */ + list_for_each_entry(op, &bo->tx_ops, list) + if (op->ifindex == dev->ifindex) + hrtimer_cancel(&op->timer); + /* remove device reference, if this is our bound device */ if (bo->bound && bo->ifindex == dev->ifindex) { #if IS_ENABLED(CONFIG_PROC_FS) From 58fd6cbc8541216af1d7ed272ea7ac2b66d50fd8 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Tue, 14 Jul 2026 18:55:32 +0200 Subject: [PATCH 1039/1054] can: bcm: fix data race on rx_stamp/rx_ifindex in bcm_rx_handler() For an rx op subscribed on all interfaces (ifindex == 0), the same op is registered once in the shared per-netns wildcard filter list, so bcm_rx_handler() can run concurrently on different CPUs for frames arriving on different net devices. op->rx_stamp and op->rx_ifindex were written before bcm_rx_update_lock was taken, allowing concurrent writers to race each other - including a torn store of the 64-bit rx_stamp on 32-bit platforms. Beyond a torn store bcm_send_to_user() must report the timestamp/ifindex of the very same frame whose content it is delivering. So the assignment is placed in the same unbroken bcm_rx_update_lock section as the content comparison. As a side effect, the RTR-request frame feature (which never reach bcm_send_to_user()) no longer updates rx_stamp/rx_ifindex, since only the notification path needs them. Fixes: ffd980f976e7 ("[CAN]: Add broadcast manager (bcm) protocol") Reported-by: sashiko-bot@kernel.org Closes: https://lore.kernel.org/linux-can/20260707145135.5BC831F00A3A@smtp.kernel.org/ Signed-off-by: Oliver Hartkopp Link: https://patch.msgid.link/20260714-bcm_fixes-v15-10-562f7e3e42da@hartkopp.net Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde --- net/can/bcm.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/net/can/bcm.c b/net/can/bcm.c index a53dba6ab8b8..f213a0b37791 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -800,11 +800,6 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data) /* disable timeout */ hrtimer_cancel(&op->timer); - /* save rx timestamp */ - op->rx_stamp = skb->tstamp; - /* save originator for recvfrom() */ - op->rx_ifindex = skb->dev->ifindex; - /* op->flags/op->frames may be updated concurrently by bcm_rx_setup() */ spin_lock_bh(&op->bcm_rx_update_lock); @@ -836,6 +831,14 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data) traffic_flags |= RX_OWN; } + /* save rx timestamp and originator for recvfrom() under lock. + * For an op subscribed on all interfaces (ifindex == 0) + * bcm_rx_handler() can run concurrently on different CPUs so + * the CAN content and the meta data must be bundled correctly. + */ + op->rx_stamp = skb->tstamp; + op->rx_ifindex = skb->dev->ifindex; + if (op->flags & RX_FILTER_ID) { /* the easiest case */ bcm_rx_update_and_send(op, op->last_frames, rxframe, From 2f5976f54a04e9f18b25283036ac3136be453b17 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Tue, 14 Jul 2026 18:55:33 +0200 Subject: [PATCH 1040/1054] can: bcm: track a single source interface for ANYDEV timeout/throttle ops An ANYDEV rx op (ifindex == 0) with an active RX timeout and/or throttle timer has no defined semantics when matching frames arrive from several interfaces: bcm_rx_handler() can run concurrently for the same op on different CPUs, racing hrtimer_cancel()/ bcm_rx_starttimer() against bcm_rx_timeout_handler() and causing spurious RX_TIMEOUT notifications and last_frames corruption. The same concurrency lets throttled multiplex frames from different interfaces clobber the single rx_ifindex/rx_stamp fields shared by the op. Add op->if_detected to track the first interface that delivers a matching frame while a timeout/throttle timer is configured, and reject frames from any other interface for that op. The claim is decided in bcm_rx_handler() before hrtimer_cancel() touches op->timer, so a rejected frame can never disturb the claimed interface's watchdog. RTR-mode ops are excluded via RX_RTR_FRAME, independent of kt_ival1/kt_ival2, since those may briefly hold a stale value from an earlier non-RTR configuration. The claim is released in bcm_notify() on NETDEV_UNREGISTER and in bcm_rx_setup() when SETTIMER reconfigures the timer values. A (re-)claim is only possible on CAN devices in NETREG_REGISTERED dev->reg_state to cover the release in bcm_notify() where reg_state becomes NETREG_UNREGISTERING until synchronize_net(). Fixes: ffd980f976e7 ("[CAN]: Add broadcast manager (bcm) protocol") Reported-by: sashiko-bot@kernel.org Closes: https://lore.kernel.org/linux-can/20260709105031.1A39C1F000E9@smtp.kernel.org/ Signed-off-by: Oliver Hartkopp Link: https://patch.msgid.link/20260714-bcm_fixes-v15-11-562f7e3e42da@hartkopp.net Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde --- net/can/bcm.c | 49 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 5 deletions(-) diff --git a/net/can/bcm.c b/net/can/bcm.c index f213a0b37791..3d637a1e0ac1 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -117,6 +117,7 @@ struct bcm_op { struct hrtimer timer, thrtimer; ktime_t rx_stamp, kt_ival1, kt_ival2, kt_lastmsg; int rx_ifindex; + int if_detected; /* first received ifindex in ANYDEV rx_op mode */ int cfsiz; u32 count; u32 nframes; @@ -797,6 +798,33 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data) return; } + /* An ANYDEV op with an active RX timeout and/or throttle timer + * tracks a single source interface: claim the first interface that + * delivers a matching frame and reject frames from any other one, + * before hrtimer_cancel() below can touch op->timer - this avoids + * racing bcm_rx_timeout_handler() across concurrent interfaces. + * RX_RTR_FRAME ops are excluded, as kt_ival1/kt_ival2 may briefly + * hold a stale value from an earlier non-RTR configuration. + */ + if (!op->ifindex) { + spin_lock_bh(&op->bcm_rx_update_lock); + + if (!(op->flags & RX_RTR_FRAME) && + (op->kt_ival1 || op->kt_ival2)) { + /* don't claim to vanishing interface */ + if (!op->if_detected && + READ_ONCE(skb->dev->reg_state) == NETREG_REGISTERED) + op->if_detected = skb->dev->ifindex; + + if (op->if_detected != skb->dev->ifindex) { + spin_unlock_bh(&op->bcm_rx_update_lock); + return; + } + } + + spin_unlock_bh(&op->bcm_rx_update_lock); + } + /* disable timeout */ hrtimer_cancel(&op->timer); @@ -831,10 +859,9 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data) traffic_flags |= RX_OWN; } - /* save rx timestamp and originator for recvfrom() under lock. - * For an op subscribed on all interfaces (ifindex == 0) - * bcm_rx_handler() can run concurrently on different CPUs so - * the CAN content and the meta data must be bundled correctly. + /* save rx timestamp and originator for recvfrom() under lock: an + * ANYDEV op without an active timer can still run concurrently on + * different CPUs, so content and meta data must be bundled here. */ op->rx_stamp = skb->tstamp; op->rx_ifindex = skb->dev->ifindex; @@ -1369,6 +1396,7 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, op->kt_ival1 = bcm_timeval_to_ktime(msg_head->ival1); op->kt_ival2 = bcm_timeval_to_ktime(msg_head->ival2); op->kt_lastmsg = 0; + op->if_detected = 0; /* reclaim ifindex in ANYDEV mode */ } spin_unlock_bh(&op->bcm_rx_update_lock); @@ -1775,10 +1803,21 @@ static void bcm_notify(struct bcm_sock *bo, unsigned long msg, lock_sock(sk); /* rx_ops: remove device specific receive entries */ - list_for_each_entry(op, &bo->rx_ops, list) + list_for_each_entry(op, &bo->rx_ops, list) { if (op->rx_reg_dev == dev) bcm_rx_unreg(dev, op); + /* release an ANYDEV op's claim (see bcm_rx_handler()) + * on this now confirmed-gone interface. + */ + if (!op->ifindex) { + spin_lock_bh(&op->bcm_rx_update_lock); + if (op->if_detected == dev->ifindex) + op->if_detected = 0; + spin_unlock_bh(&op->bcm_rx_update_lock); + } + } + /* tx_ops: stop device specific cyclic transmissions on the * vanishing ifindex. Cancelling the timer is enough to stop * cyclic bcm_can_tx() calls as there is no re-arming. From 9b1a02e0d980ac6b0e36a90378f847062f81d7e4 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Sun, 12 Jul 2026 19:59:41 +0200 Subject: [PATCH 1041/1054] can: isotp: use unconditional synchronize_rcu() in isotp_release() isotp_notify() unregisters the (RCU) CAN filters via can_rx_unregister() and clears so->bound without waiting for a grace period. isotp_release() uses so->bound to decide whether it needs to call synchronize_rcu() before cancelling so->rxtimer, so when NETDEV_UNREGISTER runs first it skips that synchronize_rcu() and can cancel the timer while an in-flight isotp_rcv() is still executing and about to re-arm it via isotp_send_fc(), leading to a use-after-free timer callback on the freed socket. sakisho-bot remarked a problem with rtnl_lock held in isotp_notify(), therefore make isotp_release() always call synchronize_rcu() before cancelling the timers, regardless of so->bound. This still closes the original race (isotp_notify() clearing so->bound without waiting for in-flight isotp_rcv() callers before isotp_release() cancels the RX timer) without adding any RCU wait to the netdevice notifier path. Fixes: 14a4696bc311 ("can: isotp: isotp_release(): omit unintended hrtimer restart on socket release") Closes: https://lore.kernel.org/linux-can/20260707085210.6B6C01F000E9@smtp.kernel.org/ Reported-by: Nico Yip Signed-off-by: Oliver Hartkopp Link: https://patch.msgid.link/20260712-isotp-fixes-v10-1-793a1b1ce17f@hartkopp.net Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde --- net/can/isotp.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/net/can/isotp.c b/net/can/isotp.c index c48b4a818297..d30937345bcd 100644 --- a/net/can/isotp.c +++ b/net/can/isotp.c @@ -1237,11 +1237,18 @@ static int isotp_release(struct socket *sock) SINGLE_MASK(so->txid), isotp_rcv_echo, sk); dev_put(dev); - synchronize_rcu(); } } } + /* Always wait for a grace period before touching the timers below. + * A concurrent NETDEV_UNREGISTER may have already unregistered our + * filters and cleared so->bound in isotp_notify() without waiting + * for in-flight isotp_rcv() callers to finish, so this call must not + * be skipped just because so->bound is already 0 here. + */ + synchronize_rcu(); + hrtimer_cancel(&so->txfrtimer); hrtimer_cancel(&so->txtimer); hrtimer_cancel(&so->rxtimer); From 20bab8b88baac140ca3701116e1d486c7f51e311 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Sun, 12 Jul 2026 19:59:42 +0200 Subject: [PATCH 1042/1054] can: isotp: fix use-after-free race with concurrent NETDEV_UNREGISTER isotp_release() looked up the bound network device via dev_get_by_index() using the stored ifindex. During device unregistration the device is unlisted from the ifindex hash before the NETDEV_UNREGISTER notifier chain runs, so a concurrent isotp_release() could find no device, skip can_rx_unregister() entirely, and still proceed to free the socket. Since isotp_release() had already removed itself from the isotp notifier list at that point, isotp_notify() would never get a chance to clean up either, leaving a stale CAN filter that keeps pointing at the freed socket. Fix this the same way raw.c already does: hold a tracked reference to the bound net_device in the socket (so->dev/so->dev_tracker) from bind() onward instead of re-resolving it from the ifindex, and serialize bind()/release() with rtnl_lock() so that so->dev is always consistent with what the NETDEV_UNREGISTER notifier sees. so->dev stays valid regardless of ifindex-hash unlisting, and is only ever cleared by whichever of isotp_release()/isotp_notify() gets there first, so the filter is always removed exactly once. isotp_bind() now rejects a (re)bind with -EAGAIN while so->[tx|rx].state isn't ISOTP_IDLE yet, so a timer left running by a prior NETDEV_UNREGISTER can't act on a newly bound so->ifindex. Both checks share the same lock_sock() section, so there is no window in which a concurrent isotp_notify() clearing so->bound could be missed. Fixes: e057dd3fc20f ("can: add ISO 15765-2:2016 transport protocol") Reported-by: sashiko-bot@kernel.org Closes: https://lore.kernel.org/linux-can/20260707101420.47F261F000E9@smtp.kernel.org/ Signed-off-by: Oliver Hartkopp Link: https://patch.msgid.link/20260712-isotp-fixes-v10-2-793a1b1ce17f@hartkopp.net Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde --- net/can/isotp.c | 85 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 58 insertions(+), 27 deletions(-) diff --git a/net/can/isotp.c b/net/can/isotp.c index d30937345bcd..44c044eb83e1 100644 --- a/net/can/isotp.c +++ b/net/can/isotp.c @@ -152,6 +152,8 @@ struct isotp_sock { struct sock sk; int bound; int ifindex; + struct net_device *dev; + netdevice_tracker dev_tracker; canid_t txid; canid_t rxid; ktime_t tx_gap; @@ -978,6 +980,14 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) goto err_event_drop; } + /* so->bound is only checked once above - a wakeup may have + * unbound/rebound the socket meanwhile, so re-validate it + */ + if (!so->bound) { + err = -EADDRNOTAVAIL; + goto err_out_drop; + } + /* PDU size > default => try max_pdu_size */ if (size > so->tx.buflen && so->tx.buflen < max_pdu_size) { u8 *newbuf = kmalloc(max_pdu_size, GFP_KERNEL); @@ -1219,28 +1229,30 @@ static int isotp_release(struct socket *sock) list_del(&so->notifier); spin_unlock(&isotp_notifier_lock); + rtnl_lock(); lock_sock(sk); - /* remove current filters & unregister */ - if (so->bound) { - if (so->ifindex) { - struct net_device *dev; + /* remove current filters & unregister + * tracked reference so->dev is taken at bind() time with rtnl_lock + */ + if (so->bound && so->dev) { + if (isotp_register_rxid(so)) + can_rx_unregister(net, so->dev, so->rxid, + SINGLE_MASK(so->rxid), + isotp_rcv, sk); - dev = dev_get_by_index(net, so->ifindex); - if (dev) { - if (isotp_register_rxid(so)) - can_rx_unregister(net, dev, so->rxid, - SINGLE_MASK(so->rxid), - isotp_rcv, sk); - - can_rx_unregister(net, dev, so->txid, - SINGLE_MASK(so->txid), - isotp_rcv_echo, sk); - dev_put(dev); - } - } + can_rx_unregister(net, so->dev, so->txid, + SINGLE_MASK(so->txid), + isotp_rcv_echo, sk); + netdev_put(so->dev, &so->dev_tracker); } + so->ifindex = 0; + so->bound = 0; + so->dev = NULL; + + rtnl_unlock(); + /* Always wait for a grace period before touching the timers below. * A concurrent NETDEV_UNREGISTER may have already unregistered our * filters and cleared so->bound in isotp_notify() without waiting @@ -1253,9 +1265,6 @@ static int isotp_release(struct socket *sock) hrtimer_cancel(&so->txtimer); hrtimer_cancel(&so->rxtimer); - so->ifindex = 0; - so->bound = 0; - sock_orphan(sk); sock->sk = NULL; @@ -1310,6 +1319,7 @@ static int isotp_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int l if (!addr->can_ifindex) return -ENODEV; + rtnl_lock(); lock_sock(sk); if (so->bound) { @@ -1317,6 +1327,17 @@ static int isotp_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int l goto out; } + /* A transmission or reception that outlived a previous binding + * (unbound by NETDEV_UNREGISTER) may still be draining; the FC/echo + * and RX watchdog timers bound how long this takes. Checked together + * with so->bound in the same lock_sock() section above, so there is + * no window in which a concurrent isotp_notify() could be missed. + */ + if (so->tx.state != ISOTP_IDLE || so->rx.state != ISOTP_IDLE) { + err = -EAGAIN; + goto out; + } + /* ensure different CAN IDs when the rx_id is to be registered */ if (isotp_register_rxid(so) && rx_id == tx_id) { err = -EADDRNOTAVAIL; @@ -1329,14 +1350,12 @@ static int isotp_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int l goto out; } if (dev->type != ARPHRD_CAN) { - dev_put(dev); err = -ENODEV; - goto out; + goto out_put_dev; } if (READ_ONCE(dev->mtu) < so->ll.mtu) { - dev_put(dev); err = -EINVAL; - goto out; + goto out_put_dev; } if (!(dev->flags & IFF_UP)) notify_enetdown = 1; @@ -1354,16 +1373,25 @@ static int isotp_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int l can_rx_register(net, dev, tx_id, SINGLE_MASK(tx_id), isotp_rcv_echo, sk, "isotpe", sk); - dev_put(dev); - /* switch to new settings */ so->ifindex = ifindex; so->rxid = rx_id; so->txid = tx_id; so->bound = 1; + /* bind() ok -> hold a reference for so->dev so that isotp_release() + * can safely reach the device later, even if a concurrent + * NETDEV_UNREGISTER has already unlisted it by ifindex. + */ + so->dev = dev; + netdev_hold(so->dev, &so->dev_tracker, GFP_KERNEL); + +out_put_dev: + /* remove potential reference from dev_get_by_index() */ + dev_put(dev); out: release_sock(sk); + rtnl_unlock(); if (notify_enetdown) { sk->sk_err = ENETDOWN; @@ -1566,7 +1594,7 @@ static void isotp_notify(struct isotp_sock *so, unsigned long msg, if (!net_eq(dev_net(dev), sock_net(sk))) return; - if (so->ifindex != dev->ifindex) + if (so->dev != dev) return; switch (msg) { @@ -1582,10 +1610,12 @@ static void isotp_notify(struct isotp_sock *so, unsigned long msg, can_rx_unregister(dev_net(dev), dev, so->txid, SINGLE_MASK(so->txid), isotp_rcv_echo, sk); + netdev_put(so->dev, &so->dev_tracker); } so->ifindex = 0; so->bound = 0; + so->dev = NULL; release_sock(sk); sk->sk_err = ENODEV; @@ -1645,6 +1675,7 @@ static int isotp_init(struct sock *sk) so->ifindex = 0; so->bound = 0; + so->dev = NULL; so->opt.flags = CAN_ISOTP_DEFAULT_FLAGS; so->opt.ext_address = CAN_ISOTP_DEFAULT_EXT_ADDRESS; From cf070fe33bfbd1a4c21236078fadb35dd223a157 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Sun, 12 Jul 2026 19:59:43 +0200 Subject: [PATCH 1043/1054] can: isotp: serialize TX state transitions under so->rx_lock The TX state machine (so->tx.state) is driven from three contexts: sendmsg() claiming and progressing a transfer, the RX path consuming Flow Control/echo frames, and two hrtimers timing out a stalled transfer. Mixing a lock-free cmpxchg() claim in sendmsg() with hrtimer_cancel() calls made under so->rx_lock elsewhere left windows where a frame or timer callback could act on a state that had already moved on, corrupting an unrelated transfer. so->rx_lock now covers the full lifecycle of a TX claim: sendmsg() takes it to check so->tx.state is ISOTP_IDLE, switch it to ISOTP_SENDING, bump so->tx_gen and drain the previous transfer's timers - all as one critical section. isotp_rcv_fc()/isotp_rcv_cf() already run under this lock via isotp_rcv(), and isotp_rcv_echo() now takes it itself, so none of them can ever observe a transfer mid-claim. This also means a transfer can no longer be handed to sendmsg()'s cleanup paths (signal or send error) while another thread is concurrently claiming or finishing it, so those paths can cancel timers and reset the state unconditionally. isotp_release() claims the socket the same way, so a racing sendmsg() sees a consistent ISOTP_SHUTDOWN and skips arming its timer or sending. Only the hrtimer callbacks stay outside so->rx_lock, since they run under so->rx_lock's cancellation elsewhere and taking it themselves would deadlock. so->tx_gen lets them recognize whether the transfer they timed out is still the one currently active, so they don't report an error against a transfer that has since completed or been superseded. Fixes: e057dd3fc20f ("can: add ISO 15765-2:2016 transport protocol") Reported-by: sashiko-bot@kernel.org Closes: https://lore.kernel.org/linux-can/20260710142146.BDAE61F000E9@smtp.kernel.org/ Signed-off-by: Oliver Hartkopp Link: https://patch.msgid.link/20260712-isotp-fixes-v10-3-793a1b1ce17f@hartkopp.net Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde --- net/can/isotp.c | 202 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 160 insertions(+), 42 deletions(-) diff --git a/net/can/isotp.c b/net/can/isotp.c index 44c044eb83e1..54becaf6898f 100644 --- a/net/can/isotp.c +++ b/net/can/isotp.c @@ -158,7 +158,7 @@ struct isotp_sock { canid_t rxid; ktime_t tx_gap; ktime_t lastrxcf_tstamp; - struct hrtimer rxtimer, txtimer, txfrtimer; + struct hrtimer rxtimer, txtimer, txfrtimer, echotimer; struct can_isotp_options opt; struct can_isotp_fc_options rxfc, txfc; struct can_isotp_ll_options ll; @@ -166,6 +166,7 @@ struct isotp_sock { u32 force_tx_stmin; u32 force_rx_stmin; u32 cfecho; /* consecutive frame echo tag */ + u32 tx_gen; /* generation, bumped per new tx transfer */ struct tpcon rx, tx; struct list_head notifier; wait_queue_head_t wait; @@ -378,6 +379,15 @@ static int isotp_rcv_fc(struct isotp_sock *so, struct canfd_frame *cf, int ae) hrtimer_cancel(&so->txtimer); + /* isotp_tx_timeout() may have given up on this job while + * hrtimer_cancel() above waited for it to finish; so->rx_lock + * (held by our caller isotp_rcv()) rules out a concurrent claim, + * so a plain recheck is enough here. + */ + if (so->tx.state != ISOTP_WAIT_FC && + so->tx.state != ISOTP_WAIT_FIRST_FC) + return 1; + if ((cf->len < ae + FC_CONTENT_SZ) || ((so->opt.flags & ISOTP_CHECK_PADDING) && check_pad(so, cf, ae + FC_CONTENT_SZ, so->opt.rxpad_content))) { @@ -424,7 +434,7 @@ static int isotp_rcv_fc(struct isotp_sock *so, struct canfd_frame *cf, int ae) so->tx.bs = 0; so->tx.state = ISOTP_SENDING; /* send CF frame and enable echo timeout handling */ - hrtimer_start(&so->txtimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0), + hrtimer_start(&so->echotimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0), HRTIMER_MODE_REL_SOFT); isotp_send_cframe(so); break; @@ -577,6 +587,14 @@ static int isotp_rcv_cf(struct sock *sk, struct canfd_frame *cf, int ae, hrtimer_cancel(&so->rxtimer); + /* isotp_rx_timer_handler() may have raced us for so->rx.state + * while hrtimer_cancel() above waited for it to finish, already + * reporting ETIMEDOUT and resetting the reception; don't process + * this CF into a reassembly that has already been given up on. + */ + if (so->rx.state != ISOTP_WAIT_DATA) + return 1; + /* CFs are never longer than the FF */ if (cf->len > so->rx.ll_dl) return 1; @@ -872,20 +890,36 @@ static void isotp_rcv_echo(struct sk_buff *skb, void *data) struct canfd_frame *cf = (struct canfd_frame *)skb->data; /* only handle my own local echo CF/SF skb's (no FF!) */ - if (skb->sk != sk || so->cfecho != *(u32 *)cf->data) + if (skb->sk != sk) return; + /* unlike isotp_rcv_fc()/isotp_rcv_cf(), not already under so->rx_lock + * (no isotp_rcv() caller here), so take it ourselves + */ + spin_lock(&so->rx_lock); + + /* so->cfecho may since belong to a new transfer; recheck under lock */ + if (so->cfecho != *(u32 *)cf->data) + goto out_unlock; + /* cancel local echo timeout */ - hrtimer_cancel(&so->txtimer); + hrtimer_cancel(&so->echotimer); /* local echo skb with consecutive frame has been consumed */ so->cfecho = 0; + /* claiming a transfer also takes so->rx_lock, so a plain recheck + * is enough: so->tx.state can't have flipped to ISOTP_SENDING for + * a new claim while we're still in here + */ + if (so->tx.state != ISOTP_SENDING) + goto out_unlock; + if (so->tx.idx >= so->tx.len) { /* we are done */ so->tx.state = ISOTP_IDLE; wake_up_interruptible(&so->wait); - return; + goto out_unlock; } if (so->txfc.bs && so->tx.bs >= so->txfc.bs) { @@ -893,53 +927,83 @@ static void isotp_rcv_echo(struct sk_buff *skb, void *data) so->tx.state = ISOTP_WAIT_FC; hrtimer_start(&so->txtimer, ktime_set(ISOTP_FC_TIMEOUT, 0), HRTIMER_MODE_REL_SOFT); - return; + goto out_unlock; } /* no gap between data frames needed => use burst mode */ if (!so->tx_gap) { /* enable echo timeout handling */ - hrtimer_start(&so->txtimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0), + hrtimer_start(&so->echotimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0), HRTIMER_MODE_REL_SOFT); isotp_send_cframe(so); - return; + goto out_unlock; } /* start timer to send next consecutive frame with correct delay */ hrtimer_start(&so->txfrtimer, so->tx_gap, HRTIMER_MODE_REL_SOFT); + +out_unlock: + spin_unlock(&so->rx_lock); } -static enum hrtimer_restart isotp_tx_timer_handler(struct hrtimer *hrtimer) +/* shared by so->txtimer's and so->echotimer's callbacks. Both timers get + * cancelled under so->rx_lock elsewhere, so this must stay lock-free to + * avoid deadlocking with that; uses so->tx_gen instead to avoid tainting + * a new transfer with an error from the one that just timed out. + */ +static enum hrtimer_restart isotp_tx_timeout(struct isotp_sock *so) { - struct isotp_sock *so = container_of(hrtimer, struct isotp_sock, - txtimer); struct sock *sk = &so->sk; + u32 gen = READ_ONCE(so->tx_gen); + u32 old_state = READ_ONCE(so->tx.state); /* don't handle timeouts in IDLE or SHUTDOWN state */ - if (so->tx.state == ISOTP_IDLE || so->tx.state == ISOTP_SHUTDOWN) + if (old_state == ISOTP_IDLE || old_state == ISOTP_SHUTDOWN) + return HRTIMER_NORESTART; + + /* only claim the timeout if the state is still unchanged */ + if (cmpxchg(&so->tx.state, old_state, ISOTP_IDLE) != old_state) return HRTIMER_NORESTART; /* we did not get any flow control or echo frame in time */ - /* report 'communication error on send' */ - sk->sk_err = ECOMM; - if (!sock_flag(sk, SOCK_DEAD)) - sk_error_report(sk); + if (READ_ONCE(so->tx_gen) == gen) { + /* report 'communication error on send' */ + sk->sk_err = ECOMM; + if (!sock_flag(sk, SOCK_DEAD)) + sk_error_report(sk); + } - /* reset tx state */ - so->tx.state = ISOTP_IDLE; wake_up_interruptible(&so->wait); return HRTIMER_NORESTART; } +/* so->txtimer: fires when a Flow Control frame does not arrive in time */ +static enum hrtimer_restart isotp_tx_timer_handler(struct hrtimer *hrtimer) +{ + struct isotp_sock *so = container_of(hrtimer, struct isotp_sock, + txtimer); + + return isotp_tx_timeout(so); +} + +/* so->echotimer: fires when a sent CF/SF's local echo does not arrive */ +static enum hrtimer_restart isotp_echo_timer_handler(struct hrtimer *hrtimer) +{ + struct isotp_sock *so = container_of(hrtimer, struct isotp_sock, + echotimer); + + return isotp_tx_timeout(so); +} + static enum hrtimer_restart isotp_txfr_timer_handler(struct hrtimer *hrtimer) { struct isotp_sock *so = container_of(hrtimer, struct isotp_sock, txfrtimer); /* start echo timeout handling and cover below protocol error */ - hrtimer_start(&so->txtimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0), + hrtimer_start(&so->echotimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0), HRTIMER_MODE_REL_SOFT); /* cfecho should be consumed by isotp_rcv_echo() here */ @@ -960,13 +1024,24 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) int ae = (so->opt.flags & CAN_ISOTP_EXTEND_ADDR) ? 1 : 0; int wait_tx_done = (so->opt.flags & CAN_ISOTP_WAIT_TX_DONE) ? 1 : 0; s64 hrtimer_sec = ISOTP_ECHO_TIMEOUT; + struct hrtimer *tx_hrt = &so->echotimer; + u32 new_state = ISOTP_SENDING; int off; int err; if (!so->bound || so->tx.state == ISOTP_SHUTDOWN) return -EADDRNOTAVAIL; - while (cmpxchg(&so->tx.state, ISOTP_IDLE, ISOTP_SENDING) != ISOTP_IDLE) { + /* claim the socket under so->rx_lock: this serializes the claim + * with the RX path and with sendmsg()'s own error paths below, so + * none of them can ever see a transfer mid-claim + */ + for (;;) { + spin_lock_bh(&so->rx_lock); + if (READ_ONCE(so->tx.state) == ISOTP_IDLE) + break; + spin_unlock_bh(&so->rx_lock); + /* we do not support multiple buffers - for now */ if (msg->msg_flags & MSG_DONTWAIT) return -EAGAIN; @@ -975,11 +1050,23 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) return -EADDRNOTAVAIL; /* wait for complete transmission of current pdu */ - err = wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE); + err = wait_event_interruptible(so->wait, + so->tx.state == ISOTP_IDLE); if (err) - goto err_event_drop; + return err; } + /* new transfer: bump so->tx_gen and drain the old one's timers, + * still under the so->rx_lock we just claimed the socket with + */ + WRITE_ONCE(so->tx.state, ISOTP_SENDING); + WRITE_ONCE(so->tx_gen, READ_ONCE(so->tx_gen) + 1); + hrtimer_cancel(&so->txtimer); + hrtimer_cancel(&so->echotimer); + hrtimer_cancel(&so->txfrtimer); + so->cfecho = 0; + spin_unlock_bh(&so->rx_lock); + /* so->bound is only checked once above - a wakeup may have * unbound/rebound the socket meanwhile, so re-validate it */ @@ -1096,18 +1183,33 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) so->cfecho = *(u32 *)cf->data; } else { /* standard flow control check */ - so->tx.state = ISOTP_WAIT_FIRST_FC; + new_state = ISOTP_WAIT_FIRST_FC; /* start timeout for FC */ hrtimer_sec = ISOTP_FC_TIMEOUT; + tx_hrt = &so->txtimer; /* no CF echo tag for isotp_rcv_echo() (FF-mode) */ so->cfecho = 0; } } - hrtimer_start(&so->txtimer, ktime_set(hrtimer_sec, 0), + spin_lock_bh(&so->rx_lock); + if (so->tx.state == ISOTP_SHUTDOWN) { + /* isotp_release() has since taken over and already drained + * our timers - don't send into a socket that's going away + */ + spin_unlock_bh(&so->rx_lock); + kfree_skb(skb); + dev_put(dev); + wake_up_interruptible(&so->wait); + return -EADDRNOTAVAIL; + } + /* WAIT_FIRST_FC for standard FF, else stays ISOTP_SENDING */ + so->tx.state = new_state; + hrtimer_start(tx_hrt, ktime_set(hrtimer_sec, 0), HRTIMER_MODE_REL_SOFT); + spin_unlock_bh(&so->rx_lock); /* send the first or only CAN frame */ cf->flags = so->ll.tx_flags; @@ -1120,13 +1222,10 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) pr_notice_once("can-isotp: %s: can_send_ret %pe\n", __func__, ERR_PTR(err)); + spin_lock_bh(&so->rx_lock); /* no transmission -> no timeout monitoring */ - hrtimer_cancel(&so->txtimer); - - /* reset consecutive frame echo tag */ - so->cfecho = 0; - - goto err_out_drop; + hrtimer_cancel(tx_hrt); + goto err_out_drop_locked; } if (wait_tx_done) { @@ -1142,14 +1241,21 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) return size; +err_out_drop: + /* claimed but nothing sent yet - no timer to cancel */ + spin_lock_bh(&so->rx_lock); + goto err_out_drop_locked; err_event_drop: - /* got signal: force tx state machine to be idle */ - so->tx.state = ISOTP_IDLE; + /* interrupted waiting on our own transfer - drain its timers */ + spin_lock_bh(&so->rx_lock); hrtimer_cancel(&so->txfrtimer); hrtimer_cancel(&so->txtimer); -err_out_drop: - /* drop this PDU and unlock a potential wait queue */ + hrtimer_cancel(&so->echotimer); +err_out_drop_locked: + /* release the claim; so->rx_lock still held from above */ + so->cfecho = 0; so->tx.state = ISOTP_IDLE; + spin_unlock_bh(&so->rx_lock); wake_up_interruptible(&so->wait); return err; @@ -1211,13 +1317,20 @@ static int isotp_release(struct socket *sock) so = isotp_sk(sk); net = sock_net(sk); - /* wait for complete transmission of current pdu */ - while (wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE) == 0 && - cmpxchg(&so->tx.state, ISOTP_IDLE, ISOTP_SHUTDOWN) != ISOTP_IDLE) + /* best-effort: wait for a running pdu to finish, but don't block on + * it forever - give up after the first signal + */ + while (so->tx.state != ISOTP_IDLE && + wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE) == 0) ; - /* force state machines to be idle also when a signal occurred */ + /* claim the socket under so->rx_lock like sendmsg() does, so its + * claim can't race the forced ISOTP_SHUTDOWN below; force it + * unconditionally, even when a signal cut the wait above short + */ + spin_lock_bh(&so->rx_lock); so->tx.state = ISOTP_SHUTDOWN; + spin_unlock_bh(&so->rx_lock); so->rx.state = ISOTP_IDLE; spin_lock(&isotp_notifier_lock); @@ -1263,6 +1376,7 @@ static int isotp_release(struct socket *sock) hrtimer_cancel(&so->txfrtimer); hrtimer_cancel(&so->txtimer); + hrtimer_cancel(&so->echotimer); hrtimer_cancel(&so->rxtimer); sock_orphan(sk); @@ -1702,10 +1816,14 @@ static int isotp_init(struct sock *sk) so->rx.buflen = ARRAY_SIZE(so->rx.sbuf); so->tx.buflen = ARRAY_SIZE(so->tx.sbuf); - hrtimer_setup(&so->rxtimer, isotp_rx_timer_handler, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT); - hrtimer_setup(&so->txtimer, isotp_tx_timer_handler, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT); - hrtimer_setup(&so->txfrtimer, isotp_txfr_timer_handler, CLOCK_MONOTONIC, - HRTIMER_MODE_REL_SOFT); + hrtimer_setup(&so->rxtimer, isotp_rx_timer_handler, + CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT); + hrtimer_setup(&so->txtimer, isotp_tx_timer_handler, + CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT); + hrtimer_setup(&so->echotimer, isotp_echo_timer_handler, + CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT); + hrtimer_setup(&so->txfrtimer, isotp_txfr_timer_handler, + CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT); init_waitqueue_head(&so->wait); spin_lock_init(&so->rx_lock); From 3917b1012ee2fef6da16d7450d4267dcb3e93363 Mon Sep 17 00:00:00 2001 From: Ihor Solodrai Date: Mon, 13 Jul 2026 16:52:22 -0700 Subject: [PATCH 1044/1054] bpf: Fix tracing of kfuncs with implicit args A kfunc marked with KF_IMPLICIT_ARGS flag takes implicit arguments (such as bpf_prog_aux) that the verifier injects at load time. resolve_btfids strips those from the kfunc's BTF-visible prototype and keeps the real kernel ABI in a counterpart _impl prototype [1]. fentry/fexit/fmod_ret/fsession programs may attach to the BPF kernel functions, including those with implicit args. However bpf_check_attach_target() and bpf_check_attach_btf_id_multi() extract the struct btf_func_model from the wrong BTF prototype of the kfunc. The btf_func_model is later read to construct the trampoline, which then causes the injected implicit argument to be clobbered and the kfunc dereferencing garbage. Add btf_attach_func_proto() to resolve the real ABI prototype of the kfunc the way the call site does: by looking up the _impl prototype for a KF_IMPLICIT_ARGS kfunc. Use it at both attach-target model construction sites. To enable this, make two supporting changes: * pass bpf_verifier_log instead of bpf_verifier_env to find_kfunc_impl_proto(), so it can be reused from the attach path * add btf_kfunc_check_flag() to test a flag across all of a kfunc's hook sets, because a program attaching to a kfunc is not in the kfunc's call-set KF_IMPLICIT_ARGS must be consistent across the sets, so btf_kfunc_check_flag() returns -EINVAL on inconsistency. btf_kfunc_check_flag() reads the kfunc's flags from the target's kfunc_set_tab. For a module BTF that table is stable only after the module is live, so take a module reference around the read, mirroring how the kfunc call path gates the same lookup with btf_try_get_module(). The remaining call sites of btf_distill_func_proto() are safe as is. The BPF_TRACE_ITER case distills a registered iterator's prototype, and bpf_struct_ops_desc_init() distills the function-pointer members of a struct_ops type. Neither is a kfunc, and so can't have implicit arguments. [1] https://lore.kernel.org/all/20260120222638.3976562-1-ihor.solodrai@linux.dev/ Fixes: 64e1360524b9 ("bpf: Verifier support for KF_IMPLICIT_ARGS") Reported-by: Tejun Heo Signed-off-by: Ihor Solodrai Link: https://github.com/sched-ext/scx/issues/3687#issuecomment-4906694106 Link: https://patch.msgid.link/20260713235223.1639022-2-ihor.solodrai@linux.dev Signed-off-by: Eduard Zingerman --- include/linux/btf.h | 1 + kernel/bpf/btf.c | 29 +++++++++++++++++++ kernel/bpf/verifier.c | 66 ++++++++++++++++++++++++++++++++++--------- 3 files changed, 83 insertions(+), 13 deletions(-) diff --git a/include/linux/btf.h b/include/linux/btf.h index 240401d9b25b..c09b7994de4e 100644 --- a/include/linux/btf.h +++ b/include/linux/btf.h @@ -578,6 +578,7 @@ const char *btf_str_by_offset(const struct btf *btf, u32 offset); struct btf *btf_parse_vmlinux(void); struct btf *bpf_prog_get_target_btf(const struct bpf_prog *prog); u32 *btf_kfunc_flags(const struct btf *btf, u32 kfunc_btf_id, const struct bpf_prog *prog); +int btf_kfunc_check_flag(const struct btf *btf, u32 kfunc_btf_id, u32 flag); bool btf_kfunc_is_allowed(const struct btf *btf, u32 kfunc_btf_id, const struct bpf_prog *prog); u32 *btf_kfunc_is_modify_return(const struct btf *btf, u32 kfunc_btf_id, const struct bpf_prog *prog); diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index 64572f85edc8..c4673a54c4ba 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -9114,6 +9114,35 @@ u32 *btf_kfunc_flags(const struct btf *btf, u32 kfunc_btf_id, const struct bpf_p return btf_kfunc_id_set_contains(btf, hook, kfunc_btf_id); } +/* + * Check a single KF_* @flag on a kfunc across all of its hook sets. + * Returns: + * * 1 if @flag is set + * * 0 if @flag is not set + * * -EINVAL if @flag is set inconsistently across the sets + * * -ENOENT if kfunc_btf_id is not a registered kfunc + */ +int btf_kfunc_check_flag(const struct btf *btf, u32 kfunc_btf_id, u32 flag) +{ + enum btf_kfunc_hook hook; + int res = -ENOENT; + bool is_set; + u32 *flags; + + for (hook = 0; hook < BTF_KFUNC_HOOK_MAX; hook++) { + flags = btf_kfunc_id_set_contains(btf, hook, kfunc_btf_id); + if (!flags) + continue; + is_set = *flags & flag; + if (res < 0) + res = is_set; + else if (res != is_set) + return -EINVAL; + } + + return res; +} + u32 *btf_kfunc_is_modify_return(const struct btf *btf, u32 kfunc_btf_id, const struct bpf_prog *prog) { diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 9f1333676365..99444eae917e 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -2584,24 +2584,25 @@ static struct btf *find_kfunc_desc_btf(struct bpf_verifier_env *env, s16 offset) #define KF_IMPL_SUFFIX "_impl" -static const struct btf_type *find_kfunc_impl_proto(struct bpf_verifier_env *env, +static const struct btf_type *find_kfunc_impl_proto(struct bpf_verifier_log *log, struct btf *btf, const char *func_name) { - char *buf = env->tmp_str_buf; const struct btf_type *func; + char buf[KSYM_NAME_LEN]; s32 impl_id; int len; - len = snprintf(buf, TMP_STR_BUF_LEN, "%s%s", func_name, KF_IMPL_SUFFIX); - if (len < 0 || len >= TMP_STR_BUF_LEN) { - verbose(env, "function name %s%s is too long\n", func_name, KF_IMPL_SUFFIX); + len = snprintf(buf, sizeof(buf), "%s%s", func_name, KF_IMPL_SUFFIX); + if (len < 0 || len >= sizeof(buf)) { + bpf_log(log, "function name %s%s is too long\n", + func_name, KF_IMPL_SUFFIX); return NULL; } impl_id = btf_find_by_name_kind(btf, buf, BTF_KIND_FUNC); if (impl_id <= 0) { - verbose(env, "cannot find function %s in BTF\n", buf); + bpf_log(log, "cannot find function %s in BTF\n", buf); return NULL; } @@ -2653,7 +2654,7 @@ static int fetch_kfunc_meta(struct bpf_verifier_env *env, * can be found through the counterpart _impl kfunc. */ if (kfunc_flags && (*kfunc_flags & KF_IMPLICIT_ARGS)) - func_proto = find_kfunc_impl_proto(env, btf, func_name); + func_proto = find_kfunc_impl_proto(&env->log, btf, func_name); else func_proto = btf_type_by_id(btf, func->type); @@ -18880,6 +18881,47 @@ static int btf_id_allow_sleepable(u32 btf_id, unsigned long addr, const struct b return -EINVAL; } +/* + * Resolve the prototype describing a trace target's real ABI. A + * KF_IMPLICIT_ARGS kfunc has its injected args stripped from the public + * prototype, so use the _impl prototype; other targets use their own. + */ +static const struct btf_type * +btf_attach_func_proto(struct bpf_verifier_log *log, struct btf *btf, u32 func_id) +{ + const struct btf_type *func; + struct module *mod = NULL; + const char *name; + int implicit; + + func = btf_type_by_id(btf, func_id); + if (!func || !btf_type_is_func(func)) + return NULL; + name = btf_name_by_offset(btf, func->name_off); + + /* + * btf_kfunc_check_flag() reads kfunc_set_tab, which for a module is + * stable only once it is live; hold a module ref across the read to + * exclude a concurrent module load. + */ + if (btf_is_module(btf)) { + mod = btf_try_get_module(btf); + if (!mod) + return NULL; + } + implicit = btf_kfunc_check_flag(btf, func_id, KF_IMPLICIT_ARGS); + module_put(mod); + + if (implicit == -EINVAL) { + bpf_log(log, "kfunc %s has inconsistent KF_IMPLICIT_ARGS\n", name); + return NULL; + } + if (implicit > 0) + return find_kfunc_impl_proto(log, btf, name); + + return btf_type_by_id(btf, func->type); +} + int bpf_check_attach_target(struct bpf_verifier_log *log, const struct bpf_prog *prog, const struct bpf_prog *tgt_prog, @@ -19128,8 +19170,8 @@ int bpf_check_attach_target(struct bpf_verifier_log *log, if (prog_extension && btf_check_type_match(log, prog, btf, t)) return -EINVAL; - t = btf_type_by_id(btf, t->type); - if (!btf_type_is_func_proto(t)) + t = btf_attach_func_proto(log, btf, btf_id); + if (!t || !btf_type_is_func_proto(t)) return -EINVAL; if ((prog->aux->saved_dst_prog_type || prog->aux->saved_dst_attach_type) && @@ -19412,10 +19454,8 @@ int bpf_check_attach_btf_id_multi(struct btf *btf, struct bpf_prog *prog, u32 bt tname = btf_name_by_offset(btf, t->name_off); if (!tname) return -EINVAL; - if (!btf_type_is_func(t)) - return -EINVAL; - t = btf_type_by_id(btf, t->type); - if (!btf_type_is_func_proto(t)) + t = btf_attach_func_proto(NULL, btf, btf_id); + if (!t || !btf_type_is_func_proto(t)) return -EINVAL; err = btf_distill_func_proto(NULL, btf, t, tname, &tgt_info->fmodel); if (err < 0) From 0af15f3057a60a038864d47fa4b54d1c3c3c654c Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Mon, 13 Jul 2026 16:52:23 -0700 Subject: [PATCH 1045/1054] selftests/bpf: Cover tracing implicit kfunc args KF_IMPLICIT_ARGS kfuncs have a BPF-call prototype and a real kernel target prototype. Add a tracing selftest that attaches fentry and fexit programs to bpf_kfunc_implicit_arg(), runs a syscall BPF program that calls it, and checks that the tracing context exposes both the explicit argument and the implicit prog aux pointer. Co-developed-by: Ihor Solodrai Signed-off-by: Kumar Kartikeya Dwivedi Signed-off-by: Ihor Solodrai Link: https://patch.msgid.link/20260713235223.1639022-3-ihor.solodrai@linux.dev Signed-off-by: Eduard Zingerman --- .../prog_tests/kfunc_implicit_args_tracing.c | 36 +++++++++ .../bpf/progs/kfunc_implicit_args_tracing.c | 77 +++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 tools/testing/selftests/bpf/prog_tests/kfunc_implicit_args_tracing.c create mode 100644 tools/testing/selftests/bpf/progs/kfunc_implicit_args_tracing.c diff --git a/tools/testing/selftests/bpf/prog_tests/kfunc_implicit_args_tracing.c b/tools/testing/selftests/bpf/prog_tests/kfunc_implicit_args_tracing.c new file mode 100644 index 000000000000..61cc5aaba025 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/kfunc_implicit_args_tracing.c @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */ + +#include +#include "kfunc_implicit_args_tracing.skel.h" + +void test_kfunc_implicit_args_tracing(void) +{ + struct kfunc_implicit_args_tracing *skel; + LIBBPF_OPTS(bpf_test_run_opts, topts); + int err, fd; + + skel = kfunc_implicit_args_tracing__open_and_load(); + if (!ASSERT_OK_PTR(skel, "open_and_load")) + return; + + err = kfunc_implicit_args_tracing__attach(skel); + if (!ASSERT_OK(err, "attach")) + goto cleanup; + + fd = bpf_program__fd(skel->progs.trigger_implicit_arg); + err = bpf_prog_test_run_opts(fd, &topts); + if (!ASSERT_OK(err, "test_run")) + goto cleanup; + + ASSERT_EQ(topts.retval, 5, "kfunc_retval"); + ASSERT_EQ(skel->bss->fentry_arg_cnt, 2, "fentry_arg_cnt"); + ASSERT_NEQ(skel->bss->fentry_aux_arg, 0, "fentry_aux_arg"); + ASSERT_EQ(skel->bss->fentry_result, 1, "fentry_result"); + ASSERT_EQ(skel->bss->fexit_arg_cnt, 2, "fexit_arg_cnt"); + ASSERT_NEQ(skel->bss->fexit_aux_arg, 0, "fexit_aux_arg"); + ASSERT_EQ(skel->bss->fexit_result, 1, "fexit_result"); + +cleanup: + kfunc_implicit_args_tracing__destroy(skel); +} diff --git a/tools/testing/selftests/bpf/progs/kfunc_implicit_args_tracing.c b/tools/testing/selftests/bpf/progs/kfunc_implicit_args_tracing.c new file mode 100644 index 000000000000..995f8b8b5b9e --- /dev/null +++ b/tools/testing/selftests/bpf/progs/kfunc_implicit_args_tracing.c @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */ + +#include +#include +#include +#include + +extern int bpf_kfunc_implicit_arg(int a) __weak __ksym; + +char _license[] SEC("license") = "GPL"; + +/* Shared arg checks; reports arg count and aux, returns 1 on success. */ +static __always_inline __u64 +check_implicit_args(void *ctx, __u64 *arg_cnt, __u64 *aux_arg) +{ + __u64 a = 0, aux = 0, z = 0; + __u64 result; + __s64 err; + + *arg_cnt = bpf_get_func_arg_cnt(ctx); + result = *arg_cnt == 2; + + err = bpf_get_func_arg(ctx, 0, &a); + result &= err == 0 && (int)a == 5; + + err = bpf_get_func_arg(ctx, 1, &aux); + *aux_arg = aux; + result &= err == 0 && aux != 0; + + err = bpf_get_func_arg(ctx, 2, &z); + result &= err == -EINVAL; + + return result; +} + +__u64 fentry_result; +__u64 fentry_arg_cnt; +__u64 fentry_aux_arg; + +SEC("fentry/bpf_kfunc_implicit_arg") +int BPF_PROG(trace_implicit_arg_fentry) +{ + __u64 ret = 0; + __s64 err; + + fentry_result = check_implicit_args(ctx, &fentry_arg_cnt, &fentry_aux_arg); + + err = bpf_get_func_ret(ctx, &ret); + fentry_result &= err == -EOPNOTSUPP; + + return 0; +} + +__u64 fexit_result; +__u64 fexit_arg_cnt; +__u64 fexit_aux_arg; + +SEC("fexit/bpf_kfunc_implicit_arg") +int BPF_PROG(trace_implicit_arg_fexit) +{ + __u64 ret = 0; + __s64 err; + + fexit_result = check_implicit_args(ctx, &fexit_arg_cnt, &fexit_aux_arg); + + err = bpf_get_func_ret(ctx, &ret); + fexit_result &= err == 0 && ret == 5; + + return 0; +} + +SEC("syscall") +int trigger_implicit_arg(void *ctx) +{ + return bpf_kfunc_implicit_arg(5); +} From df6134b527a88b3e65ba6ae5073664af091d5fd2 Mon Sep 17 00:00:00 2001 From: Zhiping Zhang Date: Thu, 2 Jul 2026 15:24:58 -0700 Subject: [PATCH 1046/1054] net/mlx5: free mlx5_st_idx_data on final dealloc Workloads that repeatedly allocate and release mkeys carrying TPH steering-tag hints (e.g. churning RDMA MRs) leak one struct mlx5_st_idx_data per cycle; kmemleak flags it as unreferenced and the kmalloc slab grows over time. When the last reference to an ST table entry is dropped, mlx5_st_dealloc_index() removed the entry from idx_xa but the backing mlx5_st_idx_data allocation was never freed. Free idx_data after the xa_erase() so the lifetime of the bookkeeping struct matches the lifetime of the ST entry it tracks. Cc: stable@vger.kernel.org Fixes: 888a7776f4fb ("net/mlx5: Add support for device steering tag") Reviewed-by: Michael Gur Signed-off-by: Zhiping Zhang Reviewed-by: Leon Romanovsky Reviewed-by: Tariq Toukan Link: https://patch.msgid.link/20260702222507.1234467-1-zhipingz@meta.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/mellanox/mlx5/core/lib/st.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/st.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/st.c index 997be91f0a13..7cedc348790d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/st.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/st.c @@ -175,6 +175,7 @@ int mlx5_st_dealloc_index(struct mlx5_core_dev *dev, u16 st_index) if (refcount_dec_and_test(&idx_data->usecount)) { xa_erase(&st->idx_xa, st_index); + kfree(idx_data); /* We leave PCI config space as was before, no mkey will refer to it */ } From 2c1931a81122c3cdc4c89448fe0442c69e21c0d5 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 10 Jul 2026 18:13:17 +0000 Subject: [PATCH 1047/1054] tcp: fix TIME_WAIT socket reference leak on PSP policy failure Release the TIME_WAIT socket reference and jump to discard_it upon PSP policy failure in both IPv4 and IPv6 receive paths. This prevents a memory leak of tcp_tw_bucket structures. Fixes: 659a2899a57d ("tcp: add datapath logic for PSP with inline key exchange") Signed-off-by: Eric Dumazet Reviewed-by: Kuniyuki Iwashima Reviewed-by: Daniel Zahka Link: https://patch.msgid.link/20260710181317.4060230-1-edumazet@google.com Signed-off-by: Paolo Abeni --- net/ipv4/tcp_ipv4.c | 6 ++++-- net/ipv6/tcp_ipv6.c | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 209ef7522508..4a46da375043 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -2318,8 +2318,10 @@ int tcp_v4_rcv(struct sk_buff *skb) } drop_reason = psp_twsk_rx_policy_check(inet_twsk(sk), skb); - if (drop_reason) - break; + if (drop_reason) { + inet_twsk_put(inet_twsk(sk)); + goto discard_it; + } } /* to ACK */ fallthrough; diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index ebe161d72fbd..522ba45ce9b7 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1977,8 +1977,10 @@ INDIRECT_CALLABLE_SCOPE int tcp_v6_rcv(struct sk_buff *skb) } drop_reason = psp_twsk_rx_policy_check(inet_twsk(sk), skb); - if (drop_reason) - break; + if (drop_reason) { + inet_twsk_put(inet_twsk(sk)); + goto discard_it; + } } /* to ACK */ fallthrough; From d2e914a4a0d0f753dbae830264850d044026167c Mon Sep 17 00:00:00 2001 From: Ivan Vecera Date: Fri, 10 Jul 2026 21:36:25 +0200 Subject: [PATCH 1048/1054] dpll: fix NULL pointer dereference in dpll_msg_add_pin_ref_sync() When a dpll_pin is shared across multiple dpll_device instances and those devices are being unregistered (e.g. during driver module removal), a NULL pointer dereference can occur in dpll_msg_add_pin_ref_sync(). This happens under the following conditions: - A pin is registered with two or more dpll devices (dpll_A, dpll_B) - The pin has ref_sync pairs with other pins - During unregistration of dpll_A's pins, a ref_sync partner pin is unregistered first, removing it from dpll_A->pin_refs - But since the partner pin is still registered with dpll_B, its dpll_refs is not empty, so dpll_pin_ref_sync_pair_del() does NOT run and the partner stays in the pin's ref_sync_pins xarray - When the pin itself is then unregistered from dpll_A, the delete notification calls dpll_msg_add_pin_ref_sync() which finds the partner in ref_sync_pins, passes dpll_pin_available() (partner is still registered with dpll_B), but dpll_pin_on_dpll_priv(dpll_A, partner) returns NULL because partner was already removed from dpll_A->pin_refs - The NULL priv pointer is passed to the driver's ref_sync_get callback, which dereferences it BUG: kernel NULL pointer dereference, address: 0000000000000034 Oops: Oops: 0000 [#1] SMP NOPTI RIP: 0010:zl3073x_dpll_input_pin_ref_sync_get+0x73/0x80 [zl3073x] Call Trace: dpll_msg_add_pin_ref_sync+0xb8/0x200 dpll_cmd_pin_get_one+0x3b6/0x4b0 dpll_pin_event_send+0x72/0x140 __dpll_pin_unregister+0x5a/0x2b0 dpll_pin_unregister+0x49/0x70 Fix this by skipping ref_sync pins whose priv pointer cannot be resolved for the current dpll device. Fixes: 58256a26bfb3 ("dpll: add reference sync get/set") Signed-off-by: Ivan Vecera Reviewed-by: Vadim Fedorenko Reviewed-by: Jiri Pirko Link: https://patch.msgid.link/20260710193625.1378822-1-ivecera@redhat.com Signed-off-by: Paolo Abeni --- drivers/dpll/dpll_netlink.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c index bf729cde796a..5703667593a7 100644 --- a/drivers/dpll/dpll_netlink.c +++ b/drivers/dpll/dpll_netlink.c @@ -567,6 +567,9 @@ dpll_msg_add_pin_ref_sync(struct sk_buff *msg, struct dpll_pin *pin, if (!dpll_pin_available(ref_sync_pin)) continue; ref_sync_pin_priv = dpll_pin_on_dpll_priv(dpll, ref_sync_pin); + /* Pin may have been unregistered from this dpll already */ + if (!ref_sync_pin_priv) + continue; if (WARN_ON(!ops->ref_sync_get)) return -EOPNOTSUPP; ret = ops->ref_sync_get(pin, pin_priv, ref_sync_pin, From f1f5c8a3955f8fda3f84ed883ac8daa1847e724c Mon Sep 17 00:00:00 2001 From: Jamal Hadi Salim Date: Sat, 11 Jul 2026 11:05:37 -0400 Subject: [PATCH 1049/1054] net/sched: act_tunnel_key: Defer dst_release to RCU callback Fix a race-condition use-after-free in tunnel_key_release_params(). The function releases the metadata_dst of the old params synchronously via dst_release() while deferring the params struct free with kfree_rcu(). A concurrent tunnel_key_act() reader on the datapath may still hold the old params pointer (under rcu_read_lock_bh) and proceed to call dst_clone(¶ms->tcft_enc_metadata->dst) after the writer's dst_release has already pushed the dst's rcuref to RCUREF_DEAD. zdi-disclosures@trendmicro.com produced a poc which i (and Victor) verified that KASAN reports: ================================================================== BUG: KASAN: slab-use-after-free in instrument_atomic_read_write include/linux/instrumented.h:112 BUG: KASAN: slab-use-after-free in atomic_sub_return_release include/linux/atomic/atomic-instrumented.h:326 BUG: KASAN: slab-use-after-free in __rcuref_put include/linux/rcuref.h:109 BUG: KASAN: slab-use-after-free in rcuref_put include/linux/rcuref.h:173 BUG: KASAN: slab-use-after-free in dst_release+0x5b/0x370 net/core/dst.c:168 Write of size 4 at addr ffff88806158de40 by task poc/9388 CPU: 0 UID: 0 PID: 9388 Comm: poc Tainted: G W 7.1.0-rc7 #7 PREEMPT(lazy) Tainted: [W]=WARN Hardware name: QEMU Ubuntu 25.10 PC v2 (i440FX + PIIX, + 10.1 machine, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014 Call Trace: __dump_stack lib/dump_stack.c:94 dump_stack_lvl+0x100/0x190 lib/dump_stack.c:120 print_address_description mm/kasan/report.c:378 print_report+0x139/0x4ad mm/kasan/report.c:482 kasan_report+0xe4/0x1d0 mm/kasan/report.c:595 check_region_inline mm/kasan/generic.c:186 kasan_check_range+0x125/0x200 mm/kasan/generic.c:200 instrument_atomic_read_write include/linux/instrumented.h:112 atomic_sub_return_release include/linux/atomic/atomic-instrumented.h:326 __rcuref_put include/linux/rcuref.h:109 rcuref_put include/linux/rcuref.h:173 dst_release+0x5b/0x370 net/core/dst.c:168 refdst_drop include/net/dst.h:272 skb_dst_drop include/net/dst.h:284 skb_release_head_state+0x293/0x400 net/core/skbuff.c:1163 skb_release_all net/core/skbuff.c:1187 [..] Allocated by task 9391: kasan_save_stack+0x30/0x50 mm/kasan/common.c:57 kasan_save_track+0x14/0x30 mm/kasan/common.c:78 poison_kmalloc_redzone mm/kasan/common.c:398 __kasan_kmalloc+0x9a/0xb0 mm/kasan/common.c:415 kasan_kmalloc include/linux/kasan.h:263 __do_kmalloc_node mm/slub.c:5296 __kmalloc_noprof+0x2f1/0x830 mm/slub.c:5308 kmalloc_noprof include/linux/slab.h:954 kzalloc_noprof include/linux/slab.h:1188 offload_action_alloc+0x2f/0x130 net/core/flow_offload.c:35 tcf_action_offload_add_ex+0x1ba/0x880 net/sched/act_api.c:258 tcf_action_offload_add net/sched/act_api.c:293 tcf_action_init+0x66e/0xa20 net/sched/act_api.c:1547 tcf_action_add+0xf6/0x5d0 net/sched/act_api.c:2101 [..] Freed by task 9391: kasan_save_stack+0x30/0x50 mm/kasan/common.c:57 kasan_save_track+0x14/0x30 mm/kasan/common.c:78 kasan_save_free_info+0x3b/0x70 mm/kasan/generic.c:584 poison_slab_object mm/kasan/common.c:253 __kasan_slab_free+0x6b/0x90 mm/kasan/common.c:285 kasan_slab_free include/linux/kasan.h:235 slab_free_hook mm/slub.c:2689 slab_free mm/slub.c:6251 kfree+0x21f/0x6b0 mm/slub.c:6566 tcf_action_offload_add_ex+0x4ad/0x880 net/sched/act_api.c:284 tcf_action_offload_add net/sched/act_api.c:293 tcf_action_init+0x66e/0xa20 net/sched/act_api.c:1547 tcf_action_add+0xf6/0x5d0 net/sched/act_api.c:2101 The buggy address belongs to the object at ffff88806158de00 which belongs to the cache kmalloc-256 of size 256 The buggy address is located 64 bytes inside of freed 256-byte region [ffff88806158de00, ffff88806158df00) The buggy address belongs to the physical page: page: refcount:0 mapcount:0 mapping:0000000000000000 index:0xffff88806158d600 pfn:0x6158c head: order:1 mapcount:0 entire_mapcount:0 nr_pages_mapped:0 pincount:0 flags: 0x4fff00000000240(workingset|head|node=1|zone=1|lastcpupid=0x7ff) page_type: f5(slab) raw: 04fff00000000240 ffff88801c841b40 ffffea0001856290 ffffea0001856190 raw: ffff88806158d600 0000000800100009 00000000f5000000 0000000000000000 head: 04fff00000000240 ffff88801c841b40 ffffea0001856290 ffffea0001856190 head: ffff88806158d600 0000000800100009 00000000f5000000 0000000000000000 head: 04fff00000000001 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 0xd2820(GFP_ATOMIC|__GFP_NOWARN|__GFP_NORETRY|__GFP_COMP|__GFP_NOMEMALLOC), pid 9391, tgid 9378 (poc), ts 123227323196, free_ts 0 set_page_owner include/linux/page_owner.h:32 post_alloc_hook+0xfe/0x140 mm/page_alloc.c:1853 prep_new_page mm/page_alloc.c:1861 get_page_from_freelist+0x110c/0x2fc0 mm/page_alloc.c:3941 __alloc_frozen_pages_noprof+0x263/0x2bc0 mm/page_alloc.c:5221 alloc_slab_page mm/slub.c:3278 allocate_slab mm/slub.c:3467 new_slab+0xa6/0x690 mm/slub.c:3525 refill_objects+0x271/0x420 mm/slub.c:7272 refill_sheaf mm/slub.c:2816 __pcs_replace_empty_main+0x373/0x630 mm/slub.c:4652 alloc_from_pcs mm/slub.c:4750 slab_alloc_node mm/slub.c:4884 __do_kmalloc_node mm/slub.c:5295 __kmalloc_noprof+0x66d/0x830 mm/slub.c:5308 kmalloc_noprof include/linux/slab.h:954 metadata_dst_alloc+0x26/0x90 net/core/dst.c:298 tun_rx_dst include/net/dst_metadata.h:144 __ip_tun_set_dst include/net/dst_metadata.h:208 tunnel_key_init+0xb01/0x1b90 net/sched/act_tunnel_key.c:451 tcf_action_init_1+0x46b/0x6c0 net/sched/act_api.c:1428 tcf_action_init+0x448/0xa20 net/sched/act_api.c:1503 tcf_action_add+0xf6/0x5d0 net/sched/act_api.c:2101 [..] ================================================================== Fix by moving dst_release() into a custom RCU callback that runs after the grace period, matching the lifetime of the containing params struct. Readers in the datapath therefore always find a live rcuref when calling dst_clone(). Fixes: 9174c3df1cd18 ("net/sched: act_tunnel_key: fix memory leak in case of action replace") Reported-by: zdi-disclosures@trendmicro.com Tested-by: Victor Nogueira Signed-off-by: Jamal Hadi Salim Reviewed-by: Davide Caratti Link: https://patch.msgid.link/20260711150537.7946-1-jhs@mojatatu.com Signed-off-by: Paolo Abeni --- net/sched/act_tunnel_key.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/net/sched/act_tunnel_key.c b/net/sched/act_tunnel_key.c index 876b30c5709e..b14807761d82 100644 --- a/net/sched/act_tunnel_key.c +++ b/net/sched/act_tunnel_key.c @@ -342,14 +342,20 @@ static const struct nla_policy tunnel_key_policy[TCA_TUNNEL_KEY_MAX + 1] = { [TCA_TUNNEL_KEY_ENC_TTL] = { .type = NLA_U8 }, }; +static void tunnel_key_release_params_rcu(struct rcu_head *head) +{ + struct tcf_tunnel_key_params *p = container_of(head, typeof(*p), rcu); + + if (p->tcft_action == TCA_TUNNEL_KEY_ACT_SET) + dst_release(&p->tcft_enc_metadata->dst); + kfree(p); +} + static void tunnel_key_release_params(struct tcf_tunnel_key_params *p) { if (!p) return; - if (p->tcft_action == TCA_TUNNEL_KEY_ACT_SET) - dst_release(&p->tcft_enc_metadata->dst); - - kfree_rcu(p, rcu); + call_rcu(&p->rcu, tunnel_key_release_params_rcu); } static int tunnel_key_init(struct net *net, struct nlattr *nla, From e0b5252a59383b77d1b8dbeda00b7184dd95f4d3 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Fri, 10 Jul 2026 14:12:35 -0400 Subject: [PATCH 1050/1054] sctp: fix auth_hmacs array size in struct sctp_cookie The auth_hmacs array in struct sctp_cookie is supposed to store a complete SCTP_AUTH_HMAC_ALGO parameter, which consists of a struct sctp_paramhdr followed by N HMAC identifiers. However, the array size was calculated using an extra 2 bytes instead of sizeof(struct sctp_paramhdr), which is 4 bytes. When four HMAC identifiers are configured, the HMAC-ALGO parameter stored in the endpoint is larger than the auth_hmacs buffer in the cookie. As a result, sctp_association_init() copies beyond the end of auth_hmacs when initializing the association, corrupting the adjacent auth_chunks field. This can lead to an invalid HMAC identifier being accepted and later cause an out-of-bounds read in sctp_auth_get_hmac(). Fix the array size calculation by including the full SCTP parameter header size. Fixes: 1f485649f529 ("[SCTP]: Implement SCTP-AUTH internals") Reported-by: Yuan Tan Reported-by: Xin Liu Reported-by: Zihan Xi Reported-by: Ren Wei Signed-off-by: Xin Long Link: https://patch.msgid.link/634a0de0d5de29532915e6d47c92a0cbc206e03f.1783707155.git.lucien.xin@gmail.com Signed-off-by: Paolo Abeni --- include/net/sctp/structs.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index affee44bd38e..cccc662561aa 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -312,7 +312,8 @@ struct sctp_cookie { __u8 auth_random[sizeof(struct sctp_paramhdr) + SCTP_AUTH_RANDOM_LENGTH]; - __u8 auth_hmacs[SCTP_AUTH_NUM_HMACS * sizeof(__u16) + 2]; + __u8 auth_hmacs[sizeof(struct sctp_paramhdr) + + SCTP_AUTH_NUM_HMACS * sizeof(__u16)]; __u8 auth_chunks[sizeof(struct sctp_paramhdr) + SCTP_AUTH_MAX_CHUNKS]; /* This is a shim for my peer's INIT packet, followed by From 1cb8553c02e93e5a150cebd42f9ee3db0ece4707 Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Sun, 12 Jul 2026 00:37:16 +0800 Subject: [PATCH 1051/1054] bnxt_en: Handle partially initialized auxiliary devices bnxt_aux_devices_init() calls auxiliary_device_init() before all fields used by bnxt_aux_dev_release() are initialized. After auxiliary_device_init() succeeds, later errors must unwind with auxiliary_device_uninit(), which invokes the release callback. The release callback assumes that aux_priv->id, aux_priv->edev, edev->net and edev->ulp_tbl are all populated. If allocation fails after auxiliary_device_init(), the release path can otherwise dereference or clear partially initialized state. Allocate and attach the bnxt_en_dev and ULP table before calling auxiliary_device_init(), so the release callback only sees a fully initialized auxiliary private object. If auxiliary_device_init() itself fails, free those allocations directly because device_initialize() has not run and the release callback will not be invoked. This issue was found by a static analysis checker and confirmed by manual source review. Fixes: 194fad5b2781 ("bnxt_en: Refactor bnxt_rdma_aux_device_init/uninit functions") Signed-off-by: Ruoyu Wang Reviewed-by: Pavan Chebbi Link: https://patch.msgid.link/20260711163716.3996929-1-ruoyuw560@gmail.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c | 39 ++++++++++--------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c index 5c751933da6a..a515c368bac0 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c @@ -566,6 +566,18 @@ void bnxt_aux_devices_init(struct bnxt *bp) if (!aux_priv) goto next_auxdev; + edev = kzalloc_obj(*edev); + if (!edev) + goto aux_priv_free; + aux_priv->edev = edev; + bnxt_set_edev_info(edev, bp); + + ulp = kzalloc_obj(*ulp); + if (!ulp) + goto edev_free; + edev->ulp_tbl = ulp; + aux_priv->id = idx; + aux_dev = &aux_priv->aux_dev; aux_dev->id = bp->auxdev_id; aux_dev->name = bnxt_aux_devices[idx].name; @@ -573,37 +585,26 @@ void bnxt_aux_devices_init(struct bnxt *bp) aux_dev->dev.release = bnxt_aux_dev_release; rc = auxiliary_device_init(aux_dev); - if (rc) { - kfree(aux_priv); - goto next_auxdev; - } + if (rc) + goto ulp_free; bp->aux_priv[idx] = aux_priv; /* From this point, all cleanup will happen via the .release * callback & any error unwinding will need to include a call * to auxiliary_device_uninit. */ - edev = kzalloc_obj(*edev); - if (!edev) - goto aux_dev_uninit; - - aux_priv->edev = edev; - bnxt_set_edev_info(edev, bp); - - ulp = kzalloc_obj(*ulp); - if (!ulp) - goto aux_dev_uninit; - - edev->ulp_tbl = ulp; bp->edev[idx] = edev; if (idx == BNXT_AUXDEV_RDMA) bp->ulp_num_msix_want = bnxt_set_dflt_ulp_msix(bp); - aux_priv->id = idx; bnxt_auxdev_set_state(bp, idx, BNXT_ADEV_STATE_INIT); continue; -aux_dev_uninit: - auxiliary_device_uninit(aux_dev); +ulp_free: + kfree(ulp); +edev_free: + kfree(edev); +aux_priv_free: + kfree(aux_priv); next_auxdev: if (idx == BNXT_AUXDEV_RDMA) bp->flags &= ~BNXT_FLAG_ROCE_CAP; From 04aeddf2dadd0eb7ad016a766dcbe9c983311f09 Mon Sep 17 00:00:00 2001 From: Andre Carvalho Date: Fri, 10 Jul 2026 23:19:17 +0100 Subject: [PATCH 1052/1054] selftests: netconsole: only restore MAC when it changed on resume The "mac" bind mode reactivation downs the interface, restores the saved MAC and renames it to trigger a target resume. This assumes the recreated interface comes back with a different MAC, which is true under MACAddressPolicy=none (as on the Netdev CI) but not when MACs are persistent. In the persistent case netconsole resumes the target on its own, and the down/restore/rename flow instead drops it and fails the test. Guard the block on the MAC having actually changed so the test passes under both policies. Fixes: 6ecc08329bab ("selftests: netconsole: validate target resume") Reported-by: Matthieu Baerts (NGI0) Closes: https://lore.kernel.org/netdev/f398373e-2cb4-4649-a491-9763df94d98b@kernel.org/ Signed-off-by: Andre Carvalho Tested-by: Matthieu Baerts (NGI0) Reviewed-by: Breno Leitao Link: https://patch.msgid.link/20260710-netcons-mac-reload-v1-1-3fb1bcc70b4a@gmail.com Signed-off-by: Paolo Abeni --- .../testing/selftests/drivers/net/netconsole/netcons_resume.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/drivers/net/netconsole/netcons_resume.sh b/tools/testing/selftests/drivers/net/netconsole/netcons_resume.sh index cb59cf436dd0..d9111f2102bc 100755 --- a/tools/testing/selftests/drivers/net/netconsole/netcons_resume.sh +++ b/tools/testing/selftests/drivers/net/netconsole/netcons_resume.sh @@ -44,7 +44,8 @@ function trigger_reactivation() { # Restore MACs ip netns exec "${NAMESPACE}" ip link set "${DSTIF}" \ address "${SAVED_DSTMAC}" - if [ "${BINDMODE}" == "mac" ]; then + if [ "${BINDMODE}" == "mac" ] && + [ "$(mac_get "${SRCIF}")" != "${SAVED_SRCMAC}" ]; then ip link set dev "${SRCIF}" down ip link set dev "${SRCIF}" address "${SAVED_SRCMAC}" # Rename device in order to trigger target resume, as initial From 2c72eb6286347d05a885412fb076993bd5286b53 Mon Sep 17 00:00:00 2001 From: Xuanqiang Luo Date: Sun, 12 Jul 2026 21:03:43 +0800 Subject: [PATCH 1053/1054] llc: fix SAP refcount leak when creating incoming sockets llc_sap_add_socket() takes a SAP reference for each socket added to a SAP, and llc_sap_remove_socket() releases it. llc_create_incoming_sock() takes an additional SAP reference after adding the child socket. This extra reference was balanced by an explicit llc_sap_put() in llc_ui_release() until commit 3100aa9d74db ("llc: fix SAP reference counting w.r.t. socket handling") removed that put. The corresponding hold in the accept path was left behind. When such a child socket is removed, only the reference taken by llc_sap_add_socket() is released. The extra reference keeps the SAP alive after its last socket is removed. Remove the obsolete hold. Fixes: 3100aa9d74db ("llc: fix SAP reference counting w.r.t. socket handling") Cc: stable@vger.kernel.org Signed-off-by: Xuanqiang Luo Link: https://patch.msgid.link/20260712130343.518797-1-xuanqiang.luo@linux.dev Signed-off-by: Paolo Abeni --- net/llc/llc_conn.c | 1 - 1 file changed, 1 deletion(-) diff --git a/net/llc/llc_conn.c b/net/llc/llc_conn.c index e8f427375c68..260460d50f54 100644 --- a/net/llc/llc_conn.c +++ b/net/llc/llc_conn.c @@ -767,7 +767,6 @@ static struct sock *llc_create_incoming_sock(struct sock *sk, newllc->dev = dev; dev_hold(dev); llc_sap_add_socket(llc->sap, newsk); - llc_sap_hold(llc->sap); out: return newsk; } From 56d96fededd61192cd7cc8d2b0f36adfd59036c3 Mon Sep 17 00:00:00 2001 From: Weiming Shi Date: Sat, 11 Jul 2026 04:50:00 -0700 Subject: [PATCH 1054/1054] mpls: fix NULL deref in mpls_valid_fib_dump_req() on CONFIG_INET=n On CONFIG_INET=n builds, mpls_valid_fib_dump_req() walks the parsed attribute table itself instead of calling ip_valid_fib_dump_req(). The RTA_OIF arm passes tb[RTA_OIF] to nla_get_u32() without checking it is present, so an RTM_GETROUTE dump for AF_MPLS with strict checking and no RTA_OIF hits a NULL dereference. RTM_GETROUTE is RTNL_KIND_GET, which rtnetlink_rcv_msg() permits without CAP_NET_ADMIN, so an unprivileged user can trigger it. Oops: general protection fault, probably for non-canonical address 0xdffffc0000000000: 0000 [#1] SMP KASAN NOPTI KASAN: null-ptr-deref in range [0x0000000000000000-0x0000000000000007] RIP: 0010:mpls_valid_fib_dump_req (net/mpls/af_mpls.c:2189) Call Trace: mpls_dump_routes (net/mpls/af_mpls.c:2236) netlink_dump (net/netlink/af_netlink.c:2331) __netlink_dump_start (net/netlink/af_netlink.c:2446) rtnetlink_rcv_msg (net/core/rtnetlink.c:7033) netlink_rcv_skb (net/netlink/af_netlink.c:2556) netlink_unicast (net/netlink/af_netlink.c:1345) netlink_sendmsg (net/netlink/af_netlink.c:1900) __sock_sendmsg (net/socket.c:790) ____sys_sendmsg (net/socket.c:2684) ___sys_sendmsg (net/socket.c:2738) __sys_sendmsg (net/socket.c:2770) do_syscall_64 (arch/x86/entry/syscall_64.c:94) entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121) Skip unset attributes, as ip_valid_fib_dump_req() does. Fixes: 196cfebf8972 ("net/mpls: Handle kernel side filtering of route dumps") Assisted-by: Claude:claude-opus-4-8 Reported-by: Xiang Mei Signed-off-by: Weiming Shi Reviewed-by: David Ahern Link: https://patch.msgid.link/20260711114958.1009619-3-bestswngs@gmail.com Signed-off-by: Paolo Abeni --- net/mpls/af_mpls.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c index ca504d9626cf..318cb7e2ac5f 100644 --- a/net/mpls/af_mpls.c +++ b/net/mpls/af_mpls.c @@ -2186,6 +2186,9 @@ static int mpls_valid_fib_dump_req(struct net *net, const struct nlmsghdr *nlh, int ifindex; if (i == RTA_OIF) { + if (!tb[i]) + continue; + ifindex = nla_get_u32(tb[i]); filter->dev = dev_get_by_index_rcu(net, ifindex); if (!filter->dev)