From ee7863e43228a3143398dc5bbb943c9a735a8fca Mon Sep 17 00:00:00 2001 From: Akhil R Date: Tue, 31 Mar 2026 15:52:55 +0530 Subject: [PATCH 001/652] 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 002/652] 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 003/652] 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 004/652] 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 005/652] 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 006/652] 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 007/652] 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 008/652] 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 009/652] 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 010/652] 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 011/652] 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 012/652] 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 013/652] 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 014/652] 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 015/652] 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 016/652] 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 017/652] 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 018/652] 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 019/652] 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 020/652] 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 021/652] 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 022/652] 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 023/652] 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 024/652] 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 025/652] 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 026/652] 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 027/652] 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 028/652] 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 029/652] 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 030/652] 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 031/652] 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 032/652] 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 033/652] 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 31620fc1c81078746c4794e0eb6de539ab37e695 Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Tue, 16 Jun 2026 13:09:16 +0300 Subject: [PATCH 034/652] 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 035/652] 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 036/652] 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 037/652] 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 038/652] 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 039/652] 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 040/652] 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 041/652] 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 042/652] 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 043/652] 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 044/652] 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 045/652] 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 046/652] 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 047/652] 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 048/652] 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 049/652] 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 050/652] 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 051/652] 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 052/652] 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 053/652] 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 054/652] 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 055/652] 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 056/652] 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 057/652] 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 058/652] 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 6860b467f569f732b11cbc588ae7e195e90e7e23 Mon Sep 17 00:00:00 2001 From: Petr Wozniak Date: Sun, 21 Jun 2026 12:03:26 +0200 Subject: [PATCH 059/652] 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 060/652] 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 30e542a36228db353e81efcd39e4dbc7a95c88c5 Mon Sep 17 00:00:00 2001 From: Anuj Gupta Date: Wed, 17 Jun 2026 21:20:51 +0530 Subject: [PATCH 061/652] 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 062/652] 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 063/652] 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 064/652] 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 83a3dfc018943b05b6daf3a6f891833e1aabfa1f Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 23 Jun 2026 17:08:15 +0200 Subject: [PATCH 065/652] 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 066/652] 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 18a4e5cf633fad5c40ac9d936c51bf38db68796d Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Wed, 10 Jun 2026 15:52:01 +0800 Subject: [PATCH 067/652] 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 068/652] 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 069/652] 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 070/652] 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 d2e52d610b9b09694261632340b801a421e0b0c5 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Sat, 6 Jun 2026 16:01:55 -0400 Subject: [PATCH 071/652] 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 cceca8cb776b03e83985578f84bd730c30d57f70 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Sat, 13 Jun 2026 23:31:52 -0700 Subject: [PATCH 072/652] 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 073/652] 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 074/652] 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 075/652] 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 076/652] 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 077/652] 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 078/652] 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 079/652] 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 080/652] 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 081/652] 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 082/652] 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 083/652] 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 cb36d81e751173c4b59b5e561c596c925144ea48 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 25 Jun 2026 13:23:56 -1000 Subject: [PATCH 084/652] 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 085/652] 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 086/652] 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 087/652] 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 088/652] 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 089/652] 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 090/652] 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 091/652] 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 092/652] 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 093/652] 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 094/652] 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 095/652] 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 096/652] 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 097/652] 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 098/652] 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 099/652] 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 100/652] 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 101/652] 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 102/652] 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 103/652] 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 104/652] 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 105/652] 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 106/652] 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 107/652] 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 108/652] 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 109/652] 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 6bfc8d01ac4068eced509f8fc74d0cd205e4dcec Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 29 Jun 2026 14:45:26 +0200 Subject: [PATCH 110/652] 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 111/652] 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 112/652] 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 226f4a490d1a938fc838d8f8c46a4eca864c0d78 Mon Sep 17 00:00:00 2001 From: Qianyu Luo Date: Thu, 25 Jun 2026 13:55:08 +0800 Subject: [PATCH 113/652] 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 b7d9c359e5cf867f7eb23df3bb1c6b9e58af24da Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 29 Jun 2026 12:55:48 -1000 Subject: [PATCH 114/652] 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 115/652] 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 116/652] 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 c9a71daaecb2fb1d8c704545cc0b1c920b9bf5d7 Mon Sep 17 00:00:00 2001 From: Chi Wang Date: Fri, 19 Jun 2026 15:42:44 +0800 Subject: [PATCH 117/652] 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 a2d30022b7c316ad845d1b696e724058b88e5a4e Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Thu, 25 Jun 2026 21:38:08 +0200 Subject: [PATCH 118/652] 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 119/652] 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 120/652] 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 3dd63dba8f9cb6990a40af7ed66ee0159f475819 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Wed, 1 Jul 2026 17:21:55 +0900 Subject: [PATCH 121/652] 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 122/652] 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 123/652] 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 f5ad2ead846e3a00d040d64c7eaf67b65629f51d Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 12 Jun 2026 22:15:59 +0200 Subject: [PATCH 124/652] 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 125/652] 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 126/652] 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 127/652] 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 128/652] 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 44c74d27d1b9aaa99fa8a83640c1223575262b80 Mon Sep 17 00:00:00 2001 From: Stephen Smalley Date: Thu, 18 Jun 2026 13:55:14 -0400 Subject: [PATCH 129/652] 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 130/652] 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 131/652] 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 56acfeb10019e200ab6787d01f8d7cbe0f01526f Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Thu, 25 Jun 2026 23:53:36 +0000 Subject: [PATCH 132/652] 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 133/652] 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 c283e9ada7fcb7dd4b10592623086b2e6d2f9925 Mon Sep 17 00:00:00 2001 From: "Xiang Mei (Microsoft)" Date: Sat, 27 Jun 2026 02:40:23 +0000 Subject: [PATCH 134/652] 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 135/652] 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 136/652] 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 6666336d8fe5f3f0cd3f8da2865ca78110a6e170 Mon Sep 17 00:00:00 2001 From: Jia He Date: Tue, 30 Jun 2026 22:23:47 +0000 Subject: [PATCH 137/652] 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 138/652] 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 139/652] 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 a846aecb931b4d65d5eafa92a0623545af46d4f2 Mon Sep 17 00:00:00 2001 From: Jacob Moroni Date: Wed, 17 Jun 2026 14:19:36 +0000 Subject: [PATCH 140/652] 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 141/652] 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 b9b0889071569d43623c260074e159cd8f26adb1 Mon Sep 17 00:00:00 2001 From: Jacob Moroni Date: Wed, 17 Jun 2026 16:40:13 +0000 Subject: [PATCH 142/652] 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 143/652] 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 144/652] 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 145/652] 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 146/652] 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 147/652] 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 148/652] 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 149/652] 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 150/652] 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 58481c749c976e81bef9a540e2225ddb021daaa6 Mon Sep 17 00:00:00 2001 From: Yang Xiuwei Date: Thu, 2 Jul 2026 16:29:37 +0800 Subject: [PATCH 151/652] 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 152/652] 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 153/652] 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 154/652] 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 77bfebf110773f5a0d6b5ff8110896adb2c9c335 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Fri, 5 Jun 2026 11:13:50 +0000 Subject: [PATCH 155/652] 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 156/652] 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 157/652] 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 158/652] 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 159/652] 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 160/652] 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 161/652] 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 162/652] 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 163/652] 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 a0738abd042f7406edd2175a819cf2e66388ed97 Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Sat, 27 Jun 2026 00:02:10 +0200 Subject: [PATCH 164/652] 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 165/652] 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 166/652] 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 167/652] 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 168/652] 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 169/652] 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 170/652] 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 171/652] 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 172/652] 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 173/652] 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 6bb8898f702385d363dc2c513a1efa62807f8068 Mon Sep 17 00:00:00 2001 From: Damon Ding Date: Tue, 23 Jun 2026 10:35:06 +0800 Subject: [PATCH 174/652] 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 aede83625ff5d9539508582036df30c809d51058 Mon Sep 17 00:00:00 2001 From: Andreas Kempe Date: Thu, 2 Jul 2026 10:41:23 +0000 Subject: [PATCH 175/652] 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 176/652] 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 177/652] 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 178/652] 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 179/652] 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 cb89f0c1aed02eb233c4271f76f830b37e222ff6 Mon Sep 17 00:00:00 2001 From: Malaya Kumar Rout Date: Sat, 4 Jul 2026 16:27:36 +0530 Subject: [PATCH 180/652] 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 181/652] 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 182/652] 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 183/652] 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 184/652] 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 5b4d8e11ba68cd9697de49861a818c75f21e463d Mon Sep 17 00:00:00 2001 From: Mehdi Hassan Date: Sat, 4 Jul 2026 02:35:06 +0000 Subject: [PATCH 185/652] 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 186/652] 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 2538bd3cd1ff5af655908469544ac7b7ae259386 Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Sat, 27 Jun 2026 11:01:17 +0800 Subject: [PATCH 187/652] 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 188/652] 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 189/652] 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 190/652] 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 191/652] 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 192/652] 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 193/652] 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 194/652] 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 195/652] 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 196/652] 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 197/652] 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 f72df3a4c33b64de3418ec74d1ad4f028e09d161 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 2 Jul 2026 20:36:59 +0900 Subject: [PATCH 198/652] 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 199/652] 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 200/652] 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 201/652] 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 202/652] 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 203/652] 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 204/652] 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 205/652] 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 206/652] 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 207/652] 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 208/652] 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 209/652] 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 210/652] 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 211/652] 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 212/652] 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 213/652] 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 214/652] 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 215/652] 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 216/652] 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 217/652] 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 218/652] 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 219/652] 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 220/652] 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 221/652] 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 222/652] 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 223/652] 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 224/652] 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 225/652] 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 226/652] 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 227/652] 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 228/652] 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 229/652] 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 230/652] 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 231/652] 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 232/652] 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 233/652] 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 234/652] 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 235/652] 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 236/652] 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 237/652] 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 238/652] 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 239/652] 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 240/652] 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 241/652] 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 242/652] 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 243/652] 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 244/652] 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 245/652] 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 246/652] 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 247/652] 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 248/652] 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 249/652] 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 250/652] 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 251/652] 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 252/652] 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 253/652] 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 a3b5f242997a3be7404112fd48784881560aea57 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Fri, 12 Jun 2026 11:27:56 +0800 Subject: [PATCH 254/652] 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 255/652] 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 256/652] 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 257/652] 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 258/652] 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 259/652] 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 260/652] 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 3d04d9f390eeaab4d9e1ed4e9737e3d83581e18b Mon Sep 17 00:00:00 2001 From: Ankit Nautiyal Date: Mon, 22 Jun 2026 15:47:36 +0530 Subject: [PATCH 261/652] 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 262/652] 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 263/652] 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 264/652] 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 265/652] 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 266/652] 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 267/652] 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 268/652] 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 269/652] 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 270/652] 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 271/652] 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 272/652] 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 273/652] 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 274/652] 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 275/652] 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 81bbcff0c053c4f5c711c31a9b72fc492bd96c3f Mon Sep 17 00:00:00 2001 From: Tao Liu Date: Mon, 6 Jul 2026 11:27:07 +1200 Subject: [PATCH 276/652] 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 277/652] 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 278/652] 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 279/652] 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 280/652] 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 281/652] 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 282/652] 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 283/652] 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 284/652] 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 285/652] 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 286/652] 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 287/652] 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 288/652] 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 289/652] 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 290/652] 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 291/652] 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 292/652] 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 293/652] 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 240c8d2c717b3f8153e7e877b22a82518d78dbdc Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Sat, 27 Jun 2026 21:13:13 +0800 Subject: [PATCH 294/652] 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 8c6db30d79528279abbeb416e4f533f1f91b8724 Mon Sep 17 00:00:00 2001 From: Mostafa Saleh Date: Thu, 2 Jul 2026 10:38:40 +0000 Subject: [PATCH 295/652] 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 296/652] 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 297/652] 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 298/652] 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 252f8c681adc8614b70f844ba3de3a138c33a783 Mon Sep 17 00:00:00 2001 From: Christopher Mackle Date: Sat, 20 Jun 2026 01:39:16 +0000 Subject: [PATCH 299/652] 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 300/652] 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 301/652] 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 302/652] 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 303/652] 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 304/652] 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 305/652] 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 306/652] 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 307/652] 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 308/652] 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 309/652] 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 310/652] 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 311/652] 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 312/652] 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 313/652] 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 314/652] 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 315/652] 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 316/652] 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 317/652] 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 318/652] 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 319/652] 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 320/652] 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 321/652] 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 322/652] 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 323/652] 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 324/652] 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 325/652] 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 326/652] 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 327/652] 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 328/652] 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 329/652] 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 330/652] 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 331/652] 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 332/652] 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 333/652] 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 334/652] 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 335/652] 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 336/652] 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 337/652] 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 338/652] 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 339/652] 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 340/652] 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 341/652] 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 342/652] 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 343/652] 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 344/652] 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 345/652] 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 346/652] 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 347/652] 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 348/652] 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 349/652] 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 350/652] 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 351/652] 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 352/652] 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 353/652] 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 354/652] 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 355/652] 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 356/652] 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 357/652] 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 358/652] 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 359/652] 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 360/652] 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 361/652] 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 362/652] 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 363/652] 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 364/652] 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 cd9993d22a577339a93dcb401574e874ea29b143 Mon Sep 17 00:00:00 2001 From: Bastian Blank Date: Sun, 5 Jul 2026 12:49:17 +0200 Subject: [PATCH 365/652] 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 07c60dda9c059c09f83d42a3ebda2e7cc1cf3bc2 Mon Sep 17 00:00:00 2001 From: Sandipan Das Date: Wed, 8 Jul 2026 16:02:04 +0530 Subject: [PATCH 366/652] 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 367/652] 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 368/652] 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 369/652] 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 370/652] 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 371/652] 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 372/652] 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 373/652] 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 374/652] 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 375/652] 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 376/652] 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 377/652] 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 378/652] 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 379/652] 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 380/652] 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 381/652] 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 382/652] 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 383/652] 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 384/652] 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 385/652] 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 386/652] 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 387/652] 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 388/652] 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 389/652] 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 390/652] 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 391/652] 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 392/652] 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 393/652] 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 394/652] 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 395/652] 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 f47b6b313df9a17010a72b3f389f0a5e9e49db3b Mon Sep 17 00:00:00 2001 From: Greg Ungerer Date: Tue, 7 Jul 2026 22:17:14 +1000 Subject: [PATCH 396/652] 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 397/652] 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 398/652] 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 399/652] 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 400/652] 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 42e74d8f21ce448fe12ba3dd59db817b3e977f49 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Tue, 7 Jul 2026 14:02:40 -0400 Subject: [PATCH 401/652] 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 abf76d3239dee97b66e7241ad04811f1ce562e28 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 9 Jun 2026 13:37:36 -0400 Subject: [PATCH 402/652] 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 403/652] 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 404/652] 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 405/652] 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 406/652] 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 407/652] 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 408/652] 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 409/652] 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 410/652] 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 411/652] 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 412/652] 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 413/652] 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 414/652] 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 415/652] 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 416/652] 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 417/652] 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 418/652] 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 419/652] 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 420/652] 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 421/652] 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 422/652] 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 423/652] 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 424/652] 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 425/652] 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 426/652] 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 427/652] 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 428/652] 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 429/652] 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 430/652] 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 431/652] 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 432/652] 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 433/652] 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 434/652] 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 435/652] 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 436/652] 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 437/652] 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 438/652] 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 439/652] 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 440/652] 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 441/652] 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 442/652] 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 443/652] 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 444/652] 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 445/652] 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 446/652] 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 447/652] 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 448/652] 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 449/652] 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 450/652] 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 451/652] 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 452/652] 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 453/652] 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 454/652] 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 455/652] 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 456/652] 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 457/652] 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 458/652] 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 459/652] 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 460/652] 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 461/652] 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 462/652] 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 463/652] 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 464/652] 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 465/652] 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 466/652] 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 467/652] 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 97272a5704bf13369bba43951ad9fe8f806ccc7f Mon Sep 17 00:00:00 2001 From: Kailang Yang Date: Thu, 9 Jul 2026 14:57:06 +0800 Subject: [PATCH 468/652] 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 469/652] 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 34a4dd45cf210c04fee773b0dbc350aec285f03c Mon Sep 17 00:00:00 2001 From: Matthew Brost Date: Wed, 1 Jul 2026 18:24:34 -0700 Subject: [PATCH 470/652] 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 471/652] 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 472/652] 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 473/652] 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 474/652] 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 a4f27ad055392fa164f5649e89a3637b033c5fcc Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Wed, 8 Jul 2026 20:35:20 +0800 Subject: [PATCH 475/652] 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 476/652] 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 477/652] 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 478/652] 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 479/652] 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 480/652] 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 481/652] 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 482/652] 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 483/652] 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 484/652] 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 485/652] 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 486/652] 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 f86a7c96406d406a66f3d7653eca86aa576fcf6b Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Thu, 9 Jul 2026 13:12:24 +0100 Subject: [PATCH 487/652] 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 488/652] 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 e3d9c7160d483fc8f9e225aafad8ecbbc43f3151 Mon Sep 17 00:00:00 2001 From: Norbert Manthey Date: Thu, 9 Jul 2026 15:54:39 +0000 Subject: [PATCH 489/652] 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 490/652] 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 491/652] 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 492/652] 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 493/652] 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 494/652] 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 495/652] 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 496/652] 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 497/652] 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 498/652] 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 499/652] 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 500/652] 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 501/652] 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 502/652] 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 503/652] 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 504/652] 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 505/652] 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 506/652] 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 507/652] 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 508/652] 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 509/652] 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 510/652] 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 511/652] 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 512/652] 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 513/652] 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 514/652] 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 515/652] 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 516/652] 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 517/652] 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 518/652] 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 519/652] 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 520/652] 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 521/652] 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 522/652] 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 523/652] 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 524/652] 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 525/652] 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 526/652] 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 527/652] 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 528/652] 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 529/652] 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 530/652] 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 531/652] 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 532/652] 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 533/652] 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 534/652] 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 535/652] 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 536/652] 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 537/652] 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 538/652] 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 539/652] 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 540/652] 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 541/652] 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 542/652] 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 543/652] 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 544/652] 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 545/652] 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 546/652] 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 547/652] 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 548/652] 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 549/652] 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 550/652] 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 551/652] 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 552/652] 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 553/652] 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 554/652] 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 555/652] 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 556/652] 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 557/652] 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 558/652] 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 559/652] 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 560/652] 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 561/652] 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 562/652] 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 563/652] 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 564/652] 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 565/652] 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 566/652] 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 567/652] 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 568/652] 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 569/652] 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 570/652] 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 571/652] 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 572/652] 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 573/652] 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 574/652] 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 575/652] 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 576/652] 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 577/652] 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 578/652] 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 579/652] 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 580/652] 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 581/652] 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 582/652] 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 583/652] 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 584/652] 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 585/652] 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 586/652] 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 587/652] 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 588/652] 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 589/652] 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 590/652] 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 591/652] 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 592/652] 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 593/652] 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 594/652] 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 595/652] 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 596/652] 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 597/652] 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 598/652] 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 599/652] 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 600/652] 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 601/652] 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 602/652] 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 603/652] 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 604/652] 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 605/652] 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 606/652] 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 607/652] 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 608/652] 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 25957f7c3dac3265332d766b71233e3622f17e14 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Wed, 3 Jun 2026 21:33:09 -0700 Subject: [PATCH 609/652] 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 610/652] 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 611/652] 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 612/652] 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 613/652] 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 614/652] 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 615/652] 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 616/652] 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 617/652] 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 899160e2774d9952e9f2770b38f701ff1906c0b2 Mon Sep 17 00:00:00 2001 From: Luke Wang Date: Wed, 15 Jul 2026 15:18:11 +0800 Subject: [PATCH 618/652] 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 619/652] 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 620/652] 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 621/652] 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 622/652] 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 623/652] 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 624/652] 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 625/652] 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 2f2312c422fd2695da772cecb30c69994b795964 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Tue, 14 Jul 2026 09:03:06 -0700 Subject: [PATCH 626/652] 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 627/652] 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 628/652] 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 629/652] 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 630/652] 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 631/652] 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 632/652] 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 633/652] 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 634/652] 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 635/652] 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 636/652] 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 637/652] 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 638/652] 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 639/652] 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 640/652] 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 641/652] 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 642/652] 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 643/652] 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 df6134b527a88b3e65ba6ae5073664af091d5fd2 Mon Sep 17 00:00:00 2001 From: Zhiping Zhang Date: Thu, 2 Jul 2026 15:24:58 -0700 Subject: [PATCH 644/652] 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 645/652] 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 646/652] 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 647/652] 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 648/652] 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 649/652] 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 650/652] 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 651/652] 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 652/652] 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)